@dodlhuat/basix 1.3.2 → 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.
- package/README.md +13 -7
- package/css/accordion.scss +0 -5
- package/css/badge.scss +1 -6
- package/css/bottom-sheet.scss +3 -8
- package/css/breadcrumb.scss +6 -15
- package/css/button.scss +2 -1
- package/css/calendar.scss +0 -54
- package/css/card.scss +0 -5
- package/css/carousel.scss +0 -3
- package/css/chart.scss +0 -25
- package/css/chat-bubbles.scss +0 -15
- package/css/checkbox.scss +3 -2
- package/css/chips.scss +3 -7
- package/css/code-viewer.scss +1 -5
- package/css/context-menu.scss +4 -6
- package/css/datepicker.scss +4 -7
- package/css/docs.scss +0 -4
- package/css/dropdown.scss +1 -1
- package/css/editor.scss +1 -23
- package/css/file-uploader.scss +2 -2
- package/css/flyout-menu.scss +66 -44
- package/css/form.scss +0 -28
- package/css/gallery.scss +2 -3
- package/css/group-picker.scss +5 -35
- package/css/icons.scss +0 -3
- package/css/lightbox.scss +2 -4
- package/css/mixins.scss +8 -0
- package/css/modal.scss +3 -3
- package/css/parameters.scss +6 -1
- package/css/popover.scss +3 -15
- package/css/progress.scss +0 -6
- package/css/push-menu.scss +3 -28
- package/css/radiobutton.scss +2 -1
- package/css/range-slider.scss +1 -7
- package/css/scrollbar.scss +2 -6
- package/css/sidebar-nav.scss +0 -12
- package/css/stepper.scss +0 -4
- package/css/style.css +63 -68
- package/css/style.css.map +1 -1
- package/css/style.min.css +1 -1
- package/css/style.min.css.map +1 -1
- package/css/style.scss +1 -1
- package/css/table.scss +0 -4
- package/css/tabs.scss +0 -2
- package/css/timeline.scss +1 -13
- package/css/timepicker.scss +6 -7
- package/css/toast.scss +1 -1
- package/css/tooltip.scss +1 -5
- package/css/tree.scss +1 -1
- package/css/typography.scss +3 -3
- package/css/virtual-dropdown.scss +3 -28
- package/js/bottom-sheet.d.ts +3 -1
- package/js/bottom-sheet.js +26 -27
- package/js/calendar.d.ts +7 -0
- package/js/calendar.js +14 -33
- package/js/carousel.d.ts +2 -0
- package/js/carousel.js +13 -5
- package/js/chart.d.ts +4 -0
- package/js/chart.js +13 -31
- package/js/code-viewer.d.ts +1 -0
- package/js/code-viewer.js +4 -0
- package/js/context-menu.d.ts +9 -2
- package/js/context-menu.js +17 -14
- package/js/datepicker.d.ts +4 -0
- package/js/datepicker.js +26 -11
- package/js/dropdown.d.ts +3 -3
- package/js/dropdown.js +6 -9
- package/js/editor.d.ts +1 -0
- package/js/editor.js +9 -3
- package/js/file-uploader.d.ts +4 -0
- package/js/file-uploader.js +52 -43
- package/js/flyout-menu.d.ts +5 -3
- package/js/flyout-menu.js +23 -46
- package/js/gallery.d.ts +3 -0
- package/js/gallery.js +22 -24
- package/js/group-picker.d.ts +5 -0
- package/js/group-picker.js +12 -17
- package/js/lightbox.d.ts +3 -0
- package/js/lightbox.js +12 -6
- package/js/modal.d.ts +3 -1
- package/js/modal.js +14 -11
- package/js/popover.d.ts +2 -0
- package/js/popover.js +26 -30
- package/js/position.d.ts +2 -0
- package/js/position.js +1 -5
- package/js/push-menu.d.ts +2 -0
- package/js/push-menu.js +22 -35
- package/js/range-slider.d.ts +1 -0
- package/js/range-slider.js +5 -3
- package/js/scroll.d.ts +2 -0
- package/js/scroll.js +1 -0
- package/js/scrollbar.d.ts +2 -0
- package/js/scrollbar.js +24 -36
- package/js/select.d.ts +1 -0
- package/js/select.js +5 -10
- package/js/sidebar-nav.d.ts +2 -0
- package/js/sidebar-nav.js +8 -0
- package/js/stepper.d.ts +2 -0
- package/js/stepper.js +7 -1
- package/js/table.d.ts +4 -0
- package/js/table.js +15 -22
- package/js/tabs.d.ts +2 -0
- package/js/tabs.js +6 -14
- package/js/theme.d.ts +1 -0
- package/js/theme.js +5 -13
- package/js/timepicker.d.ts +3 -0
- package/js/timepicker.js +81 -67
- package/js/toast.d.ts +3 -0
- package/js/toast.js +24 -15
- package/js/tooltip.d.ts +2 -0
- package/js/tooltip.js +21 -19
- package/js/tree.d.ts +3 -0
- package/js/tree.js +13 -0
- package/js/utils.d.ts +1 -3
- package/js/utils.js +0 -3
- package/js/virtual-dropdown.d.ts +3 -0
- package/js/virtual-dropdown.js +25 -0
- package/package.json +2 -2
package/js/bottom-sheet.js
CHANGED
|
@@ -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
|
-
},
|
|
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
|
-
|
|
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;
|
|
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 >=
|
|
541
|
-
return `${(v /
|
|
542
|
-
if (v >=
|
|
543
|
-
return `${(v /
|
|
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();
|
package/js/code-viewer.d.ts
CHANGED
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)
|
package/js/context-menu.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 };
|