@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,100 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvPasswordInput - a password field with a reveal toggle and an optional
4
+ * strength meter. Parity: Smart `smart-password-input`. Emits the string value.
5
+ *
6
+ * The styled renderer over the headless `createPasswordInput` core: the reveal
7
+ * toggle + strength heuristic come from the core, spread here via prop-getters.
8
+ */
9
+ import { createPasswordInput } from './createPasswordInput.svelte'
10
+
11
+ type Props = {
12
+ value?: string
13
+ onChange?: (value: string) => void
14
+ placeholder?: string
15
+ disabled?: boolean
16
+ readonly?: boolean
17
+ /** Show the show/hide eye toggle. */
18
+ revealable?: boolean
19
+ /** Show a 4-level strength meter. */
20
+ showStrength?: boolean
21
+ name?: string
22
+ size?: 'sm' | 'md' | 'lg'
23
+ ariaLabel?: string
24
+ autocomplete?: string
25
+ }
26
+
27
+ let {
28
+ value = '',
29
+ onChange,
30
+ placeholder,
31
+ disabled = false,
32
+ readonly = false,
33
+ revealable = true,
34
+ showStrength = false,
35
+ name,
36
+ size = 'md',
37
+ ariaLabel,
38
+ autocomplete = 'current-password',
39
+ }: Props = $props()
40
+
41
+ const pw = createPasswordInput({
42
+ value: () => value,
43
+ onChange: (v) => onChange?.(v),
44
+ placeholder: () => placeholder,
45
+ disabled: () => disabled,
46
+ readonly: () => readonly,
47
+ ariaLabel: () => ariaLabel,
48
+ autocomplete: () => autocomplete,
49
+ })
50
+ </script>
51
+
52
+ <div class="sv-pw sv-pw--{size}" class:is-disabled={disabled}>
53
+ <div class="sv-pw__field">
54
+ <input class="sv-pw__input" {...pw.inputProps()} />
55
+ {#if revealable}
56
+ <button class="sv-pw__eye" {...pw.toggleProps()}>
57
+ {#if pw.revealed}
58
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" /><path d="M1 1l22 22" /></svg>
59
+ {:else}
60
+ <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" /><circle cx="12" cy="12" r="3" /></svg>
61
+ {/if}
62
+ </button>
63
+ {/if}
64
+ </div>
65
+ {#if showStrength}
66
+ <div class="sv-pw__meter" aria-hidden="true">
67
+ {#each Array(4) as _, i (i)}
68
+ <span class="sv-pw__bar is-lvl{pw.strength}" class:on={i < pw.strength}></span>
69
+ {/each}
70
+ </div>
71
+ <span class="sv-pw__strength is-lvl{pw.strength}">{pw.strengthLabel}</span>
72
+ {/if}
73
+ {#if name}<input type="hidden" {name} value={value} />{/if}
74
+ </div>
75
+
76
+ <style>
77
+ .sv-pw { --_accent: var(--sg-accent, #2563eb); display: inline-flex; flex-direction: column; gap: 5px; width: 220px; }
78
+ .sv-pw.is-disabled { opacity: 0.6; }
79
+ .sv-pw__field {
80
+ display: flex; align-items: center;
81
+ background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
82
+ border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
83
+ }
84
+ .sv-pw__field:focus-within { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
85
+ .sv-pw__input { flex: 1; min-width: 0; border: 0; background: none; outline: none; color: inherit; font: inherit; padding: 0 10px; }
86
+ .sv-pw--sm .sv-pw__field { height: 28px; font-size: 12px; }
87
+ .sv-pw--md .sv-pw__field { height: 34px; font-size: 13px; }
88
+ .sv-pw--lg .sv-pw__field { height: 40px; font-size: 15px; }
89
+ .sv-pw__eye { display: grid; place-items: center; width: 32px; align-self: stretch; background: none; border: 0; color: var(--sg-muted, #64748b); cursor: pointer; }
90
+ .sv-pw__eye:hover { color: var(--_accent); }
91
+ .sv-pw__meter { display: flex; gap: 4px; }
92
+ .sv-pw__bar { flex: 1; height: 4px; border-radius: 2px; background: var(--sg-border, #e2e8f0); transition: background 0.15s; }
93
+ .sv-pw__bar.on.is-lvl1 { background: #dc2626; }
94
+ .sv-pw__bar.on.is-lvl2 { background: #f59e0b; }
95
+ .sv-pw__bar.on.is-lvl3 { background: #eab308; }
96
+ .sv-pw__bar.on.is-lvl4 { background: #16a34a; }
97
+ .sv-pw__strength { font-size: 11px; font-weight: 600; color: var(--sg-muted, #64748b); }
98
+ .sv-pw__strength.is-lvl1 { color: #dc2626; }
99
+ .sv-pw__strength.is-lvl4 { color: #16a34a; }
100
+ </style>
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvPhoneInput - a country dial-code selector + national number field. Parity:
4
+ * Smart `smart-phone-input` (simplified). Emits an E.164-ish string
5
+ * (`+<dial><digits>`). The country list is `countries.ts` (extend for full set).
6
+ *
7
+ * The styled renderer over the headless `createPhoneInput` core; country/number
8
+ * state + parsing live in the core, spread here via prop-getters.
9
+ */
10
+ import { COUNTRIES, flagEmoji } from './countries'
11
+ import { createPhoneInput } from './createPhoneInput.svelte'
12
+
13
+ type Props = {
14
+ value?: string
15
+ onChange?: (value: string, parts: { country: string; dial: string; national: string }) => void
16
+ /** Default country ISO code. */
17
+ country?: string
18
+ disabled?: boolean
19
+ readonly?: boolean
20
+ placeholder?: string
21
+ name?: string
22
+ size?: 'sm' | 'md' | 'lg'
23
+ ariaLabel?: string
24
+ }
25
+
26
+ let {
27
+ value = '',
28
+ onChange,
29
+ country = 'US',
30
+ disabled = false,
31
+ readonly = false,
32
+ placeholder = 'Phone number',
33
+ name,
34
+ size = 'md',
35
+ ariaLabel,
36
+ }: Props = $props()
37
+
38
+ const ph = createPhoneInput({
39
+ value: () => value,
40
+ onChange: (v, parts) => onChange?.(v, parts),
41
+ country: () => country,
42
+ disabled: () => disabled,
43
+ readonly: () => readonly,
44
+ placeholder: () => placeholder,
45
+ ariaLabel: () => ariaLabel,
46
+ })
47
+ </script>
48
+
49
+ <div class="sv-phone sv-phone--{size}" class:is-disabled={disabled}>
50
+ <div class="sv-phone__country">
51
+ <span class="sv-phone__flag" aria-hidden="true">{ph.flag}</span>
52
+ <span class="sv-phone__dial">{ph.dial}</span>
53
+ <select class="sv-phone__select" {...ph.selectProps()}>
54
+ {#each COUNTRIES as c (c.code)}
55
+ <option value={c.code}>{flagEmoji(c.code)} {c.name} ({c.dial})</option>
56
+ {/each}
57
+ </select>
58
+ <svg class="sv-phone__chev" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
59
+ </div>
60
+ <input class="sv-phone__number" {...ph.inputProps()} />
61
+ {#if name}<input type="hidden" {name} value={ph.value} />{/if}
62
+ </div>
63
+
64
+ <style>
65
+ .sv-phone {
66
+ --_accent: var(--sg-accent, #2563eb);
67
+ display: inline-flex; align-items: stretch; width: 250px;
68
+ background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a);
69
+ border: 1px solid var(--sg-input-border, var(--sg-border, #cbd5e1)); border-radius: var(--sg-radius, 8px);
70
+ overflow: hidden;
71
+ }
72
+ .sv-phone:focus-within { border-color: var(--_accent); box-shadow: 0 0 0 2px color-mix(in srgb, var(--_accent) 22%, transparent); }
73
+ .sv-phone.is-disabled { opacity: 0.6; }
74
+ .sv-phone__country {
75
+ position: relative; display: inline-flex; align-items: center; gap: 4px;
76
+ padding: 0 6px 0 10px; border-right: 1px solid var(--sg-border, #e2e8f0);
77
+ background: var(--sg-header-bg, #f8fafc); cursor: pointer;
78
+ }
79
+ .sv-phone__flag { font-size: 15px; }
80
+ .sv-phone__dial { font-size: 13px; font-weight: 600; }
81
+ .sv-phone__chev { color: var(--sg-muted, #64748b); }
82
+ .sv-phone__select { position: absolute; inset: 0; opacity: 0; cursor: pointer; font-size: 13px; }
83
+ .sv-phone__number { flex: 1; min-width: 0; border: 0; background: none; outline: none; color: inherit; font: inherit; padding: 0 10px; }
84
+ .sv-phone--sm { height: 28px; font-size: 12px; }
85
+ .sv-phone--md { height: 34px; font-size: 13px; }
86
+ .sv-phone--lg { height: 40px; font-size: 15px; }
87
+ </style>
@@ -0,0 +1,85 @@
1
+ <script lang="ts" module>
2
+ export type { RadioOption } from './createRadioGroup.svelte'
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ /**
7
+ * SvRadioGroup - an accessible radio group (WAI-ARIA radiogroup pattern) with
8
+ * roving tabindex + arrow-key navigation. Parity: Smart `smart-radio-button`
9
+ * (as a managed group). Controlled via `value` + `onChange`.
10
+ */
11
+ import { createRadioGroup, type RadioOption } from './createRadioGroup.svelte'
12
+
13
+ type Props = {
14
+ options: ReadonlyArray<RadioOption>
15
+ value?: string | number | null
16
+ onChange?: (value: string | number) => void
17
+ name?: string
18
+ disabled?: boolean
19
+ orientation?: 'vertical' | 'horizontal'
20
+ size?: 'sm' | 'md' | 'lg'
21
+ ariaLabel?: string
22
+ }
23
+
24
+ let {
25
+ options,
26
+ value = null,
27
+ onChange,
28
+ name,
29
+ disabled = false,
30
+ orientation = 'vertical',
31
+ size = 'md',
32
+ ariaLabel,
33
+ }: Props = $props()
34
+
35
+ // The styled radio group is just a renderer over the headless core.
36
+ const rg = createRadioGroup({
37
+ options: () => options,
38
+ value: () => value,
39
+ onChange: (v) => onChange?.(v),
40
+ disabled: () => disabled,
41
+ ariaLabel: () => ariaLabel,
42
+ })
43
+ </script>
44
+
45
+ <div
46
+ class="sv-radiogroup sv-radiogroup--{orientation} sv-radiogroup--{size}"
47
+ {...rg.groupProps()}
48
+ >
49
+ {#each options as opt (opt.value)}
50
+ <button
51
+ class="sv-radio"
52
+ class:is-checked={opt.value === value}
53
+ {...rg.radioProps(opt)}
54
+ >
55
+ <span class="sv-radio__dot"></span>
56
+ <span class="sv-radio__label">{opt.label}</span>
57
+ </button>
58
+ {/each}
59
+ {#if name}<input type="hidden" {name} value={value ?? ''} />{/if}
60
+ </div>
61
+
62
+ <style>
63
+ .sv-radiogroup { --_accent: var(--sg-accent, #2563eb); display: flex; gap: 10px; }
64
+ .sv-radiogroup--vertical { flex-direction: column; }
65
+ .sv-radiogroup--horizontal { flex-direction: row; flex-wrap: wrap; }
66
+ .sv-radio {
67
+ display: inline-flex; align-items: center; gap: 8px; background: none; border: 0; padding: 2px;
68
+ font: inherit; color: var(--sg-fg, #0f172a); cursor: pointer; user-select: none;
69
+ }
70
+ .sv-radiogroup--sm .sv-radio { font-size: 12px; }
71
+ .sv-radiogroup--md .sv-radio { font-size: 13px; }
72
+ .sv-radiogroup--lg .sv-radio { font-size: 15px; }
73
+ .sv-radio[disabled] { opacity: 0.5; cursor: not-allowed; }
74
+ .sv-radio__dot {
75
+ width: 17px; height: 17px; flex: none; border-radius: 50%;
76
+ border: 1.5px solid var(--sg-border, #cbd5e1); background: var(--sg-input-bg, #fff);
77
+ display: grid; place-items: center; transition: border-color 0.12s;
78
+ }
79
+ .sv-radio.is-checked .sv-radio__dot { border-color: var(--_accent); }
80
+ .sv-radio.is-checked .sv-radio__dot::after {
81
+ content: ''; width: 9px; height: 9px; border-radius: 50%; background: var(--_accent);
82
+ }
83
+ .sv-radio:focus-visible { outline: none; }
84
+ .sv-radio:focus-visible .sv-radio__dot { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
85
+ </style>
@@ -0,0 +1,91 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvRating - a star rating control (WAI-ARIA slider). Parity: Smart
4
+ * `smart-rating`. Hover preview, keyboard, optional half steps, readOnly.
5
+ * Uses the grid's `--sg-rating-*` tokens so it matches the in-grid editor.
6
+ */
7
+ import { createRating } from './createRating.svelte'
8
+
9
+ type Props = {
10
+ value?: number
11
+ onChange?: (value: number) => void
12
+ max?: number
13
+ /** Allow half-star precision. */
14
+ allowHalf?: boolean
15
+ /** Read-only: shown but not editable (the kit-standard name). */
16
+ readonly?: boolean
17
+ /** @deprecated use `readonly` - kept for back-compat. */
18
+ readOnly?: boolean
19
+ disabled?: boolean
20
+ size?: 'sm' | 'md' | 'lg'
21
+ name?: string
22
+ ariaLabel?: string
23
+ }
24
+
25
+ let {
26
+ value = 0,
27
+ onChange,
28
+ max = 5,
29
+ allowHalf = false,
30
+ readonly = false,
31
+ readOnly = false,
32
+ disabled = false,
33
+ size = 'md',
34
+ name,
35
+ ariaLabel,
36
+ }: Props = $props()
37
+
38
+ const ro = $derived(readonly || readOnly)
39
+
40
+ // The styled rating is just a renderer over the headless core.
41
+ const r = createRating({
42
+ value: () => value,
43
+ onChange: (v) => onChange?.(v),
44
+ max: () => max,
45
+ allowHalf: () => allowHalf,
46
+ readonly: () => ro,
47
+ disabled: () => disabled,
48
+ ariaLabel: () => ariaLabel,
49
+ })
50
+ </script>
51
+
52
+ <div
53
+ class="sv-rating sv-rating--{size}"
54
+ class:is-readonly={ro}
55
+ class:is-disabled={disabled}
56
+ {...r.rootProps()}
57
+ >
58
+ {#each Array(max) as _, i (i)}
59
+ <button
60
+ class="sv-rating__star is-{r.fillOf(i)}"
61
+ {...r.starProps(i)}
62
+ >
63
+ <svg viewBox="0 0 24 24" aria-hidden="true">
64
+ <defs>
65
+ <linearGradient id="half-{i}"><stop offset="50%" stop-color="var(--_on)" /><stop offset="50%" stop-color="var(--_off)" /></linearGradient>
66
+ </defs>
67
+ <path d="M12 2.5l2.9 6.1 6.6.9-4.8 4.6 1.2 6.6L12 18.6 6.1 21.7l1.2-6.6L2.5 9.5l6.6-.9z"
68
+ fill={r.fillOf(i) === 'full' ? 'var(--_on)' : r.fillOf(i) === 'half' ? `url(#half-${i})` : 'var(--_off)'} />
69
+ </svg>
70
+ </button>
71
+ {/each}
72
+ {#if name}<input type="hidden" {name} value={value} />{/if}
73
+ </div>
74
+
75
+ <style>
76
+ .sv-rating {
77
+ --_on: var(--sg-rating-on, #f59e0b);
78
+ --_off: var(--sg-rating-empty, #d1d5db);
79
+ --_sz: 22px;
80
+ display: inline-flex; gap: 2px; align-items: center;
81
+ }
82
+ .sv-rating--sm { --_sz: 16px; }
83
+ .sv-rating--lg { --_sz: 30px; }
84
+ .sv-rating.is-disabled { opacity: 0.5; }
85
+ .sv-rating__star {
86
+ width: var(--_sz); height: var(--_sz); padding: 0; border: 0; background: none; cursor: pointer; line-height: 0;
87
+ }
88
+ .sv-rating.is-readonly .sv-rating__star, .sv-rating.is-disabled .sv-rating__star { cursor: default; }
89
+ .sv-rating__star svg { width: 100%; height: 100%; }
90
+ .sv-rating:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--sg-accent, #2563eb)); outline-offset: 2px; border-radius: 4px; }
91
+ </style>
@@ -0,0 +1,89 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SvRepeatButton - fires `onclick` repeatedly while held down (after an
4
+ * initial delay, then at a steady interval). Parity: Smart `smart-repeat-button`.
5
+ * Used for spinners / steppers.
6
+ */
7
+ import type { Snippet } from 'svelte'
8
+
9
+ type Props = {
10
+ onclick?: () => void
11
+ /** Delay before repeats begin (ms). */
12
+ delay?: number
13
+ /** Interval between repeats (ms). */
14
+ interval?: number
15
+ disabled?: boolean
16
+ size?: 'sm' | 'md' | 'lg'
17
+ variant?: 'primary' | 'secondary' | 'outline' | 'ghost'
18
+ ariaLabel?: string
19
+ children?: Snippet
20
+ }
21
+
22
+ let {
23
+ onclick,
24
+ delay = 300,
25
+ interval = 60,
26
+ disabled = false,
27
+ size = 'md',
28
+ variant = 'secondary',
29
+ ariaLabel,
30
+ children,
31
+ }: Props = $props()
32
+
33
+ let delayTimer: ReturnType<typeof setTimeout> | null = null
34
+ let repeatTimer: ReturnType<typeof setInterval> | null = null
35
+
36
+ function stop() {
37
+ if (delayTimer) { clearTimeout(delayTimer); delayTimer = null }
38
+ if (repeatTimer) { clearInterval(repeatTimer); repeatTimer = null }
39
+ }
40
+
41
+ function start(e: PointerEvent) {
42
+ if (disabled || e.button !== 0) return
43
+ onclick?.() // fire once immediately
44
+ delayTimer = setTimeout(() => {
45
+ repeatTimer = setInterval(() => onclick?.(), Math.max(16, interval))
46
+ }, Math.max(0, delay))
47
+ }
48
+
49
+ function onKeydown(e: KeyboardEvent) {
50
+ if (disabled) return
51
+ if ((e.key === 'Enter' || e.key === ' ') && !e.repeat) { e.preventDefault(); onclick?.() }
52
+ }
53
+ </script>
54
+
55
+ <svelte:window onpointerup={stop} onpointercancel={stop} />
56
+
57
+ <button
58
+ type="button"
59
+ class="sv-btn sv-btn--{variant} sv-btn--{size} sv-repeat"
60
+ {disabled}
61
+ aria-label={ariaLabel}
62
+ onpointerdown={start}
63
+ onpointerleave={stop}
64
+ onkeydown={onKeydown}
65
+ >
66
+ {#if children}{@render children()}{/if}
67
+ </button>
68
+
69
+ <style>
70
+ /* Reuses the SvButton visual language (kept local so it works standalone). */
71
+ .sv-btn {
72
+ --_accent: var(--sg-accent, #2563eb);
73
+ --_border: var(--sg-border, #cbd5e1);
74
+ --_radius: var(--sg-radius, 8px);
75
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
76
+ font: inherit; font-weight: 600; line-height: 1; border: 1px solid transparent;
77
+ border-radius: var(--_radius); cursor: pointer; user-select: none; touch-action: none;
78
+ }
79
+ .sv-btn--sm { padding: 5px 9px; font-size: 12px; }
80
+ .sv-btn--md { padding: 8px 12px; font-size: 13px; }
81
+ .sv-btn--lg { padding: 11px 16px; font-size: 15px; }
82
+ .sv-btn[disabled] { opacity: 0.55; cursor: not-allowed; }
83
+ .sv-btn--primary { background: var(--_accent); color: var(--sg-on-accent, #fff); }
84
+ .sv-btn--secondary { background: var(--sg-header-bg, #f1f5f9); color: var(--sg-fg, #0f172a); border-color: var(--_border); }
85
+ .sv-btn--outline { background: transparent; color: var(--_accent); border-color: var(--_accent); }
86
+ .sv-btn--ghost { background: transparent; color: var(--sg-fg, #0f172a); }
87
+ .sv-btn:focus-visible { outline: 2px solid var(--sg-focus-ring, var(--_accent)); outline-offset: 2px; }
88
+ .sv-btn:active { transform: translateY(0.5px); }
89
+ </style>
@@ -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>