@dodlhuat/basix 1.2.7 → 1.2.9

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 (69) hide show
  1. package/README.md +1 -1
  2. package/js/bottom-sheet.d.ts +37 -0
  3. package/js/calendar.d.ts +115 -0
  4. package/js/carousel.d.ts +34 -0
  5. package/js/chart.d.ts +73 -0
  6. package/js/code-viewer.d.ts +16 -0
  7. package/js/context-menu.d.ts +31 -0
  8. package/js/datepicker.d.ts +55 -0
  9. package/js/dropdown.d.ts +30 -0
  10. package/js/editor.d.ts +41 -0
  11. package/js/file-uploader.d.ts +48 -0
  12. package/js/flyout-menu.d.ts +37 -0
  13. package/js/gallery.d.ts +35 -0
  14. package/js/group-picker.d.ts +59 -0
  15. package/js/lightbox.d.ts +46 -0
  16. package/js/modal.d.ts +28 -0
  17. package/js/popover.d.ts +46 -0
  18. package/js/position.d.ts +31 -0
  19. package/js/push-menu.d.ts +31 -0
  20. package/js/range-slider.d.ts +9 -0
  21. package/js/scroll.d.ts +15 -0
  22. package/js/scrollbar.d.ts +48 -0
  23. package/js/select.d.ts +16 -0
  24. package/js/sidebar-nav.d.ts +22 -0
  25. package/js/stepper.d.ts +26 -0
  26. package/js/table.d.ts +98 -0
  27. package/js/tabs.d.ts +57 -0
  28. package/js/theme.d.ts +65 -0
  29. package/js/timepicker.d.ts +37 -0
  30. package/js/toast.d.ts +26 -0
  31. package/js/tooltip.d.ts +34 -0
  32. package/js/tree.d.ts +40 -0
  33. package/js/utils.d.ts +24 -0
  34. package/js/virtual-dropdown.d.ts +55 -0
  35. package/package.json +1 -1
  36. package/js/bottom-sheet.ts +0 -224
  37. package/js/calendar.ts +0 -774
  38. package/js/carousel.ts +0 -222
  39. package/js/chart.ts +0 -694
  40. package/js/code-viewer.ts +0 -188
  41. package/js/context-menu.ts +0 -252
  42. package/js/datepicker.ts +0 -640
  43. package/js/dropdown.ts +0 -180
  44. package/js/editor.ts +0 -492
  45. package/js/file-uploader.ts +0 -361
  46. package/js/flyout-menu.ts +0 -255
  47. package/js/gallery.ts +0 -237
  48. package/js/group-picker.ts +0 -451
  49. package/js/lightbox.ts +0 -333
  50. package/js/modal.ts +0 -171
  51. package/js/popover.ts +0 -221
  52. package/js/position.ts +0 -111
  53. package/js/push-menu.ts +0 -286
  54. package/js/range-slider.ts +0 -33
  55. package/js/scroll.ts +0 -47
  56. package/js/scrollbar.ts +0 -335
  57. package/js/select.ts +0 -235
  58. package/js/sidebar-nav.ts +0 -66
  59. package/js/stepper.ts +0 -109
  60. package/js/table.ts +0 -459
  61. package/js/tabs.ts +0 -280
  62. package/js/theme.ts +0 -235
  63. package/js/timepicker.ts +0 -202
  64. package/js/toast.ts +0 -134
  65. package/js/tooltip.ts +0 -196
  66. package/js/tree.ts +0 -244
  67. package/js/tsconfig.json +0 -18
  68. package/js/utils.ts +0 -119
  69. package/js/virtual-dropdown.ts +0 -396
