@dodlhuat/basix 1.2.0 → 1.2.1

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 (93) hide show
  1. package/README.md +56 -1
  2. package/css/accordion.scss +86 -87
  3. package/css/alert.scss +137 -137
  4. package/css/button.scss +48 -0
  5. package/css/calendar.scss +957 -0
  6. package/css/card.scss +65 -65
  7. package/css/chart.scss +270 -157
  8. package/css/chat-bubbles.scss +134 -68
  9. package/css/chips.scss +109 -19
  10. package/css/colors.scss +32 -32
  11. package/css/datepicker.scss +336 -336
  12. package/css/defaults.scss +90 -90
  13. package/css/docs.scss +529 -0
  14. package/css/editor.scss +36 -0
  15. package/css/file-uploader.scss +1 -1
  16. package/css/flyout-menu.scss +361 -361
  17. package/css/form.scss +0 -15
  18. package/css/gallery.scss +65 -6
  19. package/css/grid.scss +41 -40
  20. package/css/group-picker.scss +345 -0
  21. package/css/guitar-chords.css +250 -250
  22. package/css/icons.scss +330 -330
  23. package/css/parameters.scss +3 -3
  24. package/css/placeholder.scss +33 -33
  25. package/css/popover.scss +206 -0
  26. package/css/progress.scss +76 -32
  27. package/css/properties.scss +51 -36
  28. package/css/push-menu.scss +302 -174
  29. package/css/reset.scss +39 -39
  30. package/css/scrollbar.scss +62 -5
  31. package/css/sidebar-nav.scss +92 -0
  32. package/css/spinner.scss +65 -65
  33. package/css/stepper.scss +48 -12
  34. package/css/style.css +3159 -254
  35. package/css/style.css.map +1 -1
  36. package/css/style.min.css +1 -1
  37. package/css/style.scss +51 -45
  38. package/css/table.scss +199 -199
  39. package/css/tabs.scss +154 -123
  40. package/css/timeline.scss +83 -38
  41. package/css/timepicker.scss +100 -5
  42. package/css/toast.scss +81 -81
  43. package/css/virtual-dropdown.scss +35 -29
  44. package/js/calendar.js +532 -0
  45. package/js/calendar.ts +706 -0
  46. package/js/chart.js +573 -257
  47. package/js/chart.ts +692 -0
  48. package/js/code-viewer.js +10 -10
  49. package/js/code-viewer.ts +188 -188
  50. package/js/datepicker.ts +627 -627
  51. package/js/docs-nav.js +204 -0
  52. package/js/dropdown.ts +179 -179
  53. package/js/editor.js +50 -6
  54. package/js/editor.ts +483 -444
  55. package/js/file-uploader.js +1 -0
  56. package/js/file-uploader.ts +1 -0
  57. package/js/flyout-menu.js +14 -14
  58. package/js/flyout-menu.ts +249 -249
  59. package/js/form-builder.js +106 -106
  60. package/js/gallery.js +14 -8
  61. package/js/gallery.ts +245 -236
  62. package/js/group-picker.js +342 -0
  63. package/js/group-picker.ts +447 -0
  64. package/js/guitar-chords.js +268 -268
  65. package/js/lazy-loader.js +121 -121
  66. package/js/modal.ts +166 -166
  67. package/js/popover.js +163 -0
  68. package/js/popover.ts +219 -0
  69. package/js/position.js +108 -0
  70. package/js/position.ts +111 -0
  71. package/js/push-menu.js +113 -0
  72. package/js/push-menu.ts +284 -145
  73. package/js/request.js +50 -50
  74. package/js/scroll.ts +47 -47
  75. package/js/scrollbar.js +13 -0
  76. package/js/scrollbar.ts +324 -307
  77. package/js/select.ts +216 -216
  78. package/js/sidebar-nav.js +41 -0
  79. package/js/sidebar-nav.ts +66 -0
  80. package/js/table.ts +452 -452
  81. package/js/tabs.ts +279 -279
  82. package/js/theme.js +17 -6
  83. package/js/theme.ts +234 -224
  84. package/js/toast.ts +137 -137
  85. package/js/tooltip.js +6 -60
  86. package/js/tooltip.ts +184 -251
  87. package/js/tsconfig.json +18 -18
  88. package/js/utils.ts +83 -83
  89. package/js/virtual-dropdown.js +25 -25
  90. package/js/virtual-dropdown.ts +365 -365
  91. package/package.json +39 -39
  92. package/js/index.js +0 -816
  93. package/js/index.ts +0 -987
