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 { Transition, TransitionParams } from './internal/transitions.js';
export type { CssClasses, SvelteEvent } from './types.js'; export type { CssClasses, SvelteEvent } from './types.js';
// Stores --- // Utilities ---
export { storeHighlightJs } from './utilities/CodeBlock/stores.js'; export { storeHighlightJs } from './utilities/CodeBlock/stores.js';
export { storePopup } from './utilities/Popup/popup.js'; export { storePopup } from './utilities/Popup/popup.js';
export { drawerStore } from './utilities/Drawer/stores.js'; export { getDrawerStore } from './utilities/Drawer/stores.js';
export { modalStore } from './utilities/Modal/stores.js'; export { getModalStore } from './utilities/Modal/stores.js';
export { toastStore } from './utilities/Toast/stores.js'; export { getToastStore } from './utilities/Toast/stores.js';
export { initializeStores } from './utilities/index.js';
// Utilities ---
// Lightswitch // Lightswitch
export { export {

View File

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

View File

@@ -1,8 +1,44 @@
// Drawer Stores // Drawer Stores
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
import type { DrawerSettings } from './types.js'; 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() { function drawerService() {
const { subscribe, set, update } = writable<DrawerSettings>({}); const { subscribe, set, update } = writable<DrawerSettings>({});
return { return {
@@ -22,6 +58,3 @@ function drawerService() {
}) })
}; };
} }
// Exports
export const drawerStore = drawerService();

View File

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

View File

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

View File

