@svgrid/grid 1.2.22 → 1.2.24

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 (263) hide show
  1. package/dist/GridFooter.svelte +34 -20
  2. package/dist/GridFooter.svelte.d.ts +8 -0
  3. package/dist/SvAutoComplete.svelte +89 -0
  4. package/dist/SvAutoComplete.svelte.d.ts +16 -0
  5. package/dist/SvButton.svelte +97 -0
  6. package/dist/SvButton.svelte.d.ts +23 -0
  7. package/dist/SvCalendar.svelte +409 -0
  8. package/dist/SvCalendar.svelte.d.ts +52 -0
  9. package/dist/SvCheckBox.svelte +82 -0
  10. package/dist/SvCheckBox.svelte.d.ts +19 -0
  11. package/dist/SvColorInput.svelte +160 -0
  12. package/dist/SvColorInput.svelte.d.ts +15 -0
  13. package/dist/SvComboBox.svelte +108 -0
  14. package/dist/SvComboBox.svelte.d.ts +15 -0
  15. package/dist/SvCountryInput.svelte +125 -0
  16. package/dist/SvCountryInput.svelte.d.ts +14 -0
  17. package/dist/SvDateTimePicker.svelte +265 -0
  18. package/dist/SvDateTimePicker.svelte.d.ts +38 -0
  19. package/dist/SvDropDownList.svelte +120 -0
  20. package/dist/SvDropDownList.svelte.d.ts +15 -0
  21. package/dist/SvForm.svelte +147 -0
  22. package/dist/SvForm.svelte.d.ts +29 -0
  23. package/dist/SvGauge.svelte +97 -0
  24. package/dist/SvGauge.svelte.d.ts +26 -0
  25. package/dist/SvGrid.controller.svelte.d.ts +16 -2
  26. package/dist/SvGrid.controller.svelte.js +63 -8
  27. package/dist/SvGrid.css +84 -31
  28. package/dist/SvGrid.helpers.d.ts +4 -0
  29. package/dist/SvGrid.helpers.js +40 -12
  30. package/dist/SvGrid.svelte +84 -5
  31. package/dist/SvGrid.types.d.ts +36 -0
  32. package/dist/SvGridDropdown.svelte +16 -30
  33. package/dist/SvGridDropdown.svelte.d.ts +4 -0
  34. package/dist/SvListBox.svelte +89 -0
  35. package/dist/SvListBox.svelte.d.ts +22 -0
  36. package/dist/SvMaskedInput.svelte +66 -0
  37. package/dist/SvMaskedInput.svelte.d.ts +15 -0
  38. package/dist/SvNumberInput.svelte +116 -0
  39. package/dist/SvNumberInput.svelte.d.ts +18 -0
  40. package/dist/SvPasswordInput.svelte +100 -0
  41. package/dist/SvPasswordInput.svelte.d.ts +18 -0
  42. package/dist/SvPhoneInput.svelte +87 -0
  43. package/dist/SvPhoneInput.svelte.d.ts +19 -0
  44. package/dist/SvRadioGroup.svelte +85 -0
  45. package/dist/SvRadioGroup.svelte.d.ts +20 -0
  46. package/dist/SvRating.svelte +91 -0
  47. package/dist/SvRating.svelte.d.ts +18 -0
  48. package/dist/SvRepeatButton.svelte +89 -0
  49. package/dist/SvRepeatButton.svelte.d.ts +21 -0
  50. package/dist/SvSlider.svelte +155 -0
  51. package/dist/SvSlider.svelte.d.ts +20 -0
  52. package/dist/SvSwitchButton.svelte +87 -0
  53. package/dist/SvSwitchButton.svelte.d.ts +14 -0
  54. package/dist/SvTabs.svelte +109 -0
  55. package/dist/SvTabs.svelte.d.ts +26 -0
  56. package/dist/SvTagsInput.svelte +70 -0
  57. package/dist/SvTagsInput.svelte.d.ts +14 -0
  58. package/dist/SvTimePicker.svelte +201 -0
  59. package/dist/SvTimePicker.svelte.d.ts +30 -0
  60. package/dist/SvToggleButton.svelte +57 -0
  61. package/dist/SvToggleButton.svelte.d.ts +16 -0
  62. package/dist/SvTree.svelte +131 -0
  63. package/dist/SvTree.svelte.d.ts +27 -0
  64. package/dist/build-api.js +78 -1
  65. package/dist/cdn/svgrid.js +11674 -5907
  66. package/dist/cdn/svgrid.svelte-external.js +10436 -4671
  67. package/dist/cell-render.d.ts +4 -0
  68. package/dist/cell-render.js +35 -1
  69. package/dist/clipboard.js +19 -1
  70. package/dist/collaboration.d.ts +2 -0
  71. package/dist/collaboration.js +4 -0
  72. package/dist/core.d.ts +22 -1
  73. package/dist/countries.d.ts +15 -0
  74. package/dist/countries.js +61 -0
  75. package/dist/createAutocomplete.svelte.d.ts +77 -0
  76. package/dist/createAutocomplete.svelte.js +100 -0
  77. package/dist/createCalendar.svelte.d.ts +192 -0
  78. package/dist/createCalendar.svelte.js +402 -0
  79. package/dist/createCheckbox.svelte.d.ts +45 -0
  80. package/dist/createCheckbox.svelte.js +26 -0
  81. package/dist/createColorInput.svelte.d.ts +67 -0
  82. package/dist/createColorInput.svelte.js +100 -0
  83. package/dist/createCombobox.svelte.d.ts +102 -0
  84. package/dist/createCombobox.svelte.js +171 -0
  85. package/dist/createCountryInput.svelte.d.ts +86 -0
  86. package/dist/createCountryInput.svelte.js +126 -0
  87. package/dist/createDateTimePicker.svelte.d.ts +83 -0
  88. package/dist/createDateTimePicker.svelte.js +202 -0
  89. package/dist/createDropdownList.svelte.d.ts +78 -0
  90. package/dist/createDropdownList.svelte.js +117 -0
  91. package/dist/createGauge.svelte.d.ts +56 -0
  92. package/dist/createGauge.svelte.js +49 -0
  93. package/dist/createListbox.svelte.d.ts +79 -0
  94. package/dist/createListbox.svelte.js +117 -0
  95. package/dist/createMaskedInput.svelte.d.ts +30 -0
  96. package/dist/createMaskedInput.svelte.js +62 -0
  97. package/dist/createNumberInput.svelte.d.ts +72 -0
  98. package/dist/createNumberInput.svelte.js +167 -0
  99. package/dist/createPasswordInput.svelte.d.ts +60 -0
  100. package/dist/createPasswordInput.svelte.js +72 -0
  101. package/dist/createPhoneInput.svelte.d.ts +46 -0
  102. package/dist/createPhoneInput.svelte.js +105 -0
  103. package/dist/createRadioGroup.svelte.d.ts +64 -0
  104. package/dist/createRadioGroup.svelte.js +73 -0
  105. package/dist/createRating.svelte.d.ts +70 -0
  106. package/dist/createRating.svelte.js +85 -0
  107. package/dist/createSlider.svelte.d.ts +71 -0
  108. package/dist/createSlider.svelte.js +123 -0
  109. package/dist/createSwitch.svelte.d.ts +43 -0
  110. package/dist/createSwitch.svelte.js +41 -0
  111. package/dist/createTabs.svelte.d.ts +78 -0
  112. package/dist/createTabs.svelte.js +92 -0
  113. package/dist/createTagsInput.svelte.d.ts +65 -0
  114. package/dist/createTagsInput.svelte.js +93 -0
  115. package/dist/createTimePicker.svelte.d.ts +106 -0
  116. package/dist/createTimePicker.svelte.js +248 -0
  117. package/dist/createToggle.svelte.d.ts +43 -0
  118. package/dist/createToggle.svelte.js +39 -0
  119. package/dist/createTree.svelte.d.ts +96 -0
  120. package/dist/createTree.svelte.js +194 -0
  121. package/dist/datetime/date-core.d.ts +88 -0
  122. package/dist/datetime/date-core.js +165 -0
  123. package/dist/datetime/date-format.d.ts +39 -0
  124. package/dist/datetime/date-format.js +371 -0
  125. package/dist/datetime/date-restrict.d.ts +23 -0
  126. package/dist/datetime/date-restrict.js +50 -0
  127. package/dist/datetime/date-selection.d.ts +25 -0
  128. package/dist/datetime/date-selection.js +94 -0
  129. package/dist/datetime/mask.d.ts +21 -0
  130. package/dist/datetime/mask.js +90 -0
  131. package/dist/editing.d.ts +1 -1
  132. package/dist/editing.js +7 -0
  133. package/dist/editor-contract.d.ts +48 -0
  134. package/dist/editor-contract.js +26 -0
  135. package/dist/editors/cell-editors.d.ts +2 -2
  136. package/dist/editors/cell-editors.js +3 -1
  137. package/dist/export-format.d.ts +178 -0
  138. package/dist/export-format.js +419 -0
  139. package/dist/hyperformula-adapter.d.ts +4 -0
  140. package/dist/hyperformula-adapter.js +15 -6
  141. package/dist/index.d.ts +60 -0
  142. package/dist/index.js +72 -0
  143. package/dist/list-option.d.ts +10 -0
  144. package/dist/list-option.js +7 -0
  145. package/dist/menus.js +18 -2
  146. package/dist/popover.d.ts +48 -0
  147. package/dist/popover.js +65 -0
  148. package/dist/row-resize.d.ts +4 -4
  149. package/dist/row-resize.js +8 -5
  150. package/dist/selection.d.ts +6 -0
  151. package/dist/selection.js +31 -5
  152. package/dist/server-data-source.d.ts +43 -0
  153. package/dist/server-data-source.js +100 -0
  154. package/dist/sparkline.js +8 -4
  155. package/dist/svgrid-wrapper.types.d.ts +26 -1
  156. package/package.json +6 -1
  157. package/src/GridFooter.svelte +34 -20
  158. package/src/SvAutoComplete.svelte +89 -0
  159. package/src/SvButton.svelte +97 -0
  160. package/src/SvCalendar.svelte +409 -0
  161. package/src/SvCalendar.test.ts +211 -0
  162. package/src/SvCheckBox.svelte +82 -0
  163. package/src/SvColorInput.svelte +160 -0
  164. package/src/SvComboBox.svelte +108 -0
  165. package/src/SvCountryInput.svelte +125 -0
  166. package/src/SvDateTimePicker.svelte +265 -0
  167. package/src/SvDateTimePicker.test.ts +136 -0
  168. package/src/SvDropDownList.svelte +120 -0
  169. package/src/SvForm.svelte +147 -0
  170. package/src/SvGauge.svelte +97 -0
  171. package/src/SvGrid.controller.svelte.ts +74 -8
  172. package/src/SvGrid.css +84 -31
  173. package/src/SvGrid.helpers.ts +41 -12
  174. package/src/SvGrid.svelte +84 -5
  175. package/src/SvGrid.types.ts +33 -0
  176. package/src/SvGridDropdown.svelte +16 -30
  177. package/src/SvListBox.svelte +89 -0
  178. package/src/SvMaskedInput.svelte +66 -0
  179. package/src/SvNumberInput.svelte +116 -0
  180. package/src/SvPasswordInput.svelte +100 -0
  181. package/src/SvPhoneInput.svelte +87 -0
  182. package/src/SvRadioGroup.svelte +85 -0
  183. package/src/SvRating.svelte +91 -0
  184. package/src/SvRepeatButton.svelte +89 -0
  185. package/src/SvSlider.svelte +155 -0
  186. package/src/SvSwitchButton.svelte +87 -0
  187. package/src/SvTabs.svelte +109 -0
  188. package/src/SvTagsInput.svelte +70 -0
  189. package/src/SvTimePicker.svelte +201 -0
  190. package/src/SvTimePicker.test.ts +98 -0
  191. package/src/SvToggleButton.svelte +57 -0
  192. package/src/SvTree.svelte +131 -0
  193. package/src/build-api.ts +115 -0
  194. package/src/cell-render.test.ts +38 -0
  195. package/src/cell-render.ts +43 -0
  196. package/src/clipboard.test.ts +18 -0
  197. package/src/clipboard.ts +24 -1
  198. package/src/collaboration.test.ts +30 -0
  199. package/src/collaboration.ts +6 -0
  200. package/src/core.ts +27 -3
  201. package/src/countries.ts +71 -0
  202. package/src/createAutocomplete.svelte.ts +104 -0
  203. package/src/createCalendar.svelte.ts +488 -0
  204. package/src/createCheckbox.svelte.ts +74 -0
  205. package/src/createColorInput.svelte.ts +115 -0
  206. package/src/createCombobox.svelte.ts +158 -0
  207. package/src/createCountryInput.svelte.ts +126 -0
  208. package/src/createDateTimePicker.svelte.ts +216 -0
  209. package/src/createDropdownList.svelte.ts +125 -0
  210. package/src/createGauge.svelte.ts +86 -0
  211. package/src/createListbox.svelte.ts +186 -0
  212. package/src/createMaskedInput.svelte.ts +81 -0
  213. package/src/createNumberInput.svelte.ts +193 -0
  214. package/src/createPasswordInput.svelte.ts +87 -0
  215. package/src/createPhoneInput.svelte.ts +119 -0
  216. package/src/createRadioGroup.svelte.ts +132 -0
  217. package/src/createRating.svelte.ts +145 -0
  218. package/src/createSlider.svelte.ts +160 -0
  219. package/src/createSwitch.svelte.ts +78 -0
  220. package/src/createTabs.svelte.ts +129 -0
  221. package/src/createTagsInput.svelte.ts +98 -0
  222. package/src/createTimePicker.svelte.ts +289 -0
  223. package/src/createToggle.svelte.ts +69 -0
  224. package/src/createTree.svelte.ts +216 -0
  225. package/src/datetime/date-core.test.ts +217 -0
  226. package/src/datetime/date-core.ts +204 -0
  227. package/src/datetime/date-format.test.ts +121 -0
  228. package/src/datetime/date-format.ts +317 -0
  229. package/src/datetime/date-restrict.ts +60 -0
  230. package/src/datetime/date-selection.test.ts +129 -0
  231. package/src/datetime/date-selection.ts +137 -0
  232. package/src/datetime/mask.test.ts +36 -0
  233. package/src/datetime/mask.ts +84 -0
  234. package/src/editing.test.ts +22 -0
  235. package/src/editing.ts +7 -1
  236. package/src/editor-contract.test.ts +32 -0
  237. package/src/editor-contract.ts +60 -0
  238. package/src/editors/cell-editors.ts +7 -1
  239. package/src/export-data-api.test.ts +126 -0
  240. package/src/export-format.test.ts +107 -0
  241. package/src/export-format.ts +598 -0
  242. package/src/hyperformula-adapter.test.ts +35 -1
  243. package/src/hyperformula-adapter.ts +18 -6
  244. package/src/index.ts +110 -0
  245. package/src/list-option.ts +15 -0
  246. package/src/menus.ts +21 -2
  247. package/src/popover.ts +79 -0
  248. package/src/row-resize.test.ts +25 -0
  249. package/src/row-resize.ts +8 -5
  250. package/src/selection.test.ts +36 -4
  251. package/src/selection.ts +30 -9
  252. package/src/server-data-source.test.ts +201 -0
  253. package/src/server-data-source.ts +148 -0
  254. package/src/sparkline.test.ts +9 -0
  255. package/src/sparkline.ts +9 -4
  256. package/src/svgrid-wrapper.types.ts +28 -1
  257. package/src/svgrid.behavior.test.ts +27 -0
  258. package/src/svgrid.new-features.wrapper.test.ts +27 -1
  259. package/src/ui-buttons.test.ts +105 -0
  260. package/src/ui-composite.test.ts +89 -0
  261. package/src/ui-inputs.test.ts +118 -0
  262. package/src/ui-range.test.ts +61 -0
  263. package/src/ui-selection.test.ts +119 -0