package/js/gallery.ts CHANGED
@@ -1,236 +1,245 @@
1
- interface ImageData {
2
- src: string;
3
- title: string;
4
- desc: string;
5
- }
6
-
7
- interface MasonryGalleryOptions {
8
- minColumnWidth?: number;
9
- scrollThreshold?: number;
10
- loaderSelector?: string;
11
- reload?: number;
12
- fetchFunction?: () => Promise<ImageData[]>;
13
- }
14
-
15
- class MasonryGallery {
16
- private container: HTMLElement;
17
- private readonly loader: HTMLElement | null;
18
- private options: Required<Omit<MasonryGalleryOptions, "loaderSelector">>;
19
- private columns: HTMLDivElement[] = [];
20
- private isFetching: boolean = false;
21
- private resizeObserver: ResizeObserver | null = null;
22
- private abortController: AbortController | null = null;
23
- private reloaded = 0;
24
-
25
- constructor(containerId: string, options: MasonryGalleryOptions = {}) {
26
- const container = document.getElementById(containerId);
27
- if (!container) {
28
- throw new Error(`Container with id "${containerId}" not found`);
29
- }
30
- this.container = container;
31
- this.loader = document.querySelector(options.loaderSelector || ".loader");
32
-
33
- this.options = {
34
- minColumnWidth: options.minColumnWidth ?? 250,
35
- scrollThreshold: options.scrollThreshold ?? 100,
36
- reload: 2,
37
- fetchFunction: options.fetchFunction ?? this.fetchMockImages,
38
- };
39
-
40
- this.init();
41
- }
42
-
43
- private init(): void {
44
- this.setupLayout();
45
- this.loadMoreImages();
46
- this.addEventListeners();
47
- }
48
-
49
- private setupLayout(): void {
50
- const containerWidth = this.container.getBoundingClientRect().width;
51
- const numColumns = Math.max(
52
- 1,
53
- Math.floor(containerWidth / this.options.minColumnWidth),
54
- );
55
-
56
- if (this.columns.length !== numColumns) {
57
- this.container.innerHTML = "";
58
- this.columns = [];
59
-
60
- for (let i = 0; i < numColumns; i++) {
61
- const col = document.createElement("div");
62
- col.className = "masonry-column";
63
- this.container.appendChild(col);
64
- this.columns.push(col);
65
- }
66
- }
67
- }
68
-
69
- private addEventListeners(): void {
70
- let resizeTimeout: number;
71
- window.addEventListener("resize", () => {
72
- clearTimeout(resizeTimeout);
73
- resizeTimeout = setTimeout(() => {
74
- this.reLayout();
75
- }, 200);
76
- });
77
-
78
- this.abortController = new AbortController();
79
- window.addEventListener("scroll", this.handleScroll, {
80
- passive: true,
81
- signal: this.abortController.signal,
82
- });
83
- }
84
-
85
- private reLayout(): void {
86
- const items: HTMLElement[] = [];
87
- this.columns.forEach((col) => {
88
- Array.from(col.children).forEach((child) => {
89
- items.push(child as HTMLElement);
90
- });
91
- col.innerHTML = "";
92
- });
93
-
94
- const availableWidth = Math.min(1200, window.innerWidth - 40);
95
- const numColumns = Math.max(
96
- 1,
97
- Math.floor(availableWidth / this.options.minColumnWidth),
98
- );
99
-
100
- if (this.columns.length !== numColumns) {
101
- this.container.innerHTML = "";
102
- this.columns = [];
103
-
104
- for (let i = 0; i < numColumns; i++) {
105
- const col = document.createElement("div");
106
- col.className = "masonry-column";
107
- this.container.appendChild(col);
108
- this.columns.push(col);
109
- }
110
- }
111
-
112
- items.forEach((item) => {
113
- this.addToShortestColumn(item);
114
- });
115
- }
116
-
117
- private handleScroll = (): void => {
118
- if (this.isFetching) return;
119
-
120
- const rect = this.container.getBoundingClientRect();
121
- if (rect.bottom > 0 && rect.bottom <= window.innerHeight + this.options.scrollThreshold) {
122
- this.loadMoreImages();
123
- }
124
- };
125
-
126
- private async loadMoreImages(isAutoFill = false): Promise<void> {
127
- if (!isAutoFill) this.reloaded++;
128
- if (this.options.reload > 0 && this.reloaded > this.options.reload) {
129
- console.warn("Maximum reload limit reached.");
130
- return;
131
- }
132
- if (this.isFetching) return;
133
-
134
- this.isFetching = true;
135
- this.toggleLoader(true);
136
-
137
- try {
138
- const newImages = await this.options.fetchFunction();
139
- this.renderImages(newImages);
140
- } catch (error) {
141
- throw new Error("Error loading images: " + error);
142
- } finally {
143
- this.isFetching = false;
144
- this.toggleLoader(false);
145
- // If the rendered content doesn't fill the viewport, auto-load the next
146
- // batch without waiting for a scroll event (multi-column layout is shorter)
147
- requestAnimationFrame(() => {
148
- const rect = this.container.getBoundingClientRect();
149
- if (rect.bottom <= window.innerHeight + this.options.scrollThreshold) {
150
- this.loadMoreImages(true);
151
- }
152
- });
153
- }
154
- }
155
-
156
- private toggleLoader(show: boolean): void {
157
- if (this.loader) {
158
- this.loader.classList.toggle("hidden", !show);
159
- }
160
- }
161
-
162
- private fetchMockImages = (): Promise<ImageData[]> => {
163
- throw Error("Method not implemented.");
164
- };
165
-
166
- private renderImages(imageDataList: ImageData[]): void {
167
- imageDataList.forEach((data) => {
168
- const item = this.createCard(data);
169
- this.addToShortestColumn(item);
170
-
171
- requestAnimationFrame(() => {
172
- const img = item.querySelector("img");
173
- if (img) {
174
- img.addEventListener("load", () => img.classList.add("loaded"), {
175
- once: true,
176
- });
177
- if (img.complete) {
178
- img.classList.add("loaded");
179
- }
180
- }
181
- });
182
- });
183
- }
184
-
185
- private createCard(data: ImageData): HTMLDivElement {
186
- const div = document.createElement("div");
187
- div.className = "masonry-item";
188
-
189
- div.innerHTML = `
190
- <img src="${this.escapeHtml(data.src)}" alt="${this.escapeHtml(data.title)}" loading="lazy">
191
- <div class="masonry-item-info">
192
- <h3 class="masonry-item-title">${this.escapeHtml(data.title)}</h3>
193
- <p class="masonry-item-desc">${this.escapeHtml(data.desc)}</p>
194
- </div>
195
- `;
196
-
197
- return div;
198
- }
199
-
200
- private escapeHtml(text: string): string {
201
- const div = document.createElement("div");
202
- div.textContent = text;
203
- return div.innerHTML;
204
- }
205
-
206
- private addToShortestColumn(element: HTMLElement): void {
207
- if (this.columns.length === 0) return;
208
-
209
- let shortestCol = this.columns[0];
210
- let minHeight = shortestCol.offsetHeight;
211
-
212
- for (let i = 1; i < this.columns.length; i++) {
213
- const h = this.columns[i].offsetHeight;
214
- if (h < minHeight) {
215
- minHeight = h;
216
- shortestCol = this.columns[i];
217
- }
218
- }
219
-
220
- shortestCol.appendChild(element);
221
- }
222
-
223
- public destroy(): void {
224
- if (this.resizeObserver) {
225
- this.resizeObserver.disconnect();
226
- this.resizeObserver = null;
227
- }
228
-
229
- if (this.abortController) {
230
- this.abortController.abort();
231
- this.abortController = null;
232
- }
233
- }
234
- }
235
-
236
- export { MasonryGallery, ImageData };
1
+ interface ImageData {
2
+ src: string;
3
+ title: string;
4
+ desc: string;
5
+ }
6
+
7
+ interface MasonryGalleryOptions {
8
+ minColumnWidth?: number;
9
+ scrollThreshold?: number;
10
+ loaderSelector?: string;
11
+ reload?: number;
12
+ fetchFunction?: () => Promise<ImageData[]>;
13
+ }
14
+
15
+ class MasonryGallery {
16
+ private container: HTMLElement;
17
+ private readonly loader: HTMLElement | null;
18
+ private options: Required<Omit<MasonryGalleryOptions, "loaderSelector">>;
19
+ private columns: HTMLDivElement[] = [];
20
+ private isFetching: boolean = false;
21
+ private resizeObserver: ResizeObserver | null = null;
22
+ private abortController: AbortController | null = null;
23
+ private reloaded = 0;
24
+
25
+ constructor(containerId: string, options: MasonryGalleryOptions = {}) {
26
+ const container = document.getElementById(containerId);
27
+ if (!container) {
28
+ throw new Error(`Container with id "${containerId}" not found`);
29
+ }
30
+ this.container = container;
31
+ this.loader = document.querySelector(options.loaderSelector || ".loader");
32
+
33
+ this.options = {
34
+ minColumnWidth: options.minColumnWidth ?? 250,
35
+ scrollThreshold: options.scrollThreshold ?? 100,
36
+ reload: 2,
37
+ fetchFunction: options.fetchFunction ?? this.fetchMockImages,
38
+ };
39
+
40
+ this.init();
41
+ }
42
+
43
+ private init(): void {
44
+ this.setupLayout();
45
+ this.loadMoreImages();
46
+ this.addEventListeners();
47
+ }
48
+
49
+ private setupLayout(): void {
50
+ const containerWidth = this.container.getBoundingClientRect().width;
51
+ const numColumns = Math.max(
52
+ 1,
53
+ Math.floor(containerWidth / this.options.minColumnWidth),
54
+ );
55
+
56
+ if (this.columns.length !== numColumns) {
57
+ this.container.innerHTML = "";
58
+ this.columns = [];
59
+
60
+ for (let i = 0; i < numColumns; i++) {
61
+ const col = document.createElement("div");
62
+ col.className = "masonry-column";
63
+ this.container.appendChild(col);
64
+ this.columns.push(col);
65
+ }
66
+ }
67
+ }
68
+
69
+ private addEventListeners(): void {
70
+ let resizeTimeout: number;
71
+ window.addEventListener("resize", () => {
72
+ clearTimeout(resizeTimeout);
73
+ resizeTimeout = setTimeout(() => {
74
+ this.reLayout();
75
+ }, 200);
76
+ });
77
+
78
+ this.abortController = new AbortController();
79
+ window.addEventListener("scroll", this.handleScroll, {
80
+ passive: true,
81
+ signal: this.abortController.signal,
82
+ });
83
+ }
84
+
85
+ private reLayout(): void {
86
+ const items: HTMLElement[] = [];
87
+ this.columns.forEach((col) => {
88
+ Array.from(col.children).forEach((child) => {
89
+ items.push(child as HTMLElement);
90
+ });
91
+ col.innerHTML = "";
92
+ });
93
+
94
+ const availableWidth = Math.min(1200, window.innerWidth - 40);
95
+ const numColumns = Math.max(
96
+ 1,
97
+ Math.floor(availableWidth / this.options.minColumnWidth),
98
+ );
99
+
100
+ if (this.columns.length !== numColumns) {
101
+ this.container.innerHTML = "";
102
+ this.columns = [];
103
+
104
+ for (let i = 0; i < numColumns; i++) {
105
+ const col = document.createElement("div");
106
+ col.className = "masonry-column";
107
+ this.container.appendChild(col);
108
+ this.columns.push(col);
109
+ }
110
+ }
111
+
112
+ items.forEach((item) => {
113
+ this.addToShortestColumn(item);
114
+ });
115
+ }
116
+
117
+ private handleScroll = (): void => {
118
+ if (this.isFetching) return;
119
+
120
+ const rect = this.container.getBoundingClientRect();
121
+ if (rect.bottom > 0 && rect.bottom <= window.innerHeight + this.options.scrollThreshold) {
122
+ this.loadMoreImages();
123
+ }
124
+ };
125
+
126
+ private async loadMoreImages(isAutoFill = false): Promise<void> {
127
+ if (!isAutoFill) this.reloaded++;
128
+ if (this.options.reload > 0 && this.reloaded > this.options.reload) {
129
+ console.warn("Maximum reload limit reached.");
130
+ return;
131
+ }
132
+ if (this.isFetching) return;
133
+
134
+ this.isFetching = true;
135
+ this.toggleLoader(true);
136
+
137
+ try {
138
+ const newImages = await this.options.fetchFunction();
139
+ this.renderImages(newImages);
140
+ } catch (error) {
141
+ throw new Error("Error loading images: " + error);
142
+ } finally {
143
+ this.isFetching = false;
144
+ this.toggleLoader(false);
145
+ // If the rendered content doesn't fill the viewport, auto-load the next
146
+ // batch without waiting for a scroll event (multi-column layout is shorter)
147
+ requestAnimationFrame(() => {
148
+ const rect = this.container.getBoundingClientRect();
149
+ if (rect.bottom <= window.innerHeight + this.options.scrollThreshold) {
150
+ this.loadMoreImages(true);
151
+ }
152
+ });
153
+ }
154
+ }
155
+
156
+ private toggleLoader(show: boolean): void {
157
+ if (this.loader) {
158
+ this.loader.classList.toggle("hidden", !show);
159
+ }
160
+ }
161
+
162
+ private fetchMockImages = (): Promise<ImageData[]> => {
163
+ throw Error("Method not implemented.");
164
+ };
165
+
166
+ private renderImages(imageDataList: ImageData[]): void {
167
+ // Sort columns by current height so we start filling from the shortest.
168
+ // Then round-robin across them — this avoids the problem where unloaded
169
+ // images (0 height) cause offsetHeight-based distribution to pile all
170
+ // new items into a single column.
171
+ const sorted = [...this.columns].sort(
172
+ (a, b) => a.offsetHeight - b.offsetHeight,
173
+ );
174
+
175
+ imageDataList.forEach((data, index) => {
176
+ const item = this.createCard(data);
177
+ const col = sorted[index % sorted.length];
178
+ col.appendChild(item);
179
+
180
+ requestAnimationFrame(() => {
181
+ const img = item.querySelector("img");
182
+ if (img) {
183
+ img.addEventListener("load", () => img.classList.add("loaded"), {
184
+ once: true,
185
+ });
186
+ if (img.complete) {
187
+ img.classList.add("loaded");
188
+ }
189
+ }
190
+ });
191
+ });
192
+ }
193
+
194
+ private createCard(data: ImageData): HTMLDivElement {
195
+ const div = document.createElement("div");
196
+ div.className = "masonry-item";
197
+
198
+ div.innerHTML = `
199
+ <img src="${this.escapeHtml(data.src)}" alt="${this.escapeHtml(data.title)}" loading="lazy">
200
+ <div class="masonry-item-info">
201
+ <h3 class="masonry-item-title">${this.escapeHtml(data.title)}</h3>
202
+ <p class="masonry-item-desc">${this.escapeHtml(data.desc)}</p>
203
+ </div>
204
+ `;
205
+
206
+ return div;
207
+ }
208
+
209
+ private escapeHtml(text: string): string {
210
+ const div = document.createElement("div");
211
+ div.textContent = text;
212
+ return div.innerHTML;
213
+ }
214
+
215
+ private addToShortestColumn(element: HTMLElement): void {
216
+ if (this.columns.length === 0) return;
217
+
218
+ let shortestCol = this.columns[0];
219
+ let minHeight = shortestCol.offsetHeight;
220
+
221
+ for (let i = 1; i < this.columns.length; i++) {
222
+ const h = this.columns[i].offsetHeight;
223
+ if (h < minHeight) {
224
+ minHeight = h;
225
+ shortestCol = this.columns[i];
226
+ }
227
+ }
228
+
229
+ shortestCol.appendChild(element);
230
+ }
231
+
232
+ public destroy(): void {
233
+ if (this.resizeObserver) {
234
+ this.resizeObserver.disconnect();
235
+ this.resizeObserver = null;
236
+ }
237
+
238
+ if (this.abortController) {
239
+ this.abortController.abort();
240
+ this.abortController = null;
241
+ }
242
+ }
243
+ }
244
+
245
+ export { MasonryGallery, ImageData };