@dodlhuat/basix 1.3.1 → 1.3.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 (118) hide show
  1. package/README.md +14 -8
  2. package/css/accordion.scss +0 -5
  3. package/css/badge.scss +1 -6
  4. package/css/bottom-sheet.scss +3 -8
  5. package/css/breadcrumb.scss +6 -15
  6. package/css/button.scss +2 -1
  7. package/css/calendar.scss +0 -67
  8. package/css/card.scss +0 -5
  9. package/css/carousel.scss +0 -3
  10. package/css/chart.scss +0 -25
  11. package/css/chat-bubbles.scss +0 -15
  12. package/css/checkbox.scss +3 -2
  13. package/css/chips.scss +3 -7
  14. package/css/code-viewer.scss +1 -5
  15. package/css/context-menu.scss +5 -21
  16. package/css/datepicker.scss +6 -9
  17. package/css/docs.scss +0 -4
  18. package/css/dropdown.scss +1 -1
  19. package/css/editor.scss +1 -23
  20. package/css/file-uploader.scss +2 -2
  21. package/css/flyout-menu.scss +65 -58
  22. package/css/form.scss +0 -28
  23. package/css/gallery.scss +2 -3
  24. package/css/group-picker.scss +5 -35
  25. package/css/icons.scss +0 -3
  26. package/css/lightbox.scss +2 -4
  27. package/css/mixins.scss +8 -0
  28. package/css/modal.scss +3 -3
  29. package/css/parameters.scss +6 -1
  30. package/css/popover.scss +3 -15
  31. package/css/progress.scss +0 -6
  32. package/css/push-menu.scss +3 -28
  33. package/css/radiobutton.scss +2 -1
  34. package/css/range-slider.scss +1 -7
  35. package/css/scrollbar.scss +9 -16
  36. package/css/sidebar-nav.scss +0 -12
  37. package/css/stepper.scss +0 -4
  38. package/css/style.css +108 -116
  39. package/css/style.css.map +1 -1
  40. package/css/style.min.css +1 -1
  41. package/css/style.min.css.map +1 -1
  42. package/css/style.scss +1 -1
  43. package/css/table.scss +0 -4
  44. package/css/tabs.scss +0 -2
  45. package/css/timeline.scss +1 -13
  46. package/css/timepicker.scss +55 -39
  47. package/css/toast.scss +1 -1
  48. package/css/tooltip.scss +1 -5
  49. package/css/tree.scss +1 -1
  50. package/css/typography.scss +3 -3
  51. package/css/virtual-dropdown.scss +3 -28
  52. package/js/bottom-sheet.d.ts +3 -1
  53. package/js/bottom-sheet.js +26 -27
  54. package/js/calendar.d.ts +7 -0
  55. package/js/calendar.js +14 -33
  56. package/js/carousel.d.ts +2 -0
  57. package/js/carousel.js +13 -5
  58. package/js/chart.d.ts +4 -0
  59. package/js/chart.js +13 -31
  60. package/js/code-viewer.d.ts +1 -0
  61. package/js/code-viewer.js +4 -0
  62. package/js/context-menu.d.ts +9 -2
  63. package/js/context-menu.js +17 -14
  64. package/js/datepicker.d.ts +4 -0
  65. package/js/datepicker.js +26 -11
  66. package/js/dropdown.d.ts +3 -3
  67. package/js/dropdown.js +6 -9
  68. package/js/editor.d.ts +1 -1
  69. package/js/editor.js +14 -20
  70. package/js/file-uploader.d.ts +4 -0
  71. package/js/file-uploader.js +52 -43
  72. package/js/flyout-menu.d.ts +5 -3
  73. package/js/flyout-menu.js +23 -46
  74. package/js/gallery.d.ts +4 -0
  75. package/js/gallery.js +39 -50
  76. package/js/group-picker.d.ts +5 -0
  77. package/js/group-picker.js +12 -17
  78. package/js/lightbox.d.ts +3 -0
  79. package/js/lightbox.js +12 -6
  80. package/js/modal.d.ts +3 -1
  81. package/js/modal.js +14 -11
  82. package/js/popover.d.ts +2 -0
  83. package/js/popover.js +26 -30
  84. package/js/position.d.ts +2 -0
  85. package/js/position.js +1 -5
  86. package/js/push-menu.d.ts +2 -1
  87. package/js/push-menu.js +25 -48
  88. package/js/range-slider.d.ts +1 -0
  89. package/js/range-slider.js +5 -3
  90. package/js/scroll.d.ts +2 -0
  91. package/js/scroll.js +1 -0
  92. package/js/scrollbar.d.ts +2 -0
  93. package/js/scrollbar.js +24 -36
  94. package/js/select.d.ts +1 -0
  95. package/js/select.js +5 -10
  96. package/js/sidebar-nav.d.ts +2 -0
  97. package/js/sidebar-nav.js +8 -0
  98. package/js/stepper.d.ts +2 -0
  99. package/js/stepper.js +7 -1
  100. package/js/table.d.ts +4 -0
  101. package/js/table.js +15 -22
  102. package/js/tabs.d.ts +2 -0
  103. package/js/tabs.js +6 -14
  104. package/js/theme.d.ts +1 -0
  105. package/js/theme.js +5 -13
  106. package/js/timepicker.d.ts +22 -5
  107. package/js/timepicker.js +160 -57
  108. package/js/toast.d.ts +3 -1
  109. package/js/toast.js +25 -22
  110. package/js/tooltip.d.ts +2 -0
  111. package/js/tooltip.js +21 -19
  112. package/js/tree.d.ts +3 -0
  113. package/js/tree.js +13 -0
  114. package/js/utils.d.ts +1 -3
  115. package/js/utils.js +0 -3
  116. package/js/virtual-dropdown.d.ts +3 -0
  117. package/js/virtual-dropdown.js +25 -0
  118. package/package.json +2 -2
