@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/lightbox.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ListenerGroup } from './listeners.js';
1
2
  class Lightbox {
2
3
  images;
3
4
  currentIndex;
@@ -9,7 +10,7 @@ class Lightbox {
9
10
  captionEl = null;
10
11
  counterEl = null;
11
12
  isZoomed = false;
12
- abortController = new AbortController();
13
+ listeners = new ListenerGroup();
13
14
  constructor(options) {
14
15
  if (options.images && options.images.length > 0) {
15
16
  this.images = options.images;
@@ -36,12 +37,12 @@ class Lightbox {
36
37
  this.imgEl = wrapper.querySelector('.lightbox-img');
37
38
  this.captionEl = wrapper.querySelector('.lightbox-caption');
38
39
  this.counterEl = wrapper.querySelector('.lightbox-counter');
39
- const sig = { signal: this.abortController.signal };
40
+ const sig = { signal: this.listeners.signal };
40
41
  if (this.closeable) {
41
- wrapper.querySelector('.lightbox-close')?.addEventListener('click', this.hide, sig);
42
- wrapper.querySelector('.lightbox-background')?.addEventListener('click', this.handleBackgroundClick, sig);
42
+ wrapper.querySelector('.lightbox-close')?.addEventListener('click', () => this.hide(), sig);
43
+ wrapper.querySelector('.lightbox-background')?.addEventListener('click', (e) => this.handleBackgroundClick(e), sig);
43
44
  }
44
- document.addEventListener('keydown', this.handleKeydown, sig);
45
+ document.addEventListener('keydown', (e) => this.handleKeydown(e), sig);
45
46
  if (this.images.length > 1) {
46
47
  wrapper.querySelector('.lightbox-prev')?.addEventListener('click', () => this.prev(), sig);
47
48
  wrapper.querySelector('.lightbox-next')?.addEventListener('click', () => this.next(), sig);
@@ -57,12 +58,11 @@ class Lightbox {
57
58
  });
58
59
  this.onOpen?.();
59
60
  }
60
- hide = () => {
61
+ hide() {
61
62
  const wrapper = this.wrapper;
62
63
  if (!wrapper)
63
64
  return;
64
- this.abortController.abort();
65
- this.abortController = new AbortController();
65
+ this.listeners.reset();
66
66
  document.body.style.overflow = '';
67
67
  wrapper.classList.remove('is-visible');
68
68
  setTimeout(() => {
@@ -76,7 +76,7 @@ class Lightbox {
76
76
  }
77
77
  this.onClose?.();
78
78
  }, 300);
79
- };
79
+ }
80
80
  next() {
81
81
  if (this.images.length <= 1)
82
82
  return;
@@ -137,7 +137,7 @@ class Lightbox {
137
137
  return;
138
138
  const prevIdx = (index - 1 + this.images.length) % this.images.length;
139
139
  const nextIdx = (index + 1) % this.images.length;
140
- [prevIdx, nextIdx].forEach(i => {
140
+ [prevIdx, nextIdx].forEach((i) => {
141
141
  const img = new Image();
142
142
  img.src = this.images[i].src;
143
143
  });
@@ -164,7 +164,7 @@ class Lightbox {
164
164
  this.imgEl.classList.toggle('is-zoomed', this.isZoomed);
165
165
  this.wrapper?.querySelector('.lightbox-img-wrap')?.classList.toggle('is-zoomed', this.isZoomed);
166
166
  }
167
- handleKeydown = (e) => {
167
+ handleKeydown(e) {
168
168
  switch (e.key) {
169
169
  case 'Escape':
170
170
  if (this.closeable)
@@ -180,11 +180,11 @@ class Lightbox {
180
180
  this.trapFocus(e);
181
181
  break;
182
182
  }
183
- };
183
+ }
184
184
  trapFocus(e) {
185
185
  if (!this.wrapper)
186
186
  return;
187
- const focusable = Array.from(this.wrapper.querySelectorAll('button:not([hidden]), [tabindex]:not([tabindex="-1"]):not([hidden])')).filter(el => el.offsetParent !== null);
187
+ const focusable = Array.from(this.wrapper.querySelectorAll('button:not([hidden]), [tabindex]:not([tabindex="-1"]):not([hidden])')).filter((el) => el.offsetParent !== null);
188
188
  if (focusable.length === 0)
189
189
  return;
190
190
  const first = focusable[0];
@@ -202,11 +202,11 @@ class Lightbox {
202
202
  }
203
203
  }
204
204
  }
205
- handleBackgroundClick = (e) => {
205
+ handleBackgroundClick(e) {
206
206
  if (e.target?.classList.contains('lightbox-background')) {
207
207
  this.hide();
208
208
  }
209
- };
209
+ }
210
210
  addTouchSupport() {
211
211
  const wrap = this.wrapper?.querySelector('.lightbox-img-wrap');
212
212
  if (!wrap)
@@ -216,16 +216,21 @@ class Lightbox {
216
216
  wrap.addEventListener('touchstart', (e) => {
217
217
  startX = e.touches[0].clientX;
218
218
  isDragging = true;
219
- }, { passive: true, signal: this.abortController.signal });
219
+ }, { passive: true, signal: this.listeners.signal });
220
220
  wrap.addEventListener('touchend', (e) => {
221
221
  if (!isDragging)
222
222
  return;
223
223
  const deltaX = e.changedTouches[0].clientX - startX;
224
224
  if (Math.abs(deltaX) > 50) {
225
- deltaX < 0 ? this.next() : this.prev();
225
+ if (deltaX < 0) {
226
+ this.next();
227
+ }
228
+ else {
229
+ this.prev();
230
+ }
226
231
  }
227
232
  isDragging = false;
228
- }, { signal: this.abortController.signal });
233
+ }, { signal: this.listeners.signal });
229
234
  }
230
235
  buildTemplate() {
231
236
  return `
@@ -250,13 +255,9 @@ class Lightbox {
250
255
  static bind(selector = '[data-lightbox]') {
251
256
  const elements = document.querySelectorAll(selector);
252
257
  const groups = new Map();
253
- elements.forEach(el => {
258
+ elements.forEach((el) => {
254
259
  const groupKey = el.dataset.lightbox || `__solo__${el.dataset.lightboxId ?? Math.random()}`;
255
- const src = el instanceof HTMLAnchorElement
256
- ? el.href
257
- : el instanceof HTMLImageElement
258
- ? el.src
259
- : (el.dataset.src ?? '');
260
+ const src = el instanceof HTMLAnchorElement ? el.href : el instanceof HTMLImageElement ? el.src : (el.dataset.src ?? '');
260
261
  const imgChild = el.querySelector('img');
261
262
  const alt = el instanceof HTMLImageElement ? el.alt : (imgChild?.alt ?? '');
262
263
  const caption = el.dataset.lightboxCaption;
@@ -264,13 +265,13 @@ class Lightbox {
264
265
  groups.set(groupKey, []);
265
266
  groups.get(groupKey).push({ el, image: { src, alt, caption } });
266
267
  });
267
- groups.forEach(items => {
268
+ groups.forEach((items) => {
268
269
  items.forEach(({ el }, idx) => {
269
270
  el.style.cursor = 'zoom-in';
270
- el.addEventListener('click', e => {
271
+ el.addEventListener('click', (e) => {
271
272
  e.preventDefault();
272
273
  new Lightbox({
273
- images: items.map(i => i.image),
274
+ images: items.map((i) => i.image),
274
275
  startIndex: idx,
275
276
  }).show();
276
277
  });
@@ -0,0 +1,7 @@
1
+ declare class ListenerGroup {
2
+ private controller;
3
+ get signal(): AbortSignal;
4
+ reset(): void;
5
+ destroy(): void;
6
+ }
7
+ export { ListenerGroup };
@@ -0,0 +1,14 @@
1
+ class ListenerGroup {
2
+ controller = new AbortController();
3
+ get signal() {
4
+ return this.controller.signal;
5
+ }
6
+ reset() {
7
+ this.controller.abort();
8
+ this.controller = new AbortController();
9
+ }
10
+ destroy() {
11
+ this.controller.abort();
12
+ }
13
+ }
14
+ export { ListenerGroup };
package/js/modal.d.ts CHANGED
@@ -14,10 +14,11 @@ declare class Modal {
14
14
  private readonly type;
15
15
  private template;
16
16
  private modalWrapper;
17
+ private listeners;
17
18
  constructor(options: ModalOptions);
18
19
  constructor(content: string, header?: string, footer?: string, closeable?: boolean, type?: ModalType);
19
20
  show(): void;
20
- hide: () => void;
21
+ hide(): void;
21
22
  private handleEscape;
22
23
  private handleBackgroundClick;
23
24
  private buildTemplate;
package/js/modal.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { sanitizeHtml } from './utils.js';
2
+ import { ListenerGroup } from './listeners.js';
2
3
  const CLOSE_ICON = '<div class="icon icon-close close"></div>';
3
4
  class Modal {
4
5
  content;
@@ -8,6 +9,7 @@ class Modal {
8
9
  type;
9
10
  template;
10
11
  modalWrapper = null;
12
+ listeners = new ListenerGroup();
11
13
  constructor(contentOrOptions, header, footer, closeable = true, type = 'default') {
12
14
  if (typeof contentOrOptions === 'object') {
13
15
  this.content = contentOrOptions.content;
@@ -32,31 +34,22 @@ class Modal {
32
34
  wrapper.innerHTML = this.template;
33
35
  document.body.append(wrapper);
34
36
  this.modalWrapper = wrapper;
37
+ const sig = { signal: this.listeners.signal };
35
38
  if (this.closeable) {
36
- const closeBtn = wrapper.querySelector('.close');
37
- closeBtn?.addEventListener('click', this.hide);
38
- }
39
- const background = wrapper.querySelector('.modal-background');
40
- if (this.closeable && background) {
41
- background.addEventListener('click', this.handleBackgroundClick);
42
- }
43
- if (this.closeable) {
44
- document.addEventListener('keydown', this.handleEscape);
39
+ wrapper.querySelector('.close')?.addEventListener('click', () => this.hide(), sig);
40
+ wrapper.querySelector('.modal-background')?.addEventListener('click', (e) => this.handleBackgroundClick(e), sig);
41
+ document.addEventListener('keydown', (e) => this.handleEscape(e), sig);
45
42
  }
46
43
  document.body.style.overflow = 'hidden';
47
44
  requestAnimationFrame(() => {
48
45
  wrapper.classList.add('is-visible');
49
46
  });
50
47
  }
51
- hide = () => {
48
+ hide() {
52
49
  const wrapper = this.modalWrapper;
53
50
  if (!wrapper)
54
51
  return;
55
- const closeBtn = wrapper.querySelector('.close');
56
- closeBtn?.removeEventListener('click', this.hide);
57
- const background = wrapper.querySelector('.modal-background');
58
- background?.removeEventListener('click', this.handleBackgroundClick);
59
- document.removeEventListener('keydown', this.handleEscape);
52
+ this.listeners.reset();
60
53
  document.body.style.overflow = '';
61
54
  wrapper.classList.remove('is-visible');
62
55
  setTimeout(() => {
@@ -65,17 +58,17 @@ class Modal {
65
58
  this.modalWrapper = null;
66
59
  }
67
60
  }, 300);
68
- };
69
- handleEscape = (e) => {
61
+ }
62
+ handleEscape(e) {
70
63
  if (e.key === 'Escape') {
71
64
  this.hide();
72
65
  }
73
- };
74
- handleBackgroundClick = (e) => {
66
+ }
67
+ handleBackgroundClick(e) {
75
68
  if (e.target?.classList.contains('modal-background')) {
76
69
  this.hide();
77
70
  }
78
- };
71
+ }
79
72
  buildTemplate() {
80
73
  const parts = [`<div class="modal modal-${this.type}">`];
81
74
  if (this.closeable) {
package/js/popover.d.ts CHANGED
@@ -21,8 +21,9 @@ declare class Popover {
21
21
  private readonly trigger;
22
22
  private readonly opts;
23
23
  private popoverEl;
24
- private _isOpen;
25
24
  private hoverTimer;
25
+ private listeners;
26
+ private openListeners;
26
27
  constructor(triggerEl: HTMLElement | string, options: PopoverOptions);
27
28
  get isOpen(): boolean;
28
29
  open(): void;
@@ -39,7 +40,6 @@ declare class Popover {
39
40
  private onOutsideClick;
40
41
  private onEscape;
41
42
  private attachTrigger;
42
- private detachTrigger;
43
43
  }
44
44
  export { Popover };
45
45
  export type { PopoverOptions, PopoverPlacement, PopoverAlign, PopoverTrigger };
package/js/popover.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { computePosition } from './position.js';
2
2
  import { sanitizeHtml } from './utils.js';
3
+ import { ListenerGroup } from './listeners.js';
3
4
  const ARROW_SIZE = 6;
4
5
  class Popover {
5
6
  static openPopovers = new Set();
@@ -7,12 +8,11 @@ class Popover {
7
8
  trigger;
8
9
  opts;
9
10
  popoverEl = null;
10
- _isOpen = false;
11
11
  hoverTimer = null;
12
+ listeners = new ListenerGroup();
13
+ openListeners = null;
12
14
  constructor(triggerEl, options) {
13
- const el = typeof triggerEl === 'string'
14
- ? document.querySelector(triggerEl)
15
- : triggerEl;
15
+ const el = typeof triggerEl === 'string' ? document.querySelector(triggerEl) : triggerEl;
16
16
  if (!el)
17
17
  throw new Error('Popover: trigger element not found');
18
18
  this.trigger = el;
@@ -31,9 +31,11 @@ class Popover {
31
31
  };
32
32
  this.attachTrigger();
33
33
  }
34
- get isOpen() { return this._isOpen; }
34
+ get isOpen() {
35
+ return this.popoverEl !== null;
36
+ }
35
37
  open() {
36
- if (this._isOpen)
38
+ if (this.popoverEl)
37
39
  return;
38
40
  if (this.opts.triggerMode === 'click')
39
41
  Popover.closeAll();
@@ -42,40 +44,47 @@ class Popover {
42
44
  this.reposition();
43
45
  requestAnimationFrame(() => {
44
46
  this.popoverEl?.classList.add('is-open');
45
- this._isOpen = true;
46
47
  Popover.openPopovers.add(this);
47
48
  this.opts.onOpen();
49
+ this.openListeners = new ListenerGroup();
50
+ const openSig = { signal: this.openListeners.signal };
48
51
  if (this.opts.closeOnOutsideClick)
49
- document.addEventListener('pointerdown', this.onOutsideClick, { capture: true });
52
+ document.addEventListener('pointerdown', (e) => this.onOutsideClick(e), { ...openSig, capture: true });
50
53
  if (this.opts.closeOnEscape)
51
- document.addEventListener('keydown', this.onEscape);
54
+ document.addEventListener('keydown', (e) => this.onEscape(e), openSig);
52
55
  });
53
56
  }
54
57
  close() {
55
- if (!this._isOpen || !this.popoverEl)
58
+ if (!this.popoverEl)
56
59
  return;
57
60
  this.popoverEl.classList.remove('is-open');
58
- this._isOpen = false;
59
61
  Popover.openPopovers.delete(this);
60
62
  this.opts.onClose();
61
- document.removeEventListener('pointerdown', this.onOutsideClick, { capture: true });
62
- document.removeEventListener('keydown', this.onEscape);
63
+ this.openListeners?.destroy();
64
+ this.openListeners = null;
63
65
  this.trigger.removeAttribute('aria-expanded');
64
66
  this.trigger.removeAttribute('aria-controls');
65
67
  const el = this.popoverEl;
66
68
  setTimeout(() => el.remove(), 200);
67
69
  this.popoverEl = null;
68
70
  }
69
- toggle() { this._isOpen ? this.close() : this.open(); }
71
+ toggle() {
72
+ if (this.popoverEl) {
73
+ this.close();
74
+ }
75
+ else {
76
+ this.open();
77
+ }
78
+ }
70
79
  destroy() {
71
80
  this.close();
72
- this.detachTrigger();
81
+ this.listeners.destroy();
73
82
  }
74
83
  static closeAll() {
75
- Popover.openPopovers.forEach(p => p.close());
84
+ Popover.openPopovers.forEach((p) => p.close());
76
85
  }
77
86
  static initAll() {
78
- document.querySelectorAll('[data-popover]').forEach(trigger => {
87
+ document.querySelectorAll('[data-popover]').forEach((trigger) => {
79
88
  const sel = trigger.getAttribute('data-popover');
80
89
  if (!sel)
81
90
  return;
@@ -100,9 +109,7 @@ class Popover {
100
109
  el.setAttribute('data-arrow', String(this.opts.arrow));
101
110
  const hasStructure = /class="popover-(header|body|footer|menu)/.test(this.opts.content);
102
111
  const safeContent = sanitizeHtml(this.opts.content);
103
- el.innerHTML = hasStructure
104
- ? safeContent
105
- : `<div class="popover-body">${safeContent}</div>`;
112
+ el.innerHTML = hasStructure ? safeContent : `<div class="popover-body">${safeContent}</div>`;
106
113
  this.trigger.setAttribute('aria-expanded', 'true');
107
114
  this.trigger.setAttribute('aria-controls', id);
108
115
  return el;
@@ -123,37 +130,35 @@ class Popover {
123
130
  this.popoverEl.style.left = `${left}px`;
124
131
  this.popoverEl.style.top = `${top}px`;
125
132
  }
126
- onClick = () => { this.toggle(); };
127
- onMouseEnter = () => {
133
+ onClick() {
134
+ this.toggle();
135
+ }
136
+ onMouseEnter() {
128
137
  if (this.hoverTimer !== null)
129
138
  clearTimeout(this.hoverTimer);
130
139
  this.open();
131
- };
132
- onMouseLeave = () => {
140
+ }
141
+ onMouseLeave() {
133
142
  this.hoverTimer = window.setTimeout(() => this.close(), 120);
134
- };
135
- onOutsideClick = (e) => {
143
+ }
144
+ onOutsideClick(e) {
136
145
  const t = e.target;
137
146
  if (!this.popoverEl?.contains(t) && !this.trigger.contains(t))
138
147
  this.close();
139
- };
140
- onEscape = (e) => {
148
+ }
149
+ onEscape(e) {
141
150
  if (e.key === 'Escape')
142
151
  this.close();
143
- };
152
+ }
144
153
  attachTrigger() {
154
+ const sig = { signal: this.listeners.signal };
145
155
  if (this.opts.triggerMode === 'click') {
146
- this.trigger.addEventListener('click', this.onClick);
156
+ this.trigger.addEventListener('click', () => this.onClick(), sig);
147
157
  }
148
158
  else {
149
- this.trigger.addEventListener('mouseenter', this.onMouseEnter);
150
- this.trigger.addEventListener('mouseleave', this.onMouseLeave);
159
+ this.trigger.addEventListener('mouseenter', () => this.onMouseEnter(), sig);
160
+ this.trigger.addEventListener('mouseleave', () => this.onMouseLeave(), sig);
151
161
  }
152
162
  }
153
- detachTrigger() {
154
- this.trigger.removeEventListener('click', this.onClick);
155
- this.trigger.removeEventListener('mouseenter', this.onMouseEnter);
156
- this.trigger.removeEventListener('mouseleave', this.onMouseLeave);
157
- }
158
163
  }
159
164
  export { Popover };
package/js/position.js CHANGED
@@ -13,7 +13,7 @@ function bestPlacement(trigger, floating, offset) {
13
13
  return 'right';
14
14
  if (space.left >= floating.width)
15
15
  return 'left';
16
- return (Object.entries(space).sort((a, b) => b[1] - a[1])[0][0]);
16
+ return Object.entries(space).sort((a, b) => b[1] - a[1])[0][0];
17
17
  }
18
18
  function maybeFlip(placement, trigger, floating, offset) {
19
19
  const fits = {
@@ -23,15 +23,13 @@ function maybeFlip(placement, trigger, floating, offset) {
23
23
  right: window.innerWidth - trigger.right - offset >= floating.width,
24
24
  };
25
25
  const opp = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
26
- return (!fits[placement] && fits[opp[placement]]) ? opp[placement] : placement;
26
+ return !fits[placement] && fits[opp[placement]] ? opp[placement] : placement;
27
27
  }
28
28
  function computePosition(trigger, floating, opts) {
29
29
  const offset = opts.offset ?? 8;
30
30
  const margin = opts.margin ?? 8;
31
31
  const align = opts.align ?? 'center';
32
- const placement = opts.placement === 'auto'
33
- ? bestPlacement(trigger, floating, offset)
34
- : maybeFlip(opts.placement, trigger, floating, offset);
32
+ const placement = opts.placement === 'auto' ? bestPlacement(trigger, floating, offset) : maybeFlip(opts.placement, trigger, floating, offset);
35
33
  let left = 0, top = 0;
36
34
  switch (placement) {
37
35
  case 'top':
package/js/push-menu.d.ts CHANGED
@@ -10,6 +10,8 @@ declare class PushMenu {
10
10
  private static elements;
11
11
  private static initialized;
12
12
  private static panelStack;
13
+ private static listeners;
14
+ private static clickNavListeners;
13
15
  static init(): void;
14
16
  private static buildPanels;
15
17
  private static extractSubPanels;
package/js/push-menu.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { ListenerGroup } from './listeners.js';
1
2
  class PushMenu {
2
3
  static elements = {
3
4
  navigation: null,
@@ -5,10 +6,12 @@ class PushMenu {
5
6
  menu: null,
6
7
  header: null,
7
8
  controlIcon: null,
8
- backdrop: null
9
+ backdrop: null,
9
10
  };
10
11
  static initialized = false;
11
12
  static panelStack = [];
13
+ static listeners = new ListenerGroup();
14
+ static clickNavListeners = null;
12
15
  static init() {
13
16
  if (this.initialized) {
14
17
  console.warn('PushMenu: Already initialized');
@@ -19,8 +22,9 @@ class PushMenu {
19
22
  throw new Error('PushMenu: Required elements not found (.navigation, .push-content)');
20
23
  }
21
24
  this.buildPanels();
22
- this.elements.navigation.addEventListener('change', this.handleNavigationChange);
23
- this.elements.backdrop?.addEventListener('click', this.handleBackdropClick);
25
+ const sig = { signal: this.listeners.signal };
26
+ this.elements.navigation.addEventListener('change', () => this.handleNavigationChange(), sig);
27
+ this.elements.backdrop?.addEventListener('click', () => this.handleBackdropClick(), sig);
24
28
  this.initialized = true;
25
29
  }
26
30
  static buildPanels() {
@@ -117,17 +121,19 @@ class PushMenu {
117
121
  }
118
122
  }, 300);
119
123
  }
120
- static handleNavigationChange = () => {
124
+ static handleNavigationChange() {
121
125
  const isPushed = this.elements.content?.classList.contains('pushed') ?? false;
122
126
  if (!isPushed) {
123
- this.elements.content?.addEventListener('click', this.clickNav);
127
+ this.clickNavListeners = new ListenerGroup();
128
+ this.elements.content?.addEventListener('click', () => this.clickNav(), { signal: this.clickNavListeners.signal });
124
129
  }
125
130
  else {
126
- this.elements.content?.removeEventListener('click', this.clickNav);
131
+ this.clickNavListeners?.destroy();
132
+ this.clickNavListeners = null;
127
133
  this.resetPanels();
128
134
  }
129
135
  this.pushToggle();
130
- };
136
+ }
131
137
  static pushToggle() {
132
138
  if (!this.elements.content || !this.elements.menu) {
133
139
  throw new Error('PushMenu: Required elements not found (.push-content, .push-menu)');
@@ -148,14 +154,14 @@ class PushMenu {
148
154
  }
149
155
  }
150
156
  }
151
- static clickNav = () => {
157
+ static clickNav() {
152
158
  PushMenu.elements.navigation.click();
153
- };
154
- static handleBackdropClick = () => {
159
+ }
160
+ static handleBackdropClick() {
155
161
  if (PushMenu.isOpen()) {
156
162
  PushMenu.elements.navigation.click();
157
163
  }
158
- };
164
+ }
159
165
  static open() {
160
166
  if (!this.elements.content?.classList.contains('pushed')) {
161
167
  this.pushToggle();
@@ -172,9 +178,9 @@ class PushMenu {
172
178
  static destroy() {
173
179
  if (!this.initialized)
174
180
  return;
175
- this.elements.navigation?.removeEventListener('change', this.handleNavigationChange);
176
- this.elements.content?.removeEventListener('click', this.clickNav);
177
- this.elements.backdrop?.removeEventListener('click', this.handleBackdropClick);
181
+ this.listeners.reset();
182
+ this.clickNavListeners?.destroy();
183
+ this.clickNavListeners = null;
178
184
  this.close();
179
185
  this.elements = {
180
186
  navigation: null,
@@ -182,7 +188,7 @@ class PushMenu {
182
188
  menu: null,
183
189
  header: null,
184
190
  controlIcon: null,
185
- backdrop: null
191
+ backdrop: null,
186
192
  };
187
193
  this.panelStack = [];
188
194
  this.initialized = false;
@@ -1,6 +1,6 @@
1
1
  declare class RangeSlider {
2
2
  private readonly input;
3
- private abortController;
3
+ private listeners;
4
4
  constructor(input: HTMLInputElement);
5
5
  static initAll(selector?: string): void;
6
6
  private update;
@@ -12,7 +12,7 @@ declare class RangeSliderRange {
12
12
  private readonly endInput;
13
13
  private readonly fill;
14
14
  private readonly fillCreatedByUs;
15
- private abortController;
15
+ private listeners;
16
16
  constructor(container: HTMLElement);
17
17
  static initAll(selector?: string): void;
18
18
  values(): [number, number];
@@ -1,10 +1,11 @@
1
+ import { ListenerGroup } from './listeners.js';
1
2
  class RangeSlider {
2
3
  input;
3
- abortController = new AbortController();
4
+ listeners = new ListenerGroup();
4
5
  constructor(input) {
5
6
  this.input = input;
6
7
  this.update();
7
- this.input.addEventListener('input', () => this.update(), { signal: this.abortController.signal });
8
+ this.input.addEventListener('input', () => this.update(), { signal: this.listeners.signal });
8
9
  }
9
10
  static initAll(selector = '.range-slider input[type="range"]') {
10
11
  document.querySelectorAll(selector).forEach((input) => {
@@ -18,7 +19,7 @@ class RangeSlider {
18
19
  this.input.style.setProperty('--range-fill', `${pct}%`);
19
20
  }
20
21
  destroy() {
21
- this.abortController.abort();
22
+ this.listeners.destroy();
22
23
  this.input.style.removeProperty('--range-fill');
23
24
  }
24
25
  }
@@ -28,7 +29,7 @@ class RangeSliderRange {
28
29
  endInput;
29
30
  fill;
30
31
  fillCreatedByUs;
31
- abortController = new AbortController();
32
+ listeners = new ListenerGroup();
32
33
  constructor(container) {
33
34
  const inputs = container.querySelectorAll('input[type="range"]');
34
35
  if (inputs.length !== 2) {
@@ -45,7 +46,7 @@ class RangeSliderRange {
45
46
  container.insertBefore(this.fill, container.firstChild);
46
47
  }
47
48
  this.update();
48
- const signal = this.abortController.signal;
49
+ const signal = this.listeners.signal;
49
50
  this.startInput.addEventListener('input', () => this.handleInput(this.startInput), { signal });
50
51
  this.endInput.addEventListener('input', () => this.handleInput(this.endInput), { signal });
51
52
  }
@@ -75,7 +76,7 @@ class RangeSliderRange {
75
76
  this.container.style.setProperty('--range-end', `${endPct}%`);
76
77
  }
77
78
  destroy() {
78
- this.abortController.abort();
79
+ this.listeners.destroy();
79
80
  this.container.style.removeProperty('--range-start');
80
81
  this.container.style.removeProperty('--range-end');
81
82
  if (this.fillCreatedByUs) {
package/js/scroll.js CHANGED
@@ -4,14 +4,11 @@ class Scroll {
4
4
  const offset = fixedHeader ? fixedHeader.offsetHeight : 0;
5
5
  const settings = {
6
6
  behavior: 'smooth',
7
- offset: offset,
7
+ offset,
8
8
  block: 'start',
9
- ...options
9
+ ...options,
10
10
  };
11
- let el = target instanceof Element ? target : null;
12
- if (typeof target === 'string') {
13
- el = document.querySelector(target);
14
- }
11
+ const el = typeof target === 'string' ? document.querySelector(target) : target;
15
12
  if (!el)
16
13
  return;
17
14
  const rect = el.getBoundingClientRect();
@@ -19,7 +16,7 @@ class Scroll {
19
16
  const offsetTop = rect.top + scrollTop - settings.offset;
20
17
  window.scrollTo({
21
18
  top: offsetTop,
22
- behavior: settings.behavior
19
+ behavior: settings.behavior,
23
20
  });
24
21
  }
25
22
  }