@dodlhuat/basix 1.4.2 → 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.
Files changed (90) hide show
  1. package/README.md +13 -1
  2. package/css/calendar.scss +4 -3
  3. package/css/code-viewer.scss +1 -1
  4. package/css/datepicker.scss +83 -70
  5. package/css/editor.scss +2 -1
  6. package/css/flyout-menu.scss +2 -1
  7. package/css/form.scss +11 -32
  8. package/css/mixins.scss +25 -0
  9. package/css/range-slider.scss +7 -6
  10. package/css/stepper.scss +2 -1
  11. package/css/style.css +84 -71
  12. package/css/style.css.map +1 -1
  13. package/css/style.min.css +1 -1
  14. package/css/style.min.css.map +1 -1
  15. package/css/timepicker.scss +1 -1
  16. package/css/virtual-dropdown.scss +6 -16
  17. package/js/bottom-sheet.d.ts +3 -5
  18. package/js/bottom-sheet.js +39 -58
  19. package/js/calendar.d.ts +1 -2
  20. package/js/calendar.js +77 -84
  21. package/js/carousel.d.ts +1 -1
  22. package/js/carousel.js +22 -14
  23. package/js/chart.d.ts +1 -1
  24. package/js/chart.js +104 -65
  25. package/js/code-viewer.d.ts +1 -1
  26. package/js/code-viewer.js +7 -18
  27. package/js/color-picker.d.ts +2 -2
  28. package/js/color-picker.js +20 -22
  29. package/js/context-menu.d.ts +1 -1
  30. package/js/context-menu.js +10 -12
  31. package/js/datepicker.d.ts +14 -3
  32. package/js/datepicker.js +180 -99
  33. package/js/dropdown.d.ts +1 -1
  34. package/js/dropdown.js +4 -5
  35. package/js/editor.d.ts +1 -1
  36. package/js/editor.js +30 -34
  37. package/js/file-uploader.d.ts +2 -9
  38. package/js/file-uploader.js +57 -86
  39. package/js/flyout-menu.d.ts +3 -3
  40. package/js/flyout-menu.js +20 -22
  41. package/js/gallery.d.ts +1 -2
  42. package/js/gallery.js +14 -20
  43. package/js/group-picker.d.ts +1 -1
  44. package/js/group-picker.js +22 -29
  45. package/js/lightbox.d.ts +2 -2
  46. package/js/lightbox.js +28 -27
  47. package/js/listeners.d.ts +7 -0
  48. package/js/listeners.js +14 -0
  49. package/js/modal.d.ts +2 -1
  50. package/js/modal.js +13 -20
  51. package/js/popover.d.ts +2 -2
  52. package/js/popover.js +42 -37
  53. package/js/position.js +3 -5
  54. package/js/push-menu.d.ts +2 -0
  55. package/js/push-menu.js +21 -15
  56. package/js/range-slider.d.ts +2 -2
  57. package/js/range-slider.js +7 -6
  58. package/js/scroll.js +4 -7
  59. package/js/scrollbar.d.ts +3 -3
  60. package/js/scrollbar.js +39 -42
  61. package/js/select.d.ts +1 -1
  62. package/js/select.js +19 -23
  63. package/js/sidebar-nav.d.ts +1 -1
  64. package/js/sidebar-nav.js +13 -9
  65. package/js/stepper.d.ts +1 -1
  66. package/js/stepper.js +9 -11
  67. package/js/table.d.ts +1 -1
  68. package/js/table.js +24 -23
  69. package/js/tabs.d.ts +1 -1
  70. package/js/tabs.js +10 -17
  71. package/js/theme.d.ts +1 -0
  72. package/js/theme.js +6 -15
  73. package/js/timepicker.d.ts +8 -6
  74. package/js/timepicker.js +36 -36
  75. package/js/toast.d.ts +2 -1
  76. package/js/toast.js +8 -4
  77. package/js/tooltip.d.ts +1 -4
  78. package/js/tooltip.js +14 -23
  79. package/js/tree.d.ts +0 -1
  80. package/js/tree.js +6 -11
  81. package/js/utils.js +7 -8
  82. package/js/virtual-dropdown.d.ts +1 -1
  83. package/js/virtual-dropdown.js +26 -28
  84. package/package.json +1 -1
  85. package/css/calendar.css +0 -928
  86. package/css/guitar-chords.css +0 -251
  87. package/js/form-builder.js +0 -107
  88. package/js/guitar-chords.js +0 -268
  89. package/js/lazy-loader.js +0 -121
  90. package/js/request.js +0 -51