@@ -1,3 +1,4 @@
1
+ /** Options for configuring a BottomSheet instance. */
1
2
  interface BottomSheetOptions {
2
3
  content: string;
3
4
  header?: string;
@@ -6,6 +7,7 @@ interface BottomSheetOptions {
6
7
  snapHeight?: 'auto' | 'half' | 'full';
7
8
  onClose?: () => void;
8
9
  }
10
+ /** Slide-up sheet that attaches to the bottom of the viewport. */
9
11
  declare class BottomSheet {
10
12
  private readonly content;
11
13
  private readonly header?;
@@ -22,7 +24,7 @@ declare class BottomSheet {
22
24
  private isDragging;
23
25
  constructor(options: BottomSheetOptions);
24
26
  show(): void;
25
- hide(): void;
27
+ hide: () => void;
26
28
  snapTo(height: 'auto' | 'half' | 'full'): void;
27
29
  private handleEscape;
28
30
  private handleBackdropClick;
@@ -1,26 +1,26 @@
1
1
  import { sanitizeHtml } from './utils.js';
2
+ /** Slide-up sheet that attaches to the bottom of the viewport. */
2
3
  class BottomSheet {
4
+ content;
5
+ header;
6
+ footer;
7
+ closeable;
8
+ snapHeight;
9
+ onClose;
10
+ wrapper = null;
11
+ sheet = null;
12
+ handle = null;
13
+ body = null;
14
+ dragStartY = 0;
15
+ currentDragY = 0;
16
+ isDragging = false;
3
17
  constructor(options) {
4
- this.wrapper = null;
5
- this.sheet = null;
6
- this.handle = null;
7
- this.body = null;
8
- // Touch drag state
9
- this.dragStartY = 0;
10
- this.currentDragY = 0;
11
- this.isDragging = false;
12
18
  this.content = options.content;
13
19
  this.header = options.header;
14
20
  this.footer = options.footer;
15
21
  this.closeable = options.closeable ?? true;
16
22
  this.snapHeight = options.snapHeight ?? 'auto';
17
23
  this.onClose = options.onClose;
18
- this.hide = this.hide.bind(this);
19
- this.handleEscape = this.handleEscape.bind(this);
20
- this.handleBackdropClick = this.handleBackdropClick.bind(this);
21
- this.handleTouchStart = this.handleTouchStart.bind(this);
22
- this.handleTouchMove = this.handleTouchMove.bind(this);
23
- this.handleTouchEnd = this.handleTouchEnd.bind(this);
24
24
  }
25
25
  show() {
26
26
  this.hide();
@@ -53,7 +53,7 @@ class BottomSheet {
53
53
  wrapper.classList.add('is-visible');
54
54
  });
55
55
  }
56
- hide() {
56
+ hide = () => {
57
57
  if (!this.wrapper)
58
58
  return;
59
59
  const backdrop = this.wrapper.querySelector('.bottom-sheet-backdrop');
@@ -75,7 +75,7 @@ class BottomSheet {
75
75
  wrapper.remove();
76
76
  this.onClose?.();
77
77
  }, 420);
78
- }
78
+ };
79
79
  snapTo(height) {
80
80
  if (!this.sheet)
81
81
  return;
@@ -85,24 +85,24 @@ class BottomSheet {
85
85
  this.sheet.classList.add(`snap-${height}`);
86
86
  }
87
87
  }
88
- handleEscape(e) {
88
+ handleEscape = (e) => {
89
89
  if (e.key === 'Escape')
90
90
  this.hide();
91
- }
92
- handleBackdropClick(e) {
91
+ };
92
+ handleBackdropClick = (e) => {
93
93
  if (e.target?.classList.contains('bottom-sheet-backdrop')) {
94
94
  this.hide();
95
95
  }
96
- }
97
- handleTouchStart(e) {
96
+ };
97
+ handleTouchStart = (e) => {
98
98
  this.dragStartY = e.touches[0].clientY;
99
99
  this.currentDragY = 0;
100
100
  this.isDragging = true;
101
101
  if (this.sheet) {
102
102
  this.sheet.style.transition = 'none';
103
103
  }
104
- }
105
- handleTouchMove(e) {
104
+ };
105
+ handleTouchMove = (e) => {
106
106
  if (!this.isDragging || !this.sheet)
107
107
  return;
108
108
  const deltaY = e.touches[0].clientY - this.dragStartY;
@@ -118,8 +118,8 @@ class BottomSheet {
118
118
  const translateX = isDesktop ? '-50%' : '0';
119
119
  this.sheet.style.transform = `translateX(${translateX}) translateY(${this.currentDragY}px)`;
120
120
  e.preventDefault();
121
- }
122
- handleTouchEnd() {
121
+ };
122
+ handleTouchEnd = () => {
123
123
  if (!this.isDragging || !this.sheet)
124
124
  return;
125
125
  this.isDragging = false;
@@ -128,11 +128,10 @@ class BottomSheet {
128
128
  this.hide();
129
129
  }
130
130
  else {
131
- // Spring back
132
131
  this.sheet.style.transition = '';
133
132
  this.sheet.style.transform = '';
134
133
  }
135
- }
134
+ };
136
135
  updateScrollMask() {
137
136
  if (!this.body)
138
137
  return;
package/js/calendar.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /** Represents a single calendar event with a start/end date. */
1
2
  export interface CalendarEvent {
2
3
  id: string;
3
4
  title: string;
@@ -7,6 +8,7 @@ export interface CalendarEvent {
7
8
  className?: string;
8
9
  }
9
10
  export type CalendarView = 'month' | 'week' | 'agenda';
11
+ /** Localisation strings and week-start configuration for the Calendar. */
10
12
  export interface CalendarLocale {
11
13
  monthNames: string[];
12
14
  dayNamesShort: string[];
@@ -19,6 +21,7 @@ export interface CalendarLocale {
19
21
  allDay: string;
20
22
  noEvents: string;
21
23
  }
24
+ /** Configuration options for the Calendar component. */
22
25
  export interface CalendarOptions {
23
26
  container: HTMLElement | string;
24
27
  events?: CalendarEvent[];
@@ -31,6 +34,7 @@ export interface CalendarOptions {
31
34
  className?: string;
32
35
  iconBasePath?: string;
33
36
  }
37
+ /** Layout descriptor for a multi-day event spanning columns in a week row. */
34
38
  interface SpanLayout {
35
39
  event: CalendarEvent;
36
40
  colStart: number;
@@ -39,6 +43,7 @@ interface SpanLayout {
39
43
  continuesBefore: boolean;
40
44
  continuesAfter: boolean;
41
45
  }
46
+ /** Layout descriptor for a timed event positioned in a day column. */
42
47
  interface TimedEventLayout {
43
48
  event: CalendarEvent;
44
49
  top: number;
@@ -68,6 +73,7 @@ export declare const CalendarLogic: {
68
73
  computeTimedLayout(events: CalendarEvent[], day: Date): TimedEventLayout[];
69
74
  nowLinePct(): number;
70
75
  };
76
+ /** Produces HTML strings for each Calendar view (month, week, agenda). */
71
77
  export declare class CalendarRenderer {
72
78
  private locale;
73
79
  constructor(locale: CalendarLocale);
@@ -80,6 +86,7 @@ export declare class CalendarRenderer {
80
86
  renderWeekView(date: Date, events: CalendarEvent[], firstDayOfWeek: number, showNowLine?: boolean): string;
81
87
  renderAgendaView(year: number, month: number, events: CalendarEvent[]): string;
82
88
  }
89
+ /** Main Calendar controller — manages state, rendering, and event delegation. */
83
90
  export declare class Calendar {
84
91
  private container;
85
92
  private options;
package/js/calendar.js CHANGED
@@ -1,9 +1,3 @@
1
- // ============================================================
2
- // calendar.ts — Basix Calendar Component
3
- // ============================================================
4
- // -----------------------------------------------------------
5
- // Date Logic
6
- // -----------------------------------------------------------
7
1
  export const CalendarLogic = {
8
2
  getMonthGrid(year, month, firstDayOfWeek) {
9
3
  const firstOfMonth = new Date(year, month, 1);
@@ -146,7 +140,6 @@ export const CalendarLogic = {
146
140
  return diff;
147
141
  return (b.end.getTime() - b.start.getTime()) - (a.end.getTime() - a.start.getTime());
148
142
  });
149
- // Greedy sub-column assignment
150
143
  const colEnds = [];
151
144
  const assigns = [];
152
145
  for (const event of sorted) {
@@ -160,7 +153,6 @@ export const CalendarLogic = {
160
153
  assigns.push({ event, col });
161
154
  }
162
155
  return assigns.map(({ event, col }) => {
163
- // cols = highest sub-column among events that overlap this one + 1
164
156
  const cols = assigns
165
157
  .filter(a => a.event.start < event.end && a.event.end > event.start)
166
158
  .reduce((max, a) => Math.max(max, a.col), 0) + 1;
@@ -173,9 +165,6 @@ export const CalendarLogic = {
173
165
  return (now.getHours() * 60 + now.getMinutes()) / 1440 * 100;
174
166
  },
175
167
  };
176
- // -----------------------------------------------------------
177
- // Default Locale
178
- // -----------------------------------------------------------
179
168
  const DEFAULT_LOCALE = {
180
169
  monthNames: [
181
170
  'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
@@ -191,10 +180,9 @@ const DEFAULT_LOCALE = {
191
180
  allDay: 'Ganztägig',
192
181
  noEvents: 'Keine Termine',
193
182
  };
194
- // -----------------------------------------------------------
195
- // Renderer
196
- // -----------------------------------------------------------
183
+ /** Produces HTML strings for each Calendar view (month, week, agenda). */
197
184
  export class CalendarRenderer {
185
+ locale;
198
186
  constructor(locale) {
199
187
  this.locale = locale;
200
188
  }
@@ -288,7 +276,6 @@ export class CalendarRenderer {
288
276
  const dow = this.locale.dayNamesShort[(d.getDay() + 7) % 7];
289
277
  return `<div class="${cls}">${dow}<span>${d.getDate()}</span></div>`;
290
278
  }).join('');
291
- // All-day row: span layout for all allDay events (both single-day and multi-day)
292
279
  const allDayEvents = events.filter(e => e.allDay);
293
280
  const allDayLayouts = CalendarLogic.computeSpanLayout(days, allDayEvents);
294
281
  const allDayLanes = allDayLayouts.length > 0 ? Math.max(...allDayLayouts.map(l => l.lane)) + 1 : 0;
@@ -349,7 +336,6 @@ export class CalendarRenderer {
349
336
  for (let d = 1; d <= daysInMonth; d++) {
350
337
  const day = new Date(year, month, d);
351
338
  const dayEvents = CalendarLogic.getEventsForDay(events, day);
352
- // Multi-day events show only once (first occurrence in this month)
353
339
  const filtered = dayEvents.filter(e => {
354
340
  if (!CalendarLogic.isMultiDay(e))
355
341
  return true;
@@ -394,18 +380,17 @@ export class CalendarRenderer {
394
380
  return `<div class="cal__agenda">${html}</div>`;
395
381
  }
396
382
  }
397
- // -----------------------------------------------------------
398
- // Calendar — main controller
399
- // -----------------------------------------------------------
383
+ /** Main Calendar controller — manages state, rendering, and event delegation. */
400
384
  export class Calendar {
385
+ container;
386
+ options;
387
+ locale;
388
+ renderer;
389
+ currentDate;
390
+ currentView;
391
+ events = [];
392
+ nowLineTimer = null;
401
393
  constructor(options) {
402
- this.events = [];
403
- this.nowLineTimer = null;
404
- // ----------------------------------------------------------
405
- // Event delegation
406
- // ----------------------------------------------------------
407
- this.boundHandleClick = (e) => this.handleClick(e);
408
- this.boundHandleKeydown = (e) => this.handleKeydown(e);
409
394
  if (typeof options.container === 'string') {
410
395
  const el = document.querySelector(options.container);
411
396
  if (!el)
@@ -435,9 +420,6 @@ export class Calendar {
435
420
  this.render();
436
421
  this.attachEvents();
437
422
  }
438
- // ----------------------------------------------------------
439
- // Public API
440
- // ----------------------------------------------------------
441
423
  setView(view) {
442
424
  this.currentView = view;
443
425
  this.render();
@@ -490,9 +472,6 @@ export class Calendar {
490
472
  this.container.innerHTML = '';
491
473
  this.container.removeAttribute('data-cal');
492
474
  }
493
- // ----------------------------------------------------------
494
- // Rendering
495
- // ----------------------------------------------------------
496
475
  getTitle() {
497
476
  const { monthNames } = this.locale;
498
477
  const y = this.currentDate.getFullYear();
@@ -571,7 +550,7 @@ export class Calendar {
571
550
  const line = this.container.querySelector('.cal__now-line');
572
551
  if (line)
573
552
  line.style.top = `${CalendarLogic.nowLinePct().toFixed(3)}%`;
574
- }, 60000);
553
+ }, 60_000);
575
554
  }
576
555
  clearNowLineTimer() {
577
556
  if (this.nowLineTimer !== null) {
@@ -579,6 +558,8 @@ export class Calendar {
579
558
  this.nowLineTimer = null;
580
559
  }
581
560
  }
561
+ boundHandleClick = (e) => this.handleClick(e);
562
+ boundHandleKeydown = (e) => this.handleKeydown(e);
582
563
  attachEvents() {
583
564
  this.container.addEventListener('click', this.boundHandleClick);
584
565
  this.container.addEventListener('keydown', this.boundHandleKeydown);
package/js/carousel.d.ts CHANGED
@@ -1,8 +1,10 @@
1
+ /** Options for configuring a Carousel instance. */
1
2
  interface CarouselOptions {
2
3
  loop?: boolean;
3
4
  autoPlay?: boolean;
4
5
  autoPlayInterval?: number;
5
6
  }
7
+ /** Slide-based carousel with optional autoplay, loop, dot navigation, and touch support. */
6
8
  declare class Carousel {
7
9
  private root;
8
10
  private options;
package/js/carousel.js CHANGED
@@ -1,7 +1,18 @@
1
+ /** Slide-based carousel with optional autoplay, loop, dot navigation, and touch support. */
1
2
  class Carousel {
3
+ root;
4
+ options;
5
+ track;
6
+ slides;
7
+ slideWidth;
8
+ currentIndex;
9
+ prevButton;
10
+ nextButton;
11
+ dotsNav;
12
+ dots;
13
+ autoPlayTimer = null;
14
+ abortController = new AbortController();
2
15
  constructor(elementOrSelector, options = {}) {
3
- this.autoPlayTimer = null;
4
- this.abortController = new AbortController();
5
16
  const element = typeof elementOrSelector === 'string'
6
17
  ? document.querySelector(elementOrSelector)
7
18
  : elementOrSelector;
@@ -60,7 +71,6 @@ class Carousel {
60
71
  this.dots.push(dot);
61
72
  });
62
73
  this.root.appendChild(this.dotsNav);
63
- // Make focusable for keyboard nav
64
74
  this.root.setAttribute('tabindex', '0');
65
75
  }
66
76
  bindEvents() {
@@ -78,14 +88,12 @@ class Carousel {
78
88
  this.slideWidth = this.slides[0].getBoundingClientRect().width;
79
89
  this.moveToSlide(this.currentIndex, false);
80
90
  }, sig);
81
- // Keyboard navigation
82
91
  this.root.addEventListener('keydown', (e) => {
83
92
  if (e.key === 'ArrowLeft')
84
93
  this.moveToPrevSlide();
85
94
  if (e.key === 'ArrowRight')
86
95
  this.moveToNextSlide();
87
96
  }, sig);
88
- // Pause autoplay on hover / focus
89
97
  if (this.options.autoPlay) {
90
98
  this.root.addEventListener('mouseenter', () => this.pauseAutoPlay(), sig);
91
99
  this.root.addEventListener('mouseleave', () => this.resumeAutoPlay(), sig);
package/js/chart.d.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  export type ChartType = 'line' | 'area' | 'column' | 'bar' | 'pie';
2
2
  export type ChartCurve = 'smooth' | 'linear' | 'step';
3
+ /** A single labelled data value within a chart series. */
3
4
  export interface ChartDataPoint {
4
5
  label: string;
5
6
  value: number;
6
7
  }
8
+ /** A named data series with optional per-series colour override. */
7
9
  export interface ChartSeries {
8
10
  name: string;
9
11
  data: ChartDataPoint[];
10
12
  color?: string;
11
13
  }
14
+ /** Configuration options for a Chart instance. */
12
15
  export interface ChartOptions {
13
16
  type: ChartType;
14
17
  series: ChartSeries[];
@@ -27,6 +30,7 @@ export interface ChartOptions {
27
30
  yMax?: number;
28
31
  onPointClick?: (series: ChartSeries, point: ChartDataPoint, index: number) => void;
29
32
  }
33
+ /** SVG-based chart component supporting line, area, column, bar, and pie types. */
30
34
  declare class Chart {
31
35
  private container;
32
36
  private opts;
package/js/chart.js CHANGED
@@ -7,13 +7,16 @@ const FALLBACK_COLORS = [
7
7
  '#8B5CF6', '#06B6D4', '#F97316', '#EC4899',
8
8
  ];
9
9
  const SVG_NS = 'http://www.w3.org/2000/svg';
10
- // ─── Chart ──────────────────────────────────────────────────────────────────
10
+ /** SVG-based chart component supporting line, area, column, bar, and pie types. */
11
11
  class Chart {
12
+ container;
13
+ opts;
14
+ tooltip;
15
+ colors = [];
16
+ abortController = new AbortController();
17
+ resizeTimer = null;
18
+ resizeObserver = null;
12
19
  constructor(selector, options) {
13
- this.colors = [];
14
- this.abortController = new AbortController();
15
- this.resizeTimer = null;
16
- this.resizeObserver = null;
17
20
  const el = typeof selector === 'string'
18
21
  ? document.querySelector(selector)
19
22
  : selector;
@@ -37,7 +40,6 @@ class Chart {
37
40
  this.render();
38
41
  this.attachResizeObserver();
39
42
  }
40
- // ── Render ──────────────────────────────────────────────────────────────
41
43
  render() {
42
44
  this.abortController.abort();
43
45
  this.abortController = new AbortController();
@@ -72,7 +74,6 @@ class Chart {
72
74
  this.container.appendChild(this.buildLegend());
73
75
  }
74
76
  }
75
- // ── Line / Area ──────────────────────────────────────────────────────────
76
77
  renderLineOrArea(canvas, isArea) {
77
78
  const { series, height, showGrid, animate, yMin } = this.opts;
78
79
  if (!series.length || !series[0].data.length)
@@ -119,7 +120,6 @@ class Chart {
119
120
  });
120
121
  }
121
122
  svg.appendChild(linePath);
122
- // Data point markers
123
123
  s.data.forEach((d, i) => {
124
124
  const g = this.svgEl('g', {
125
125
  class: 'chart-point-group',
@@ -145,7 +145,6 @@ class Chart {
145
145
  });
146
146
  });
147
147
  }
148
- // ── Column ───────────────────────────────────────────────────────────────
149
148
  renderColumn(canvas) {
150
149
  const { series, height, showGrid, animate, yMin } = this.opts;
151
150
  if (!series.length || !series[0].data.length)
@@ -190,7 +189,6 @@ class Chart {
190
189
  });
191
190
  });
192
191
  }
193
- // ── Bar (horizontal) ─────────────────────────────────────────────────────
194
192
  renderBar(canvas) {
195
193
  const { series, height, animate } = this.opts;
196
194
  if (!series.length || !series[0].data.length)
@@ -206,7 +204,6 @@ class Chart {
206
204
  const numPts = labels.length;
207
205
  const numSeries = series.length;
208
206
  const svg = this.createSVG(canvas, svgW, svgH);
209
- // Vertical grid lines
210
207
  const numTicks = 5;
211
208
  for (let t = 0; t <= numTicks; t++) {
212
209
  const x = m.left + (t / numTicks) * w;
@@ -223,7 +220,6 @@ class Chart {
223
220
  label.textContent = this.fmt(xMax * t / numTicks);
224
221
  svg.appendChild(label);
225
222
  }
226
- // Category labels on Y axis
227
223
  const groupH = h / numPts;
228
224
  labels.forEach((label, i) => {
229
225
  const y = m.top + i * groupH + groupH / 2;
@@ -235,7 +231,6 @@ class Chart {
235
231
  text.textContent = label;
236
232
  svg.appendChild(text);
237
233
  });
238
- // Bars
239
234
  const innerPad = groupH * 0.18;
240
235
  const barH = Math.max(2, (groupH - innerPad) / numSeries - 2);
241
236
  series.forEach((s, si) => {
@@ -259,7 +254,6 @@ class Chart {
259
254
  });
260
255
  });
261
256
  }
262
- // ── Pie ──────────────────────────────────────────────────────────────────
263
257
  renderPie(canvas) {
264
258
  const { series, height, animate, showLegend } = this.opts;
265
259
  const s = series[0];
@@ -273,7 +267,7 @@ class Chart {
273
267
  const r = Math.min(svgW, svgH) / 2 - Math.max(m.top, m.left) - 8;
274
268
  const total = s.data.reduce((sum, d) => sum + d.value, 0);
275
269
  const svg = this.createSVG(canvas, svgW, svgH);
276
- let startAngle = -90; // start at 12 o'clock
270
+ let startAngle = -90;
277
271
  s.data.forEach((d, i) => {
278
272
  const color = this.colors[i % this.colors.length];
279
273
  const sweep = (d.value / total) * 360;
@@ -290,7 +284,6 @@ class Chart {
290
284
  const delay = i * 70;
291
285
  path.style.animationDelay = `${delay}ms`;
292
286
  }
293
- // Hover: nudge slice outward
294
287
  const { x: dx, y: dy } = this.polar(0, 0, 8, midAngle);
295
288
  path.addEventListener('mouseenter', (e) => {
296
289
  path.style.transform = `translate(${dx}px, ${dy}px)`;
@@ -310,7 +303,6 @@ class Chart {
310
303
  this.container.appendChild(this.buildPieLegend(s, total));
311
304
  }
312
305
  }
313
- // ── Axis helpers ─────────────────────────────────────────────────────────
314
306
  renderHGrid(svg, m, w, h, yMin, yMax) {
315
307
  const numTicks = 5;
316
308
  for (let i = 0; i <= numTicks; i++) {
@@ -355,7 +347,6 @@ class Chart {
355
347
  svg.appendChild(text);
356
348
  }
357
349
  }
358
- // ── Geometry helpers ─────────────────────────────────────────────────────
359
350
  buildPath(pts) {
360
351
  switch (this.opts.curve) {
361
352
  case 'linear': return this.linearPath(pts);
@@ -410,7 +401,6 @@ class Chart {
410
401
  const rad = deg * Math.PI / 180;
411
402
  return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
412
403
  }
413
- // ── Legend builders ──────────────────────────────────────────────────────
414
404
  buildHeader() {
415
405
  const el = this.div('chart-header');
416
406
  if (this.opts.title) {
@@ -454,7 +444,6 @@ class Chart {
454
444
  });
455
445
  return el;
456
446
  }
457
- // ── Tooltip ──────────────────────────────────────────────────────────────
458
447
  showTooltip(e, html) {
459
448
  this.tooltip.innerHTML = html;
460
449
  this.tooltip.classList.add('is-visible');
@@ -466,7 +455,6 @@ class Chart {
466
455
  const vh = window.innerHeight;
467
456
  let x = e.clientX + 14;
468
457
  let y = e.clientY - 36;
469
- // Keep inside viewport
470
458
  if (x + 200 > vw)
471
459
  x = e.clientX - 14 - tt.offsetWidth;
472
460
  if (y < 0)
@@ -479,7 +467,6 @@ class Chart {
479
467
  hideTooltip() {
480
468
  this.tooltip.classList.remove('is-visible');
481
469
  }
482
- // ── Event wiring ─────────────────────────────────────────────────────────
483
470
  onPoint(g, s, d, i) {
484
471
  const sig = { signal: this.abortController.signal };
485
472
  g.addEventListener('mouseenter', (e) => {
@@ -499,7 +486,6 @@ class Chart {
499
486
  rect.addEventListener('mouseleave', () => this.hideTooltip(), sig);
500
487
  rect.addEventListener('click', () => this.opts.onPointClick(s, d, i), sig);
501
488
  }
502
- // ── Color resolution ─────────────────────────────────────────────────────
503
489
  resolveColors() {
504
490
  const style = getComputedStyle(this.container);
505
491
  this.colors = (this.opts.type === 'pie' ? this.opts.series[0]?.data ?? [] : this.opts.series)
@@ -507,7 +493,6 @@ class Chart {
507
493
  const css = style.getPropertyValue(`--chart-color-${i + 1}`).trim();
508
494
  return css || FALLBACK_COLORS[i % FALLBACK_COLORS.length];
509
495
  });
510
- // Allow per-series color override (not pie)
511
496
  if (this.opts.type !== 'pie') {
512
497
  this.opts.series.forEach((s, i) => {
513
498
  if (s.color)
@@ -515,7 +500,6 @@ class Chart {
515
500
  });
516
501
  }
517
502
  }
518
- // ── DOM & SVG helpers ────────────────────────────────────────────────────
519
503
  div(className) {
520
504
  const el = document.createElement('div');
521
505
  el.className = className;
@@ -537,13 +521,12 @@ class Chart {
537
521
  return el;
538
522
  }
539
523
  fmt(v) {
540
- if (v >= 1000000)
541
- return `${(v / 1000000).toFixed(1)}M`;
542
- if (v >= 1000)
543
- return `${(v / 1000).toFixed(1)}K`;
524
+ if (v >= 1_000_000)
525
+ return `${(v / 1_000_000).toFixed(1)}M`;
526
+ if (v >= 1_000)
527
+ return `${(v / 1_000).toFixed(1)}K`;
544
528
  return v % 1 === 0 ? String(Math.round(v)) : v.toFixed(1);
545
529
  }
546
- // ── Resize ───────────────────────────────────────────────────────────────
547
530
  attachResizeObserver() {
548
531
  this.resizeObserver = new ResizeObserver(() => {
549
532
  if (this.resizeTimer)
@@ -552,7 +535,6 @@ class Chart {
552
535
  });
553
536
  this.resizeObserver.observe(this.container);
554
537
  }
555
- // ── Public API ───────────────────────────────────────────────────────────
556
538
  update(series) {
557
539
  this.opts.series = series;
558
540
  this.render();
@@ -1,4 +1,5 @@
1
1
  type SupportedLanguage = 'javascript' | 'js' | 'html' | 'css';
2
+ /** Renders syntax-highlighted code inside a container element. */
2
3
  declare class CodeViewer {
3
4
  private container;
4
5
  private code;
package/js/code-viewer.js CHANGED
@@ -1,4 +1,8 @@
1
+ /** Renders syntax-highlighted code inside a container element. */
1
2
  class CodeViewer {
3
+ container;
4
+ code;
5
+ language;
2
6
  constructor(elementOrSelector, code, language = 'javascript') {
3
7
  const element = typeof elementOrSelector === 'string'
4
8
  ? document.querySelector(elementOrSelector)
@@ -1,3 +1,4 @@
1
+ /** Definition for a single context menu item including optional submenu. */
1
2
  interface ContextMenuItemDef {
2
3
  label: string;
3
4
  icon?: string;
@@ -10,13 +11,19 @@ interface ContextMenuItemDef {
10
11
  type ContextMenuInput = ContextMenuItemDef | 'separator' | {
11
12
  group: string;
12
13
  };
14
+ interface ContextMenuOptions {
15
+ /** Path to the SVG sprite file, e.g. `'svg-icons/icons.svg'`. Required to render icons. */
16
+ spritePath?: string;
17
+ }
18
+ /** Right-click context menu with keyboard navigation and nested submenu support. */
13
19
  declare class ContextMenu {
14
20
  private items;
15
21
  private targets;
16
22
  private menuEl;
17
23
  private currentTarget;
18
24
  private abortController;
19
- constructor(selectorOrElement: string | HTMLElement | HTMLElement[], items: ContextMenuInput[]);
25
+ private spritePath;
26
+ constructor(selectorOrElement: string | HTMLElement | HTMLElement[], items: ContextMenuInput[], options?: ContextMenuOptions);
20
27
  private init;
21
28
  private open;
22
29
  private close;
@@ -28,4 +35,4 @@ declare class ContextMenu {
28
35
  private activateFocused;
29
36
  destroy(): void;
30
37
  }
31
- export { ContextMenu, type ContextMenuInput, type ContextMenuItemDef };
38
+ export { ContextMenu, type ContextMenuInput, type ContextMenuItemDef, type ContextMenuOptions };