@geoffcox/sterling-svelte 0.0.2 → 0.0.4
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.
- package/clickOutside.d.ts +3 -0
- package/clickOutside.js +14 -0
- package/index.d.ts +3 -0
- package/index.js +3 -0
- package/inputs/Checkbox.svelte +140 -0
- package/inputs/Checkbox.svelte.d.ts +40 -0
- package/inputs/Input.svelte +148 -0
- package/inputs/Input.svelte.d.ts +46 -0
- package/inputs/Radio.svelte +154 -0
- package/inputs/Radio.svelte.d.ts +41 -0
- package/inputs/Select.svelte +360 -0
- package/inputs/Select.svelte.d.ts +59 -0
- package/inputs/Slider.svelte +280 -0
- package/inputs/Slider.svelte.d.ts +26 -0
- package/lists/List.svelte +313 -0
- package/lists/List.svelte.d.ts +59 -0
- package/package.json +53 -42
- package/theme/darkTheme.js +8 -10
- package/theme/lightTheme.js +7 -9
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
<script>import { createEventDispatcher, onMount, tick } from "svelte";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import { computePosition, flip, offset, shift, autoUpdate } from "@floating-ui/dom";
|
|
4
|
+
import { clickOutside } from "../clickOutside";
|
|
5
|
+
import List from "../lists/List.svelte";
|
|
6
|
+
export let disabled = false;
|
|
7
|
+
export let items = [];
|
|
8
|
+
export let open = false;
|
|
9
|
+
export let selectedIndex = 0;
|
|
10
|
+
export let selectedItem = void 0;
|
|
11
|
+
$: {
|
|
12
|
+
selectedItem = items[selectedIndex];
|
|
13
|
+
}
|
|
14
|
+
let prevOpen = false;
|
|
15
|
+
let pendingSelectedIndex = selectedIndex;
|
|
16
|
+
let selectRef;
|
|
17
|
+
let popupRef;
|
|
18
|
+
let listRef;
|
|
19
|
+
const popupId = uuidv4();
|
|
20
|
+
let popupPosition = {
|
|
21
|
+
x: void 0,
|
|
22
|
+
y: void 0
|
|
23
|
+
};
|
|
24
|
+
const dispatch = createEventDispatcher();
|
|
25
|
+
const raiseItemSelected = (index) => {
|
|
26
|
+
dispatch("itemSelected", { index, item: items[index] });
|
|
27
|
+
};
|
|
28
|
+
const raiseItemSelectPending = (index) => {
|
|
29
|
+
dispatch("itemSelectPending", { index, item: items[index] });
|
|
30
|
+
};
|
|
31
|
+
$:
|
|
32
|
+
selectedIndex, () => {
|
|
33
|
+
pendingSelectedIndex = selectedIndex;
|
|
34
|
+
};
|
|
35
|
+
$: {
|
|
36
|
+
raiseItemSelected(selectedIndex);
|
|
37
|
+
}
|
|
38
|
+
$: {
|
|
39
|
+
console.log("raise pendingSelectedIndex changed");
|
|
40
|
+
raiseItemSelectPending(pendingSelectedIndex);
|
|
41
|
+
}
|
|
42
|
+
$: {
|
|
43
|
+
if (open && !prevOpen) {
|
|
44
|
+
prevOpen = true;
|
|
45
|
+
tick().then(() => listRef?.focusSelectedItem());
|
|
46
|
+
} else if (prevOpen) {
|
|
47
|
+
prevOpen = false;
|
|
48
|
+
tick().then(() => selectRef?.focus());
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
let mounted = false;
|
|
52
|
+
onMount(() => {
|
|
53
|
+
mounted = true;
|
|
54
|
+
const cleanup = autoUpdate(selectRef, popupRef, async () => {
|
|
55
|
+
const { x, y } = await computePosition(selectRef, popupRef, {
|
|
56
|
+
placement: "bottom-end",
|
|
57
|
+
middleware: [offset({ mainAxis: 2 }), flip(), shift({ padding: 0 })]
|
|
58
|
+
});
|
|
59
|
+
if (open) {
|
|
60
|
+
popupPosition = { x, y };
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return cleanup;
|
|
64
|
+
});
|
|
65
|
+
const onSelectClick = (event) => {
|
|
66
|
+
if (!disabled) {
|
|
67
|
+
open = !open;
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
event.stopPropagation();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const onSelectKeydown = (event) => {
|
|
73
|
+
if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
|
|
74
|
+
switch (event.key) {
|
|
75
|
+
case " ":
|
|
76
|
+
{
|
|
77
|
+
open = !open;
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
event.stopPropagation();
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case "ArrowUp":
|
|
83
|
+
{
|
|
84
|
+
listRef.selectPreviousItem();
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
event.stopPropagation();
|
|
87
|
+
}
|
|
88
|
+
break;
|
|
89
|
+
case "ArrowDown":
|
|
90
|
+
{
|
|
91
|
+
listRef.selectNextItem();
|
|
92
|
+
event.preventDefault();
|
|
93
|
+
event.stopPropagation();
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
const onListKeydown = (event) => {
|
|
100
|
+
if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
|
|
101
|
+
switch (event.key) {
|
|
102
|
+
case "Enter":
|
|
103
|
+
{
|
|
104
|
+
selectedIndex = pendingSelectedIndex;
|
|
105
|
+
open = !open;
|
|
106
|
+
event.preventDefault();
|
|
107
|
+
event.stopPropagation();
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
case "Escape":
|
|
111
|
+
{
|
|
112
|
+
pendingSelectedIndex = selectedIndex;
|
|
113
|
+
open = !open;
|
|
114
|
+
event.preventDefault();
|
|
115
|
+
event.stopPropagation();
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
const onListClick = (event) => {
|
|
122
|
+
if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
|
|
123
|
+
selectedIndex = pendingSelectedIndex;
|
|
124
|
+
open = false;
|
|
125
|
+
event.preventDefault();
|
|
126
|
+
event.stopPropagation();
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
const onPendingItemSelected = (event) => {
|
|
130
|
+
pendingSelectedIndex = event.detail.index;
|
|
131
|
+
console.log("pendingSelectedIndex changed");
|
|
132
|
+
if (!open) {
|
|
133
|
+
selectedIndex = pendingSelectedIndex;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<!--
|
|
139
|
+
@component
|
|
140
|
+
Pops up a list of items when clicked.
|
|
141
|
+
A single item can be selected and is displayed as the value.
|
|
142
|
+
-->
|
|
143
|
+
<div
|
|
144
|
+
bind:this={selectRef}
|
|
145
|
+
use:clickOutside
|
|
146
|
+
aria-controls={popupId}
|
|
147
|
+
aria-haspopup="listbox"
|
|
148
|
+
aria-expanded={open}
|
|
149
|
+
class="sterling-select"
|
|
150
|
+
class:disabled
|
|
151
|
+
role="combobox"
|
|
152
|
+
tabindex="0"
|
|
153
|
+
on:click_outside={() => (open = false)}
|
|
154
|
+
on:click={onSelectClick}
|
|
155
|
+
on:blur
|
|
156
|
+
on:click
|
|
157
|
+
on:copy
|
|
158
|
+
on:cut
|
|
159
|
+
on:dblclick
|
|
160
|
+
on:focus
|
|
161
|
+
on:focusin
|
|
162
|
+
on:focusout
|
|
163
|
+
on:keydown={onSelectKeydown}
|
|
164
|
+
on:keydown
|
|
165
|
+
on:keypress
|
|
166
|
+
on:keyup
|
|
167
|
+
on:mousedown
|
|
168
|
+
on:mouseenter
|
|
169
|
+
on:mouseleave
|
|
170
|
+
on:mousemove
|
|
171
|
+
on:mouseover
|
|
172
|
+
on:mouseout
|
|
173
|
+
on:mouseup
|
|
174
|
+
on:wheel
|
|
175
|
+
on:paste
|
|
176
|
+
{...$$restProps}
|
|
177
|
+
>
|
|
178
|
+
<!-- svelte-ignore a11y-label-has-associated-control -->
|
|
179
|
+
<label class="sterling-select-label">
|
|
180
|
+
{#if $$slots.label}
|
|
181
|
+
<div class="label-content">
|
|
182
|
+
<slot name="label" />
|
|
183
|
+
</div>
|
|
184
|
+
{/if}
|
|
185
|
+
<div class="input">
|
|
186
|
+
<div class="value">
|
|
187
|
+
<slot name="value">
|
|
188
|
+
{items[selectedIndex]}
|
|
189
|
+
</slot>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="button">
|
|
192
|
+
<slot name="button">
|
|
193
|
+
<div class="chevron" />
|
|
194
|
+
</slot>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</label>
|
|
198
|
+
<div
|
|
199
|
+
bind:this={popupRef}
|
|
200
|
+
class="popup"
|
|
201
|
+
class:open
|
|
202
|
+
id={popupId}
|
|
203
|
+
style="left:{popupPosition.x}px; top:{popupPosition.y}px"
|
|
204
|
+
>
|
|
205
|
+
<div class="popup-content">
|
|
206
|
+
<slot name="list">
|
|
207
|
+
{#if $$slots.default === true}
|
|
208
|
+
<List
|
|
209
|
+
bind:this={listRef}
|
|
210
|
+
selectedIndex={pendingSelectedIndex}
|
|
211
|
+
{items}
|
|
212
|
+
{disabled}
|
|
213
|
+
let:disabled
|
|
214
|
+
let:index
|
|
215
|
+
let:item
|
|
216
|
+
let:selected
|
|
217
|
+
on:click={onListClick}
|
|
218
|
+
on:keydown={onListKeydown}
|
|
219
|
+
on:itemSelected={onPendingItemSelected}
|
|
220
|
+
>
|
|
221
|
+
<slot {disabled} {index} {item} {selected} />
|
|
222
|
+
</List>
|
|
223
|
+
{:else}
|
|
224
|
+
<List
|
|
225
|
+
bind:this={listRef}
|
|
226
|
+
selectedIndex={pendingSelectedIndex}
|
|
227
|
+
{items}
|
|
228
|
+
{disabled}
|
|
229
|
+
let:disabled
|
|
230
|
+
let:index
|
|
231
|
+
let:item
|
|
232
|
+
let:selected
|
|
233
|
+
on:click={onListClick}
|
|
234
|
+
on:keydown={onListKeydown}
|
|
235
|
+
on:itemSelected={onPendingItemSelected}
|
|
236
|
+
/>
|
|
237
|
+
{/if}
|
|
238
|
+
</slot>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<style>
|
|
244
|
+
.sterling-select {
|
|
245
|
+
background-color: var(--Input__background-color);
|
|
246
|
+
border-color: var(--Input__border-color);
|
|
247
|
+
border-radius: var(--Input__border-radius);
|
|
248
|
+
border-style: var(--Input__border-style);
|
|
249
|
+
border-width: var(--Input__border-width);
|
|
250
|
+
color: var(--Input__color);
|
|
251
|
+
display: flex;
|
|
252
|
+
flex-direction: row;
|
|
253
|
+
padding: 0;
|
|
254
|
+
position: relative;
|
|
255
|
+
transition: background-color 250ms, color 250ms, border-color 250ms;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.sterling-select:hover {
|
|
259
|
+
background-color: var(--Input__background-color--hover);
|
|
260
|
+
border-color: var(--Input__border-color--hover);
|
|
261
|
+
color: var(--Input__color--hover);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.sterling-select:focus-within {
|
|
265
|
+
background-color: var(--Input__background-color--focus);
|
|
266
|
+
border-color: var(--Input__border-color--focus);
|
|
267
|
+
color: var(--Input__color--focus);
|
|
268
|
+
outline-color: var(--Common__outline-color);
|
|
269
|
+
outline-offset: var(--Common__outline-offset);
|
|
270
|
+
outline-style: var(--Common__outline-style);
|
|
271
|
+
outline-width: var(--Common__outline-width);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.sterling-select.disabled {
|
|
275
|
+
background-color: var(--Input__background-color--disabled);
|
|
276
|
+
border-color: var(---Input__border-color--disabled);
|
|
277
|
+
color: var(--Input__color--disabled);
|
|
278
|
+
cursor: not-allowed;
|
|
279
|
+
outline: none;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.label {
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.label-content {
|
|
288
|
+
font-size: 0.7em;
|
|
289
|
+
margin: 0.5em 0.7em 0 0.7em;
|
|
290
|
+
color: var(--Display__color--subtle);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.input {
|
|
294
|
+
display: flex;
|
|
295
|
+
flex-direction: row;
|
|
296
|
+
align-items: stretch;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.value {
|
|
300
|
+
padding: 0.5em;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.chevron {
|
|
304
|
+
display: block;
|
|
305
|
+
position: relative;
|
|
306
|
+
border: none;
|
|
307
|
+
background: none;
|
|
308
|
+
margin: 0;
|
|
309
|
+
height: 100%;
|
|
310
|
+
width: 32px;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.chevron::after {
|
|
314
|
+
position: absolute;
|
|
315
|
+
content: '';
|
|
316
|
+
top: 50%;
|
|
317
|
+
left: 50%;
|
|
318
|
+
width: 7px;
|
|
319
|
+
height: 7px;
|
|
320
|
+
border-right: 3px solid currentColor;
|
|
321
|
+
border-top: 3px solid currentColor;
|
|
322
|
+
/*
|
|
323
|
+
The chevron is a right triangle, rotated to face down.
|
|
324
|
+
It should be moved up so it is centered vertically after rotation.
|
|
325
|
+
The amount to move is the hypotenuse of the right triangle of the chevron.
|
|
326
|
+
For a right triangle with equal a and b where c=1
|
|
327
|
+
a^2 + b^2 = c^2
|
|
328
|
+
a^2 + a^2 = c^2
|
|
329
|
+
2a^2 = c^2
|
|
330
|
+
2a^2 = 1
|
|
331
|
+
a^2 = 0.5
|
|
332
|
+
a = sqrt(0.5)
|
|
333
|
+
a = 0.707
|
|
334
|
+
*/
|
|
335
|
+
transform: translate(-50%, calc(-50% / 0.707)) rotate(135deg);
|
|
336
|
+
transform-origin: 50% 50%;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.popup {
|
|
340
|
+
box-sizing: border-box;
|
|
341
|
+
display: none;
|
|
342
|
+
overflow: hidden;
|
|
343
|
+
position: absolute;
|
|
344
|
+
box-shadow: rgba(0, 0, 0, 0.2) 0px 2px 1px -1px, rgba(0, 0, 0, 0.14) 0px 1px 1px 0px,
|
|
345
|
+
rgba(0, 0, 0, 0.12) 0px 1px 3px 0px;
|
|
346
|
+
width: fit-content;
|
|
347
|
+
height: fit-content;
|
|
348
|
+
z-index: 1;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.popup.open {
|
|
352
|
+
display: grid;
|
|
353
|
+
grid-template-columns: 1fr;
|
|
354
|
+
grid-template-rows: 1fr;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.popup-content {
|
|
358
|
+
max-height: 15em;
|
|
359
|
+
}
|
|
360
|
+
</style>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
[x: string]: any;
|
|
5
|
+
disabled?: boolean | undefined;
|
|
6
|
+
items?: any[] | undefined;
|
|
7
|
+
open?: boolean | undefined;
|
|
8
|
+
selectedIndex?: number | undefined;
|
|
9
|
+
selectedItem?: any;
|
|
10
|
+
};
|
|
11
|
+
events: {
|
|
12
|
+
blur: FocusEvent;
|
|
13
|
+
click: MouseEvent;
|
|
14
|
+
copy: ClipboardEvent;
|
|
15
|
+
cut: ClipboardEvent;
|
|
16
|
+
dblclick: MouseEvent;
|
|
17
|
+
focus: FocusEvent;
|
|
18
|
+
focusin: FocusEvent;
|
|
19
|
+
focusout: FocusEvent;
|
|
20
|
+
keydown: KeyboardEvent;
|
|
21
|
+
keypress: KeyboardEvent;
|
|
22
|
+
keyup: KeyboardEvent;
|
|
23
|
+
mousedown: MouseEvent;
|
|
24
|
+
mouseenter: MouseEvent;
|
|
25
|
+
mouseleave: MouseEvent;
|
|
26
|
+
mousemove: MouseEvent;
|
|
27
|
+
mouseover: MouseEvent;
|
|
28
|
+
mouseout: MouseEvent;
|
|
29
|
+
mouseup: MouseEvent;
|
|
30
|
+
wheel: WheelEvent;
|
|
31
|
+
paste: ClipboardEvent;
|
|
32
|
+
itemSelected: CustomEvent<any>;
|
|
33
|
+
itemSelectPending: CustomEvent<any>;
|
|
34
|
+
} & {
|
|
35
|
+
[evt: string]: CustomEvent<any>;
|
|
36
|
+
};
|
|
37
|
+
slots: {
|
|
38
|
+
label: {};
|
|
39
|
+
value: {};
|
|
40
|
+
button: {};
|
|
41
|
+
list: {};
|
|
42
|
+
default: {
|
|
43
|
+
disabled: boolean;
|
|
44
|
+
index: any;
|
|
45
|
+
item: any;
|
|
46
|
+
selected: any;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
export type SelectProps = typeof __propDef.props;
|
|
51
|
+
export type SelectEvents = typeof __propDef.events;
|
|
52
|
+
export type SelectSlots = typeof __propDef.slots;
|
|
53
|
+
/**
|
|
54
|
+
* Pops up a list of items when clicked.
|
|
55
|
+
* A single item can be selected and is displayed as the value.
|
|
56
|
+
*/
|
|
57
|
+
export default class Select extends SvelteComponentTyped<SelectProps, SelectEvents, SelectSlots> {
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
<script>import { round } from "lodash-es";
|
|
2
|
+
import { createEventDispatcher } from "svelte";
|
|
3
|
+
export let value = 0;
|
|
4
|
+
export let min = 0;
|
|
5
|
+
export let max = 100;
|
|
6
|
+
export let step = void 0;
|
|
7
|
+
export let precision = 0;
|
|
8
|
+
export let vertical = false;
|
|
9
|
+
export let disabled = false;
|
|
10
|
+
let sliderRef;
|
|
11
|
+
const dispatch = createEventDispatcher();
|
|
12
|
+
const raiseChange = (newValue) => {
|
|
13
|
+
dispatch("change", { value: newValue });
|
|
14
|
+
};
|
|
15
|
+
const getPrecision = (value2) => {
|
|
16
|
+
if (value2 !== void 0 && Number !== null && !Number.isNaN(value2)) {
|
|
17
|
+
const text = value2.toString();
|
|
18
|
+
const position = text.indexOf(".");
|
|
19
|
+
if (position !== -1) {
|
|
20
|
+
const fraction = text.substring(position + 1);
|
|
21
|
+
if (fraction) {
|
|
22
|
+
return fraction.length;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return 0;
|
|
27
|
+
};
|
|
28
|
+
$:
|
|
29
|
+
highestPrecision = Math.max(
|
|
30
|
+
precision,
|
|
31
|
+
getPrecision(min),
|
|
32
|
+
getPrecision(max),
|
|
33
|
+
getPrecision(step)
|
|
34
|
+
);
|
|
35
|
+
const setValue = (newValue) => {
|
|
36
|
+
value = round(Math.max(min, Math.min(max, newValue)), highestPrecision);
|
|
37
|
+
};
|
|
38
|
+
$: {
|
|
39
|
+
if (min > max) {
|
|
40
|
+
min = max;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
$: {
|
|
44
|
+
if (value < min || value > max || value !== round(value, highestPrecision)) {
|
|
45
|
+
setValue(value);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
$: {
|
|
49
|
+
if (step) {
|
|
50
|
+
let stepValue = Math.max(min, Math.min(value, max));
|
|
51
|
+
stepValue = Math.round(stepValue / step) * step + min;
|
|
52
|
+
if (stepValue !== value) {
|
|
53
|
+
setValue(stepValue);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
$:
|
|
58
|
+
ratio = (value - min) / (max - min);
|
|
59
|
+
$:
|
|
60
|
+
changeBy = step ? step : 1;
|
|
61
|
+
const setValueByOffset = (offset) => {
|
|
62
|
+
if (sliderSize > 0) {
|
|
63
|
+
const positionRatio = Math.max(0, Math.min(1, offset / sliderSize));
|
|
64
|
+
const newValue = min + positionRatio * (max - min);
|
|
65
|
+
setValue(newValue);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
$: {
|
|
69
|
+
raiseChange(value);
|
|
70
|
+
}
|
|
71
|
+
let sliderWidth;
|
|
72
|
+
let sliderHeight;
|
|
73
|
+
$:
|
|
74
|
+
sliderSize = vertical ? sliderHeight : sliderWidth;
|
|
75
|
+
$:
|
|
76
|
+
valueOffset = sliderSize * ratio;
|
|
77
|
+
const onPointerDown = (event) => {
|
|
78
|
+
if (!disabled) {
|
|
79
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
80
|
+
if (vertical) {
|
|
81
|
+
setValueByOffset(sliderRef.getBoundingClientRect().bottom - event.y);
|
|
82
|
+
} else {
|
|
83
|
+
setValueByOffset(event.x - sliderRef.getBoundingClientRect().left);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const onPointerMove = (event) => {
|
|
88
|
+
if (!disabled && event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
89
|
+
if (vertical) {
|
|
90
|
+
setValueByOffset(sliderRef.getBoundingClientRect().bottom - event.y);
|
|
91
|
+
} else {
|
|
92
|
+
setValueByOffset(event.x - sliderRef.getBoundingClientRect().left);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const onPointerUp = (event) => {
|
|
97
|
+
if (!disabled) {
|
|
98
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
const onKeyDown = (event) => {
|
|
102
|
+
if (!disabled && !event.ctrlKey && !event.shiftKey && !event.altKey) {
|
|
103
|
+
switch (event.code) {
|
|
104
|
+
case "ArrowDown":
|
|
105
|
+
case "ArrowLeft":
|
|
106
|
+
setValue(value - changeBy);
|
|
107
|
+
event.preventDefault();
|
|
108
|
+
event.stopPropagation();
|
|
109
|
+
return;
|
|
110
|
+
case "ArrowRight":
|
|
111
|
+
case "ArrowUp":
|
|
112
|
+
setValue(value + changeBy);
|
|
113
|
+
event.preventDefault();
|
|
114
|
+
event.stopPropagation();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
</script>
|
|
120
|
+
|
|
121
|
+
<!-- @component
|
|
122
|
+
Slider lets the user chose a value within a min/max range by dragging a thumb button.
|
|
123
|
+
-->
|
|
124
|
+
<div
|
|
125
|
+
class="sterling-slider"
|
|
126
|
+
class:disabled
|
|
127
|
+
class:horizontal={!vertical}
|
|
128
|
+
class:vertical
|
|
129
|
+
tabindex={!disabled ? 0 : undefined}
|
|
130
|
+
{...$$restProps}
|
|
131
|
+
on:keydown={onKeyDown}
|
|
132
|
+
on:pointerdown={onPointerDown}
|
|
133
|
+
on:pointermove={onPointerMove}
|
|
134
|
+
on:pointerup={onPointerUp}
|
|
135
|
+
>
|
|
136
|
+
<div
|
|
137
|
+
class="container"
|
|
138
|
+
bind:this={sliderRef}
|
|
139
|
+
bind:clientWidth={sliderWidth}
|
|
140
|
+
bind:clientHeight={sliderHeight}
|
|
141
|
+
>
|
|
142
|
+
<div class="track" />
|
|
143
|
+
<div class="fill" style={vertical ? `height: ${valueOffset}px` : `width: ${valueOffset}px`} />
|
|
144
|
+
<div class="thumb" style={vertical ? `bottom: ${valueOffset}px` : `left: ${valueOffset}px`} />
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<style>
|
|
149
|
+
.sterling-slider {
|
|
150
|
+
box-sizing: border-box;
|
|
151
|
+
outline: none;
|
|
152
|
+
padding: 0;
|
|
153
|
+
overflow: visible;
|
|
154
|
+
display: grid;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.sterling-slider.horizontal {
|
|
158
|
+
height: 2em;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.sterling-slider.vertical {
|
|
162
|
+
width: 2em;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
.sterling-slider:focus-visible {
|
|
166
|
+
outline-color: var(--Common__outline-color);
|
|
167
|
+
outline-offset: var(--Common__outline-offset);
|
|
168
|
+
outline-style: var(--Common__outline-style);
|
|
169
|
+
outline-width: var(--Common__outline-width);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.container {
|
|
173
|
+
position: relative;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.sterling-slider.horizontal .container {
|
|
177
|
+
margin: 0 0.75em;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.sterling-slider.vertical .container {
|
|
181
|
+
margin: 0.75em 0;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.track {
|
|
185
|
+
position: absolute;
|
|
186
|
+
background: var(--Display__background-color);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.sterling-slider.horizontal .track {
|
|
190
|
+
left: 0;
|
|
191
|
+
right: 0;
|
|
192
|
+
top: 50%;
|
|
193
|
+
height: 3px;
|
|
194
|
+
transform: translate(0, -50%);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.sterling-slider.vertical .track {
|
|
198
|
+
bottom: 0;
|
|
199
|
+
left: 50%;
|
|
200
|
+
top: 0;
|
|
201
|
+
transform: translate(-50%, 0);
|
|
202
|
+
width: 3px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.sterling-slider.disabled .track {
|
|
206
|
+
background: var(--Common__background-color--disabled);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.fill {
|
|
210
|
+
background: var(--Display__color);
|
|
211
|
+
position: absolute;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.sterling-slider.horizontal .fill {
|
|
215
|
+
height: 3px;
|
|
216
|
+
top: 50%;
|
|
217
|
+
transform: translate(0, -50%);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.sterling-slider.vertical .fill {
|
|
221
|
+
bottom: 0;
|
|
222
|
+
left: 50%;
|
|
223
|
+
transform: translate(-50%, 0);
|
|
224
|
+
width: 3px;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.sterling-slider.disabled .fill {
|
|
228
|
+
background: var(--Common__color--disabled);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.thumb {
|
|
232
|
+
background-color: var(--Button__background-color);
|
|
233
|
+
border-color: var(--Button__border-color);
|
|
234
|
+
border-radius: 10000px;
|
|
235
|
+
border-style: var(--Button__border-style);
|
|
236
|
+
border-width: var(--Button__border-width);
|
|
237
|
+
box-sizing: border-box;
|
|
238
|
+
color: var(--Button__color);
|
|
239
|
+
cursor: pointer;
|
|
240
|
+
display: block;
|
|
241
|
+
font: inherit;
|
|
242
|
+
height: 1.5em;
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
padding: 0;
|
|
245
|
+
text-decoration: none;
|
|
246
|
+
transition: background-color 250ms, color 250ms, border-color 250ms;
|
|
247
|
+
white-space: nowrap;
|
|
248
|
+
position: absolute;
|
|
249
|
+
width: 1.5em;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.sterling-slider.horizontal .thumb {
|
|
253
|
+
top: 50%;
|
|
254
|
+
transform: translate(-50%, -50%);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.sterling-slider.vertical .thumb {
|
|
258
|
+
left: 50%;
|
|
259
|
+
transform: translate(-50%, 50%);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.thumb:hover {
|
|
263
|
+
background-color: var(--Button__background-color--hover);
|
|
264
|
+
border-color: var(--Button__border-color--hover);
|
|
265
|
+
color: var(--Button__color--hover);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.thumb:active {
|
|
269
|
+
background-color: var(--Button__background-color--active);
|
|
270
|
+
border-color: var(--Button__border-color--active);
|
|
271
|
+
color: var(--Button__color--active);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.sterling-slider.disabled .thumb {
|
|
275
|
+
background-color: var(--Common__background-color--disabled);
|
|
276
|
+
border-color: var(--Common__border-color--disabled);
|
|
277
|
+
color: var(--Common__color--disabled);
|
|
278
|
+
cursor: not-allowed;
|
|
279
|
+
}
|
|
280
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SvelteComponentTyped } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
[x: string]: any;
|
|
5
|
+
value?: number | undefined;
|
|
6
|
+
min?: number | undefined;
|
|
7
|
+
max?: number | undefined;
|
|
8
|
+
step?: number | undefined;
|
|
9
|
+
precision?: number | undefined;
|
|
10
|
+
vertical?: boolean | undefined;
|
|
11
|
+
disabled?: boolean | undefined;
|
|
12
|
+
};
|
|
13
|
+
events: {
|
|
14
|
+
change: CustomEvent<any>;
|
|
15
|
+
} & {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
};
|
|
18
|
+
slots: {};
|
|
19
|
+
};
|
|
20
|
+
export type SliderProps = typeof __propDef.props;
|
|
21
|
+
export type SliderEvents = typeof __propDef.events;
|
|
22
|
+
export type SliderSlots = typeof __propDef.slots;
|
|
23
|
+
/** Slider lets the user chose a value within a min/max range by dragging a thumb button. */
|
|
24
|
+
export default class Slider extends SvelteComponentTyped<SliderProps, SliderEvents, SliderSlots> {
|
|
25
|
+
}
|
|
26
|
+
export {};
|