@dodlhuat/basix 1.3.5 → 1.4.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 +92 -7
- 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 +0 -4
- package/js/editor.js +3 -5
- 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 +6 -2
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
class ColorPicker {
|
|
2
|
+
container;
|
|
3
|
+
onChange;
|
|
4
|
+
canvas;
|
|
5
|
+
ctx;
|
|
6
|
+
cursor;
|
|
7
|
+
hueSlider;
|
|
8
|
+
hexInput;
|
|
9
|
+
rInput;
|
|
10
|
+
gInput;
|
|
11
|
+
bInput;
|
|
12
|
+
preview;
|
|
13
|
+
hue = 0;
|
|
14
|
+
saturation = 100;
|
|
15
|
+
brightness = 100;
|
|
16
|
+
isDragging = false;
|
|
17
|
+
abortController = new AbortController();
|
|
18
|
+
ro;
|
|
19
|
+
constructor(elementOrSelector, options = {}) {
|
|
20
|
+
const el = typeof elementOrSelector === 'string'
|
|
21
|
+
? document.querySelector(elementOrSelector)
|
|
22
|
+
: elementOrSelector;
|
|
23
|
+
if (!el)
|
|
24
|
+
throw new Error(`ColorPicker: element not found for "${elementOrSelector}"`);
|
|
25
|
+
this.container = el;
|
|
26
|
+
this.onChange = options.onChange;
|
|
27
|
+
this.build();
|
|
28
|
+
this.bindEvents();
|
|
29
|
+
this.ro = new ResizeObserver(() => this.resizeCanvas());
|
|
30
|
+
this.ro.observe(this.canvas.parentElement);
|
|
31
|
+
this.resizeCanvas();
|
|
32
|
+
if (options.value) {
|
|
33
|
+
this.setValue(options.value);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.updateFromHSB();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
build() {
|
|
40
|
+
this.container.classList.add('color-picker');
|
|
41
|
+
this.container.innerHTML = `
|
|
42
|
+
<div class="color-picker__field">
|
|
43
|
+
<canvas class="color-picker__canvas"></canvas>
|
|
44
|
+
<div class="color-picker__cursor" aria-hidden="true"></div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="color-picker__controls">
|
|
47
|
+
<div class="color-picker__hue-track">
|
|
48
|
+
<input type="range" class="color-picker__hue-slider" min="0" max="360" value="0" aria-label="Hue">
|
|
49
|
+
</div>
|
|
50
|
+
<div class="color-picker__preview" aria-hidden="true"></div>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="color-picker__inputs">
|
|
53
|
+
<div class="color-picker__input-group color-picker__input-group--hex">
|
|
54
|
+
<label>HEX</label>
|
|
55
|
+
<input type="text" class="color-picker__hex" value="#FF0000" spellcheck="false" autocomplete="off" maxlength="7">
|
|
56
|
+
</div>
|
|
57
|
+
<div class="color-picker__input-group">
|
|
58
|
+
<label>R</label>
|
|
59
|
+
<input type="number" class="color-picker__r" min="0" max="255" value="255">
|
|
60
|
+
</div>
|
|
61
|
+
<div class="color-picker__input-group">
|
|
62
|
+
<label>G</label>
|
|
63
|
+
<input type="number" class="color-picker__g" min="0" max="255" value="0">
|
|
64
|
+
</div>
|
|
65
|
+
<div class="color-picker__input-group">
|
|
66
|
+
<label>B</label>
|
|
67
|
+
<input type="number" class="color-picker__b" min="0" max="255" value="0">
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
`;
|
|
71
|
+
const canvas = this.container.querySelector('.color-picker__canvas');
|
|
72
|
+
const ctx = canvas?.getContext('2d');
|
|
73
|
+
if (!canvas || !ctx)
|
|
74
|
+
throw new Error('ColorPicker: canvas context unavailable');
|
|
75
|
+
this.canvas = canvas;
|
|
76
|
+
this.ctx = ctx;
|
|
77
|
+
this.cursor = this.container.querySelector('.color-picker__cursor');
|
|
78
|
+
this.hueSlider = this.container.querySelector('.color-picker__hue-slider');
|
|
79
|
+
this.hexInput = this.container.querySelector('.color-picker__hex');
|
|
80
|
+
this.rInput = this.container.querySelector('.color-picker__r');
|
|
81
|
+
this.gInput = this.container.querySelector('.color-picker__g');
|
|
82
|
+
this.bInput = this.container.querySelector('.color-picker__b');
|
|
83
|
+
this.preview = this.container.querySelector('.color-picker__preview');
|
|
84
|
+
}
|
|
85
|
+
bindEvents() {
|
|
86
|
+
const sig = { signal: this.abortController.signal };
|
|
87
|
+
this.canvas.addEventListener('pointerdown', (e) => {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
this.canvas.setPointerCapture(e.pointerId);
|
|
90
|
+
this.isDragging = true;
|
|
91
|
+
this.handleFieldInteraction(e);
|
|
92
|
+
}, sig);
|
|
93
|
+
this.canvas.addEventListener('pointermove', (e) => {
|
|
94
|
+
if (!this.isDragging)
|
|
95
|
+
return;
|
|
96
|
+
this.handleFieldInteraction(e);
|
|
97
|
+
}, sig);
|
|
98
|
+
this.canvas.addEventListener('pointerup', () => {
|
|
99
|
+
this.isDragging = false;
|
|
100
|
+
}, sig);
|
|
101
|
+
this.canvas.addEventListener('pointercancel', () => {
|
|
102
|
+
this.isDragging = false;
|
|
103
|
+
}, sig);
|
|
104
|
+
this.hueSlider.addEventListener('input', () => {
|
|
105
|
+
this.hue = +this.hueSlider.value;
|
|
106
|
+
this.drawField();
|
|
107
|
+
this.updateFromHSB();
|
|
108
|
+
}, sig);
|
|
109
|
+
this.hexInput.addEventListener('change', () => this.handleHexInput(), sig);
|
|
110
|
+
this.rInput.addEventListener('change', () => this.handleRGBInput(), sig);
|
|
111
|
+
this.gInput.addEventListener('change', () => this.handleRGBInput(), sig);
|
|
112
|
+
this.bInput.addEventListener('change', () => this.handleRGBInput(), sig);
|
|
113
|
+
}
|
|
114
|
+
resizeCanvas() {
|
|
115
|
+
const w = this.canvas.offsetWidth;
|
|
116
|
+
const h = this.canvas.offsetHeight;
|
|
117
|
+
if (w === 0 || h === 0)
|
|
118
|
+
return;
|
|
119
|
+
if (this.canvas.width === w && this.canvas.height === h)
|
|
120
|
+
return;
|
|
121
|
+
this.canvas.width = w;
|
|
122
|
+
this.canvas.height = h;
|
|
123
|
+
this.drawField();
|
|
124
|
+
this.updateCursor();
|
|
125
|
+
}
|
|
126
|
+
drawField() {
|
|
127
|
+
const { width, height } = this.canvas;
|
|
128
|
+
if (width === 0 || height === 0)
|
|
129
|
+
return;
|
|
130
|
+
const gradH = this.ctx.createLinearGradient(0, 0, width, 0);
|
|
131
|
+
gradH.addColorStop(0, '#fff');
|
|
132
|
+
gradH.addColorStop(1, `hsl(${this.hue}, 100%, 50%)`);
|
|
133
|
+
this.ctx.fillStyle = gradH;
|
|
134
|
+
this.ctx.fillRect(0, 0, width, height);
|
|
135
|
+
const gradV = this.ctx.createLinearGradient(0, 0, 0, height);
|
|
136
|
+
gradV.addColorStop(0, 'rgba(0,0,0,0)');
|
|
137
|
+
gradV.addColorStop(1, '#000');
|
|
138
|
+
this.ctx.fillStyle = gradV;
|
|
139
|
+
this.ctx.fillRect(0, 0, width, height);
|
|
140
|
+
}
|
|
141
|
+
handleFieldInteraction(e) {
|
|
142
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
143
|
+
const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
|
|
144
|
+
const y = Math.max(0, Math.min(e.clientY - rect.top, rect.height));
|
|
145
|
+
this.saturation = (x / rect.width) * 100;
|
|
146
|
+
this.brightness = 100 - (y / rect.height) * 100;
|
|
147
|
+
this.updateCursor();
|
|
148
|
+
this.updateFromHSB();
|
|
149
|
+
}
|
|
150
|
+
updateCursor() {
|
|
151
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
152
|
+
const x = (this.saturation / 100) * rect.width;
|
|
153
|
+
const y = (1 - this.brightness / 100) * rect.height;
|
|
154
|
+
this.cursor.style.left = `${x}px`;
|
|
155
|
+
this.cursor.style.top = `${y}px`;
|
|
156
|
+
this.cursor.classList.toggle('color-picker__cursor--dark', this.brightness > 50 && this.saturation < 80);
|
|
157
|
+
}
|
|
158
|
+
updateFromHSB() {
|
|
159
|
+
const rgb = this.hsbToRgb(this.hue, this.saturation, this.brightness);
|
|
160
|
+
const hex = this.rgbToHex(rgb);
|
|
161
|
+
this.syncInputs(rgb, hex);
|
|
162
|
+
this.syncPreview(rgb);
|
|
163
|
+
this.onChange?.(hex, rgb);
|
|
164
|
+
}
|
|
165
|
+
syncInputs(rgb, hex) {
|
|
166
|
+
this.hexInput.value = hex;
|
|
167
|
+
this.rInput.value = String(rgb.r);
|
|
168
|
+
this.gInput.value = String(rgb.g);
|
|
169
|
+
this.bInput.value = String(rgb.b);
|
|
170
|
+
}
|
|
171
|
+
syncPreview(rgb) {
|
|
172
|
+
this.preview.style.backgroundColor = `rgb(${rgb.r},${rgb.g},${rgb.b})`;
|
|
173
|
+
}
|
|
174
|
+
handleHexInput() {
|
|
175
|
+
const hex = this.hexInput.value.trim();
|
|
176
|
+
if (/^#[0-9a-f]{6}$/i.test(hex)) {
|
|
177
|
+
this.setFromRGB(this.hexToRgb(hex));
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
this.hexInput.value = this.rgbToHex(this.hsbToRgb(this.hue, this.saturation, this.brightness));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
handleRGBInput() {
|
|
184
|
+
const clamp = (v) => Math.max(0, Math.min(255, isNaN(v) ? 0 : v));
|
|
185
|
+
this.setFromRGB({
|
|
186
|
+
r: clamp(+this.rInput.value),
|
|
187
|
+
g: clamp(+this.gInput.value),
|
|
188
|
+
b: clamp(+this.bInput.value),
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
setFromRGB(rgb) {
|
|
192
|
+
const { h, s, v } = this.rgbToHsb(rgb);
|
|
193
|
+
this.hue = h;
|
|
194
|
+
this.saturation = s;
|
|
195
|
+
this.brightness = v;
|
|
196
|
+
this.hueSlider.value = String(this.hue);
|
|
197
|
+
this.drawField();
|
|
198
|
+
this.updateCursor();
|
|
199
|
+
this.updateFromHSB();
|
|
200
|
+
}
|
|
201
|
+
getValue() {
|
|
202
|
+
return this.rgbToHex(this.hsbToRgb(this.hue, this.saturation, this.brightness));
|
|
203
|
+
}
|
|
204
|
+
setValue(hex) {
|
|
205
|
+
if (!/^#[0-9a-f]{6}$/i.test(hex))
|
|
206
|
+
return;
|
|
207
|
+
this.setFromRGB(this.hexToRgb(hex));
|
|
208
|
+
}
|
|
209
|
+
destroy() {
|
|
210
|
+
this.abortController.abort();
|
|
211
|
+
this.ro.disconnect();
|
|
212
|
+
this.container.classList.remove('color-picker');
|
|
213
|
+
this.container.innerHTML = '';
|
|
214
|
+
}
|
|
215
|
+
hsbToRgb(h, s, v) {
|
|
216
|
+
s /= 100;
|
|
217
|
+
v /= 100;
|
|
218
|
+
const c = v * s;
|
|
219
|
+
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
220
|
+
const m = v - c;
|
|
221
|
+
let r = 0, g = 0, b = 0;
|
|
222
|
+
if (h < 60) {
|
|
223
|
+
r = c;
|
|
224
|
+
g = x;
|
|
225
|
+
b = 0;
|
|
226
|
+
}
|
|
227
|
+
else if (h < 120) {
|
|
228
|
+
r = x;
|
|
229
|
+
g = c;
|
|
230
|
+
b = 0;
|
|
231
|
+
}
|
|
232
|
+
else if (h < 180) {
|
|
233
|
+
r = 0;
|
|
234
|
+
g = c;
|
|
235
|
+
b = x;
|
|
236
|
+
}
|
|
237
|
+
else if (h < 240) {
|
|
238
|
+
r = 0;
|
|
239
|
+
g = x;
|
|
240
|
+
b = c;
|
|
241
|
+
}
|
|
242
|
+
else if (h < 300) {
|
|
243
|
+
r = x;
|
|
244
|
+
g = 0;
|
|
245
|
+
b = c;
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
r = c;
|
|
249
|
+
g = 0;
|
|
250
|
+
b = x;
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
r: Math.round((r + m) * 255),
|
|
254
|
+
g: Math.round((g + m) * 255),
|
|
255
|
+
b: Math.round((b + m) * 255),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
rgbToHsb({ r, g, b }) {
|
|
259
|
+
r /= 255;
|
|
260
|
+
g /= 255;
|
|
261
|
+
b /= 255;
|
|
262
|
+
const max = Math.max(r, g, b);
|
|
263
|
+
const min = Math.min(r, g, b);
|
|
264
|
+
const d = max - min;
|
|
265
|
+
let h = 0;
|
|
266
|
+
if (d !== 0) {
|
|
267
|
+
if (max === r)
|
|
268
|
+
h = ((g - b) / d) % 6;
|
|
269
|
+
else if (max === g)
|
|
270
|
+
h = (b - r) / d + 2;
|
|
271
|
+
else
|
|
272
|
+
h = (r - g) / d + 4;
|
|
273
|
+
}
|
|
274
|
+
h = Math.round(h * 60);
|
|
275
|
+
if (h < 0)
|
|
276
|
+
h += 360;
|
|
277
|
+
return {
|
|
278
|
+
h,
|
|
279
|
+
s: max === 0 ? 0 : Math.round((d / max) * 100),
|
|
280
|
+
v: Math.round(max * 100),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
rgbToHex({ r, g, b }) {
|
|
284
|
+
return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('');
|
|
285
|
+
}
|
|
286
|
+
hexToRgb(hex) {
|
|
287
|
+
const m = hex.match(/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
|
|
288
|
+
return { r: parseInt(m[1], 16), g: parseInt(m[2], 16), b: parseInt(m[3], 16) };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
export { ColorPicker };
|
package/js/context-menu.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Definition for a single context menu item including optional submenu. */
|
|
2
1
|
interface ContextMenuItemDef {
|
|
3
2
|
label: string;
|
|
4
3
|
icon?: string;
|
|
@@ -12,10 +11,8 @@ type ContextMenuInput = ContextMenuItemDef | 'separator' | {
|
|
|
12
11
|
group: string;
|
|
13
12
|
};
|
|
14
13
|
interface ContextMenuOptions {
|
|
15
|
-
/** Path to the SVG sprite file, e.g. `'svg-icons/icons.svg'`. Required to render icons. */
|
|
16
14
|
spritePath?: string;
|
|
17
15
|
}
|
|
18
|
-
/** Right-click context menu with keyboard navigation and nested submenu support. */
|
|
19
16
|
declare class ContextMenu {
|
|
20
17
|
private items;
|
|
21
18
|
private targets;
|
package/js/context-menu.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Right-click context menu with keyboard navigation and nested submenu support. */
|
|
2
1
|
class ContextMenu {
|
|
3
2
|
items;
|
|
4
3
|
targets;
|
|
@@ -148,8 +147,6 @@ class ContextMenu {
|
|
|
148
147
|
const rect = li.getBoundingClientRect();
|
|
149
148
|
return rect.right + submenuEl.offsetWidth > window.innerWidth;
|
|
150
149
|
};
|
|
151
|
-
// Delay timer prevents the submenu closing when mouse travels from
|
|
152
|
-
// item → submenu (mouseleave fires before mouseenter on the submenu)
|
|
153
150
|
let closeTimer = null;
|
|
154
151
|
const openSub = () => {
|
|
155
152
|
if (closeTimer) {
|
package/js/datepicker.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
/** Localised day and month names for the DatePicker. */
|
|
2
1
|
interface DatePickerLocales {
|
|
3
2
|
days: string[];
|
|
4
3
|
months: string[];
|
|
5
4
|
}
|
|
6
|
-
/** Configuration options for the DatePicker. */
|
|
7
5
|
interface DatePickerOptions {
|
|
8
6
|
mode?: 'single' | 'range';
|
|
9
7
|
startDay?: number;
|
|
@@ -12,12 +10,10 @@ interface DatePickerOptions {
|
|
|
12
10
|
format?: (date: Date) => string;
|
|
13
11
|
onSelect?: (date: Date | DateRange) => void;
|
|
14
12
|
}
|
|
15
|
-
/** A date range with optional start and end dates. */
|
|
16
13
|
interface DateRange {
|
|
17
14
|
start: Date | null;
|
|
18
15
|
end: Date | null;
|
|
19
16
|
}
|
|
20
|
-
/** Calendar-based date (or date-range) picker that attaches to an input element. */
|
|
21
17
|
declare class DatePicker {
|
|
22
18
|
private input;
|
|
23
19
|
private options;
|
package/js/datepicker.js
CHANGED
package/js/docs-nav.js
CHANGED
|
@@ -16,6 +16,7 @@ const NAV = [
|
|
|
16
16
|
items: [
|
|
17
17
|
{ label: 'Inputs & Forms', href: 'forms/inputs.html' },
|
|
18
18
|
{ label: 'Date Picker', href: 'forms/datepicker.html' },
|
|
19
|
+
{ label: 'Color Picker', href: 'forms/color-picker.html' },
|
|
19
20
|
{ label: 'Range Slider', href: 'forms/range-slider.html' },
|
|
20
21
|
{ label: 'File Uploader', href: 'forms/file-uploader.html' },
|
|
21
22
|
],
|
package/js/dropdown.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
/** Configuration options for a Dropdown instance. */
|
|
2
1
|
interface DropdownOptions {
|
|
3
2
|
closeOnSelect?: boolean;
|
|
4
3
|
allowMultipleOpen?: boolean;
|
|
5
4
|
}
|
|
6
|
-
/** Event detail payload for the `dropdown-select` custom event. */
|
|
7
5
|
interface DropdownSelectDetail {
|
|
8
6
|
text: string;
|
|
9
7
|
element: HTMLElement;
|
|
10
8
|
}
|
|
11
|
-
/** Hierarchical dropdown menu with optional multi-open and close-on-select behaviour. */
|
|
12
9
|
declare class Dropdown {
|
|
13
10
|
private container;
|
|
14
11
|
private trigger;
|
|
@@ -28,4 +25,4 @@ declare class Dropdown {
|
|
|
28
25
|
private handleSelection;
|
|
29
26
|
destroy(): void;
|
|
30
27
|
}
|
|
31
|
-
export { Dropdown, DropdownSelectDetail };
|
|
28
|
+
export { Dropdown, type DropdownSelectDetail };
|
package/js/dropdown.js
CHANGED
package/js/editor.d.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
interface EditorOptions {
|
|
2
|
-
/** Hides the entire side panel (code/preview) permanently. Safe to use
|
|
3
|
-
* without [data-editor="code"], [data-editor="preview"], or [data-editor="side-panel"] in the DOM. */
|
|
4
2
|
simple?: boolean;
|
|
5
|
-
/** Root container element or CSS selector. Required when using multiple editors on one page. */
|
|
6
3
|
root?: string | HTMLElement;
|
|
7
4
|
}
|
|
8
|
-
/** Rich-text editor built on contenteditable with undo/redo and code/preview panels. */
|
|
9
5
|
declare class Editor {
|
|
10
6
|
private readonly root;
|
|
11
7
|
private readonly editable;
|
package/js/editor.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { sanitizeHtml } from './utils.js';
|
|
2
|
-
/** Rich-text editor built on contenteditable with undo/redo and code/preview panels. */
|
|
3
2
|
class Editor {
|
|
4
3
|
root;
|
|
5
4
|
editable;
|
|
@@ -111,17 +110,16 @@ class Editor {
|
|
|
111
110
|
}, sig);
|
|
112
111
|
if (this.code) {
|
|
113
112
|
const code = this.code;
|
|
114
|
-
|
|
115
|
-
codeActions[0]?.addEventListener('click', () => {
|
|
113
|
+
this.q('[data-editor-action="apply-code"]')?.addEventListener('click', () => {
|
|
116
114
|
this.editable.innerHTML = sanitizeHtml(code.value);
|
|
117
115
|
this.onContentChange();
|
|
118
116
|
}, sig);
|
|
119
|
-
|
|
117
|
+
this.q('[data-editor-action="sanitize-code"]')?.addEventListener('click', () => {
|
|
120
118
|
code.value = sanitizeHtml(code.value);
|
|
121
119
|
this.editable.innerHTML = code.value;
|
|
122
120
|
this.onContentChange();
|
|
123
121
|
}, sig);
|
|
124
|
-
|
|
122
|
+
this.q('[data-editor-action="minify-code"]')?.addEventListener('click', () => {
|
|
125
123
|
code.value = code.value
|
|
126
124
|
.replace(/\n/g, '')
|
|
127
125
|
.replace(/>\s+</g, '><')
|
package/js/file-uploader.d.ts
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
/** Event detail payload for the `upload-completed` custom event. */
|
|
2
1
|
interface UploadCompletedDetail {
|
|
3
2
|
fileCount: number;
|
|
4
3
|
files: File[];
|
|
5
4
|
results: PromiseSettledResult<unknown>[];
|
|
6
5
|
}
|
|
7
|
-
/** Event detail payload for the `file-validation-error` custom event. */
|
|
8
6
|
interface FileValidationErrorDetail {
|
|
9
7
|
file: File;
|
|
10
8
|
reason: 'size' | 'type';
|
|
11
9
|
}
|
|
12
|
-
/** Configuration options for the FileUploader. */
|
|
13
10
|
interface FileUploaderConfig {
|
|
14
11
|
uploadUrl?: string;
|
|
15
12
|
maxFileSize?: number;
|
|
16
13
|
allowedTypes?: string[];
|
|
17
14
|
}
|
|
18
|
-
/** Drag-and-drop file uploader with progress tracking and XHR-based uploads. */
|
|
19
15
|
declare class FileUploader {
|
|
20
16
|
private container;
|
|
21
17
|
private dropZone;
|
package/js/file-uploader.js
CHANGED
package/js/flyout-menu.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/** Configuration options for the FlyoutMenu. */
|
|
2
1
|
interface FlyoutMenuOptions {
|
|
3
2
|
triggerSelector?: string;
|
|
4
3
|
menuSelector?: string;
|
|
@@ -12,7 +11,6 @@ interface FlyoutMenuOptions {
|
|
|
12
11
|
enableHeader?: boolean;
|
|
13
12
|
enableFooter?: boolean;
|
|
14
13
|
}
|
|
15
|
-
/** Off-canvas flyout navigation with nested submenu support. */
|
|
16
14
|
declare class FlyoutMenu {
|
|
17
15
|
private options;
|
|
18
16
|
private menuTrigger;
|
package/js/flyout-menu.js
CHANGED
package/js/gallery.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
/** A single image record for MasonryGallery. */
|
|
2
1
|
interface ImageData {
|
|
3
2
|
src: string;
|
|
4
3
|
title: string;
|
|
5
4
|
desc: string;
|
|
6
5
|
}
|
|
7
|
-
/** Configuration options for MasonryGallery. */
|
|
8
6
|
interface MasonryGalleryOptions {
|
|
9
7
|
fetchFunction: () => Promise<ImageData[]>;
|
|
10
8
|
minColumnWidth?: number;
|
|
@@ -13,7 +11,6 @@ interface MasonryGalleryOptions {
|
|
|
13
11
|
reload?: number;
|
|
14
12
|
enableLightbox?: boolean;
|
|
15
13
|
}
|
|
16
|
-
/** Infinite-scroll masonry gallery that distributes images across dynamically sized columns. */
|
|
17
14
|
declare class MasonryGallery {
|
|
18
15
|
private container;
|
|
19
16
|
private readonly loader;
|
|
@@ -38,4 +35,4 @@ declare class MasonryGallery {
|
|
|
38
35
|
private addToShortestColumn;
|
|
39
36
|
destroy(): void;
|
|
40
37
|
}
|
|
41
|
-
export { MasonryGallery, ImageData };
|
|
38
|
+
export { MasonryGallery, type ImageData };
|
package/js/gallery.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { escapeHtml } from './utils.js';
|
|
2
2
|
import { Lightbox } from './lightbox.js';
|
|
3
|
-
/** Infinite-scroll masonry gallery that distributes images across dynamically sized columns. */
|
|
4
3
|
class MasonryGallery {
|
|
5
4
|
container;
|
|
6
5
|
loader;
|
|
@@ -17,11 +16,11 @@ class MasonryGallery {
|
|
|
17
16
|
throw new Error(`Container with id "${containerId}" not found`);
|
|
18
17
|
}
|
|
19
18
|
this.container = container;
|
|
20
|
-
this.loader = document.querySelector(options.loaderSelector
|
|
19
|
+
this.loader = document.querySelector(options.loaderSelector ?? '.loader');
|
|
21
20
|
this.options = {
|
|
22
21
|
minColumnWidth: options.minColumnWidth ?? 250,
|
|
23
22
|
scrollThreshold: options.scrollThreshold ?? 100,
|
|
24
|
-
reload: 2,
|
|
23
|
+
reload: options.reload ?? 2,
|
|
25
24
|
fetchFunction: options.fetchFunction,
|
|
26
25
|
enableLightbox: options.enableLightbox ?? true,
|
|
27
26
|
};
|
|
@@ -54,11 +53,11 @@ class MasonryGallery {
|
|
|
54
53
|
this.abortController = new AbortController();
|
|
55
54
|
const sig = this.abortController.signal;
|
|
56
55
|
let resizeTimeout;
|
|
57
|
-
window.addEventListener(
|
|
56
|
+
window.addEventListener('resize', () => {
|
|
58
57
|
clearTimeout(resizeTimeout);
|
|
59
58
|
resizeTimeout = setTimeout(() => this.reLayout(), 200);
|
|
60
59
|
}, { signal: sig });
|
|
61
|
-
window.addEventListener(
|
|
60
|
+
window.addEventListener('scroll', this.handleScroll, { passive: true, signal: sig });
|
|
62
61
|
}
|
|
63
62
|
reLayout() {
|
|
64
63
|
const items = this.columns.flatMap(col => Array.from(col.children));
|
|
@@ -84,7 +83,7 @@ class MasonryGallery {
|
|
|
84
83
|
if (!isAutoFill)
|
|
85
84
|
this.reloaded++;
|
|
86
85
|
if (this.options.reload > 0 && this.reloaded > this.options.reload) {
|
|
87
|
-
console.warn(
|
|
86
|
+
console.warn('Maximum reload limit reached.');
|
|
88
87
|
return;
|
|
89
88
|
}
|
|
90
89
|
if (this.isFetching)
|
|
@@ -96,7 +95,7 @@ class MasonryGallery {
|
|
|
96
95
|
this.renderImages(newImages);
|
|
97
96
|
}
|
|
98
97
|
catch (error) {
|
|
99
|
-
|
|
98
|
+
console.error('MasonryGallery: error loading images', error);
|
|
100
99
|
}
|
|
101
100
|
finally {
|
|
102
101
|
this.isFetching = false;
|
|
@@ -111,16 +110,12 @@ class MasonryGallery {
|
|
|
111
110
|
}
|
|
112
111
|
toggleLoader(show) {
|
|
113
112
|
if (this.loader) {
|
|
114
|
-
this.loader.classList.toggle(
|
|
113
|
+
this.loader.classList.toggle('hidden', !show);
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
renderImages(imageDataList) {
|
|
118
117
|
const startIndex = this.allImages.length;
|
|
119
118
|
this.allImages.push(...imageDataList);
|
|
120
|
-
// Sort columns by current height so we start filling from the shortest.
|
|
121
|
-
// Then round-robin across them — this avoids the problem where unloaded
|
|
122
|
-
// images (0 height) cause offsetHeight-based distribution to pile all
|
|
123
|
-
// new items into a single column.
|
|
124
119
|
const sorted = [...this.columns].sort((a, b) => a.offsetHeight - b.offsetHeight);
|
|
125
120
|
imageDataList.forEach((data, i) => {
|
|
126
121
|
const item = this.createCard(data);
|
|
@@ -140,28 +135,28 @@ class MasonryGallery {
|
|
|
140
135
|
const col = sorted[i % sorted.length];
|
|
141
136
|
col.appendChild(item);
|
|
142
137
|
requestAnimationFrame(() => {
|
|
143
|
-
const img = item.querySelector(
|
|
138
|
+
const img = item.querySelector('img');
|
|
144
139
|
if (img) {
|
|
145
|
-
img.addEventListener(
|
|
140
|
+
img.addEventListener('load', () => img.classList.add('loaded'), {
|
|
146
141
|
once: true,
|
|
147
142
|
});
|
|
148
143
|
if (img.complete) {
|
|
149
|
-
img.classList.add(
|
|
144
|
+
img.classList.add('loaded');
|
|
150
145
|
}
|
|
151
146
|
}
|
|
152
147
|
});
|
|
153
148
|
});
|
|
154
149
|
}
|
|
155
150
|
createCard(data) {
|
|
156
|
-
const div = document.createElement(
|
|
157
|
-
div.className =
|
|
151
|
+
const div = document.createElement('div');
|
|
152
|
+
div.className = 'masonry-item';
|
|
158
153
|
div.innerHTML = `
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
154
|
+
<img src="${escapeHtml(data.src)}" alt="${escapeHtml(data.title)}" loading="lazy">
|
|
155
|
+
<div class="masonry-item-info">
|
|
156
|
+
<h3 class="masonry-item-title">${escapeHtml(data.title)}</h3>
|
|
157
|
+
<p class="masonry-item-desc">${escapeHtml(data.desc)}</p>
|
|
158
|
+
</div>
|
|
159
|
+
`;
|
|
165
160
|
return div;
|
|
166
161
|
}
|
|
167
162
|
addToShortestColumn(element) {
|
package/js/group-picker.d.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
/** A single subgroup item within a GroupPicker group. */
|
|
2
1
|
interface SubgroupData {
|
|
3
2
|
id: string;
|
|
4
3
|
label: string;
|
|
5
4
|
}
|
|
6
|
-
/** A group with an optional list of subgroups for GroupPicker. */
|
|
7
5
|
interface GroupData {
|
|
8
6
|
id: string;
|
|
9
7
|
label: string;
|
|
10
8
|
subgroups?: SubgroupData[];
|
|
11
9
|
}
|
|
12
|
-
/** The current selection state returned by GroupPicker. */
|
|
13
10
|
interface GroupPickerSelection {
|
|
14
11
|
parentGroups: string[];
|
|
15
12
|
subgroups: {
|
|
@@ -17,7 +14,6 @@ interface GroupPickerSelection {
|
|
|
17
14
|
subgroupId: string;
|
|
18
15
|
}[];
|
|
19
16
|
}
|
|
20
|
-
/** Configuration options for the GroupPicker component. */
|
|
21
17
|
interface GroupPickerOptions {
|
|
22
18
|
onSelectionChange?: (selection: GroupPickerSelection) => void;
|
|
23
19
|
searchPlaceholder?: string;
|
|
@@ -26,7 +22,6 @@ interface GroupPickerOptions {
|
|
|
26
22
|
emptyLabel?: string;
|
|
27
23
|
selectionPlaceholder?: string;
|
|
28
24
|
}
|
|
29
|
-
/** Searchable picker for selecting groups and their subgroups. */
|
|
30
25
|
declare class GroupPicker {
|
|
31
26
|
private container;
|
|
32
27
|
private data;
|