jsl-ui 10.0.5 → 10.1.0

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.
@@ -0,0 +1,399 @@
1
+ import "./DropdownMenuStyle.scss";
2
+ import { JSLRender } from "jsl-render";
3
+ import { helper } from "../../core/helper";
4
+ /** A button-like trigger that opens an anchored, keyboard-accessible menu. */
5
+ export class DropdownMenuComponent {
6
+ constructor(options) {
7
+ this.State = { id: helper.newDomGuid(), open: false };
8
+ this.options = {
9
+ width: "12rem",
10
+ height: "max-content",
11
+ maxWidth: "calc(100vw - 1rem)",
12
+ maxHeight: "calc(100vh - 1rem)",
13
+ closeOnClick: false,
14
+ placement: "bottom-start",
15
+ showIndicator: true,
16
+ children: [],
17
+ ...options
18
+ };
19
+ }
20
+ isEqual(node) {
21
+ return Object.getPrototypeOf(this) === Object.getPrototypeOf(node);
22
+ }
23
+ render() {
24
+ var _a, _b;
25
+ this.State.owner = this;
26
+ const tag = (_a = this.options.tag) !== null && _a !== void 0 ? _a : "button";
27
+ const isButton = tag.toLowerCase() === "button";
28
+ const disabled = this.options.disabled === true;
29
+ const customContent = typeof this.options.content !== "string";
30
+ return {
31
+ tag,
32
+ attr: {
33
+ id: this.State.id + "-trigger",
34
+ class: ["jsl-dropdown-trigger", this.options.showIndicator ? "" : "jsl-dropdown-trigger--no-indicator", this.options.class].filter(Boolean).join(" "),
35
+ style: this.options.style,
36
+ title: this.options.title,
37
+ type: isButton ? (_b = this.options.type) !== null && _b !== void 0 ? _b : "button" : undefined,
38
+ disabled: isButton && disabled ? "disabled" : undefined,
39
+ role: isButton ? undefined : "button",
40
+ tabindex: !isButton ? (disabled ? "-1" : "0") : undefined,
41
+ "aria-disabled": !isButton && disabled ? "true" : undefined,
42
+ "aria-label": this.options.ariaLabel,
43
+ "aria-haspopup": "menu",
44
+ "aria-expanded": String(this.State.open),
45
+ "aria-controls": this.State.id + "-menu",
46
+ click: (event) => this.onTriggerClick(event),
47
+ keydown: (event) => this.onTriggerKeydown(event)
48
+ },
49
+ content: typeof this.options.content === "string" ? this.options.content : undefined,
50
+ raw: this.options.rawContent,
51
+ children: customContent ? [this.options.content] : undefined
52
+ };
53
+ }
54
+ onCreate(node) {
55
+ this.State.trigger = node.dom;
56
+ this.State.owner = this;
57
+ }
58
+ onUpdate(node) {
59
+ this.State.trigger = node.dom;
60
+ this.State.owner = this;
61
+ this.syncTrigger();
62
+ }
63
+ onRemove() {
64
+ this.closeInternal(false);
65
+ this.State.trigger = undefined;
66
+ this.State.owner = undefined;
67
+ }
68
+ /** Open the menu and move focus to its first or last enabled item. */
69
+ open(initialFocus = "first") {
70
+ var _a, _b, _c;
71
+ if (this.State.open || this.options.disabled || !this.State.trigger) {
72
+ return;
73
+ }
74
+ if (((_b = (_a = this.options).onOpen) === null || _b === void 0 ? void 0 : _b.call(_a, this.options)) === false) {
75
+ return;
76
+ }
77
+ const state = this.State;
78
+ state.open = true;
79
+ this.syncTrigger();
80
+ const portal = document.createElement("div");
81
+ portal.className = "jsl-dropdown-portal";
82
+ this.inheritTheme(portal);
83
+ const dialogHost = state.trigger.closest("dialog[open]");
84
+ (dialogHost !== null && dialogHost !== void 0 ? dialogHost : document.body).appendChild(portal);
85
+ state.portal = portal;
86
+ state.renderer = new JSLRender(portal, false);
87
+ state.renderer.render(this.menuNode());
88
+ state.menu = portal.querySelector("[role=menu]");
89
+ this.prepareMenuItems();
90
+ this.positionMenu();
91
+ const outsidePointer = (event) => {
92
+ var _a, _b, _c;
93
+ const target = event.target;
94
+ if (!((_a = state.trigger) === null || _a === void 0 ? void 0 : _a.contains(target)) && !((_b = state.menu) === null || _b === void 0 ? void 0 : _b.contains(target))) {
95
+ (_c = state.owner) === null || _c === void 0 ? void 0 : _c.closeInternal(false);
96
+ }
97
+ };
98
+ const outsideFocus = (event) => {
99
+ var _a, _b, _c;
100
+ const target = event.target;
101
+ if (!((_a = state.trigger) === null || _a === void 0 ? void 0 : _a.contains(target)) && !((_b = state.menu) === null || _b === void 0 ? void 0 : _b.contains(target))) {
102
+ (_c = state.owner) === null || _c === void 0 ? void 0 : _c.closeInternal(false);
103
+ }
104
+ };
105
+ const reposition = () => { var _a; return (_a = state.owner) === null || _a === void 0 ? void 0 : _a.positionMenu(); };
106
+ document.addEventListener("pointerdown", outsidePointer, true);
107
+ document.addEventListener("focusin", outsideFocus, true);
108
+ document.addEventListener("scroll", reposition, true);
109
+ window.addEventListener("resize", reposition);
110
+ const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(reposition);
111
+ if (state.trigger) {
112
+ observer === null || observer === void 0 ? void 0 : observer.observe(state.trigger);
113
+ }
114
+ if (state.menu) {
115
+ observer === null || observer === void 0 ? void 0 : observer.observe(state.menu);
116
+ }
117
+ state.cleanup = () => {
118
+ document.removeEventListener("pointerdown", outsidePointer, true);
119
+ document.removeEventListener("focusin", outsideFocus, true);
120
+ document.removeEventListener("scroll", reposition, true);
121
+ window.removeEventListener("resize", reposition);
122
+ observer === null || observer === void 0 ? void 0 : observer.disconnect();
123
+ if (state.typeaheadTimer != null) {
124
+ window.clearTimeout(state.typeaheadTimer);
125
+ }
126
+ };
127
+ if (initialFocus !== "none") {
128
+ const items = this.menuItems();
129
+ const item = initialFocus === "last" ? items[items.length - 1] : items[0];
130
+ (_c = (item !== null && item !== void 0 ? item : state.menu)) === null || _c === void 0 ? void 0 : _c.focus();
131
+ }
132
+ }
133
+ toggle() {
134
+ if (this.State.open) {
135
+ this.close();
136
+ }
137
+ else {
138
+ this.open();
139
+ }
140
+ }
141
+ /** Close the menu and restore focus to its trigger. */
142
+ close() {
143
+ this.closeInternal(true);
144
+ }
145
+ menuNode() {
146
+ var _a, _b;
147
+ const style = [
148
+ "position:fixed", "left:0", "top:0", "width:" + this.options.width,
149
+ "height:" + this.options.height, "max-width:" + this.options.maxWidth,
150
+ "max-height:" + this.options.maxHeight, this.options.menuStyle
151
+ ].filter(Boolean).join(";");
152
+ return {
153
+ tag: "div",
154
+ attr: {
155
+ id: this.State.id + "-menu",
156
+ class: "jsl-dropdown-menu " + ((_a = this.options.menuClass) !== null && _a !== void 0 ? _a : ""),
157
+ style,
158
+ role: "menu",
159
+ tabindex: "-1",
160
+ "aria-label": (_b = this.options.menuLabel) !== null && _b !== void 0 ? _b : "Actions",
161
+ keydown: (event) => { var _a; return ((_a = this.State.owner) !== null && _a !== void 0 ? _a : this).onMenuKeydown(event); },
162
+ click: () => {
163
+ var _a, _b;
164
+ const owner = (_a = this.State.owner) !== null && _a !== void 0 ? _a : this;
165
+ if (owner.options.closeOnClick) {
166
+ const active = document.activeElement;
167
+ owner.closeInternal(!!((_b = owner.State.menu) === null || _b === void 0 ? void 0 : _b.contains(active)));
168
+ }
169
+ return false;
170
+ }
171
+ },
172
+ children: [{
173
+ tag: "div",
174
+ attr: { class: "dd-menu-comp" },
175
+ children: this.options.children
176
+ }]
177
+ };
178
+ }
179
+ onTriggerClick(event) {
180
+ event.preventDefault();
181
+ if (!this.options.disabled) {
182
+ this.toggle();
183
+ }
184
+ return false;
185
+ }
186
+ onTriggerKeydown(event) {
187
+ var _a;
188
+ if (this.options.disabled) {
189
+ return false;
190
+ }
191
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
192
+ event.preventDefault();
193
+ event.stopPropagation();
194
+ this.open(event.key === "ArrowUp" ? "last" : "first");
195
+ return false;
196
+ }
197
+ const isNativeButton = ((_a = this.options.tag) !== null && _a !== void 0 ? _a : "button").toLowerCase() === "button";
198
+ if (!isNativeButton && (event.key === "Enter" || event.key === " ")) {
199
+ event.preventDefault();
200
+ this.toggle();
201
+ }
202
+ else if (event.key === "Escape" && this.State.open) {
203
+ event.preventDefault();
204
+ this.close();
205
+ }
206
+ return false;
207
+ }
208
+ onMenuKeydown(event) {
209
+ var _a;
210
+ const items = this.menuItems();
211
+ const active = document.activeElement;
212
+ const index = items.indexOf(active);
213
+ let next = -1;
214
+ switch (event.key) {
215
+ case "ArrowDown":
216
+ next = index < 0 ? 0 : (index + 1) % items.length;
217
+ break;
218
+ case "ArrowUp":
219
+ next = index < 0 ? items.length - 1 : (index - 1 + items.length) % items.length;
220
+ break;
221
+ case "Home":
222
+ next = 0;
223
+ break;
224
+ case "End":
225
+ next = items.length - 1;
226
+ break;
227
+ case "Escape":
228
+ event.preventDefault();
229
+ event.stopPropagation();
230
+ this.close();
231
+ return false;
232
+ case "Tab":
233
+ event.preventDefault();
234
+ event.stopPropagation();
235
+ this.moveFocusFromTrigger(event.shiftKey);
236
+ return false;
237
+ case "Enter":
238
+ if (active && active.tagName !== "BUTTON" && !(active.tagName === "A" && active.hasAttribute("href")) &&
239
+ ((_a = active.getAttribute("role")) === null || _a === void 0 ? void 0 : _a.startsWith("menuitem"))) {
240
+ event.preventDefault();
241
+ active.click();
242
+ }
243
+ return false;
244
+ case " ":
245
+ if ((active === null || active === void 0 ? void 0 : active.tagName) !== "BUTTON") {
246
+ event.preventDefault();
247
+ active === null || active === void 0 ? void 0 : active.click();
248
+ }
249
+ return false;
250
+ default:
251
+ if (event.key.length === 1 && event.key !== " " && !event.ctrlKey && !event.altKey && !event.metaKey) {
252
+ this.typeahead(event.key, index);
253
+ event.preventDefault();
254
+ }
255
+ return false;
256
+ }
257
+ if (items.length) {
258
+ event.preventDefault();
259
+ event.stopPropagation();
260
+ this.focusMenuItem(items[next]);
261
+ }
262
+ return false;
263
+ }
264
+ prepareMenuItems() {
265
+ const menu = this.State.menu;
266
+ if (!menu) {
267
+ return;
268
+ }
269
+ const selector = "button, a, [role^='menuitem'], [data-menu-item]";
270
+ menu.querySelectorAll(selector).forEach(item => {
271
+ const role = item.getAttribute("role");
272
+ if (!role || role === "button" || role === "link") {
273
+ item.setAttribute("role", "menuitem");
274
+ }
275
+ item.setAttribute("tabindex", "-1");
276
+ });
277
+ }
278
+ menuItems() {
279
+ const menu = this.State.menu;
280
+ if (!menu) {
281
+ return [];
282
+ }
283
+ return Array.from(menu.querySelectorAll("[role^='menuitem']"))
284
+ .filter(item => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true" &&
285
+ item.getAttribute("hidden") == null && item.getClientRects().length > 0);
286
+ }
287
+ focusMenuItem(item) {
288
+ const menu = this.State.menu;
289
+ if (!menu) {
290
+ return;
291
+ }
292
+ menu.querySelectorAll("[role^='menuitem']").forEach(entry => entry.setAttribute("tabindex", "-1"));
293
+ item.setAttribute("tabindex", "0");
294
+ item.focus();
295
+ }
296
+ typeahead(character, currentIndex) {
297
+ var _a;
298
+ const state = this.State;
299
+ state.typeahead = ((_a = state.typeahead) !== null && _a !== void 0 ? _a : "") + character.toLocaleLowerCase();
300
+ if (state.typeaheadTimer != null) {
301
+ window.clearTimeout(state.typeaheadTimer);
302
+ }
303
+ state.typeaheadTimer = window.setTimeout(() => { state.typeahead = ""; }, 500);
304
+ const items = this.menuItems();
305
+ for (let offset = 1; offset <= items.length; offset++) {
306
+ const index = (currentIndex + offset + items.length) % items.length;
307
+ const item = items[index];
308
+ const label = (item.getAttribute("aria-label") || item.textContent || "").trim().toLocaleLowerCase();
309
+ if (label.startsWith(state.typeahead)) {
310
+ this.focusMenuItem(item);
311
+ return;
312
+ }
313
+ }
314
+ }
315
+ moveFocusFromTrigger(backward) {
316
+ var _a;
317
+ const trigger = this.State.trigger;
318
+ const menu = this.State.menu;
319
+ const selector = "a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex='-1'])";
320
+ const candidates = Array.from(document.querySelectorAll(selector)).filter(element => !(menu === null || menu === void 0 ? void 0 : menu.contains(element)) && element.getAttribute("aria-disabled") !== "true" &&
321
+ !element.closest("[hidden]") && element.getClientRects().length > 0);
322
+ const current = candidates.indexOf(trigger);
323
+ const target = candidates[current + (backward ? -1 : 1)];
324
+ this.closeInternal(false);
325
+ (_a = (target !== null && target !== void 0 ? target : trigger)) === null || _a === void 0 ? void 0 : _a.focus();
326
+ }
327
+ positionMenu() {
328
+ var _a, _b, _c, _d, _e;
329
+ const menu = this.State.menu;
330
+ const trigger = this.State.trigger;
331
+ if (!menu || !trigger) {
332
+ return;
333
+ }
334
+ const rect = trigger.getBoundingClientRect();
335
+ const width = menu.getBoundingClientRect().width;
336
+ const height = menu.getBoundingClientRect().height;
337
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
338
+ const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
339
+ const placement = (_a = this.options.placement) !== null && _a !== void 0 ? _a : "bottom-start";
340
+ const topPlacement = placement.startsWith("top");
341
+ const endPlacement = placement.endsWith("end");
342
+ const rtl = getComputedStyle(trigger).direction === "rtl";
343
+ let top = (topPlacement ? rect.top - height : rect.bottom) + ((_b = this.options.offsetY) !== null && _b !== void 0 ? _b : 0);
344
+ let left = (endPlacement !== rtl ? rect.right - width : rect.left) + ((_c = this.options.offsetX) !== null && _c !== void 0 ? _c : 0);
345
+ if (!topPlacement && top + height > viewportHeight - 8 && rect.top - height >= 8) {
346
+ top = rect.top - height + ((_d = this.options.offsetY) !== null && _d !== void 0 ? _d : 0);
347
+ }
348
+ else if (topPlacement && top < 8 && rect.bottom + height <= viewportHeight - 8) {
349
+ top = rect.bottom + ((_e = this.options.offsetY) !== null && _e !== void 0 ? _e : 0);
350
+ }
351
+ left = Math.max(8, Math.min(left, viewportWidth - width - 8));
352
+ top = Math.max(8, Math.min(top, viewportHeight - height - 8));
353
+ menu.style.left = left + "px";
354
+ menu.style.top = top + "px";
355
+ }
356
+ inheritTheme(portal) {
357
+ const trigger = this.State.trigger;
358
+ if (!trigger) {
359
+ return;
360
+ }
361
+ const computed = getComputedStyle(trigger);
362
+ portal.style.color = computed.color;
363
+ portal.style.font = computed.font;
364
+ for (let index = 0; index < computed.length; index++) {
365
+ const property = computed.item(index);
366
+ if (property.startsWith("--jsl-")) {
367
+ portal.style.setProperty(property, computed.getPropertyValue(property));
368
+ }
369
+ }
370
+ }
371
+ syncTrigger() {
372
+ var _a;
373
+ (_a = this.State.trigger) === null || _a === void 0 ? void 0 : _a.setAttribute("aria-expanded", String(this.State.open));
374
+ }
375
+ closeInternal(restoreFocus) {
376
+ var _a;
377
+ const state = this.State;
378
+ if (!state.open && !state.portal) {
379
+ return;
380
+ }
381
+ const trigger = state.trigger;
382
+ const renderer = state.renderer;
383
+ const portal = state.portal;
384
+ state.open = false;
385
+ (_a = state.cleanup) === null || _a === void 0 ? void 0 : _a.call(state);
386
+ state.cleanup = undefined;
387
+ state.renderer = undefined;
388
+ state.portal = undefined;
389
+ state.menu = undefined;
390
+ state.typeahead = "";
391
+ this.syncTrigger();
392
+ renderer === null || renderer === void 0 ? void 0 : renderer.render({ tag: "div" });
393
+ portal === null || portal === void 0 ? void 0 : portal.remove();
394
+ if (restoreFocus) {
395
+ trigger === null || trigger === void 0 ? void 0 : trigger.focus();
396
+ }
397
+ }
398
+ }
399
+ export { DropdownMenuComponent as DropdownMenuButtonComponent };
@@ -0,0 +1,48 @@
1
+ @import "../../ui/theme.css";
2
+
3
+ .jsl-dropdown-trigger:not(.jsl-dropdown-trigger--no-indicator)::after {
4
+ content: "";
5
+ display: inline-block;
6
+ flex: 0 0 auto;
7
+ width: .42em;
8
+ height: .42em;
9
+ margin-inline-start: .55em;
10
+ border-right: 1.5px solid currentColor;
11
+ border-bottom: 1.5px solid currentColor;
12
+ transform: translateY(-.12em) rotate(45deg);
13
+ transition: transform .15s ease;
14
+ }
15
+ .jsl-dropdown-trigger[aria-expanded="true"]::after { transform: translateY(.08em) rotate(225deg); }
16
+ .jsl-dropdown-portal { position: fixed; inset: 0; z-index: 3000; pointer-events: none; }
17
+ .jsl-dropdown-menu {
18
+ box-sizing: border-box;
19
+ overflow: auto;
20
+ padding: .2rem;
21
+ border: 1px solid var(--jsl-color-border);
22
+ border-radius: .5rem;
23
+ background: var(--jsl-color-surface);
24
+ color: var(--jsl-color-text);
25
+ box-shadow: 0 8px 24px rgba(0, 0, 0, .16), 0 2px 6px rgba(0, 0, 0, .08);
26
+ pointer-events: auto;
27
+ outline: none;
28
+ }
29
+ .dd-menu-comp > * { display: block; width: calc(100% - .2rem); margin: .1rem; }
30
+ .dd-menu-comp {
31
+ --jsl-button-primary: var(--jsl-color-surface);
32
+ --jsl-button-on-primary: var(--jsl-color-text);
33
+ --jsl-button-primary-hover: var(--jsl-color-surface-hover);
34
+ --jsl-button-primary-active: var(--jsl-color-surface-selected);
35
+ --jsl-button-radius: .25rem;
36
+ display: flex;
37
+ flex-direction: column;
38
+ }
39
+ .dd-menu-comp > .jsl-button {
40
+ --jsl-button-height: 2.2rem;
41
+ display: flex;
42
+ flex: 0 0 auto;
43
+ align-items: center;
44
+ justify-content: flex-start;
45
+ text-align: left;
46
+ }
47
+ .jsl-dropdown-menu [role^="menuitem"]:focus-visible { outline: 2px solid var(--jsl-color-focus); outline-offset: -2px; }
48
+ @media (prefers-reduced-motion: reduce) { .jsl-dropdown-trigger::after { transition: none; } }
@@ -7,7 +7,7 @@
7
7
  width: 14rem;
