Svelte — конструкция toggle и выделение текущего

Admin WordPress

Пример toggle на svelte.

Меняет состояние по клику на противоположное

Создаем переменную active:

<script>
    let active = true;
</script>

Добавляем конструкцию class:active={active} в то место, которое хотим изменять:

<div
   class="другие классы"
   class:active="{active}"
   on:click="{() => active = !active}"
>
Скрытый текст
</div>

Добавляем стиль:

<style lang="scss">
.active {
    display: none;
}
</style>

Выделяет текущую кнопку из множества

Если кнопок (любых других элементов) много на странице, то подойдет другая конструкция. Для каждой кнопки мы назначаем по клику присвоение своего значения. А если оно присвоено, то добавляется класс.

<script>
    let current = '';
</script>
   
<div
   class="другие классы"
   class:active="{current === 'btn-1'}"
   on:click="{() => current = 'btn-1'}"
>
btn-1
</div>

<div
   class="другие классы"
   class:active="{current === 'btn-2'}"
   on:click="{() => current = 'btn-2'}"
>
btn-2
</div>

<style lang="scss">
.active {
    background-color: #ff0000;
}
</style>

Расширенный вариант предыдущего

Получаем статус выделенной кнопки из БД. Т.е. при загрузке страницы наша кнопка с выбранной настройкой всегда будет выделена.

<script>

    let current = '';
    let settingButtonActive;

    function __getSettingCategoryButtonActive() {
        getFetch('settings/get-setting-value/' + 'button-active').then((r) => {
            settingButtonActive = r
        });
    }

    function __change_button(button) {
        let data = {
            'settings': button
        }

        postFetch('settings/change-settings-to-button/', data).then((r) => {
            __getData();
        });
    }

</script>
   
<div class="btn-1"
     class:active="{settingButtonActive === 'all'}"
     on:click="{() => __change_button('all')}"
     on:click="{() => settingButtonActive = 'all'}"
>Все
</div>

   
<div class="btn-1"
     class:active="{settingButtonActive === 'not-all'}"
     on:click="{() => __change_button('not-all')}"
     on:click="{() => settingButtonActive = 'not-all'}"
>Не все
</div>

<style lang="scss">
.active {
    background-color: #ff0000;
}
</style>

Стоит обратить внимание на то, чтобы функция по on:click (если она есть), стояла выше присвоения настройки переменной. Иначе функция может не работать.

Как сделать toggle для блока each

В этом примере будет показано как можно сделать toggle на каждый элемент блока each, чтобы он работал независимо от других блоков.

Для этого воспользуемся уникальным идентификатором.

