@geoffcox/sterling-svelte 2.0.2 → 2.0.3

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 (131) hide show
  1. package/README.md +18 -0
  2. package/dist/@types/clickOutside.d.ts +15 -0
  3. package/dist/Button.svelte +29 -0
  4. package/dist/Button.svelte.d.ts +8 -0
  5. package/dist/Callout.svelte +243 -0
  6. package/dist/Callout.svelte.d.ts +14 -0
  7. package/dist/Callout.types.d.ts +11 -0
  8. package/dist/Callout.types.js +1 -0
  9. package/dist/Checkbox.svelte +62 -0
  10. package/dist/Checkbox.svelte.d.ts +9 -0
  11. package/dist/Dialog.svelte +201 -0
  12. package/dist/Dialog.svelte.d.ts +14 -0
  13. package/dist/Dropdown.svelte +159 -0
  14. package/dist/Dropdown.svelte.d.ts +23 -0
  15. package/dist/Input.svelte +80 -0
  16. package/dist/Input.svelte.d.ts +11 -0
  17. package/dist/Label.constants.d.ts +2 -0
  18. package/dist/Label.constants.js +2 -0
  19. package/dist/Label.svelte +135 -0
  20. package/dist/Label.svelte.d.ts +17 -0
  21. package/dist/Link.svelte +31 -0
  22. package/dist/Link.svelte.d.ts +11 -0
  23. package/dist/List.constants.d.ts +1 -0
  24. package/dist/List.constants.js +1 -0
  25. package/dist/List.svelte +258 -0
  26. package/dist/List.svelte.d.ts +19 -0
  27. package/dist/List.types.d.ts +5 -0
  28. package/dist/List.types.js +1 -0
  29. package/dist/ListItem.svelte +64 -0
  30. package/dist/ListItem.svelte.d.ts +12 -0
  31. package/dist/Menu.svelte +105 -0
  32. package/dist/Menu.svelte.d.ts +12 -0
  33. package/dist/MenuBar.constants.d.ts +1 -0
  34. package/dist/MenuBar.constants.js +1 -0
  35. package/dist/MenuBar.svelte +144 -0
  36. package/dist/MenuBar.svelte.d.ts +12 -0
  37. package/dist/MenuBar.types.d.ts +4 -0
  38. package/dist/MenuBar.types.js +1 -0
  39. package/dist/MenuButton.svelte +156 -0
  40. package/dist/MenuButton.svelte.d.ts +20 -0
  41. package/dist/MenuItem.constants.d.ts +2 -0
  42. package/dist/MenuItem.constants.js +2 -0
  43. package/dist/MenuItem.svelte +431 -0
  44. package/dist/MenuItem.svelte.d.ts +22 -0
  45. package/dist/MenuItem.types.d.ts +20 -0
  46. package/dist/MenuItem.types.js +1 -0
  47. package/dist/MenuItem.utils.d.ts +7 -0
  48. package/dist/MenuItem.utils.js +36 -0
  49. package/dist/MenuSeparator.svelte +11 -0
  50. package/dist/MenuSeparator.svelte.d.ts +5 -0
  51. package/dist/Pagination.svelte +267 -0
  52. package/dist/Pagination.svelte.d.ts +4 -0
  53. package/dist/Pagination.types.d.ts +24 -0
  54. package/dist/Pagination.types.js +1 -0
  55. package/dist/Popover.constants.d.ts +1 -0
  56. package/dist/Popover.constants.js +14 -0
  57. package/dist/Popover.svelte +175 -0
  58. package/dist/Popover.svelte.d.ts +14 -0
  59. package/dist/Popover.types.d.ts +4 -0
  60. package/dist/Popover.types.js +1 -0
  61. package/dist/Portal.constants.d.ts +2 -0
  62. package/dist/Portal.constants.js +2 -0
  63. package/dist/Portal.types.d.ts +3 -0
  64. package/dist/Portal.types.js +1 -0
  65. package/dist/Progress.constants.d.ts +1 -0
  66. package/dist/Progress.constants.js +1 -0
  67. package/dist/Progress.svelte +61 -0
  68. package/dist/Progress.svelte.d.ts +11 -0
  69. package/dist/Progress.types.d.ts +4 -0
  70. package/dist/Progress.types.js +1 -0
  71. package/dist/Radio.svelte +65 -0
  72. package/dist/Radio.svelte.d.ts +12 -0
  73. package/dist/Select.svelte +262 -0
  74. package/dist/Select.svelte.d.ts +26 -0
  75. package/dist/Slider.svelte +182 -0
  76. package/dist/Slider.svelte.d.ts +18 -0
  77. package/dist/Switch.svelte +92 -0
  78. package/dist/Switch.svelte.d.ts +21 -0
  79. package/dist/Tab.svelte +66 -0
  80. package/dist/Tab.svelte.d.ts +11 -0
  81. package/dist/TabList.constants.d.ts +1 -0
  82. package/dist/TabList.constants.js +1 -0
  83. package/dist/TabList.svelte +253 -0
  84. package/dist/TabList.svelte.d.ts +17 -0
  85. package/dist/TabList.types.d.ts +5 -0
  86. package/dist/TabList.types.js +1 -0
  87. package/dist/TextArea.constants.d.ts +1 -0
  88. package/dist/TextArea.constants.js +1 -0
  89. package/dist/TextArea.svelte +116 -0
  90. package/dist/TextArea.svelte.d.ts +18 -0
  91. package/dist/TextArea.types.d.ts +4 -0
  92. package/dist/TextArea.types.js +1 -0
  93. package/dist/Tooltip.svelte +95 -0
  94. package/dist/Tooltip.svelte.d.ts +10 -0
  95. package/dist/Tree.constants.d.ts +1 -0
  96. package/dist/Tree.constants.js +1 -0
  97. package/dist/Tree.svelte +81 -0
  98. package/dist/Tree.svelte.d.ts +14 -0
  99. package/dist/Tree.types.d.ts +5 -0
  100. package/dist/Tree.types.js +1 -0
  101. package/dist/TreeChevron.svelte +39 -0
  102. package/dist/TreeChevron.svelte.d.ts +8 -0
  103. package/dist/TreeItem.constants.d.ts +1 -0
  104. package/dist/TreeItem.constants.js +1 -0
  105. package/dist/TreeItem.svelte +396 -0
  106. package/dist/TreeItem.svelte.d.ts +22 -0
  107. package/dist/TreeItem.types.d.ts +4 -0
  108. package/dist/TreeItem.types.js +1 -0
  109. package/dist/actions/applyLightDarkMode.d.ts +10 -0
  110. package/dist/actions/applyLightDarkMode.js +36 -0
  111. package/dist/actions/clickOutside.d.ts +15 -0
  112. package/dist/actions/clickOutside.js +28 -0
  113. package/dist/actions/extraClass.d.ts +9 -0
  114. package/dist/actions/extraClass.js +15 -0
  115. package/dist/actions/forwardEvents.d.ts +12 -0
  116. package/dist/actions/forwardEvents.js +32 -0
  117. package/dist/actions/portal.d.ts +8 -0
  118. package/dist/actions/portal.js +19 -0
  119. package/dist/actions/trapKeyboardFocus.d.ts +3 -0
  120. package/dist/actions/trapKeyboardFocus.js +52 -0
  121. package/dist/idGenerator.d.ts +5 -0
  122. package/dist/idGenerator.js +11 -0
  123. package/dist/index.d.ts +60 -0
  124. package/dist/index.js +55 -0
  125. package/dist/mediaQueries/prefersColorSchemeDark.d.ts +1 -0
  126. package/dist/mediaQueries/prefersColorSchemeDark.js +14 -0
  127. package/dist/mediaQueries/prefersReducedMotion.d.ts +1 -0
  128. package/dist/mediaQueries/prefersReducedMotion.js +14 -0
  129. package/dist/mediaQueries/usingKeyboard.d.ts +1 -0
  130. package/dist/mediaQueries/usingKeyboard.js +17 -0
  131. package/package.json +8 -7
