@kupola/kupola 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.
- package/README.md +3 -3
- package/css/kupola.css +3 -2
- package/css/table.css +74 -0
- package/dist/css/kupola.css +12 -1
- package/dist/css/kupola.min.css +1 -1
- package/dist/kupola.cjs.js +1800 -3609
- package/dist/kupola.cjs.js.map +1 -1
- package/dist/kupola.css +100 -3
- package/dist/kupola.esm.js +1791 -3610
- package/dist/kupola.esm.js.map +1 -1
- package/dist/kupola.min.css +1 -1
- package/dist/kupola.min.js +1 -1
- package/dist/kupola.min.js.map +1 -1
- package/dist/kupola.umd.js +1800 -3609
- package/dist/kupola.umd.js.map +1 -1
- package/dist/types/kupola.d.ts +41 -25
- package/js/calendar.js +3 -10
- package/js/carousel.js +3 -10
- package/js/collapse.js +3 -10
- package/js/color-picker.js +5 -16
- package/js/component.js +4 -18
- package/js/countdown.js +3 -10
- package/js/data-bind.js +56 -36
- package/js/datepicker.js +16 -24
- package/js/depends.js +27 -16
- package/js/dialog.js +1 -5
- package/js/drawer.js +3 -11
- package/js/dropdown.js +11 -18
- package/js/dynamic-tags.js +3 -10
- package/js/fileupload.js +10 -12
- package/js/form.js +26 -20
- package/js/global-events.js +1 -14
- package/js/heatmap.js +3 -12
- package/js/i18n.js +1 -14
- package/js/icons.js +2 -2
- package/js/image-preview.js +1 -7
- package/js/initializer.js +0 -43
- package/js/kupola-core.js +1 -13
- package/js/kupola-lifecycle.js +1 -9
- package/js/message.js +1 -6
- package/js/modal.js +22 -20
- package/js/notification.js +1 -6
- package/js/numberinput.js +3 -10
- package/js/pagination.js +0 -5
- package/js/registry.js +0 -4
- package/js/select.js +5 -16
- package/js/slide-captcha.js +8 -21
- package/js/slider.js +3 -10
- package/js/statcard.js +3 -11
- package/js/table.js +240 -262
- package/js/tag.js +3 -10
- package/js/theme.js +1 -12
- package/js/timepicker.js +6 -19
- package/js/tooltip.js +3 -10
- package/js/utils.js +281 -1439
- package/js/validation.js +2 -7
- package/js/virtual-list.js +13 -38
- package/js/web-components.js +0 -9
- package/package.json +10 -5
- package/scripts/build-css.cjs +3 -2
- package/types/kupola.d.ts +41 -25
- package/version.json +10 -0
- /package/css/{colors_and_type.css → colors-and-type.css} +0 -0
package/dist/types/kupola.d.ts
CHANGED
|
@@ -468,6 +468,18 @@ declare class KupolaUtils {
|
|
|
468
468
|
|
|
469
469
|
declare const kupolaUtils: KupolaUtils;
|
|
470
470
|
|
|
471
|
+
declare const stringUtils: KupolaUtils['string'];
|
|
472
|
+
declare const arrayUtils: KupolaUtils['array'];
|
|
473
|
+
declare const objectUtils: KupolaUtils['object'];
|
|
474
|
+
declare const numberUtils: KupolaUtils['number'];
|
|
475
|
+
declare const dateUtils: KupolaUtils['date'];
|
|
476
|
+
declare const validatorUtils: KupolaUtils['validator'];
|
|
477
|
+
declare const cryptoUtils: KupolaUtils['crypto'];
|
|
478
|
+
declare const preloadUtils: KupolaUtils['preload'];
|
|
479
|
+
|
|
480
|
+
declare function debounce<T extends (...args: unknown[]) => void>(func: T, wait: number, options?: DebounceOptions): T;
|
|
481
|
+
declare function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number, options?: ThrottleOptions): T;
|
|
482
|
+
|
|
471
483
|
declare interface FormState {
|
|
472
484
|
valid: boolean;
|
|
473
485
|
errors: Record<string, string>;
|
|
@@ -681,14 +693,14 @@ declare class KupolaModalElement extends HTMLElement {
|
|
|
681
693
|
close(): void;
|
|
682
694
|
}
|
|
683
695
|
|
|
684
|
-
declare function createModal(options?: Record<string, unknown>):
|
|
685
|
-
declare function confirmModal(options: Record<string, unknown> | string):
|
|
686
|
-
declare function alertModal(options: Record<string, unknown> | string):
|
|
696
|
+
declare function createModal(options?: Record<string, unknown>): Modal;
|
|
697
|
+
declare function confirmModal(options: Record<string, unknown> | string): Modal;
|
|
698
|
+
declare function alertModal(options: Record<string, unknown> | string): Modal;
|
|
687
699
|
|
|
688
700
|
declare function cleanupAllDropdowns(): void;
|
|
689
701
|
declare function cleanupAllSlideCaptchas(): void;
|
|
690
702
|
|
|
691
|
-
declare function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function
|
|
703
|
+
declare function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function>; subscribe: (callback: Function) => { unsubscribe: () => void } };
|
|
692
704
|
|
|
693
705
|
declare class Modal {
|
|
694
706
|
constructor(element: HTMLElement, options?: Record<string, unknown>);
|
|
@@ -720,7 +732,7 @@ interface KupolaHttpResponse {
|
|
|
720
732
|
statusText?: string;
|
|
721
733
|
headers: Record<string, string>;
|
|
722
734
|
url?: string;
|
|
723
|
-
json(): Promise<
|
|
735
|
+
json(): Promise<unknown>;
|
|
724
736
|
text(): Promise<string>;
|
|
725
737
|
}
|
|
726
738
|
|
|
@@ -816,16 +828,16 @@ declare function clearCache(): void;
|
|
|
816
828
|
// Table & Pagination Components
|
|
817
829
|
// ============================================================
|
|
818
830
|
|
|
819
|
-
interface KupolaTableColumn {
|
|
831
|
+
interface KupolaTableColumn<T = Record<string, unknown>> {
|
|
820
832
|
key: string;
|
|
821
833
|
title: string;
|
|
822
834
|
width?: string | number;
|
|
823
835
|
minWidth?: string | number;
|
|
824
836
|
align?: 'left' | 'center' | 'right';
|
|
825
837
|
sortable?: boolean;
|
|
826
|
-
sorter?: (a:
|
|
838
|
+
sorter?: (a: T, b: T, order?: string) => number;
|
|
827
839
|
fixed?: 'left' | 'right';
|
|
828
|
-
render?: (value: unknown, row:
|
|
840
|
+
render?: (value: unknown, row: T, index?: number) => string | HTMLElement;
|
|
829
841
|
editable?: boolean;
|
|
830
842
|
editType?: string;
|
|
831
843
|
editOptions?: Array<{ value: string; label: string }>;
|
|
@@ -842,8 +854,8 @@ interface KupolaTableVirtualScrollOptions {
|
|
|
842
854
|
maxHeight?: string;
|
|
843
855
|
}
|
|
844
856
|
|
|
845
|
-
interface KupolaTableOptions {
|
|
846
|
-
columns: KupolaTableColumn[];
|
|
857
|
+
interface KupolaTableOptions<T = Record<string, unknown>> {
|
|
858
|
+
columns: KupolaTableColumn<T>[];
|
|
847
859
|
rowKey?: string;
|
|
848
860
|
striped?: boolean;
|
|
849
861
|
bordered?: boolean;
|
|
@@ -858,31 +870,31 @@ interface KupolaTableOptions {
|
|
|
858
870
|
showExport?: boolean;
|
|
859
871
|
showPageSize?: boolean;
|
|
860
872
|
selection?: 'checkbox' | 'radio';
|
|
861
|
-
expandable?: (row:
|
|
873
|
+
expandable?: (row: T) => string | HTMLElement;
|
|
862
874
|
editable?: boolean;
|
|
863
875
|
resizable?: boolean;
|
|
864
876
|
draggable?: boolean;
|
|
865
877
|
multiSort?: boolean;
|
|
866
878
|
tree?: KupolaTableTreeOptions;
|
|
867
879
|
virtualScroll?: KupolaTableVirtualScrollOptions;
|
|
868
|
-
mergeCells?: (data:
|
|
880
|
+
mergeCells?: (data: T[]) => Array<{ row: number; col: number; rowSpan: number; colSpan: number }>;
|
|
869
881
|
onSort?: (sorts: Array<{ key: string; order: string }>) => void;
|
|
870
882
|
onFilter?: (text: string) => void;
|
|
871
|
-
onRowClick?: (row:
|
|
883
|
+
onRowClick?: (row: T, index: number, event: Event) => void;
|
|
872
884
|
onPageChange?: (page: number, pageSize: number) => void;
|
|
873
|
-
onSelect?: (selectedKeys: unknown[], selectedRows:
|
|
885
|
+
onSelect?: (selectedKeys: unknown[], selectedRows: T[]) => void;
|
|
874
886
|
onExpand?: (key: unknown, isExpanded: boolean) => void;
|
|
875
|
-
onEditSave?: (row:
|
|
876
|
-
onEditCancel?: (editingCell: unknown) => void;
|
|
887
|
+
onEditSave?: (row: T, colKey: string, newValue: unknown, allData: T[]) => void;
|
|
888
|
+
onEditCancel?: (editingCell: { row: T; colKey: string; value: unknown }) => void;
|
|
877
889
|
onColumnResize?: (colKey: string, newWidth: number) => void;
|
|
878
|
-
onRowDragEnd?: (movedRow:
|
|
890
|
+
onRowDragEnd?: (movedRow: T, fromIndex: number, toIndex: number, newData: T[]) => void;
|
|
879
891
|
}
|
|
880
892
|
|
|
881
|
-
declare class KupolaTable {
|
|
882
|
-
constructor(element: HTMLElement | string, options?: KupolaTableOptions);
|
|
883
|
-
setData(data:
|
|
893
|
+
declare class KupolaTable<T = Record<string, unknown>> {
|
|
894
|
+
constructor(element: HTMLElement | string, options?: KupolaTableOptions<T>);
|
|
895
|
+
setData(data: T[] | { value: T[] | null }): void;
|
|
884
896
|
setLoading(loading: boolean): void;
|
|
885
|
-
setColumns(columns: KupolaTableColumn[]): void;
|
|
897
|
+
setColumns(columns: KupolaTableColumn<T>[]): void;
|
|
886
898
|
setPageSize(size: number): void;
|
|
887
899
|
getSortState(): { key: string | null; order: string | null } | Array<{ key: string; order: string }>;
|
|
888
900
|
getPage(): { current: number; pageSize: number; total: number };
|
|
@@ -894,11 +906,11 @@ declare class KupolaTable {
|
|
|
894
906
|
selectRow(key: unknown): void;
|
|
895
907
|
deselectRow(key: unknown): void;
|
|
896
908
|
getSelectedKeys(): unknown[];
|
|
897
|
-
getSelectedRows():
|
|
909
|
+
getSelectedRows(): T[];
|
|
898
910
|
destroy(): void;
|
|
899
911
|
}
|
|
900
912
|
|
|
901
|
-
declare function initTable(element: HTMLElement | string, options?: KupolaTableOptions): KupolaTable
|
|
913
|
+
declare function initTable<T = Record<string, unknown>>(element: HTMLElement | string, options?: KupolaTableOptions<T>): KupolaTable<T>;
|
|
902
914
|
declare function initAllTables(): KupolaTable[];
|
|
903
915
|
|
|
904
916
|
interface KupolaPaginationOptions {
|
|
@@ -1334,7 +1346,7 @@ declare module 'kupola' {
|
|
|
1334
1346
|
export function cleanupAllDropdowns(): void;
|
|
1335
1347
|
export function cleanupAllSlideCaptchas(): void;
|
|
1336
1348
|
|
|
1337
|
-
export function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function
|
|
1349
|
+
export function ref<T = unknown>(initialValue?: T): { value: T; _subscribers: Set<Function>; subscribe: (callback: Function) => { unsubscribe: () => void } };
|
|
1338
1350
|
|
|
1339
1351
|
export class Modal {
|
|
1340
1352
|
constructor(element: HTMLElement, options?: Record<string, unknown>);
|
|
@@ -1358,7 +1370,7 @@ declare module 'kupola' {
|
|
|
1358
1370
|
statusText?: string;
|
|
1359
1371
|
headers: Record<string, string>;
|
|
1360
1372
|
url?: string;
|
|
1361
|
-
json(): Promise<
|
|
1373
|
+
json(): Promise<unknown>;
|
|
1362
1374
|
text(): Promise<string>;
|
|
1363
1375
|
}
|
|
1364
1376
|
|
|
@@ -1454,4 +1466,8 @@ declare module 'kupola' {
|
|
|
1454
1466
|
export { KupolaTable, KupolaTableColumn, KupolaTableOptions, KupolaPagination, KupolaPaginationOptions };
|
|
1455
1467
|
export { initTable, initAllTables, initPagination };
|
|
1456
1468
|
|
|
1469
|
+
// Utils (tree-shakeable namespace exports)
|
|
1470
|
+
export { KupolaUtils, stringUtils, arrayUtils, objectUtils, numberUtils, dateUtils };
|
|
1471
|
+
export { debounce, throttle, validatorUtils, cryptoUtils, preloadUtils };
|
|
1472
|
+
|
|
1457
1473
|
}
|
package/js/calendar.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { kupolaInitializer } from './initializer.js';
|
|
2
|
+
|
|
1
3
|
class Calendar {
|
|
2
4
|
constructor(element, options = {}) {
|
|
3
5
|
this.element = element;
|
|
@@ -481,13 +483,4 @@ function initCalendars() {
|
|
|
481
483
|
|
|
482
484
|
export { Calendar, initCalendars, initCalendar, cleanupCalendar };
|
|
483
485
|
|
|
484
|
-
|
|
485
|
-
window.Calendar = Calendar;
|
|
486
|
-
window.initCalendar = initCalendar;
|
|
487
|
-
window.cleanupCalendar = cleanupCalendar;
|
|
488
|
-
window.initCalendars = initCalendars;
|
|
489
|
-
|
|
490
|
-
if (window.kupolaInitializer) {
|
|
491
|
-
window.kupolaInitializer.register('calendar', initCalendar, cleanupCalendar);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
486
|
+
kupolaInitializer.register('calendar', initCalendar, cleanupCalendar);
|
package/js/carousel.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { kupolaInitializer } from './initializer.js';
|
|
2
|
+
|
|
1
3
|
class Carousel {
|
|
2
4
|
constructor(element, options = {}) {
|
|
3
5
|
this.element = element;
|
|
@@ -295,13 +297,4 @@ function cleanupCarousel(element) {
|
|
|
295
297
|
|
|
296
298
|
export { Carousel, initCarousel, initCarousels, cleanupCarousel };
|
|
297
299
|
|
|
298
|
-
|
|
299
|
-
window.Carousel = Carousel;
|
|
300
|
-
window.initCarousel = initCarousel;
|
|
301
|
-
window.initCarousels = initCarousels;
|
|
302
|
-
|
|
303
|
-
if (window.kupolaInitializer) {
|
|
304
|
-
window.kupolaInitializer.register('carousel', initCarousel, cleanupCarousel);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
300
|
+
kupolaInitializer.register('carousel', initCarousel, cleanupCarousel);
|
package/js/collapse.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { kupolaInitializer } from './initializer.js';
|
|
2
|
+
|
|
1
3
|
class Collapse {
|
|
2
4
|
constructor(element, options = {}) {
|
|
3
5
|
this.element = element;
|
|
@@ -229,13 +231,4 @@ function initCollapses() {
|
|
|
229
231
|
|
|
230
232
|
export { Collapse, initCollapses, initCollapse, cleanupCollapse };
|
|
231
233
|
|
|
232
|
-
|
|
233
|
-
window.Collapse = Collapse;
|
|
234
|
-
window.initCollapse = initCollapse;
|
|
235
|
-
window.cleanupCollapse = cleanupCollapse;
|
|
236
|
-
window.initCollapses = initCollapses;
|
|
237
|
-
|
|
238
|
-
if (window.kupolaInitializer) {
|
|
239
|
-
window.kupolaInitializer.register('collapse', initCollapse, cleanupCollapse);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
234
|
+
kupolaInitializer.register('collapse', initCollapse, cleanupCollapse);
|
package/js/color-picker.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { globalEvents } from './global-events.js';
|
|
2
|
+
import { kupolaInitializer } from './initializer.js';
|
|
3
|
+
|
|
1
4
|
class ColorPicker {
|
|
2
5
|
constructor(element, options = {}) {
|
|
3
6
|
this.element = element;
|
|
@@ -251,11 +254,7 @@ class ColorPicker {
|
|
|
251
254
|
}
|
|
252
255
|
});
|
|
253
256
|
|
|
254
|
-
|
|
255
|
-
this._documentClickListener = window.globalEvents.on(document, 'click', this._documentClickHandler, { scope: this.scope });
|
|
256
|
-
} else {
|
|
257
|
-
document.addEventListener('click', this._documentClickHandler);
|
|
258
|
-
}
|
|
257
|
+
this._documentClickListener = globalEvents.on(document, 'click', this._documentClickHandler, { scope: this.scope });
|
|
259
258
|
|
|
260
259
|
this._renderPreviousColors();
|
|
261
260
|
this._renderColorPanel();
|
|
@@ -439,14 +438,4 @@ function cleanupAllColorPickers() {
|
|
|
439
438
|
|
|
440
439
|
export { ColorPicker, initColorPicker, initColorPickers, cleanupColorPicker, cleanupAllColorPickers };
|
|
441
440
|
|
|
442
|
-
|
|
443
|
-
window.ColorPicker = ColorPicker;
|
|
444
|
-
window.initColorPicker = initColorPicker;
|
|
445
|
-
window.initColorPickers = initColorPickers;
|
|
446
|
-
window.cleanupColorPicker = cleanupColorPicker;
|
|
447
|
-
window.cleanupAllColorPickers = cleanupAllColorPickers;
|
|
448
|
-
|
|
449
|
-
if (window.kupolaInitializer) {
|
|
450
|
-
window.kupolaInitializer.register('color-picker', initColorPicker, cleanupColorPicker);
|
|
451
|
-
}
|
|
452
|
-
}
|
|
441
|
+
kupolaInitializer.register('color-picker', initColorPicker, cleanupColorPicker);
|
package/js/component.js
CHANGED
|
@@ -120,15 +120,10 @@ export class KupolaComponent {
|
|
|
120
120
|
|
|
121
121
|
await this.lifecycle.bootstrap();
|
|
122
122
|
|
|
123
|
-
if (typeof
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (typeof this.setup === 'function') {
|
|
128
|
-
const result = this.setup();
|
|
129
|
-
if (result instanceof Promise) {
|
|
130
|
-
await result;
|
|
131
|
-
}
|
|
123
|
+
if (typeof this.setup === 'function') {
|
|
124
|
+
const result = this.setup();
|
|
125
|
+
if (result instanceof Promise) {
|
|
126
|
+
await result;
|
|
132
127
|
}
|
|
133
128
|
}
|
|
134
129
|
|
|
@@ -136,10 +131,6 @@ export class KupolaComponent {
|
|
|
136
131
|
await this.lifecycle.mount();
|
|
137
132
|
|
|
138
133
|
this.setupContext?._executeMounted();
|
|
139
|
-
|
|
140
|
-
if (typeof window !== 'undefined' && window._clearSetupContext) {
|
|
141
|
-
window._clearSetupContext();
|
|
142
|
-
}
|
|
143
134
|
} catch (error) {
|
|
144
135
|
console.error(`[KupolaComponent] Error mounting component "${this.constructor.name}":`, error);
|
|
145
136
|
|
|
@@ -257,9 +248,4 @@ export function applyMixin(componentClass, mixin) {
|
|
|
257
248
|
componentClass.prototype[key] = mixin[key];
|
|
258
249
|
}
|
|
259
250
|
});
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (typeof window !== 'undefined') {
|
|
263
|
-
window.KupolaComponent = KupolaComponent;
|
|
264
|
-
window.applyMixin = applyMixin;
|
|
265
251
|
}
|
package/js/countdown.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { kupolaInitializer } from './initializer.js';
|
|
2
|
+
|
|
1
3
|
class Countdown {
|
|
2
4
|
constructor(element) {
|
|
3
5
|
this.element = element;
|
|
@@ -133,13 +135,4 @@ function initCountdowns() {
|
|
|
133
135
|
|
|
134
136
|
export { Countdown, initCountdowns, initCountdown, cleanupCountdown };
|
|
135
137
|
|
|
136
|
-
|
|
137
|
-
window.Countdown = Countdown;
|
|
138
|
-
window.initCountdown = initCountdown;
|
|
139
|
-
window.cleanupCountdown = cleanupCountdown;
|
|
140
|
-
window.initCountdowns = initCountdowns;
|
|
141
|
-
|
|
142
|
-
if (window.kupolaInitializer) {
|
|
143
|
-
window.kupolaInitializer.register('countdown', initCountdown, cleanupCountdown);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
138
|
+
kupolaInitializer.register('countdown', initCountdown, cleanupCountdown);
|
package/js/data-bind.js
CHANGED
|
@@ -11,13 +11,26 @@
|
|
|
11
11
|
*/
|
|
12
12
|
function sanitizeHtml(html) {
|
|
13
13
|
if (!html) return '';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
let str = String(html);
|
|
15
|
+
|
|
16
|
+
// Iteratively remove dangerous tags (handles nested attempts like <scr<script>ipt>)
|
|
17
|
+
const dangerousTags = /<\s*(script|iframe|object|embed|applet|form|base|link|meta|style)\b[^>]*>[\s\S]*?<\s*\/\s*\1\s*>|<\s*(script|iframe|object|embed|applet|form|base|link|meta|style)\b[^>]*\/?>/gi;
|
|
18
|
+
let prev;
|
|
19
|
+
do {
|
|
20
|
+
prev = str;
|
|
21
|
+
str = str.replace(dangerousTags, '');
|
|
22
|
+
} while (str !== prev);
|
|
23
|
+
|
|
24
|
+
// Remove event handler attributes (on* = ...)
|
|
25
|
+
str = str.replace(/\bon\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, '');
|
|
26
|
+
|
|
27
|
+
// Remove javascript:, vbscript:, data: URIs in href/src/action attributes
|
|
28
|
+
str = str.replace(/(href|src|action|background)\s*=\s*(?:"[^"]*(?:javascript|vbscript|data)\s*:[^"]*"|'[^']*(?:javascript|vbscript|data)\s*:[^']*'|[^\s>]*(?:javascript|vbscript|data)\s*:[^\s>]*)/gi, '$1=""');
|
|
29
|
+
|
|
30
|
+
// Remove expression() in style attributes (IE CSS expression)
|
|
31
|
+
str = str.replace(/expression\s*\([^)]*\)/gi, '');
|
|
32
|
+
|
|
33
|
+
return str;
|
|
21
34
|
}
|
|
22
35
|
|
|
23
36
|
class TrieNode {
|
|
@@ -946,18 +959,26 @@ class KupolaDataBind {
|
|
|
946
959
|
});
|
|
947
960
|
|
|
948
961
|
if (!this._mutationObserver) {
|
|
962
|
+
this._isObserving = false;
|
|
949
963
|
this._mutationObserver = new MutationObserver((mutations) => {
|
|
950
|
-
mutations
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
964
|
+
// Re-entry guard: skip mutations caused by _bindElement itself
|
|
965
|
+
if (this._isObserving) return;
|
|
966
|
+
this._isObserving = true;
|
|
967
|
+
try {
|
|
968
|
+
mutations.forEach(mutation => {
|
|
969
|
+
mutation.addedNodes.forEach(node => {
|
|
970
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
971
|
+
const bindElements = node.querySelectorAll('[data-bind]');
|
|
972
|
+
bindElements.forEach(element => this._bindElement(element));
|
|
973
|
+
if (node.hasAttribute && node.hasAttribute('data-bind')) {
|
|
974
|
+
this._bindElement(node);
|
|
975
|
+
}
|
|
957
976
|
}
|
|
958
|
-
}
|
|
977
|
+
});
|
|
959
978
|
});
|
|
960
|
-
}
|
|
979
|
+
} finally {
|
|
980
|
+
this._isObserving = false;
|
|
981
|
+
}
|
|
961
982
|
});
|
|
962
983
|
|
|
963
984
|
this._mutationObserver.observe(document.body, {
|
|
@@ -1052,12 +1073,12 @@ class KupolaStore {
|
|
|
1052
1073
|
|
|
1053
1074
|
this.observers = {};
|
|
1054
1075
|
|
|
1055
|
-
if (
|
|
1056
|
-
|
|
1057
|
-
this.state =
|
|
1058
|
-
|
|
1076
|
+
if (kupolaData) {
|
|
1077
|
+
kupolaData.set(this._stateKey, initialState);
|
|
1078
|
+
this.state = kupolaData.data?.[this._stateKey] ||
|
|
1079
|
+
kupolaData.createReactive(initialState, this._stateKey);
|
|
1059
1080
|
|
|
1060
|
-
|
|
1081
|
+
kupolaData.observe(this._stateKey, (newState) => {
|
|
1061
1082
|
this.notify(newState);
|
|
1062
1083
|
});
|
|
1063
1084
|
} else {
|
|
@@ -1140,8 +1161,8 @@ class KupolaStore {
|
|
|
1140
1161
|
});
|
|
1141
1162
|
}
|
|
1142
1163
|
|
|
1143
|
-
if (
|
|
1144
|
-
|
|
1164
|
+
if (kupolaData) {
|
|
1165
|
+
kupolaData.set(this.name, newState);
|
|
1145
1166
|
}
|
|
1146
1167
|
}
|
|
1147
1168
|
|
|
@@ -1303,6 +1324,18 @@ function ref(initialValue = null) {
|
|
|
1303
1324
|
}
|
|
1304
1325
|
});
|
|
1305
1326
|
|
|
1327
|
+
/**
|
|
1328
|
+
* Subscribe to value changes.
|
|
1329
|
+
* @param {Function} callback - Called with new value on change
|
|
1330
|
+
* @returns {{ unsubscribe: Function }} Unsubscribe handle
|
|
1331
|
+
*/
|
|
1332
|
+
refObj.subscribe = (callback) => {
|
|
1333
|
+
refObj._subscribers.add(callback);
|
|
1334
|
+
return {
|
|
1335
|
+
unsubscribe() { refObj._subscribers.delete(callback); }
|
|
1336
|
+
};
|
|
1337
|
+
};
|
|
1338
|
+
|
|
1306
1339
|
return refObj;
|
|
1307
1340
|
}
|
|
1308
1341
|
|
|
@@ -1328,16 +1361,3 @@ export { KupolaDataBind, KupolaEventBus, KupolaStore, KupolaStoreManager };
|
|
|
1328
1361
|
export { kupolaData, kupolaEvents, kupolaStoreManager };
|
|
1329
1362
|
export { createStore, getStore };
|
|
1330
1363
|
export { ref };
|
|
1331
|
-
|
|
1332
|
-
if (typeof window !== 'undefined') {
|
|
1333
|
-
window.KupolaDataBind = KupolaDataBind;
|
|
1334
|
-
window.KupolaEventBus = KupolaEventBus;
|
|
1335
|
-
window.KupolaStore = KupolaStore;
|
|
1336
|
-
window.KupolaStoreManager = KupolaStoreManager;
|
|
1337
|
-
window.kupolaData = kupolaData;
|
|
1338
|
-
window.kupolaEvents = kupolaEvents;
|
|
1339
|
-
window.kupolaStoreManager = kupolaStoreManager;
|
|
1340
|
-
window.createStore = createStore;
|
|
1341
|
-
window.getStore = getStore;
|
|
1342
|
-
window.kupolaRef = ref;
|
|
1343
|
-
}
|
package/js/datepicker.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { globalEvents } from './global-events.js';
|
|
2
|
+
import { kupolaInitializer } from './initializer.js';
|
|
3
|
+
|
|
1
4
|
class Datepicker {
|
|
2
5
|
constructor(element, options = {}) {
|
|
3
6
|
this.element = element;
|
|
@@ -35,6 +38,7 @@ class Datepicker {
|
|
|
35
38
|
|
|
36
39
|
this._iconClickHandler = null;
|
|
37
40
|
this._inputClickHandler = null;
|
|
41
|
+
this._endInputClickHandler = null;
|
|
38
42
|
this._documentClickHandler = null;
|
|
39
43
|
this._documentClickListener = null;
|
|
40
44
|
this._resizeHandler = null;
|
|
@@ -70,21 +74,17 @@ class Datepicker {
|
|
|
70
74
|
|
|
71
75
|
if (this.icon) this.icon.addEventListener('click', this._iconClickHandler);
|
|
72
76
|
if (this.input) this.input.addEventListener('click', this._inputClickHandler);
|
|
73
|
-
if (this.endInput)
|
|
74
|
-
this.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
this._documentClickListener = window.globalEvents.on(document, 'click', (e) => this.hideCalendar(e), { scope: this.scope });
|
|
80
|
-
this._resizeListener = window.globalEvents.on(window, 'resize', () => this.resizeHandler(), { scope: this.scope });
|
|
81
|
-
} else {
|
|
82
|
-
document.addEventListener('click', (e) => this.hideCalendar(e));
|
|
83
|
-
window.addEventListener('resize', () => this.resizeHandler());
|
|
84
|
-
this._documentClickHandler = (e) => this.hideCalendar(e);
|
|
85
|
-
this._resizeHandler = () => this.resizeHandler();
|
|
77
|
+
if (this.endInput) {
|
|
78
|
+
this._endInputClickHandler = (e) => {
|
|
79
|
+
this.isSelectingEnd = true;
|
|
80
|
+
this.toggleCalendar(e);
|
|
81
|
+
};
|
|
82
|
+
this.endInput.addEventListener('click', this._endInputClickHandler);
|
|
86
83
|
}
|
|
87
84
|
|
|
85
|
+
this._documentClickListener = globalEvents.on(document, 'click', (e) => this.hideCalendar(e), { scope: this.scope });
|
|
86
|
+
this._resizeListener = globalEvents.on(window, 'resize', () => this.resizeHandler(), { scope: this.scope });
|
|
87
|
+
|
|
88
88
|
// Keyboard
|
|
89
89
|
this._keydownHandler = (e) => {
|
|
90
90
|
if (e.key === 'Escape' && this.calendarEl.style.display === 'block') {
|
|
@@ -577,6 +577,7 @@ class Datepicker {
|
|
|
577
577
|
|
|
578
578
|
if (this.icon && this._iconClickHandler) this.icon.removeEventListener('click', this._iconClickHandler);
|
|
579
579
|
if (this.input && this._inputClickHandler) this.input.removeEventListener('click', this._inputClickHandler);
|
|
580
|
+
if (this.endInput && this._endInputClickHandler) this.endInput.removeEventListener('click', this._endInputClickHandler);
|
|
580
581
|
if (this._keydownHandler) document.removeEventListener('keydown', this._keydownHandler);
|
|
581
582
|
|
|
582
583
|
if (this._documentClickListener && this._documentClickListener.unsubscribe) {
|
|
@@ -603,6 +604,7 @@ class Datepicker {
|
|
|
603
604
|
this._resizeListener = null;
|
|
604
605
|
this._iconClickHandler = null;
|
|
605
606
|
this._inputClickHandler = null;
|
|
607
|
+
this._endInputClickHandler = null;
|
|
606
608
|
this._keydownHandler = null;
|
|
607
609
|
this.element.__kupolaInitialized = false;
|
|
608
610
|
}
|
|
@@ -635,14 +637,4 @@ function cleanupAllDatepickers() {
|
|
|
635
637
|
|
|
636
638
|
export { Datepicker, initDatepicker, initDatepickers, cleanupDatepicker, cleanupAllDatepickers };
|
|
637
639
|
|
|
638
|
-
|
|
639
|
-
window.Datepicker = Datepicker;
|
|
640
|
-
window.initDatepicker = initDatepicker;
|
|
641
|
-
window.initDatepickers = initDatepickers;
|
|
642
|
-
window.cleanupDatepicker = cleanupDatepicker;
|
|
643
|
-
window.cleanupAllDatepickers = cleanupAllDatepickers;
|
|
644
|
-
|
|
645
|
-
if (window.kupolaInitializer) {
|
|
646
|
-
window.kupolaInitializer.register('datepicker', initDatepicker, cleanupDatepicker);
|
|
647
|
-
}
|
|
648
|
-
}
|
|
640
|
+
kupolaInitializer.register('datepicker', initDatepicker, cleanupDatepicker);
|
package/js/depends.js
CHANGED
|
@@ -8,6 +8,8 @@ class Scheduler {
|
|
|
8
8
|
constructor() {
|
|
9
9
|
this._queue = new Set();
|
|
10
10
|
this._scheduled = false;
|
|
11
|
+
this._flushDepth = 0;
|
|
12
|
+
this._maxDepth = 10;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
schedule(fn) {
|
|
@@ -19,9 +21,16 @@ class Scheduler {
|
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
_flush() {
|
|
24
|
+
if (this._flushDepth >= this._maxDepth) {
|
|
25
|
+
console.warn('[Kupola Scheduler] Max flush depth reached, possible infinite loop detected');
|
|
26
|
+
this._queue.clear();
|
|
27
|
+
this._scheduled = false;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
22
30
|
const tasks = Array.from(this._queue);
|
|
23
31
|
this._queue.clear();
|
|
24
32
|
this._scheduled = false;
|
|
33
|
+
this._flushDepth++;
|
|
25
34
|
// Deduplicate: same function only runs once
|
|
26
35
|
const seen = new Set();
|
|
27
36
|
for (const task of tasks) {
|
|
@@ -30,6 +39,7 @@ class Scheduler {
|
|
|
30
39
|
try { task(); } catch (e) { console.error('[DependsScheduler]', e); }
|
|
31
40
|
}
|
|
32
41
|
}
|
|
42
|
+
this._flushDepth--;
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
45
|
|
|
@@ -216,7 +226,10 @@ class DependsSource {
|
|
|
216
226
|
return result;
|
|
217
227
|
} catch (error) {
|
|
218
228
|
if (attempt < this.retryCount) {
|
|
219
|
-
|
|
229
|
+
// Exponential backoff + random jitter to prevent thundering herd
|
|
230
|
+
const baseDelay = this.retryDelay * Math.pow(2, attempt);
|
|
231
|
+
const jitter = Math.random() * baseDelay * 0.5;
|
|
232
|
+
const delay = baseDelay + jitter;
|
|
220
233
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
221
234
|
return this._fetchWithRetry(params, attempt + 1);
|
|
222
235
|
}
|
|
@@ -400,8 +413,11 @@ class WebSocketSource extends DependsSource {
|
|
|
400
413
|
this.ws = null;
|
|
401
414
|
this.reconnect = config.reconnect !== false;
|
|
402
415
|
this.reconnectDelay = config.reconnectDelay || 3000;
|
|
416
|
+
this._reconnectAttempt = 0;
|
|
417
|
+
this._maxReconnectDelay = config.maxReconnectDelay || 30000;
|
|
403
418
|
this.messageHandler = null;
|
|
404
419
|
this._connected = false;
|
|
420
|
+
this._destroyed = false;
|
|
405
421
|
}
|
|
406
422
|
|
|
407
423
|
async fetch() {
|
|
@@ -411,6 +427,7 @@ class WebSocketSource extends DependsSource {
|
|
|
411
427
|
|
|
412
428
|
this.ws.onopen = () => {
|
|
413
429
|
this._connected = true;
|
|
430
|
+
this._reconnectAttempt = 0; // Reset on successful connection
|
|
414
431
|
// Resolve with current cached data or null
|
|
415
432
|
resolve(this.cache.getStale(this.cacheKey));
|
|
416
433
|
};
|
|
@@ -432,10 +449,15 @@ class WebSocketSource extends DependsSource {
|
|
|
432
449
|
|
|
433
450
|
this.ws.onclose = () => {
|
|
434
451
|
this._connected = false;
|
|
435
|
-
if (this.reconnect) {
|
|
452
|
+
if (this.reconnect && !this._destroyed) {
|
|
453
|
+
// Exponential backoff + jitter for reconnection
|
|
454
|
+
const baseDelay = this.reconnectDelay * Math.pow(2, this._reconnectAttempt);
|
|
455
|
+
const jitter = Math.random() * baseDelay * 0.3;
|
|
456
|
+
const delay = Math.min(baseDelay + jitter, this._maxReconnectDelay);
|
|
457
|
+
this._reconnectAttempt++;
|
|
436
458
|
setTimeout(() => {
|
|
437
|
-
this.fetch().catch(() => {});
|
|
438
|
-
},
|
|
459
|
+
if (!this._destroyed) this.fetch().catch(() => {});
|
|
460
|
+
}, delay);
|
|
439
461
|
}
|
|
440
462
|
};
|
|
441
463
|
} catch (e) {
|
|
@@ -451,6 +473,7 @@ class WebSocketSource extends DependsSource {
|
|
|
451
473
|
}
|
|
452
474
|
|
|
453
475
|
destroy() {
|
|
476
|
+
this._destroyed = true;
|
|
454
477
|
super.destroy();
|
|
455
478
|
if (this.ws) {
|
|
456
479
|
this.ws.onmessage = null;
|
|
@@ -696,15 +719,3 @@ export {
|
|
|
696
719
|
};
|
|
697
720
|
|
|
698
721
|
// HTTP Client Plugin System exports are above (configureHttpClient, getHttpClient, resetHttpClient)
|
|
699
|
-
|
|
700
|
-
if (typeof window !== 'undefined') {
|
|
701
|
-
window.useDeps = useDeps;
|
|
702
|
-
window.useQuery = useQuery;
|
|
703
|
-
window.clearCache = clearCache;
|
|
704
|
-
window.DependsError = DependsError;
|
|
705
|
-
window.CacheManager = CacheManager;
|
|
706
|
-
window.Scheduler = Scheduler;
|
|
707
|
-
window.configureHttpClient = configureHttpClient;
|
|
708
|
-
window.getHttpClient = getHttpClient;
|
|
709
|
-
window.resetHttpClient = resetHttpClient;
|
|
710
|
-
}
|
package/js/dialog.js
CHANGED
package/js/drawer.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { kupolaInitializer } from './initializer.js';
|
|
2
|
+
|
|
1
3
|
class Drawer {
|
|
2
4
|
constructor(element, options = {}) {
|
|
3
5
|
this.element = element;
|
|
@@ -269,14 +271,4 @@ function initDrawers() {
|
|
|
269
271
|
|
|
270
272
|
export { Drawer, initDrawer, initDrawers, cleanupDrawer, createDrawer };
|
|
271
273
|
|
|
272
|
-
|
|
273
|
-
window.Drawer = Drawer;
|
|
274
|
-
window.initDrawer = initDrawer;
|
|
275
|
-
window.initDrawers = initDrawers;
|
|
276
|
-
window.cleanupDrawer = cleanupDrawer;
|
|
277
|
-
window.createDrawer = createDrawer;
|
|
278
|
-
|
|
279
|
-
if (window.kupolaInitializer) {
|
|
280
|
-
window.kupolaInitializer.register('drawer', initDrawer, cleanupDrawer);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
274
|
+
kupolaInitializer.register('drawer', initDrawer, cleanupDrawer);
|