svseeds 0.5.3 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +1 -1
  2. package/_svseeds/Accordion.svelte +119 -83
  3. package/_svseeds/Accordion.svelte.d.ts +56 -27
  4. package/_svseeds/Button.svelte +116 -0
  5. package/_svseeds/Button.svelte.d.ts +55 -0
  6. package/_svseeds/Calendar.svelte +349 -0
  7. package/_svseeds/Calendar.svelte.d.ts +113 -0
  8. package/_svseeds/CheckField.svelte +228 -0
  9. package/_svseeds/CheckField.svelte.d.ts +80 -0
  10. package/_svseeds/ColorPicker.svelte +123 -0
  11. package/_svseeds/ColorPicker.svelte.d.ts +56 -0
  12. package/_svseeds/ComboBox.svelte +278 -0
  13. package/_svseeds/ComboBox.svelte.d.ts +72 -0
  14. package/_svseeds/ContextMenu.svelte +129 -0
  15. package/_svseeds/ContextMenu.svelte.d.ts +53 -0
  16. package/_svseeds/DarkToggle.svelte +125 -52
  17. package/_svseeds/DarkToggle.svelte.d.ts +38 -18
  18. package/_svseeds/DateField.svelte +278 -0
  19. package/_svseeds/DateField.svelte.d.ts +93 -0
  20. package/_svseeds/DateInput.svelte +347 -0
  21. package/_svseeds/DateInput.svelte.d.ts +105 -0
  22. package/_svseeds/Disclosure.svelte +233 -0
  23. package/_svseeds/Disclosure.svelte.d.ts +76 -0
  24. package/_svseeds/Drawer.svelte +179 -0
  25. package/_svseeds/Drawer.svelte.d.ts +62 -0
  26. package/_svseeds/FileField.svelte +276 -0
  27. package/_svseeds/FileField.svelte.d.ts +95 -0
  28. package/_svseeds/FileInput.svelte +279 -0
  29. package/_svseeds/FileInput.svelte.d.ts +103 -0
  30. package/_svseeds/HotkeyCapture.svelte +189 -0
  31. package/_svseeds/HotkeyCapture.svelte.d.ts +71 -0
  32. package/_svseeds/Image.svelte +211 -0
  33. package/_svseeds/Image.svelte.d.ts +83 -0
  34. package/_svseeds/MenuGroup.svelte +80 -0
  35. package/_svseeds/MenuGroup.svelte.d.ts +52 -0
  36. package/_svseeds/MenuItem.svelte +120 -0
  37. package/_svseeds/MenuItem.svelte.d.ts +63 -0
  38. package/_svseeds/MenuList.svelte +227 -0
  39. package/_svseeds/MenuList.svelte.d.ts +92 -0
  40. package/_svseeds/MenuSeparator.svelte +70 -0
  41. package/_svseeds/MenuSeparator.svelte.d.ts +44 -0
  42. package/_svseeds/Modal.svelte +121 -0
  43. package/_svseeds/Modal.svelte.d.ts +54 -0
  44. package/_svseeds/MonthPicker.svelte +172 -0
  45. package/_svseeds/MonthPicker.svelte.d.ts +66 -0
  46. package/_svseeds/NumberField.svelte +213 -0
  47. package/_svseeds/NumberField.svelte.d.ts +79 -0
  48. package/_svseeds/NumberInput.svelte +395 -0
  49. package/_svseeds/NumberInput.svelte.d.ts +95 -0
  50. package/_svseeds/Pagination.svelte +271 -0
  51. package/_svseeds/Pagination.svelte.d.ts +78 -0
  52. package/_svseeds/Popover.svelte +215 -0
  53. package/_svseeds/Popover.svelte.d.ts +79 -0
  54. package/_svseeds/ProgressTracker.svelte +124 -0
  55. package/_svseeds/{_ProgressTracker.svelte.d.ts → ProgressTracker.svelte.d.ts} +23 -12
  56. package/_svseeds/SelectField.svelte +213 -0
  57. package/_svseeds/{_SelectField.svelte.d.ts → SelectField.svelte.d.ts} +31 -20
  58. package/_svseeds/Slider.svelte +139 -0
  59. package/_svseeds/Slider.svelte.d.ts +75 -0
  60. package/_svseeds/Sortable.svelte +801 -0
  61. package/_svseeds/Sortable.svelte.d.ts +164 -0
  62. package/_svseeds/SortableGroup.svelte +87 -0
  63. package/_svseeds/SortableGroup.svelte.d.ts +52 -0
  64. package/_svseeds/Splitter.svelte +194 -0
  65. package/_svseeds/Splitter.svelte.d.ts +71 -0
  66. package/_svseeds/Tabs.svelte +230 -0
  67. package/_svseeds/Tabs.svelte.d.ts +72 -0
  68. package/_svseeds/TagsInput.svelte +260 -0
  69. package/_svseeds/TagsInput.svelte.d.ts +103 -0
  70. package/_svseeds/TagsInputField.svelte +118 -84
  71. package/_svseeds/TagsInputField.svelte.d.ts +31 -35
  72. package/_svseeds/TextField.svelte +238 -0
  73. package/_svseeds/TextField.svelte.d.ts +93 -0
  74. package/_svseeds/Toast.svelte +290 -0
  75. package/_svseeds/Toast.svelte.d.ts +87 -0
  76. package/_svseeds/Toggle.svelte +131 -0
  77. package/_svseeds/Toggle.svelte.d.ts +57 -0
  78. package/_svseeds/ToggleGroup.svelte +231 -0
  79. package/_svseeds/ToggleGroup.svelte.d.ts +76 -0
  80. package/_svseeds/ToggleGroupField.svelte +138 -66
  81. package/_svseeds/ToggleGroupField.svelte.d.ts +51 -29
  82. package/_svseeds/Tooltip.svelte +342 -0
  83. package/_svseeds/Tooltip.svelte.d.ts +74 -0
  84. package/_svseeds/WheelPicker.svelte +326 -0
  85. package/_svseeds/WheelPicker.svelte.d.ts +84 -0
  86. package/_svseeds/_core.d.ts +239 -0
  87. package/_svseeds/_core.js +290 -0
  88. package/index.d.ts +43 -24
  89. package/index.js +40 -21
  90. package/package.json +3 -3
  91. package/_svseeds/_Button.svelte +0 -92
  92. package/_svseeds/_Button.svelte.d.ts +0 -49
  93. package/_svseeds/_CheckField.svelte +0 -182
  94. package/_svseeds/_CheckField.svelte.d.ts +0 -71
  95. package/_svseeds/_ColorPicker.svelte +0 -107
  96. package/_svseeds/_ColorPicker.svelte.d.ts +0 -51
  97. package/_svseeds/_ComboBox.svelte +0 -163
  98. package/_svseeds/_ComboBox.svelte.d.ts +0 -52
  99. package/_svseeds/_ContextMenu.svelte +0 -82
  100. package/_svseeds/_ContextMenu.svelte.d.ts +0 -37
  101. package/_svseeds/_Disclosure.svelte +0 -148
  102. package/_svseeds/_Disclosure.svelte.d.ts +0 -48
  103. package/_svseeds/_Drawer.svelte +0 -130
  104. package/_svseeds/_Drawer.svelte.d.ts +0 -48
  105. package/_svseeds/_HotkeyCapture.svelte +0 -113
  106. package/_svseeds/_HotkeyCapture.svelte.d.ts +0 -34
  107. package/_svseeds/_Modal.svelte +0 -107
  108. package/_svseeds/_Modal.svelte.d.ts +0 -44
  109. package/_svseeds/_ProgressTracker.svelte +0 -101
  110. package/_svseeds/_SelectField.svelte +0 -190
  111. package/_svseeds/_Slider.svelte +0 -104
  112. package/_svseeds/_Slider.svelte.d.ts +0 -62
  113. package/_svseeds/_Sortable.svelte +0 -795
  114. package/_svseeds/_Sortable.svelte.d.ts +0 -111
  115. package/_svseeds/_Tabs.svelte +0 -118
  116. package/_svseeds/_Tabs.svelte.d.ts +0 -42
  117. package/_svseeds/_TagsInput.svelte +0 -155
  118. package/_svseeds/_TagsInput.svelte.d.ts +0 -81
  119. package/_svseeds/_TextField.svelte +0 -206
  120. package/_svseeds/_TextField.svelte.d.ts +0 -81
  121. package/_svseeds/_Toggle.svelte +0 -112
  122. package/_svseeds/_Toggle.svelte.d.ts +0 -51
  123. package/_svseeds/_ToggleGroup.svelte +0 -102
  124. package/_svseeds/_ToggleGroup.svelte.d.ts +0 -49
  125. package/_svseeds/_Tooltip.svelte +0 -296
  126. package/_svseeds/_Tooltip.svelte.d.ts +0 -60
  127. package/_svseeds/core.d.ts +0 -229
  128. package/_svseeds/core.js +0 -1
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # SvSeeds - Simple Components for Svelte
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/svseeds)](https://www.npmjs.com/package/svseeds)
4
- [![JSR](https://jsr.io/badges/@svseeds/ui)](https://jsr.io/@svseeds/ui)
4
+ [![JSR Version](https://img.shields.io/jsr/v/%40svseeds/ui)](https://jsr.io/@svseeds/ui)
5
5
  [![license](https://img.shields.io/npm/l/svseeds)](LICENSE.md)
6
6
 
7
7
  SvSeeds is a collection of essential functional components for Svelte development. These components are intentionally designed with minimal styling, giving you complete freedom to apply your own styles based on your project requirements. For specialized needs, you can download the components as .svelte files through our CLI tool and customize their functionality directly.
@@ -1,123 +1,159 @@
1
1
  <!--
2
2
  @component
3
+ ### Usage
4
+ Wrap one or more `Disclosure` children.
5
+ ```svelte
6
+ <Accordion {...props}>
7
+ <Disclosure label="Section 1" {...props}>Content</Disclosure>
8
+ <Disclosure label="Section 2" {...props}>Content</Disclosure>
9
+ </Accordion>
10
+ ```
3
11
  ### Types
4
12
  default value: *`(value)`*
5
13
  ```ts
6
14
  interface AccordionProps {
7
- labels?: string[];
8
- current?: number; // bindable (-1)
15
+ items?: AccordionItem[];
16
+ children?: Snippet;
17
+ current?: string; // bindable, undefined = all closed
9
18
  styling?: SVSClass;
10
- variant?: string; // bindable (VARIANT.NEUTRAL)
11
- deps?: AccordionDeps;
12
- [key: string]: unknown | Snippet; // labels or contents of each disclosure
13
- }
14
- interface AccordionDeps {
15
- svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
19
+ variant?: SVSVariant; // (VARIANT.NEUTRAL)
20
+ disclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps | "id" | "inactive">;
16
21
  }
22
+ type AccordionComponent = { component: Component<any>; props?: Record<string, unknown> };
23
+ type AccordionItem = {
24
+ value: string; // REQUIRED, unique within `items`. Addresses `current`.
25
+ label: string | Snippet<[boolean, string]> | AccordionComponent;
26
+ panel: Snippet<[string]> | AccordionComponent;
27
+ disabled?: boolean; // (false)
28
+ };
17
29
  ```
30
+ `value`s must be unique within `items`. Accordion is exclusive: at most one item is open at a time.
31
+ Provide `items` for data mode or `children` for declarative mode; `children` wins when both are present.
18
32
  ### Anatomy
19
33
  ```svelte
20
- <div class="whole">
21
- {#each labels as label, i}
22
- <Disclosure {label}>
23
- {@render restProps[i]()}
24
- </Disclosure>
25
- {/each}
34
+ <div class="whole" role="group">
35
+ {#if children}
36
+ {children}
37
+ {:else}
38
+ {#each items as item}
39
+ <Disclosure id={item.value} label={item.label} inactive={item.disabled || undefined}>
40
+ {item.panel}
41
+ </Disclosure>
42
+ {/each}
43
+ {/if}
26
44
  </div>
27
45
  ```
46
+ Data-mode child styling defaults to the combined parent/child preset and can be customized via `disclosure`.
47
+ ### Behavior
48
+ Declarative `<Disclosure id=...>` children automatically coordinate exclusive open state through `current` and inherit the base `variant`/`styling`.
28
49
  -->
29
50
  <script module lang="ts">
30
51
  export interface AccordionProps {
31
- labels?: string[];
32
- current?: number; // bindable (-1)
52
+ items?: AccordionItem[];
53
+ children?: Snippet;
54
+ current?: string; // bindable, undefined = all closed
33
55
  styling?: SVSClass;
34
- variant?: string; // bindable (VARIANT.NEUTRAL)
35
- deps?: AccordionDeps;
36
- [key: string]: unknown | Snippet; // labels or contents of each disclosure
37
- }
38
- export interface AccordionDeps {
39
- svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
56
+ variant?: SVSVariant; // (VARIANT.NEUTRAL)
57
+ disclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps | "id" | "inactive">;
40
58
  }
41
59
  export type AccordionReqdProps = never;
42
- export type AccordionBindProps = "current" | "variant";
60
+ export type AccordionBindProps = "current";
61
+ export type AccordionComponent = { component: Component<any>; props?: Record<string, unknown> };
62
+ export type AccordionItem = {
63
+ value: string; // REQUIRED, unique within `items`. Addresses `current`.
64
+ label: string | Snippet<[boolean, string]> | AccordionComponent;
65
+ panel: Snippet<[string]> | AccordionComponent;
66
+ disabled?: boolean; // (false)
67
+ };
43
68
 
44
- type NamedId = { id: string, name: string };
45
- const preset = "svs-accordion";
46
- const roleLabel = "label";
47
- const rolePanel = "panel";
69
+ export const _ACCORDION_PRESET = "svs-accordion";
48
70
 
49
- function getSnippetNames(role: string, rest: Record<string, unknown>): string[] {
50
- return Object.keys(rest)
51
- .filter((x) => x.startsWith(role) && typeof rest[x] === "function")
52
- .toSorted((x,y) => x.localeCompare(y));
53
- }
54
- function toNamedId(names: string[]): NamedId[] {
55
- return names.map((x) => ({ id: elemId.id, name: x }));
71
+ function isComponent(x: unknown): x is AccordionComponent {
72
+ return typeof x === "object" && x !== null && "component" in x;
56
73
  }
57
74
 
58
- import { type Snippet, untrack } from "svelte";
59
- import { type SVSClass, VARIANT, PARTS, elemId, fnClass, omit } from "./core";
60
- import Disclosure, { type DisclosureProps, type DisclosureReqdProps, type DisclosureBindProps } from "./_Disclosure.svelte";
75
+ import { untrack } from "svelte";
76
+ import { VARIANT, PARTS, _fnClass, _omit } from "./_core";
77
+ import Disclosure, { _DISCLOSURE_PRESET, _setDisclosureContext } from "./Disclosure.svelte";
78
+ import type { Component, Snippet } from "svelte";
79
+ import type { SVSClass, SVSVariant } from "./_core";
80
+ import type { DisclosureContext, DisclosureProps, DisclosureReqdProps, DisclosureBindProps } from "./Disclosure.svelte";
61
81
  </script>
62
82
 
63
83
  <script lang="ts">
64
- let { labels = [], current = $bindable(-1), styling, variant = $bindable(""), deps, ...rest }: AccordionProps = $props();
84
+ // prettier-ignore
85
+ let { items, children, current = $bindable<string | undefined>(), styling, variant = VARIANT.NEUTRAL, disclosure }: AccordionProps = $props();
65
86
 
66
87
  // *** Initialize *** //
67
- if (!variant) variant = VARIANT.NEUTRAL;
68
- const cls = fnClass(preset, styling);
69
- const isStrLabel = labels.length > 0;
70
- const lbls = toNamedId(isStrLabel ? labels : getSnippetNames(roleLabel, rest));
71
- const panels = getSnippetNames(rolePanel, rest);
72
- const isValidAccordion = lbls.length && panels.length && lbls.length === panels.length;
73
- let guard = current >= 0 && current < lbls.length;
74
- let event = false;
75
- let opens = $state(Array(lbls.length).fill(false));
76
- let elems: HTMLDetailsElement[] = $state([]);
77
-
78
- // *** Initialize Deps *** //
79
- const svsDisclosure = {
80
- ...omit(deps?.svsDisclosure, "attributes", "styling"),
81
- styling: deps?.svsDisclosure?.styling ?? `${preset} svs-disclosure`,
88
+ const cls = $derived(_fnClass(_ACCORDION_PRESET, styling));
89
+ const ctxCurrent = $derived(current);
90
+ const ctxVariant = $derived(variant);
91
+ const ctxStyling = $derived(styling);
92
+ const ctx: DisclosureContext = {
93
+ get current() {
94
+ return ctxCurrent;
95
+ },
96
+ set current(v: string | undefined) {
97
+ current = v;
98
+ },
99
+ get variant() {
100
+ return ctxVariant;
101
+ },
102
+ get styling() {
103
+ return ctxStyling;
104
+ },
82
105
  };
106
+ _setDisclosureContext(ctx);
107
+ const selected = $derived(children || !items ? current : items.some((it) => it.value === current && !it.disabled) ? current : undefined);
83
108
 
84
- // *** Bind Handlers *** //
85
- $effect.pre(() => {
86
- current;
87
- untrack(() => toggle());
109
+ // *** Initialize Child Props *** //
110
+ const childProps = $derived({
111
+ ..._omit(disclosure, "styling"),
112
+ styling: disclosure?.styling ?? `${_ACCORDION_PRESET} ${_DISCLOSURE_PRESET}`,
88
113
  });
89
- function toggle() {
90
- if (!event) opens = opens.fill(false).map((_, i) => i === current);
91
- event = false;
92
- }
93
- function setCurrent(index: number) {
94
- current = index;
95
- if (guard) return guard = false;
96
- event = true;
97
- }
98
114
 
99
- // *** Event Handlers *** //
100
- function exclusiveToggle(index: number): (ev: Event) => void {
101
- return (ev) => {
102
- deps?.svsDisclosure?.attributes?.ontoggle?.(ev as any);
103
- if (opens.every((x) => !x)) return setCurrent(-1);
104
- if (elems[index].open) {
105
- opens = opens.map((_, i) => i === index);
106
- setCurrent(index);
107
- }
108
- };
109
- }
115
+ // *** Reactive Handlers *** //
116
+ $effect(() => {
117
+ if (current !== selected) untrack(() => (current = selected));
118
+ });
110
119
  </script>
111
120
 
112
121
  <!---------------------------------------->
113
122
 
114
- {#if isValidAccordion}
123
+ {#if children}
115
124
  <div class={cls(PARTS.WHOLE, variant)} role="group">
116
- {#each lbls as { id, name }, i (id)}
117
- {@const ontoggle = exclusiveToggle(i)}
118
- <Disclosure bind:open={opens[i]} bind:element={elems[i]} label={isStrLabel ? name : (rest[name] as Snippet)} attributes={{...deps?.svsDisclosure?.attributes, ontoggle}} {...svsDisclosure}>
119
- {@render (rest[panels[i]] as Snippet)()}
120
- </Disclosure>
125
+ {@render children()}
126
+ </div>
127
+ {:else if items && items.length > 0}
128
+ <div class={cls(PARTS.WHOLE, variant)} role="group">
129
+ {#each items as item (item.value)}
130
+ {#snippet label(open: boolean, variant: string)}
131
+ {@render labelContent(item.label, open, variant)}
132
+ {/snippet}
133
+ {#snippet panel(variant: string)}
134
+ {@render panelContent(item.panel, variant)}
135
+ {/snippet}
136
+ <Disclosure id={item.value} {label} children={panel} inactive={item.disabled || undefined} {variant} {...childProps} />
121
137
  {/each}
122
138
  </div>
123
139
  {/if}
140
+
141
+ {#snippet labelContent(c: string | Snippet<[boolean, string]> | AccordionComponent, open: boolean, variant: string)}
142
+ {#if typeof c === "string"}
143
+ {c}
144
+ {:else if isComponent(c)}
145
+ {@const C = c.component}
146
+ <C {...c.props} />
147
+ {:else}
148
+ {@render c(open, variant)}
149
+ {/if}
150
+ {/snippet}
151
+
152
+ {#snippet panelContent(c: Snippet<[string]> | AccordionComponent, variant: string)}
153
+ {#if isComponent(c)}
154
+ {@const C = c.component}
155
+ <C {...c.props} />
156
+ {:else}
157
+ {@render c(variant)}
158
+ {/if}
159
+ {/snippet}
@@ -1,46 +1,75 @@
1
1
  export interface AccordionProps {
2
- labels?: string[];
3
- current?: number;
2
+ items?: AccordionItem[];
3
+ children?: Snippet;
4
+ current?: string;
4
5
  styling?: SVSClass;
5
- variant?: string;
6
- deps?: AccordionDeps;
7
- [key: string]: unknown | Snippet;
8
- }
9
- export interface AccordionDeps {
10
- svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
6
+ variant?: SVSVariant;
7
+ disclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps | "id" | "inactive">;
11
8
  }
12
9
  export type AccordionReqdProps = never;
13
- export type AccordionBindProps = "current" | "variant";
14
- import { type Snippet } from "svelte";
15
- import { type SVSClass } from "./core";
16
- import { type DisclosureProps, type DisclosureReqdProps, type DisclosureBindProps } from "./_Disclosure.svelte";
10
+ export type AccordionBindProps = "current";
11
+ export type AccordionComponent = {
12
+ component: Component<any>;
13
+ props?: Record<string, unknown>;
14
+ };
15
+ export type AccordionItem = {
16
+ value: string;
17
+ label: string | Snippet<[boolean, string]> | AccordionComponent;
18
+ panel: Snippet<[string]> | AccordionComponent;
19
+ disabled?: boolean;
20
+ };
21
+ export declare const _ACCORDION_PRESET = "svs-accordion";
22
+ import type { Component, Snippet } from "svelte";
23
+ import type { SVSClass, SVSVariant } from "./_core";
24
+ import type { DisclosureProps, DisclosureReqdProps, DisclosureBindProps } from "./Disclosure.svelte";
17
25
  /**
26
+ * ### Usage
27
+ * Wrap one or more `Disclosure` children.
28
+ * ```svelte
29
+ * <Accordion {...props}>
30
+ * <Disclosure label="Section 1" {...props}>Content</Disclosure>
31
+ * <Disclosure label="Section 2" {...props}>Content</Disclosure>
32
+ * </Accordion>
33
+ * ```
18
34
  * ### Types
19
35
  * default value: *`(value)`*
20
36
  * ```ts
21
37
  * interface AccordionProps {
22
- * labels?: string[];
23
- * current?: number; // bindable (-1)
38
+ * items?: AccordionItem[];
39
+ * children?: Snippet;
40
+ * current?: string; // bindable, undefined = all closed
24
41
  * styling?: SVSClass;
25
- * variant?: string; // bindable (VARIANT.NEUTRAL)
26
- * deps?: AccordionDeps;
27
- * [key: string]: unknown | Snippet; // labels or contents of each disclosure
28
- * }
29
- * interface AccordionDeps {
30
- * svsDisclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps>;
42
+ * variant?: SVSVariant; // (VARIANT.NEUTRAL)
43
+ * disclosure?: Omit<DisclosureProps, DisclosureReqdProps | DisclosureBindProps | "id" | "inactive">;
31
44
  * }
45
+ * type AccordionComponent = { component: Component<any>; props?: Record<string, unknown> };
46
+ * type AccordionItem = {
47
+ * value: string; // REQUIRED, unique within `items`. Addresses `current`.
48
+ * label: string | Snippet<[boolean, string]> | AccordionComponent;
49
+ * panel: Snippet<[string]> | AccordionComponent;
50
+ * disabled?: boolean; // (false)
51
+ * };
32
52
  * ```
53
+ * `value`s must be unique within `items`. Accordion is exclusive: at most one item is open at a time.
54
+ * Provide `items` for data mode or `children` for declarative mode; `children` wins when both are present.
33
55
  * ### Anatomy
34
56
  * ```svelte
35
- * <div class="whole">
36
- * {#each labels as label, i}
37
- * <Disclosure {label}>
38
- * {@render restProps[i]()}
39
- * </Disclosure>
40
- * {/each}
57
+ * <div class="whole" role="group">
58
+ * {#if children}
59
+ * {children}
60
+ * {:else}
61
+ * {#each items as item}
62
+ * <Disclosure id={item.value} label={item.label} inactive={item.disabled || undefined}>
63
+ * {item.panel}
64
+ * </Disclosure>
65
+ * {/each}
66
+ * {/if}
41
67
  * </div>
42
68
  * ```
69
+ * Data-mode child styling defaults to the combined parent/child preset and can be customized via `disclosure`.
70
+ * ### Behavior
71
+ * Declarative `<Disclosure id=...>` children automatically coordinate exclusive open state through `current` and inherit the base `variant`/`styling`.
43
72
  */
44
- declare const Accordion: import("svelte").Component<AccordionProps, {}, "variant" | "current">;
73
+ declare const Accordion: Component<AccordionProps, {}, "current">;
45
74
  type Accordion = ReturnType<typeof Accordion>;
46
75
  export default Accordion;
@@ -0,0 +1,116 @@
1
+ <!--
2
+ @component
3
+ ### Usage
4
+ Use standalone.
5
+ ```svelte
6
+ <Button {...props}>Label</Button>
7
+ ```
8
+ ### Types
9
+ default value: *`(value)`*
10
+ ```ts
11
+ interface ButtonProps extends Omit<HTMLButtonAttributes, "children" | "form" | "type" | "onclick"> {
12
+ children: Snippet<[string]>; // Snippet<[variant]>
13
+ left?: Snippet<[string, HTMLButtonElement | undefined]>; // Snippet<[variant,element]>
14
+ right?: Snippet<[string, HTMLButtonElement | undefined]>; // Snippet<[variant,element]>
15
+ type?: "submit" | "reset" | "button"; // ("button")
16
+ onclick?: MouseEventHandler<HTMLButtonElement> | null;
17
+ form?: HTMLFormElement;
18
+ inactive?: string; // reason; when set: aria-disabled + aria-description, variant->INACTIVE, onclick suppressed
19
+ attach?: Attachment<HTMLButtonElement>;
20
+ element?: HTMLButtonElement; // bindable
21
+ styling?: SVSClass;
22
+ variant?: SVSVariant; // bindable (VARIANT.NEUTRAL)
23
+ // other HTMLButtonAttributes are passed to <button> via ...rest; `class` is merged onto root <button>
24
+ }
25
+ ```
26
+ ### Anatomy
27
+ ```svelte
28
+ <button class="whole" {...rest} {type} {onclick} aria-disabled aria-description>
29
+ <span class="left" conditional>{left}</span>
30
+ <span class="main">{children}</span>
31
+ <span class="right" conditional>{right}</span>
32
+ </button>
33
+ ```
34
+ -->
35
+ <script module lang="ts">
36
+ export interface ButtonProps extends Omit<HTMLButtonAttributes, "children" | "form" | "type"> {
37
+ children: Snippet<[string]>; // Snippet<[variant]>
38
+ left?: Snippet<[string, HTMLButtonElement | undefined]>; // Snippet<[variant,element]>
39
+ right?: Snippet<[string, HTMLButtonElement | undefined]>; // Snippet<[variant,element]>
40
+ type?: "submit" | "reset" | "button"; // ("button")
41
+ form?: HTMLFormElement;
42
+ inactive?: string; // reason; when set: aria-disabled + aria-description, variant->INACTIVE, onclick suppressed
43
+ attach?: Attachment<HTMLButtonElement>;
44
+ element?: HTMLButtonElement; // bindable
45
+ styling?: SVSClass;
46
+ variant?: SVSVariant; // bindable (VARIANT.NEUTRAL)
47
+ }
48
+ export type ButtonReqdProps = "children";
49
+ export type ButtonBindProps = "element" | "variant";
50
+
51
+ export const _BUTTON_PRESET = "svs-button";
52
+
53
+ import { VARIANT, PARTS, _fnClass } from "./_core";
54
+ import type { Snippet } from "svelte";
55
+ import type { Attachment } from "svelte/attachments";
56
+ import type { HTMLButtonAttributes, MouseEventHandler } from "svelte/elements";
57
+ import type { SVSClass, SVSVariant } from "./_core";
58
+ </script>
59
+
60
+ <script lang="ts">
61
+ // prettier-ignore
62
+ let { children, left, right, type = "button", onclick, form, attach, element = $bindable(), styling, variant = $bindable(VARIANT.NEUTRAL), inactive, class: c, ...rest }: ButtonProps = $props();
63
+
64
+ // *** Initialize *** //
65
+ const cls = $derived(_fnClass(_BUTTON_PRESET, styling));
66
+ const idForm = $derived(form?.id || undefined);
67
+ const reason = $derived(inactive?.trim() ? inactive : undefined);
68
+ const inactiveAttrs = $derived(reason ? { "aria-disabled": true, "aria-description": reason } : {});
69
+
70
+ let prevVariant: SVSVariant | undefined;
71
+ $effect(() => {
72
+ if (reason) {
73
+ if (variant !== VARIANT.INACTIVE) {
74
+ prevVariant = variant;
75
+ variant = VARIANT.INACTIVE;
76
+ }
77
+ } else if (prevVariant !== undefined) {
78
+ variant = prevVariant;
79
+ prevVariant = undefined;
80
+ }
81
+ });
82
+
83
+ // *** Event Handlers *** //
84
+ const hclick: MouseEventHandler<HTMLButtonElement> = (ev) => {
85
+ if (reason) return;
86
+ if (form?.checkValidity() ?? true) onclick?.(ev);
87
+ };
88
+ </script>
89
+
90
+ <!---------------------------------------->
91
+
92
+ <button
93
+ bind:this={element}
94
+ class={[cls(PARTS.WHOLE, variant), c]}
95
+ {type}
96
+ onclick={hclick}
97
+ form={idForm}
98
+ {...rest}
99
+ {...inactiveAttrs}
100
+ {@attach attach}
101
+ >
102
+ {@render whole()}
103
+ </button>
104
+
105
+ {#snippet side(area: string, body?: Snippet<[string, HTMLButtonElement | undefined]>)}
106
+ {#if body}
107
+ <span class={cls(area, variant)}>{@render body(variant, element)}</span>
108
+ {/if}
109
+ {/snippet}
110
+ {#snippet whole()}
111
+ {@render side(PARTS.LEFT, left)}
112
+ <span class={cls(PARTS.MAIN, variant)}>
113
+ {@render children(variant)}
114
+ </span>
115
+ {@render side(PARTS.RIGHT, right)}
116
+ {/snippet}
@@ -0,0 +1,55 @@
1
+ export interface ButtonProps extends Omit<HTMLButtonAttributes, "children" | "form" | "type"> {
2
+ children: Snippet<[string]>;
3
+ left?: Snippet<[string, HTMLButtonElement | undefined]>;
4
+ right?: Snippet<[string, HTMLButtonElement | undefined]>;
5
+ type?: "submit" | "reset" | "button";
6
+ form?: HTMLFormElement;
7
+ inactive?: string;
8
+ attach?: Attachment<HTMLButtonElement>;
9
+ element?: HTMLButtonElement;
10
+ styling?: SVSClass;
11
+ variant?: SVSVariant;
12
+ }
13
+ export type ButtonReqdProps = "children";
14
+ export type ButtonBindProps = "element" | "variant";
15
+ export declare const _BUTTON_PRESET = "svs-button";
16
+ import type { Snippet } from "svelte";
17
+ import type { Attachment } from "svelte/attachments";
18
+ import type { HTMLButtonAttributes } from "svelte/elements";
19
+ import type { SVSClass, SVSVariant } from "./_core";
20
+ /**
21
+ * ### Usage
22
+ * Use standalone.
23
+ * ```svelte
24
+ * <Button {...props}>Label</Button>
25
+ * ```
26
+ * ### Types
27
+ * default value: *`(value)`*
28
+ * ```ts
29
+ * interface ButtonProps extends Omit<HTMLButtonAttributes, "children" | "form" | "type" | "onclick"> {
30
+ * children: Snippet<[string]>; // Snippet<[variant]>
31
+ * left?: Snippet<[string, HTMLButtonElement | undefined]>; // Snippet<[variant,element]>
32
+ * right?: Snippet<[string, HTMLButtonElement | undefined]>; // Snippet<[variant,element]>
33
+ * type?: "submit" | "reset" | "button"; // ("button")
34
+ * onclick?: MouseEventHandler<HTMLButtonElement> | null;
35
+ * form?: HTMLFormElement;
36
+ * inactive?: string; // reason; when set: aria-disabled + aria-description, variant->INACTIVE, onclick suppressed
37
+ * attach?: Attachment<HTMLButtonElement>;
38
+ * element?: HTMLButtonElement; // bindable
39
+ * styling?: SVSClass;
40
+ * variant?: SVSVariant; // bindable (VARIANT.NEUTRAL)
41
+ * // other HTMLButtonAttributes are passed to <button> via ...rest; `class` is merged onto root <button>
42
+ * }
43
+ * ```
44
+ * ### Anatomy
45
+ * ```svelte
46
+ * <button class="whole" {...rest} {type} {onclick} aria-disabled aria-description>
47
+ * <span class="left" conditional>{left}</span>
48
+ * <span class="main">{children}</span>
49
+ * <span class="right" conditional>{right}</span>
50
+ * </button>
51
+ * ```
52
+ */
53
+ declare const Button: import("svelte").Component<ButtonProps, {}, "variant" | "element">;
54
+ type Button = ReturnType<typeof Button>;
55
+ export default Button;