package/js/popover.ts DELETED
@@ -1,221 +0,0 @@
1
- import { computePosition } from './position.js';
2
- import type { Placement } from './position.js';
3
- import { sanitizeHtml } from './utils.js';
4
-
5
- type PopoverPlacement = Placement | 'auto';
6
- type PopoverAlign = 'start' | 'center' | 'end';
7
- type PopoverTrigger = 'click' | 'hover';
8
-
9
- interface PopoverOptions {
10
- content: string;
11
- placement?: PopoverPlacement;
12
- align?: PopoverAlign;
13
- offset?: number;
14
- arrow?: boolean;
15
- triggerMode?: PopoverTrigger;
16
- closeOnOutsideClick?: boolean;
17
- closeOnEscape?: boolean;
18
- className?: string;
19
- onOpen?: () => void;
20
- onClose?: () => void;
21
- }
22
-
23
- // Must match $arrow in popover.scss
24
- const ARROW_SIZE = 6;
25
-
26
- class Popover {
27
- private static openPopovers: Set<Popover> = new Set();
28
- private static idCounter = 0;
29
-
30
- private readonly trigger: HTMLElement;
31
- private readonly opts: Required<PopoverOptions>;
32
- private popoverEl: HTMLElement | null = null;
33
- private _isOpen = false;
34
- private hoverTimer: number | null = null;
35
-
36
- constructor(triggerEl: HTMLElement | string, options: PopoverOptions) {
37
- const el = typeof triggerEl === 'string'
38
- ? document.querySelector<HTMLElement>(triggerEl)
39
- : triggerEl;
40
- if (!el) throw new Error('Popover: trigger element not found');
41
-
42
- this.trigger = el;
43
- this.opts = {
44
- content: options.content,
45
- placement: options.placement ?? 'bottom',
46
- align: options.align ?? 'center',
47
- offset: options.offset ?? 8,
48
- arrow: options.arrow ?? true,
49
- triggerMode: options.triggerMode ?? 'click',
50
- closeOnOutsideClick: options.closeOnOutsideClick ?? true,
51
- closeOnEscape: options.closeOnEscape ?? true,
52
- className: options.className ?? '',
53
- onOpen: options.onOpen ?? (() => {}),
54
- onClose: options.onClose ?? (() => {}),
55
- };
56
-
57
- this.attachTrigger();
58
- }
59
-
60
- // ── Public API ─────────────────────────────────────────────────────────────
61
-
62
- get isOpen(): boolean { return this._isOpen; }
63
-
64
- open(): void {
65
- if (this._isOpen) return;
66
- if (this.opts.triggerMode === 'click') Popover.closeAll();
67
-
68
- this.popoverEl = this.buildEl();
69
- document.body.appendChild(this.popoverEl);
70
- this.reposition();
71
-
72
- requestAnimationFrame(() => {
73
- this.popoverEl?.classList.add('is-open');
74
- this._isOpen = true;
75
- Popover.openPopovers.add(this);
76
- this.opts.onOpen();
77
-
78
- if (this.opts.closeOnOutsideClick)
79
- document.addEventListener('pointerdown', this.onOutsideClick, { capture: true });
80
- if (this.opts.closeOnEscape)
81
- document.addEventListener('keydown', this.onEscape);
82
- });
83
- }
84
-
85
- close(): void {
86
- if (!this._isOpen || !this.popoverEl) return;
87
-
88
- this.popoverEl.classList.remove('is-open');
89
- this._isOpen = false;
90
- Popover.openPopovers.delete(this);
91
- this.opts.onClose();
92
-
93
- document.removeEventListener('pointerdown', this.onOutsideClick, { capture: true });
94
- document.removeEventListener('keydown', this.onEscape);
95
-
96
- this.trigger.removeAttribute('aria-expanded');
97
- this.trigger.removeAttribute('aria-controls');
98
-
99
- const el = this.popoverEl;
100
- setTimeout(() => el.remove(), 200);
101
- this.popoverEl = null;
102
- }
103
-
104
- toggle(): void { this._isOpen ? this.close() : this.open(); }
105
-
106
- destroy(): void {
107
- this.close();
108
- this.detachTrigger();
109
- }
110
-
111
- static closeAll(): void {
112
- Popover.openPopovers.forEach(p => p.close());
113
- }
114
-
115
- /** Declarative init — reads [data-popover="#selector"] attributes */
116
- static initAll(): void {
117
- document.querySelectorAll<HTMLElement>('[data-popover]').forEach(trigger => {
118
- const sel = trigger.getAttribute('data-popover');
119
- if (!sel) return;
120
- const contentEl = document.querySelector(sel);
121
- if (!contentEl) return;
122
-
123
- new Popover(trigger, {
124
- content: (contentEl as HTMLElement).innerHTML,
125
- placement: (trigger.getAttribute('data-popover-placement') as PopoverPlacement) ?? 'bottom',
126
- align: (trigger.getAttribute('data-popover-align') as PopoverAlign) ?? 'center',
127
- triggerMode:(trigger.getAttribute('data-popover-trigger') as PopoverTrigger) ?? 'click',
128
- arrow: trigger.getAttribute('data-popover-arrow') !== 'false',
129
- });
130
- });
131
- }
132
-
133
- // ── Build ──────────────────────────────────────────────────────────────────
134
-
135
- private buildEl(): HTMLElement {
136
- const id = `popover-${++Popover.idCounter}`;
137
- const el = document.createElement('div');
138
- el.className = ['popover', this.opts.className].filter(Boolean).join(' ');
139
- el.id = id;
140
- el.setAttribute('role', 'dialog');
141
- el.setAttribute('data-arrow', String(this.opts.arrow));
142
-
143
- // Wrap plain content in .popover-body so it gets proper padding.
144
- // Skip wrapping when content already uses structured popover elements.
145
- const hasStructure = /class="popover-(header|body|footer|menu)/.test(this.opts.content);
146
- const safeContent = sanitizeHtml(this.opts.content);
147
- el.innerHTML = hasStructure
148
- ? safeContent
149
- : `<div class="popover-body">${safeContent}</div>`;
150
-
151
- this.trigger.setAttribute('aria-expanded', 'true');
152
- this.trigger.setAttribute('aria-controls', id);
153
-
154
- return el;
155
- }
156
-
157
- // ── Positioning ────────────────────────────────────────────────────────────
158
-
159
- private reposition(): void {
160
- if (!this.popoverEl) return;
161
-
162
- const { left, top, placement, arrowOffset } = computePosition(
163
- this.trigger.getBoundingClientRect(),
164
- this.popoverEl.getBoundingClientRect(),
165
- {
166
- placement: this.opts.placement,
167
- align: this.opts.align,
168
- offset: this.opts.offset,
169
- arrowSize: this.opts.arrow ? ARROW_SIZE : undefined,
170
- }
171
- );
172
-
173
- if (arrowOffset !== undefined)
174
- this.popoverEl.style.setProperty('--popover-arrow-offset', `${arrowOffset}px`);
175
-
176
- this.popoverEl.setAttribute('data-placement', placement);
177
- this.popoverEl.setAttribute('data-align', this.opts.align);
178
- this.popoverEl.style.left = `${left}px`;
179
- this.popoverEl.style.top = `${top}px`;
180
- }
181
-
182
- // ── Event handlers ─────────────────────────────────────────────────────────
183
-
184
- private onClick = (): void => { this.toggle(); };
185
-
186
- private onMouseEnter = (): void => {
187
- if (this.hoverTimer !== null) clearTimeout(this.hoverTimer);
188
- this.open();
189
- };
190
-
191
- private onMouseLeave = (): void => {
192
- this.hoverTimer = window.setTimeout(() => this.close(), 120);
193
- };
194
-
195
- private onOutsideClick = (e: Event): void => {
196
- const t = e.target as Node;
197
- if (!this.popoverEl?.contains(t) && !this.trigger.contains(t)) this.close();
198
- };
199
-
200
- private onEscape = (e: KeyboardEvent): void => {
201
- if (e.key === 'Escape') this.close();
202
- };
203
-
204
- private attachTrigger(): void {
205
- if (this.opts.triggerMode === 'click') {
206
- this.trigger.addEventListener('click', this.onClick);
207
- } else {
208
- this.trigger.addEventListener('mouseenter', this.onMouseEnter);
209
- this.trigger.addEventListener('mouseleave', this.onMouseLeave);
210
- }
211
- }
212
-
213
- private detachTrigger(): void {
214
- this.trigger.removeEventListener('click', this.onClick);
215
- this.trigger.removeEventListener('mouseenter', this.onMouseEnter);
216
- this.trigger.removeEventListener('mouseleave', this.onMouseLeave);
217
- }
218
- }
219
-
220
- export { Popover };
221
- export type { PopoverOptions, PopoverPlacement, PopoverAlign, PopoverTrigger };
package/js/position.ts DELETED
@@ -1,111 +0,0 @@
1
- /**
2
- * Shared floating-element positioning utility.
3
- * Used by Tooltip and Popover.
4
- */
5
-
6
- type Placement = 'top' | 'bottom' | 'left' | 'right';
7
- type Align = 'start' | 'center' | 'end';
8
-
9
- interface PositionOptions {
10
- placement: Placement | 'auto';
11
- align?: Align;
12
- offset?: number; // gap between trigger and floating element (default: 8)
13
- margin?: number; // minimum distance from viewport edge (default: 8)
14
- arrowSize?: number; // if set, returns arrowOffset in result
15
- }
16
-
17
- interface PositionResult {
18
- left: number;
19
- top: number;
20
- placement: Placement; // resolved (after auto/flip)
21
- arrowOffset?: number; // px offset for arrow centering on trigger axis
22
- }
23
-
24
- /** Pick the placement with the most available space, preferring bottom > top > right > left. */
25
- function bestPlacement(trigger: DOMRect, floating: DOMRect, offset: number): Placement {
26
- const space = {
27
- bottom: window.innerHeight - trigger.bottom - offset,
28
- top: trigger.top - offset,
29
- right: window.innerWidth - trigger.right - offset,
30
- left: trigger.left - offset,
31
- };
32
- if (space.bottom >= floating.height) return 'bottom';
33
- if (space.top >= floating.height) return 'top';
34
- if (space.right >= floating.width) return 'right';
35
- if (space.left >= floating.width) return 'left';
36
- // Fallback: largest available side
37
- return (Object.entries(space).sort((a, b) => b[1] - a[1])[0][0]) as Placement;
38
- }
39
-
40
- /** Flip to opposite side if preferred placement doesn't fit. */
41
- function maybeFlip(placement: Placement, trigger: DOMRect, floating: DOMRect, offset: number): Placement {
42
- const fits: Record<Placement, boolean> = {
43
- top: trigger.top - offset >= floating.height,
44
- bottom: window.innerHeight - trigger.bottom - offset >= floating.height,
45
- left: trigger.left - offset >= floating.width,
46
- right: window.innerWidth - trigger.right - offset >= floating.width,
47
- };
48
- const opp: Record<Placement, Placement> = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
49
- return (!fits[placement] && fits[opp[placement]]) ? opp[placement] : placement;
50
- }
51
-
52
- /**
53
- * Compute `left` / `top` for a `position: fixed` floating element anchored to a trigger.
54
- * Handles placement resolution (auto + flip), cross-axis alignment, viewport clamping,
55
- * and optional arrow offset calculation.
56
- */
57
- function computePosition(trigger: DOMRect, floating: DOMRect, opts: PositionOptions): PositionResult {
58
- const offset = opts.offset ?? 8;
59
- const margin = opts.margin ?? 8;
60
- const align = opts.align ?? 'center';
61
-
62
- const placement: Placement = opts.placement === 'auto'
63
- ? bestPlacement(trigger, floating, offset)
64
- : maybeFlip(opts.placement, trigger, floating, offset);
65
-
66
- // Main-axis offset
67
- let left = 0, top = 0;
68
- switch (placement) {
69
- case 'top': top = trigger.top - floating.height - offset; break;
70
- case 'bottom': top = trigger.bottom + offset; break;
71
- case 'left': left = trigger.left - floating.width - offset; break;
72
- case 'right': left = trigger.right + offset; break;
73
- }
74
-
75
- // Cross-axis alignment
76
- if (placement === 'top' || placement === 'bottom') {
77
- switch (align) {
78
- case 'start': left = trigger.left; break;
79
- case 'center': left = trigger.left + (trigger.width - floating.width) / 2; break;
80
- case 'end': left = trigger.right - floating.width; break;
81
- }
82
- } else {
83
- switch (align) {
84
- case 'start': top = trigger.top; break;
85
- case 'center': top = trigger.top + (trigger.height - floating.height) / 2; break;
86
- case 'end': top = trigger.bottom - floating.height; break;
87
- }
88
- }
89
-
90
- // Clamp to viewport
91
- const l = Math.max(margin, Math.min(window.innerWidth - floating.width - margin, left));
92
- const t = Math.max(margin, Math.min(window.innerHeight - floating.height - margin, top));
93
-
94
- // Arrow offset: keep arrow centred on the trigger even after viewport clamping
95
- let arrowOffset: number | undefined;
96
- if (opts.arrowSize !== undefined) {
97
- const minOff = opts.arrowSize + 8; // min distance from rounded corner
98
- if (placement === 'top' || placement === 'bottom') {
99
- const raw = trigger.left + trigger.width / 2 - l;
100
- arrowOffset = Math.max(minOff, Math.min(floating.width - minOff, raw));
101
- } else {
102
- const raw = trigger.top + trigger.height / 2 - t;
103
- arrowOffset = Math.max(minOff, Math.min(floating.height - minOff, raw));
104
- }
105
- }
106
-
107
- return { left: l, top: t, placement, arrowOffset };
108
- }
109
-
110
- export { computePosition, bestPlacement, maybeFlip };
111
- export type { Placement, Align, PositionOptions, PositionResult };
package/js/push-menu.ts DELETED
@@ -1,286 +0,0 @@
1
- interface PushMenuElements {
2
- navigation: HTMLElement | null;
3
- content: HTMLElement | null;
4
- menu: HTMLElement | null;
5
- header: HTMLElement | null;
6
- controlIcon: HTMLElement | null;
7
- backdrop: HTMLElement | null;
8
- }
9
-
10
- class PushMenu {
11
- private static elements: PushMenuElements = {
12
- navigation: null,
13
- content: null,
14
- menu: null,
15
- header: null,
16
- controlIcon: null,
17
- backdrop: null
18
- };
19
-
20
- private static initialized = false;
21
- private static panelStack: HTMLElement[] = [];
22
- private static boundHandleNavigationChange: () => void;
23
-
24
- public static init(): void {
25
- if (this.initialized) {
26
- console.warn('PushMenu: Already initialized');
27
- return;
28
- }
29
-
30
- this.refresh();
31
-
32
- if (!this.elements.navigation || !this.elements.content) {
33
- throw new Error('PushMenu: Required elements not found (.navigation, .push-content)');
34
- }
35
-
36
- this.buildPanels();
37
-
38
- this.boundHandleNavigationChange = this.handleNavigationChange.bind(this);
39
- this.elements.navigation.addEventListener('change', this.boundHandleNavigationChange);
40
- this.elements.backdrop?.addEventListener('click', this.handleBackdropClick);
41
-
42
- this.initialized = true;
43
- }
44
-
45
- // ─── Panel construction ────────────────────────────────────────────────
46
-
47
- private static buildPanels(): void {
48
- const menu = this.elements.menu;
49
- if (!menu) return;
50
-
51
- const rootUl = menu.querySelector(':scope > ul');
52
- if (!rootUl) return;
53
-
54
- // Wrap root ul in a panel
55
- const rootPanel = document.createElement('div');
56
- rootPanel.classList.add('push-menu-panel', 'is-active');
57
- rootPanel.dataset.level = '0';
58
- rootPanel.appendChild(rootUl);
59
- menu.appendChild(rootPanel);
60
-
61
- // Recursively extract nested uls into sibling panels
62
- this.extractSubPanels(rootPanel, 1);
63
-
64
- this.panelStack = [rootPanel];
65
- }
66
-
67
- private static extractSubPanels(panel: HTMLElement, level: number): void {
68
- // Collect all uls currently in this panel before any mutations
69
- const uls = Array.from(panel.querySelectorAll('ul')) as HTMLUListElement[];
70
-
71
- for (const ul of uls) {
72
- const listItems = Array.from(ul.children) as HTMLElement[];
73
-
74
- for (const li of listItems) {
75
- const childUl = li.querySelector(':scope > ul') as HTMLElement | null;
76
- if (!childUl) continue;
77
-
78
- // Determine label from the immediate anchor child
79
- const parentAnchor = li.querySelector(':scope > a') as HTMLElement | null;
80
- const title = parentAnchor?.textContent?.trim() ?? '';
81
-
82
- // ── Build sub-panel ──────────────────────────────────────
83
- const subPanel = document.createElement('div');
84
- subPanel.classList.add('push-menu-panel');
85
- subPanel.dataset.level = String(level);
86
-
87
- // Header: back button + breadcrumb title
88
- const header = document.createElement('div');
89
- header.classList.add('push-menu-panel-header');
90
-
91
- const backBtn = document.createElement('button');
92
- backBtn.classList.add('push-menu-back');
93
- backBtn.setAttribute('aria-label', 'Back');
94
- backBtn.innerHTML = `<span class="icon icon-navigate_before" aria-hidden="true"></span>`;
95
- header.addEventListener('click', () => PushMenu.goBack());
96
-
97
- const titleEl = document.createElement('span');
98
- titleEl.classList.add('push-menu-panel-title');
99
- titleEl.textContent = title;
100
-
101
- header.appendChild(backBtn);
102
- header.appendChild(titleEl);
103
- subPanel.appendChild(header);
104
-
105
- // Move the child ul into the sub-panel
106
- subPanel.appendChild(childUl);
107
-
108
- // Append sub-panel as sibling inside the nav
109
- this.elements.menu?.appendChild(subPanel);
110
-
111
- // ── Replace anchor with a trigger span in the parent li ──
112
- const trigger = document.createElement('span');
113
- trigger.classList.add('push-menu-item');
114
- trigger.textContent = title;
115
-
116
- // Chevron icon
117
- const chevron = document.createElement('span');
118
- chevron.classList.add('push-menu-chevron');
119
- chevron.setAttribute('aria-hidden', 'true');
120
- chevron.innerHTML = `<span class="icon icon-navigate_next" aria-hidden="true"></span>`;
121
- trigger.appendChild(chevron);
122
-
123
- if (parentAnchor) {
124
- parentAnchor.replaceWith(trigger);
125
- } else {
126
- li.prepend(trigger);
127
- }
128
-
129
- trigger.addEventListener('click', () => PushMenu.openPanel(subPanel));
130
-
131
- // Recurse into the newly created sub-panel
132
- this.extractSubPanels(subPanel, level + 1);
133
- }
134
- }
135
- }
136
-
137
- // ─── Panel navigation ──────────────────────────────────────────────────
138
-
139
- public static openPanel(panel: HTMLElement): void {
140
- const currentPanel = this.panelStack[this.panelStack.length - 1];
141
-
142
- currentPanel.classList.remove('is-active');
143
- currentPanel.classList.add('is-prev');
144
-
145
- panel.classList.add('is-active');
146
-
147
- this.panelStack.push(panel);
148
- }
149
-
150
- public static goBack(): void {
151
- if (this.panelStack.length <= 1) return;
152
-
153
- const currentPanel = this.panelStack.pop()!;
154
- const prevPanel = this.panelStack[this.panelStack.length - 1];
155
-
156
- currentPanel.classList.remove('is-active');
157
- prevPanel.classList.remove('is-prev');
158
- prevPanel.classList.add('is-active');
159
- }
160
-
161
- private static resetPanels(): void {
162
- const menu = this.elements.menu;
163
- if (!menu) return;
164
-
165
- // Wait for the close animation before snapping panels back
166
- setTimeout(() => {
167
- const panels = Array.from(menu.querySelectorAll('.push-menu-panel')) as HTMLElement[];
168
- panels.forEach((panel, index) => {
169
- panel.classList.remove('is-active', 'is-prev');
170
- if (index === 0) panel.classList.add('is-active');
171
- });
172
-
173
- if (panels[0]) {
174
- this.panelStack = [panels[0]];
175
- }
176
- }, 300);
177
- }
178
-
179
- // ─── Open / close ──────────────────────────────────────────────────────
180
-
181
- private static handleNavigationChange(): void {
182
- const isPushed = this.elements.content?.classList.contains('pushed') ?? false;
183
-
184
- if (!isPushed) {
185
- this.elements.content?.addEventListener('click', this.clickNav);
186
- } else {
187
- this.elements.content?.removeEventListener('click', this.clickNav);
188
- this.resetPanels();
189
- }
190
-
191
- this.pushToggle();
192
- }
193
-
194
- public static pushToggle(): void {
195
- if (!this.elements.content || !this.elements.menu) {
196
- throw new Error('PushMenu: Required elements not found (.push-content, .push-menu)');
197
- }
198
-
199
- const isPushed = this.elements.content.classList.contains('pushed');
200
-
201
- this.toggleClass(this.elements.content, 'pushed', !isPushed);
202
- this.toggleClass(this.elements.menu, 'pushed', !isPushed);
203
- this.toggleClass(this.elements.header, 'pushed', !isPushed);
204
- this.toggleClass(this.elements.backdrop, 'pushed', !isPushed);
205
-
206
- if (this.elements.controlIcon) {
207
- if (isPushed) {
208
- this.elements.controlIcon.classList.remove('icon-menu_open');
209
- this.elements.controlIcon.classList.add('icon-menu');
210
- } else {
211
- this.elements.controlIcon.classList.add('icon-menu_open');
212
- this.elements.controlIcon.classList.remove('icon-menu');
213
- }
214
- }
215
- }
216
-
217
- private static toggleClass(element: HTMLElement | null, className: string, add: boolean): void {
218
- if (!element) return;
219
- if (add) {
220
- element.classList.add(className);
221
- } else {
222
- element.classList.remove(className);
223
- }
224
- }
225
-
226
- private static clickNav = (): void => {
227
- const navigation = PushMenu.elements.navigation as HTMLElement;
228
- navigation?.click();
229
- };
230
-
231
- private static handleBackdropClick = (): void => {
232
- if (PushMenu.isOpen()) {
233
- const navigation = PushMenu.elements.navigation as HTMLElement;
234
- navigation?.click();
235
- }
236
- };
237
-
238
- public static open(): void {
239
- if (!this.elements.content?.classList.contains('pushed')) {
240
- this.pushToggle();
241
- }
242
- }
243
-
244
- public static close(): void {
245
- if (this.elements.content?.classList.contains('pushed')) {
246
- this.pushToggle();
247
- }
248
- }
249
-
250
- public static isOpen(): boolean {
251
- return this.elements.content?.classList.contains('pushed') ?? false;
252
- }
253
-
254
- public static destroy(): void {
255
- if (!this.initialized) return;
256
-
257
- this.elements.navigation?.removeEventListener('change', this.boundHandleNavigationChange);
258
- this.elements.content?.removeEventListener('click', this.clickNav);
259
- this.elements.backdrop?.removeEventListener('click', this.handleBackdropClick);
260
-
261
- this.close();
262
-
263
- this.elements = {
264
- navigation: null,
265
- content: null,
266
- menu: null,
267
- header: null,
268
- controlIcon: null,
269
- backdrop: null
270
- };
271
-
272
- this.panelStack = [];
273
- this.initialized = false;
274
- }
275
-
276
- public static refresh(): void {
277
- this.elements.navigation = document.querySelector('.navigation');
278
- this.elements.content = document.querySelector('.push-content');
279
- this.elements.menu = document.querySelector('.push-menu');
280
- this.elements.header = document.querySelector('.main-header');
281
- this.elements.controlIcon = document.querySelector('.navigation-controls .icon');
282
- this.elements.backdrop = document.querySelector('.push-menu-backdrop');
283
- }
284
- }
285
-
286
- export { PushMenu, type PushMenuElements };
@@ -1,33 +0,0 @@
1
- class RangeSlider {
2
- private readonly input: HTMLInputElement;
3
-
4
- constructor(input: HTMLInputElement) {
5
- this.input = input;
6
- this.update();
7
- this.input.addEventListener('input', this.handleInput);
8
- }
9
-
10
- public static initAll(selector: string = '.range-slider input[type="range"]'): void {
11
- document.querySelectorAll<HTMLInputElement>(selector).forEach(input => {
12
- new RangeSlider(input);
13
- });
14
- }
15
-
16
- private update(): void {
17
- const min = +this.input.min || 0;
18
- const max = +this.input.max || 100;
19
- const pct = ((+this.input.value - min) / (max - min)) * 100;
20
- this.input.style.setProperty('--range-fill', `${pct}%`);
21
- }
22
-
23
- private handleInput = (): void => {
24
- this.update();
25
- };
26
-
27
- public destroy(): void {
28
- this.input.removeEventListener('input', this.handleInput);
29
- this.input.style.removeProperty('--range-fill');
30
- }
31
- }
32
-
33
- export { RangeSlider };