8
8
  max-width: 100%;
9
9
  min-width: 0;
10
- min-height: 2.5rem;
10
+ min-height: 2.2rem;
11
11
  vertical-align: middle;
12
12
  color: var(--jsl-color-text);
13
13
  font: inherit;
@@ -0,0 +1,97 @@
1
+ import "./SliderStyle.scss";
2
+ import { IJSLComponent, IJSLVNode } from "jsl-render";
3
+ export type ISliderValue = number | number[];
4
+ export interface INoUiSliderFormat {
5
+ to(value: number): string;
6
+ from(value: string): number;
7
+ }
8
+ export interface INoUiSliderRange {
9
+ min: number;
10
+ max: number;
11
+ [percentage: string]: number | [number, number];
12
+ }
13
+ export interface INoUiSliderPipsOptions {
14
+ mode: "range" | "steps" | "positions" | "count" | "values";
15
+ values?: number | number[];
16
+ density?: number;
17
+ stepped?: boolean;
18
+ filter?: (value: number, type: number) => number;
19
+ format?: {
20
+ to(value: number): string;
21
+ };
22
+ }
23
+ export type ISliderEventHandler = (value: ISliderValue, handle: number, values: number[]) => void;
24
+ /** Options supported by the bundled noUiSlider 14.6 build and the component wrapper. */
25
+ export interface ISliderComponentOptions {
26
+ start: number | number[];
27
+ range: INoUiSliderRange;
28
+ connect?: boolean | "lower" | "upper" | boolean[];
29
+ direction?: "ltr" | "rtl";
30
+ orientation?: "horizontal" | "vertical";
31
+ step?: number;
32
+ margin?: number;
33
+ limit?: number;
34
+ padding?: number | [number, number];
35
+ behaviour?: string;
36
+ snap?: boolean;
37
+ animate?: boolean;
38
+ animationDuration?: number;
39
+ keyboardSupport?: boolean;
40
+ keyboardPageMultiplier?: number;
41
+ keyboardDefaultStep?: number;
42
+ format?: INoUiSliderFormat;
43
+ ariaFormat?: INoUiSliderFormat;
44
+ tooltips?: boolean | {
45
+ to(value: number): string;
46
+ } | Array<boolean | {
47
+ to(value: number): string;
48
+ }>;
49
+ pips?: INoUiSliderPipsOptions;
50
+ cssPrefix?: string | false;
51
+ cssClasses?: {
52
+ [key: string]: string;
53
+ };
54
+ documentElement?: HTMLElement;
55
+ enabled?: boolean;
56
+ ariaLabel?: string | string[];
57
+ class?: string;
58
+ style?: string;
59
+ onStart?: ISliderEventHandler;
60
+ onSlide?: ISliderEventHandler;
61
+ onUpdate?: ISliderEventHandler;
62
+ onChange?: ISliderEventHandler;
63
+ onSet?: ISliderEventHandler;
64
+ onEnd?: ISliderEventHandler;
65
+ }
66
+ export interface INoUiSliderInstance {
67
+ destroy(): void;
68
+ get(): string | string[];
69
+ set(value: number | string | Array<number | string | null>, fireSetEvent?: boolean): void;
70
+ setHandle(handle: number, value: number | string, fireSetEvent?: boolean): void;
71
+ reset(fireSetEvent?: boolean): void;
72
+ updateOptions(options: Partial<ISliderComponentOptions>, fireSetEvent?: boolean): void;
73
+ on(event: string, callback: (formattedValues: string[], handle: number, unencodedValues: number[]) => void): void;
74
+ off(event?: string): void;
75
+ target: HTMLElement;
76
+ }
77
+ export declare class SliderComponent implements IJSLComponent {
78
+ private options;
79
+ private slider;
80
+ private target;
81
+ private currentValue;
82
+ constructor(options: ISliderComponentOptions);
83
+ isEqual(node: IJSLComponent): boolean;
84
+ render(): IJSLVNode;
85
+ onCreate(node: IJSLVNode): void;
86
+ onRemove(): void;
87
+ getValue(): ISliderValue;
88
+ /** Set a value without firing noUiSlider's `set` event unless requested. */
89
+ setValue(value: ISliderValue, fireSetEvent?: boolean): void;
90
+ setEnabled(enabled: boolean): void;
91
+ getRef(): INoUiSliderInstance | null;
92
+ private bindEvent;
93
+ private applyHandleLabels;
94
+ private applyEnabledState;
95
+ private normalize;
96
+ private copyValue;
97
+ }
@@ -0,0 +1,135 @@
1
+ import "./SliderStyle.scss";
2
+ import { numeric } from "../../core/numeric";
3
+ import { noUiSlider } from "./nouislider";
4
+ const numericFormat = {
5
+ to: value => numeric.format(value),
6
+ from: value => numeric.parse(value)
7
+ };
8
+ export class SliderComponent {
9
+ constructor(options) {
10
+ var _a;
11
+ this.options = {
12
+ enabled: true, keyboardSupport: true, ...options,
13
+ format: (_a = options.format) !== null && _a !== void 0 ? _a : numericFormat
14
+ };
15
+ this.currentValue = this.normalize(options.start);
16
+ }
17
+ isEqual(node) {
18
+ return this.__proto__ === node.__proto__;
19
+ }
20
+ render() {
21
+ return {
22
+ tag: "div",
23
+ attr: {
24
+ class: "jsl-slider" + (this.options.orientation === "vertical" ? " jsl-slider-vertical" : "") +
25
+ (this.options.class ? " " + this.options.class : "") + (this.options.enabled ? "" : " jsl-slider-disabled"),
26
+ style: this.options.style,
27
+ disabled: this.options.enabled ? undefined : ""
28
+ }
29
+ };
30
+ }
31
+ onCreate(node) {
32
+ this.target = node.dom;
33
+ const { start: _start, enabled: _enabled, ariaLabel: _ariaLabel, class: _class, style: _style, onStart: _onStart, onSlide: _onSlide, onUpdate: _onUpdate, onChange: _onChange, onSet: _onSet, onEnd: _onEnd, ...sliderOptions } = this.options;
34
+ this.slider = noUiSlider.create(this.target, {
35
+ ...sliderOptions,
36
+ start: Array.isArray(this.currentValue) ? this.currentValue.slice() : this.currentValue
37
+ });
38
+ this.applyHandleLabels();
39
+ this.applyEnabledState();
40
+ this.slider.on("update", (_formatted, handle, values) => {
41
+ var _a, _b;
42
+ this.currentValue = this.normalize(values);
43
+ (_b = (_a = this.options).onUpdate) === null || _b === void 0 ? void 0 : _b.call(_a, this.copyValue(this.currentValue), handle, values.slice());
44
+ });
45
+ this.bindEvent("start", this.options.onStart);
46
+ this.bindEvent("slide", this.options.onSlide);
47
+ this.bindEvent("change", this.options.onChange);
48
+ this.bindEvent("set", this.options.onSet);
49
+ this.bindEvent("end", this.options.onEnd);
50
+ }
51
+ onRemove() {
52
+ var _a;
53
+ (_a = this.slider) === null || _a === void 0 ? void 0 : _a.destroy();
54
+ this.slider = null;
55
+ this.target = null;
56
+ }
57
+ getValue() {
58
+ return this.copyValue(this.currentValue);
59
+ }
60
+ /** Set a value without firing noUiSlider's `set` event unless requested. */
61
+ setValue(value, fireSetEvent = false) {
62
+ this.currentValue = this.normalize(value);
63
+ if (this.slider) {
64
+ this.slider.set(Array.isArray(value) ? value.slice() : value, fireSetEvent);
65
+ }
66
+ else {
67
+ this.options.start = this.copyValue(this.currentValue);
68
+ }
69
+ }
70
+ setEnabled(enabled) {
71
+ this.options.enabled = enabled;
72
+ this.applyEnabledState();
73
+ }
74
+ getRef() {
75
+ var _a;
76
+ return (_a = this.slider) !== null && _a !== void 0 ? _a : null;
77
+ }
78
+ bindEvent(event, callback) {
79
+ if (!callback) {
80
+ return;
81
+ }
82
+ this.slider.on(event, (_formatted, handle, values) => {
83
+ const numericValues = values.map(Number);
84
+ callback(this.normalize(numericValues), handle, numericValues);
85
+ });
86
+ }
87
+ applyHandleLabels() {
88
+ if (!this.target) {
89
+ return;
90
+ }
91
+ const handles = this.target.querySelectorAll("[role=slider]");
92
+ handles.forEach((handle, index) => {
93
+ const configured = Array.isArray(this.options.ariaLabel)
94
+ ? this.options.ariaLabel[index]
95
+ : this.options.ariaLabel;
96
+ const label = configured || (handles.length === 1 ? "Slider value" : "Slider value " + (index + 1));
97
+ handle.setAttribute("aria-label", label);
98
+ });
99
+ }
100
+ applyEnabledState() {
101
+ if (!this.target) {
102
+ return;
103
+ }
104
+ const enabled = this.options.enabled !== false;
105
+ if (enabled) {
106
+ this.target.removeAttribute("disabled");
107
+ this.target.classList.remove("jsl-slider-disabled");
108
+ }
109
+ else {
110
+ this.target.setAttribute("disabled", "");
111
+ this.target.classList.add("jsl-slider-disabled");
112
+ }
113
+ this.target.querySelectorAll("[role=slider]").forEach(handle => {
114
+ if (enabled) {
115
+ handle.removeAttribute("aria-disabled");
116
+ if (this.options.keyboardSupport !== false) {
117
+ handle.setAttribute("tabindex", "0");
118
+ }
119
+ else {
120
+ handle.removeAttribute("tabindex");
121
+ }
122
+ }
123
+ else {
124
+ handle.setAttribute("aria-disabled", "true");
125
+ handle.setAttribute("tabindex", "-1");
126
+ }
127
+ });
128
+ }
129
+ normalize(value) {
130
+ return Array.isArray(value) ? value.map(Number) : Number(value);
131
+ }
132
+ copyValue(value) {
133
+ return Array.isArray(value) ? value.slice() : value;
134
+ }
135
+ }