mirror of
https://github.com/LukeHagar/skeleton.git
synced 2025-12-06 12:47:44 +00:00
Merge fore release 10/10 (#2130)
Co-authored-by: Mahmoud Zino <74062808+Mahmoud-zino@users.noreply.github.com> Co-authored-by: CokaKoala <31664583+AdrianGonz97@users.noreply.github.com> Co-authored-by: Edrich Hans Chua <edrich@xendit.co> Co-authored-by: A̴m̴r̴ ع̲مِےـرۅ <akkk33@protonmail.com> Co-authored-by: Jordan Watts <91865823+jwatts777@users.noreply.github.com> Co-authored-by: Hugo Korte <63101006+Hugos68@users.noreply.github.com> Co-authored-by: Aaron Crockett <aaron.crockett.00@gmail.com>
This commit is contained in:
5
.changeset/angry-fishes-study.md
Normal file
5
.changeset/angry-fishes-study.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/tw-plugin": patch
|
||||
---
|
||||
|
||||
bugfix: Separated light/dark classes for input validation variants
|
||||
5
.changeset/empty-trees-fetch.md
Normal file
5
.changeset/empty-trees-fetch.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": patch
|
||||
---
|
||||
|
||||
bugfix: Resolved an issue that allowed focusTrap to escape hidden inputs
|
||||
6
.changeset/fast-camels-sort.md
Normal file
6
.changeset/fast-camels-sort.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": minor
|
||||
---
|
||||
|
||||
- feat: File Dropzone and File Button now include a `fileInput` prop to reference to the input element.
|
||||
- feat: File Dropzone now forwards the `on:focus`, `on:focusin`, and `on:focusout` events.
|
||||
5
.changeset/funny-cows-cry.md
Normal file
5
.changeset/funny-cows-cry.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/tw-plugin": patch
|
||||
---
|
||||
|
||||
bugfix: Resolved an issue where the Ring design token would generate invalid CSS when using the important modifier `!`
|
||||
5
.changeset/late-cherries-admire.md
Normal file
5
.changeset/late-cherries-admire.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": minor
|
||||
---
|
||||
|
||||
feat: Added `input-chip-interface` and `input-chip-wrapper` region classes to Input Chips.
|
||||
5
.changeset/little-dingos-repeat.md
Normal file
5
.changeset/little-dingos-repeat.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": patch
|
||||
---
|
||||
|
||||
feat: Added a `strokeLinecap` property to to Progress Radials, enabling rounded styling
|
||||
5
.changeset/modern-bags-join.md
Normal file
5
.changeset/modern-bags-join.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": patch
|
||||
---
|
||||
|
||||
chore: Accordion now has RTL compatibility via the use of the `text-start` class
|
||||
15
.changeset/nine-worms-cheat.md
Normal file
15
.changeset/nine-worms-cheat.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": minor
|
||||
---
|
||||
|
||||
feat: Multiple revisions to the Tree View component:
|
||||
|
||||
- Enhanced and properly named non-recursive Tree View events.
|
||||
- Separated the recursive Tree View under the new component RecursiveTreeView.
|
||||
- RecursiveTreeView now utilizes the `relational` prop to enable relational checking.
|
||||
- RecursiveTreeView is now using ID arrays with 2-way binding to control the Tree View state, including:
|
||||
- `expandedNodes`
|
||||
- `disabledNodes`
|
||||
- `checkedNodes`
|
||||
- `indeterminateNodes` (affects only multiple relational mode)
|
||||
- TreeViewNode now requires a unique ID to support the new checking system.
|
||||
5
.changeset/quick-rats-doubt.md
Normal file
5
.changeset/quick-rats-doubt.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@skeletonlabs/skeleton": patch
|
||||
---
|
||||
|
||||
bugfix: Added `title` prop to Lightswitch component for better i18n support
|
||||
@@ -222,9 +222,21 @@ import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit(), purgeCss()]
|
||||
});
|
||||
`;
|
||||
plugins: [sveltekit(), purgeCss(`;
|
||||
|
||||
if (opts.codeblocks) {
|
||||
contents += `{
|
||||
safelist: {
|
||||
// any selectors that begin with "hljs-" will not be purged
|
||||
greedy: [/^hljs-/],
|
||||
},
|
||||
}),
|
||||
],
|
||||
});`;
|
||||
} else {
|
||||
contents += `)]
|
||||
});`;
|
||||
}
|
||||
writeFileSync(filename, contents);
|
||||
}
|
||||
|
||||
@@ -475,9 +487,18 @@ function copyTemplate(opts) {
|
||||
scriptEndReg,
|
||||
`
|
||||
// Highlight JS
|
||||
import hljs from 'highlight.js';
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import 'highlight.js/styles/github-dark.css';
|
||||
import { storeHighlightJs } from '@skeletonlabs/skeleton';
|
||||
import xml from 'highlight.js/lib/languages/xml'; // for HTML
|
||||
import css from 'highlight.js/lib/languages/css';
|
||||
import javascript from 'highlight.js/lib/languages/javascript';
|
||||
import typescript from 'highlight.js/lib/languages/typescript';
|
||||
|
||||
hljs.registerLanguage('xml', xml); // for HTML
|
||||
hljs.registerLanguage('css', css);
|
||||
hljs.registerLanguage('javascript', javascript);
|
||||
hljs.registerLanguage('typescript', typescript);
|
||||
storeHighlightJs.set(hljs);
|
||||
</script>`,
|
||||
);
|
||||
|
||||
@@ -256,7 +256,7 @@ Problems? Open an issue on ${cyan('https://github.com/skeletonlabs/skeleton/issu
|
||||
let packages = [
|
||||
{ value: 'forms', label: 'Add Tailwind forms?', package: '@tailwindcss/forms', force: false },
|
||||
{ value: 'typography', label: 'Add Tailwind typography?', package: '@tailwindcss/typography', force: false },
|
||||
// { value: 'codeblocks', label: 'Add CodeBlock (installs highlight.js)?', package: 'highlight.js', force: false },
|
||||
{ value: 'codeblocks', label: 'Add CodeBlock (installs highlight.js)?', package: 'highlight.js', force: false },
|
||||
{ value: 'popups', label: 'Add Popups (installs floating-ui)?', package: '@floating-ui/dom', force: false },
|
||||
// { value: 'mdsvex', label: 'Add Markdown support (installs mdsvex)?', package: 'mdsvex', force: false },
|
||||
];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- YOU CAN DELETE EVERYTHING IN THIS PAGE -->
|
||||
|
||||
<div class="container h-full mx-auto flex justify-center items-center">
|
||||
<div class="container h-screen mx-auto flex justify-center items-center">
|
||||
<div class="space-y-5">
|
||||
<h1 class="h1">Let's get cracking bones!</h1>
|
||||
<p>Start by exploring:</p>
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
|
||||
/* success */
|
||||
.input-success {
|
||||
@apply !bg-success-200 !border-success-500 !text-success-700;
|
||||
@apply bg-success-200 dark:bg-success-200 border-success-500 dark:border-success-500 text-success-700 dark:text-success-700;
|
||||
}
|
||||
.input-success::-moz-placeholder {
|
||||
@apply text-success-700;
|
||||
@@ -176,7 +176,7 @@
|
||||
|
||||
/* warning */
|
||||
.input-warning {
|
||||
@apply !bg-warning-200 !border-warning-500 !text-warning-700;
|
||||
@apply bg-warning-200 dark:bg-warning-200 border-warning-500 dark:border-warning-500 text-warning-700 dark:text-warning-700;
|
||||
}
|
||||
.input-warning::-moz-placeholder {
|
||||
@apply text-warning-700;
|
||||
@@ -190,7 +190,7 @@
|
||||
|
||||
/* error */
|
||||
.input-error {
|
||||
@apply !bg-error-200 !border-error-500 !text-error-500;
|
||||
@apply bg-error-200 dark:bg-error-200 border-error-500 dark:border-error-500 text-error-500 dark:text-error-500;
|
||||
}
|
||||
.input-error::-moz-placeholder {
|
||||
@apply text-error-500;
|
||||
|
||||
@@ -27,7 +27,7 @@ export const rings = (): CssClasses => {
|
||||
// Example: .ring-outline-token
|
||||
'.ring-outline-token': {
|
||||
...ringOutlineShared,
|
||||
'--tw-ring-color': 'rgb(23 23 23 / 0.05);' // neutral-900, 5% opacity
|
||||
'--tw-ring-color': 'rgb(23 23 23 / 0.05)' // neutral-900, 5% opacity
|
||||
},
|
||||
'.dark .ring-outline-token': {
|
||||
...ringOutlineShared,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Action: Focus Trap
|
||||
export function focusTrap(node: HTMLElement, enabled: boolean) {
|
||||
const elemWhitelist = 'a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])';
|
||||
const elemWhitelist =
|
||||
'a[href]:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), textarea:not([tabindex="-1"]), select:not([tabindex="-1"]), details:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])';
|
||||
let elemFirst: HTMLElement;
|
||||
let elemLast: HTMLElement;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
// Classes
|
||||
const cBase = '';
|
||||
const cControl = 'text-left w-full flex items-center space-x-4';
|
||||
const cControl = 'text-start w-full flex items-center space-x-4';
|
||||
const cControlCaret = 'fill-current w-3 transition-transform duration-[200ms]';
|
||||
const cPanel = '';
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
* @type {FileList}
|
||||
*/
|
||||
export let files: FileList | undefined = undefined;
|
||||
/**
|
||||
* File input reference.
|
||||
*/
|
||||
export let fileInput: HTMLInputElement | undefined = undefined;
|
||||
/**
|
||||
* Required. Set a unique name for the file input.
|
||||
* @type {string}
|
||||
@@ -18,11 +22,8 @@
|
||||
/** Provide a button variant or other class styles. */
|
||||
export let button: CssClasses = 'btn variant-filled';
|
||||
|
||||
// Local
|
||||
let elemFileInput: HTMLElement;
|
||||
|
||||
function onButtonClick(): void {
|
||||
elemFileInput.click();
|
||||
if (fileInput) fileInput.click();
|
||||
}
|
||||
|
||||
function prunedRestProps() {
|
||||
@@ -38,7 +39,7 @@
|
||||
<div class="file-button {classesBase}" data-testid="file-button">
|
||||
<!-- NOTE: Don't use `hidden` as it prevents `required` from operating -->
|
||||
<div class="w-0 h-0 overflow-hidden">
|
||||
<input type="file" bind:this={elemFileInput} bind:files {name} {...prunedRestProps()} on:change />
|
||||
<input type="file" bind:this={fileInput} bind:files {name} {...prunedRestProps()} on:change />
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<button
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
* @type {FileList}
|
||||
*/
|
||||
export let files: FileList | undefined = undefined;
|
||||
/**
|
||||
* File input reference.
|
||||
*/
|
||||
export let fileInput: HTMLInputElement | undefined = undefined;
|
||||
/**
|
||||
* Required. Set a unique name for the file input.
|
||||
* @type {string}
|
||||
@@ -55,6 +59,7 @@
|
||||
<!-- NOTE: keep `bind:files` here, unlike FileButton -->
|
||||
<input
|
||||
bind:files
|
||||
bind:this={fileInput}
|
||||
type="file"
|
||||
{name}
|
||||
class="dropzone-input {classesInput}"
|
||||
@@ -68,6 +73,9 @@
|
||||
on:keydown
|
||||
on:keyup
|
||||
on:keypress
|
||||
on:focus
|
||||
on:focusin
|
||||
on:focusout
|
||||
/>
|
||||
<!-- Interface -->
|
||||
<div class="dropzone-interface {classesInterface} {regionInterface}">
|
||||
|
||||
@@ -78,6 +78,14 @@
|
||||
/** Provide classes to set border radius styles. */
|
||||
export let rounded: CssClasses = 'rounded-container-token';
|
||||
|
||||
// Props (regions)
|
||||
/** Provide arbitrary classes to style the chip wrapper region. */
|
||||
export let regionChipWrapper: CssClasses = '';
|
||||
/** Provide arbitrary classes to style the chip list region. */
|
||||
export let regionChipList: CssClasses = '';
|
||||
/** Provide arbitrary classes to style the input field region. */
|
||||
export let regionInput: CssClasses = '';
|
||||
|
||||
// Props (transition)
|
||||
/**
|
||||
* Enable/Disable transitions
|
||||
@@ -127,7 +135,7 @@
|
||||
|
||||
// Classes
|
||||
const cBase = 'textarea cursor-pointer';
|
||||
const cInterface = 'space-y-4';
|
||||
const cChipWrapper = 'space-y-4';
|
||||
const cChipList = 'flex flex-wrap gap-2';
|
||||
const cInputField = 'unstyled bg-transparent border-0 !ring-0 p-0 w-full';
|
||||
|
||||
@@ -220,9 +228,9 @@
|
||||
$: classesInvalid = inputValid === false ? invalid : '';
|
||||
// Reactive
|
||||
$: classesBase = `${cBase} ${padding} ${rounded} ${$$props.class ?? ''} ${classesInvalid}`;
|
||||
$: classesInterface = `${cInterface}`;
|
||||
$: classesChipList = `${cChipList}`;
|
||||
$: classesInputField = `${cInputField}`;
|
||||
$: classesChipWrapper = `${cChipWrapper} ${regionChipWrapper}`;
|
||||
$: classesChipList = `${cChipList} ${regionChipList}`;
|
||||
$: classesInput = `${cInputField} ${regionInput}`;
|
||||
$: chipValues =
|
||||
value?.map((val, i) => {
|
||||
if (chipValues[i]?.val === val) return chipValues[i];
|
||||
@@ -238,15 +246,15 @@
|
||||
{#each value as option}<option value={option}>{option}</option>{/each}
|
||||
</select>
|
||||
</div>
|
||||
<!-- Interface -->
|
||||
<div class="input-chip-interface {classesInterface}">
|
||||
<!-- Chip Wrapper -->
|
||||
<div class="input-chip-wrapper {classesChipWrapper}">
|
||||
<!-- Input Field -->
|
||||
<form on:submit={addChip}>
|
||||
<input
|
||||
type="text"
|
||||
bind:value={input}
|
||||
placeholder={$$restProps.placeholder ?? 'Enter values...'}
|
||||
class="input-chip-field {classesInputField}"
|
||||
class="input-chip-field {classesInput}"
|
||||
on:input={onInputHandler}
|
||||
on:input
|
||||
on:focus
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
export let stroke = 40; // px
|
||||
/** Sets the base font size. Scales responsively. */
|
||||
export let font = 56; // px
|
||||
/** Sets the stoke-linecap value */
|
||||
export let strokeLinecap: 'butt' | 'round' | 'square' = 'butt';
|
||||
|
||||
// Props (styles)
|
||||
/** Provide classes to set the width. */
|
||||
@@ -37,7 +39,7 @@
|
||||
|
||||
// Calculated Values
|
||||
const baseSize = 512; // px
|
||||
const radius: number = baseSize / 2;
|
||||
const radius: number = baseSize / 2 - stroke / 2;
|
||||
let circumference: number = radius;
|
||||
let dashoffset: number;
|
||||
|
||||
@@ -73,18 +75,19 @@
|
||||
<!-- Draw SVG -->
|
||||
<svg viewBox="0 0 {baseSize} {baseSize}" class="rounded-full" class:animate-spin={value === undefined}>
|
||||
<!-- Track -->
|
||||
<circle class="progress-radial-track {cBaseTrack} {track}" stroke-width={stroke} r={baseSize / 2} cx="50%" cy="50%" />
|
||||
<circle class="progress-radial-track {cBaseTrack} {track}" stroke-width={stroke} r={radius} cx="50%" cy="50%" />
|
||||
|
||||
<!-- Meter -->
|
||||
<circle
|
||||
class="progress-radial-meter {cBaseMeter} {meter}"
|
||||
stroke-width={stroke}
|
||||
r={baseSize / 2}
|
||||
r={radius}
|
||||
cx="50%"
|
||||
cy="50%"
|
||||
style:stroke-dasharray="{circumference}
|
||||
{circumference}"
|
||||
style:stroke-dashoffset={dashoffset}
|
||||
stroke-linecap={strokeLinecap}
|
||||
/>
|
||||
|
||||
<!-- Center Text -->
|
||||
|
||||
@@ -18,7 +18,8 @@ describe('ProgressRadial.svelte', () => {
|
||||
meter: 'stroke-black dark:stroke-white',
|
||||
color: 'fill-black dark:fill-white',
|
||||
font: 56,
|
||||
label: 'testProgressRadial1'
|
||||
label: 'testProgressRadial1',
|
||||
roundedLineCap: true
|
||||
}
|
||||
});
|
||||
expect(getByTestId('progress-radial')).toBeTruthy();
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<script lang="ts">
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
// Types
|
||||
import type { CssClasses, TreeViewNode } from '../../index.js';
|
||||
import RecursiveTreeViewItem from './RecursiveTreeViewItem.svelte';
|
||||
|
||||
// Props (parent)
|
||||
/** Enable tree-view selection. */
|
||||
export let selection = false;
|
||||
/** Enable selection of multiple items. */
|
||||
export let multiple = false;
|
||||
/** Enable relational checking. */
|
||||
export let relational = false;
|
||||
/**
|
||||
* Provide data-driven nodes.
|
||||
* @type {TreeViewNode[]}
|
||||
*/
|
||||
export let nodes: TreeViewNode[] = [];
|
||||
/**
|
||||
* provides id's of expanded nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let expandedNodes: string[] = [];
|
||||
/**
|
||||
* provides id's of disabled nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let disabledNodes: string[] = [];
|
||||
/**
|
||||
* provides id's of checked nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let checkedNodes: string[] = [];
|
||||
/**
|
||||
* provides id's of indeterminate nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let indeterminateNodes: string[] = [];
|
||||
/** Provide classes to set the tree width. */
|
||||
export let width: CssClasses = 'w-full';
|
||||
/** Provide classes to set the vertical spacing between items. */
|
||||
export let spacing: CssClasses = 'space-y-1';
|
||||
|
||||
// Props (children)
|
||||
/** Set open by default on load. */
|
||||
export let open = false;
|
||||
/** Set the tree disabled state */
|
||||
export let disabled = false;
|
||||
/** Provide classes to set the tree item padding styles. */
|
||||
export let padding: CssClasses = 'py-4 px-4';
|
||||
/** Provide classes to set the tree children indentation */
|
||||
export let indent: CssClasses = 'ml-4';
|
||||
/** Provide classes to set the tree item hover styles. */
|
||||
export let hover: CssClasses = 'hover:variant-soft';
|
||||
/** Provide classes to set the tree item rounded styles. */
|
||||
export let rounded: CssClasses = 'rounded-container-token';
|
||||
|
||||
// Props (symbols)
|
||||
/** Set the rotation of the item caret in the open state. */
|
||||
export let caretOpen: CssClasses = 'rotate-180';
|
||||
/** Set the rotation of the item caret in the closed state. */
|
||||
export let caretClosed: CssClasses = '';
|
||||
/* Set the hyphen symbol opacity for non-expandable rows. */
|
||||
export let hyphenOpacity: CssClasses = 'opacity-10';
|
||||
|
||||
// Props (regions)
|
||||
/** Provide arbitrary classes to the tree item summary region. */
|
||||
export let regionSummary: CssClasses = '';
|
||||
/** Provide arbitrary classes to the symbol icon region. */
|
||||
export let regionSymbol: CssClasses = '';
|
||||
/** Provide arbitrary classes to the children region. */
|
||||
export let regionChildren: CssClasses = '';
|
||||
|
||||
// Props A11y
|
||||
/** Provide the ARIA labelledby value. */
|
||||
export let labelledby = '';
|
||||
|
||||
// Context API
|
||||
setContext('open', open);
|
||||
setContext('selection', selection);
|
||||
setContext('multiple', multiple);
|
||||
setContext('relational', relational);
|
||||
setContext('disabled', disabled);
|
||||
setContext('padding', padding);
|
||||
setContext('indent', indent);
|
||||
setContext('hover', hover);
|
||||
setContext('rounded', rounded);
|
||||
setContext('caretOpen', caretOpen);
|
||||
setContext('caretClosed', caretClosed);
|
||||
setContext('hyphenOpacity', hyphenOpacity);
|
||||
setContext('regionSummary', regionSummary);
|
||||
setContext('regionSymbol', regionSymbol);
|
||||
setContext('regionChildren', regionChildren);
|
||||
|
||||
// Reactive
|
||||
$: classesBase = `${width} ${spacing} ${$$props.class ?? ''}`;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="tree {classesBase}"
|
||||
data-testid="tree"
|
||||
role="tree"
|
||||
aria-multiselectable={multiple}
|
||||
aria-label={labelledby}
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
{#if nodes && nodes.length > 0}
|
||||
<RecursiveTreeViewItem {nodes} bind:expandedNodes bind:disabledNodes bind:checkedNodes bind:indeterminateNodes />
|
||||
{/if}
|
||||
</div>
|
||||
@@ -0,0 +1,161 @@
|
||||
<script lang="ts">
|
||||
import TreeViewItem from './TreeViewItem.svelte';
|
||||
import RecursiveTreeViewItem from './RecursiveTreeViewItem.svelte';
|
||||
import type { TreeViewNode } from './types.js';
|
||||
import { getContext, onMount, tick } from 'svelte';
|
||||
|
||||
// this can't be passed using context, since we have to pass it to recursive children.
|
||||
/** Provide data-driven nodes. */
|
||||
export let nodes: TreeViewNode[] = [];
|
||||
|
||||
/**
|
||||
* provides id's of expanded nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let expandedNodes: string[] = [];
|
||||
/**
|
||||
* provides id's of disabled nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let disabledNodes: string[] = [];
|
||||
/**
|
||||
* provides id's of checked nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let checkedNodes: string[] = [];
|
||||
/**
|
||||
* provides id's of indeterminate nodes
|
||||
* @type {string[]}
|
||||
*/
|
||||
export let indeterminateNodes: string[] = [];
|
||||
|
||||
// Context API
|
||||
let selection: boolean = getContext('selection');
|
||||
let multiple: boolean = getContext('multiple');
|
||||
let relational: boolean = getContext('relational');
|
||||
|
||||
let tempCheckedNodes: string[] = [];
|
||||
|
||||
// Locals
|
||||
let group: unknown;
|
||||
let name = '';
|
||||
|
||||
function toggleNode(node: TreeViewNode, open: boolean) {
|
||||
// toggle only nodes with children
|
||||
if (!node.children?.length) return;
|
||||
if (open) {
|
||||
// node is not registered as opened
|
||||
if (!expandedNodes.includes(node.id)) {
|
||||
expandedNodes.push(node.id);
|
||||
expandedNodes = expandedNodes;
|
||||
}
|
||||
} else {
|
||||
// node is registered as open
|
||||
if (expandedNodes.includes(node.id)) {
|
||||
expandedNodes.splice(expandedNodes.indexOf(node.id), 1);
|
||||
expandedNodes = expandedNodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkNode(node: TreeViewNode, checked: boolean, indeterminate: boolean) {
|
||||
if (checked) {
|
||||
// node is not registered as checked
|
||||
if (!checkedNodes.includes(node.id)) {
|
||||
checkedNodes.push(node.id);
|
||||
checkedNodes = checkedNodes;
|
||||
}
|
||||
|
||||
// node is not indeterminate but registered as indeterminate
|
||||
if (!indeterminate && indeterminateNodes.includes(node.id)) {
|
||||
indeterminateNodes.splice(indeterminateNodes.indexOf(node.id), 1);
|
||||
indeterminateNodes = indeterminateNodes;
|
||||
}
|
||||
} else {
|
||||
// node is registered as checked
|
||||
if (checkedNodes.includes(node.id)) {
|
||||
checkedNodes.splice(checkedNodes.indexOf(node.id), 1);
|
||||
checkedNodes = checkedNodes;
|
||||
}
|
||||
|
||||
// node is indeterminate but not registered as indeterminate
|
||||
if (indeterminate && !indeterminateNodes.includes(node.id)) {
|
||||
indeterminateNodes.push(node.id);
|
||||
indeterminateNodes = indeterminateNodes;
|
||||
// node is not indeterminate but registered as indeterminate
|
||||
} else if (!indeterminate && indeterminateNodes.includes(node.id)) {
|
||||
indeterminateNodes.splice(indeterminateNodes.indexOf(node.id), 1);
|
||||
indeterminateNodes = indeterminateNodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init check flow will messup the checked nodes, so we save it to reassign it onMount.
|
||||
tempCheckedNodes = [...checkedNodes];
|
||||
onMount(async () => {
|
||||
if (selection) {
|
||||
// random number as name
|
||||
name = String(Math.random());
|
||||
|
||||
// init groups if not initialized yet
|
||||
if (group === undefined) {
|
||||
if (multiple) {
|
||||
group = [];
|
||||
nodes.forEach((node) => {
|
||||
if (checkedNodes.includes(node.id) && Array.isArray(group)) group.push(node.id);
|
||||
});
|
||||
group = group;
|
||||
} else if (!nodes.some((node) => checkedNodes.includes(node.id))) {
|
||||
group = '';
|
||||
}
|
||||
}
|
||||
|
||||
// remove relational links
|
||||
if (!relational) treeItems = [];
|
||||
|
||||
// reassign checkNodes to ensure component starting with the correct check values.
|
||||
checkedNodes = [];
|
||||
await tick();
|
||||
checkedNodes = [...tempCheckedNodes];
|
||||
}
|
||||
});
|
||||
|
||||
// important to pass children up to items (recursively)
|
||||
export let treeItems: TreeViewItem[] = [];
|
||||
let children: TreeViewItem[][] = [];
|
||||
</script>
|
||||
|
||||
{#if nodes && nodes.length > 0}
|
||||
{#each nodes as node, i}
|
||||
<TreeViewItem
|
||||
bind:this={treeItems[i]}
|
||||
bind:children={children[i]}
|
||||
bind:group
|
||||
bind:name
|
||||
bind:value={node.id}
|
||||
hideLead={!node.lead}
|
||||
hideChildren={!node.children || node.children.length === 0}
|
||||
open={expandedNodes.includes(node.id)}
|
||||
disabled={disabledNodes.includes(node.id)}
|
||||
checked={checkedNodes.includes(node.id)}
|
||||
indeterminate={indeterminateNodes.includes(node.id)}
|
||||
on:toggle={(e) => toggleNode(node, e.detail.open)}
|
||||
on:groupChange={(e) => checkNode(node, e.detail.checked, e.detail.indeterminate)}
|
||||
>
|
||||
{@html node.content}
|
||||
<svelte:fragment slot="lead">
|
||||
{@html node.lead}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="children">
|
||||
<RecursiveTreeViewItem
|
||||
nodes={node.children}
|
||||
bind:expandedNodes
|
||||
bind:disabledNodes
|
||||
bind:checkedNodes
|
||||
bind:indeterminateNodes
|
||||
bind:treeItems={children[i]}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</TreeViewItem>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -2,19 +2,13 @@
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
// Types
|
||||
import type { CssClasses, TreeViewNode } from '../../index.js';
|
||||
import TreeViewDataDrivenItem from './TreeViewDataDrivenItem.svelte';
|
||||
import type { CssClasses } from '../../index.js';
|
||||
|
||||
// Props (parent)
|
||||
/** Enable tree-view selection. */
|
||||
export let selection = false;
|
||||
/** Enable selection of multiple items. */
|
||||
export let multiple = false;
|
||||
/**
|
||||
* Provide data-driven nodes.
|
||||
* @type {TreeViewNode[]}
|
||||
*/
|
||||
export let nodes: TreeViewNode[] = [];
|
||||
/** Provide classes to set the tree width. */
|
||||
export let width: CssClasses = 'w-full';
|
||||
/** Provide classes to set the vertical spacing between items. */
|
||||
@@ -114,9 +108,5 @@
|
||||
aria-label={labelledby}
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
{#if nodes && nodes.length > 0}
|
||||
<TreeViewDataDrivenItem bind:nodes on:change on:click on:toggle on:keydown on:keyup />
|
||||
{:else}
|
||||
<slot />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* This component is only in Data-driven tree-view to add children recursively.
|
||||
*/
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import TreeViewDataDrivenItem from './TreeViewDataDrivenItem.svelte';
|
||||
import TreeViewItem from './TreeViewItem.svelte';
|
||||
import type { TreeViewNode } from './types.js';
|
||||
|
||||
// this can't be passed using context, since we have to pass it to recursive children.
|
||||
/** Provide data-driven nodes. */
|
||||
export let nodes: TreeViewNode[] = [];
|
||||
|
||||
// Context API
|
||||
/** Enable tree-view selection */
|
||||
export let selection: boolean = getContext('selection');
|
||||
/** Enable selection of multiple items. */
|
||||
export let multiple: boolean = getContext('multiple');
|
||||
|
||||
// Locals
|
||||
let group: unknown;
|
||||
let name = '';
|
||||
|
||||
// Lifecycle
|
||||
onMount(() => {
|
||||
// random number as name
|
||||
name = String(Math.random());
|
||||
|
||||
if (selection) {
|
||||
group = multiple ? [] : '';
|
||||
// manage group (checking) on initialization.
|
||||
if (multiple) {
|
||||
nodes.forEach((node) => {
|
||||
if (!Array.isArray(group)) return;
|
||||
// handle relations
|
||||
if (node.children && node.children.length > 0) {
|
||||
// at least one child is indeterminate => indeterminate item
|
||||
if (node.children.some((c) => c.indeterminate)) {
|
||||
node.indeterminate = true;
|
||||
}
|
||||
// all children are checked => check item
|
||||
else if (node.children.every((c) => c.checked)) {
|
||||
node.indeterminate = false;
|
||||
group.push(node.value);
|
||||
group = group;
|
||||
}
|
||||
// not all children are checked => indeterminate item
|
||||
else if (node.children.some((c) => c.checked)) {
|
||||
node.indeterminate = true;
|
||||
}
|
||||
// all children are unchecked => uncheck item
|
||||
else {
|
||||
node.indeterminate = false;
|
||||
node.checked = false;
|
||||
}
|
||||
} else if (node.checked) {
|
||||
group.push(node.value);
|
||||
group = group;
|
||||
}
|
||||
});
|
||||
// single selection mode
|
||||
} else {
|
||||
nodes.forEach((node) => {
|
||||
if (!node.checked) return;
|
||||
group = node.value;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Functionality
|
||||
function onGroupChange() {
|
||||
if (multiple) {
|
||||
nodes.forEach((node) => {
|
||||
if (!Array.isArray(group)) return;
|
||||
node.checked = group.includes(node.value);
|
||||
});
|
||||
} else {
|
||||
nodes.forEach((node) => {
|
||||
node.checked = node.value === group;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// important to pass children up to items (recursively)
|
||||
export let treeItems: TreeViewItem[] = [];
|
||||
let children: TreeViewItem[][] = [];
|
||||
</script>
|
||||
|
||||
{#if nodes && nodes.length > 0}
|
||||
{#each nodes as node, i}
|
||||
<TreeViewItem
|
||||
bind:this={treeItems[i]}
|
||||
bind:open={node.open}
|
||||
hideLead={!node.lead}
|
||||
hideChildren={!node.children || node.children.length === 0}
|
||||
bind:disabled={node.disabled}
|
||||
bind:group
|
||||
bind:name
|
||||
bind:indeterminate={node.indeterminate}
|
||||
bind:value={node.value}
|
||||
bind:children={children[i]}
|
||||
on:change={onGroupChange}
|
||||
on:change
|
||||
on:click
|
||||
on:toggle
|
||||
on:keydown
|
||||
on:keyup
|
||||
>
|
||||
{@html node.content}
|
||||
<svelte:fragment slot="lead">
|
||||
{@html node.lead}
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="children">
|
||||
<TreeViewDataDrivenItem bind:nodes={node.children} bind:treeItems={children[i]} />
|
||||
</svelte:fragment>
|
||||
</TreeViewItem>
|
||||
{/each}
|
||||
{/if}
|
||||
@@ -7,7 +7,7 @@
|
||||
* @slot {{}} lead - Allows for an optional leading element, such as an icon.
|
||||
* @slot {{}} children - Provide TreeView item children.
|
||||
*/
|
||||
import { getContext, createEventDispatcher } from 'svelte';
|
||||
import { getContext, createEventDispatcher, onMount } from 'svelte';
|
||||
|
||||
// Types
|
||||
import type { CssClasses, SvelteEvent, TreeViewItem } from '../../index.js';
|
||||
@@ -28,6 +28,8 @@
|
||||
* @type {unknown}
|
||||
*/
|
||||
export let value: unknown = undefined;
|
||||
/** Set the input's check state */
|
||||
export let checked = false;
|
||||
/**
|
||||
* Provide children references to support relational checking.
|
||||
* @type {TreeViewItem[]}
|
||||
@@ -78,7 +80,6 @@
|
||||
export let hideChildren = false;
|
||||
|
||||
// Locals
|
||||
let checked = false;
|
||||
let treeItem: HTMLDetailsElement;
|
||||
let childrenDiv: HTMLDivElement;
|
||||
|
||||
@@ -90,28 +91,54 @@
|
||||
// Svelte Checkbox Bugfix
|
||||
// GitHub: https://github.com/sveltejs/svelte/issues/2308
|
||||
// REPL: https://svelte.dev/repl/de117399559f4e7e9e14e2fc9ab243cc?version=3.12.1
|
||||
$: if (multiple) updateCheckbox(group);
|
||||
$: if (multiple) updateGroup(checked);
|
||||
function updateCheckbox(group: unknown) {
|
||||
$: if (multiple) updateCheckbox(group, indeterminate);
|
||||
$: if (multiple) updateGroup(checked, indeterminate);
|
||||
$: if (!multiple) updateRadio(group);
|
||||
$: if (!multiple) updateRadioGroup(checked);
|
||||
let initUpdate = true;
|
||||
function updateCheckbox(group: unknown, indeterminate: boolean) {
|
||||
if (!Array.isArray(group)) return;
|
||||
checked = group.indexOf(value) >= 0;
|
||||
/** @event {{checked: boolean, indeterminate: boolean}} groupChange - Fires when the group changes */
|
||||
dispatch('groupChange', { checked: checked, indeterminate: indeterminate });
|
||||
dispatch('childChange');
|
||||
// called only once when initializing to apply default checks
|
||||
if (initUpdate) {
|
||||
onParentChange();
|
||||
initUpdate = false;
|
||||
}
|
||||
function updateGroup(checked: boolean) {
|
||||
}
|
||||
function updateGroup(checked: boolean, indeterminate: boolean) {
|
||||
if (!Array.isArray(group)) return;
|
||||
const index = group.indexOf(value);
|
||||
if (checked) {
|
||||
if (index < 0) {
|
||||
group.push(value);
|
||||
group = group;
|
||||
// called only when the group changes
|
||||
onParentChange();
|
||||
}
|
||||
} else {
|
||||
if (index >= 0) {
|
||||
group.splice(index, 1);
|
||||
group = group;
|
||||
// called only when the group changes
|
||||
onParentChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateRadio(group: unknown) {
|
||||
checked = group === value;
|
||||
/** @event {{checked: boolean, indeterminate: boolean}} groupChange - Fires when the group changes */
|
||||
dispatch('groupChange', { checked: checked, indeterminate: false });
|
||||
if (group) dispatch('childChange');
|
||||
}
|
||||
function updateRadioGroup(checked: boolean) {
|
||||
if (checked && group !== value) group = value;
|
||||
else if (!checked && group === value) group = '';
|
||||
}
|
||||
|
||||
// called when a child's value is changed
|
||||
function onChildValueChange() {
|
||||
if (multiple) {
|
||||
@@ -123,6 +150,10 @@
|
||||
// at least one child is indeterminate => indeterminate item
|
||||
if (children.some((c) => c.indeterminate)) {
|
||||
indeterminate = true;
|
||||
if (index >= 0) {
|
||||
group.splice(index, 1);
|
||||
group = group;
|
||||
}
|
||||
}
|
||||
// all children are checked => check item
|
||||
else if (childrenValues.every((c) => Array.isArray(childrenGroup) && childrenGroup.includes(c))) {
|
||||
@@ -135,6 +166,10 @@
|
||||
// not all children are checked => indeterminate item
|
||||
else if (childrenValues.some((c) => Array.isArray(childrenGroup) && childrenGroup.includes(c))) {
|
||||
indeterminate = true;
|
||||
if (index >= 0) {
|
||||
group.splice(index, 1);
|
||||
group = group;
|
||||
}
|
||||
}
|
||||
// all children are unchecked => uncheck item
|
||||
else {
|
||||
@@ -147,13 +182,17 @@
|
||||
}
|
||||
// single selection mode
|
||||
else {
|
||||
if (group !== value) {
|
||||
// check item
|
||||
// one of the children is checked => check item
|
||||
if (group !== value && children.some((c) => c.checked)) {
|
||||
group = value;
|
||||
// none of the children are checked => uncheck item
|
||||
} else if (group === value && !children.some((c) => c.checked)) {
|
||||
group = '';
|
||||
}
|
||||
}
|
||||
// important to notify parent of item
|
||||
dispatch('change');
|
||||
/** @event childChange - Fires when the group of the child changes */
|
||||
dispatch('childChange');
|
||||
}
|
||||
|
||||
// used to update children of item when checked / unchecked in multiple mode
|
||||
@@ -168,7 +207,6 @@
|
||||
if (!child || !Array.isArray(child.group)) return;
|
||||
child.indeterminate = false;
|
||||
if (child.group.indexOf(child.value) < 0) {
|
||||
// child.group = [...child.group, child.value] won't work here.
|
||||
child.group.push(child.value);
|
||||
child.group = child.group;
|
||||
}
|
||||
@@ -184,6 +222,7 @@
|
||||
};
|
||||
|
||||
children.forEach((child) => {
|
||||
if (!child) return;
|
||||
// if parent is checked, check all children, else uncheck all children
|
||||
index >= 0 ? checkChild(child) : uncheckChild(child);
|
||||
// notify children to update values
|
||||
@@ -192,11 +231,11 @@
|
||||
}
|
||||
|
||||
// used to update children of item when checked / unchecked in single mode
|
||||
$: if (!multiple && group) {
|
||||
$: if (!multiple && group !== undefined) {
|
||||
if (group !== value) {
|
||||
// uncheck all children
|
||||
children.forEach((child) => {
|
||||
if (child) child.group = [];
|
||||
if (child) child.group = '';
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -208,7 +247,7 @@
|
||||
|
||||
// whenever children are changed, reassign on:change events.
|
||||
$: children.forEach((child) => {
|
||||
if (child) child.$on('change', () => onChildValueChange());
|
||||
if (child) child.$on('childChange', onChildValueChange);
|
||||
});
|
||||
|
||||
// A11y Key Down Handler
|
||||
@@ -337,12 +376,10 @@
|
||||
{value}
|
||||
bind:checked
|
||||
bind:indeterminate
|
||||
on:click
|
||||
on:change
|
||||
on:change={onParentChange}
|
||||
/>
|
||||
{:else}
|
||||
<input class="radio tree-item-radio" type="radio" bind:group {name} {value} on:click on:change />
|
||||
<input class="radio tree-item-radio" type="radio" bind:group {name} {value} />
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
export interface TreeViewNode {
|
||||
/** Nodes Unique ID */
|
||||
id: string;
|
||||
/** Main content. accepts HTML. */
|
||||
content: string;
|
||||
/** Lead content. accepts HTML. */
|
||||
lead?: string;
|
||||
/** Set open by default on load. */
|
||||
open?: boolean;
|
||||
/** Set the tree disabled state. */
|
||||
disabled?: boolean;
|
||||
/** children nodes. */
|
||||
children?: TreeViewNode[];
|
||||
/** Set the input's value. */
|
||||
value?: unknown;
|
||||
/** input checked */
|
||||
checked?: boolean;
|
||||
/** input is set to indeterminate, only availabe in multiple selection mode. */
|
||||
indeterminate?: boolean;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,8 @@ export { default as Tab } from './components/Tab/Tab.svelte';
|
||||
export { default as TabAnchor } from './components/Tab/TabAnchor.svelte';
|
||||
export { default as TreeView } from './components/TreeView/TreeView.svelte';
|
||||
export { default as TreeViewItem } from './components/TreeView/TreeViewItem.svelte';
|
||||
export { default as TreeViewDataDrivenItem } from './components/TreeView/TreeViewDataDrivenItem.svelte';
|
||||
export { default as RecursiveTreeView } from './components/TreeView/RecursiveTreeView.svelte';
|
||||
export { default as RecursiveTreeViewItem } from './components/TreeView/RecursiveTreeViewItem.svelte';
|
||||
// Utility Components
|
||||
export { default as CodeBlock } from './utilities/CodeBlock/CodeBlock.svelte';
|
||||
export { default as Modal } from './utilities/Modal/Modal.svelte';
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
import type { CssClasses, SvelteEvent } from '../../index.js';
|
||||
|
||||
// Props
|
||||
/** Customize the `title` attribute for the component. */
|
||||
export let title = 'Toggle light or dark mode.';
|
||||
// Props (styles)
|
||||
/** Provide classes to set the light background color. */
|
||||
export let bgLight: CssClasses = 'bg-surface-50';
|
||||
/** Provide classes to set the dark background color. */
|
||||
@@ -85,7 +88,7 @@
|
||||
role="switch"
|
||||
aria-label="Light Switch"
|
||||
aria-checked={$modeCurrent}
|
||||
title="Toggle {$modeCurrent === true ? 'Dark' : 'Light'} Mode"
|
||||
{title}
|
||||
tabindex="0"
|
||||
>
|
||||
<!-- Thumb -->
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://skeleton.dev/",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@floating-ui/dom": "^1.2.9",
|
||||
@@ -49,6 +52,7 @@
|
||||
"tslib": "^2.5.3",
|
||||
"typescript": "^5.0.3",
|
||||
"vite": "^4.3.9",
|
||||
"vite-plugin-tailwind-purgecss": "^0.1.3",
|
||||
"vitest": "^0.32.0"
|
||||
},
|
||||
"type": "module"
|
||||
|
||||
67
sites/skeleton.dev/pnpm-lock.yaml
generated
67
sites/skeleton.dev/pnpm-lock.yaml
generated
@@ -4,6 +4,11 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-free':
|
||||
specifier: ^6.4.2
|
||||
version: 6.4.2
|
||||
|
||||
devDependencies:
|
||||
'@faker-js/faker':
|
||||
specifier: ^8.0.2
|
||||
@@ -92,6 +97,9 @@ devDependencies:
|
||||
vite:
|
||||
specifier: ^4.3.9
|
||||
version: 4.3.9(@types/node@20.1.4)
|
||||
vite-plugin-tailwind-purgecss:
|
||||
specifier: ^0.1.3
|
||||
version: 0.1.3(vite@4.3.9)
|
||||
vitest:
|
||||
specifier: ^0.32.0
|
||||
version: 0.32.0
|
||||
@@ -353,6 +361,12 @@ packages:
|
||||
'@floating-ui/core': 1.2.6
|
||||
dev: true
|
||||
|
||||
/@fortawesome/fontawesome-free@6.4.2:
|
||||
resolution: {integrity: sha512-m5cPn3e2+FDCOgi1mz0RexTUvvQibBebOUlUlW0+YrMjDTPkiJ6VTKukA1GRsvRw+12KyJndNjj0O4AgTxm2Pg==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/@humanwhocodes/config-array@0.11.10:
|
||||
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
@@ -601,6 +615,10 @@ packages:
|
||||
resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
|
||||
dev: true
|
||||
|
||||
/@types/estree@1.0.2:
|
||||
resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema@7.0.11:
|
||||
resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
|
||||
dev: true
|
||||
@@ -1089,6 +1107,11 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/commander@10.0.1:
|
||||
resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
|
||||
engines: {node: '>=14'}
|
||||
dev: true
|
||||
|
||||
/commander@4.1.1:
|
||||
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -1422,6 +1445,12 @@ packages:
|
||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||
dev: true
|
||||
|
||||
/estree-walker@3.0.3:
|
||||
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
|
||||
dependencies:
|
||||
'@types/estree': 1.0.2
|
||||
dev: true
|
||||
|
||||
/esutils@2.0.3:
|
||||
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -1585,6 +1614,17 @@ packages:
|
||||
path-is-absolute: 1.0.1
|
||||
dev: true
|
||||
|
||||
/glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 5.1.6
|
||||
once: 1.4.0
|
||||
dev: true
|
||||
|
||||
/globals@13.20.0:
|
||||
resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1908,6 +1948,13 @@ packages:
|
||||
brace-expansion: 1.1.11
|
||||
dev: true
|
||||
|
||||
/minimatch@5.1.6:
|
||||
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
dev: true
|
||||
|
||||
/minimatch@9.0.3:
|
||||
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
@@ -2299,6 +2346,16 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/purgecss@6.0.0-alpha.0:
|
||||
resolution: {integrity: sha512-UC7d7uIyZsky+srEsSXny9BkbTcVn3ZtBCNX3rW3DsqJKhvUXFRpufA4ktcHzWF0+JLZgmsqjUm/8R82x9bHpw==}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
commander: 10.0.1
|
||||
glob: 8.1.0
|
||||
postcss: 8.4.24
|
||||
postcss-selector-parser: 6.0.12
|
||||
dev: true
|
||||
|
||||
/query-string@8.1.0:
|
||||
resolution: {integrity: sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw==}
|
||||
engines: {node: '>=14.16'}
|
||||
@@ -3059,6 +3116,16 @@ packages:
|
||||
- terser
|
||||
dev: true
|
||||
|
||||
/vite-plugin-tailwind-purgecss@0.1.3(vite@4.3.9):
|
||||
resolution: {integrity: sha512-VVz9fwKBEEFSbj/rKxtwtczvoSrIqbzbo6S+MT7gH0CsmKNwlx947VMoV8B085ocxGCuFlddOPRDszNXLi2nTQ==}
|
||||
peerDependencies:
|
||||
vite: ^4.1.1
|
||||
dependencies:
|
||||
estree-walker: 3.0.3
|
||||
purgecss: 6.0.0-alpha.0
|
||||
vite: 4.3.9(@types/node@20.1.4)
|
||||
dev: true
|
||||
|
||||
/vite@4.3.9(@types/node@20.1.4):
|
||||
resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
|
||||
@@ -3,17 +3,11 @@
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<!-- NOTE: SEO handled in root page layout: /src/+layout.svelte -->
|
||||
<!-- NOTE: SEO handled in root layout: /src/+layout.svelte -->
|
||||
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
|
||||
<!-- Font Awesome -->
|
||||
<!-- https://fontawesome.com/docs/web/setup/host-yourself/webfonts -->
|
||||
<link href="/font-awesome/css/fontawesome.min.css" rel="stylesheet" />
|
||||
<link href="/font-awesome/css/brands.min.css" rel="stylesheet" />
|
||||
<link href="/font-awesome/css/solid.min.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body data-theme="">
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
const cBase = 'bg-surface-50 dark:bg-surface-900 border-t border-surface-500/10 text-xs md:text-base';
|
||||
const cRowOne = 'flex flex-col md:flex-row justify-between items-center md:items-start space-y-5 md:space-y-0';
|
||||
const cRowTwo = 'flex flex-col md:flex-row justify-between items-center md:items-start space-y-4 md:space-y-0';
|
||||
|
||||
// Social Icons
|
||||
const socialLinks = [
|
||||
{ title: 'GitHub', href: 'https://github.com/skeletonlabs/skeleton', icon: 'fa-github' },
|
||||
{ title: 'Discord', href: 'https://discord.gg/EXqV7W8MtY', icon: 'fa-discord' },
|
||||
{ title: 'X (Twitter)', href: 'https://x.com/SkeletonUI', icon: 'fa-x-twitter' },
|
||||
{ title: 'YouTube', href: 'https://www.youtube.com/@skeletonlabs', icon: 'fa-youtube' }
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="page-footer {cBase}">
|
||||
@@ -67,23 +75,12 @@
|
||||
<span class="opacity-10 mx-2">|</span>
|
||||
<a class="anchor" href="https://skeletonlabs.co/" target="_blank" rel="noreferrer">Skeleton Labs </a>
|
||||
</p>
|
||||
<div class="flex space-x-4">
|
||||
<a class="btn variant-soft" href="https://github.com/skeletonlabs/skeleton" target="_blank" rel="noreferrer">
|
||||
<i class="fa-brands fa-github" />
|
||||
<span class="hidden md:inline-block ml-2">Github</span>
|
||||
</a>
|
||||
<a class="btn variant-soft" href="https://discord.gg/EXqV7W8MtY" target="_blank" rel="noreferrer">
|
||||
<i class="fa-brands fa-discord" />
|
||||
<span class="hidden md:inline-block ml-2">Discord</span>
|
||||
</a>
|
||||
<a class="btn variant-soft" href="https://twitter.com/SkeletonUI" target="_blank" rel="noreferrer">
|
||||
<i class="fa-brands fa-twitter" />
|
||||
<span class="hidden md:inline-block ml-2">Twitter</span>
|
||||
</a>
|
||||
<a class="btn variant-soft" href="https://www.youtube.com/@skeletonlabs" target="_blank" rel="noreferrer">
|
||||
<i class="fa-brands fa-youtube" />
|
||||
<span class="hidden md:inline-block ml-2">YouTube</span>
|
||||
<div class="flex gap-6">
|
||||
{#each socialLinks as sl}
|
||||
<a class="opacity-75 hover:opacity-100" href={sl.href} target="_blank" rel="noreferrer" title={sl.title}>
|
||||
<i class="fa-brands text-xl {sl.icon}" />
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@@ -226,6 +226,8 @@
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
import { afterNavigate } from '$app/navigation';
|
||||
|
||||
afterNavigate((params: any) => {
|
||||
const isNewPage: boolean = params.from && params.to && params.from.route.id !== params.to.route.id;
|
||||
const elemPage = document.querySelector('#page');
|
||||
|
||||
@@ -47,12 +47,12 @@
|
||||
|
||||
<!-- Slot: Usage -->
|
||||
<svelte:fragment slot="usage">
|
||||
<p>
|
||||
<section class="space-y-4">
|
||||
Uses <code class="code">input[type='file']</code> and allows for all native input features and accessibility. Including
|
||||
<code class="code">multiple</code>,
|
||||
<code class="code">accept</code>, and <code class="code">required</code>.
|
||||
</p>
|
||||
<div class="space-y-4">
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Customization</h2>
|
||||
<p>
|
||||
Customize the component with the available <code class="code">icon</code>, <code class="code">message</code>, and
|
||||
@@ -80,18 +80,18 @@
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Binding Method</h2>
|
||||
<p>Use a <code class="code">FileList</code> to bind the file data.</p>
|
||||
<CodeBlock language="ts" code={`let files: FileList;`} />
|
||||
<CodeBlock language="html" code={`<FileDropzone ... bind:files={files} />`} />
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">On Change Event</h2>
|
||||
<p>Use the <code class="code">on:change</code> event to monitor file selection or changes.</p>
|
||||
<CodeBlock language="ts" code={`function onChangeHandler(e: Event): void {\n\tconsole.log('file data:', e);\n}`} />
|
||||
<CodeBlock language="html" code={`<FileDropzone ... on:change={onChangeHandler}>Upload</FileDropzone>`} />
|
||||
</div>
|
||||
</section>
|
||||
</svelte:fragment>
|
||||
</DocsShell>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
// Reactive
|
||||
$: props = { value: 50, max: 100, step: 10 };
|
||||
$: strokeProps = { value: 100, max: 400, step: 20 };
|
||||
let strokeLinecap: 'butt' | 'round' | 'square' = 'butt';
|
||||
</script>
|
||||
|
||||
<DocsShell {settings}>
|
||||
@@ -45,47 +46,96 @@
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Styling</h2>
|
||||
<p>
|
||||
Use the <code class="code">stroke</code> <code class="code">meter</code> or <code class="code">track</code>properties to style the
|
||||
radial.
|
||||
Use the <code class="code">meter</code>, or <code class="code">track</code>, <code class="code">stroke</code>,
|
||||
<code class="code">strokeLinecap</code> properties to style the radial.
|
||||
</p>
|
||||
<DocsPreview background="neutral">
|
||||
<svelte:fragment slot="preview">
|
||||
<div class="w-full grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-2 text-center">
|
||||
<div class="p-4 space-y-2">
|
||||
<ProgressRadial stroke={strokeProps.value} meter="stroke-primary-500" track="stroke-primary-500/30" width="w-full" />
|
||||
<ProgressRadial
|
||||
stroke={strokeProps.value}
|
||||
meter="stroke-primary-500"
|
||||
track="stroke-primary-500/30"
|
||||
width="w-full"
|
||||
{strokeLinecap}
|
||||
value={50}
|
||||
/>
|
||||
<p>Primary</p>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<ProgressRadial stroke={strokeProps.value} meter="stroke-secondary-500" track="stroke-secondary-500/30" width="w-full" />
|
||||
<ProgressRadial
|
||||
stroke={strokeProps.value}
|
||||
meter="stroke-secondary-500"
|
||||
track="stroke-secondary-500/30"
|
||||
width="w-full"
|
||||
{strokeLinecap}
|
||||
value={50}
|
||||
/>
|
||||
<p>Secondary</p>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<ProgressRadial stroke={strokeProps.value} meter="stroke-tertiary-500" track="stroke-tertiary-500/30" width="w-full" />
|
||||
<ProgressRadial
|
||||
stroke={strokeProps.value}
|
||||
meter="stroke-tertiary-500"
|
||||
track="stroke-tertiary-500/30"
|
||||
width="w-full"
|
||||
{strokeLinecap}
|
||||
value={50}
|
||||
/>
|
||||
<p>Tertiary</p>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<ProgressRadial stroke={strokeProps.value} meter="stroke-success-500" track="stroke-success-500/30" width="w-full" />
|
||||
<ProgressRadial
|
||||
stroke={strokeProps.value}
|
||||
meter="stroke-success-500"
|
||||
track="stroke-success-500/30"
|
||||
width="w-full"
|
||||
{strokeLinecap}
|
||||
value={50}
|
||||
/>
|
||||
<p>Success</p>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<ProgressRadial stroke={strokeProps.value} meter="stroke-warning-500" track="stroke-warning-500/30" width="w-full" />
|
||||
<ProgressRadial
|
||||
stroke={strokeProps.value}
|
||||
meter="stroke-warning-500"
|
||||
track="stroke-warning-500/30"
|
||||
width="w-full"
|
||||
{strokeLinecap}
|
||||
value={50}
|
||||
/>
|
||||
<p>Warning</p>
|
||||
</div>
|
||||
<div class="p-4 space-y-2">
|
||||
<ProgressRadial stroke={strokeProps.value} meter="stroke-error-500" track="stroke-error-500/30" width="w-full" />
|
||||
<ProgressRadial
|
||||
stroke={strokeProps.value}
|
||||
meter="stroke-error-500"
|
||||
track="stroke-error-500/30"
|
||||
width="w-full"
|
||||
{strokeLinecap}
|
||||
value={50}
|
||||
/>
|
||||
<p>Error</p>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<div class="w-48 mx-auto">
|
||||
<div class="flex justify-between items-center gap-4">
|
||||
<div class="w-60">
|
||||
<input type="range" min="20" max={strokeProps.max} step={strokeProps.step} bind:value={strokeProps.value} />
|
||||
</div>
|
||||
<select bind:value={strokeLinecap} class="select w-auto">
|
||||
{#each ['butt', 'round', 'square'] as v}
|
||||
<option value={v}>{v}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`<ProgressRadial ... stroke={${strokeProps.value}} meter="stroke-primary-500" track="stroke-primary-500/30" />`}
|
||||
code={`<ProgressRadial ... stroke={${strokeProps.value}} meter="stroke-primary-500" track="stroke-primary-500/30" strokeLinecap={${strokeLinecap}} />`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
import { DocsFeature, type DocsShellSettings } from '$lib/layouts/DocsShell/types';
|
||||
import DocsPreview from '$lib/components/DocsPreview/DocsPreview.svelte';
|
||||
// Components
|
||||
import { TreeView, TreeViewItem, type TreeViewNode } from '@skeletonlabs/skeleton';
|
||||
import { TreeView, TreeViewItem, RecursiveTreeView } from '@skeletonlabs/skeleton';
|
||||
// Utilities
|
||||
import { CodeBlock } from '@skeletonlabs/skeleton';
|
||||
// Sveld
|
||||
import sveldTreeView from '@skeletonlabs/skeleton/components/TreeView/TreeView.svelte?raw&sveld';
|
||||
import sveldTreeViewItem from '@skeletonlabs/skeleton/components/TreeView/TreeViewItem.svelte?raw&sveld';
|
||||
import sveldRecursiveTreeView from '@skeletonlabs/skeleton/components/TreeView/RecursiveTreeView.svelte?raw&sveld';
|
||||
import { nodes } from './exampleData';
|
||||
|
||||
// Docs Shell
|
||||
const settings: DocsShellSettings = {
|
||||
feature: DocsFeature.Component,
|
||||
name: 'Tree Views',
|
||||
description: 'Display information in a hierarchical structure using collapsible nodes.',
|
||||
imports: ['TreeView', 'TreeViewItem', 'type TreeViewNode'],
|
||||
imports: ['TreeView', 'TreeViewItem', 'RecursiveTreeView', 'type TreeViewNode'],
|
||||
source: 'packages/skeleton/src/lib/components/TreeView',
|
||||
aria: 'https://www.w3.org/WAI/ARIA/apg/patterns/treeview/',
|
||||
components: [
|
||||
@@ -38,7 +40,8 @@
|
||||
'regionSymbol',
|
||||
'regionChildren'
|
||||
]
|
||||
}
|
||||
},
|
||||
{ label: 'RecursiveTreeView', sveld: sveldRecursiveTreeView }
|
||||
],
|
||||
keyboard: [
|
||||
['<kbd class="kbd">Tab</kbd>', "Focus the next tree-view item or it's input."],
|
||||
@@ -67,94 +70,11 @@
|
||||
|
||||
let expandTree: TreeView;
|
||||
|
||||
let simpleDD: TreeViewNode[] = [
|
||||
{
|
||||
content: 'Books',
|
||||
lead: '<i class="fa-solid fa-book-skull"></i>',
|
||||
open: true,
|
||||
children: [
|
||||
{ content: 'Clean Code', value: 'Clean Code' },
|
||||
{ content: 'The Clean Coder', value: 'The Clean Coder' },
|
||||
{ content: 'The Art of Unix Programming', value: 'The Art of Unix Programming' }
|
||||
],
|
||||
value: 'books'
|
||||
},
|
||||
{
|
||||
content: 'Movies',
|
||||
lead: '<i class="fa-solid fa-film"></i>',
|
||||
children: [
|
||||
{ content: 'The Flash', value: 'The Flash' },
|
||||
{ content: 'Guardians of the Galaxy', value: 'Guardians of the Galaxy' },
|
||||
{ content: 'Black Panther', value: 'Black Panther' }
|
||||
],
|
||||
value: 'movies'
|
||||
},
|
||||
{
|
||||
content: 'TV',
|
||||
lead: '<i class="fa-solid fa-tv"></i>',
|
||||
value: 'tv'
|
||||
}
|
||||
];
|
||||
|
||||
let singleDD: TreeViewNode[] = [
|
||||
{
|
||||
content: 'Books',
|
||||
lead: '<i class="fa-solid fa-book-skull"></i>',
|
||||
open: true,
|
||||
checked: true,
|
||||
children: [
|
||||
{ content: 'Clean Code', value: 'Clean Code' },
|
||||
{ content: 'The Clean Coder', value: 'The Clean Coder' },
|
||||
{ content: 'The Art of Unix Programming', value: 'The Art of Unix Programming', checked: true }
|
||||
],
|
||||
value: 'books'
|
||||
},
|
||||
{
|
||||
content: 'Movies',
|
||||
lead: '<i class="fa-solid fa-film"></i>',
|
||||
children: [
|
||||
{ content: 'The Flash', value: 'The Flash' },
|
||||
{ content: 'Guardians of the Galaxy', value: 'Guardians of the Galaxy' },
|
||||
{ content: 'Black Panther', value: 'Black Panther' }
|
||||
],
|
||||
value: 'movies'
|
||||
},
|
||||
{
|
||||
content: 'TV',
|
||||
lead: '<i class="fa-solid fa-tv"></i>',
|
||||
value: 'tv'
|
||||
}
|
||||
];
|
||||
|
||||
let multipleDD: TreeViewNode[] = [
|
||||
{
|
||||
content: 'Books',
|
||||
lead: '<i class="fa-solid fa-book-skull"></i>',
|
||||
open: true,
|
||||
indeterminate: true,
|
||||
children: [
|
||||
{ content: 'Clean Code', value: 'Clean Code' },
|
||||
{ content: 'The Clean Coder', value: 'The Clean Coder', checked: true },
|
||||
{ content: 'The Art of Unix Programming', value: 'The Art of Unix Programming', checked: true }
|
||||
],
|
||||
value: 'books'
|
||||
},
|
||||
{
|
||||
content: 'Movies',
|
||||
lead: '<i class="fa-solid fa-film"></i>',
|
||||
children: [
|
||||
{ content: 'The Flash', value: 'The Flash' },
|
||||
{ content: 'Guardians of the Galaxy', value: 'Guardians of the Galaxy' },
|
||||
{ content: 'Black Panther', value: 'Black Panther' }
|
||||
],
|
||||
value: 'movies'
|
||||
},
|
||||
{
|
||||
content: 'TV',
|
||||
lead: '<i class="fa-solid fa-tv"></i>',
|
||||
value: 'tv'
|
||||
}
|
||||
];
|
||||
let expandedNodes: string[] = [];
|
||||
let disabledNodes: string[] = ['programming'];
|
||||
let singleCheckedNodes: string[] = [];
|
||||
let multiCheckedNodes: string[] = ['javascript'];
|
||||
let indeterminateNodes: string[] = [];
|
||||
</script>
|
||||
|
||||
<DocsShell {settings}>
|
||||
@@ -734,62 +654,25 @@ let booksChildren: TreeViewItem[] = [];
|
||||
<!-- Recursive Mode -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Recursive Mode</h2>
|
||||
<p>Tree views can be generated using a recursive data-driven method.</p>
|
||||
<DocsPreview background="neutral" regionFooter="flex justify-center gap-4">
|
||||
<svelte:fragment slot="preview">
|
||||
<TreeView bind:nodes={simpleDD} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
let myTreeViewNodes: TreeViewNode[] = [
|
||||
{
|
||||
content: 'Books',
|
||||
lead: '(icon)',
|
||||
open: true,
|
||||
children: [
|
||||
{ content: 'Clean Code' },
|
||||
{ content: 'The Clean Coder' },
|
||||
{ content: 'The Art of Unix Programming' },
|
||||
]
|
||||
},
|
||||
// ...
|
||||
]
|
||||
`}
|
||||
/>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
<TreeView nodes={myTreeViewNodes}/>
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
<!-- Single Selection -->
|
||||
<h3 class="h3">Single Selection</h3>
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
Relational checking is automatically applied when generating your list in a recursive manner. Setting a child as <code class="code">checked</code> will not automatically affect the parent.
|
||||
Tree views can be generated with a recursive data-driven method using the <code class="code">RecursiveTreeView</code> components.
|
||||
</p>
|
||||
<DocsPreview background="neutral" regionFooter="flex justify-center gap-4">
|
||||
<svelte:fragment slot="preview">
|
||||
<TreeView bind:nodes={singleDD} selection />
|
||||
<RecursiveTreeView {nodes} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<p>To get expected results make sure to include a <em>unique Id</em> for each node.</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
let myTreeViewNodes: TreeViewNode[] = [
|
||||
{
|
||||
content: 'Books',
|
||||
id: 'unique-id'
|
||||
content: 'content',
|
||||
lead: '(icon)',
|
||||
open: true,
|
||||
checked: true,
|
||||
children: [
|
||||
{ content: 'Clean Code' },
|
||||
{ content: 'The Clean Coder' },
|
||||
{ content: 'The Art of Unix Programming', checked: true },
|
||||
//...
|
||||
]
|
||||
},
|
||||
// ...
|
||||
@@ -799,45 +682,140 @@ let myTreeViewNodes: TreeViewNode[] = [
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
<TreeView bind:nodes={myTreeViewNodes} selection/>
|
||||
<RecursiveTreeView nodes={nodes} />
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
<!-- Multiple Selection -->
|
||||
<h3 class="h3">Multiple Selection</h3>
|
||||
<p>Relational checking is automatically applied when generating your list in a recursive manner.</p>
|
||||
<!-- Expanded -->
|
||||
<h3 class="h3">Expanded</h3>
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
To access and modify the expanded nodes use <code class="code">expandedNodes</code> array prop.
|
||||
</p>
|
||||
<DocsPreview background="neutral" regionFooter="flex justify-center gap-4">
|
||||
<svelte:fragment slot="preview">
|
||||
<TreeView bind:nodes={multipleDD} selection multiple />
|
||||
<RecursiveTreeView {nodes} bind:expandedNodes />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
let myTreeViewNodes: TreeViewNode[] = [
|
||||
{
|
||||
content: 'Books',
|
||||
lead: '(icon)',
|
||||
open: true,
|
||||
indeterminate: true,
|
||||
children: [
|
||||
{ content: 'Clean Code' },
|
||||
{ content: 'The Clean Coder', checked: true },
|
||||
{ content: 'The Art of Unix Programming', checked: true },
|
||||
]
|
||||
},
|
||||
// ...
|
||||
]
|
||||
let myTreeViewNodes: TreeViewNode[] = //...
|
||||
let expandedNodes : string[] = [];
|
||||
`}
|
||||
/>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
<TreeView bind:nodes={myTreeViewNodes} selection multiple/>
|
||||
<RecursiveTreeView nodes={nodes} bind:expandedNodes={expandedNodes} />
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<span>Expanded nodes: <code class="code">{expandedNodes}</code></span>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
<!-- Disabled -->
|
||||
<h3 class="h3">Disabled</h3>
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
To access and modify the disabled nodes use <code class="code">disabledNodes</code> array prop.
|
||||
</p>
|
||||
<DocsPreview background="neutral" regionFooter="flex justify-center gap-4">
|
||||
<svelte:fragment slot="preview">
|
||||
<RecursiveTreeView {nodes} bind:disabledNodes />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
let myTreeViewNodes: TreeViewNode[] = //...
|
||||
let disabledNodes : string[] = [];
|
||||
`}
|
||||
/>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
<RecursiveTreeView nodes={nodes} bind:disabledNodes={disabledNodes} />
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<span>Disabled nodes: <code class="code">{disabledNodes}</code></span>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
<!-- Selection -->
|
||||
<h3 class="h3">Selection</h3>
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
Just like normal Tree-view, Recursive Tree-view supports selection with both <em>single</em> and <em>multiple</em> modes.
|
||||
</p>
|
||||
<p>To access and modify the checked nodes use <code class="code">checkedNodes</code> array prop.</p>
|
||||
<DocsPreview background="neutral" regionFooter="flex justify-center gap-4">
|
||||
<svelte:fragment slot="preview">
|
||||
<RecursiveTreeView selection {nodes} bind:checkedNodes={singleCheckedNodes} />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
let myTreeViewNodes: TreeViewNode[] = //...
|
||||
let checkedNodes : string[] = [];
|
||||
`}
|
||||
/>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
<RecursiveTreeView selection nodes={nodes} bind:checkedNodes={checkedNodes} />
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<span>checked nodes: <code class="code">{singleCheckedNodes}</code></span>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
|
||||
<!-- Relational Selection -->
|
||||
<h3 class="h3">Relational Selection</h3>
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
Just like normal Tree-view, Recursive Tree-view supports relational selection using the prop <code class="code">relational</code>.
|
||||
</p>
|
||||
<p>To access and modify the checked nodes use <code class="code">checkedNodes</code> array prop.</p>
|
||||
<p>
|
||||
In multiple relational selection mode, an extra array prop <code class="code">indeterminateNodes</code> is available to indicate indeterminate
|
||||
nodes.
|
||||
</p>
|
||||
<DocsPreview background="neutral" regionFooter="flex justify-center gap-4">
|
||||
<svelte:fragment slot="preview">
|
||||
<RecursiveTreeView selection multiple relational {nodes} bind:checkedNodes={multiCheckedNodes} bind:indeterminateNodes />
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
let myTreeViewNodes: TreeViewNode[] = //...
|
||||
let checkedNodes : string[] = [];
|
||||
let indeterminateNodes : string[] = [];
|
||||
`}
|
||||
/>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
<RecursiveTreeView
|
||||
selection
|
||||
multiple
|
||||
relational
|
||||
nodes={nodes}
|
||||
bind:checkedNodes={checkedNodes}
|
||||
bind:indeterminateNodes={indeterminateNodes}/>
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<span>indeterminate nodes: <code class="code whitespace-normal">{indeterminateNodes}</code></span>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
</section>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
import type { TreeViewNode } from '@skeletonlabs/skeleton';
|
||||
|
||||
export const nodes: TreeViewNode[] = [
|
||||
{
|
||||
id: 'programming',
|
||||
content: 'programming',
|
||||
value: 'programming',
|
||||
children: [
|
||||
{
|
||||
id: 'language',
|
||||
content: 'language',
|
||||
value: 'language',
|
||||
children: [
|
||||
{
|
||||
id: 'javascript',
|
||||
content: 'javascript',
|
||||
value: 'javascript'
|
||||
},
|
||||
{
|
||||
id: 'c#',
|
||||
content: 'c#',
|
||||
value: 'c#'
|
||||
},
|
||||
{
|
||||
id: 'rust',
|
||||
content: 'rust',
|
||||
value: 'rust'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
content: 'database',
|
||||
value: 'database',
|
||||
id: 'database',
|
||||
children: [
|
||||
{
|
||||
id: 'mongodb',
|
||||
content: 'mongodb',
|
||||
value: 'mongodb'
|
||||
},
|
||||
{
|
||||
id: 'mssql',
|
||||
content: 'mssql',
|
||||
value: 'mssql'
|
||||
},
|
||||
{
|
||||
id: 'casandra',
|
||||
content: 'casandra',
|
||||
value: 'casandra'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
content: 'framework',
|
||||
value: 'framework',
|
||||
id: 'framework',
|
||||
children: [
|
||||
{
|
||||
id: 'svelte',
|
||||
content: 'svelte',
|
||||
value: 'svelte'
|
||||
},
|
||||
{
|
||||
id: 'angular',
|
||||
content: 'angular',
|
||||
value: 'angular'
|
||||
},
|
||||
{
|
||||
id: 'react',
|
||||
content: 'react',
|
||||
value: 'react'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
content: 'books',
|
||||
value: 'books',
|
||||
id: 'books',
|
||||
children: [
|
||||
{
|
||||
id: 'clean code',
|
||||
content: 'clean code',
|
||||
value: 'clean code',
|
||||
children: [
|
||||
{
|
||||
id: 'clean code - section 1',
|
||||
content: 'clean code - section 1',
|
||||
value: 'clean code - section 1'
|
||||
},
|
||||
{
|
||||
id: 'clean code - section 2',
|
||||
content: 'clean code - section 2',
|
||||
value: 'clean code - section 2'
|
||||
},
|
||||
{
|
||||
id: 'clean code - section 3',
|
||||
content: 'clean code - section 3',
|
||||
value: 'clean code - section 3'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'structure',
|
||||
content: 'structure',
|
||||
value: 'structure',
|
||||
children: [
|
||||
{
|
||||
id: 'structure - section 1',
|
||||
content: 'structure - section 1',
|
||||
value: 'structure - section 1'
|
||||
},
|
||||
{
|
||||
id: 'structure - section 2',
|
||||
content: 'structure - section 2',
|
||||
value: 'structure - section 2'
|
||||
},
|
||||
{
|
||||
id: 'structure - section 3',
|
||||
content: 'structure - section 3',
|
||||
value: 'structure - section 3'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'clean coder',
|
||||
content: 'clean coder',
|
||||
value: 'clean coder',
|
||||
children: [
|
||||
{
|
||||
id: 'clean coder - section 1',
|
||||
content: 'clean coder - section 1',
|
||||
value: 'clean coder - section 1'
|
||||
},
|
||||
{
|
||||
id: 'clean coder - section 2',
|
||||
content: 'clean coder - section 2',
|
||||
value: 'clean coder - section 2'
|
||||
},
|
||||
{
|
||||
id: 'clean coder - section 3',
|
||||
content: 'clean coder - section 3',
|
||||
value: 'clean coder - section 3'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'series',
|
||||
content: 'series',
|
||||
value: 'series',
|
||||
children: [
|
||||
{
|
||||
id: 'Mr. Robot',
|
||||
content: 'Mr. Robot',
|
||||
value: 'Mr. Robot',
|
||||
children: [
|
||||
{
|
||||
id: 'Mr. Robot - season 1',
|
||||
content: 'Mr. Robot - season 1',
|
||||
value: 'Mr. Robot - season 1'
|
||||
},
|
||||
{
|
||||
id: 'Mr. Robot - season 2',
|
||||
content: 'Mr. Robot - season 2',
|
||||
value: 'Mr. Robot - season 2'
|
||||
},
|
||||
{
|
||||
id: 'Mr. Robot - season 3',
|
||||
content: 'Mr. Robot - season 3',
|
||||
value: 'Mr. Robot - season 3'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'silicon valley',
|
||||
content: 'silicon valley',
|
||||
value: 'silicon valley',
|
||||
children: [
|
||||
{
|
||||
id: 'silicon valley - season 1',
|
||||
content: 'silicon valley - season 1',
|
||||
value: 'silicon valley - season 1'
|
||||
},
|
||||
{
|
||||
id: 'silicon valley - season 2',
|
||||
content: 'silicon valley - season 2',
|
||||
value: 'silicon valley - season 2'
|
||||
},
|
||||
{
|
||||
id: 'silicon valley - season 3',
|
||||
content: 'silicon valley - season 3',
|
||||
value: 'silicon valley - season 3'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'code monkeys',
|
||||
content: 'code monkeys',
|
||||
value: 'code monkeys',
|
||||
children: [
|
||||
{
|
||||
id: 'code monkeys - season 1',
|
||||
content: 'code monkeys - season 1',
|
||||
value: 'code monkeys - season 1'
|
||||
},
|
||||
{
|
||||
id: 'code monkeys - season 2',
|
||||
content: 'code monkeys - season 2',
|
||||
value: 'code monkeys - season 2'
|
||||
},
|
||||
{
|
||||
id: 'code monkeys - season 3',
|
||||
content: 'code monkeys - season 3',
|
||||
value: 'code monkeys - season 3'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -20,6 +20,9 @@
|
||||
[`<div class="${cSwatch} bg-surface-500" />`, '[style]-surface-[50-900]', 'The base level colors, used for backgrounds.']
|
||||
]
|
||||
};
|
||||
|
||||
const shadesArr = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900];
|
||||
const colorsArr = ['primary', 'secondary', 'tertiary', 'success', 'warning', 'error', 'surface'];
|
||||
</script>
|
||||
|
||||
<LayoutPage>
|
||||
@@ -32,11 +35,117 @@
|
||||
|
||||
<Table source={tableProps} />
|
||||
|
||||
<!-- Color Palette -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Color Palette</h2>
|
||||
<p>Shade 500 is used as the default value.</p>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 gap-2">
|
||||
<!-- primary -->
|
||||
<div class="rounded-container-token overflow-hidden">
|
||||
<div class="bg-primary-500 p-2 py-4"><span class="text-on-primary-token">Primary</span></div>
|
||||
<div class="bg-primary-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-primary-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-primary-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-primary-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-primary-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-primary-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-primary-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-primary-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-primary-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-primary-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
<!-- secondary -->
|
||||
<div class="rounded-container-token overflow-hidden">
|
||||
<div class="bg-secondary-500 p-2 py-4"><span class="text-on-secondary-token">Secondary</span></div>
|
||||
<div class="bg-secondary-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-secondary-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-secondary-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-secondary-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-secondary-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-secondary-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-secondary-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-secondary-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-secondary-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-secondary-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
<!-- tertiary -->
|
||||
<div class="rounded-container-token overflow-hidden">
|
||||
<div class="bg-tertiary-500 p-2 py-4"><span class="text-on-tertiary-token">Tertiary</span></div>
|
||||
<div class="bg-tertiary-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-tertiary-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-tertiary-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-tertiary-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-tertiary-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-tertiary-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-tertiary-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-tertiary-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-tertiary-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-tertiary-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
<!-- success -->
|
||||
<div class="rounded-container-token overflow-hidden">
|
||||
<div class="bg-success-500 p-2 py-4"><span class="text-on-success-token">Success</span></div>
|
||||
<div class="bg-success-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-success-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-success-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-success-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-success-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-success-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-success-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-success-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-success-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-success-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
<!-- warning -->
|
||||
<div class="rounded-container-token overflow-hidden">
|
||||
<div class="bg-warning-500 p-2 py-4"><span class="text-on-warning-token">Warning</span></div>
|
||||
<div class="bg-warning-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-warning-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-warning-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-warning-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-warning-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-warning-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-warning-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-warning-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-warning-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-warning-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
<!-- error -->
|
||||
<div class="rounded-container-token overflow-hidden">
|
||||
<div class="bg-error-500 p-2 py-4"><span class="text-on-error-token">Error</span></div>
|
||||
<div class="bg-error-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-error-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-error-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-error-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-error-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-error-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-error-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-error-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-error-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-error-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
<!-- surface -->
|
||||
<div class="rounded-container-token overflow-hidden col-span-2">
|
||||
<div class="bg-surface-500 p-2 py-4"><span class="text-on-surface-token">Surface</span></div>
|
||||
<div class="bg-surface-50 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">50</span></div>
|
||||
<div class="bg-surface-100 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">100</span></div>
|
||||
<div class="bg-surface-200 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">200</span></div>
|
||||
<div class="bg-surface-300 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">300</span></div>
|
||||
<div class="bg-surface-400 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">400</span></div>
|
||||
<div class="bg-surface-500 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">500 ★</span></div>
|
||||
<div class="bg-surface-600 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">600</span></div>
|
||||
<div class="bg-surface-700 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">700</span></div>
|
||||
<div class="bg-surface-800 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">800</span></div>
|
||||
<div class="bg-surface-900 p-2"><span class="bg-black/30 text-white p-1 text-xs rounded">900</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Reference -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Usage</h2>
|
||||
<CodeBlock language="html" code={`<!-- Inlined classes -->\n<div class="bg-primary-500 text-secondary-500">Skeleton</div>`} />
|
||||
<CodeBlock language="html" code={`<!-- Tailwind opacity scale -->\n<div class="border border-primary-500/50">Skeleton</div>`} />
|
||||
<CodeBlock language="html" code={`<!-- Tailwind opacity scale -->\n<div class="border border-primary-500/30">Skeleton</div>`} />
|
||||
<CodeBlock language="css" code={`/* Using Tailwind @apply */\n.example { @apply text-primary-500; }`} />
|
||||
<CodeBlock
|
||||
language="css"
|
||||
|
||||
@@ -118,7 +118,7 @@ export let parameters: Record<string, string> = { foo: 'bar' };
|
||||
<h3 class="h3">Tailwind Class Props</h3>
|
||||
<p>
|
||||
For props that pass one or more CSS utility classes, make sure to import and append the <code class="code">CSSClasses</code> type.
|
||||
This resolve to a type of <code class="code">string</code> and allows our build process to identify props that support Tailwind Intellisense.
|
||||
This resolves to a type of <code class="code">string</code> and allows our build process to identify props that support Tailwind Intellisense.
|
||||
</p>
|
||||
<CodeBlock language="typescript" code={`import type { CssClasses } from '../..';`} />
|
||||
<CodeBlock
|
||||
@@ -216,7 +216,7 @@ $: classesLabel = \`\${cBaseLabel}\`; // child element
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Dynamic Transitions</h2>
|
||||
<p>
|
||||
Skeleton has a convention for implement dynamic transitions within components. Please follow the guidelines below to ensure you are
|
||||
Skeleton has a convention for implementing dynamic transitions within components. Please follow the guidelines below to ensure you are
|
||||
following our standard for this process.
|
||||
</p>
|
||||
<blockquote class="blockquote">
|
||||
|
||||
@@ -213,7 +213,7 @@ body { background: red; }
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn variant-filled-secondary font-bold" href="https://csshero.org/mesher/" target="_blank" rel="noreferrer">
|
||||
<a class="btn variant-filled font-bold" href="https://csshero.org/mesher/" target="_blank" rel="noreferrer">
|
||||
<span>Create a Mesh</span>
|
||||
<i class="fa-solid fa-arrow-up-right-from-square" />
|
||||
</a>
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
</p>
|
||||
<h3 class="h3">Force Enable</h3>
|
||||
<p>
|
||||
For components with subtle transitions, you may choose to override this behavior by applying a property of <code class="code">{`transition={true}`}</code> to the component. We encourage you to use this setting with caution though.
|
||||
For components with subtle transitions, you may choose to override this behavior by applying a property of <code class="code">{`transitions={true}`}</code> to the component. We encourage you to use this setting with caution though.
|
||||
</p>
|
||||
<h3 class="h3">Store</h3>
|
||||
<p>
|
||||
|
||||
@@ -280,8 +280,26 @@ modalStore.trigger(modal);
|
||||
</section>
|
||||
<!-- Modal Settings -->
|
||||
<section class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-[1fr_auto] gap-4 items-center">
|
||||
<div class="space-y-4">
|
||||
<h2 class="h2">Modal Settings</h2>
|
||||
<p>These additional settings are available to all modals.</p>
|
||||
<p>
|
||||
Define settings <u>per modal instance</u> via the <code class="code">trigger()</code> method. These are similar to modal properties,
|
||||
but do not provide the same breath of options.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<a
|
||||
class="btn variant-filled font-bold"
|
||||
href="https://github.com/skeletonlabs/skeleton/blob/master/packages/skeleton/src/lib/utilities/Modal/types.ts#L14"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<span>Available Settings</span>
|
||||
<i class="fa-solid fa-arrow-up-right-from-square" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- prettier-ignore -->
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
@@ -295,6 +313,13 @@ const modal: ModalSettings = {\n
|
||||
};`}
|
||||
/>
|
||||
</section>
|
||||
<!-- Modal Properties -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Modal Properties</h2>
|
||||
<p>
|
||||
Define <u>global settings</u> for all modal instances. Tap the "Props" tab at the top of the page for a full list of options.
|
||||
</p>
|
||||
</section>
|
||||
<!-- Async Response -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Async Response</h2>
|
||||
@@ -325,7 +350,8 @@ new Promise<boolean>((resolve) => {
|
||||
<h2 class="h2">Component Modals</h2>
|
||||
<span class="badge variant-filled-warning">Advanced</span>
|
||||
</div>
|
||||
<p>To create custom modals, generate a new component, then pass this to the Modal system.</p>
|
||||
<p>Skeleton allows you to generate custom modals using Svelte components.</p>
|
||||
<h3 class="sr-only">Example Modals</h3>
|
||||
<DocsPreview background="neutral">
|
||||
<svelte:fragment slot="preview">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 md:max-w-[480px] mx-auto">
|
||||
@@ -335,89 +361,70 @@ new Promise<boolean>((resolve) => {
|
||||
<button class="btn variant-filled" on:click={modalComponentImage}>Image</button>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<svelte:fragment slot="source">
|
||||
<div class="text-center">
|
||||
<!-- prettier-ignore -->
|
||||
<a class="btn variant-ghost" href="https://github.com/skeletonlabs/skeleton/tree/master/sites/skeleton.dev/src/lib/modals/examples" target="_blank" rel="noreferrer">View Source Code</a>
|
||||
<a
|
||||
class="btn variant-ghost"
|
||||
href="https://github.com/skeletonlabs/skeleton/tree/master/sites/skeleton.dev/src/lib/modals/examples"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<i class="fa-brands fa-github" />
|
||||
<span>View Source on GitHub</span>
|
||||
<i class="fa-solid fa-arrow-up-right-from-square" />
|
||||
</a>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<TabGroup regionPanel="space-y-4">
|
||||
<Tab bind:group={tabCustom} name="component-modals" value="register">Reusable Registry</Tab>
|
||||
<Tab bind:group={tabCustom} name="component-modals" value="direct">Direct Method</Tab>
|
||||
</DocsPreview>
|
||||
<h3 class="h3">Choose a Method</h3>
|
||||
<TabGroup regionList="!grid grid-cols-2" regionPanel="space-y-4">
|
||||
<Tab bind:group={tabCustom} name="component-modals" value="register">Registry (recommended)</Tab>
|
||||
<Tab bind:group={tabCustom} name="component-modals" value="direct">Direct</Tab>
|
||||
<!-- Panel -->
|
||||
<svelte:fragment slot="panel">
|
||||
{#if tabCustom === 'register'}
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
Import custom components in your root layout, create a modal registry object, then pass this object to the Modal <code class="code">components</code> property.
|
||||
This will create a set of reusable custom modals that are globally avialable to your application. Add the following to your
|
||||
your root layout in <code class="code">/src/routes/+layout.svelte</code>.
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
// import ModalComponentOne from '...';
|
||||
// import ModalComponentTwo from '...';\n
|
||||
const modalComponentRegistry: Record<string, ModalComponent> = {\n
|
||||
// Custom Modal 1
|
||||
modalComponentOne: {
|
||||
// Pass a reference to your custom component
|
||||
ref: ModalComponentOne,
|
||||
// Add the component properties as key/value pairs
|
||||
props: { background: 'bg-red-500' },
|
||||
// Provide a template literal for the default component slot
|
||||
slot: '<p>Skeleton</p>'
|
||||
},\n
|
||||
// Custom Modal 2
|
||||
modalComponentTwo: { ref: ModalComponentTwo },\n
|
||||
import ModalComponentOne from '/example/path/here';
|
||||
import ModalComponentTwo from '/example/path/here';\n
|
||||
const modalRegistry: Record<string, ModalComponent> = {
|
||||
// Set a unique modal ID, then pass the component reference
|
||||
modalComponentOne: { ref: ModalComponentOne },
|
||||
modalComponentTwo: { ref: ModalComponentTwo },
|
||||
// ...
|
||||
};
|
||||
`}
|
||||
/>
|
||||
<CodeBlock language="html" code={`<Modal components={modalComponentRegistry} />`} />
|
||||
<p>Provide the <code class="code">modalRegistry</code> to the modal component, which also resides in your root layout.</p>
|
||||
<CodeBlock language="html" code={`<Modal components={modalRegistry} />`} />
|
||||
<p>
|
||||
When triggering a component, pass <code class="code">component: string</code>, where the value represents the registry
|
||||
object key.
|
||||
Then, when triggering a new component, set the value of <code class="code">component</code> to the unique modal ID as registered
|
||||
above.
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
const modal: ModalSettings = {
|
||||
type: 'component',
|
||||
// Pass the component registry key as a string:
|
||||
component: 'modalComponentOne',
|
||||
};
|
||||
modalStore.trigger(modal);
|
||||
`}
|
||||
/>
|
||||
{:else if tabCustom === 'direct'}
|
||||
<p>
|
||||
For one-off components you can create a <code class="code">ModalComponent</code> object containing your component, props, and
|
||||
slot values.
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
// import MyCustomComponent from '...';\n
|
||||
const modalComponent: ModalComponent = {
|
||||
// Pass a reference to your custom component
|
||||
ref: MyCustomComponent,
|
||||
// Add the component properties as key/value pairs
|
||||
props: { background: 'bg-red-500' },
|
||||
// Provide a template literal for the default component slot
|
||||
slot: '<p>Skeleton</p>'
|
||||
};
|
||||
`}
|
||||
/>
|
||||
<p>
|
||||
When triggering a component, pass the <code class="code">component: ModalComponent</code> directly to
|
||||
<code class="code">ModalSettings</code>.
|
||||
</p>
|
||||
<p>This will implement a single component for a one-off modal instance.</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
import MyCustomComponent from '/example/path/here';\n
|
||||
const modalComponent: ModalComponent = { ref: MyCustomComponent };\n
|
||||
const modal: ModalSettings = {
|
||||
type: 'component',
|
||||
// Pass the component directly:
|
||||
component: modalComponent,
|
||||
};
|
||||
modalStore.trigger(modal);
|
||||
@@ -426,68 +433,169 @@ modalStore.trigger(modal);
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</TabGroup>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
<p>See the additional information below to learn how to use custom component modals.</p>
|
||||
<h3 class="h3">Creating a Component</h3>
|
||||
<p>Learn more about how to construct a custom modal component via the tips below.</p>
|
||||
<Accordion autocollapse class="card variant-glass p-4">
|
||||
<AccordionItem open>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Accessing Store Data</h3></svelte:fragment>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Accessing Modal Store Data</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
Import and use the <code class="code">modalStore</code>. All provided data is available within your component via
|
||||
<code class="code">$modalStore[0]</code>.
|
||||
</p>
|
||||
<p>When creating a custom component, make sure to import the modal store. This should proceed all following tips.</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
import { getModalStore } from '@skeletonlabs/skeleton';\n
|
||||
const modalStore = getModalStore();
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>The Visible Modal</h3></svelte:fragment>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Determining the Active Modal</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>The foremost and visible modal in your queue uses index zero <code class="code">$modalStore[0]</code>.</p>
|
||||
<p>
|
||||
The active and visible modal in your queue is always available at the zero index: <code class="code">$modalStore[0]</code>.
|
||||
</p>
|
||||
<CodeBlock language="ts" code={`if ($modalStore[0]) console.log($modalStore[0].title);`} />
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Modal Parent Properties</h3></svelte:fragment>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Template Conditional</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
The Modal component in your root layout is considered the "parent" component. Your custom modal component will be generated
|
||||
within this. All properties for the parent component are passed down via the <code class="code">parent</code> prop. For
|
||||
example
|
||||
<code class="code">parent.background</code> would provide the <em>background</em> property value.
|
||||
Wrap your component template markup in an <code class="code">#if</code> statement before accessing modal store values.
|
||||
</p>
|
||||
<p>Tap the <em>Props</em> tab on this page to view a full list of available <code class="code">parent</code> props.</p>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
{#if $modalStore[0]}
|
||||
<header>{$modalStore[0].title}</header>
|
||||
<article>{$modalStore[0].body}</article>
|
||||
{/if}
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Triggering a Response</h3></svelte:fragment>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Inherit Modal Component Props</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
Use the <code class="code">$modalStore[0].response('myResponseDataHere');</code> trigger the response function and return a value.
|
||||
The modal component in your root layout is considered the "parent" component, which includes a set of global configuration
|
||||
properties. For example:
|
||||
</p>
|
||||
<CodeBlock language="html" code={`<Modal background="bg-green-500" />`} />
|
||||
<blockquote class="blockquote">
|
||||
TIP: tap the <u>Props</u> tab at the top of this page for a full list of available properties.
|
||||
</blockquote>
|
||||
<p>
|
||||
Custom modal components are then generated within this using <code class="code">svelte:component</code>. By default, Skeleton
|
||||
provides all parent props to your custom component using a
|
||||
<code class="code">parent</code>
|
||||
prop. This can be enabled by adding the following to your custom component.
|
||||
</p>
|
||||
<CodeBlock language="ts" code={`export let parent: any;`} />
|
||||
<p>You may then access and use any parent value.</p>
|
||||
<CodeBlock language="html" code={`<pre class="pre">{JSON.stringify(parent)}</pre>`} />
|
||||
<CodeBlock language="html" code={`<pre class="pre">background: {parent.background}</pre>`} />
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Returning a Response Value</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
Use the <code class="code">$modalStore[0].response()</code> callback method to return a modal response value.
|
||||
</p>
|
||||
<CodeBlock language="ts" code={`$modalStore[0].response({ foo: 'bar' });`} />
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Passing Arbitrary Data</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>When triggering a component, use the Modal Settings <code class="code">meta</code> key to pass arbitrary data.</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
const modal: ModalSettings = {
|
||||
// ...
|
||||
meta: { foo: 'bar', fizz: 'buzz', fn: myCustomFunction }
|
||||
};
|
||||
`}
|
||||
/>
|
||||
<p>You can then use <code class="code">$modalStore[0].meta</code> within your custom component to retrieve this data.</p>
|
||||
<CodeBlock language="html" code={`<pre class="pre">{JSON.stringify($modalStore[0].meta)}</pre>`} />
|
||||
<CodeBlock language="html" code={`<pre class="pre">foo: {$modalStore[0].meta.foo}</pre>`} />
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Component Props and Slots</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
When creating a <code class="code">ModalComponent</code> instance, you can <u>optionally</u> pass
|
||||
<code class="code">props</code>
|
||||
and default
|
||||
<code class="code">slot</code> values as shown.
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="ts"
|
||||
code={`
|
||||
import MyCustomComponent from '/example/path/here';\n
|
||||
const modalComponent: ModalComponent = {
|
||||
ref: MyCustomComponent
|
||||
props: { foo: 'bar' },
|
||||
slot: '<p>Skeleton</p>'
|
||||
};
|
||||
`}
|
||||
/>
|
||||
<p>These values be utilized per standard Svelte component conventions.</p>
|
||||
<CodeBlock language="ts" code={`export let foo = 'fizzbuzz';`} />
|
||||
<CodeBlock language="html" code={`<slot />`} />
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Setting the Modal Width</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
Skeleton's modal component uses a <code class="code">width</code> property, which can use either an arbitrary width class, or any
|
||||
of the canned options provided below:
|
||||
</p>
|
||||
<ul class="list-disc list-outside ml-4 space-y-1">
|
||||
<li><code class="code">.w-modal</code> - the standard modal size.</li>
|
||||
<li><code class="code">.w-modal-slim</code> - the smaller modal width.</li>
|
||||
<li><code class="code">.w-modal-wide</code> - the larger modal width.</li>
|
||||
</ul>
|
||||
<p>Your custom component can either inherit this from the <code class="code">parent.width</code> property.</p>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
{#if $modalStore[0]}
|
||||
<div class="{parent.width}">
|
||||
<h2 class="h2">Wide Modal</h2>
|
||||
<p>This will be a wide modal.</p>
|
||||
</div>
|
||||
{/if}
|
||||
`}
|
||||
/>
|
||||
<p>Likewise these classes can be defined directly into the template.</p>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
{#if $modalStore[0]}
|
||||
<div class="w-modal-wide">
|
||||
<h2 class="h2">Wide Modal</h2>
|
||||
<p>This will be a wide modal.</p>
|
||||
</div>
|
||||
{/if}
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Closing a Modal</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>
|
||||
Use the <code class="code">parent.onClose()</code> or <code class="code">modalStore.close()</code> methods to close the modal.
|
||||
</p>
|
||||
<p>You can use either method below to close self close the modal. Use this for "cancel" actions.</p>
|
||||
<CodeBlock language="ts" code={`parent.onClose();`} />
|
||||
<CodeBlock language="ts" code={`modalStore.close();`} />
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Accessing Metadata</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<p>Arbitrary metadata is available using <code class="code">$modalStore[0].meta?.someKey</code>.</p>
|
||||
</svelte:fragment>
|
||||
</AccordionItem>
|
||||
<AccordionItem>
|
||||
<svelte:fragment slot="summary"><h3 class="h3" data-toc-ignore>Standardize Modal Widths</h3></svelte:fragment>
|
||||
<svelte:fragment slot="content">
|
||||
<!-- prettier-ignore -->
|
||||
<p>
|
||||
Standard <em>alert</em>, <em>confirm</em>, and <em>prompt</em> modals include a <code class="code">width</code> property to adjust the width size. If you wish to resize your custom component modals, apply the Tailwind width utility classes directly within your component markup. To help standardize modal widths we provided three canned options: <code class="code">.w-modal</code>, <code class="code">.w-modal-slim</code>, and <code class="code">.w-modal-wide</code>.
|
||||
</svelte:fragment
|
||||
>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</section>
|
||||
<section class="space-y-4">
|
||||
|
||||
@@ -530,6 +530,50 @@ const popupState: PopupSettings = {
|
||||
|
||||
<hr />
|
||||
|
||||
<!-- Handling Loops -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Handling Loops</h2>
|
||||
<p>
|
||||
Popups maintain a 1-1 relationship between the trigger and the popup element. This means when using <code class="code">#each</code> block
|
||||
to iterate and create a set of popups, you must provide a unique popup element and popup settings.
|
||||
</p>
|
||||
<DocsPreview background="neutral" regionPreview="text-token">
|
||||
<svelte:fragment slot="preview">
|
||||
<div class="grid grid-cols-1 gap-2">
|
||||
{#each ['A', 'B', 'C'] as label, i}
|
||||
<!-- Trigger -->
|
||||
<button class="btn variant-filled" use:popup={{ event: 'click', target: 'loopExample-' + i, placement: 'top' }}>
|
||||
Show {label}
|
||||
</button>
|
||||
<!-- Popup -->
|
||||
<div class="card p-4 shadow-xl" data-popup="loopExample-{i}">Popup {label}</div>
|
||||
{/each}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="source">
|
||||
<p>
|
||||
Inline popup settings for each <code class="code">use:popup</code> directive, and take note of how the index
|
||||
<code class="code">i</code>
|
||||
is appended to both <code class="code">target</code> and
|
||||
<code class="code">data-popup</code>.
|
||||
</p>
|
||||
<CodeBlock
|
||||
language="html"
|
||||
code={`
|
||||
{#each ['A', 'B', 'C'] as label, i}
|
||||
<!-- Trigger -->
|
||||
<button use:popup={{ event: 'click', target: 'loopExample-' + i, placement: 'top' }}>
|
||||
Show {label}
|
||||
</button>
|
||||
<!-- Popup -->
|
||||
<div data-popup="loopExample-{i}">Popup {label}</div>
|
||||
{/each}
|
||||
`}
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</DocsPreview>
|
||||
</section>
|
||||
|
||||
<!-- Combobox -->
|
||||
<section class="space-y-4">
|
||||
<h2 class="h2">Combobox</h2>
|
||||
|
||||
@@ -54,6 +54,10 @@
|
||||
import '$lib/styles/blog.css';
|
||||
// Global Stylesheets
|
||||
import '../app.postcss';
|
||||
// Font Awesome
|
||||
import '@fortawesome/fontawesome-free/css/fontawesome.css';
|
||||
import '@fortawesome/fontawesome-free/css/brands.css';
|
||||
import '@fortawesome/fontawesome-free/css/solid.css';
|
||||
|
||||
// Handle Vercel Production Mode
|
||||
import type { LayoutServerData } from './$types';
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +0,0 @@
|
||||
/*!
|
||||
* Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2023 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { purgeCss } from 'vite-plugin-tailwind-purgecss';
|
||||
import type { UserConfig } from 'vite';
|
||||
import skeletonPluginWatcher from './vite-plugin-skeleton-plugin-watcher';
|
||||
import sveld from './vite-plugin-sveld';
|
||||
@@ -9,7 +10,17 @@ const json = readFileSync('../../packages/skeleton/package.json', 'utf8');
|
||||
const pkg = JSON.parse(json);
|
||||
|
||||
const config: UserConfig = {
|
||||
plugins: [sveltekit(), sveld(), skeletonPluginWatcher()],
|
||||
plugins: [
|
||||
sveltekit(),
|
||||
sveld(),
|
||||
skeletonPluginWatcher(),
|
||||
purgeCss({
|
||||
safelist: {
|
||||
// any selectors that begin with "hljs-" will not be purged
|
||||
greedy: [/^hljs-/]
|
||||
}
|
||||
})
|
||||
],
|
||||
define: {
|
||||
__PACKAGE__: pkg
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user