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 и выделение текущего"