package/js/chart.js CHANGED
@@ -1,24 +1,20 @@
1
1
  import { escapeHtml } from './utils.js';
2
+ import { ListenerGroup } from './listeners.js';
2
3
  const MARGIN_XY = { top: 16, right: 24, bottom: 44, left: 52 };
3
4
  const MARGIN_BAR = { top: 8, right: 52, bottom: 24, left: 120 };
4
5
  const MARGIN_PIE = { top: 8, right: 8, bottom: 8, left: 8 };
5
- const FALLBACK_COLORS = [
6
- '#3D63DD', '#2E8B57', '#C28A00', '#D64545',
7
- '#8B5CF6', '#06B6D4', '#F97316', '#EC4899',
8
- ];
6
+ const FALLBACK_COLORS = ['#3D63DD', '#2E8B57', '#C28A00', '#D64545', '#8B5CF6', '#06B6D4', '#F97316', '#EC4899'];
9
7
  const SVG_NS = 'http://www.w3.org/2000/svg';
10
8
  class Chart {
11
9
  container;
12
10
  opts;
13
11
  tooltip;
14
12
  colors = [];
15
- abortController = new AbortController();
13
+ listeners = new ListenerGroup();
16
14
  resizeTimer = null;
17
15
  resizeObserver = null;
18
16
  constructor(selector, options) {
19
- const el = typeof selector === 'string'
20
- ? document.querySelector(selector)
21
- : selector;
17
+ const el = typeof selector === 'string' ? document.querySelector(selector) : selector;
22
18
  if (!el)
23
19
  throw new Error(`Chart: element not found for "${selector}"`);
24
20
  this.container = el;
@@ -40,8 +36,7 @@ class Chart {
40
36
  this.attachResizeObserver();
41
37
  }
42
38
  render() {
43
- this.abortController.abort();
44
- this.abortController = new AbortController();
39
+ this.listeners.reset();
45
40
  this.container.innerHTML = '';
46
41
  this.container.classList.add('chart');
47
42
  this.resolveColors();
@@ -82,12 +77,12 @@ class Chart {
82
77
  const svgH = height + m.top + m.bottom;
83
78
  const w = svgW - m.left - m.right;
84
79
  const h = height;
85
- const allValues = series.flatMap(s => s.data.map(d => d.value));
80
+ const allValues = series.flatMap((s) => s.data.map((d) => d.value));
86
81
  const yMax = this.opts.yMax ?? Math.max(...allValues) * 1.1;
87
- const labels = series[0].data.map(d => d.label);
82
+ const labels = series[0].data.map((d) => d.label);
88
83
  const svg = this.createSVG(canvas, svgW, svgH);
89
84
  if (showGrid)
90
- this.renderHGrid(svg, m, w, h, yMin, yMax);
85
+ this.renderHGrid(svg, m, w, h);
91
86
  this.renderXAxisLine(svg, m, w, h);
92
87
  this.renderXLabels(svg, m, w, h, labels);
93
88
  this.renderYLabels(svg, m, h, yMin, yMax);
@@ -101,15 +96,20 @@ class Chart {
101
96
  if (isArea) {
102
97
  const areaD = `${this.buildPath(pts)} L ${pts[pts.length - 1].x} ${m.top + h} L ${pts[0].x} ${m.top + h} Z`;
103
98
  svg.appendChild(this.svgEl('path', {
104
- d: areaD, fill: color,
105
- 'fill-opacity': '0.12', stroke: 'none',
99
+ d: areaD,
100
+ fill: color,
101
+ 'fill-opacity': '0.12',
102
+ stroke: 'none',
106
103
  class: 'chart-area',
107
104
  }));
108
105
  }
109
106
  const linePath = this.svgEl('path', {
110
- d: this.buildPath(pts), fill: 'none',
111
- stroke: color, 'stroke-width': '2.5',
112
- 'stroke-linecap': 'round', 'stroke-linejoin': 'round',
107
+ d: this.buildPath(pts),
108
+ fill: 'none',
109
+ stroke: color,
110
+ 'stroke-width': '2.5',
111
+ 'stroke-linecap': 'round',
112
+ 'stroke-linejoin': 'round',
113
113
  class: 'chart-line',
114
114
  });
115
115
  if (animate) {
@@ -126,17 +126,28 @@ class Chart {
126
126
  });
127
127
  const { x, y } = pts[i];
128
128
  g.appendChild(this.svgEl('circle', {
129
- cx: x, cy: y, r: 14,
130
- fill: 'transparent', class: 'chart-hit',
129
+ cx: x,
130
+ cy: y,
131
+ r: 14,
132
+ fill: 'transparent',
133
+ class: 'chart-hit',
131
134
  }));
132
135
  g.appendChild(this.svgEl('circle', {
133
- cx: x, cy: y, r: 7,
134
- fill: 'none', stroke: color, 'stroke-width': '2',
136
+ cx: x,
137
+ cy: y,
138
+ r: 7,
139
+ fill: 'none',
140
+ stroke: color,
141
+ 'stroke-width': '2',
135
142
  class: 'chart-point-ring',
136
143
  }));
137
144
  g.appendChild(this.svgEl('circle', {
138
- cx: x, cy: y, r: 4,
139
- fill: color, stroke: 'var(--background)', 'stroke-width': '2',
145
+ cx: x,
146
+ cy: y,
147
+ r: 4,
148
+ fill: color,
149
+ stroke: 'var(--background)',
150
+ 'stroke-width': '2',
140
151
  class: 'chart-point-dot',
141
152
  }));
142
153
  this.onPoint(g, s, d, i);
@@ -153,14 +164,14 @@ class Chart {
153
164
  const svgH = height + m.top + m.bottom;
154
165
  const w = svgW - m.left - m.right;
155
166
  const h = height;
156
- const allValues = series.flatMap(s => s.data.map(d => d.value));
167
+ const allValues = series.flatMap((s) => s.data.map((d) => d.value));
157
168
  const yMax = this.opts.yMax ?? Math.max(...allValues) * 1.1;
158
- const labels = series[0].data.map(d => d.label);
169
+ const labels = series[0].data.map((d) => d.label);
159
170
  const numPts = labels.length;
160
171
  const numSeries = series.length;
161
172
  const svg = this.createSVG(canvas, svgW, svgH);
162
173
  if (showGrid)
163
- this.renderHGrid(svg, m, w, h, yMin, yMax);
174
+ this.renderHGrid(svg, m, w, h);
164
175
  this.renderXAxisLine(svg, m, w, h);
165
176
  this.renderXLabels(svg, m, w, h, labels);
166
177
  this.renderYLabels(svg, m, h, yMin, yMax);
@@ -174,8 +185,12 @@ class Chart {
174
185
  const x = m.left + i * groupW + innerPad / 2 + si * (barW + 2);
175
186
  const y = m.top + h - barH;
176
187
  const rect = this.svgEl('rect', {
177
- x, y, width: barW, height: barH,
178
- fill: color, rx: 3,
188
+ x,
189
+ y,
190
+ width: barW,
191
+ height: barH,
192
+ fill: color,
193
+ rx: 3,
179
194
  class: 'chart-bar chart-bar--vertical',
180
195
  });
181
196
  if (animate) {
@@ -197,9 +212,9 @@ class Chart {
197
212
  const svgH = height + m.top + m.bottom;
198
213
  const w = svgW - m.left - m.right;
199
214
  const h = height;
200
- const allValues = series.flatMap(s => s.data.map(d => d.value));
201
- const xMax = this.opts.yMax || Math.max(...allValues) * 1.1;
202
- const labels = series[0].data.map(d => d.label);
215
+ const allValues = series.flatMap((s) => s.data.map((d) => d.value));
216
+ const xMax = this.opts.yMax ?? Math.max(...allValues) * 1.1;
217
+ const labels = series[0].data.map((d) => d.label);
203
218
  const numPts = labels.length;
204
219
  const numSeries = series.length;
205
220
  const svg = this.createSVG(canvas, svgW, svgH);
@@ -207,24 +222,32 @@ class Chart {
207
222
  for (let t = 0; t <= numTicks; t++) {
208
223
  const x = m.left + (t / numTicks) * w;
209
224
  svg.appendChild(this.svgEl('line', {
210
- x1: x, x2: x, y1: m.top, y2: m.top + h,
211
- stroke: 'var(--divider)', 'stroke-width': '1',
225
+ x1: x,
226
+ x2: x,
227
+ y1: m.top,
228
+ y2: m.top + h,
229
+ stroke: 'var(--divider)',
230
+ 'stroke-width': '1',
212
231
  'stroke-dasharray': t === 0 ? 'none' : '3 4',
213
232
  class: 'chart-grid-line',
214
233
  }));
215
234
  const label = this.svgEl('text', {
216
- x, y: m.top + h + 14,
217
- 'text-anchor': 'middle', class: 'chart-axis-label',
235
+ x,
236
+ y: m.top + h + 14,
237
+ 'text-anchor': 'middle',
238
+ class: 'chart-axis-label',
218
239
  });
219
- label.textContent = this.fmt(xMax * t / numTicks);
240
+ label.textContent = this.fmt((xMax * t) / numTicks);
220
241
  svg.appendChild(label);
221
242
  }
222
243
  const groupH = h / numPts;
223
244
  labels.forEach((label, i) => {
224
245
  const y = m.top + i * groupH + groupH / 2;
225
246
  const text = this.svgEl('text', {
226
- x: m.left - 10, y,
227
- 'text-anchor': 'end', 'dominant-baseline': 'middle',
247
+ x: m.left - 10,
248
+ y,
249
+ 'text-anchor': 'end',
250
+ 'dominant-baseline': 'middle',
228
251
  class: 'chart-axis-label',
229
252
  });
230
253
  text.textContent = label;
@@ -239,8 +262,12 @@ class Chart {
239
262
  const x = m.left;
240
263
  const y = m.top + i * groupH + innerPad / 2 + si * (barH + 2);
241
264
  const rect = this.svgEl('rect', {
242
- x, y, width: barW, height: barH,
243
- fill: color, rx: 3,
265
+ x,
266
+ y,
267
+ width: barW,
268
+ height: barH,
269
+ fill: color,
270
+ rx: 3,
244
271
  class: 'chart-bar chart-bar--horizontal',
245
272
  });
246
273
  if (animate) {
@@ -284,17 +311,18 @@ class Chart {
284
311
  path.style.animationDelay = `${delay}ms`;
285
312
  }
286
313
  const { x: dx, y: dy } = this.polar(0, 0, 8, midAngle);
314
+ const sig = { signal: this.listeners.signal };
315
+ const tipHtml = `<strong>${escapeHtml(d.label)}</strong>${this.fmt(d.value)} &nbsp;·&nbsp; ${((d.value / total) * 100).toFixed(1)}%`;
287
316
  path.addEventListener('mouseenter', (e) => {
288
317
  path.style.transform = `translate(${dx}px, ${dy}px)`;
289
- this.showTooltip(e, `<strong>${escapeHtml(d.label)}</strong>${this.fmt(d.value)} &nbsp;·&nbsp; ${((d.value / total) * 100).toFixed(1)}%`);
290
- }, { signal: this.abortController.signal });
318
+ this.showTooltip(e, tipHtml);
319
+ }, sig);
320
+ path.addEventListener('mousemove', (e) => this.moveTooltip(e), sig);
291
321
  path.addEventListener('mouseleave', () => {
292
322
  path.style.transform = '';
293
323
  this.hideTooltip();
294
- }, { signal: this.abortController.signal });
295
- path.addEventListener('click', () => {
296
- this.opts.onPointClick(s, d, i);
297
- }, { signal: this.abortController.signal });
324
+ }, sig);
325
+ path.addEventListener('click', () => this.opts.onPointClick(s, d, i), sig);
298
326
  svg.appendChild(path);
299
327
  startAngle = endAngle;
300
328
  });
@@ -302,20 +330,25 @@ class Chart {
302
330
  this.container.appendChild(this.buildPieLegend(s, total));
303
331
  }
304
332
  }
305
- renderHGrid(svg, m, w, h, yMin, yMax) {
333
+ renderHGrid(svg, m, w, h) {
306
334
  const numTicks = 5;
307
335
  for (let i = 0; i <= numTicks; i++) {
308
336
  const y = m.top + h - (i / numTicks) * h;
309
337
  svg.appendChild(this.svgEl('line', {
310
- x1: m.left, x2: m.left + w, y1: y, y2: y,
338
+ x1: m.left,
339
+ x2: m.left + w,
340
+ y1: y,
341
+ y2: y,
311
342
  class: i === 0 ? 'chart-axis-line' : 'chart-grid-line',
312
343
  }));
313
344
  }
314
345
  }
315
346
  renderXAxisLine(svg, m, w, h) {
316
347
  svg.appendChild(this.svgEl('line', {
317
- x1: m.left, x2: m.left + w,
318
- y1: m.top + h, y2: m.top + h,
348
+ x1: m.left,
349
+ x2: m.left + w,
350
+ y1: m.top + h,
351
+ y2: m.top + h,
319
352
  class: 'chart-axis-line',
320
353
  }));
321
354
  }
@@ -325,8 +358,10 @@ class Chart {
325
358
  labels.forEach((label, i) => {
326
359
  const x = m.left + (n > 1 ? i * step : w / 2);
327
360
  const text = this.svgEl('text', {
328
- x, y: m.top + h + 18,
329
- 'text-anchor': 'middle', class: 'chart-axis-label',
361
+ x,
362
+ y: m.top + h + 18,
363
+ 'text-anchor': 'middle',
364
+ class: 'chart-axis-label',
330
365
  });
331
366
  text.textContent = label;
332
367
  svg.appendChild(text);
@@ -338,8 +373,10 @@ class Chart {
338
373
  const val = yMin + (yMax - yMin) * (i / numTicks);
339
374
  const y = m.top + h - (i / numTicks) * h;
340
375
  const text = this.svgEl('text', {
341
- x: m.left - 8, y,
342
- 'text-anchor': 'end', 'dominant-baseline': 'middle',
376
+ x: m.left - 8,
377
+ y,
378
+ 'text-anchor': 'end',
379
+ 'dominant-baseline': 'middle',
343
380
  class: 'chart-axis-label',
344
381
  });
345
382
  text.textContent = this.fmt(val);
@@ -348,9 +385,12 @@ class Chart {
348
385
  }
349
386
  buildPath(pts) {
350
387
  switch (this.opts.curve) {
351
- case 'linear': return this.linearPath(pts);
352
- case 'step': return this.stepPath(pts);
353
- default: return this.smoothPath(pts);
388
+ case 'linear':
389
+ return this.linearPath(pts);
390
+ case 'step':
391
+ return this.stepPath(pts);
392
+ default:
393
+ return this.smoothPath(pts);
354
394
  }
355
395
  }
356
396
  linearPath(pts) {
@@ -392,11 +432,11 @@ class Chart {
392
432
  arcPath(cx, cy, r, startDeg, endDeg) {
393
433
  const start = this.polar(cx, cy, r, startDeg);
394
434
  const end = this.polar(cx, cy, r, endDeg);
395
- const large = (endDeg - startDeg) > 180 ? 1 : 0;
435
+ const large = endDeg - startDeg > 180 ? 1 : 0;
396
436
  return `M ${cx} ${cy} L ${start.x.toFixed(2)} ${start.y.toFixed(2)} A ${r} ${r} 0 ${large} 1 ${end.x.toFixed(2)} ${end.y.toFixed(2)} Z`;
397
437
  }
398
438
  polar(cx, cy, r, deg) {
399
- const rad = deg * Math.PI / 180;
439
+ const rad = (deg * Math.PI) / 180;
400
440
  return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
401
441
  }
402
442
  buildHeader() {
@@ -466,7 +506,7 @@ class Chart {
466
506
  this.tooltip.classList.remove('is-visible');
467
507
  }
468
508
  onPoint(g, s, d, i) {
469
- const sig = { signal: this.abortController.signal };
509
+ const sig = { signal: this.listeners.signal };
470
510
  g.addEventListener('mouseenter', (e) => {
471
511
  this.showTooltip(e, `<strong>${escapeHtml(d.label)}</strong>${escapeHtml(s.name)}: ${this.fmt(d.value)}`);
472
512
  }, sig);
@@ -475,7 +515,7 @@ class Chart {
475
515
  g.addEventListener('click', () => this.opts.onPointClick(s, d, i), sig);
476
516
  }
477
517
  onBar(rect, s, d, i) {
478
- const sig = { signal: this.abortController.signal };
518
+ const sig = { signal: this.listeners.signal };
479
519
  rect.style.cursor = 'pointer';
480
520
  rect.addEventListener('mouseenter', (e) => {
481
521
  this.showTooltip(e, `<strong>${escapeHtml(d.label)}</strong>${escapeHtml(s.name)}: ${this.fmt(d.value)}`);
@@ -486,8 +526,7 @@ class Chart {
486
526
  }
487
527
  resolveColors() {
488
528
  const style = getComputedStyle(this.container);
489
- this.colors = (this.opts.type === 'pie' ? this.opts.series[0]?.data ?? [] : this.opts.series)
490
- .map((_, i) => {
529
+ this.colors = (this.opts.type === 'pie' ? (this.opts.series[0]?.data ?? []) : this.opts.series).map((_, i) => {
491
530
  const css = style.getPropertyValue(`--chart-color-${i + 1}`).trim();
492
531
  return css || FALLBACK_COLORS[i % FALLBACK_COLORS.length];
493
532
  });
@@ -542,7 +581,7 @@ class Chart {
542
581
  this.render();
543
582
  }
544
583
  destroy() {
545
- this.abortController.abort();
584
+ this.listeners.destroy();
546
585
  this.resizeObserver?.disconnect();
547
586
  if (this.resizeTimer)
548
587
  clearTimeout(this.resizeTimer);
@@ -3,6 +3,7 @@ declare class CodeViewer {
3
3
  private container;
4
4
  private code;
5
5
  private language;
6
+ private listeners;
6
7
  constructor(elementOrSelector: string | HTMLElement, code: string, language?: SupportedLanguage);
7
8
  private highlight;
8
9
  private escape;
@@ -11,7 +12,6 @@ declare class CodeViewer {
11
12
  private highlightCSS;
12
13
  private copyCode;
13
14
  private render;
14
- private handleCopy;
15
15
  destroy(): void;
16
16
  }
17
17
  export { CodeViewer };
package/js/code-viewer.js CHANGED
@@ -1,11 +1,11 @@
1
+ import { ListenerGroup } from './listeners.js';
1
2
  class CodeViewer {
2
3
  container;
3
4
  code;
4
5
  language;
6
+ listeners = new ListenerGroup();
5
7
  constructor(elementOrSelector, code, language = 'javascript') {
6
- const element = typeof elementOrSelector === 'string'
7
- ? document.querySelector(elementOrSelector)
8
- : elementOrSelector;
8
+ const element = typeof elementOrSelector === 'string' ? document.querySelector(elementOrSelector) : elementOrSelector;
9
9
  if (!element) {
10
10
  throw new Error(`CodeViewer: Element not found for selector "${elementOrSelector}"`);
11
11
  }
@@ -28,12 +28,7 @@ class CodeViewer {
28
28
  }
29
29
  }
30
30
  escape(str) {
31
- return str
32
- .replace(/&/g, '&amp;')
33
- .replace(/</g, '&lt;')
34
- .replace(/>/g, '&gt;')
35
- .replace(/"/g, '&quot;')
36
- .replace(/'/g, '&#039;');
31
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
37
32
  }
38
33
  highlightJavaScript(code) {
39
34
  const strings = [];
@@ -88,7 +83,6 @@ class CodeViewer {
88
83
  return code;
89
84
  }
90
85
  highlightCSS(code) {
91
- code = this.escape(code);
92
86
  const comments = [];
93
87
  code = code.replace(/(\/\*[\s\S]*?\*\/)/g, (match) => {
94
88
  comments.push(match);
@@ -99,6 +93,7 @@ class CodeViewer {
99
93
  strings.push(match);
100
94
  return `###STRING${strings.length - 1}###`;
101
95
  });
96
+ code = this.escape(code);
102
97
  code = code.replace(/^([^{]+)(?={)/gm, (match) => {
103
98
  return '<span class="selector">' + match.trim() + '</span>';
104
99
  });
@@ -143,16 +138,10 @@ class CodeViewer {
143
138
  </div>
144
139
  `;
145
140
  const copyButton = this.container.querySelector('.copy-button');
146
- if (copyButton) {
147
- copyButton.addEventListener('click', this.handleCopy);
148
- }
141
+ copyButton?.addEventListener('click', () => this.copyCode(), { signal: this.listeners.signal });
149
142
  }
150
- handleCopy = () => {
151
- this.copyCode();
152
- };
153
143
  destroy() {
154
- const copyButton = this.container.querySelector('.copy-button');
155
- copyButton?.removeEventListener('click', this.handleCopy);
144
+ this.listeners.destroy();
156
145
  }
157
146
  }
158
147
  export { CodeViewer };
@@ -23,8 +23,8 @@ declare class ColorPicker {
23
23
  private saturation;
24
24
  private brightness;
25
25
  private isDragging;
26
- private abortController;
27
- private ro;
26
+ private listeners;
27
+ private resizeObserver;
28
28
  constructor(elementOrSelector: string | HTMLElement, options?: ColorPickerOptions);
29
29
  private build;
30
30
  private bindEvents;
@@ -1,3 +1,4 @@
1
+ import { ListenerGroup } from './listeners.js';
1
2
  class ColorPicker {
2
3
  container;
3
4
  onChange;
@@ -14,20 +15,18 @@ class ColorPicker {
14
15
  saturation = 100;
15
16
  brightness = 100;
16
17
  isDragging = false;
17
- abortController = new AbortController();
18
- ro;
18
+ listeners = new ListenerGroup();
19
+ resizeObserver;
19
20
  constructor(elementOrSelector, options = {}) {
20
- const el = typeof elementOrSelector === 'string'
21
- ? document.querySelector(elementOrSelector)
22
- : elementOrSelector;
21
+ const el = typeof elementOrSelector === 'string' ? document.querySelector(elementOrSelector) : elementOrSelector;
23
22
  if (!el)
24
23
  throw new Error(`ColorPicker: element not found for "${elementOrSelector}"`);
25
24
  this.container = el;
26
25
  this.onChange = options.onChange;
27
26
  this.build();
28
27
  this.bindEvents();
29
- this.ro = new ResizeObserver(() => this.resizeCanvas());
30
- this.ro.observe(this.canvas.parentElement);
28
+ this.resizeObserver = new ResizeObserver(() => this.resizeCanvas());
29
+ this.resizeObserver.observe(this.canvas.parentElement);
31
30
  this.resizeCanvas();
32
31
  if (options.value) {
33
32
  this.setValue(options.value);
@@ -83,7 +82,7 @@ class ColorPicker {
83
82
  this.preview = this.container.querySelector('.color-picker__preview');
84
83
  }
85
84
  bindEvents() {
86
- const sig = { signal: this.abortController.signal };
85
+ const sig = { signal: this.listeners.signal };
87
86
  this.canvas.addEventListener('pointerdown', (e) => {
88
87
  e.preventDefault();
89
88
  this.canvas.setPointerCapture(e.pointerId);
@@ -95,12 +94,11 @@ class ColorPicker {
95
94
  return;
96
95
  this.handleFieldInteraction(e);
97
96
  }, sig);
98
- this.canvas.addEventListener('pointerup', () => {
99
- this.isDragging = false;
100
- }, sig);
101
- this.canvas.addEventListener('pointercancel', () => {
102
- this.isDragging = false;
103
- }, sig);
97
+ for (const type of ['pointerup', 'pointercancel']) {
98
+ this.canvas.addEventListener(type, () => {
99
+ this.isDragging = false;
100
+ }, sig);
101
+ }
104
102
  this.hueSlider.addEventListener('input', () => {
105
103
  this.hue = +this.hueSlider.value;
106
104
  this.drawField();
@@ -144,13 +142,13 @@ class ColorPicker {
144
142
  const y = Math.max(0, Math.min(e.clientY - rect.top, rect.height));
145
143
  this.saturation = (x / rect.width) * 100;
146
144
  this.brightness = 100 - (y / rect.height) * 100;
147
- this.updateCursor();
145
+ this.updateCursor(rect);
148
146
  this.updateFromHSB();
149
147
  }
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;
148
+ updateCursor(rect) {
149
+ const r = rect ?? this.canvas.getBoundingClientRect();
150
+ const x = (this.saturation / 100) * r.width;
151
+ const y = (1 - this.brightness / 100) * r.height;
154
152
  this.cursor.style.left = `${x}px`;
155
153
  this.cursor.style.top = `${y}px`;
156
154
  this.cursor.classList.toggle('color-picker__cursor--dark', this.brightness > 50 && this.saturation < 80);
@@ -207,8 +205,8 @@ class ColorPicker {
207
205
  this.setFromRGB(this.hexToRgb(hex));
208
206
  }
209
207
  destroy() {
210
- this.abortController.abort();
211
- this.ro.disconnect();
208
+ this.listeners.destroy();
209
+ this.resizeObserver.disconnect();
212
210
  this.container.classList.remove('color-picker');
213
211
  this.container.innerHTML = '';
214
212
  }
@@ -281,7 +279,7 @@ class ColorPicker {
281
279
  };
282
280
  }
283
281
  rgbToHex({ r, g, b }) {
284
- return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('');
282
+ return '#' + [r, g, b].map((c) => c.toString(16).padStart(2, '0')).join('');
285
283
  }
286
284
  hexToRgb(hex) {
287
285
  const m = hex.match(/^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
@@ -18,7 +18,7 @@ declare class ContextMenu {
18
18
  private targets;
19
19
  private menuEl;
20
20
  private currentTarget;
21
- private abortController;
21
+ private listeners;
22
22
  private spritePath;
23
23
  constructor(selectorOrElement: string | HTMLElement | HTMLElement[], items: ContextMenuInput[], options?: ContextMenuOptions);
24
24
  private init;
@@ -1,9 +1,10 @@
1
+ import { ListenerGroup } from './listeners.js';
1
2
  class ContextMenu {
2
3
  items;
3
4
  targets;
4
5
  menuEl = null;
5
6
  currentTarget = null;
6
- abortController = new AbortController();
7
+ listeners = new ListenerGroup();
7
8
  spritePath;
8
9
  constructor(selectorOrElement, items, options = {}) {
9
10
  this.items = items;
@@ -20,7 +21,7 @@ class ContextMenu {
20
21
  this.init();
21
22
  }
22
23
  init() {
23
- const { signal } = this.abortController;
24
+ const { signal } = this.listeners;
24
25
  this.targets.forEach((target) => {
25
26
  target.addEventListener('contextmenu', (e) => {
26
27
  e.preventDefault();
@@ -40,15 +41,15 @@ class ContextMenu {
40
41
  if (e.key === 'Escape') {
41
42
  this.close();
42
43
  }
43
- if (e.key === 'ArrowDown') {
44
+ else if (e.key === 'ArrowDown') {
44
45
  e.preventDefault();
45
46
  this.moveFocus(1);
46
47
  }
47
- if (e.key === 'ArrowUp') {
48
+ else if (e.key === 'ArrowUp') {
48
49
  e.preventDefault();
49
50
  this.moveFocus(-1);
50
51
  }
51
- if (e.key === 'Enter') {
52
+ else if (e.key === 'Enter') {
52
53
  e.preventDefault();
53
54
  this.activateFocused();
54
55
  }
@@ -187,26 +188,23 @@ class ContextMenu {
187
188
  getFocusableItems() {
188
189
  if (!this.menuEl)
189
190
  return [];
190
- return Array.from(this.menuEl.children).filter((el) => el.classList.contains('context-menu-item') &&
191
- !el.classList.contains('is-disabled'));
191
+ return Array.from(this.menuEl.children).filter((el) => el.classList.contains('context-menu-item') && !el.classList.contains('is-disabled'));
192
192
  }
193
193
  moveFocus(direction) {
194
194
  const items = this.getFocusableItems();
195
195
  if (!items.length)
196
196
  return;
197
197
  const currentIndex = items.findIndex((el) => el.classList.contains('is-focused'));
198
- const nextIndex = (currentIndex + direction + items.length) % items.length;
198
+ const nextIndex = currentIndex === -1 ? (direction === 1 ? 0 : items.length - 1) : (currentIndex + direction + items.length) % items.length;
199
199
  items[currentIndex]?.classList.remove('is-focused');
200
200
  items[nextIndex].classList.add('is-focused');
201
201
  }
202
202
  activateFocused() {
203
- this.menuEl
204
- ?.querySelector('.context-menu-item.is-focused')
205
- ?.click();
203
+ this.menuEl?.querySelector('.context-menu-item.is-focused')?.click();
206
204
  }
207
205
  destroy() {
208
206
  this.close();
209
- this.abortController.abort();
207
+ this.listeners.destroy();
210
208
  }
211
209
  }
212
210
  export { ContextMenu };
@@ -15,6 +15,8 @@ interface DateRange {
15
15
  end: Date | null;
16
16
  }
17
17
  declare class DatePicker {
18
+ private static readonly CLOCK_OUTER_RADIUS_PERCENT;
19
+ private static readonly CLOCK_INNER_RADIUS_PERCENT;
18
20
  private input;
19
21
  private options;
20
22
  private currentDate;
@@ -29,8 +31,10 @@ declare class DatePicker {
29
31
  private selectedMinutes;
30
32
  private calendar;
31
33
  private backdrop;
32
- private handleDocumentClick;
33
- private abortController;
34
+ private clockMode;
35
+ private listeners;
36
+ private showListeners;
37
+ private clockListeners;
34
38
  constructor(elementOrSelector: string | HTMLInputElement, options?: DatePickerOptions);
35
39
  private init;
36
40
  private createCalendarElement;
@@ -38,13 +42,20 @@ declare class DatePicker {
38
42
  private show;
39
43
  private hide;
40
44
  private render;
45
+ private renderTimePicker;
41
46
  private createHeader;
42
47
  private navigate;
43
48
  private createMonthGrid;
44
49
  private createYearGrid;
45
50
  private createGrid;
46
51
  private createTimePicker;
47
- private createSpinner;
52
+ private createClockHeader;
53
+ private createClockFace;
54
+ private createClockNumber;
55
+ private clockPosition;
56
+ private positionHand;
57
+ private bindClockDrag;
58
+ private selectClockValue;
48
59
  private applyTimeToSelection;
49
60
  private changeMonth;
50
61
  private handleDateClick;