@dodlhuat/basix 1.3.4 → 1.4.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.
- package/README.md +160 -18
- package/css/checkbox.scss +15 -2
- package/css/color-picker.scss +278 -0
- package/css/datepicker.scss +5 -3
- package/css/push-menu.scss +7 -0
- package/css/stepper.scss +1 -1
- package/css/style.css +291 -6
- package/css/style.css.map +1 -1
- package/css/style.min.css +1 -1
- package/css/style.min.css.map +1 -1
- package/css/style.scss +1 -0
- package/css/table.scss +33 -0
- package/js/bottom-sheet.d.ts +0 -2
- package/js/bottom-sheet.js +0 -2
- package/js/calendar.d.ts +9 -17
- package/js/calendar.js +4 -7
- package/js/carousel.d.ts +0 -2
- package/js/carousel.js +1 -3
- package/js/chart.d.ts +6 -14
- package/js/chart.js +3 -5
- package/js/code-viewer.d.ts +2 -1
- package/js/code-viewer.js +8 -2
- package/js/color-picker.d.ts +50 -0
- package/js/color-picker.js +291 -0
- package/js/context-menu.d.ts +0 -3
- package/js/context-menu.js +0 -3
- package/js/datepicker.d.ts +0 -4
- package/js/datepicker.js +0 -1
- package/js/docs-nav.js +1 -0
- package/js/dropdown.d.ts +1 -4
- package/js/dropdown.js +0 -1
- package/js/editor.d.ts +4 -3
- package/js/editor.js +50 -35
- package/js/file-uploader.d.ts +0 -4
- package/js/file-uploader.js +0 -1
- package/js/flyout-menu.d.ts +0 -2
- package/js/flyout-menu.js +0 -1
- package/js/gallery.d.ts +1 -4
- package/js/gallery.js +18 -23
- package/js/group-picker.d.ts +0 -5
- package/js/group-picker.js +6 -8
- package/js/lightbox.d.ts +1 -4
- package/js/lightbox.js +6 -10
- package/js/modal.d.ts +0 -2
- package/js/modal.js +0 -1
- package/js/popover.d.ts +0 -3
- package/js/popover.js +0 -2
- package/js/position.d.ts +0 -13
- package/js/position.js +0 -12
- package/js/push-menu.d.ts +0 -3
- package/js/push-menu.js +6 -11
- package/js/range-slider.d.ts +0 -1
- package/js/range-slider.js +2 -3
- package/js/scroll.d.ts +0 -2
- package/js/scroll.js +5 -6
- package/js/scrollbar.d.ts +2 -10
- package/js/scrollbar.js +25 -42
- package/js/select.d.ts +2 -3
- package/js/select.js +6 -9
- package/js/sidebar-nav.d.ts +4 -15
- package/js/sidebar-nav.js +21 -35
- package/js/stepper.d.ts +5 -2
- package/js/stepper.js +42 -6
- package/js/table.d.ts +1 -56
- package/js/table.js +4 -56
- package/js/tabs.d.ts +1 -32
- package/js/tabs.js +7 -39
- package/js/theme.d.ts +0 -44
- package/js/theme.js +0 -44
- package/js/timepicker.d.ts +1 -4
- package/js/timepicker.js +1 -2
- package/js/toast.d.ts +0 -2
- package/js/toast.js +0 -1
- package/js/tooltip.d.ts +0 -4
- package/js/tooltip.js +0 -1
- package/js/tree.d.ts +0 -3
- package/js/tree.js +9 -11
- package/js/utils.d.ts +0 -10
- package/js/utils.js +2 -42
- package/js/virtual-dropdown.d.ts +3 -5
- package/js/virtual-dropdown.js +17 -66
- package/package.json +1 -1
package/js/scrollbar.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Custom scrollbar overlay that replaces the native scrollbar with a draggable thumb. */
|
|
2
1
|
class Scrollbar {
|
|
3
2
|
static instances = new WeakMap();
|
|
4
3
|
static activeInstance = null;
|
|
@@ -16,13 +15,7 @@ class Scrollbar {
|
|
|
16
15
|
activePointerId = null;
|
|
17
16
|
startPointerY = 0;
|
|
18
17
|
startThumbTop = 0;
|
|
19
|
-
|
|
20
|
-
boundPointerUp;
|
|
21
|
-
boundThumbPointerDown;
|
|
22
|
-
boundTrackClick;
|
|
23
|
-
boundViewportScroll;
|
|
24
|
-
boundUpdateThumb;
|
|
25
|
-
boundContainerWheel;
|
|
18
|
+
abortController = new AbortController();
|
|
26
19
|
constructor(container) {
|
|
27
20
|
this.container = container;
|
|
28
21
|
const elements = this.getRequiredElements(container);
|
|
@@ -31,21 +24,14 @@ class Scrollbar {
|
|
|
31
24
|
this.track = elements.track;
|
|
32
25
|
this.thumb = elements.thumb;
|
|
33
26
|
this.MIN_THUMB_HEIGHT = this.getMinThumbHeight();
|
|
34
|
-
this.
|
|
35
|
-
this.boundPointerUp = this.handlePointerUp.bind(this);
|
|
36
|
-
this.boundThumbPointerDown = this.handleThumbPointerDown.bind(this);
|
|
37
|
-
this.boundTrackClick = this.handleTrackClick.bind(this);
|
|
38
|
-
this.boundViewportScroll = this.updateThumb.bind(this);
|
|
39
|
-
this.boundUpdateThumb = this.updateThumb.bind(this);
|
|
40
|
-
this.boundContainerWheel = this.handleContainerWheel.bind(this);
|
|
41
|
-
this.ro = new ResizeObserver(this.boundUpdateThumb);
|
|
27
|
+
this.ro = new ResizeObserver(this.updateThumb);
|
|
42
28
|
this.attachEventListeners();
|
|
43
29
|
Scrollbar.instances.set(container, this);
|
|
44
30
|
Scrollbar.instanceCount++;
|
|
45
31
|
if (!Scrollbar.globalListenersInstalled) {
|
|
46
32
|
Scrollbar.installGlobalListeners();
|
|
47
33
|
}
|
|
48
|
-
requestAnimationFrame(this.
|
|
34
|
+
requestAnimationFrame(this.updateThumb);
|
|
49
35
|
}
|
|
50
36
|
getRequiredElements(container) {
|
|
51
37
|
const viewport = container.querySelector('.viewport');
|
|
@@ -70,26 +56,27 @@ class Scrollbar {
|
|
|
70
56
|
const ac = new AbortController();
|
|
71
57
|
Scrollbar.globalListenerAbortController = ac;
|
|
72
58
|
document.addEventListener('pointermove', (e) => {
|
|
73
|
-
Scrollbar.activeInstance?.
|
|
59
|
+
Scrollbar.activeInstance?.handlePointerMove(e);
|
|
74
60
|
}, { passive: false, signal: ac.signal });
|
|
75
61
|
document.addEventListener('pointerup', (e) => {
|
|
76
|
-
Scrollbar.activeInstance?.
|
|
62
|
+
Scrollbar.activeInstance?.handlePointerUp(e);
|
|
77
63
|
}, { signal: ac.signal });
|
|
78
64
|
document.addEventListener('pointercancel', (e) => {
|
|
79
|
-
Scrollbar.activeInstance?.
|
|
65
|
+
Scrollbar.activeInstance?.handlePointerUp(e);
|
|
80
66
|
}, { signal: ac.signal });
|
|
81
67
|
Scrollbar.globalListenersInstalled = true;
|
|
82
68
|
}
|
|
83
69
|
attachEventListeners() {
|
|
84
|
-
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
this.
|
|
70
|
+
const sig = { signal: this.abortController.signal };
|
|
71
|
+
this.viewport.addEventListener('scroll', this.updateThumb, { passive: true, signal: sig.signal });
|
|
72
|
+
this.thumb.addEventListener('pointerdown', this.handleThumbPointerDown, sig);
|
|
73
|
+
this.track.addEventListener('click', this.handleTrackClick, sig);
|
|
74
|
+
this.container.addEventListener('wheel', this.handleContainerWheel, { passive: false, signal: sig.signal });
|
|
75
|
+
window.addEventListener('resize', this.updateThumb, sig);
|
|
88
76
|
this.ro.observe(this.viewport);
|
|
89
77
|
this.ro.observe(this.content);
|
|
90
|
-
window.addEventListener('resize', this.boundUpdateThumb);
|
|
91
78
|
}
|
|
92
|
-
updateThumb() {
|
|
79
|
+
updateThumb = () => {
|
|
93
80
|
const viewportHeight = this.viewport.clientHeight;
|
|
94
81
|
const contentHeight = this.content.scrollHeight;
|
|
95
82
|
const trackHeight = this.track.clientHeight;
|
|
@@ -106,8 +93,8 @@ class Scrollbar {
|
|
|
106
93
|
const scrollRatio = this.viewport.scrollTop / (maxScroll || 1);
|
|
107
94
|
const thumbTop = scrollRatio * (maxThumbTop || 0);
|
|
108
95
|
this.thumb.style.top = `${thumbTop}px`;
|
|
109
|
-
}
|
|
110
|
-
handleThumbPointerDown(e) {
|
|
96
|
+
};
|
|
97
|
+
handleThumbPointerDown = (e) => {
|
|
111
98
|
e.preventDefault();
|
|
112
99
|
this.dragging = true;
|
|
113
100
|
this.activePointerId = e.pointerId;
|
|
@@ -123,8 +110,8 @@ class Scrollbar {
|
|
|
123
110
|
const trackRect = this.track.getBoundingClientRect();
|
|
124
111
|
this.startThumbTop = thumbRect.top - trackRect.top;
|
|
125
112
|
document.body.style.userSelect = 'none';
|
|
126
|
-
}
|
|
127
|
-
handlePointerMove(e) {
|
|
113
|
+
};
|
|
114
|
+
handlePointerMove = (e) => {
|
|
128
115
|
if (!this.dragging || this.activePointerId !== e.pointerId) {
|
|
129
116
|
return;
|
|
130
117
|
}
|
|
@@ -140,8 +127,8 @@ class Scrollbar {
|
|
|
140
127
|
const maxScroll = contentHeight - viewportHeight;
|
|
141
128
|
const scrollRatio = newThumbTop / (maxThumbTop || 1);
|
|
142
129
|
this.viewport.scrollTop = scrollRatio * (maxScroll || 0);
|
|
143
|
-
}
|
|
144
|
-
handlePointerUp(e) {
|
|
130
|
+
};
|
|
131
|
+
handlePointerUp = (e) => {
|
|
145
132
|
if (!this.dragging || this.activePointerId !== e.pointerId) {
|
|
146
133
|
return;
|
|
147
134
|
}
|
|
@@ -155,8 +142,8 @@ class Scrollbar {
|
|
|
155
142
|
this.activePointerId = null;
|
|
156
143
|
Scrollbar.activeInstance = null;
|
|
157
144
|
document.body.style.userSelect = '';
|
|
158
|
-
}
|
|
159
|
-
handleTrackClick(e) {
|
|
145
|
+
};
|
|
146
|
+
handleTrackClick = (e) => {
|
|
160
147
|
if (e.target === this.thumb) {
|
|
161
148
|
return;
|
|
162
149
|
}
|
|
@@ -173,8 +160,8 @@ class Scrollbar {
|
|
|
173
160
|
const scrollRatio = clampedThumbTop / (maxThumbTop || 1);
|
|
174
161
|
const scrollTop = scrollRatio * (maxScroll || 0);
|
|
175
162
|
this.viewport.scrollTo({ top: scrollTop, behavior: 'smooth' });
|
|
176
|
-
}
|
|
177
|
-
handleContainerWheel(e) {
|
|
163
|
+
};
|
|
164
|
+
handleContainerWheel = (e) => {
|
|
178
165
|
const { scrollTop, scrollHeight, clientHeight } = this.viewport;
|
|
179
166
|
const scrollable = scrollHeight > clientHeight;
|
|
180
167
|
const atTop = scrollTop === 0 && e.deltaY < 0;
|
|
@@ -183,13 +170,9 @@ class Scrollbar {
|
|
|
183
170
|
e.preventDefault();
|
|
184
171
|
}
|
|
185
172
|
this.viewport.scrollTop += e.deltaY;
|
|
186
|
-
}
|
|
173
|
+
};
|
|
187
174
|
destroy() {
|
|
188
|
-
this.
|
|
189
|
-
this.thumb.removeEventListener('pointerdown', this.boundThumbPointerDown);
|
|
190
|
-
this.track.removeEventListener('click', this.boundTrackClick);
|
|
191
|
-
this.container.removeEventListener('wheel', this.boundContainerWheel);
|
|
192
|
-
window.removeEventListener('resize', this.boundUpdateThumb);
|
|
175
|
+
this.abortController.abort();
|
|
193
176
|
this.ro.disconnect();
|
|
194
177
|
Scrollbar.instances.delete(this.container);
|
|
195
178
|
if (Scrollbar.activeInstance === this) {
|
package/js/select.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Enhances a native `<select>` element with a custom styled dropdown. */
|
|
2
1
|
declare class Select {
|
|
3
2
|
private readonly element;
|
|
4
3
|
private readonly isMultiselect;
|
|
@@ -6,8 +5,8 @@ declare class Select {
|
|
|
6
5
|
private readonly documentClickHandler;
|
|
7
6
|
constructor(elementOrSelector: string | HTMLSelectElement);
|
|
8
7
|
destroy(): void;
|
|
9
|
-
value(): string | string[]
|
|
10
|
-
static init(elementOrSelector: string | HTMLSelectElement):
|
|
8
|
+
value(): string | string[];
|
|
9
|
+
static init(elementOrSelector: string | HTMLSelectElement): (() => void) | null;
|
|
11
10
|
private static initElement;
|
|
12
11
|
private static closeAllDropdowns;
|
|
13
12
|
private static handleMultiSelect;
|
package/js/select.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Enhances a native `<select>` element with a custom styled dropdown. */
|
|
2
1
|
class Select {
|
|
3
2
|
element;
|
|
4
3
|
isMultiselect;
|
|
@@ -30,9 +29,6 @@ class Select {
|
|
|
30
29
|
this.dropdown?.classList.remove('open');
|
|
31
30
|
}
|
|
32
31
|
value() {
|
|
33
|
-
if (!this.element) {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
32
|
const selectedValues = Array.from(this.element.options)
|
|
37
33
|
.filter(option => option.selected)
|
|
38
34
|
.map(option => option.value);
|
|
@@ -42,18 +38,18 @@ class Select {
|
|
|
42
38
|
const element = typeof elementOrSelector === 'string'
|
|
43
39
|
? document.querySelector(elementOrSelector)
|
|
44
40
|
: elementOrSelector;
|
|
45
|
-
if (!element)
|
|
41
|
+
if (!element)
|
|
46
42
|
return null;
|
|
47
|
-
}
|
|
48
43
|
const result = Select.initElement(element);
|
|
49
44
|
if (!result)
|
|
50
45
|
return null;
|
|
51
|
-
|
|
46
|
+
const handler = (e) => {
|
|
52
47
|
if (!result.dropdown.contains(e.target)) {
|
|
53
48
|
result.dropdown.classList.remove('open');
|
|
54
49
|
}
|
|
55
|
-
}
|
|
56
|
-
|
|
50
|
+
};
|
|
51
|
+
document.addEventListener('click', handler);
|
|
52
|
+
return () => document.removeEventListener('click', handler);
|
|
57
53
|
}
|
|
58
54
|
static initElement(element) {
|
|
59
55
|
if (!Select.transformSelect(element)) {
|
|
@@ -122,6 +118,7 @@ class Select {
|
|
|
122
118
|
selected.textContent = option.textContent?.trim() || '';
|
|
123
119
|
dropdown.classList.remove('open');
|
|
124
120
|
selectElement.value = option.dataset.value || '';
|
|
121
|
+
selectElement.dispatchEvent(new Event('change'));
|
|
125
122
|
}
|
|
126
123
|
static transformSelect(select) {
|
|
127
124
|
const parent = select.closest('.select-group');
|
package/js/sidebar-nav.d.ts
CHANGED
|
@@ -1,29 +1,18 @@
|
|
|
1
|
-
/** Configuration options for a SidebarNav instance. */
|
|
2
1
|
interface SidebarNavOptions {
|
|
3
|
-
/** Selector for the toggle button. Default: '.sidebar-toggle' */
|
|
4
2
|
toggleSelector?: string;
|
|
5
|
-
/** Breakpoint (px) above which the sidebar is always visible. Default: 768 */
|
|
6
3
|
breakpoint?: number;
|
|
7
|
-
/** Minimum horizontal swipe distance (px) to trigger open/close. Default: 60 */
|
|
8
4
|
swipeThreshold?: number;
|
|
9
|
-
/** Width of the left-edge zone (px) that triggers open on swipe-right. Default: 20 */
|
|
10
5
|
swipeEdge?: number;
|
|
11
6
|
}
|
|
12
|
-
/** Collapsible sidebar navigation with backdrop, swipe gestures, and responsive breakpoint support. */
|
|
13
7
|
declare class SidebarNav {
|
|
14
8
|
private nav;
|
|
15
9
|
private backdrop;
|
|
16
10
|
private toggleBtn;
|
|
17
11
|
private closeBtn;
|
|
18
12
|
private opts;
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
private
|
|
22
|
-
private _onBackdrop;
|
|
23
|
-
private _onResize;
|
|
24
|
-
private _onClose;
|
|
25
|
-
private _onTouchStart;
|
|
26
|
-
private _onTouchEnd;
|
|
13
|
+
private touchStartX;
|
|
14
|
+
private touchStartY;
|
|
15
|
+
private abortController;
|
|
27
16
|
constructor(containerOrSelector: string | HTMLElement, options?: SidebarNavOptions);
|
|
28
17
|
open(): void;
|
|
29
18
|
close(): void;
|
|
@@ -31,4 +20,4 @@ declare class SidebarNav {
|
|
|
31
20
|
isOpen(): boolean;
|
|
32
21
|
destroy(): void;
|
|
33
22
|
}
|
|
34
|
-
export { SidebarNav, SidebarNavOptions };
|
|
23
|
+
export { SidebarNav, type SidebarNavOptions };
|
package/js/sidebar-nav.js
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
|
-
/** Collapsible sidebar navigation with backdrop, swipe gestures, and responsive breakpoint support. */
|
|
2
1
|
class SidebarNav {
|
|
3
2
|
nav;
|
|
4
3
|
backdrop;
|
|
5
4
|
toggleBtn;
|
|
6
5
|
closeBtn = null;
|
|
7
6
|
opts;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
_onBackdrop;
|
|
12
|
-
_onResize;
|
|
13
|
-
_onClose;
|
|
14
|
-
_onTouchStart;
|
|
15
|
-
_onTouchEnd;
|
|
7
|
+
touchStartX = 0;
|
|
8
|
+
touchStartY = 0;
|
|
9
|
+
abortController = new AbortController();
|
|
16
10
|
constructor(containerOrSelector, options = {}) {
|
|
17
11
|
const container = typeof containerOrSelector === 'string'
|
|
18
12
|
? document.querySelector(containerOrSelector)
|
|
@@ -26,39 +20,36 @@ class SidebarNav {
|
|
|
26
20
|
this.nav = container?.querySelector('.sidebar-nav') ?? null;
|
|
27
21
|
this.backdrop = container?.querySelector('.sidebar-backdrop') ?? null;
|
|
28
22
|
this.toggleBtn = document.querySelector(this.opts.toggleSelector);
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
23
|
+
const sig = { signal: this.abortController.signal };
|
|
24
|
+
this.toggleBtn?.addEventListener('click', () => this.toggle(), sig);
|
|
25
|
+
this.backdrop?.addEventListener('click', () => this.close(), sig);
|
|
26
|
+
window.addEventListener('resize', () => {
|
|
27
|
+
if (window.innerWidth > this.opts.breakpoint)
|
|
28
|
+
this.close();
|
|
29
|
+
}, sig);
|
|
30
|
+
document.addEventListener('touchstart', (e) => {
|
|
31
|
+
this.touchStartX = e.touches[0].clientX;
|
|
32
|
+
this.touchStartY = e.touches[0].clientY;
|
|
33
|
+
}, { passive: true, signal: this.abortController.signal });
|
|
34
|
+
document.addEventListener('touchend', (e) => {
|
|
39
35
|
if (window.innerWidth > this.opts.breakpoint)
|
|
40
36
|
return;
|
|
41
|
-
const dx = e.changedTouches[0].clientX - this.
|
|
42
|
-
const dy = e.changedTouches[0].clientY - this.
|
|
37
|
+
const dx = e.changedTouches[0].clientX - this.touchStartX;
|
|
38
|
+
const dy = e.changedTouches[0].clientY - this.touchStartY;
|
|
43
39
|
if (Math.abs(dx) < Math.abs(dy))
|
|
44
40
|
return;
|
|
45
|
-
if (!this.isOpen() && this.
|
|
41
|
+
if (!this.isOpen() && this.touchStartX <= this.opts.swipeEdge && dx >= this.opts.swipeThreshold) {
|
|
46
42
|
this.open();
|
|
47
43
|
}
|
|
48
44
|
else if (this.isOpen() && dx <= -this.opts.swipeThreshold) {
|
|
49
45
|
this.close();
|
|
50
46
|
}
|
|
51
|
-
};
|
|
52
|
-
this.toggleBtn?.addEventListener('click', this._onToggle);
|
|
53
|
-
this.backdrop?.addEventListener('click', this._onBackdrop);
|
|
54
|
-
window.addEventListener('resize', this._onResize);
|
|
55
|
-
document.addEventListener('touchstart', this._onTouchStart, { passive: true });
|
|
56
|
-
document.addEventListener('touchend', this._onTouchEnd, { passive: true });
|
|
47
|
+
}, { passive: true, signal: this.abortController.signal });
|
|
57
48
|
this.closeBtn = document.createElement('button');
|
|
58
49
|
this.closeBtn.className = 'sidebar-close';
|
|
59
50
|
this.closeBtn.setAttribute('aria-label', 'Close navigation');
|
|
60
51
|
this.closeBtn.innerHTML = '<div class="icon icon-close"></div>';
|
|
61
|
-
this.closeBtn.addEventListener('click', this.
|
|
52
|
+
this.closeBtn.addEventListener('click', () => this.close(), sig);
|
|
62
53
|
this.nav?.append(this.closeBtn);
|
|
63
54
|
}
|
|
64
55
|
open() {
|
|
@@ -76,13 +67,8 @@ class SidebarNav {
|
|
|
76
67
|
return this.nav?.classList.contains('is-open') ?? false;
|
|
77
68
|
}
|
|
78
69
|
destroy() {
|
|
79
|
-
this.
|
|
80
|
-
this.backdrop?.removeEventListener('click', this._onBackdrop);
|
|
81
|
-
window.removeEventListener('resize', this._onResize);
|
|
82
|
-
this.closeBtn?.removeEventListener('click', this._onClose);
|
|
70
|
+
this.abortController.abort();
|
|
83
71
|
this.closeBtn?.remove();
|
|
84
|
-
document.removeEventListener('touchstart', this._onTouchStart);
|
|
85
|
-
document.removeEventListener('touchend', this._onTouchEnd);
|
|
86
72
|
}
|
|
87
73
|
}
|
|
88
74
|
export { SidebarNav };
|
package/js/stepper.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
/** Configuration options for a Stepper instance. */
|
|
2
1
|
interface StepperOptions {
|
|
3
2
|
defaultStep?: number;
|
|
4
3
|
clickable?: boolean;
|
|
5
4
|
onChange?: (current: number, previous: number) => void;
|
|
5
|
+
iconBasePath?: string;
|
|
6
6
|
}
|
|
7
|
-
/** Multi-step progress indicator with clickable steps and connector state. */
|
|
8
7
|
declare class Stepper {
|
|
9
8
|
private container;
|
|
10
9
|
private steps;
|
|
11
10
|
private connectors;
|
|
12
11
|
private current;
|
|
13
12
|
private readonly onChange?;
|
|
13
|
+
private readonly iconBasePath;
|
|
14
14
|
private abortController;
|
|
15
|
+
private injectedConnectors;
|
|
15
16
|
constructor(elementOrSelector: string | HTMLElement, options?: StepperOptions);
|
|
17
|
+
private injectConnectors;
|
|
18
|
+
private checkSvg;
|
|
16
19
|
private render;
|
|
17
20
|
next(): void;
|
|
18
21
|
prev(): void;
|
package/js/stepper.js
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
|
-
/** Multi-step progress indicator with clickable steps and connector state. */
|
|
2
1
|
class Stepper {
|
|
3
2
|
container;
|
|
4
3
|
steps;
|
|
5
4
|
connectors;
|
|
6
5
|
current;
|
|
7
6
|
onChange;
|
|
7
|
+
iconBasePath;
|
|
8
8
|
abortController = new AbortController();
|
|
9
|
+
injectedConnectors = false;
|
|
9
10
|
constructor(elementOrSelector, options = {}) {
|
|
10
11
|
const element = typeof elementOrSelector === 'string'
|
|
11
12
|
? document.querySelector(elementOrSelector)
|
|
12
13
|
: elementOrSelector;
|
|
13
14
|
if (!element)
|
|
14
|
-
throw new Error(
|
|
15
|
+
throw new Error('Stepper: element not found');
|
|
15
16
|
this.container = element;
|
|
16
17
|
this.steps = Array.from(this.container.querySelectorAll('.stepper-step'));
|
|
17
|
-
this.connectors = Array.from(this.container.querySelectorAll('.stepper-connector'));
|
|
18
18
|
this.onChange = options.onChange;
|
|
19
|
+
this.iconBasePath = options.iconBasePath ?? 'svg-icons/';
|
|
19
20
|
this.current = options.defaultStep ?? 0;
|
|
20
21
|
if (this.steps.length === 0) {
|
|
21
22
|
console.warn('Stepper: no .stepper-step elements found');
|
|
23
|
+
this.connectors = [];
|
|
22
24
|
return;
|
|
23
25
|
}
|
|
26
|
+
this.connectors = this.injectConnectors();
|
|
24
27
|
if (options.clickable) {
|
|
25
28
|
this.container.classList.add('stepper-clickable');
|
|
26
29
|
this.steps.forEach((step, i) => {
|
|
@@ -29,13 +32,41 @@ class Stepper {
|
|
|
29
32
|
}
|
|
30
33
|
this.render();
|
|
31
34
|
}
|
|
35
|
+
injectConnectors() {
|
|
36
|
+
const injected = [];
|
|
37
|
+
for (let i = 0; i < this.steps.length - 1; i++) {
|
|
38
|
+
const connector = document.createElement('div');
|
|
39
|
+
connector.className = 'stepper-connector';
|
|
40
|
+
this.steps[i].insertAdjacentElement('afterend', connector);
|
|
41
|
+
injected.push(connector);
|
|
42
|
+
}
|
|
43
|
+
this.injectedConnectors = true;
|
|
44
|
+
return injected;
|
|
45
|
+
}
|
|
46
|
+
checkSvg() {
|
|
47
|
+
return `<svg class="icon-svg" aria-hidden="true"><use href="${this.iconBasePath}icons.svg#check"/></svg>`;
|
|
48
|
+
}
|
|
32
49
|
render() {
|
|
33
50
|
this.steps.forEach((step, i) => {
|
|
34
|
-
step.classList.remove('active', 'completed');
|
|
35
|
-
|
|
51
|
+
step.classList.remove('active', 'completed', 'error');
|
|
52
|
+
const indicator = step.querySelector('.stepper-indicator');
|
|
53
|
+
if (i < this.current) {
|
|
36
54
|
step.classList.add('completed');
|
|
37
|
-
|
|
55
|
+
step.removeAttribute('aria-current');
|
|
56
|
+
if (indicator)
|
|
57
|
+
indicator.innerHTML = this.checkSvg();
|
|
58
|
+
}
|
|
59
|
+
else if (i === this.current) {
|
|
38
60
|
step.classList.add('active');
|
|
61
|
+
step.setAttribute('aria-current', 'step');
|
|
62
|
+
if (indicator)
|
|
63
|
+
indicator.textContent = String(i + 1);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
step.removeAttribute('aria-current');
|
|
67
|
+
if (indicator)
|
|
68
|
+
indicator.textContent = String(i + 1);
|
|
69
|
+
}
|
|
39
70
|
});
|
|
40
71
|
this.connectors.forEach((connector, i) => {
|
|
41
72
|
connector.classList.toggle('completed', i < this.current);
|
|
@@ -85,6 +116,11 @@ class Stepper {
|
|
|
85
116
|
}
|
|
86
117
|
destroy() {
|
|
87
118
|
this.abortController.abort();
|
|
119
|
+
if (this.injectedConnectors) {
|
|
120
|
+
this.connectors.forEach(c => c.remove());
|
|
121
|
+
this.connectors = [];
|
|
122
|
+
}
|
|
123
|
+
this.steps.forEach(step => step.removeAttribute('aria-current'));
|
|
88
124
|
}
|
|
89
125
|
}
|
|
90
126
|
export { Stepper };
|
package/js/table.d.ts
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
/** Descriptor for a single table column. */
|
|
2
1
|
interface TableColumn {
|
|
3
2
|
key: string;
|
|
4
3
|
label: string;
|
|
5
4
|
sortable?: boolean;
|
|
6
5
|
}
|
|
7
|
-
/** A single data row, keyed by column key. */
|
|
8
6
|
interface TableRow {
|
|
9
7
|
[key: string]: string | number | boolean;
|
|
10
8
|
}
|
|
11
|
-
/** Configuration options for a Table instance. */
|
|
12
9
|
interface TableOptions {
|
|
13
10
|
data?: TableRow[];
|
|
14
11
|
columns?: TableColumn[];
|
|
15
12
|
pageSize?: number;
|
|
16
13
|
}
|
|
17
|
-
/** Dynamic data table with sorting, filtering, and pagination. */
|
|
18
14
|
declare class Table {
|
|
19
15
|
private container;
|
|
20
16
|
private data;
|
|
@@ -29,74 +25,23 @@ declare class Table {
|
|
|
29
25
|
private paginationContainer;
|
|
30
26
|
private abortController;
|
|
31
27
|
constructor(elementOrSelector: string | HTMLElement, options?: TableOptions);
|
|
32
|
-
/**
|
|
33
|
-
* Parses an existing HTML table in the DOM to extract data and columns
|
|
34
|
-
*/
|
|
35
28
|
private parseTableFromDOM;
|
|
36
|
-
/**
|
|
37
|
-
* Initializes the table by rendering controls, structure, and content
|
|
38
|
-
*/
|
|
39
29
|
private init;
|
|
40
|
-
/**
|
|
41
|
-
* Renders the search and page size controls
|
|
42
|
-
*/
|
|
43
30
|
private renderControls;
|
|
44
|
-
/**
|
|
45
|
-
* Creates the table structure (table, thead, tbody, pagination container)
|
|
46
|
-
*/
|
|
47
31
|
private renderTableStructure;
|
|
48
|
-
/**
|
|
49
|
-
* Returns filtered and sorted data based on current state
|
|
50
|
-
*/
|
|
51
32
|
private getFilteredAndSortedData;
|
|
52
|
-
/**
|
|
53
|
-
* Renders the table body, pagination, and header sort indicators
|
|
54
|
-
*/
|
|
55
33
|
private render;
|
|
56
|
-
/**
|
|
57
|
-
* Renders the table body rows
|
|
58
|
-
*/
|
|
59
34
|
private renderBody;
|
|
60
|
-
/**
|
|
61
|
-
* Updates the sort direction indicators in table headers
|
|
62
|
-
*/
|
|
63
35
|
private updateHeaderSortIcons;
|
|
64
|
-
/**
|
|
65
|
-
* Renders pagination controls and info
|
|
66
|
-
*/
|
|
67
36
|
private renderPagination;
|
|
68
|
-
/**
|
|
69
|
-
* Handles search input changes
|
|
70
|
-
*/
|
|
71
37
|
private handleSearch;
|
|
72
|
-
/**
|
|
73
|
-
* Handles column header clicks for sorting
|
|
74
|
-
*/
|
|
75
38
|
private handleSort;
|
|
76
|
-
/**
|
|
77
|
-
* Handles page size changes
|
|
78
|
-
*/
|
|
79
39
|
private handlePageSizeChange;
|
|
80
|
-
/**
|
|
81
|
-
* Sets the current page and re-renders
|
|
82
|
-
*/
|
|
83
40
|
private setPage;
|
|
84
|
-
/**
|
|
85
|
-
* Assigns a unique ID to an element, incrementing if necessary
|
|
86
|
-
*/
|
|
87
41
|
private assignUniqueId;
|
|
88
|
-
/**
|
|
89
|
-
* Public API: Updates the table data and re-renders
|
|
90
|
-
*/
|
|
91
42
|
setData(data: TableRow[]): void;
|
|
92
|
-
/**
|
|
93
|
-
* Public API: Updates the columns and re-renders
|
|
94
|
-
*/
|
|
95
43
|
setColumns(columns: TableColumn[]): void;
|
|
96
|
-
/**
|
|
97
|
-
* Public API: Gets the current filtered and sorted data
|
|
98
|
-
*/
|
|
99
44
|
getData(): TableRow[];
|
|
100
45
|
destroy(): void;
|
|
101
46
|
}
|
|
102
|
-
export { Table, TableRow, TableColumn, TableOptions };
|
|
47
|
+
export { Table, type TableRow, type TableColumn, type TableOptions };
|