@@ -1,8 +1,44 @@
// Modal Store Queue // Modal Store Queue
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
import type { ModalSettings } from './types.js'; 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() { function modalService() {
const { subscribe, set, update } = writable<ModalSettings[]>([]); const { subscribe, set, update } = writable<ModalSettings[]>([]);
return { return {
@@ -25,5 +61,3 @@ function modalService() {
clear: () => set([]) clear: () => set([])
}; };
} }
export const modalStore = modalService();

View File

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

View File

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

View File

@@ -1,29 +1,85 @@
// Toast Store Queue // Toast Store Queue
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import { getContext, setContext } from 'svelte';
import type { ToastSettings, Toast } from './types.js'; import type { ToastSettings, Toast } from './types.js';
const toastDefaults: ToastSettings = { message: 'Missing Toast Message', autohide: true, timeout: 5000 }; 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 // Note for security; differentiates the queued toasts
function randomUUID(): string { function randomUUID(): string {
const random = Math.random(); const random = Math.random();
return Number(random).toString(32); return Number(random).toString(32);
} }
// If toast should auto-hide, wait X time, then close by ID type ToastStore = ReturnType<typeof toastService>;
function handleAutoHide(toast: Toast) {
if (toast.autohide === true) {
return setTimeout(() => {
toastStore.close(toast.id);
}, toast.timeout);
}
}
function toastService() { function toastService() {
const { subscribe, set, update } = writable<Toast[]>([]); 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 { return {
subscribe, subscribe,
close,
/** Add a new toast to the queue. */ /** Add a new toast to the queue. */
trigger: (toast: ToastSettings) => { trigger: (toast: ToastSettings) => {
const id: string = randomUUID(); const id: string = randomUUID();
@@ -43,30 +99,13 @@ function toastService() {
}); });
return id; return id;
}, },
/** Remove toast in queue*/ /** Remain visible on hover */
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 */
freeze: (index: number) => freeze: (index: number) =>
update((tStore) => { update((tStore) => {
if (tStore.length > 0) clearTimeout(tStore[index].timeoutId); if (tStore.length > 0) clearTimeout(tStore[index].timeoutId);
return tStore; return tStore;
}), }),
/** cancel remain visible on leave */ /** Cancel remain visible on leave */
unfreeze: (index: number) => unfreeze: (index: number) =>
update((tStore) => { update((tStore) => {
if (tStore.length > 0) tStore[index].timeoutId = handleAutoHide(tStore[index]); if (tStore.length > 0) tStore[index].timeoutId = handleAutoHide(tStore[index]);
@@ -76,5 +115,3 @@ function toastService() {
clear: () => set([]) 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'; import DocsIcon from '$lib/components/DocsIcon/DocsIcon.svelte';
// Components & Utilities // Components & Utilities
import { AppBar, LightSwitch, popup, modalStore } from '@skeletonlabs/skeleton'; import { AppBar, LightSwitch, popup, getModalStore } from '@skeletonlabs/skeleton';
// Stores // Stores
import { drawerStore } from '@skeletonlabs/skeleton'; import { getDrawerStore } from '@skeletonlabs/skeleton';
import { storeTheme } from '$lib/stores/stores'; import { storeTheme } from '$lib/stores/stores';
const drawerStore = getDrawerStore();
// Local // Local
let isOsMac = false; let isOsMac = false;
const modalStore = getModalStore();
// Set Search Keyboard Shortcut // Set Search Keyboard Shortcut
if (browser) { if (browser) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte'; import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
import { variants } from '$lib/components/DocsPreview/options'; import { variants } from '$lib/components/DocsPreview/options';
// Components // Components
import { CodeBlock, toastStore, type ToastSettings } from '@skeletonlabs/skeleton'; import { CodeBlock, getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
// Docs Shell // Docs Shell
const settings: DocsShellSettings = { const settings: DocsShellSettings = {
@@ -35,6 +35,7 @@
chocolate: false, chocolate: false,
strawberry: false strawberry: false
}; };
const toastStore = getToastStore();
function triggerToast(term: string): void { function triggerToast(term: string): void {
const t: ToastSettings = { message: `You selected the <u>${term}</u> action.` }; 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'; import sveldDrawer from '@skeletonlabs/skeleton/utilities/Drawer/Drawer.svelte?raw&sveld';
// Drawer Utils // Drawer Utils
import { drawerStore, type DrawerSettings } from '@skeletonlabs/skeleton'; import { getDrawerStore, type DrawerSettings } from '@skeletonlabs/skeleton';
const drawerStore = getDrawerStore();
// Docs Shell // Docs Shell
const settings: DocsShellSettings = { const settings: DocsShellSettings = {
feature: DocsFeature.Utility, feature: DocsFeature.Utility,
name: 'Drawers', name: 'Drawers',
description: 'Displays an overlay panel that attaches to any side of the screen.', description: 'Displays an overlay panel that attaches to any side of the screen.',
imports: ['Drawer', 'drawerStore'], imports: ['Drawer', 'getDrawerStore'],
types: ['DrawerSettings'], types: ['DrawerSettings'],
source: 'utilities/Drawer', source: 'utilities/Drawer',
aria: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/', aria: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialogmodal/',
@@ -78,8 +79,14 @@
</div> </div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="source"> <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> -->`} /> <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> </svelte:fragment>
</DocsPreview> </DocsPreview>
</svelte:fragment> </svelte:fragment>
@@ -96,6 +103,7 @@
<section class="space-y-4"> <section class="space-y-4">
<h2 class="h2">Drawer Store</h2> <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> <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> <h3 class="h3">Open</h3>
<CodeBlock language="ts" code={`drawerStore.open();`} /> <CodeBlock language="ts" code={`drawerStore.open();`} />
<h3 class="h3">Close</h3> <h3 class="h3">Close</h3>
@@ -213,16 +221,5 @@ drawerStore.open(settings);
<!-- prettier-ignore --> <!-- 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> <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> </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> </svelte:fragment>
</DocsShell> </DocsShell>

View File

@@ -13,7 +13,9 @@
// Modals Utils // Modals Utils
import type { ModalSettings, ModalComponent } from '@skeletonlabs/skeleton'; import type { ModalSettings, ModalComponent } from '@skeletonlabs/skeleton';
import { modalStore } from '@skeletonlabs/skeleton'; import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
// Stores // Stores
let tabCustom = 'register'; let tabCustom = 'register';
@@ -23,7 +25,7 @@
feature: DocsFeature.Utility, feature: DocsFeature.Utility,
name: 'Modals', name: 'Modals',
description: 'High priority dialogs and modals using a dynamic queue system.', description: 'High priority dialogs and modals using a dynamic queue system.',
imports: ['Modal', 'modalStore'], imports: ['Modal', 'getModalStore'],
types: ['ModalSettings', 'ModalComponent'], types: ['ModalSettings', 'ModalComponent'],
source: 'utilities/Modal', source: 'utilities/Modal',
aria: 'https://www.w3.org/WAI/ARIA/apg/patterns/dialog-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> <button class="btn variant-filled" on:click={modalDemo}>Show Modal</button>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="source"> <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> -->`} /> <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> </svelte:fragment>
</DocsPreview> </DocsPreview>
</svelte:fragment> </svelte:fragment>
@@ -166,8 +181,18 @@
</aside> </aside>
<section class="space-y-4"> <section class="space-y-4">
<h2 class="h2">Modal Store</h2> <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> <p>
<CodeBlock language="ts" code={`import { modalStore } from '@skeletonlabs/skeleton';`} /> 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> <h3 class="h3">Trigger</h3>
<p> <p>
The <code class="code">title</code>, <code class="code">body</code>, and <code class="code">image</code> are available to all modals. 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 --> <!-- 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> <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> </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> </svelte:fragment>
</DocsShell> </DocsShell>

View File

@@ -3,8 +3,11 @@
import { DocsFeature, type DocsShellSettings } from '$lib/layouts/DocsShell/types'; import { DocsFeature, type DocsShellSettings } from '$lib/layouts/DocsShell/types';
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte'; import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
import { CodeBlock } from '@skeletonlabs/skeleton'; import { CodeBlock } from '@skeletonlabs/skeleton';
// Toasts // Toasts
import { toastStore, type ToastSettings } from '@skeletonlabs/skeleton'; import { getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
const toastStore = getToastStore();
// Sveld // Sveld
import sveldToast from '@skeletonlabs/skeleton/utilities/Toast/Toast.svelte?raw&sveld'; import sveldToast from '@skeletonlabs/skeleton/utilities/Toast/Toast.svelte?raw&sveld';
@@ -13,7 +16,7 @@
feature: DocsFeature.Utility, feature: DocsFeature.Utility,
name: 'Toasts', name: 'Toasts',
description: 'Simple notifications utilizing a dynamic queue system.', description: 'Simple notifications utilizing a dynamic queue system.',
imports: ['Toast', 'toastStore'], imports: ['Toast', 'getToastStore'],
types: ['ToastSettings'], types: ['ToastSettings'],
source: 'utilities/Toast', source: 'utilities/Toast',
components: [{ sveld: sveldToast }], components: [{ sveld: sveldToast }],
@@ -138,11 +141,14 @@
</div> </div>
</svelte:fragment> </svelte:fragment>
<svelte:fragment slot="source"> <svelte:fragment slot="source">
<!-- prettier-ignore -->
<p> <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 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>.
possible to reuse this feature throughout your entire application.
</p> </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> </svelte:fragment>
</DocsPreview> </DocsPreview>
</svelte:fragment> </svelte:fragment>
@@ -162,7 +168,7 @@
<p>The Toast Store acts as a queue for your toast messages.</p> <p>The Toast Store acts as a queue for your toast messages.</p>
<CodeBlock <CodeBlock
language="ts" language="ts"
code={`import { toastStore } from '@skeletonlabs/skeleton'; code={`import { getToastStore } from '@skeletonlabs/skeleton';\n\nconst toastStore = getToastStore();
`} `}
/> />
<!-- Trigger --> <!-- Trigger -->
@@ -415,16 +421,5 @@ const t: ToastSettings = {
`} `}
/> />
</section> </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> </svelte:fragment>
</DocsShell> </DocsShell>

View File

@@ -23,7 +23,8 @@
import { storePreview } from '$lib/layouts/DocsThemer/stores'; import { storePreview } from '$lib/layouts/DocsThemer/stores';
// Components & Utilities // Components & Utilities
import { AppShell, Modal, Toast } from '@skeletonlabs/skeleton'; import { AppShell, Modal, Toast, initializeStores } from '@skeletonlabs/skeleton';
initializeStores();
// Docs Components // Docs Components
import DocsAppBar from '$lib/components/DocsAppBar/DocsAppBar.svelte'; import DocsAppBar from '$lib/components/DocsAppBar/DocsAppBar.svelte';
@@ -144,7 +145,6 @@
meta.twitter.image = post.twitter_image || post.feature_image; meta.twitter.image = post.twitter_image || post.feature_image;
} }
}); });
// Reactive // Reactive
// Disable left sidebar on homepage // Disable left sidebar on homepage
$: slotSidebarLeft = matchPathWhitelist($page.url.pathname) ? 'w-0' : 'bg-surface-50-900-token lg:w-auto'; $: 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'; import type { PageData } from './$types';
export let data: PageData; export let data: PageData;
import { toastStore, type ToastSettings } from '@skeletonlabs/skeleton'; import { getToastStore, type ToastSettings } from '@skeletonlabs/skeleton';
const toastStore = getToastStore();
// Blog Utils // Blog Utils
import { getBlogList, blogDateFormatter } from './blog-service'; import { getBlogList, blogDateFormatter } from './blog-service';

View File

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