@geoffcox/sterling-svelte 2.0.1 → 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 (71) hide show
  1. package/README.md +1 -1
  2. package/dist/Button.svelte +18 -14
  3. package/dist/Button.svelte.d.ts +0 -1
  4. package/dist/Callout.svelte +162 -96
  5. package/dist/Callout.svelte.d.ts +1 -2
  6. package/dist/Checkbox.svelte +34 -15
  7. package/dist/Checkbox.svelte.d.ts +0 -1
  8. package/dist/Dialog.svelte +121 -71
  9. package/dist/Dialog.svelte.d.ts +1 -1
  10. package/dist/Dropdown.svelte +106 -56
  11. package/dist/Dropdown.svelte.d.ts +8 -3
  12. package/dist/Input.svelte +54 -29
  13. package/dist/Input.svelte.d.ts +1 -2
  14. package/dist/Label.svelte +99 -55
  15. package/dist/Label.svelte.d.ts +4 -4
  16. package/dist/Link.svelte +20 -14
  17. package/dist/Link.svelte.d.ts +0 -1
  18. package/dist/List.svelte +181 -126
  19. package/dist/List.svelte.d.ts +0 -1
  20. package/dist/ListItem.svelte +36 -21
  21. package/dist/ListItem.svelte.d.ts +0 -1
  22. package/dist/Menu.svelte +67 -45
  23. package/dist/Menu.svelte.d.ts +0 -1
  24. package/dist/MenuBar.svelte +96 -65
  25. package/dist/MenuBar.svelte.d.ts +0 -1
  26. package/dist/MenuButton.svelte +102 -62
  27. package/dist/MenuButton.svelte.d.ts +1 -1
  28. package/dist/MenuItem.svelte +332 -243
  29. package/dist/MenuItem.svelte.d.ts +3 -3
  30. package/dist/MenuSeparator.svelte +7 -7
  31. package/dist/MenuSeparator.svelte.d.ts +0 -1
  32. package/dist/Pagination.svelte +267 -0
  33. package/dist/Pagination.svelte.d.ts +4 -0
  34. package/dist/Pagination.types.d.ts +24 -0
  35. package/dist/Pagination.types.js +1 -0
  36. package/dist/Popover.svelte +114 -60
  37. package/dist/Popover.svelte.d.ts +1 -2
  38. package/dist/Portal.types.d.ts +1 -4
  39. package/dist/Progress.svelte +40 -15
  40. package/dist/Progress.svelte.d.ts +0 -1
  41. package/dist/Radio.svelte +37 -25
  42. package/dist/Radio.svelte.d.ts +0 -1
  43. package/dist/Select.svelte +191 -125
  44. package/dist/Select.svelte.d.ts +8 -2
  45. package/dist/Slider.svelte +120 -71
  46. package/dist/Slider.svelte.d.ts +0 -1
  47. package/dist/Switch.svelte +51 -20
  48. package/dist/Switch.svelte.d.ts +1 -1
  49. package/dist/Tab.svelte +39 -24
  50. package/dist/Tab.svelte.d.ts +0 -1
  51. package/dist/TabList.svelte +176 -125
  52. package/dist/TabList.svelte.d.ts +0 -1
  53. package/dist/TextArea.svelte +83 -41
  54. package/dist/TextArea.svelte.d.ts +2 -3
  55. package/dist/Tooltip.svelte +68 -36
  56. package/dist/Tree.svelte +52 -24
  57. package/dist/Tree.svelte.d.ts +0 -1
  58. package/dist/TreeChevron.svelte +24 -12
  59. package/dist/TreeChevron.svelte.d.ts +0 -1
  60. package/dist/TreeItem.svelte +292 -225
  61. package/dist/TreeItem.svelte.d.ts +1 -1
  62. package/dist/actions/extraClass.d.ts +1 -0
  63. package/dist/actions/extraClass.js +1 -0
  64. package/dist/idGenerator.d.ts +1 -0
  65. package/dist/idGenerator.js +1 -0
  66. package/dist/index.d.ts +3 -2
  67. package/dist/index.js +3 -2
  68. package/dist/mediaQueries/prefersColorSchemeDark.d.ts +0 -1
  69. package/dist/mediaQueries/prefersReducedMotion.d.ts +0 -1
  70. package/dist/mediaQueries/usingKeyboard.d.ts +0 -1
  71. package/package.json +21 -22