@@ -0,0 +1,155 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvSlider - a single or range slider (WAI-ARIA slider) with steps, ticks,
4
+ * keyboard + pointer drag. Parity: Smart `smart-slider`. Single emits number;
5
+ * range emits [number, number].
6
+ *
7
+ * The behavior lives in the headless `createSlider` core; this component is
8
+ * just one styled renderer over it (it only owns pointer/DOM measurement).
9
+ */
10
+ import { createSlider } from './createSlider.svelte'
11
+
12
+ type Props = {
13
+ value?: number | [number, number]
14
+ onChange?: (value: any) => void
15
+ min?: number
16
+ max?: number
17
+ step?: number
18
+ range?: boolean
19
+ /** Show evenly spaced tick marks (count or explicit values). */
20
+ ticks?: number | number[]
21
+ /** Show the value bubble above the thumb. */
22
+ showValue?: boolean
23
+ disabled?: boolean
24
+ orientation?: 'horizontal' | 'vertical'
25
+ name?: string
26
+ ariaLabel?: string
27
+ formatValue?: (v: number) => string
28
+ }
29
+
30
+ let {
31
+ value = 0,
32
+ onChange,
33
+ min = 0,
34
+ max = 100,
35
+ step = 1,
36
+ range = false,
37
+ ticks,
38
+ showValue = false,
39
+ disabled = false,
40
+ orientation = 'horizontal',
41
+ name,
42
+ ariaLabel,
43
+ formatValue,
44
+ }: Props = $props()
45
+
46
+ // Behavior (value<->position math, stepping, keyboard, drag state, ARIA) lives
47
+ // in the headless `createSlider` core; the component keeps the DOM measuring.
48
+ const slider = createSlider({
49
+ value: () => value,
50
+ onChange: (v) => onChange?.(v),
51
+ min: () => min,
52
+ max: () => max,
53
+ step: () => step,
54
+ range: () => range,
55
+ orientation: () => orientation,
56
+ disabled: () => disabled,
57
+ ariaLabel: () => ariaLabel,
58
+ })
59
+ const lo = $derived(slider.lo)
60
+ const hi = $derived(slider.hi)
61
+ const pct = (v: number) => slider.percentOf(v)
62
+
63
+ const tickValues = $derived.by<number[]>(() => {
64
+ if (!ticks) return []
65
+ if (Array.isArray(ticks)) return ticks
66
+ return Array.from({ length: ticks }, (_, i) => min + ((max - min) / (ticks - 1)) * i)
67
+ })
68
+
69
+ // The rect is measured here (a DOM concern) and passed INTO the core.
70
+ let trackEl: HTMLDivElement | null = null
71
+ function onTrackDown(e: PointerEvent) {
72
+ if (disabled) return
73
+ slider.pointerDown({ x: e.clientX, y: e.clientY }, trackEl!.getBoundingClientRect())
74
+ trackEl?.setPointerCapture(e.pointerId)
75
+ }
76
+ function onMove(e: PointerEvent) {
77
+ if (!slider.dragging) return
78
+ slider.setFromPointer({ x: e.clientX, y: e.clientY }, trackEl!.getBoundingClientRect())
79
+ }
80
+ function onUp(e: PointerEvent) { if (slider.dragging) { slider.endDrag(); trackEl?.releasePointerCapture(e.pointerId) } }
81
+
82
+ const fmt = (v: number) => (formatValue ? formatValue(v) : String(v))
83
+ </script>
84
+
85
+ <div
86
+ class="sv-slider sv-slider--{orientation}"
87
+ class:is-disabled={disabled}
88
+ aria-label={ariaLabel}
89
+ >
90
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
91
+ <div
92
+ bind:this={trackEl}
93
+ class="sv-slider__track"
94
+ {...slider.trackProps()}
95
+ onpointerdown={onTrackDown}
96
+ onpointermove={onMove}
97
+ onpointerup={onUp}
98
+ onpointercancel={onUp}
99
+ >
100
+ <div
101
+ class="sv-slider__fill"
102
+ style:--a={`${range ? pct(lo) : 0}%`}
103
+ style:--b={`${range ? pct(hi) : pct(hi)}%`}
104
+ ></div>
105
+ {#each tickValues as t (t)}
106
+ <span class="sv-slider__tick" style:--p={`${pct(t)}%`}></span>
107
+ {/each}
108
+
109
+ {#if range}
110
+ <div class="sv-slider__thumb" style:--p={`${pct(lo)}%`} {...slider.thumbProps(0)}>
111
+ {#if showValue}<span class="sv-slider__bubble">{fmt(lo)}</span>{/if}
112
+ </div>
113
+ <div class="sv-slider__thumb" style:--p={`${pct(hi)}%`} {...slider.thumbProps(1)}>
114
+ {#if showValue}<span class="sv-slider__bubble">{fmt(hi)}</span>{/if}
115
+ </div>
116
+ {:else}
117
+ <div class="sv-slider__thumb" style:--p={`${pct(hi)}%`} {...slider.thumbProps(0)}>
118
+ {#if showValue}<span class="sv-slider__bubble">{fmt(hi)}</span>{/if}
119
+ </div>
120
+ {/if}
121
+ </div>
122
+ {#if name}<input type="hidden" {name} value={range ? `${lo},${hi}` : hi} />{/if}
123
+ </div>
124
+
125
+ <style>
126
+ .sv-slider { --_accent: var(--sg-accent, #2563eb); --_track: var(--sg-border, #e2e8f0); padding: 10px; }
127
+ .sv-slider--horizontal { width: 240px; }
128
+ .sv-slider--vertical { height: 200px; width: 40px; }
129
+ .sv-slider.is-disabled { opacity: 0.5; pointer-events: none; }
130
+ .sv-slider__track {
131
+ position: relative; background: var(--_track); border-radius: 999px; cursor: pointer; touch-action: none;
132
+ }
133
+ .sv-slider--horizontal .sv-slider__track { height: 6px; }
134
+ .sv-slider--vertical .sv-slider__track { width: 6px; height: 100%; margin: 0 auto; }
135
+ .sv-slider__fill { position: absolute; background: var(--_accent); border-radius: 999px; }
136
+ .sv-slider--horizontal .sv-slider__fill { left: var(--a); right: calc(100% - var(--b)); top: 0; bottom: 0; }
137
+ .sv-slider--vertical .sv-slider__fill { bottom: var(--a); top: calc(100% - var(--b)); left: 0; right: 0; }
138
+ .sv-slider__thumb {
139
+ position: absolute; width: 16px; height: 16px; border-radius: 50%;
140
+ background: #fff; border: 2px solid var(--_accent); box-shadow: 0 1px 3px rgba(0,0,0,0.25);
141
+ cursor: grab; touch-action: none;
142
+ }
143
+ .sv-slider--horizontal .sv-slider__thumb { left: var(--p); top: 50%; transform: translate(-50%, -50%); }
144
+ .sv-slider--vertical .sv-slider__thumb { bottom: var(--p); left: 50%; transform: translate(-50%, 50%); }
145
+ .sv-slider__thumb:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
146
+ .sv-slider__thumb:active { cursor: grabbing; }
147
+ .sv-slider__bubble {
148
+ position: absolute; bottom: 130%; left: 50%; transform: translateX(-50%);
149
+ background: var(--_accent); color: var(--sg-on-accent, #fff); font-size: 11px; font-weight: 600;
150
+ padding: 2px 6px; border-radius: 5px; white-space: nowrap;
151
+ }
152
+ .sv-slider__tick { position: absolute; width: 2px; height: 2px; border-radius: 50%; background: var(--sg-muted, #94a3b8); }
153
+ .sv-slider--horizontal .sv-slider__tick { left: var(--p); top: 50%; transform: translate(-50%, -50%); }
154
+ .sv-slider--vertical .sv-slider__tick { bottom: var(--p); left: 50%; transform: translate(-50%, 50%); }
155
+ </style>
@@ -0,0 +1,20 @@
1
+ type Props = {
2
+ value?: number | [number, number];
3
+ onChange?: (value: any) => void;
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ range?: boolean;
8
+ /** Show evenly spaced tick marks (count or explicit values). */
9
+ ticks?: number | number[];
10
+ /** Show the value bubble above the thumb. */
11
+ showValue?: boolean;
12
+ disabled?: boolean;
13
+ orientation?: 'horizontal' | 'vertical';
14
+ name?: string;
15
+ ariaLabel?: string;
16
+ formatValue?: (v: number) => string;
17
+ };
18
+ declare const SvSlider: import("svelte").Component<Props, {}, "">;
19
+ type SvSlider = ReturnType<typeof SvSlider>;
20
+ export default SvSlider;
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvSwitchButton - an on/off sliding switch (ARIA `switch`). Parity: Smart
4
+ * `smart-switch-button`. Controlled via `checked` + `onChange`; keyboard
5
+ * togglable; optional inline on/off labels.
6
+ */
7
+ import { createSwitch } from './createSwitch.svelte'
8
+
9
+ type Props = {
10
+ checked?: boolean
11
+ onChange?: (checked: boolean) => void
12
+ disabled?: boolean
13
+ size?: 'sm' | 'md' | 'lg'
14
+ name?: string
15
+ ariaLabel?: string
16
+ /** Optional labels rendered inside the track. */
17
+ onLabel?: string
18
+ offLabel?: string
19
+ }
20
+
21
+ let {
22
+ checked = false,
23
+ onChange,
24
+ disabled = false,
25
+ size = 'md',
26
+ name,
27
+ ariaLabel,
28
+ onLabel,
29
+ offLabel,
30
+ }: Props = $props()
31
+
32
+ // The styled switch is just a renderer over the headless core.
33
+ const sw = createSwitch({
34
+ checked: () => checked,
35
+ onChange: (v) => onChange?.(v),
36
+ disabled: () => disabled,
37
+ ariaLabel: () => ariaLabel,
38
+ })
39
+ </script>
40
+
41
+ <button
42
+ class="sv-switch sv-switch--{size}"
43
+ class:is-on={checked}
44
+ {...sw.switchProps()}
45
+ >
46
+ <span class="sv-switch__track">
47
+ {#if onLabel || offLabel}
48
+ <span class="sv-switch__txt sv-switch__txt--on">{onLabel ?? ''}</span>
49
+ <span class="sv-switch__txt sv-switch__txt--off">{offLabel ?? ''}</span>
50
+ {/if}
51
+ <span class="sv-switch__thumb"></span>
52
+ </span>
53
+ {#if name}<input type="hidden" {name} value={checked ? 'true' : 'false'} />{/if}
54
+ </button>
55
+
56
+ <style>
57
+ .sv-switch {
58
+ --_accent: var(--sg-accent, #2563eb);
59
+ --_h: 22px; --_w: 40px;
60
+ display: inline-flex; align-items: center; background: none; border: 0; padding: 2px; cursor: pointer;
61
+ }
62
+ .sv-switch--sm { --_h: 18px; --_w: 32px; }
63
+ .sv-switch--lg { --_h: 28px; --_w: 52px; }
64
+ .sv-switch[disabled] { opacity: 0.5; cursor: not-allowed; }
65
+ .sv-switch__track {
66
+ position: relative; width: var(--_w); height: var(--_h); border-radius: 999px;
67
+ background: var(--sg-border, #cbd5e1); transition: background 0.16s;
68
+ display: inline-flex; align-items: center;
69
+ }
70
+ .sv-switch.is-on .sv-switch__track { background: var(--_accent); }
71
+ .sv-switch__thumb {
72
+ position: absolute; left: 2px; top: 50%; transform: translateY(-50%);
73
+ width: calc(var(--_h) - 4px); height: calc(var(--_h) - 4px); border-radius: 50%;
74
+ background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,0.3); transition: left 0.16s;
75
+ }
76
+ .sv-switch.is-on .sv-switch__thumb { left: calc(var(--_w) - var(--_h) + 2px); }
77
+ .sv-switch__txt {
78
+ position: absolute; font-size: 9px; font-weight: 700; color: #fff; text-transform: uppercase;
79
+ top: 50%; transform: translateY(-50%);
80
+ }
81
+ .sv-switch__txt--on { left: 6px; opacity: 0; }
82
+ .sv-switch__txt--off { right: 5px; color: var(--sg-muted, #64748b); }
83
+ .sv-switch.is-on .sv-switch__txt--on { opacity: 1; }
84
+ .sv-switch.is-on .sv-switch__txt--off { opacity: 0; }
85
+ .sv-switch:focus-visible { outline: none; }
86
+ .sv-switch:focus-visible .sv-switch__track { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
87
+ </style>
@@ -0,0 +1,14 @@
1
+ type Props = {
2
+ checked?: boolean;
3
+ onChange?: (checked: boolean) => void;
4
+ disabled?: boolean;
5
+ size?: 'sm' | 'md' | 'lg';
6
+ name?: string;
7
+ ariaLabel?: string;
8
+ /** Optional labels rendered inside the track. */
9
+ onLabel?: string;
10
+ offLabel?: string;
11
+ };
12
+ declare const SvSwitchButton: import("svelte").Component<Props, {}, "">;
13
+ type SvSwitchButton = ReturnType<typeof SvSwitchButton>;
14
+ export default SvSwitchButton;
@@ -0,0 +1,109 @@
1
+ <script lang="ts" module>
2
+ export type { TabItem } from './createTabs.svelte'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvTabs - a WAI-ARIA tabs widget with roving tabindex + arrow-key navigation.
8
+ * Parity: Smart `smart-tabs`. Controlled via `value` + `onChange`; the active
9
+ * panel is rendered through the `panel` snippet (receives the active id).
10
+ *
11
+ * The behavior (roving focus, activation, ARIA) lives in the headless
12
+ * `createTabs` core; this component is just one styled renderer over it.
13
+ */
14
+ import type { Snippet } from 'svelte'
15
+ import { createTabs, type TabItem } from './createTabs.svelte'
16
+
17
+ type Props = {
18
+ tabs: ReadonlyArray<TabItem>
19
+ value?: string
20
+ onChange?: (id: string) => void
21
+ orientation?: 'horizontal' | 'vertical'
22
+ /** 'line' underline (default) or 'pill' segmented look. */
23
+ variant?: 'line' | 'pill'
24
+ /** Activate on focus (automatic) vs. on Enter/Space (manual). Default automatic. */
25
+ activation?: 'automatic' | 'manual'
26
+ panel?: Snippet<[string]>
27
+ ariaLabel?: string
28
+ }
29
+
30
+ let {
31
+ tabs,
32
+ value = $bindable(),
33
+ onChange,
34
+ orientation = 'horizontal',
35
+ variant = 'line',
36
+ activation = 'automatic',
37
+ panel,
38
+ ariaLabel,
39
+ }: Props = $props()
40
+
41
+ const tabsCtl = createTabs({
42
+ tabs: () => tabs,
43
+ value: () => value,
44
+ onChange: (id) => { value = id; onChange?.(id) },
45
+ orientation: () => orientation,
46
+ activation: () => activation,
47
+ ariaLabel: () => ariaLabel,
48
+ })
49
+ const active = $derived(tabsCtl.activeId)
50
+
51
+ // DOM focus movement is a render concern: follow the core's roving focus target.
52
+ let tablistEl: HTMLDivElement | null = null
53
+ let lastFocusTick = 0
54
+ $effect(() => {
55
+ if (tabsCtl.focusTick !== lastFocusTick) {
56
+ lastFocusTick = tabsCtl.focusTick
57
+ const fid = tabsCtl.focusId
58
+ queueMicrotask(() => tablistEl?.querySelector<HTMLElement>(`[data-tab="${fid}"]`)?.focus())
59
+ }
60
+ })
61
+ </script>
62
+
63
+ <div class="sv-tabs sv-tabs--{orientation} sv-tabs--{variant}">
64
+ <div bind:this={tablistEl} class="sv-tabs__list" {...tabsCtl.tablistProps()}>
65
+ {#each tabs as tab (tab.id)}
66
+ <button
67
+ type="button"
68
+ class="sv-tabs__tab"
69
+ class:is-active={tab.id === active}
70
+ {...tabsCtl.tabProps(tab.id)}
71
+ >{tab.label}</button>
72
+ {/each}
73
+ </div>
74
+ {#if panel}
75
+ <div class="sv-tabs__panel" {...tabsCtl.panelProps(active)}>
76
+ {@render panel(active)}
77
+ </div>
78
+ {/if}
79
+ </div>
80
+
81
+ <style>
82
+ .sv-tabs { --_accent: var(--sg-accent, #2563eb); display: flex; gap: 0; }
83
+ .sv-tabs--horizontal { flex-direction: column; }
84
+ .sv-tabs--vertical { flex-direction: row; }
85
+ .sv-tabs__list { display: flex; gap: 2px; }
86
+ .sv-tabs--horizontal .sv-tabs__list { border-bottom: 1px solid var(--sg-border, #e2e8f0); }
87
+ .sv-tabs--vertical .sv-tabs__list { flex-direction: column; border-right: 1px solid var(--sg-border, #e2e8f0); }
88
+ .sv-tabs--pill .sv-tabs__list { border: 0; background: var(--sg-header-bg, #f1f5f9); padding: 3px; border-radius: 9px; gap: 3px; }
89
+
90
+ .sv-tabs__tab {
91
+ background: none; border: 0; font: inherit; font-size: 13px; font-weight: 600; color: var(--sg-muted, #64748b);
92
+ padding: 9px 14px; cursor: pointer; white-space: nowrap; position: relative;
93
+ }
94
+ .sv-tabs__tab:hover:not(:disabled):not(.is-active) { color: var(--sg-fg, #0f172a); }
95
+ .sv-tabs__tab:disabled { opacity: 0.45; cursor: not-allowed; }
96
+ .sv-tabs__tab:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: -2px; border-radius: 6px; }
97
+
98
+ .sv-tabs--line .sv-tabs__tab.is-active { color: var(--_accent); }
99
+ .sv-tabs--line .sv-tabs__tab.is-active::after {
100
+ content: ''; position: absolute; left: 8px; right: 8px; height: 2px; background: var(--_accent);
101
+ }
102
+ .sv-tabs--horizontal.sv-tabs--line .sv-tabs__tab.is-active::after { bottom: -1px; }
103
+ .sv-tabs--vertical.sv-tabs--line .sv-tabs__tab.is-active::after { top: 8px; bottom: 8px; left: auto; right: -1px; width: 2px; height: auto; }
104
+
105
+ .sv-tabs--pill .sv-tabs__tab { border-radius: 7px; padding: 7px 14px; }
106
+ .sv-tabs--pill .sv-tabs__tab.is-active { background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); box-shadow: 0 1px 2px rgba(0,0,0,0.08); }
107
+
108
+ .sv-tabs__panel { padding: 16px 2px; outline: none; }
109
+ </style>
@@ -0,0 +1,26 @@
1
+ export type { TabItem } from './createTabs.svelte';
2
+ /**
3
+ * SvTabs - a WAI-ARIA tabs widget with roving tabindex + arrow-key navigation.
4
+ * Parity: Smart `smart-tabs`. Controlled via `value` + `onChange`; the active
5
+ * panel is rendered through the `panel` snippet (receives the active id).
6
+ *
7
+ * The behavior (roving focus, activation, ARIA) lives in the headless
8
+ * `createTabs` core; this component is just one styled renderer over it.
9
+ */
10
+ import type { Snippet } from 'svelte';
11
+ import { type TabItem } from './createTabs.svelte';
12
+ type Props = {
13
+ tabs: ReadonlyArray<TabItem>;
14
+ value?: string;
15
+ onChange?: (id: string) => void;
16
+ orientation?: 'horizontal' | 'vertical';
17
+ /** 'line' underline (default) or 'pill' segmented look. */
18
+ variant?: 'line' | 'pill';
19
+ /** Activate on focus (automatic) vs. on Enter/Space (manual). Default automatic. */
20
+ activation?: 'automatic' | 'manual';
21
+ panel?: Snippet<[string]>;
22
+ ariaLabel?: string;
23
+ };
24
+ declare const SvTabs: import("svelte").Component<Props, {}, "value">;
25
+ type SvTabs = ReturnType<typeof SvTabs>;
26
+ export default SvTabs;
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvTagsInput - editable token/chips input. Type + Enter/comma to add,
4
+ * Backspace to remove the last, click × to remove a chip. Parity: Smart
5
+ * `smart-tags` / `smart-input` token mode. Emits string[].
6
+ *
7
+ * One styled renderer over the headless `createTagsInput` core (draft +
8
+ * add/remove + keyboard).
9
+ */
10
+ import { createTagsInput } from './createTagsInput.svelte'
11
+
12
+ type Props = {
13
+ value?: string[]
14
+ onChange?: (tags: string[]) => void
15
+ placeholder?: string
16
+ disabled?: boolean
17
+ /** Reject duplicate tags. Default true. */
18
+ unique?: boolean
19
+ max?: number
20
+ name?: string
21
+ ariaLabel?: string
22
+ }
23
+
24
+ let { value = [], onChange, placeholder = 'Add tag…', disabled = false, unique = true, max = Infinity, name, ariaLabel }: Props = $props()
25
+
26
+ const ti = createTagsInput({
27
+ value: () => value,
28
+ onChange: (t) => onChange?.(t),
29
+ disabled: () => disabled,
30
+ unique: () => unique,
31
+ max: () => max,
32
+ ariaLabel: () => ariaLabel,
33
+ })
34
+ </script>
35
+
36
+ <div class="sv-tags" class:is-disabled={disabled} {...ti.rootProps()}>
37
+ {#each value as tag, i (tag + i)}
38
+ <span class="sv-tags__chip" {...ti.tagProps(i)}>
39
+ <span class="sv-tags__label">{tag}</span>
40
+ {#if !disabled}
41
+ <button class="sv-tags__x" {...ti.removeProps(i)}>&times;</button>
42
+ {/if}
43
+ </span>
44
+ {/each}
45
+ <input
46
+ class="sv-tags__input"
47
+ placeholder={value.length ? '' : placeholder}
48
+ {...ti.inputProps()}
49
+ />
50
+ {#if name}<input type="hidden" {name} value={value.join(',')} />{/if}
51
+ </div>
52
+
53
+ <style>
54
+ .sv-tags {
55
+ --_accent: var(--sg-accent, #2563eb);
56
+ display: flex; flex-wrap: wrap; gap: 5px; align-items: center; width: 280px; min-height: 34px; padding: 4px 6px;
57
+ background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
58
+ border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
59
+ }
60
+ .sv-tags:focus-within { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
61
+ .sv-tags.is-disabled { opacity: 0.6; }
62
+ .sv-tags__chip {
63
+ display: inline-flex; align-items: center; gap: 4px; height: 22px; padding: 0 4px 0 8px;
64
+ background: color-mix(in srgb, var(--_accent) 14%, transparent); color: var(--_accent);
65
+ border-radius: 5px; font-size: 12px; font-weight: 600;
66
+ }
67
+ .sv-tags__x { background: none; border: 0; color: inherit; cursor: pointer; font-size: 15px; line-height: 1; padding: 0 2px; opacity: 0.7; }
68
+ .sv-tags__x:hover { opacity: 1; }
69
+ .sv-tags__input { flex: 1; min-width: 80px; border: 0; background: none; outline: none; color: inherit; font: inherit; font-size: 13px; height: 24px; }
70
+ </style>
@@ -0,0 +1,14 @@
1
+ type Props = {
2
+ value?: string[];
3
+ onChange?: (tags: string[]) => void;
4
+ placeholder?: string;
5
+ disabled?: boolean;
6
+ /** Reject duplicate tags. Default true. */
7
+ unique?: boolean;
8
+ max?: number;
9
+ name?: string;
10
+ ariaLabel?: string;
11
+ };
12
+ declare const SvTagsInput: import("svelte").Component<Props, {}, "">;
13
+ type SvTagsInput = ReturnType<typeof SvTagsInput>;
14
+ export default SvTagsInput;