@@ -0,0 +1,65 @@
1
+ <svelte:options runes={true} />
2
+
3
+ <script lang="ts">
4
+ import type { HTMLInputAttributes } from 'svelte/elements';
5
+ import { usingKeyboard } from './mediaQueries/usingKeyboard';
6
+
7
+ const uuid = $props.id();
8
+
9
+ type Props = HTMLInputAttributes & {
10
+ group?: any | null;
11
+ };
12
+
13
+ let {
14
+ id,
15
+ children,
16
+ checked = $bindable(false),
17
+ class: _class,
18
+ disabled = false,
19
+ group = $bindable(),
20
+ ...rest
21
+ }: Props = $props();
22
+
23
+ let inputRef: HTMLInputElement;
24
+
25
+ $effect(() => {
26
+ if (children && id === undefined) {
27
+ id = `Radio-${uuid}`;
28
+ }
29
+ });
30
+
31
+ // ----- Methods ----- //
32
+
33
+ export const blur = () => {
34
+ inputRef?.blur();
35
+ };
36
+
37
+ export const click = () => {
38
+ inputRef?.click();
39
+ };
40
+
41
+ export const focus = (options?: FocusOptions) => {
42
+ inputRef?.focus(options);
43
+ };
44
+ </script>
45
+
46
+ <!--
47
+ @component
48
+ A styled HTML input type=radio element with optional label.
49
+ -->
50
+ <div
51
+ class={['sterling-radio', _class]}
52
+ class:checked
53
+ class:disabled
54
+ class:using-keyboard={$usingKeyboard}
55
+ >
56
+ <div class="container">
57
+ <input bind:this={inputRef} checked {disabled} bind:group {id} type="radio" {...rest} />
58
+ <div class="indicator"></div>
59
+ </div>
60
+ {#if children}
61
+ <label for={id}>
62
+ {@render children()}
63
+ </label>
64
+ {/if}
65
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ type Props = HTMLInputAttributes & {
3
+ group?: any | null;
4
+ };
5
+ /** A styled HTML input type=radio element with optional label. */
6
+ declare const Radio: import("svelte").Component<Props, {
7
+ blur: () => void;
8
+ click: () => void;
9
+ focus: (options?: FocusOptions) => void;
10
+ }, "group" | "checked">;
11
+ type Radio = ReturnType<typeof Radio>;
12
+ export default Radio;
@@ -0,0 +1,262 @@
1
+ <svelte:options runes={true} />
2
+
3
+ <script lang="ts">
4
+ import { tick, type Snippet } from 'svelte';
5
+ import type { HTMLAttributes, KeyboardEventHandler, MouseEventHandler } from 'svelte/elements';
6
+ import { clickOutside } from './actions/clickOutside';
7
+ import List from './List.svelte';
8
+ import Popover from './Popover.svelte';
9
+
10
+ const uuid = $props.id();
11
+
12
+ type DeprecatedProps = {
13
+ /** @deprecated Use icon instead */
14
+ buttonSnippet?: Snippet;
15
+
16
+ /** @deprecated Use value instead */
17
+ valueSnippet?: Snippet<[string | undefined]>;
18
+ };
19
+
20
+ type Props = HTMLAttributes<HTMLDivElement> &
21
+ DeprecatedProps & {
22
+ disabled?: boolean | null;
23
+ icon?: Snippet;
24
+ listClass?: string;
25
+ onPending?: (value?: string) => void;
26
+ onSelect?: (value?: string) => void;
27
+ open?: boolean | null;
28
+ selectedValue?: string;
29
+ value?: string | Snippet<[string | undefined]>;
30
+ };
31
+
32
+ let {
33
+ children,
34
+ class: _class,
35
+ disabled = false,
36
+ icon,
37
+ listClass,
38
+ open = $bindable(false),
39
+ onSelect,
40
+ onPending,
41
+ selectedValue = $bindable(),
42
+ value,
43
+ buttonSnippet,
44
+ valueSnippet,
45
+ ...rest
46
+ }: Props = $props();
47
+
48
+ // backwards compatibility
49
+ icon = icon || buttonSnippet;
50
+ value = value || valueSnippet;
51
+
52
+ const popoverId = `Select-Popover-${uuid}`;
53
+
54
+ // ----- State ----- //
55
+
56
+ // Tracks the pending selected index
57
+ let pendingSelectedValue: string | undefined = $state(selectedValue);
58
+
59
+ // svelte-ignore non_reactive_update
60
+ let selectRef: HTMLElement | undefined = $state(undefined);
61
+ let listRef: List;
62
+
63
+ // ----- Reactions ----- //
64
+
65
+ $effect(() => {
66
+ pendingSelectedValue = selectedValue;
67
+ });
68
+
69
+ $effect(() => {
70
+ onSelect?.(selectedValue);
71
+ });
72
+
73
+ $effect(() => {
74
+ if (open) {
75
+ onPending?.(pendingSelectedValue);
76
+ }
77
+ });
78
+
79
+ $effect(() => {
80
+ if (open) {
81
+ tick().then(() => {
82
+ setTimeout(() => {
83
+ listRef?.focus();
84
+ listRef?.scrollToSelectedItem();
85
+ }, 10);
86
+ });
87
+ }
88
+ });
89
+
90
+ $effect(() => {
91
+ if (!open) {
92
+ tick().then(() => selectRef?.focus());
93
+ }
94
+ });
95
+
96
+ // ----- Methods ----- //
97
+
98
+ export const blur = () => {
99
+ selectRef?.blur();
100
+ };
101
+
102
+ export const click = () => {
103
+ selectRef?.click();
104
+ };
105
+
106
+ export const focus = (options?: FocusOptions) => {
107
+ selectRef?.focus();
108
+ };
109
+
110
+ export const scrollToSelectedItem = () => {
111
+ if (open) {
112
+ listRef?.scrollToSelectedItem();
113
+ }
114
+ };
115
+
116
+ // ----- Event Handlers ----- //
117
+
118
+ const onSelectClick: MouseEventHandler<HTMLDivElement> = (event) => {
119
+ if (!disabled) {
120
+ open = !open;
121
+ event.preventDefault();
122
+ event.stopPropagation();
123
+ }
124
+ rest.onclick?.(event);
125
+ };
126
+
127
+ const onSelectKeydown: KeyboardEventHandler<HTMLDivElement> = (event) => {
128
+ if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
129
+ switch (event.key) {
130
+ case ' ':
131
+ {
132
+ if (!open) {
133
+ open = true;
134
+ }
135
+ event.preventDefault();
136
+ event.stopPropagation();
137
+ return false;
138
+ }
139
+ break;
140
+ case 'ArrowUp':
141
+ {
142
+ if (selectedValue) {
143
+ listRef?.selectPreviousItem();
144
+ } else {
145
+ listRef?.selectLastItem();
146
+ }
147
+ event.preventDefault();
148
+ event.stopPropagation();
149
+ return false;
150
+ }
151
+ break;
152
+ case 'ArrowDown':
153
+ {
154
+ if (selectedValue) {
155
+ listRef?.selectNextItem();
156
+ } else {
157
+ listRef?.selectFirstItem();
158
+ }
159
+ event.preventDefault();
160
+ event.stopPropagation();
161
+ return false;
162
+ }
163
+ break;
164
+ }
165
+ }
166
+ rest.onkeydown?.(event);
167
+ };
168
+
169
+ const onListKeydown = (event: KeyboardEvent) => {
170
+ if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
171
+ switch (event.key) {
172
+ case 'Enter':
173
+ {
174
+ selectedValue = pendingSelectedValue;
175
+ open = !open;
176
+ event.preventDefault();
177
+ event.stopPropagation();
178
+ return false;
179
+ }
180
+ break;
181
+ case 'Escape':
182
+ {
183
+ pendingSelectedValue = selectedValue;
184
+ open = !open;
185
+ event.preventDefault();
186
+ event.stopPropagation();
187
+ return false;
188
+ }
189
+ break;
190
+ }
191
+ }
192
+ };
193
+
194
+ const onListClick = (event: MouseEvent) => {
195
+ open = false;
196
+ event.preventDefault();
197
+ event.stopPropagation();
198
+ return false;
199
+ };
200
+
201
+ const onListSelect = (value?: string) => {
202
+ pendingSelectedValue = value;
203
+ if (!open) {
204
+ selectedValue = pendingSelectedValue;
205
+ }
206
+ };
207
+ </script>
208
+
209
+ <div
210
+ bind:this={selectRef}
211
+ aria-controls={popoverId}
212
+ aria-haspopup="listbox"
213
+ aria-expanded={open}
214
+ class={['sterling-select', _class]}
215
+ class:disabled
216
+ role="combobox"
217
+ tabindex="0"
218
+ use:clickOutside={{ onclickoutside: () => (open = false) }}
219
+ {...rest}
220
+ onclick={onSelectClick}
221
+ onkeydown={onSelectKeydown}
222
+ >
223
+ <div class="value">
224
+ {#if value}
225
+ {#if typeof value === 'string'}
226
+ {value}
227
+ {:else}
228
+ {@render value(selectedValue)}
229
+ {/if}
230
+ {:else if selectedValue}
231
+ {selectedValue}
232
+ {:else}
233
+ <span>&nbsp;</span>
234
+ {/if}
235
+ </div>
236
+ <!-- button class was initially incorrect, so leaving it here for now -->
237
+ <div class="button icon">
238
+ {#if icon}
239
+ {@render icon()}
240
+ {:else}
241
+ <div class="chevron"></div>
242
+ {/if}
243
+ </div>
244
+ <Popover id={popoverId} reference={selectRef} bind:open conditionalRender={false}>
245
+ <div class={['sterling-select-popup-content', 'sterling-select-content', _class]}>
246
+ <List
247
+ bind:this={listRef}
248
+ {disabled}
249
+ selectedValue={pendingSelectedValue}
250
+ onclick={onListClick}
251
+ onkeydown={onListKeydown}
252
+ onSelect={onListSelect}
253
+ tabindex={open ? 0 : -1}
254
+ class={['composed', listClass]}
255
+ >
256
+ {#if children}
257
+ {@render children()}
258
+ {/if}
259
+ </List>
260
+ </div>
261
+ </Popover>
262
+ </div>
@@ -0,0 +1,26 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ type DeprecatedProps = {
4
+ /** @deprecated Use icon instead */
5
+ buttonSnippet?: Snippet;
6
+ /** @deprecated Use value instead */
7
+ valueSnippet?: Snippet<[string | undefined]>;
8
+ };
9
+ type Props = HTMLAttributes<HTMLDivElement> & DeprecatedProps & {
10
+ disabled?: boolean | null;
11
+ icon?: Snippet;
12
+ listClass?: string;
13
+ onPending?: (value?: string) => void;
14
+ onSelect?: (value?: string) => void;
15
+ open?: boolean | null;
16
+ selectedValue?: string;
17
+ value?: string | Snippet<[string | undefined]>;
18
+ };
19
+ declare const Select: import("svelte").Component<Props, {
20
+ blur: () => void;
21
+ click: () => void;
22
+ focus: (options?: FocusOptions) => void;
23
+ scrollToSelectedItem: () => void;
24
+ }, "open" | "selectedValue">;
25
+ type Select = ReturnType<typeof Select>;
26
+ export default Select;
@@ -0,0 +1,182 @@
1
+ <svelte:options runes={true} />
2
+
3
+ <script lang="ts">
4
+ import { round } from 'lodash-es';
5
+ import type { HTMLAttributes, KeyboardEventHandler, PointerEventHandler } from 'svelte/elements';
6
+
7
+ type Props = HTMLAttributes<HTMLDivElement> & {
8
+ disabled?: boolean | null;
9
+ min?: number;
10
+ max?: number;
11
+ precision?: number;
12
+ step?: number;
13
+ value?: number;
14
+ vertical?: boolean | null;
15
+ onChange?: (value: number) => void;
16
+ };
17
+
18
+ let {
19
+ class: _class,
20
+ disabled,
21
+ min = 0,
22
+ max = 100,
23
+ onChange,
24
+ precision = 0,
25
+ step = 1,
26
+ value = $bindable(0),
27
+ vertical,
28
+ ...rest
29
+ }: Props = $props();
30
+
31
+ let sliderRef: HTMLDivElement;
32
+
33
+ export const blur = () => {
34
+ sliderRef?.blur();
35
+ };
36
+
37
+ export const click = () => {
38
+ sliderRef?.click();
39
+ };
40
+
41
+ export const focus = (options?: FocusOptions) => {
42
+ sliderRef?.parentElement?.focus(options);
43
+ };
44
+
45
+ let ratio = $derived((value - min) / (max - min));
46
+
47
+ const setValue = (newValue: number) => {
48
+ const clamped = Math.max(min, Math.min(max, newValue));
49
+ value = precision !== undefined ? round(clamped, precision) : clamped;
50
+ };
51
+
52
+ // ensure min <= max
53
+ $effect(() => {
54
+ if (min > max) {
55
+ min = max;
56
+ }
57
+ });
58
+
59
+ $effect(() => {
60
+ const clamped = Math.max(min, Math.min(max, value));
61
+ const newValue = precision !== undefined ? round(clamped, precision) : clamped;
62
+ if (value !== newValue) {
63
+ value = newValue;
64
+ }
65
+ });
66
+
67
+ const setValueByOffset = (offset: number) => {
68
+ if (sliderSize > 0) {
69
+ const positionRatio = Math.max(0, Math.min(1, offset / sliderSize));
70
+ const newValue = min + positionRatio * (max - min);
71
+ setValue(newValue);
72
+ }
73
+ };
74
+
75
+ // Raise change event when value changes
76
+ $effect(() => {
77
+ onChange?.(value);
78
+ });
79
+
80
+ // ----- Size tracking ----- //
81
+
82
+ let sliderWidth: number = $state(0);
83
+ let sliderHeight: number = $state(0);
84
+
85
+ let sliderSize = $derived(vertical ? sliderHeight : sliderWidth);
86
+
87
+ let valueOffset = $derived(sliderSize * ratio);
88
+
89
+ // ----- Event handlers ----- //
90
+
91
+ const onPointerDown: PointerEventHandler<HTMLDivElement> = (event) => {
92
+ if (!disabled) {
93
+ event.currentTarget.setPointerCapture(event.pointerId);
94
+ if (vertical) {
95
+ setValueByOffset(sliderRef.getBoundingClientRect().bottom - event.y);
96
+ } else {
97
+ setValueByOffset(event.x - sliderRef.getBoundingClientRect().left);
98
+ }
99
+ event.preventDefault();
100
+ focus();
101
+ }
102
+
103
+ rest?.onpointerdown?.(event);
104
+ };
105
+
106
+ const onPointerMove: PointerEventHandler<HTMLDivElement> = (event) => {
107
+ if (!disabled && event.currentTarget.hasPointerCapture(event.pointerId)) {
108
+ if (vertical) {
109
+ setValueByOffset(sliderRef.getBoundingClientRect().bottom - event.y);
110
+ } else {
111
+ setValueByOffset(event.x - sliderRef.getBoundingClientRect().left);
112
+ }
113
+ event.preventDefault();
114
+ }
115
+
116
+ rest?.onpointermove?.(event);
117
+ };
118
+
119
+ const onPointerUp: PointerEventHandler<HTMLDivElement> = (event) => {
120
+ if (!disabled) {
121
+ event.currentTarget.releasePointerCapture(event.pointerId);
122
+ event.preventDefault();
123
+ focus();
124
+ }
125
+ rest?.onpointerup?.(event);
126
+ };
127
+
128
+ const onKeyDown: KeyboardEventHandler<HTMLDivElement> = (event) => {
129
+ if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey) {
130
+ switch (event.code) {
131
+ case 'ArrowDown':
132
+ case 'ArrowLeft':
133
+ setValue(value - step);
134
+ event.preventDefault();
135
+ event.stopPropagation();
136
+ return;
137
+ case 'ArrowRight':
138
+ case 'ArrowUp':
139
+ setValue(value + step);
140
+ event.preventDefault();
141
+ event.stopPropagation();
142
+ return;
143
+ }
144
+ }
145
+ rest?.onkeydown?.(event);
146
+ };
147
+ </script>
148
+
149
+ <div
150
+ aria-disabled={disabled}
151
+ aria-valuemin={min}
152
+ aria-valuenow={value}
153
+ aria-valuemax={max}
154
+ class={['sterling-slider', _class]}
155
+ class:disabled
156
+ class:horizontal={!vertical}
157
+ class:vertical
158
+ role="slider"
159
+ tabindex={!disabled ? 0 : undefined}
160
+ {...rest}
161
+ onkeydown={onKeyDown}
162
+ onpointerdown={onPointerDown}
163
+ onpointermove={onPointerMove}
164
+ onpointerup={onPointerUp}
165
+ >
166
+ <div
167
+ class="container"
168
+ bind:this={sliderRef}
169
+ bind:clientWidth={sliderWidth}
170
+ bind:clientHeight={sliderHeight}
171
+ >
172
+ <div class="track"></div>
173
+ <div
174
+ class="fill"
175
+ style={vertical ? `height: ${valueOffset}px` : `width: ${valueOffset}px`}
176
+ ></div>
177
+ <div
178
+ class="thumb"
179
+ style={vertical ? `bottom: ${valueOffset}px` : `left: ${valueOffset}px`}
180
+ ></div>
181
+ </div>
182
+ </div>
@@ -0,0 +1,18 @@
1
+ import type { HTMLAttributes } from 'svelte/elements';
2
+ type Props = HTMLAttributes<HTMLDivElement> & {
3
+ disabled?: boolean | null;
4
+ min?: number;
5
+ max?: number;
6
+ precision?: number;
7
+ step?: number;
8
+ value?: number;
9
+ vertical?: boolean | null;
10
+ onChange?: (value: number) => void;
11
+ };
12
+ declare const Slider: import("svelte").Component<Props, {
13
+ blur: () => void;
14
+ click: () => void;
15
+ focus: (options?: FocusOptions) => void;
16
+ }, "value">;
17
+ type Slider = ReturnType<typeof Slider>;
18
+ export default Slider;
@@ -0,0 +1,92 @@
1
+ <svelte:options runes={true} />
2
+
3
+ <script lang="ts">
4
+ import type { Snippet } from 'svelte';
5
+ import type { HTMLInputAttributes } from 'svelte/elements';
6
+ import { usingKeyboard } from './mediaQueries/usingKeyboard';
7
+
8
+ const uuid = $props.id();
9
+
10
+ type LabelSnippet = Snippet<
11
+ [{ checked: boolean | null | undefined; disabled: boolean | null | undefined; inputId: string }]
12
+ >;
13
+
14
+ type Props = HTMLInputAttributes & {
15
+ offLabel?: string | LabelSnippet;
16
+ onLabel?: string | LabelSnippet;
17
+ vertical?: boolean | null | undefined;
18
+ };
19
+
20
+ let {
21
+ checked = $bindable(false),
22
+ class: _class,
23
+ disabled,
24
+ id,
25
+ offLabel,
26
+ onLabel,
27
+ vertical,
28
+ ...rest
29
+ }: Props = $props();
30
+
31
+ const inputId = id || `Switch-${uuid}`;
32
+
33
+ let inputRef: HTMLInputElement;
34
+
35
+ let switchWidth: number = $state(0);
36
+ let switchHeight: number = $state(0);
37
+ let thumbWidth: number = $state(0);
38
+ let thumbHeight: number = $state(0);
39
+
40
+ let switchSize = $derived(vertical ? switchHeight : switchWidth);
41
+ let thumbSize = $derived(vertical ? thumbHeight : thumbWidth);
42
+ let ratio = $derived(vertical ? (checked ? 0 : 1) : checked ? 1 : 0);
43
+ let valueOffset = $derived((switchSize - thumbSize) * ratio);
44
+
45
+ export const blur = () => {
46
+ inputRef?.blur();
47
+ };
48
+
49
+ export const click = () => {
50
+ inputRef?.click();
51
+ };
52
+
53
+ export const focus = (options?: FocusOptions) => {
54
+ inputRef?.focus(options);
55
+ };
56
+ </script>
57
+
58
+ <div
59
+ class={['sterling-switch', _class]}
60
+ class:checked
61
+ class:disabled
62
+ class:vertical
63
+ class:using-keyboard={$usingKeyboard}
64
+ >
65
+ <input bind:this={inputRef} bind:checked {disabled} id={inputId} type="checkbox" {...rest} />
66
+ {#if offLabel}
67
+ <div class="off-label">
68
+ {#if typeof offLabel === 'string'}
69
+ {offLabel}
70
+ {:else}
71
+ {@render offLabel({ checked, disabled, inputId })}
72
+ {/if}
73
+ </div>
74
+ {/if}
75
+ <div class="toggle" bind:offsetWidth={switchWidth} bind:offsetHeight={switchHeight}>
76
+ <div
77
+ class="thumb"
78
+ bind:offsetWidth={thumbWidth}
79
+ bind:offsetHeight={thumbHeight}
80
+ style={`--thumb-offset: ${valueOffset}px`}
81
+ ></div>
82
+ </div>
83
+ {#if onLabel}
84
+ <div class="on-label">
85
+ {#if typeof onLabel === 'string'}
86
+ {onLabel}
87
+ {:else}
88
+ {@render onLabel({ checked, disabled, inputId })}
89
+ {/if}
90
+ </div>
91
+ {/if}
92
+ </div>
@@ -0,0 +1,21 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ type LabelSnippet = Snippet<[
4
+ {
5
+ checked: boolean | null | undefined;
6
+ disabled: boolean | null | undefined;
7
+ inputId: string;
8
+ }
9
+ ]>;
10
+ type Props = HTMLInputAttributes & {
11
+ offLabel?: string | LabelSnippet;
12
+ onLabel?: string | LabelSnippet;
13
+ vertical?: boolean | null | undefined;
14
+ };
15
+ declare const Switch: import("svelte").Component<Props, {
16
+ blur: () => void;
17
+ click: () => void;
18
+ focus: (options?: FocusOptions) => void;
19
+ }, "checked">;
20
+ type Switch = ReturnType<typeof Switch>;
21
+ export default Switch;