@@ -0,0 +1,267 @@
1
+ <script lang="ts">
2
+ import type { ChangeEventHandler } from 'svelte/elements';
3
+ import type { PaginationProps } from './Pagination.types';
4
+
5
+ let {
6
+ class: _class,
7
+ itemCount,
8
+ itemRange = $bindable(), //readonly
9
+ page = $bindable(),
10
+ pageCount = $bindable(), //readonly
11
+ pageSize = 1,
12
+ pageStep = 10,
13
+ onChange,
14
+ firstNumber,
15
+ stepPreviousNumber,
16
+ previousNumber,
17
+ currentNumber,
18
+ nextNumber,
19
+ stepNextNumber,
20
+ lastNumber
21
+ }: PaginationProps = $props();
22
+
23
+ let _pageCount = $derived(
24
+ itemCount > 0 && pageSize > 0 ? Math.ceil(itemCount / pageSize) : undefined
25
+ );
26
+
27
+ // 1 time update of pageCount when loaded because effect runs too late
28
+ // svelte-ignore state_referenced_locally
29
+ pageCount = _pageCount;
30
+
31
+ // readonly props cannot be assigned $derived values
32
+ $effect(() => {
33
+ pageCount = _pageCount;
34
+ });
35
+
36
+ // clamp the page internally
37
+ let _page = $derived(_pageCount && page ? Math.max(1, Math.min(page, _pageCount)) : undefined);
38
+
39
+ let _itemRange = $derived(
40
+ _pageCount && _page
41
+ ? {
42
+ index: _page ? Math.max(0, (_page - 1) * pageSize) : 0,
43
+ count: _page ? Math.max(0, Math.min(pageSize, itemCount - (_page - 1) * pageSize)) : 0
44
+ }
45
+ : undefined
46
+ );
47
+
48
+ // 1 time update of itemRange when loaded because effect runs too late
49
+ // svelte-ignore state_referenced_locally
50
+ itemRange = _itemRange;
51
+
52
+ // readonly props cannot be assigned $derived values yet
53
+ $effect(() => {
54
+ itemRange = _itemRange;
55
+ });
56
+
57
+ $effect(() => {
58
+ onChange?.(_page, _itemRange);
59
+ });
60
+
61
+ let firstValue = $derived(_pageCount && _page && _page >= 3 ? 1 : undefined);
62
+ let stepPreviousValue = $derived(_pageCount && _page ? Math.max(1, _page - pageStep) : undefined);
63
+ let previousValue = $derived(_pageCount && _page && _page >= 2 ? _page - 1 : undefined);
64
+ // _page acts as the currentValue;
65
+ let nextValue = $derived(_pageCount && _page && _page <= _pageCount - 1 ? _page + 1 : undefined);
66
+ let stepNextValue = $derived(
67
+ _pageCount && _page ? Math.min(_pageCount, _page + pageStep) : undefined
68
+ );
69
+ let lastValue = $derived(_pageCount && _page && _page <= _pageCount - 2 ? _pageCount : undefined);
70
+
71
+ let numberOfDigits = $derived(_pageCount ? _pageCount.toString().length : 1);
72
+
73
+ // clamp page when the value changes
74
+ const setPage = (newPage?: number) => {
75
+ page = _pageCount && newPage ? Math.max(1, Math.min(newPage, _pageCount)) : undefined;
76
+ };
77
+
78
+ const onFirst = () => {
79
+ setPage(1);
80
+ };
81
+
82
+ const onStepPrevious = () => {
83
+ _page && setPage(_page - Math.abs(pageStep));
84
+ };
85
+
86
+ const onPrevious = () => {
87
+ _page && setPage(_page - 1);
88
+ };
89
+
90
+ const onNext = () => {
91
+ _page && setPage(_page + 1);
92
+ };
93
+
94
+ const onStepNext = () => {
95
+ _page && setPage(_page + Math.abs(pageStep));
96
+ };
97
+
98
+ const onLast = () => {
99
+ _pageCount && setPage(_pageCount);
100
+ };
101
+
102
+ const onInputChange: ChangeEventHandler<HTMLInputElement> = (event) => {
103
+ const inputValue = event.currentTarget.value;
104
+ if (inputValue.length > 0) {
105
+ const value = parseInt(inputValue, 10);
106
+ value && setPage(value);
107
+ }
108
+ };
109
+
110
+ let holdTimeout: NodeJS.Timeout | undefined = undefined;
111
+ let holdInterval: NodeJS.Timeout | undefined = undefined;
112
+
113
+ const startHoldButton = (
114
+ event: MouseEvent,
115
+ action: () => void,
116
+ delayMs: number = 500,
117
+ repeatMs: number = 100
118
+ ) => {
119
+ clearInterval(holdTimeout);
120
+ clearInterval(holdInterval);
121
+ if (event.button !== 0) {
122
+ return;
123
+ }
124
+ action();
125
+ holdTimeout = setTimeout(() => {
126
+ holdInterval = setInterval(action, repeatMs);
127
+ }, delayMs);
128
+ };
129
+
130
+ const stopHoldButton = (event: MouseEvent) => {
131
+ clearInterval(holdTimeout);
132
+ clearInterval(holdInterval);
133
+ };
134
+
135
+ const onKeydown = (event: KeyboardEvent, action: () => void) => {
136
+ if (event.key === ' ') {
137
+ event.preventDefault();
138
+ event.stopPropagation();
139
+ action();
140
+ return false;
141
+ }
142
+ };
143
+ </script>
144
+
145
+ <div class="sterling-pagination" style={`--page-number-width: ${numberOfDigits}ch`}>
146
+ <div class="page-number first">
147
+ <button
148
+ disabled={firstValue === undefined}
149
+ type="button"
150
+ onclick={onFirst}
151
+ onkeydown={(event) => onKeydown(event, onFirst)}
152
+ data-value={firstValue}
153
+ >
154
+ {#if firstNumber}
155
+ {@render firstNumber(firstValue)}
156
+ {:else if firstValue}
157
+ {firstValue}
158
+ {:else}
159
+ -
160
+ {/if}</button
161
+ >
162
+ </div>
163
+ <div class="page-number step-previous">
164
+ <button
165
+ disabled={stepPreviousValue === undefined}
166
+ type="button"
167
+ onmousedown={(event) => startHoldButton(event, onStepPrevious)}
168
+ onmouseup={stopHoldButton}
169
+ onmouseleave={stopHoldButton}
170
+ onkeydown={(event) => onKeydown(event, onStepPrevious)}
171
+ data-value={stepPreviousValue}
172
+ >
173
+ {#if stepPreviousNumber}
174
+ {@render stepPreviousNumber(stepPreviousValue)}
175
+ {:else if stepPreviousValue}
176
+ &lt;&lt;
177
+ {:else}
178
+ -
179
+ {/if}
180
+ </button>
181
+ </div>
182
+ <div class="page-number previous">
183
+ <button
184
+ disabled={previousValue === undefined}
185
+ type="button"
186
+ onmousedown={(event) => startHoldButton(event, onPrevious)}
187
+ onmouseup={stopHoldButton}
188
+ onmouseleave={stopHoldButton}
189
+ onkeydown={(event) => onKeydown(event, onPrevious)}
190
+ data-value={previousValue}
191
+ >
192
+ {#if previousNumber}
193
+ {@render previousNumber(previousValue)}
194
+ {:else if previousValue}
195
+ {previousValue}
196
+ {:else}
197
+ -
198
+ {/if}
199
+ </button>
200
+ </div>
201
+ <div class="page-number current">
202
+ {#if _page}
203
+ {#if currentNumber}
204
+ {@render currentNumber(_page)}
205
+ {:else}
206
+ <input disabled={_page === undefined} value={_page} onchange={onInputChange} />
207
+ {/if}
208
+ {:else}
209
+ -
210
+ {/if}
211
+ </div>
212
+ <div class="page-number next">
213
+ <button
214
+ disabled={nextValue === undefined}
215
+ type="button"
216
+ onmousedown={(event) => startHoldButton(event, onNext)}
217
+ onmouseup={stopHoldButton}
218
+ onmouseleave={stopHoldButton}
219
+ onkeydown={(event) => onKeydown(event, onNext)}
220
+ data-value={nextValue}
221
+ >
222
+ {#if nextNumber}
223
+ {@render nextNumber(nextValue)}
224
+ {:else if nextValue}
225
+ {nextValue}
226
+ {:else}
227
+ -
228
+ {/if}
229
+ </button>
230
+ </div>
231
+ <div class="page-number step-next">
232
+ <button
233
+ disabled={stepNextValue === undefined}
234
+ type="button"
235
+ onmousedown={(event) => startHoldButton(event, onStepNext)}
236
+ onmouseup={stopHoldButton}
237
+ onmouseleave={stopHoldButton}
238
+ onkeydown={(event) => onKeydown(event, onStepNext)}
239
+ data-value={stepNextValue}
240
+ >
241
+ {#if stepNextNumber}
242
+ {@render stepNextNumber(stepNextValue)}
243
+ {:else if stepNextValue}
244
+ &gt;&gt;
245
+ {:else}
246
+ -
247
+ {/if}
248
+ </button>
249
+ </div>
250
+ <div class="page-number last">
251
+ <button
252
+ disabled={lastValue === undefined}
253
+ type="button"
254
+ onclick={() => onLast()}
255
+ onkeydown={(event) => onKeydown(event, onLast)}
256
+ data-value={lastValue}
257
+ >
258
+ {#if lastNumber}
259
+ {@render lastNumber(lastValue)}
260
+ {:else if lastValue}
261
+ {lastValue}
262
+ {:else}
263
+ -
264
+ {/if}
265
+ </button>
266
+ </div>
267
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { PaginationProps } from './Pagination.types';
2
+ declare const Pagination: import("svelte").Component<PaginationProps, {}, "page" | "itemRange" | "pageCount">;
3
+ type Pagination = ReturnType<typeof Pagination>;
4
+ export default Pagination;
@@ -0,0 +1,24 @@
1
+ import { type Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ export type PaginationProps = HTMLAttributes<HTMLDivElement> & {
4
+ itemCount: number;
5
+ itemRange?: {
6
+ index: number;
7
+ count: number;
8
+ };
9
+ page?: number;
10
+ pageCount?: number;
11
+ pageSize?: number;
12
+ pageStep?: number;
13
+ onChange?: (page?: number, itemRange?: {
14
+ index: number;
15
+ count: number;
16
+ }) => void;
17
+ firstNumber?: Snippet<[number | undefined]>;
18
+ stepPreviousNumber?: Snippet<[number | undefined]>;
19
+ previousNumber?: Snippet<[number | undefined]>;
20
+ currentNumber?: Snippet<[number | undefined]>;
21
+ nextNumber?: Snippet<[number | undefined]>;
22
+ stepNextNumber?: Snippet<[number | undefined]>;
23
+ lastNumber?: Snippet<[number | undefined]>;
24
+ };
@@ -0,0 +1 @@
1
+ import {} from 'svelte';
@@ -1,93 +1,147 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">import { getContext, onMount, tick } from 'svelte';
4
- import { autoUpdate, computePosition, flip, offset } from '@floating-ui/dom';
5
- import { portal } from './actions/portal';
6
- import { STERLING_PORTAL_HOST_ID, STERLING_PORTAL_CONTEXT_ID } from './Portal.constants';
7
- let { children, conditionalRender = $bindable(true), crossAxisOffset = $bindable(0), mainAxisOffset = $bindable(0), open = $bindable(false), placement = $bindable('top-start'), portalHost, reference, class: _class, ...rest } = $props();
8
- let popupRef = $state(undefined);
9
- let popupPosition = $state({ x: 0, y: 0 });
10
- let floatingUIPlacement = $derived(placement);
11
- let bodyHeight = $state(0);
12
- let resizeObserver = undefined;
13
- const { portalHost: contextPortalHost } = getContext(STERLING_PORTAL_CONTEXT_ID) || {
3
+ <script lang="ts">
4
+ import {
5
+ autoUpdate,
6
+ computePosition,
7
+ flip,
8
+ offset,
9
+ type ComputePositionReturn,
10
+ type Placement
11
+ } from '@floating-ui/dom';
12
+ import { getContext, tick } from 'svelte';
13
+ import type { HTMLAttributes, KeyboardEventHandler } from 'svelte/elements';
14
+ import { portal } from './actions/portal';
15
+ import type { PopoverPlacement } from './Popover.types';
16
+ import { STERLING_PORTAL_CONTEXT_ID, STERLING_PORTAL_HOST_ID } from './Portal.constants';
17
+ import type { PortalContext } from './Portal.types';
18
+
19
+ // ----- Props ----- //
20
+
21
+ type Props = HTMLAttributes<HTMLDivElement> & {
22
+ conditionalRender?: boolean;
23
+ crossAxisOffset?: number;
24
+ mainAxisOffset?: number;
25
+ open?: boolean | null;
26
+ placement?: PopoverPlacement;
27
+ portalHost?: HTMLElement;
28
+ reference?: HTMLElement;
29
+ };
30
+
31
+ let {
32
+ children,
33
+ conditionalRender = $bindable(true),
34
+ crossAxisOffset = $bindable(0),
35
+ mainAxisOffset = $bindable(0),
36
+ open = $bindable(false),
37
+ placement = $bindable('top-start'),
38
+ portalHost,
39
+ reference,
40
+ class: _class,
41
+ ...rest
42
+ }: Props = $props();
43
+
44
+ let popupRef: HTMLDivElement | undefined = $state(undefined);
45
+ let popupPosition: Partial<ComputePositionReturn> = $state({ x: 0, y: 0 });
46
+ let floatingUIPlacement = $derived(placement as Placement);
47
+ let bodyHeight = $state(0);
48
+ let resizeObserver: ResizeObserver | undefined = undefined;
49
+
50
+ const portalContext = getContext<PortalContext>(STERLING_PORTAL_CONTEXT_ID) || {
14
51
  portalHost: undefined
15
- };
16
- // ----- Portal Host ----- //
17
- const ensurePortalHost = async () => {
52
+ };
53
+
54
+ // ----- Portal Host ----- //
55
+
56
+ const ensurePortalHost = async () => {
18
57
  await tick();
58
+
19
59
  // use the host set from context, usually set from a Dialog
20
- let host = $contextPortalHost;
60
+ let host = portalContext.portalHost;
61
+
21
62
  // use or create the sterling portal host
22
63
  if (!host && globalThis?.document) {
23
- host = globalThis.document.querySelector(`#${STERLING_PORTAL_HOST_ID}`);
24
- // fallback to creating the sterling portal host
25
- if (!host) {
26
- host = globalThis.document.createElement('div');
27
- host.id = STERLING_PORTAL_HOST_ID;
28
- host.style.overflow = 'visible';
29
- globalThis.document.body.append(host);
30
- }
64
+ host = globalThis.document.querySelector(`#${STERLING_PORTAL_HOST_ID}`) as HTMLElement;
65
+
66
+ // fallback to creating the sterling portal host
67
+ if (!host) {
68
+ host = globalThis.document.createElement('div');
69
+ host.id = STERLING_PORTAL_HOST_ID;
70
+ host.style.overflow = 'visible';
71
+ globalThis.document.body.append(host);
72
+ }
31
73
  }
74
+
32
75
  portalHost = host;
33
- };
34
- // ----- Position ----- //
35
- let middleware = $derived([
76
+ };
77
+
78
+ // ----- Position ----- //
79
+
80
+ let middleware = $derived([
36
81
  offset({ mainAxis: mainAxisOffset, crossAxis: crossAxisOffset }),
37
82
  flip()
38
- ]);
39
- const computePopoverPosition = async () => {
83
+ ]);
84
+
85
+ const computePopoverPosition = async () => {
40
86
  if (reference && popupRef) {
41
- popupPosition = await computePosition(reference, popupRef, {
42
- placement: floatingUIPlacement,
43
- middleware
44
- });
45
- }
46
- else {
47
- popupPosition = { x: 0, y: 0 };
87
+ popupPosition = await computePosition(reference, popupRef, {
88
+ placement: floatingUIPlacement,
89
+ middleware
90
+ });
91
+ } else {
92
+ popupPosition = { x: 0, y: 0 };
48
93
  }
49
- };
50
- // whenever a positioned element is portaled it needs resubscription to auto-update
51
- let cleanupAutoUpdate = () => { };
52
- const autoUpdatePopoverPosition = () => {
94
+ };
95
+
96
+ // whenever a positioned element is portaled it needs resubscription to auto-update
97
+ let cleanupAutoUpdate = () => {};
98
+ const autoUpdatePopoverPosition = () => {
53
99
  cleanupAutoUpdate();
54
100
  if (reference && popupRef) {
55
- cleanupAutoUpdate = autoUpdate(reference, popupRef, computePopoverPosition);
101
+ cleanupAutoUpdate = autoUpdate(reference, popupRef, computePopoverPosition);
56
102
  }
57
- };
58
- $effect(() => {
103
+ };
104
+
105
+ $effect(() => {
59
106
  autoUpdatePopoverPosition();
60
107
  return () => {
61
- cleanupAutoUpdate();
62
- cleanupAutoUpdate = () => { };
108
+ cleanupAutoUpdate();
109
+ cleanupAutoUpdate = () => {};
63
110
  };
64
- });
65
- $effect(() => {
111
+ });
112
+
113
+ $effect(() => {
66
114
  bodyHeight;
67
115
  computePopoverPosition();
68
- });
69
- // ----- EventHandlers ----- //
70
- $effect(() => {
116
+ });
117
+
118
+ // ----- EventHandlers ----- //
119
+ $effect(() => {
71
120
  ensurePortalHost();
121
+
72
122
  resizeObserver = new ResizeObserver((entries) => {
73
- bodyHeight = entries[0].target.clientHeight;
123
+ bodyHeight = entries[0].target.clientHeight;
74
124
  });
125
+
75
126
  // start observing a DOM node
76
127
  resizeObserver.observe(document.body);
128
+
77
129
  return () => {
78
- resizeObserver?.unobserve(document.body);
79
- resizeObserver?.disconnect();
80
- resizeObserver = undefined;
130
+ resizeObserver?.unobserve(document.body);
131
+ resizeObserver?.disconnect();
132
+ resizeObserver = undefined;
81
133
  };
82
- });
83
- const onKeydown = (event) => {
134
+ });
135
+
136
+ const onKeydown: KeyboardEventHandler<HTMLDivElement> = (event) => {
84
137
  if (event.key === 'Escape') {
85
- open = false;
138
+ open = false;
86
139
  }
87
140
  rest.onkeydown?.(event);
88
- };
89
- //TODO: Is this necessary?
90
- ensurePortalHost();
141
+ };
142
+
143
+ //TODO: Is this necessary?
144
+ ensurePortalHost();
91
145
  </script>
92
146
 
93
147
  {#if open || !conditionalRender}
@@ -95,7 +149,7 @@ ensurePortalHost();
95
149
  <!-- svelte-ignore a11y_no_static_element_interactions -->
96
150
  <div
97
151
  bind:this={popupRef}
98
- class={['sterling-popover', _class].filter(Boolean).join(' ')}
152
+ class={['sterling-popover', _class]}
99
153
  class:open
100
154
  class:top={popupPosition.placement === 'top'}
101
155
  class:top-start={popupPosition.placement === 'top-start'}
@@ -1,6 +1,5 @@
1
- /// <reference types="svelte" />
2
- import type { PopoverPlacement } from './Popover.types';
3
1
  import type { HTMLAttributes } from 'svelte/elements';
2
+ import type { PopoverPlacement } from './Popover.types';
4
3
  type Props = HTMLAttributes<HTMLDivElement> & {
5
4
  conditionalRender?: boolean;
6
5
  crossAxisOffset?: number;
@@ -1,6 +1,3 @@
1
- /// <reference types="svelte" />
2
- import type { Readable } from 'svelte/store';
3
1
  export type PortalContext = {
4
- /** The portal host for usePortal */
5
- portalHost: Readable<HTMLElement | undefined>;
2
+ portalHost: HTMLElement | undefined;
6
3
  };
@@ -1,26 +1,51 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">let { class: _class, disabled = false, max = 100, percent = $bindable(0), //readonly
4
- value = $bindable(0), vertical, ...rest } = $props();
5
- //----- State ----- //
6
- let clientHeight = $state(0);
7
- let clientWidth = $state(0);
8
- let clampMax = $derived(Math.max(1, max));
9
- let clampValue = $derived(Math.max(0, Math.min(value, clampMax)));
10
- let ratio = $derived(clampValue / clampMax);
11
- $effect(() => {
3
+ <script lang="ts">
4
+ import type { HTMLAttributes } from 'svelte/elements';
5
+
6
+ type Props = HTMLAttributes<HTMLDivElement> & {
7
+ disabled?: boolean | null;
8
+ max?: number;
9
+ percent?: number;
10
+ value?: number;
11
+ vertical?: boolean | null;
12
+ };
13
+
14
+ let {
15
+ class: _class,
16
+ disabled = false,
17
+ max = 100,
18
+ percent = $bindable(0), //readonly
19
+ value = $bindable(0),
20
+ vertical,
21
+ ...rest
22
+ }: Props = $props();
23
+
24
+ //----- State ----- //
25
+
26
+ let clientHeight: number = $state(0);
27
+ let clientWidth: number = $state(0);
28
+
29
+ let clampMax = $derived(Math.max(1, max));
30
+ let clampValue = $derived(Math.max(0, Math.min(value, clampMax)));
31
+ let ratio = $derived(clampValue / clampMax);
32
+
33
+ $effect(() => {
12
34
  percent = Math.round(ratio * 100);
13
- });
14
- let percentHeight = $derived(clientHeight * ratio);
15
- let percentWidth = $derived(clientWidth * ratio);
16
- let indicatorStyle = $derived(vertical ? `height: ${percentHeight}px` : `width: ${percentWidth}px`);
17
- export {};
35
+ });
36
+
37
+ let percentHeight = $derived(clientHeight * ratio);
38
+ let percentWidth = $derived(clientWidth * ratio);
39
+
40
+ let indicatorStyle = $derived(
41
+ vertical ? `height: ${percentHeight}px` : `width: ${percentWidth}px`
42
+ );
18
43
  </script>
19
44
 
20
45
  <!-- svelte-ignore a11y_role_supports_aria_props -->
21
46
  <div
22
47
  aria-orientation={vertical ? 'vertical' : 'horizontal'}
23
- class={['sterling-progress', _class].filter(Boolean).join(' ')}
48
+ class={['sterling-progress', _class]}
24
49
  class:disabled
25
50
  class:horizontal={!vertical}
26
51
  class:vertical
@@ -1,4 +1,3 @@
1
- /// <reference types="svelte" />
2
1
  import type { HTMLAttributes } from 'svelte/elements';
3
2
  type Props = HTMLAttributes<HTMLDivElement> & {
4
3
  disabled?: boolean | null;
package/dist/Radio.svelte CHANGED
@@ -1,34 +1,46 @@
1
1
  <svelte:options runes={true} />
2
2
 
3
- <script lang="ts">import { idGenerator } from './idGenerator';
4
- import { usingKeyboard } from './mediaQueries/usingKeyboard';
5
- let { id, children, checked = $bindable(false), class: _class, disabled = false, group = $bindable(), ...rest } = $props();
6
- let inputRef;
7
- $effect(() => {
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(() => {
8
26
  if (children && id === undefined) {
9
- id = idGenerator.nextId('Radio');
27
+ id = `Radio-${uuid}`;
10
28
  }
11
- });
12
- // ----- Methods ----- //
13
- export const blur = () => {
29
+ });
30
+
31
+ // ----- Methods ----- //
32
+
33
+ export const blur = () => {
14
34
  inputRef?.blur();
15
- };
16
- export const click = () => {
35
+ };
36
+
37
+ export const click = () => {
17
38
  inputRef?.click();
18
- };
19
- export const focus = (options) => {
39
+ };
40
+
41
+ export const focus = (options?: FocusOptions) => {
20
42
  inputRef?.focus(options);
21
- };
22
- // ----- Event Handlers ----- //
23
- const onChange = (e) => {
24
- console.log('onChange', e);
25
- // if ((e.currentTarget && e.currentTarget.checked) || (e.target && e.target.checked)) {
26
- // group = value;
27
- // }
28
- };
29
- $effect(() => {
30
- console.log(id, '-checked', checked);
31
- });
43
+ };
32
44
  </script>
33
45
 
34
46
  <!--
@@ -36,7 +48,7 @@ $effect(() => {
36
48
  A styled HTML input type=radio element with optional label.
37
49
  -->
38
50
  <div
39
- class={`sterling-radio ${_class}`}
51
+ class={['sterling-radio', _class]}
40
52
  class:checked
41
53
  class:disabled
42
54
  class:using-keyboard={$usingKeyboard}