breaking: Refactor global stores for Modal, Toast, and Drawer (#1831)

Co-authored-by: endigo9740 <gundamx9740@gmail.com>
This commit is contained in:
CokaKoala
2023-08-09 13:57:54 -04:00
committed by GitHub
parent c812b36d93
commit 0b7add15c1
27 changed files with 272 additions and 109 deletions

View File

@@ -0,0 +1,5 @@
---
"@skeletonlabs/skeleton": major
---
breaking: Refactored global stores to use contexts for `Toast`, `Drawer`, and `Modal` utilities

View File

@@ -13,15 +13,13 @@ export type { PopupSettings } from './utilities/Popup/types.js';
export type { Transition, TransitionParams } from './internal/transitions.js';
export type { CssClasses, SvelteEvent } from './types.js';
// Stores ---
// Utilities ---
export { storeHighlightJs } from './utilities/CodeBlock/stores.js';
export { storePopup } from './utilities/Popup/popup.js';
export { drawerStore } from './utilities/Drawer/stores.js';
export { modalStore } from './utilities/Modal/stores.js';
export { toastStore } from './utilities/Toast/stores.js';
// Utilities ---
export { getDrawerStore } from './utilities/Drawer/stores.js';
export { getModalStore } from './utilities/Modal/stores.js';
export { getToastStore } from './utilities/Toast/stores.js';
export { initializeStores } from './utilities/index.js';
// Lightswitch
export {

View File

@@ -17,7 +17,7 @@
// Drawer Utils
import type { DrawerSettings } from './types.js';
import { drawerStore } from './stores.js';
import { getDrawerStore } from './stores.js';
import { fade, fly } from 'svelte/transition';
import { dynamicTransition } from '../../internal/transitions.js';
@@ -85,6 +85,7 @@
let elemBackdrop: HTMLElement;
let elemDrawer: HTMLElement;
let anim = { x: 0, y: 0 };
const drawerStore = getDrawerStore();
// Classes
const cBackdrop = 'fixed top-0 left-0 right-0 bottom-0 flex';

View File

@@ -1,8 +1,44 @@
// Drawer Stores
import { writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
import type { DrawerSettings } from './types.js';
const DRAWER_STORE_KEY = 'drawerStore';
/**
* Retrieves the `drawerStore`.
*
* @example
* ```ts
* import { getDrawerStore } from "@skeletonlabs/skeleton";
*
* const drawerStore = getDrawerStore();
*
* drawerStore.open();
* ```
*/
export function getDrawerStore(): DrawerStore {
const drawerStore = getContext<DrawerStore | undefined>(DRAWER_STORE_KEY);
if (!drawerStore)
throw new Error(
'drawerStore is not initialized. Please ensure that `initializeStores()` is invoked in the root layout file of this app!'
);
return drawerStore;
}
/**
* Initializes the `drawerStore`.
*/
export function initializeDrawerStore(): DrawerStore {
const drawerStore = drawerService();
return setContext(DRAWER_STORE_KEY, drawerStore);
}
type DrawerStore = ReturnType<typeof drawerService>;
function drawerService() {
const { subscribe, set, update } = writable<DrawerSettings>({});
return {
@@ -22,6 +58,3 @@ function drawerService() {
})
};
}
// Exports
export const drawerStore = drawerService();

View File

@@ -21,8 +21,8 @@
// Types
import type { CssClasses, SvelteEvent } from '../../index.js';
import { modalStore } from './stores.js';
import { focusTrap } from '../../actions/FocusTrap/focusTrap.js';
import { getModalStore } from './stores.js';
import type { ModalComponent, ModalSettings } from './types.js';
// Props
@@ -116,6 +116,8 @@
let currentComponent: ModalComponent | undefined;
let registeredInteractionWithBackdrop = false;
const modalStore = getModalStore();
// Modal Store Subscription
modalStore.subscribe((modals: ModalSettings[]) => {
if (!modals.length) return;

View File

@@ -1,7 +1,7 @@
import { render } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import { modalStore } from '$lib/utilities/Modal/stores.js';
import { getModalStore } from '$lib/utilities/Modal/stores.js';
import type { ModalSettings } from '$lib/utilities/Modal/types.js';
import Modal from '$lib/utilities/Modal/Modal.svelte';
@@ -27,6 +27,8 @@ const modalPrompt: ModalSettings = {
};
describe('Modal.svelte', () => {
const modalStore = getModalStore();
it('Renders modal alert', async () => {
modalStore.trigger(modalAlert);
const { getByTestId } = render(Modal);

View File

@@ -1,8 +1,44 @@
// Modal Store Queue
import { writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
import type { ModalSettings } from './types.js';
const MODAL_STORE_KEY = 'modalStore';
/**
* Retrieves the `modalStore`.
*
* @example
* ```ts
* import { getmodalStore } from "@skeletonlabs/skeleton";
*
* const modalStore = getModalStore();
*
* modalStore.trigger({ type: "alert", title: "Welcome!" });
* ```
*/
export function getModalStore(): ModalStore {
const modalStore = getContext<ModalStore | undefined>(MODAL_STORE_KEY);
if (!modalStore)
throw new Error(
'modalStore is not initialized. Please ensure that `initializeStores()` is invoked in the root layout file of this app!'
);
return modalStore;
}
/**
* Initializes the `modalStore`.
*/
export function initializeModalStore(): ModalStore {
const modalStore = modalService();
return setContext(MODAL_STORE_KEY, modalStore);
}
type ModalStore = ReturnType<typeof modalService>;
function modalService() {
const { subscribe, set, update } = writable<ModalSettings[]>([]);
return {
@@ -25,5 +61,3 @@ function modalService() {
clear: () => set([])
};
}
export const modalStore = modalService();

View File

@@ -13,7 +13,8 @@
import { flip } from 'svelte/animate';
// Stores
import { toastStore } from './stores.js';
import { getToastStore } from './stores.js';
const toastStore = getToastStore();
// Props
/** Set the toast position.

View File

@@ -1,7 +1,7 @@
import { render } from '@testing-library/svelte';
import { describe, it, expect } from 'vitest';
import { toastStore } from '$lib/utilities/Toast/stores.js';
import { getToastStore } from '$lib/utilities/Toast/stores.js';
import type { ToastSettings } from './types.js';
import Toast from '$lib/utilities/Toast/Toast.svelte';
@@ -17,6 +17,8 @@ const toastMessage: ToastSettings = {
};
describe('Toast.svelte', () => {
const toastStore = getToastStore();
it('Renders modal alert', async () => {
toastStore.trigger(toastMessage);
const { getByTestId } = render(Toast);

View File

@@ -1,29 +1,85 @@
// Toast Store Queue
import { writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
import type { ToastSettings, Toast } from './types.js';
const toastDefaults: ToastSettings = { message: 'Missing Toast Message', autohide: true, timeout: 5000 };
const TOAST_STORE_KEY = 'toastStore';
/**
* Retrieves the `toastStore`.
*
* @example
* ```ts
* import { getToastStore } from "@skeletonlabs/skeleton";
*
* const toastStore = getToastStore();
*
* toastStore.open({ message: "Welcome!" });
* ```
*/
export function getToastStore(): ToastStore {
const toastStore = getContext<ToastStore | undefined>(TOAST_STORE_KEY);
if (!toastStore)
throw new Error(
'toastStore is not initialized. Please ensure that `initializeStores()` is invoked in the root layout file of this app!'
);
return toastStore;
}
/**
* Initializes the `toastStore`.
*/
export function initializeToastStore(): ToastStore {
const toastStore = toastService();
return setContext(TOAST_STORE_KEY, toastStore);
}
// Note for security; differentiates the queued toasts
function randomUUID(): string {
const random = Math.random();
return Number(random).toString(32);
}
// If toast should auto-hide, wait X time, then close by ID
function handleAutoHide(toast: Toast) {
if (toast.autohide === true) {
return setTimeout(() => {
toastStore.close(toast.id);
}, toast.timeout);
}
}
type ToastStore = ReturnType<typeof toastService>;
function toastService() {
const { subscribe, set, update } = writable<Toast[]>([]);
/** Remove toast in queue*/
const close = (id: string) =>
update((tStore) => {
if (tStore.length > 0) {
const index = tStore.findIndex((t) => t.id === id);
const selectedToast = tStore[index];
if (selectedToast) {
// Trigger Callback
if (selectedToast.callback) selectedToast.callback({ id, status: 'closed' });
// Clear timeout
if (selectedToast.timeoutId) clearTimeout(selectedToast.timeoutId);
// Remove
tStore.splice(index, 1);
}
}
return tStore;
});
// If toast should auto-hide, wait X time, then close by ID
function handleAutoHide(toast: Toast) {
if (toast.autohide === true) {
return setTimeout(() => {
close(toast.id);
}, toast.timeout);
}
}
return {
subscribe,
close,
/** Add a new toast to the queue. */
trigger: (toast: ToastSettings) => {
const id: string = randomUUID();
@@ -43,30 +99,13 @@ function toastService() {
});
return id;
},
/** Remove toast in queue*/
close: (id: string) =>
update((tStore) => {
if (tStore.length > 0) {
const index = tStore.findIndex((t) => t.id === id);
const selectedToast = tStore[index];
if (selectedToast) {
// Trigger Callback
if (selectedToast.callback) selectedToast.callback({ id, status: 'closed' });
// Clear timeout
if (selectedToast.timeoutId) clearTimeout(selectedToast.timeoutId);
// Remove
tStore.splice(index, 1);
}
}
return tStore;
}),
/** remain visible on hover */
/** Remain visible on hover */
freeze: (index: number) =>
update((tStore) => {
if (tStore.length > 0) clearTimeout(tStore[index].timeoutId);
return tStore;
}),
/** cancel remain visible on leave */
/** Cancel remain visible on leave */
unfreeze: (index: number) =>
update((tStore) => {
if (tStore.length > 0) tStore[index].timeoutId = handleAutoHide(tStore[index]);
@@ -76,5 +115,3 @@ function toastService() {
clear: () => set([])
};
}
export const toastStore = toastService();

View File

@@ -0,0 +1,26 @@
import { initializeModalStore } from './Modal/stores.js';
import { initializeToastStore } from './Toast/stores.js';
import { initializeDrawerStore } from './Drawer/stores.js';
/**
* Used to initialize the stores for the `Modal`, `Toast`, and `Drawer` utilities.
*
* @example
* ```svelte
* <!-- App's root +layout.svelte -->
* <script>
* import { initializeStores, Toast, Modal, Drawer } from "@skeletonlabs/skeleton";
*
* initializeStores();
* </script>
*
* <Toast />
* <Modal />
* <Drawer />
* ```
*/
export function initializeStores() {
initializeModalStore();
initializeToastStore();
initializeDrawerStore();
}

View File

@@ -11,14 +11,16 @@
import DocsIcon from '$lib/components/DocsIcon/DocsIcon.svelte';
// Components & Utilities
import { AppBar, LightSwitch, popup, modalStore } from '@skeletonlabs/skeleton';
import { AppBar, LightSwitch, popup, getModalStore } from '@skeletonlabs/skeleton';
// Stores
import { drawerStore } from '@skeletonlabs/skeleton';
import { getDrawerStore } from '@skeletonlabs/skeleton';
import { storeTheme } from '$lib/stores/stores';
const drawerStore = getDrawerStore();
// Local
let isOsMac = false;
const modalStore = getModalStore();
// Set Search Keyboard Shortcut
if (browser) {

View File

@@ -1,7 +1,9 @@
<script lang="ts">
import { drawerStore, Drawer } from '@skeletonlabs/skeleton';
import { getDrawerStore, Drawer } from '@skeletonlabs/skeleton';
import DocsSidebar from '../DocsSidebar/DocsSidebar.svelte';
const drawerStore = getDrawerStore();
$: classesDrawer = $drawerStore.id === 'doc-sidenav' ? 'lg:hidden' : '';
</script>

View File

@@ -3,11 +3,12 @@
import DocsIcon from '$lib/components/DocsIcon/DocsIcon.svelte';
import { AppRail, AppRailTile, AppRailAnchor } from '@skeletonlabs/skeleton';
import { drawerStore } from '@skeletonlabs/skeleton';
import { getDrawerStore } from '@skeletonlabs/skeleton';
import { menuNavLinks } from '$lib/links';
// Local
let currentRailCategory: keyof typeof menuNavLinks | undefined = undefined;
const drawerStore = getDrawerStore();
function onClickAnchor(): void {
currentRailCategory = undefined;

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { menuNavLinks, type List } from '$lib/links';
import { modalStore } from '@skeletonlabs/skeleton';
import { getModalStore } from '@skeletonlabs/skeleton';
// Classes
const cBase =
@@ -15,6 +15,7 @@
let searchTerm = '';
let resultsCopy = [...menuNavLinks['/docs'], ...menuNavLinks['/elements'], ...menuNavLinks['/svelte'], ...menuNavLinks['/utilities']];
let results = resultsCopy;
const modalStore = getModalStore();
// Elements
let elemDocSearch: HTMLElement;

View File

@@ -1,5 +1,7 @@
<script lang="ts">
import { modalStore } from '@skeletonlabs/skeleton';
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
// Props
/** Exposes parent props to this component. */

View File

@@ -4,7 +4,8 @@
export let parent: any;
// Stores
import { modalStore } from '@skeletonlabs/skeleton';
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
// Form Data
const formData = {

View File

@@ -1,8 +1,10 @@
<script lang="ts">
import { modalStore } from '@skeletonlabs/skeleton';
import { getModalStore } from '@skeletonlabs/skeleton';
export let parent: any;
const modalStore = getModalStore();
const cButton = 'fixed top-4 right-4 z-50 font-bold shadow-xl';
const cImage = 'max-w-[90%] max-h-[90%] rounded-container-token overflow-hidden shadow-xl';
</script>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { ListBox, ListBoxItem, modalStore } from '@skeletonlabs/skeleton';
import { ListBox, ListBoxItem, getModalStore } from '@skeletonlabs/skeleton';
// Props
/** Exposes parent props to this component. */
@@ -7,6 +7,7 @@
// Local
let flavor = 'chocolate';
const modalStore = getModalStore();
// Handle Form Submission
function onFormSubmit(): void {

View File

@@ -3,7 +3,7 @@
import { DocsFeature, type DocsShellSettings } from '$lib/layouts/DocsShell/types';
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
// Components
import { CodeBlock, InputChip, toastStore } from '@skeletonlabs/skeleton';
import { CodeBlock, InputChip, getToastStore } from '@skeletonlabs/skeleton';
// Sveld
import sveldInputChip from '@skeletonlabs/skeleton/components/InputChip/InputChip.svelte?raw&sveld';
@@ -30,6 +30,7 @@
let emails = ['john@email.com', 'jane@email.com', 'sally@email.com'];
let musicalGenres = ['rock', 'r&b', 'pop'];
let musicalGenresWhitelist = ['rock', 'pop', 'hip-hop', 'metal', 'techno', 'r&b'];
const toastStore = getToastStore();
function isValidEmail(value: string): boolean {
return value.includes('@') && value.includes('.');

View File

@@ -4,7 +4,7 @@
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
import { variants } from '$lib/components/DocsPreview/options';
// Components
import { CodeBlock, toastStore, type ToastSettings } from '@skeletonlabs/skeleton';
import { CodeBlock, getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
// Docs Shell
const settings: DocsShellSettings = {
@@ -35,6 +35,7 @@
chocolate: false,
strawberry: false
};
const toastStore = getToastStore();
function triggerToast(term: string): void {
const t: ToastSettings = { message: `You selected the <u>${term}</u> action.` };

View File

@@ -8,14 +8,15 @@
import sveldDrawer from '@skeletonlabs/skeleton/utilities/Drawer/Drawer.svelte?raw&sveld';
// Drawer Utils
import { drawerStore, type DrawerSettings } from '@skeletonlabs/skeleton';
import { getDrawerStore, type DrawerSettings } from '@skeletonlabs/skeleton';
const drawerStore = getDrawerStore();
// Docs Shell
const settings: DocsShellSettings = {
feature: DocsFeature.Utility,
name: 'Drawers',
description: 'Displays an overlay panel that attaches to any side of the screen.',
imports: ['Drawer', 'drawerStore'],
imports: ['Drawer', 'getDrawerStore'],
types: ['DrawerSettings'],
source: 'utilities/Drawer',
aria: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/',
@@ -78,8 +79,14 @@
</div>
</svelte:fragment>
<svelte:fragment slot="source">
<p>Implement a single instance of the drawer component in your app's root layout above the App Shell (if present).</p>
<!-- prettier-ignore -->
<p>
Implement the following in the root layout of your application. This is required only once when implementing Skeleton's Drawer, Modal, or Toast features, and will prevent known issues with <a class="anchor" href="https://github.com/skeletonlabs/skeleton/wiki/SvelteKit-SSR-Warning" target="_blank">SvelteKit SSR</a>.
</p>
<CodeBlock language="ts" code={`import { initializeStores } from '@skeletonlabs/skeleton';\n\ninitializeStores();`} />
<p>Implement a single instance of the drawer component in your app's root layout, above the App Shell (if present).</p>
<CodeBlock language="html" code={`<Drawer />\n\n<!-- <AppShell>...</AppShell> -->`} />
<p>We'll cover triggering this feature on-demand in the documentation below.</p>
</svelte:fragment>
</DocsPreview>
</svelte:fragment>
@@ -96,6 +103,7 @@
<section class="space-y-4">
<h2 class="h2">Drawer Store</h2>
<p>Import this anywhere you wish to control the Drawer. Provides an interface to control the drawer component.</p>
<CodeBlock language="ts" code={`import { getDrawerStore } from "@skeletonlabs/skeleton";\n\nconst drawerStore = getDrawerStore();`} />
<h3 class="h3">Open</h3>
<CodeBlock language="ts" code={`drawerStore.open();`} />
<h3 class="h3">Close</h3>
@@ -213,16 +221,5 @@ drawerStore.open(settings);
<!-- prettier-ignore -->
<p>Skeleton <u>does not</u> provide a means to disable the backdrop's click to close feature, as this would be harmful to accessibility. View the <a class="anchor" href="https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/" target="_blank" rel="noreferrer">ARIA APG guidelines</a> to learn more about modal accessibility.</p>
</section>
<!-- SvelteKit SSR Warning -->
<!-- prettier-ignore -->
<section class="space-y-4">
<h2 class="h2">SvelteKit SSR Warning</h2>
<div class="space-y-4">
<div class="!flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0 md:space-x-4">
<p>There are known security risks when using Svelte writable stores within SvelteKit load functions.</p>
<a class="btn variant-filled" href="https://github.com/skeletonlabs/skeleton/wiki/SvelteKit-SSR-Warning" target="_blank" rel="noreferrer">Details &rarr;</a>
</div>
</div>
</section>
</svelte:fragment>
</DocsShell>

View File

@@ -13,7 +13,9 @@
// Modals Utils
import type { ModalSettings, ModalComponent } from '@skeletonlabs/skeleton';
import { modalStore } from '@skeletonlabs/skeleton';
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
// Stores
let tabCustom = 'register';
@@ -23,7 +25,7 @@
feature: DocsFeature.Utility,
name: 'Modals',
description: 'High priority dialogs and modals using a dynamic queue system.',
imports: ['Modal', 'modalStore'],
imports: ['Modal', 'getModalStore'],
types: ['ModalSettings', 'ModalComponent'],
source: 'utilities/Modal',
aria: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/',
@@ -147,8 +149,21 @@
<button class="btn variant-filled" on:click={modalDemo}>Show Modal</button>
</svelte:fragment>
<svelte:fragment slot="source">
<p>Implement a single instance of the modal component in your app's root layout above the App Shell (if present).</p>
<!-- prettier-ignore -->
<p>
Implement the following in the root layout of your application. This is required only once when implementing Skeleton's Drawer, Modal, or Toast features, and will prevent known issues with <a class="anchor" href="https://github.com/skeletonlabs/skeleton/wiki/SvelteKit-SSR-Warning" target="_blank">SvelteKit SSR</a>.
</p>
<CodeBlock
language="typescript"
code={`
import { Modal, initializeStores } from "@skeletonlabs/skeleton";
initializeStores();
`}
/>
<p>Implement a single instance of the modal component in your app's root layout, above the App Shell (if present).</p>
<CodeBlock language="html" code={`<Modal />\n\n<!-- <AppShell>...</AppShell> -->`} />
<p>We'll cover triggering this feature on-demand in the documentation below.</p>
</svelte:fragment>
</DocsPreview>
</svelte:fragment>
@@ -166,8 +181,18 @@
</aside>
<section class="space-y-4">
<h2 class="h2">Modal Store</h2>
<p>When you wish to trigger a modal, import the <code class="code">modalStore</code>, which acts as the modal queue.</p>
<CodeBlock language="ts" code={`import { modalStore } from '@skeletonlabs/skeleton';`} />
<p>
When you wish to trigger a modal, import the <code class="code">getModalStore</code> function and invoke it to retrieve the
<code class="code">modalStore</code>, which is a Svelte store that acts as the modal queue.
</p>
<CodeBlock
language="ts"
code={`
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
`}
/>
<h3 class="h3">Trigger</h3>
<p>
The <code class="code">title</code>, <code class="code">body</code>, and <code class="code">image</code> are available to all modals.
@@ -469,16 +494,5 @@ modalStore.trigger(modal);
<!-- prettier-ignore -->
<p>Skeleton <u>does not</u> provide a means to disable the backdrop's click to close feature, as this would be harmful to accessibility. View the <a class="anchor" href="https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/" target="_blank" rel="noreferrer">ARIA APG guidelines</a> to learn more about modal accessibility.</p>
</section>
<!-- SvelteKit SSR Warning -->
<!-- prettier-ignore -->
<section class="space-y-4">
<h2 class="h2">SvelteKit SSR Warning</h2>
<div class="space-y-4">
<div class="!flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0 md:space-x-4">
<p>There are known security risks when using Svelte writable stores within SvelteKit load functions.</p>
<a class="btn variant-filled" href="https://github.com/skeletonlabs/skeleton/wiki/SvelteKit-SSR-Warning" target="_blank" rel="noreferrer">Details &rarr;</a>
</div>
</div>
</section>
</svelte:fragment>
</DocsShell>

View File

@@ -3,8 +3,11 @@
import { DocsFeature, type DocsShellSettings } from '$lib/layouts/DocsShell/types';
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
import { CodeBlock } from '@skeletonlabs/skeleton';
// Toasts
import { toastStore, type ToastSettings } from '@skeletonlabs/skeleton';
import { getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
const toastStore = getToastStore();
// Sveld
import sveldToast from '@skeletonlabs/skeleton/utilities/Toast/Toast.svelte?raw&sveld';
@@ -13,7 +16,7 @@
feature: DocsFeature.Utility,
name: 'Toasts',
description: 'Simple notifications utilizing a dynamic queue system.',
imports: ['Toast', 'toastStore'],
imports: ['Toast', 'getToastStore'],
types: ['ToastSettings'],
source: 'utilities/Toast',
components: [{ sveld: sveldToast }],
@@ -138,11 +141,14 @@
</div>
</svelte:fragment>
<svelte:fragment slot="source">
<!-- prettier-ignore -->
<p>
Import and add a single instance of the Toast component in your app's root layout. Since this is in global scope it will be
possible to reuse this feature throughout your entire application.
Implement the following in the root layout of your application. This is required only once when implementing Skeleton's Drawer, Modal, or Toast features, and will prevent known issues with <a class="anchor" href="https://github.com/skeletonlabs/skeleton/wiki/SvelteKit-SSR-Warning" target="_blank">SvelteKit SSR</a>.
</p>
<CodeBlock language="html" code={`<Toast />`} />
<CodeBlock language="ts" code={`import { initializeStores } from '@skeletonlabs/skeleton';\n\ninitializeStores();`} />
<p>Implement a single instance of the toast component in your app's root layout, above the App Shell (if present).</p>
<CodeBlock language="html" code={`<Toast />\n\n<!-- <AppShell>...</AppShell> -->`} />
<p>We'll cover triggering this feature on-demand in the documentation below.</p>
</svelte:fragment>
</DocsPreview>
</svelte:fragment>
@@ -162,7 +168,7 @@
<p>The Toast Store acts as a queue for your toast messages.</p>
<CodeBlock
language="ts"
code={`import { toastStore } from '@skeletonlabs/skeleton';
code={`import { getToastStore } from '@skeletonlabs/skeleton';\n\nconst toastStore = getToastStore();
`}
/>
<!-- Trigger -->
@@ -415,16 +421,5 @@ const t: ToastSettings = {
`}
/>
</section>
<!-- SvelteKit SSR Warning -->
<!-- prettier-ignore -->
<section class="space-y-4">
<h2 class="h2">SvelteKit SSR Warning</h2>
<div class="space-y-4">
<div class="!flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0 md:space-x-4">
<p>There are known security risks when using Svelte writable stores within SvelteKit load functions.</p>
<a class="btn variant-filled" href="https://github.com/skeletonlabs/skeleton/wiki/SvelteKit-SSR-Warning" target="_blank" rel="noreferrer">Details &rarr;</a>
</div>
</div>
</section>
</svelte:fragment>
</DocsShell>

View File

@@ -23,7 +23,8 @@
import { storePreview } from '$lib/layouts/DocsThemer/stores';
// Components & Utilities
import { AppShell, Modal, Toast } from '@skeletonlabs/skeleton';
import { AppShell, Modal, Toast, initializeStores } from '@skeletonlabs/skeleton';
initializeStores();
// Docs Components
import DocsAppBar from '$lib/components/DocsAppBar/DocsAppBar.svelte';
@@ -144,7 +145,6 @@
meta.twitter.image = post.twitter_image || post.feature_image;
}
});
// Reactive
// Disable left sidebar on homepage
$: slotSidebarLeft = matchPathWhitelist($page.url.pathname) ? 'w-0' : 'bg-surface-50-900-token lg:w-auto';

View File

@@ -3,7 +3,8 @@
import type { PageData } from './$types';
export let data: PageData;
import { toastStore, type ToastSettings } from '@skeletonlabs/skeleton';
import { getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
const toastStore = getToastStore();
// Blog Utils
import { getBlogList, blogDateFormatter } from './blog-service';

View File

@@ -2,7 +2,7 @@
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
import { getImageLink } from '$lib/images';
// Utilities
import { modalStore, type ModalSettings } from '@skeletonlabs/skeleton';
import { getModalStore, type ModalSettings } from '@skeletonlabs/skeleton';
// Components
import { Accordion, AccordionItem, ListBox, ListBoxItem } from '@skeletonlabs/skeleton';
// Actions
@@ -13,6 +13,7 @@
// Local
let showcase = 'components';
const imgPlaceholder = `${getImageLink({ id: 'YOErFW8AfkI', w: 200, h: 200 })}`;
const modalStore = getModalStore();
function modalDemo(): void {
const modal: ModalSettings = {