ember-primitives 0.59.1 → 0.60.1
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/README.md +27 -13
- package/declarations/components/heading.d.ts +1 -1
- package/declarations/components/heading.d.ts.map +1 -1
- package/declarations/components/slider/store.d.ts +64 -0
- package/declarations/components/slider/store.d.ts.map +1 -0
- package/declarations/components/slider.d.ts +150 -0
- package/declarations/components/slider.d.ts.map +1 -0
- package/declarations/index.d.ts +1 -0
- package/declarations/index.d.ts.map +1 -1
- package/dist/components/slider/store.js +239 -0
- package/dist/components/slider/store.js.map +1 -0
- package/dist/components/slider.css +203 -0
- package/dist/components/slider.js +88 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +10 -3
- package/src/components/slider/store.ts +374 -0
- package/src/components/slider.css +203 -0
- package/src/components/slider.gts +281 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import "./slider.css";
|
|
2
|
+
|
|
3
|
+
import Component from "@glimmer/component";
|
|
4
|
+
import { hash } from "@ember/helper";
|
|
5
|
+
import { on } from "@ember/modifier";
|
|
6
|
+
|
|
7
|
+
import { SliderStore, type SliderThumb, type StyleString } from "./slider/store.ts";
|
|
8
|
+
|
|
9
|
+
import type { TOC } from "@ember/component/template-only";
|
|
10
|
+
import type Owner from "@ember/owner";
|
|
11
|
+
import type { WithBoundArgs } from "@glint/template";
|
|
12
|
+
|
|
13
|
+
export type { SliderThumb };
|
|
14
|
+
|
|
15
|
+
export interface Signature {
|
|
16
|
+
Element: HTMLSpanElement;
|
|
17
|
+
Args: {
|
|
18
|
+
/**
|
|
19
|
+
* The current value(s) of the slider.
|
|
20
|
+
* For single value slider, pass a single number.
|
|
21
|
+
* For range slider, pass an array of numbers [min, max].
|
|
22
|
+
*/
|
|
23
|
+
value?: number | number[];
|
|
24
|
+
/**
|
|
25
|
+
* The minimum value of the slider.
|
|
26
|
+
* Defaults to 0.
|
|
27
|
+
*/
|
|
28
|
+
min?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The maximum value of the slider.
|
|
31
|
+
* Defaults to 100.
|
|
32
|
+
*/
|
|
33
|
+
max?: number;
|
|
34
|
+
/**
|
|
35
|
+
* The stepping interval.
|
|
36
|
+
*
|
|
37
|
+
* When passed a number, the slider moves in fixed increments.
|
|
38
|
+
* When passed an array of numbers, the slider snaps to those discrete values.
|
|
39
|
+
* Defaults to 1.
|
|
40
|
+
*/
|
|
41
|
+
step?: number | number[];
|
|
42
|
+
/**
|
|
43
|
+
* The orientation of the slider.
|
|
44
|
+
* Defaults to 'horizontal'.
|
|
45
|
+
*/
|
|
46
|
+
orientation?: "horizontal" | "vertical";
|
|
47
|
+
/**
|
|
48
|
+
* Whether the slider is disabled.
|
|
49
|
+
*/
|
|
50
|
+
disabled?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Callback when the value changes during dragging.
|
|
53
|
+
*/
|
|
54
|
+
onValueChange?: (value: number | number[]) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Callback when the value is committed (after dragging ends).
|
|
57
|
+
*/
|
|
58
|
+
onValueCommit?: (value: number | number[]) => void;
|
|
59
|
+
};
|
|
60
|
+
Blocks: {
|
|
61
|
+
default: [
|
|
62
|
+
{
|
|
63
|
+
/**
|
|
64
|
+
* The track element - the rail along which the thumb moves
|
|
65
|
+
*/
|
|
66
|
+
Track: typeof Track;
|
|
67
|
+
/**
|
|
68
|
+
* The range element - the filled portion of the track
|
|
69
|
+
*/
|
|
70
|
+
Range: WithBoundArgs<typeof Range, "rangeStyle">;
|
|
71
|
+
/**
|
|
72
|
+
* The thumb element - the draggable handle(s)
|
|
73
|
+
*/
|
|
74
|
+
Thumb: WithBoundArgs<typeof ThumbComponent, "store">;
|
|
75
|
+
/**
|
|
76
|
+
* The current value(s)
|
|
77
|
+
*/
|
|
78
|
+
values: number[];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The tick values, if any.
|
|
82
|
+
*/
|
|
83
|
+
tickValues: number[] | null;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A stable list of thumbs to iterate over.
|
|
87
|
+
* Prefer this over iterating `values` directly to avoid DOM churn during dragging.
|
|
88
|
+
*/
|
|
89
|
+
thumbs: SliderThumb[];
|
|
90
|
+
/**
|
|
91
|
+
* The minimum value
|
|
92
|
+
*/
|
|
93
|
+
min: number;
|
|
94
|
+
/**
|
|
95
|
+
* The maximum value
|
|
96
|
+
*/
|
|
97
|
+
max: number;
|
|
98
|
+
/**
|
|
99
|
+
* The step value
|
|
100
|
+
*/
|
|
101
|
+
step: number;
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface TrackSignature {
|
|
108
|
+
Element: HTMLSpanElement;
|
|
109
|
+
Args: Record<string, never>;
|
|
110
|
+
Blocks: {
|
|
111
|
+
default: [];
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const Track: TOC<TrackSignature> = <template>
|
|
116
|
+
<span ...attributes class="ember-primitives__slider__track">
|
|
117
|
+
{{yield}}
|
|
118
|
+
</span>
|
|
119
|
+
</template>;
|
|
120
|
+
|
|
121
|
+
interface RangeSignature {
|
|
122
|
+
Element: HTMLSpanElement;
|
|
123
|
+
Args: {
|
|
124
|
+
rangeStyle: StyleString;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const Range: TOC<RangeSignature> = <template>
|
|
129
|
+
<span ...attributes class="ember-primitives__slider__range" style={{@rangeStyle}} />
|
|
130
|
+
</template>;
|
|
131
|
+
|
|
132
|
+
class ThumbComponent extends Component<{
|
|
133
|
+
/**
|
|
134
|
+
* `...attributes` land on the invisible native input, which is the
|
|
135
|
+
* interactive element (pass `aria-label` / `aria-labelledby` here).
|
|
136
|
+
*/
|
|
137
|
+
Element: HTMLInputElement;
|
|
138
|
+
Args: {
|
|
139
|
+
store: SliderStore;
|
|
140
|
+
/**
|
|
141
|
+
* Optional convenience: pass the full thumb object instead of `@value` + `@index`.
|
|
142
|
+
*/
|
|
143
|
+
thumb?: SliderThumb;
|
|
144
|
+
value?: number;
|
|
145
|
+
index?: number;
|
|
146
|
+
};
|
|
147
|
+
Blocks: {
|
|
148
|
+
/**
|
|
149
|
+
* Rendered inside the visual thumb — useful for tooltips / value labels.
|
|
150
|
+
*/
|
|
151
|
+
default: [];
|
|
152
|
+
};
|
|
153
|
+
}> {
|
|
154
|
+
get index(): number {
|
|
155
|
+
return this.args.thumb?.index ?? this.args.index ?? 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
get value(): number {
|
|
159
|
+
// When using tick values, the `input` needs the internal index.
|
|
160
|
+
return this.args.thumb?.inputValue ?? this.args.value ?? this.args.store.internalMin;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
get isActive(): boolean {
|
|
164
|
+
return this.args.store.activeThumbIndex === this.index;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
get positionStyle() {
|
|
168
|
+
const percent = this.args.thumb?.percent ?? this.args.store.thumbPercents[this.index] ?? 0;
|
|
169
|
+
|
|
170
|
+
return this.args.store.thumbPositionStyle(percent);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private readValue(event: Event): number {
|
|
174
|
+
// In docs live previews the component may run in an iframe/shadow realm,
|
|
175
|
+
// where `instanceof HTMLInputElement` is not reliable. `currentTarget` is
|
|
176
|
+
// the element the handler is attached to.
|
|
177
|
+
const el = event.currentTarget as { value?: string } | null;
|
|
178
|
+
const raw = el?.value;
|
|
179
|
+
const parsed = raw === undefined ? NaN : Number.parseFloat(raw);
|
|
180
|
+
|
|
181
|
+
return Number.isFinite(parsed) ? parsed : this.value;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private onInput = (event: Event) => {
|
|
185
|
+
this.args.store.handleThumbActivate(this.index);
|
|
186
|
+
this.args.store.handleThumbInput(this.index, this.readValue(event));
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
private onChange = (event: Event) => {
|
|
190
|
+
this.args.store.handleThumbActivate(this.index);
|
|
191
|
+
this.args.store.handleThumbChange(this.index, this.readValue(event));
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
private onPointerUp = () => {
|
|
195
|
+
this.args.store.handleThumbActivate(this.index);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
private onGotPointerCapture = () => {
|
|
199
|
+
this.args.store.handleThumbActivate(this.index);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
private onFocus = () => {
|
|
203
|
+
this.args.store.handleThumbActivate(this.index);
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
<template>
|
|
207
|
+
<input
|
|
208
|
+
...attributes
|
|
209
|
+
class="ember-primitives__slider__thumb-input"
|
|
210
|
+
type="range"
|
|
211
|
+
min={{@store.internalMin}}
|
|
212
|
+
max={{@store.internalMax}}
|
|
213
|
+
step={{@store.internalStep}}
|
|
214
|
+
value={{this.value}}
|
|
215
|
+
disabled={{@store.disabled}}
|
|
216
|
+
data-active={{if this.isActive ""}}
|
|
217
|
+
{{on "gotpointercapture" this.onGotPointerCapture}}
|
|
218
|
+
{{on "pointerup" this.onPointerUp}}
|
|
219
|
+
{{on "focus" this.onFocus}}
|
|
220
|
+
{{on "input" this.onInput}}
|
|
221
|
+
{{on "change" this.onChange}}
|
|
222
|
+
/>
|
|
223
|
+
<span
|
|
224
|
+
class="ember-primitives__slider__thumb"
|
|
225
|
+
style={{this.positionStyle}}
|
|
226
|
+
data-active={{if this.isActive ""}}
|
|
227
|
+
data-disabled={{if @store.disabled ""}}
|
|
228
|
+
aria-hidden="true"
|
|
229
|
+
>{{yield}}</span>
|
|
230
|
+
</template>
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export class Slider extends Component<Signature> {
|
|
234
|
+
store: SliderStore;
|
|
235
|
+
|
|
236
|
+
constructor(owner: Owner, args: Signature["Args"]) {
|
|
237
|
+
super(owner, args);
|
|
238
|
+
|
|
239
|
+
this.store = new SliderStore(() => this.args);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
<template>
|
|
243
|
+
<span
|
|
244
|
+
...attributes
|
|
245
|
+
class="ember-primitives__slider"
|
|
246
|
+
data-orientation={{this.store.orientation}}
|
|
247
|
+
data-disabled={{if this.store.disabled ""}}
|
|
248
|
+
data-multi={{if this.store.isMulti ""}}
|
|
249
|
+
>
|
|
250
|
+
{{#if (has-block)}}
|
|
251
|
+
{{yield
|
|
252
|
+
(hash
|
|
253
|
+
Track=Track
|
|
254
|
+
Range=(component Range rangeStyle=this.store.rangeStyle)
|
|
255
|
+
Thumb=(component ThumbComponent store=this.store)
|
|
256
|
+
values=this.store.values
|
|
257
|
+
tickValues=this.store.tickValues
|
|
258
|
+
thumbs=this.store.thumbs
|
|
259
|
+
min=this.store.min
|
|
260
|
+
max=this.store.max
|
|
261
|
+
step=this.store.step
|
|
262
|
+
)
|
|
263
|
+
}}
|
|
264
|
+
{{else}}
|
|
265
|
+
<Track>
|
|
266
|
+
<Range @rangeStyle={{this.store.rangeStyle}} />
|
|
267
|
+
|
|
268
|
+
{{#each this.store.thumbs as |thumb|}}
|
|
269
|
+
<ThumbComponent
|
|
270
|
+
@store={{this.store}}
|
|
271
|
+
@thumb={{thumb}}
|
|
272
|
+
aria-label={{this.store.defaultThumbLabel thumb.index}}
|
|
273
|
+
/>
|
|
274
|
+
{{/each}}
|
|
275
|
+
</Track>
|
|
276
|
+
{{/if}}
|
|
277
|
+
</span>
|
|
278
|
+
</template>
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export default Slider;
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ export { Rating } from './components/rating.gts';
|
|
|
39
39
|
export { Scroller } from './components/scroller.gts';
|
|
40
40
|
export { Separator } from './components/separator.gts';
|
|
41
41
|
export { Shadowed } from './components/shadowed.gts';
|
|
42
|
+
export { Slider } from './components/slider.gts';
|
|
42
43
|
export { Switch } from './components/switch.gts';
|
|
43
44
|
export { Toggle } from './components/toggle.gts';
|
|
44
45
|
export { ToggleGroup } from './components/toggle-group.gts';
|