{#await elements}
{:then elements}
    {#each elements as el}
       <div class="btn"
           on:click="{() => el.id = !el.id}"
       >
           Кнопка открытия/закрытия
       </div>

       <div class="block-hidden" class:hide="{el.id}">
           скрытый блок
       </div>
    {/each}
{/await}


<style lang="scss">
    .hide {
        display: none !important;
    }
</style>

Минус такого подхода в том, что мы перезапишем наш идентификатор. Если он нам не нужен в дальнейшем для взаимодействия, то ничего страшного.

Другой вариант вместо уникального идентификатора использовать выдуманное свойство. В этом случае его не будет, а значит нужно изменить показ класса на отрицание. Плюс такого подхода, что уникальный идентификатор, не будет затираться.

{#await elements}
{:then elements}
    {#each elements as el}

    <div class="btn icon-when-close"
        class:icon-when-open="{el.temp}"
        on:click="{() => el.temp = !el.temp}"
    >
    Кнопка открытия/закрытия
    </div>

    <div class="block-hidden" class:hide="{!el.temp}">
        скрытый блок
    </div>

    {/each}
{/await}


<style lang="scss">
    .hide {
        display: none !important;
    }
</style>

Последний рабочий вариант от 15 мая 2022

В предыдущем варианте, если открыть элемент, а затем подтянуть асинхронные изменения к текущему блоку, то он снова закроется. Это неудобно, когда хочется держать изменения перед глазами в развернутом виде.

В текущей реализации будет:
— каждый toggle работает независимо от других блоков;
— при открытии одного блока закрывается другой;
— при изменении асинхронных данных текущий блок не закрывается.

Содержимое файла store.js

import { writable } from 'svelte/store';

export const store = writable({
    numbers: []
})

В другом файле импортируем:

import {store} from "../../assets/js/store";

Далее в этом же файле делаем функцию:

function writeToStore(id) {

        // Добавим ID при первом открытии
        if ($store.numbers.length === 0) {
            $store.numbers.push(id);

            // Удалим ID при повторном нажатии
        } else if ($store.numbers.includes(id)) {

            // Удаление из массива
            let index = $store.numbers.indexOf(id);
            if (index !== -1) {
                $store.numbers.splice(index, 1);
            }

            // Добавим ID при втором и следующем открытии
        } else {
            $store.numbers.push(id);
        }
    }

И сам блок, которые требуется разворачивать:

<div class="cell icon-when-close-block"
     on:click="{() => writeToStore(done.id)}"
     on:click="{() => done.temp = !done.temp}"
>
    <div class="icon-when-close"
         class:icon-when-open="{done.temp}"
    >
    </div>
</div>

    <div class="block-hidden hide" class:show="{
           $store.numbers.includes(done.id)
       }"
>

       Здесь то, что будет разворачиваться.

    </div>
</div>

Старый вариант (есть ошибка, но оставил на память)

Для текущей реализации воспользуемся хранилищем в Svetle.

Создадим файл store.js с содержимым:

import { writable } from 'svelte/store';
export const store = writable()

Импортируем его в нужном нам файле. В нём же создадим функцию записи в хранилище:

<script>
import {store} from '../../assets/js/store.js'

let currentItem;

function writeToStore(id) {

    // Создадим булевое свойство, если оно не существует.
    if (!$store.bool) {
        $store.bool = true;
    }

    // Если свойство для хранения ID, если в нём еще не хранится текущий ID.
    if ($store.id !== id) {
        $store.id = id;

    // Если же в свойство ID хранится текущий ID, то изменим булевое свойство.
    } else {
        $store.bool = !$store.bool;
    }

}
</script>

В этом же файле основной блок данных:

{#await elements}
{:then elements}
    {#each elements as el}
        <div class="block-container" class:current="{currentItem === el.id}">

            <div class="btn icon-when-close"
                  class:icon-when-open="{el.temp}"
                  on:click="{() => writeToStore(el.id)}"
            >
                Кнопка открытия/закрытия
            </div>

                <div class="block-hidden hide" class:show="{
                    $store.id === el.id &&
                    $store.bool === true
                }"
>
                скрытый блок
            </div>
        </div>
    {/each}
{/await}


<style lang="scss">
    .hide {
        display: none;
    }
   
    .show {
        display: block !important;
    }

    .current {
        background-color: #f9f9f9;
    }
</style>

Из кода выше всё должно быть понятно, разве что разберем этот участок:

<div class="block-hidden hide" class:show="{
    $store.id === done.id &&
    $store.bool === true
}"
>

По умолчанию в блоке стоит класс hide, который прячет элемент. Мы добавим класс show, который покажет спрятанный элемент, если будут соблюдены ряд условий.

Мы также можем у всего контейнера изменить свойство (например, выделить его цветом при открытии). Для этого мы добавили в контейнер класс current.

Кстати, на сайте нет рекламы. У сайта нет цели самоокупаться, но если вам пригодилась информация можете задонатить мне на чашечку кофе в макдаке. Лайкнуть страницу или просто поблагодарить. Карма вам зачтется.

Добавить комментарий

Напишите свой комментарий, если вам есть что добавить/поправить/спросить по теме текущей статьи:
"Svelte — конструкция toggle и выделение текущего"