@dodlhuat/basix 1.4.1 → 1.4.3
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 +38 -1
- package/css/calendar.scss +4 -3
- package/css/code-viewer.scss +1 -1
- package/css/datepicker.scss +83 -70
- package/css/editor.scss +2 -1
- package/css/flyout-menu.scss +2 -1
- package/css/form.scss +11 -32
- package/css/mixins.scss +25 -0
- package/css/range-slider.scss +63 -6
- package/css/stepper.scss +2 -1
- package/css/style.css +124 -71
- package/css/style.css.map +1 -1
- package/css/style.min.css +1 -1
- package/css/style.min.css.map +1 -1
- package/css/timepicker.scss +1 -1
- package/css/virtual-dropdown.scss +6 -16
- package/js/bottom-sheet.d.ts +3 -5
- package/js/bottom-sheet.js +39 -58
- package/js/calendar.d.ts +1 -2
- package/js/calendar.js +77 -84
- package/js/carousel.d.ts +1 -1
- package/js/carousel.js +22 -14
- package/js/chart.d.ts +1 -1
- package/js/chart.js +104 -65
- package/js/code-viewer.d.ts +1 -1
- package/js/code-viewer.js +7 -18
- package/js/color-picker.d.ts +2 -2
- package/js/color-picker.js +20 -22
- package/js/context-menu.d.ts +1 -1
- package/js/context-menu.js +10 -12
- package/js/datepicker.d.ts +14 -3
- package/js/datepicker.js +180 -99
- package/js/dropdown.d.ts +1 -1
- package/js/dropdown.js +4 -5
- package/js/editor.d.ts +1 -1
- package/js/editor.js +30 -34
- package/js/file-uploader.d.ts +2 -9
- package/js/file-uploader.js +57 -86
- package/js/flyout-menu.d.ts +3 -3
- package/js/flyout-menu.js +20 -22
- package/js/gallery.d.ts +1 -2
- package/js/gallery.js +14 -20
- package/js/group-picker.d.ts +1 -1
- package/js/group-picker.js +22 -29
- package/js/lightbox.d.ts +2 -2
- package/js/lightbox.js +28 -27
- package/js/listeners.d.ts +7 -0
- package/js/listeners.js +14 -0
- package/js/modal.d.ts +2 -1
- package/js/modal.js +13 -20
- package/js/popover.d.ts +2 -2
- package/js/popover.js +42 -37
- package/js/position.js +3 -5
- package/js/push-menu.d.ts +2 -0
- package/js/push-menu.js +21 -15
- package/js/range-slider.d.ts +15 -1
- package/js/range-slider.js +67 -7
- package/js/scroll.js +4 -7
- package/js/scrollbar.d.ts +3 -3
- package/js/scrollbar.js +39 -42
- package/js/select.d.ts +1 -1
- package/js/select.js +19 -23
- package/js/sidebar-nav.d.ts +1 -1
- package/js/sidebar-nav.js +13 -9
- package/js/stepper.d.ts +1 -1
- package/js/stepper.js +9 -11
- package/js/table.d.ts +1 -1
- package/js/table.js +24 -23
- package/js/tabs.d.ts +1 -1
- package/js/tabs.js +10 -17
- package/js/theme.d.ts +1 -0
- package/js/theme.js +6 -15
- package/js/timepicker.d.ts +8 -6
- package/js/timepicker.js +36 -36
- package/js/toast.d.ts +2 -1
- package/js/toast.js +8 -4
- package/js/tooltip.d.ts +1 -4
- package/js/tooltip.js +14 -23
- package/js/tree.d.ts +0 -1
- package/js/tree.js +6 -11
- package/js/utils.js +7 -8
- package/js/virtual-dropdown.d.ts +1 -1
- package/js/virtual-dropdown.js +26 -28
- package/package.json +1 -1
- package/css/calendar.css +0 -928
- package/css/guitar-chords.css +0 -251
- package/js/form-builder.js +0 -107
- package/js/guitar-chords.js +0 -268
- package/js/lazy-loader.js +0 -121
- package/js/request.js +0 -51
package/js/datepicker.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { computePosition } from './position.js';
|
|
2
|
+
import { ListenerGroup } from './listeners.js';
|
|
2
3
|
class DatePicker {
|
|
4
|
+
static CLOCK_OUTER_RADIUS_PERCENT = 38;
|
|
5
|
+
static CLOCK_INNER_RADIUS_PERCENT = 23;
|
|
3
6
|
input;
|
|
4
7
|
options;
|
|
5
8
|
currentDate;
|
|
@@ -14,12 +17,12 @@ class DatePicker {
|
|
|
14
17
|
selectedMinutes;
|
|
15
18
|
calendar;
|
|
16
19
|
backdrop;
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
clockMode = 'hours';
|
|
21
|
+
listeners = new ListenerGroup();
|
|
22
|
+
showListeners = null;
|
|
23
|
+
clockListeners = null;
|
|
19
24
|
constructor(elementOrSelector, options = {}) {
|
|
20
|
-
this.input = typeof elementOrSelector === 'string'
|
|
21
|
-
? document.querySelector(elementOrSelector)
|
|
22
|
-
: elementOrSelector;
|
|
25
|
+
this.input = typeof elementOrSelector === 'string' ? document.querySelector(elementOrSelector) : elementOrSelector;
|
|
23
26
|
if (!this.input) {
|
|
24
27
|
throw new Error(`DatePicker: Element not found for selector "${elementOrSelector}"`);
|
|
25
28
|
}
|
|
@@ -30,10 +33,7 @@ class DatePicker {
|
|
|
30
33
|
timePicker,
|
|
31
34
|
locales: {
|
|
32
35
|
days: ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'],
|
|
33
|
-
months: [
|
|
34
|
-
'January', 'February', 'March', 'April', 'May', 'June',
|
|
35
|
-
'July', 'August', 'September', 'October', 'November', 'December'
|
|
36
|
-
]
|
|
36
|
+
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
|
37
37
|
},
|
|
38
38
|
format: timePicker
|
|
39
39
|
? (date) => {
|
|
@@ -43,7 +43,7 @@ class DatePicker {
|
|
|
43
43
|
}
|
|
44
44
|
: (date) => date.toDateString(),
|
|
45
45
|
onSelect: () => { },
|
|
46
|
-
...options
|
|
46
|
+
...options,
|
|
47
47
|
};
|
|
48
48
|
this.currentDate = new Date();
|
|
49
49
|
this.selectedDate = null;
|
|
@@ -69,7 +69,6 @@ class DatePicker {
|
|
|
69
69
|
this.backdrop = document.createElement('div');
|
|
70
70
|
this.backdrop.className = 'datepicker-backdrop';
|
|
71
71
|
document.body.appendChild(this.backdrop);
|
|
72
|
-
this.backdrop.addEventListener('click', () => this.hide(), { signal: this.abortController.signal });
|
|
73
72
|
}
|
|
74
73
|
attachEvents() {
|
|
75
74
|
const toggle = (e) => {
|
|
@@ -82,23 +81,19 @@ class DatePicker {
|
|
|
82
81
|
this.show();
|
|
83
82
|
}
|
|
84
83
|
};
|
|
85
|
-
const sig = { signal: this.
|
|
84
|
+
const sig = { signal: this.listeners.signal };
|
|
86
85
|
this.input?.addEventListener('click', toggle, sig);
|
|
87
86
|
this.backdrop.addEventListener('click', (e) => {
|
|
88
87
|
e.preventDefault();
|
|
89
88
|
e.stopPropagation();
|
|
90
89
|
this.hide();
|
|
91
90
|
}, sig);
|
|
92
|
-
this.handleDocumentClick = (e) => {
|
|
93
|
-
if (this.calendar.classList.contains('mobile'))
|
|
94
|
-
return;
|
|
95
|
-
const target = e.target;
|
|
96
|
-
if (!this.calendar.contains(target) && target !== this.input) {
|
|
97
|
-
this.hide();
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
91
|
}
|
|
101
92
|
show() {
|
|
93
|
+
if (this.options.timePicker) {
|
|
94
|
+
this.clockMode = 'hours';
|
|
95
|
+
this.renderTimePicker();
|
|
96
|
+
}
|
|
102
97
|
const isMobile = window.innerWidth <= 640;
|
|
103
98
|
if (isMobile) {
|
|
104
99
|
this.calendar.classList.add('mobile');
|
|
@@ -123,7 +118,13 @@ class DatePicker {
|
|
|
123
118
|
}
|
|
124
119
|
setTimeout(() => {
|
|
125
120
|
if (this.calendar.classList.contains('visible')) {
|
|
126
|
-
|
|
121
|
+
this.showListeners = new ListenerGroup();
|
|
122
|
+
document.addEventListener('click', (e) => {
|
|
123
|
+
const target = e.target;
|
|
124
|
+
if (!this.calendar.contains(target) && target !== this.input) {
|
|
125
|
+
this.hide();
|
|
126
|
+
}
|
|
127
|
+
}, { signal: this.showListeners.signal });
|
|
127
128
|
}
|
|
128
129
|
}, 0);
|
|
129
130
|
}
|
|
@@ -133,7 +134,8 @@ class DatePicker {
|
|
|
133
134
|
this.calendar.classList.remove('visible');
|
|
134
135
|
this.backdrop.classList.remove('visible');
|
|
135
136
|
document.body.style.overflow = '';
|
|
136
|
-
|
|
137
|
+
this.showListeners?.destroy();
|
|
138
|
+
this.showListeners = null;
|
|
137
139
|
}
|
|
138
140
|
render() {
|
|
139
141
|
this.calendar.innerHTML = '';
|
|
@@ -151,8 +153,7 @@ class DatePicker {
|
|
|
151
153
|
this.calendar.appendChild(header);
|
|
152
154
|
this.calendar.appendChild(content);
|
|
153
155
|
if (this.options.timePicker && this.viewMode === 'days') {
|
|
154
|
-
|
|
155
|
-
this.calendar.appendChild(timeSection);
|
|
156
|
+
this.calendar.appendChild(this.createTimePicker());
|
|
156
157
|
const setBtn = document.createElement('button');
|
|
157
158
|
setBtn.className = 'datepicker-set-btn';
|
|
158
159
|
setBtn.textContent = 'Set';
|
|
@@ -163,6 +164,16 @@ class DatePicker {
|
|
|
163
164
|
this.calendar.appendChild(setBtn);
|
|
164
165
|
}
|
|
165
166
|
}
|
|
167
|
+
renderTimePicker() {
|
|
168
|
+
if (!this.options.timePicker || this.viewMode !== 'days')
|
|
169
|
+
return;
|
|
170
|
+
const existing = this.calendar.querySelector('.datepicker-clock');
|
|
171
|
+
if (!existing) {
|
|
172
|
+
this.render();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
existing.replaceWith(this.createTimePicker());
|
|
176
|
+
}
|
|
166
177
|
createHeader() {
|
|
167
178
|
const header = document.createElement('div');
|
|
168
179
|
header.className = 'datepicker-header';
|
|
@@ -242,6 +253,7 @@ class DatePicker {
|
|
|
242
253
|
createMonthGrid() {
|
|
243
254
|
const grid = document.createElement('div');
|
|
244
255
|
grid.className = 'datepicker-grid-months';
|
|
256
|
+
const now = new Date();
|
|
245
257
|
this.options?.locales?.months.forEach((month, index) => {
|
|
246
258
|
const el = document.createElement('div');
|
|
247
259
|
el.className = 'datepicker-month';
|
|
@@ -249,7 +261,7 @@ class DatePicker {
|
|
|
249
261
|
if (index === this.viewMonth) {
|
|
250
262
|
el.classList.add('selected');
|
|
251
263
|
}
|
|
252
|
-
if (index ===
|
|
264
|
+
if (index === now.getMonth() && this.viewYear === now.getFullYear()) {
|
|
253
265
|
el.classList.add('current');
|
|
254
266
|
}
|
|
255
267
|
el.onclick = (e) => {
|
|
@@ -292,7 +304,7 @@ class DatePicker {
|
|
|
292
304
|
const days = this.options?.locales?.days ?? ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
293
305
|
const startDay = this.options.startDay ?? 0;
|
|
294
306
|
const adjustedDays = [...days.slice(startDay), ...days.slice(0, startDay)];
|
|
295
|
-
adjustedDays.forEach(day => {
|
|
307
|
+
adjustedDays.forEach((day) => {
|
|
296
308
|
const el = document.createElement('div');
|
|
297
309
|
el.className = 'datepicker-day-header';
|
|
298
310
|
el.textContent = day;
|
|
@@ -353,88 +365,156 @@ class DatePicker {
|
|
|
353
365
|
return grid;
|
|
354
366
|
}
|
|
355
367
|
createTimePicker() {
|
|
368
|
+
this.clockListeners?.destroy();
|
|
369
|
+
this.clockListeners = new ListenerGroup();
|
|
356
370
|
const wrapper = document.createElement('div');
|
|
357
|
-
wrapper.className = 'datepicker-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
label.textContent = 'Time';
|
|
361
|
-
wrapper.appendChild(label);
|
|
362
|
-
const controls = document.createElement('div');
|
|
363
|
-
controls.className = 'datepicker-time-controls';
|
|
364
|
-
const hoursSpinner = this.createSpinner(this.selectedHours, 0, 23, (value) => {
|
|
365
|
-
this.selectedHours = value;
|
|
366
|
-
this.applyTimeToSelection();
|
|
367
|
-
});
|
|
368
|
-
const separator = document.createElement('span');
|
|
369
|
-
separator.className = 'datepicker-time-separator';
|
|
370
|
-
separator.textContent = ':';
|
|
371
|
-
const minutesSpinner = this.createSpinner(this.selectedMinutes, 0, 59, (value) => {
|
|
372
|
-
this.selectedMinutes = value;
|
|
373
|
-
this.applyTimeToSelection();
|
|
374
|
-
});
|
|
375
|
-
controls.appendChild(hoursSpinner);
|
|
376
|
-
controls.appendChild(separator);
|
|
377
|
-
controls.appendChild(minutesSpinner);
|
|
378
|
-
wrapper.appendChild(controls);
|
|
371
|
+
wrapper.className = 'datepicker-clock';
|
|
372
|
+
wrapper.appendChild(this.createClockHeader());
|
|
373
|
+
wrapper.appendChild(this.createClockFace());
|
|
379
374
|
return wrapper;
|
|
380
375
|
}
|
|
381
|
-
|
|
382
|
-
const
|
|
383
|
-
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
376
|
+
createClockHeader() {
|
|
377
|
+
const header = document.createElement('div');
|
|
378
|
+
header.className = 'datepicker-clock-header';
|
|
379
|
+
const display = document.createElement('div');
|
|
380
|
+
display.className = 'datepicker-clock-display';
|
|
381
|
+
const hourSeg = document.createElement('span');
|
|
382
|
+
hourSeg.className = 'datepicker-clock-display-segment';
|
|
383
|
+
hourSeg.classList.toggle('active', this.clockMode === 'hours');
|
|
384
|
+
hourSeg.textContent = String(this.selectedHours).padStart(2, '0');
|
|
385
|
+
hourSeg.onclick = (e) => {
|
|
388
386
|
e.stopPropagation();
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
this.render();
|
|
387
|
+
this.clockMode = 'hours';
|
|
388
|
+
this.renderTimePicker();
|
|
392
389
|
};
|
|
393
|
-
const
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
display.addEventListener('change', (e) => {
|
|
390
|
+
const separator = document.createElement('span');
|
|
391
|
+
separator.className = 'datepicker-clock-display-separator';
|
|
392
|
+
separator.textContent = ':';
|
|
393
|
+
const minuteSeg = document.createElement('span');
|
|
394
|
+
minuteSeg.className = 'datepicker-clock-display-segment';
|
|
395
|
+
minuteSeg.classList.toggle('active', this.clockMode === 'minutes');
|
|
396
|
+
minuteSeg.textContent = String(this.selectedMinutes).padStart(2, '0');
|
|
397
|
+
minuteSeg.onclick = (e) => {
|
|
402
398
|
e.stopPropagation();
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
399
|
+
this.clockMode = 'minutes';
|
|
400
|
+
this.renderTimePicker();
|
|
401
|
+
};
|
|
402
|
+
display.appendChild(hourSeg);
|
|
403
|
+
display.appendChild(separator);
|
|
404
|
+
display.appendChild(minuteSeg);
|
|
405
|
+
header.appendChild(display);
|
|
406
|
+
return header;
|
|
407
|
+
}
|
|
408
|
+
createClockFace() {
|
|
409
|
+
const face = document.createElement('div');
|
|
410
|
+
face.className = 'datepicker-clock-face';
|
|
411
|
+
const center = document.createElement('div');
|
|
412
|
+
center.className = 'datepicker-clock-center';
|
|
413
|
+
face.appendChild(center);
|
|
414
|
+
const hand = document.createElement('div');
|
|
415
|
+
hand.className = 'datepicker-clock-hand';
|
|
416
|
+
face.appendChild(hand);
|
|
417
|
+
if (this.clockMode === 'hours') {
|
|
418
|
+
for (let slot = 0; slot < 12; slot++) {
|
|
419
|
+
const outerValue = slot * 2;
|
|
420
|
+
const innerValue = slot * 2 + 1;
|
|
421
|
+
face.appendChild(this.createClockNumber(outerValue, this.clockPosition(slot, 12, DatePicker.CLOCK_OUTER_RADIUS_PERCENT), outerValue === this.selectedHours, false));
|
|
422
|
+
face.appendChild(this.createClockNumber(innerValue, this.clockPosition(slot, 12, DatePicker.CLOCK_INNER_RADIUS_PERCENT), innerValue === this.selectedHours, true));
|
|
407
423
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const next = value + 1 > max ? min : value + 1;
|
|
415
|
-
onChange(next);
|
|
416
|
-
this.render();
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
for (let slot = 0; slot < 12; slot++) {
|
|
427
|
+
const value = slot * 5;
|
|
428
|
+
const isSelected = Math.round(this.selectedMinutes / 5) % 12 === slot;
|
|
429
|
+
face.appendChild(this.createClockNumber(value, this.clockPosition(slot, 12, DatePicker.CLOCK_OUTER_RADIUS_PERCENT), isSelected, false));
|
|
417
430
|
}
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
431
|
+
}
|
|
432
|
+
const activeValue = this.clockMode === 'hours' ? this.selectedHours : this.selectedMinutes;
|
|
433
|
+
this.positionHand(hand, activeValue);
|
|
434
|
+
this.bindClockDrag(face, hand);
|
|
435
|
+
return face;
|
|
436
|
+
}
|
|
437
|
+
createClockNumber(value, position, selected, inner) {
|
|
438
|
+
const number = document.createElement('div');
|
|
439
|
+
number.className = 'datepicker-clock-number';
|
|
440
|
+
number.classList.toggle('inner', inner);
|
|
441
|
+
number.classList.toggle('selected', selected);
|
|
442
|
+
number.style.left = `${position.x}%`;
|
|
443
|
+
number.style.top = `${position.y}%`;
|
|
444
|
+
number.textContent = String(value).padStart(2, '0');
|
|
445
|
+
return number;
|
|
446
|
+
}
|
|
447
|
+
clockPosition(index, count, radiusPercent) {
|
|
448
|
+
const angle = (index / count) * 2 * Math.PI - Math.PI / 2;
|
|
449
|
+
const x = 50 + radiusPercent * Math.cos(angle);
|
|
450
|
+
const y = 50 + radiusPercent * Math.sin(angle);
|
|
451
|
+
return { x, y };
|
|
452
|
+
}
|
|
453
|
+
positionHand(hand, value) {
|
|
454
|
+
if (this.clockMode === 'hours') {
|
|
455
|
+
const slot = Math.floor(value / 2) % 12;
|
|
456
|
+
const isInner = value % 2 === 1;
|
|
457
|
+
hand.style.transform = `rotate(${(slot / 12) * 360}deg)`;
|
|
458
|
+
hand.style.height = `${isInner ? DatePicker.CLOCK_INNER_RADIUS_PERCENT : DatePicker.CLOCK_OUTER_RADIUS_PERCENT}%`;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
hand.style.transform = `rotate(${(value / 60) * 360}deg)`;
|
|
462
|
+
hand.style.height = `${DatePicker.CLOCK_OUTER_RADIUS_PERCENT}%`;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
bindClockDrag(face, hand) {
|
|
466
|
+
const sig = { signal: this.clockListeners.signal };
|
|
467
|
+
let dragging = false;
|
|
468
|
+
const valueFromPointer = (e) => {
|
|
469
|
+
const rect = face.getBoundingClientRect();
|
|
470
|
+
const dx = e.clientX - (rect.left + rect.width / 2);
|
|
471
|
+
const dy = e.clientY - (rect.top + rect.height / 2);
|
|
472
|
+
const angle = Math.atan2(dy, dx) + Math.PI / 2;
|
|
473
|
+
const normalized = (angle / (2 * Math.PI) + 1) % 1;
|
|
474
|
+
const slot = Math.round(normalized * 12) % 12;
|
|
475
|
+
let value;
|
|
476
|
+
if (this.clockMode === 'hours') {
|
|
477
|
+
const distancePercent = (Math.sqrt(dx * dx + dy * dy) / rect.width) * 100;
|
|
478
|
+
const ringThreshold = (DatePicker.CLOCK_OUTER_RADIUS_PERCENT + DatePicker.CLOCK_INNER_RADIUS_PERCENT) / 2;
|
|
479
|
+
value = distancePercent > ringThreshold ? slot * 2 : slot * 2 + 1;
|
|
423
480
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
e.stopPropagation();
|
|
430
|
-
const next = value - 1 < min ? max : value - 1;
|
|
431
|
-
onChange(next);
|
|
432
|
-
this.render();
|
|
481
|
+
else {
|
|
482
|
+
value = Math.round(normalized * 60) % 60;
|
|
483
|
+
}
|
|
484
|
+
this.positionHand(hand, value);
|
|
485
|
+
return value;
|
|
433
486
|
};
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
487
|
+
face.addEventListener('pointerdown', (e) => {
|
|
488
|
+
e.stopPropagation();
|
|
489
|
+
face.setPointerCapture(e.pointerId);
|
|
490
|
+
dragging = true;
|
|
491
|
+
valueFromPointer(e);
|
|
492
|
+
}, sig);
|
|
493
|
+
face.addEventListener('pointermove', (e) => {
|
|
494
|
+
if (!dragging)
|
|
495
|
+
return;
|
|
496
|
+
valueFromPointer(e);
|
|
497
|
+
}, sig);
|
|
498
|
+
for (const type of ['pointerup', 'pointercancel']) {
|
|
499
|
+
face.addEventListener(type, (e) => {
|
|
500
|
+
if (!dragging)
|
|
501
|
+
return;
|
|
502
|
+
dragging = false;
|
|
503
|
+
this.selectClockValue(valueFromPointer(e));
|
|
504
|
+
}, sig);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
selectClockValue(value) {
|
|
508
|
+
if (this.clockMode === 'hours') {
|
|
509
|
+
this.selectedHours = value;
|
|
510
|
+
this.applyTimeToSelection();
|
|
511
|
+
this.clockMode = 'minutes';
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
this.selectedMinutes = value;
|
|
515
|
+
this.applyTimeToSelection();
|
|
516
|
+
}
|
|
517
|
+
this.renderTimePicker();
|
|
438
518
|
}
|
|
439
519
|
applyTimeToSelection() {
|
|
440
520
|
if (this.options.mode === 'single' && this.selectedDate) {
|
|
@@ -521,7 +601,8 @@ class DatePicker {
|
|
|
521
601
|
}
|
|
522
602
|
destroy() {
|
|
523
603
|
this.hide();
|
|
524
|
-
this.
|
|
604
|
+
this.listeners.destroy();
|
|
605
|
+
this.clockListeners?.destroy();
|
|
525
606
|
this.calendar.remove();
|
|
526
607
|
this.backdrop.remove();
|
|
527
608
|
}
|
package/js/dropdown.d.ts
CHANGED
package/js/dropdown.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { bestPlacement } from './position.js';
|
|
2
|
+
import { ListenerGroup } from './listeners.js';
|
|
2
3
|
class Dropdown {
|
|
3
4
|
container;
|
|
4
5
|
trigger;
|
|
5
6
|
menu;
|
|
6
7
|
options;
|
|
7
|
-
|
|
8
|
+
listeners = new ListenerGroup();
|
|
8
9
|
constructor(selector, options = {}) {
|
|
9
10
|
const container = document.querySelector(selector);
|
|
10
11
|
if (!container) {
|
|
11
|
-
console.error(`Dropdown container not found: ${selector}`);
|
|
12
12
|
throw new Error(`Dropdown container "${selector}" not found`);
|
|
13
13
|
}
|
|
14
14
|
this.container = container;
|
|
@@ -23,7 +23,6 @@ class Dropdown {
|
|
|
23
23
|
closeOnSelect: options.closeOnSelect ?? true,
|
|
24
24
|
allowMultipleOpen: options.allowMultipleOpen ?? false,
|
|
25
25
|
};
|
|
26
|
-
this.abortController = new AbortController();
|
|
27
26
|
this.init();
|
|
28
27
|
}
|
|
29
28
|
init() {
|
|
@@ -31,7 +30,7 @@ class Dropdown {
|
|
|
31
30
|
this.attachEventListeners();
|
|
32
31
|
}
|
|
33
32
|
attachEventListeners() {
|
|
34
|
-
const { signal } = this.
|
|
33
|
+
const { signal } = this.listeners;
|
|
35
34
|
this.trigger.addEventListener('click', (e) => {
|
|
36
35
|
e.stopPropagation();
|
|
37
36
|
this.toggle();
|
|
@@ -122,7 +121,7 @@ class Dropdown {
|
|
|
122
121
|
this.container.dispatchEvent(event);
|
|
123
122
|
}
|
|
124
123
|
destroy() {
|
|
125
|
-
this.
|
|
124
|
+
this.listeners.destroy();
|
|
126
125
|
this.close();
|
|
127
126
|
}
|
|
128
127
|
}
|
package/js/editor.d.ts
CHANGED
package/js/editor.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { sanitizeHtml } from './utils.js';
|
|
2
|
+
import { ListenerGroup } from './listeners.js';
|
|
2
3
|
class Editor {
|
|
3
4
|
root;
|
|
4
5
|
editable;
|
|
@@ -8,7 +9,7 @@ class Editor {
|
|
|
8
9
|
wordCount;
|
|
9
10
|
undoStack = [];
|
|
10
11
|
redoStack = [];
|
|
11
|
-
|
|
12
|
+
listeners = new ListenerGroup();
|
|
12
13
|
constructor(options = {}) {
|
|
13
14
|
if (options.root instanceof HTMLElement) {
|
|
14
15
|
this.root = options.root;
|
|
@@ -60,8 +61,8 @@ class Editor {
|
|
|
60
61
|
return this.root.querySelectorAll(selector);
|
|
61
62
|
}
|
|
62
63
|
bindToolbar() {
|
|
63
|
-
const sig = { signal: this.
|
|
64
|
-
this.qAll('[data-cmd]').forEach(btn => {
|
|
64
|
+
const sig = { signal: this.listeners.signal };
|
|
65
|
+
this.qAll('[data-cmd]').forEach((btn) => {
|
|
65
66
|
btn.addEventListener('click', () => {
|
|
66
67
|
const cmd = btn.dataset.cmd;
|
|
67
68
|
const val = btn.dataset.value ?? null;
|
|
@@ -71,7 +72,7 @@ class Editor {
|
|
|
71
72
|
});
|
|
72
73
|
}
|
|
73
74
|
bindActions() {
|
|
74
|
-
const sig = { signal: this.
|
|
75
|
+
const sig = { signal: this.listeners.signal };
|
|
75
76
|
this.q('[data-editor-action="link"]')?.addEventListener('click', () => {
|
|
76
77
|
const url = prompt('Enter URL:', 'https://');
|
|
77
78
|
if (url)
|
|
@@ -120,10 +121,7 @@ class Editor {
|
|
|
120
121
|
this.onContentChange();
|
|
121
122
|
}, sig);
|
|
122
123
|
this.q('[data-editor-action="minify-code"]')?.addEventListener('click', () => {
|
|
123
|
-
code.value = code.value
|
|
124
|
-
.replace(/\n/g, '')
|
|
125
|
-
.replace(/>\s+</g, '><')
|
|
126
|
-
.trim();
|
|
124
|
+
code.value = code.value.replace(/\n/g, '').replace(/>\s+</g, '><').trim();
|
|
127
125
|
}, sig);
|
|
128
126
|
}
|
|
129
127
|
this.q('[data-editor-action="save"]')?.addEventListener('click', () => this.downloadHTML(), sig);
|
|
@@ -172,10 +170,10 @@ class Editor {
|
|
|
172
170
|
e.preventDefault();
|
|
173
171
|
this.redo();
|
|
174
172
|
}
|
|
175
|
-
}, { signal: this.
|
|
173
|
+
}, { signal: this.listeners.signal });
|
|
176
174
|
}
|
|
177
175
|
bindEditable() {
|
|
178
|
-
const sig = { signal: this.
|
|
176
|
+
const sig = { signal: this.listeners.signal };
|
|
179
177
|
this.editable.addEventListener('input', () => this.onContentChange(), sig);
|
|
180
178
|
this.editable.addEventListener('paste', (e) => {
|
|
181
179
|
e.preventDefault();
|
|
@@ -186,12 +184,12 @@ class Editor {
|
|
|
186
184
|
this.editable.addEventListener('mouseup', () => this.refreshActiveState(), sig);
|
|
187
185
|
}
|
|
188
186
|
bindTabs() {
|
|
189
|
-
const sig = { signal: this.
|
|
190
|
-
this.qAll('.side-tab[data-tab]').forEach(tab => {
|
|
187
|
+
const sig = { signal: this.listeners.signal };
|
|
188
|
+
this.qAll('.side-tab[data-tab]').forEach((tab) => {
|
|
191
189
|
tab.addEventListener('click', () => {
|
|
192
190
|
const target = tab.dataset.tab;
|
|
193
|
-
this.qAll('.side-tab').forEach(t => t.classList.remove('active'));
|
|
194
|
-
this.qAll('.side-panel').forEach(p => p.classList.remove('active'));
|
|
191
|
+
this.qAll('.side-tab').forEach((t) => t.classList.remove('active'));
|
|
192
|
+
this.qAll('.side-panel').forEach((p) => p.classList.remove('active'));
|
|
195
193
|
tab.classList.add('active');
|
|
196
194
|
this.q(`[data-editor="${target}"]`)?.classList.add('active');
|
|
197
195
|
}, sig);
|
|
@@ -211,8 +209,11 @@ class Editor {
|
|
|
211
209
|
updateWordCount() {
|
|
212
210
|
if (!this.wordCount)
|
|
213
211
|
return;
|
|
214
|
-
const text = this.editable.innerText
|
|
215
|
-
const words = text
|
|
212
|
+
const text = this.editable.innerText;
|
|
213
|
+
const words = text
|
|
214
|
+
.trim()
|
|
215
|
+
.split(/\s+/)
|
|
216
|
+
.filter((w) => w.length > 0);
|
|
216
217
|
const count = words.length;
|
|
217
218
|
this.wordCount.textContent = `${count} word${count !== 1 ? 's' : ''}`;
|
|
218
219
|
}
|
|
@@ -310,12 +311,11 @@ class Editor {
|
|
|
310
311
|
return;
|
|
311
312
|
const range = sel.getRangeAt(0);
|
|
312
313
|
const container = range.commonAncestorContainer;
|
|
313
|
-
let current = container.nodeType === Node.TEXT_NODE
|
|
314
|
-
|
|
315
|
-
: container;
|
|
314
|
+
let current = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
|
|
315
|
+
const upperTag = tagName.toUpperCase();
|
|
316
316
|
let wrapper = null;
|
|
317
317
|
while (current && current !== this.editable) {
|
|
318
|
-
if (current.tagName ===
|
|
318
|
+
if (current.tagName === upperTag) {
|
|
319
319
|
wrapper = current;
|
|
320
320
|
break;
|
|
321
321
|
}
|
|
@@ -357,9 +357,7 @@ class Editor {
|
|
|
357
357
|
return;
|
|
358
358
|
const range = sel.getRangeAt(0);
|
|
359
359
|
const container = range.commonAncestorContainer;
|
|
360
|
-
let blockElement = container.nodeType === Node.TEXT_NODE
|
|
361
|
-
? container.parentElement
|
|
362
|
-
: container;
|
|
360
|
+
let blockElement = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
|
|
363
361
|
while (blockElement && blockElement !== this.editable && blockElement.parentElement !== this.editable) {
|
|
364
362
|
blockElement = blockElement.parentElement;
|
|
365
363
|
}
|
|
@@ -377,7 +375,7 @@ class Editor {
|
|
|
377
375
|
const range = sel.getRangeAt(0);
|
|
378
376
|
const text = range.toString();
|
|
379
377
|
const list = document.createElement(listTag);
|
|
380
|
-
const lines = text ? text.split('\n').filter(l => l.trim()) : [''];
|
|
378
|
+
const lines = text ? text.split('\n').filter((l) => l.trim()) : [''];
|
|
381
379
|
for (const line of lines) {
|
|
382
380
|
const li = document.createElement('li');
|
|
383
381
|
li.textContent = line.trim() || '';
|
|
@@ -396,15 +394,15 @@ class Editor {
|
|
|
396
394
|
}
|
|
397
395
|
setAlignment(cmd) {
|
|
398
396
|
const align = {
|
|
399
|
-
justifyLeft: 'left',
|
|
397
|
+
justifyLeft: 'left',
|
|
398
|
+
justifyCenter: 'center',
|
|
399
|
+
justifyRight: 'right',
|
|
400
400
|
};
|
|
401
401
|
const sel = window.getSelection();
|
|
402
402
|
if (!sel || sel.rangeCount === 0)
|
|
403
403
|
return;
|
|
404
404
|
const container = sel.getRangeAt(0).commonAncestorContainer;
|
|
405
|
-
let block = container.nodeType === Node.TEXT_NODE
|
|
406
|
-
? container.parentElement
|
|
407
|
-
: container;
|
|
405
|
+
let block = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
|
|
408
406
|
while (block && block !== this.editable && block.parentElement !== this.editable) {
|
|
409
407
|
block = block.parentElement;
|
|
410
408
|
}
|
|
@@ -451,15 +449,13 @@ ${content}
|
|
|
451
449
|
return;
|
|
452
450
|
const range = sel.getRangeAt(0);
|
|
453
451
|
const container = range.commonAncestorContainer;
|
|
454
|
-
const element = container.nodeType === Node.TEXT_NODE
|
|
455
|
-
|
|
456
|
-
: container;
|
|
457
|
-
this.qAll('[data-cmd]').forEach(btn => {
|
|
452
|
+
const element = container.nodeType === Node.TEXT_NODE ? container.parentElement : container;
|
|
453
|
+
this.qAll('[data-cmd]').forEach((btn) => {
|
|
458
454
|
const cmd = btn.dataset.cmd;
|
|
459
455
|
let active = false;
|
|
460
456
|
let current = element;
|
|
461
457
|
while (current && current !== this.editable) {
|
|
462
|
-
const tag = current.tagName
|
|
458
|
+
const tag = current.tagName.toLowerCase();
|
|
463
459
|
if ((cmd === 'bold' && (tag === 'strong' || tag === 'b')) ||
|
|
464
460
|
(cmd === 'italic' && (tag === 'em' || tag === 'i')) ||
|
|
465
461
|
(cmd === 'underline' && tag === 'u') ||
|
|
@@ -473,7 +469,7 @@ ${content}
|
|
|
473
469
|
});
|
|
474
470
|
}
|
|
475
471
|
destroy() {
|
|
476
|
-
this.
|
|
472
|
+
this.listeners.destroy();
|
|
477
473
|
}
|
|
478
474
|
}
|
|
479
475
|
export { Editor };
|
package/js/file-uploader.d.ts
CHANGED
|
@@ -22,18 +22,11 @@ declare class FileUploader {
|
|
|
22
22
|
private uploadUrl;
|
|
23
23
|
private maxFileSize?;
|
|
24
24
|
private allowedTypes?;
|
|
25
|
-
private
|
|
25
|
+
private xhrAborts;
|
|
26
|
+
private listeners;
|
|
26
27
|
constructor(elementOrSelector: string | HTMLElement, config?: FileUploaderConfig);
|
|
27
|
-
private init;
|
|
28
28
|
private fileKey;
|
|
29
29
|
private setupEventListeners;
|
|
30
|
-
private preventDefaults;
|
|
31
|
-
private handleDragEnter;
|
|
32
|
-
private handleDragLeave;
|
|
33
|
-
private handleDrop;
|
|
34
|
-
private handleDropZoneClick;
|
|
35
|
-
private handleFileInputChange;
|
|
36
|
-
private handleUploadClick;
|
|
37
30
|
private handleFiles;
|
|
38
31
|
private validateFile;
|
|
39
32
|
private addFileToUI;
|