@keenthemes/ktui 1.2.6 → 1.2.7
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 +14 -5
- package/dist/ktui.js +3775 -2298
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +25 -5
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-checkbox.js +143 -156
- package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js +42 -0
- package/lib/cjs/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/cjs/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-defaults.js +193 -0
- package/lib/cjs/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-layout-plugin.js +11 -1
- package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.js +80 -24
- package/lib/cjs/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js +3 -2
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/cjs/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-registry.js +66 -0
- package/lib/cjs/components/datatable/datatable-registry.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js +65 -0
- package/lib/cjs/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/cjs/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-sort.js +86 -58
- package/lib/cjs/components/datatable/datatable-sort.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/cjs/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-spinner.js +54 -0
- package/lib/cjs/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js +59 -0
- package/lib/cjs/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/cjs/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-utils.js +15 -0
- package/lib/cjs/components/datatable/datatable-utils.js.map +1 -0
- package/lib/cjs/components/datatable/datatable.d.ts +26 -34
- package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +155 -492
- package/lib/cjs/components/datatable/datatable.js.map +1 -1
- package/lib/cjs/components/datatable/index.d.ts +1 -1
- package/lib/cjs/components/datatable/index.d.ts.map +1 -1
- package/lib/cjs/components/datatable/types.d.ts +100 -11
- package/lib/cjs/components/datatable/types.d.ts.map +1 -1
- package/lib/cjs/index.d.ts +1 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +6 -0
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts +37 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.js +142 -155
- package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/esm/components/datatable/datatable-column-utils.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-column-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-column-utils.js +38 -0
- package/lib/esm/components/datatable/datatable-column-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable-contracts.d.ts +2 -4
- package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-defaults.d.ts +20 -0
- package/lib/esm/components/datatable/datatable-defaults.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-defaults.js +190 -0
- package/lib/esm/components/datatable/datatable-defaults.js.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-layout-plugin.js +11 -1
- package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.js +80 -24
- package/lib/esm/components/datatable/datatable-local-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-pagination-renderer.js +3 -2
- package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-registry.d.ts +18 -0
- package/lib/esm/components/datatable/datatable-registry.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-registry.js +63 -0
- package/lib/esm/components/datatable/datatable-registry.js.map +1 -0
- package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-remote-provider.js +1 -2
- package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-search-handler.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-search-handler.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-search-handler.js +62 -0
- package/lib/esm/components/datatable/datatable-search-handler.js.map +1 -0
- package/lib/esm/components/datatable/datatable-sort.d.ts +31 -4
- package/lib/esm/components/datatable/datatable-sort.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-sort.js +85 -57
- package/lib/esm/components/datatable/datatable-sort.js.map +1 -1
- package/lib/esm/components/datatable/datatable-spinner.d.ts +30 -0
- package/lib/esm/components/datatable/datatable-spinner.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-spinner.js +51 -0
- package/lib/esm/components/datatable/datatable-spinner.js.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts +19 -0
- package/lib/esm/components/datatable/datatable-state-persistence.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js +55 -0
- package/lib/esm/components/datatable/datatable-state-persistence.js.map +1 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts +2 -0
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-table-renderer.js +75 -16
- package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-utils.d.ts +10 -0
- package/lib/esm/components/datatable/datatable-utils.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-utils.js +12 -0
- package/lib/esm/components/datatable/datatable-utils.js.map +1 -0
- package/lib/esm/components/datatable/datatable.d.ts +26 -34
- package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable.js +157 -494
- package/lib/esm/components/datatable/datatable.js.map +1 -1
- package/lib/esm/components/datatable/index.d.ts +1 -1
- package/lib/esm/components/datatable/index.d.ts.map +1 -1
- package/lib/esm/components/datatable/types.d.ts +100 -11
- package/lib/esm/components/datatable/types.d.ts.map +1 -1
- package/lib/esm/index.d.ts +1 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +6 -0
- package/lib/esm/index.js.map +1 -1
- package/package.json +5 -1
- package/skills/ktui/SKILL.md +711 -0
- package/skills/ktui-datatable/SKILL.md +302 -0
- package/skills/ktui-install/SKILL.md +150 -0
- package/skills/ktui-select/SKILL.md +271 -0
- package/src/components/__tests__/component.test.ts +347 -0
- package/src/components/collapse/collapse.css +2 -2
- package/src/components/datatable/__tests__/architecture-boundaries.test.ts +56 -8
- package/src/components/datatable/__tests__/currency-sort.test.ts +25 -28
- package/src/components/datatable/__tests__/datatable-checkbox.test.ts +527 -0
- package/src/components/datatable/__tests__/datatable-column-utils.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-defaults.test.ts +57 -0
- package/src/components/datatable/__tests__/datatable-finalize-extended.test.ts +361 -0
- package/src/components/datatable/__tests__/datatable-fixed-layout.test.ts +427 -0
- package/src/components/datatable/__tests__/datatable-improvements.test.ts +484 -0
- package/src/components/datatable/__tests__/datatable-pagination-extended.test.ts +508 -0
- package/src/components/datatable/__tests__/datatable-public-api.test.ts +269 -0
- package/src/components/datatable/__tests__/datatable-registry.test.ts +172 -0
- package/src/components/datatable/__tests__/datatable-remote-provider.test.ts +468 -0
- package/src/components/datatable/__tests__/datatable-search-handler.test.ts +124 -0
- package/src/components/datatable/__tests__/datatable-sort-extended.test.ts +417 -0
- package/src/components/datatable/__tests__/datatable-spinner.test.ts +95 -0
- package/src/components/datatable/__tests__/datatable-table-renderer-extended.test.ts +425 -0
- package/src/components/datatable/__tests__/datatable-types.test.ts +117 -0
- package/src/components/datatable/__tests__/datatable-utils.test.ts +52 -0
- package/src/components/datatable/__tests__/multi-row-headers.test.ts +7 -7
- package/src/components/datatable/__tests__/pagination-reset.test.ts +129 -6
- package/src/components/datatable/__tests__/race-conditions.test.ts +11 -11
- package/src/components/datatable/__tests__/setup.ts +12 -4
- package/src/components/datatable/datatable-checkbox.ts +144 -145
- package/src/components/datatable/datatable-column-utils.ts +63 -0
- package/src/components/datatable/datatable-contracts.ts +2 -3
- package/src/components/datatable/datatable-defaults.ts +204 -0
- package/src/components/datatable/datatable-layout-plugin.ts +11 -1
- package/src/components/datatable/datatable-local-provider.ts +91 -28
- package/src/components/datatable/datatable-pagination-renderer.ts +3 -2
- package/src/components/datatable/datatable-registry.ts +89 -0
- package/src/components/datatable/datatable-remote-provider.ts +1 -3
- package/src/components/datatable/datatable-search-handler.ts +97 -0
- package/src/components/datatable/datatable-sort.ts +111 -66
- package/src/components/datatable/datatable-spinner.ts +103 -0
- package/src/components/datatable/datatable-state-persistence.ts +67 -0
- package/src/components/datatable/datatable-table-renderer.ts +81 -18
- package/src/components/datatable/datatable-utils.ts +12 -0
- package/src/components/datatable/datatable.ts +191 -580
- package/src/components/datatable/index.ts +3 -0
- package/src/components/datatable/types.ts +124 -23
- package/src/helpers/__tests__/dom.test.ts +776 -0
- package/src/helpers/__tests__/utils.test.ts +332 -0
- package/src/index.ts +10 -0
- package/skills/ktui-components/SKILL.md +0 -41
- package/skills/ktui-theming/SKILL.md +0 -50
- package/src/components/datatable/datatable-event-adapter.ts +0 -21
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import KTUtils from '../utils';
|
|
3
|
+
|
|
4
|
+
describe('KTUtils', () => {
|
|
5
|
+
describe('geUID', () => {
|
|
6
|
+
it('returns string starting with prefix', () => {
|
|
7
|
+
const uid = KTUtils.geUID('test-');
|
|
8
|
+
expect(uid).toMatch(/^test-/);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns different values on successive calls', () => {
|
|
12
|
+
const a = KTUtils.geUID('p-');
|
|
13
|
+
const b = KTUtils.geUID('p-');
|
|
14
|
+
expect(a).not.toBe(b);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('works without prefix', () => {
|
|
18
|
+
const uid = KTUtils.geUID();
|
|
19
|
+
expect(typeof uid).toBe('string');
|
|
20
|
+
expect(uid.length).toBeGreaterThan(0);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('getCssVar', () => {
|
|
25
|
+
it('reads computed style from document.documentElement', () => {
|
|
26
|
+
const spy = vi.spyOn(
|
|
27
|
+
window,
|
|
28
|
+
'getComputedStyle',
|
|
29
|
+
).mockReturnValue({
|
|
30
|
+
getPropertyValue: (prop: string) =>
|
|
31
|
+
prop === '--my-var' ? ' red ' : '',
|
|
32
|
+
} as unknown as CSSStyleDeclaration);
|
|
33
|
+
|
|
34
|
+
const result = KTUtils.getCssVar('--my-var');
|
|
35
|
+
expect(result).toBe('red');
|
|
36
|
+
spy.mockRestore();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns empty string when property is empty', () => {
|
|
40
|
+
const spy = vi.spyOn(
|
|
41
|
+
window,
|
|
42
|
+
'getComputedStyle',
|
|
43
|
+
).mockReturnValue({
|
|
44
|
+
getPropertyValue: () => '',
|
|
45
|
+
} as unknown as CSSStyleDeclaration);
|
|
46
|
+
|
|
47
|
+
const result = KTUtils.getCssVar('--missing');
|
|
48
|
+
expect(result).toBe('');
|
|
49
|
+
spy.mockRestore();
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('parseDataAttribute', () => {
|
|
54
|
+
it('"true" returns true', () => {
|
|
55
|
+
expect(KTUtils.parseDataAttribute('true')).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('"false" returns false', () => {
|
|
59
|
+
expect(KTUtils.parseDataAttribute('false')).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('"123" returns 123', () => {
|
|
63
|
+
expect(KTUtils.parseDataAttribute('123')).toBe(123);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('"3.14" returns 3.14', () => {
|
|
67
|
+
expect(KTUtils.parseDataAttribute('3.14')).toBeCloseTo(3.14);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('"" returns null', () => {
|
|
71
|
+
expect(KTUtils.parseDataAttribute('')).toBeNull();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('"null" returns null', () => {
|
|
75
|
+
expect(KTUtils.parseDataAttribute('null')).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('"hello" returns "hello" (string fallback)', () => {
|
|
79
|
+
expect(KTUtils.parseDataAttribute('hello')).toBe('hello');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('JSON object string returns parsed object', () => {
|
|
83
|
+
const result = KTUtils.parseDataAttribute('{"key":"value"}');
|
|
84
|
+
expect(result).toEqual({ key: 'value' });
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('parseJson', () => {
|
|
89
|
+
it('parses valid JSON string', () => {
|
|
90
|
+
expect(KTUtils.parseJson('{"a":1}')).toEqual({ a: 1 });
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns null for empty string', () => {
|
|
94
|
+
expect(KTUtils.parseJson('')).toBeNull();
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('decodes URI-encoded JSON', () => {
|
|
98
|
+
const encoded = encodeURIComponent('{"x":"y"}');
|
|
99
|
+
expect(KTUtils.parseJson(encoded)).toEqual({ x: 'y' });
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('parseSelector', () => {
|
|
104
|
+
it('escapes ID with CSS.escape', () => {
|
|
105
|
+
vi.stubGlobal('CSS', { escape: (s: string) => `escaped(${s})` });
|
|
106
|
+
const result = KTUtils.parseSelector('#my-id');
|
|
107
|
+
expect(result).toBe('#escaped(my-id)');
|
|
108
|
+
vi.unstubAllGlobals();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('returns unchanged when no ID present', () => {
|
|
112
|
+
const result = KTUtils.parseSelector('.class');
|
|
113
|
+
expect(result).toBe('.class');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('returns selector as-is when CSS.escape is unavailable', () => {
|
|
117
|
+
const orig = window.CSS;
|
|
118
|
+
// @ts-ignore
|
|
119
|
+
delete window.CSS;
|
|
120
|
+
expect(KTUtils.parseSelector('#my-id')).toBe('#my-id');
|
|
121
|
+
window.CSS = orig;
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('capitalize', () => {
|
|
126
|
+
it('capitalizes first character', () => {
|
|
127
|
+
expect(KTUtils.capitalize('hello')).toBe('Hello');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('does not change already capitalized string', () => {
|
|
131
|
+
expect(KTUtils.capitalize('Hello')).toBe('Hello');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('uncapitalize', () => {
|
|
136
|
+
it('lowercases first character', () => {
|
|
137
|
+
expect(KTUtils.uncapitalize('Hello')).toBe('hello');
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('does not change already lowercase string', () => {
|
|
141
|
+
expect(KTUtils.uncapitalize('hello')).toBe('hello');
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('camelCase', () => {
|
|
146
|
+
it('converts hyphenated to camelCase', () => {
|
|
147
|
+
expect(KTUtils.camelCase('my-class-name')).toBe('myClassName');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('returns unchanged when no hyphens', () => {
|
|
151
|
+
expect(KTUtils.camelCase('foo')).toBe('foo');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe('camelReverseCase', () => {
|
|
156
|
+
it('converts camelCase to hyphenated', () => {
|
|
157
|
+
expect(KTUtils.camelReverseCase('myClassName')).toBe(
|
|
158
|
+
'my-class-name',
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('returns unchanged when no uppercase', () => {
|
|
163
|
+
expect(KTUtils.camelReverseCase('foo')).toBe('foo');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
describe('isRTL', () => {
|
|
168
|
+
it('returns true when direction is rtl', () => {
|
|
169
|
+
const html = document.documentElement;
|
|
170
|
+
html.setAttribute('direction', 'rtl');
|
|
171
|
+
expect(KTUtils.isRTL()).toBe(true);
|
|
172
|
+
html.removeAttribute('direction');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('returns false when direction is not rtl', () => {
|
|
176
|
+
expect(KTUtils.isRTL()).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('returns false when html element not found', () => {
|
|
180
|
+
const orig = document.querySelector;
|
|
181
|
+
document.querySelector = vi.fn().mockReturnValue(null);
|
|
182
|
+
expect(KTUtils.isRTL()).toBe(false);
|
|
183
|
+
document.querySelector = orig;
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe('checksum', () => {
|
|
188
|
+
it('returns consistent 8-char hex string', () => {
|
|
189
|
+
const result = KTUtils.checksum('hello');
|
|
190
|
+
expect(result).toMatch(/^[0-9a-f]{8}$/);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('returns "00000000" for empty string', () => {
|
|
194
|
+
expect(KTUtils.checksum('')).toBe('00000000');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('is deterministic (same input → same output)', () => {
|
|
198
|
+
expect(KTUtils.checksum('hello')).toBe(KTUtils.checksum('hello'));
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('different inputs produce different hashes', () => {
|
|
202
|
+
expect(KTUtils.checksum('hello')).not.toBe(
|
|
203
|
+
KTUtils.checksum('world'),
|
|
204
|
+
);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('stringToBoolean', () => {
|
|
209
|
+
it('returns true for boolean true', () => {
|
|
210
|
+
expect(KTUtils.stringToBoolean(true)).toBe(true);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('returns true for "true"', () => {
|
|
214
|
+
expect(KTUtils.stringToBoolean('true')).toBe(true);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('returns false for "false"', () => {
|
|
218
|
+
expect(KTUtils.stringToBoolean('false')).toBe(false);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('returns true for "TRUE" (case insensitive)', () => {
|
|
222
|
+
expect(KTUtils.stringToBoolean('TRUE')).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('returns null for number 123', () => {
|
|
226
|
+
expect(KTUtils.stringToBoolean(123)).toBeNull();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('returns null for unrecognized string', () => {
|
|
230
|
+
expect(KTUtils.stringToBoolean('yes')).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe('stringToObject', () => {
|
|
235
|
+
it('parses valid JSON object string', () => {
|
|
236
|
+
expect(KTUtils.stringToObject('{"key":"value"}')).toEqual({
|
|
237
|
+
key: 'value',
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('returns null for "invalid"', () => {
|
|
242
|
+
expect(KTUtils.stringToObject('invalid')).toBeNull();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('returns null for null', () => {
|
|
246
|
+
expect(KTUtils.stringToObject(null)).toBeNull();
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('returns null for array (not object)', () => {
|
|
250
|
+
expect(KTUtils.stringToObject('[1,2]')).toBeNull();
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe('stringToInteger', () => {
|
|
255
|
+
it('returns integer for number 42', () => {
|
|
256
|
+
expect(KTUtils.stringToInteger(42)).toBe(42);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('floors decimal number 3.7 to 3', () => {
|
|
260
|
+
expect(KTUtils.stringToInteger(3.7)).toBe(3);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('parses string "123" to 123', () => {
|
|
264
|
+
expect(KTUtils.stringToInteger('123')).toBe(123);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('returns null for "abc"', () => {
|
|
268
|
+
expect(KTUtils.stringToInteger('abc')).toBeNull();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('returns null for null', () => {
|
|
272
|
+
expect(KTUtils.stringToInteger(null)).toBeNull();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('returns null for NaN number', () => {
|
|
276
|
+
expect(KTUtils.stringToInteger(NaN)).toBeNull();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('handles negative numbers', () => {
|
|
280
|
+
expect(KTUtils.stringToInteger('-5')).toBe(-5);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
describe('stringToFloat', () => {
|
|
285
|
+
it('returns float for number 3.14', () => {
|
|
286
|
+
expect(KTUtils.stringToFloat(3.14)).toBeCloseTo(3.14);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('parses string "2.5" to 2.5', () => {
|
|
290
|
+
expect(KTUtils.stringToFloat('2.5')).toBeCloseTo(2.5);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('returns null for "abc"', () => {
|
|
294
|
+
expect(KTUtils.stringToFloat('abc')).toBeNull();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('returns null for null', () => {
|
|
298
|
+
expect(KTUtils.stringToFloat(null)).toBeNull();
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('returns null for NaN number', () => {
|
|
302
|
+
expect(KTUtils.stringToFloat(NaN)).toBeNull();
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
describe('throttle', () => {
|
|
307
|
+
beforeEach(() => {
|
|
308
|
+
vi.useFakeTimers();
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
afterEach(() => {
|
|
312
|
+
vi.useRealTimers();
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('calls func after delay when timer is undefined', () => {
|
|
316
|
+
const func = vi.fn();
|
|
317
|
+
KTUtils.throttle(undefined, func, 300);
|
|
318
|
+
expect(func).not.toHaveBeenCalled();
|
|
319
|
+
vi.advanceTimersByTime(300);
|
|
320
|
+
expect(func).toHaveBeenCalledTimes(1);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it('does not schedule new timeout when timer is set', () => {
|
|
324
|
+
const func = vi.fn();
|
|
325
|
+
const existingTimer = setTimeout(() => {}, 1000);
|
|
326
|
+
KTUtils.throttle(existingTimer, func, 300);
|
|
327
|
+
vi.advanceTimersByTime(500);
|
|
328
|
+
expect(func).not.toHaveBeenCalled();
|
|
329
|
+
clearTimeout(existingTimer);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -84,6 +84,9 @@ export type {
|
|
|
84
84
|
KTDataTableConfigInterface,
|
|
85
85
|
KTDataTableColumnFilterTypeInterface,
|
|
86
86
|
KTDataTableColumnFilterInterface,
|
|
87
|
+
KTDataTableTextFilterInterface,
|
|
88
|
+
KTDataTableNumericFilterInterface,
|
|
89
|
+
KTDataTableDateRangeFilterInterface,
|
|
87
90
|
KTDataTableCheckConfigInterface,
|
|
88
91
|
KTDataTableCheckInterface,
|
|
89
92
|
KTDataTableCheckChangePayloadInterface,
|
|
@@ -234,6 +237,13 @@ export const KTComponents = {
|
|
|
234
237
|
},
|
|
235
238
|
};
|
|
236
239
|
|
|
240
|
+
// Livewire wire:navigate support: re-init all components after SPA navigation
|
|
241
|
+
if (typeof window !== 'undefined') {
|
|
242
|
+
document.addEventListener('livewire:navigate', () => {
|
|
243
|
+
KTComponents.init();
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
237
247
|
declare global {
|
|
238
248
|
interface Window {
|
|
239
249
|
KTUtils: typeof KTUtils;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ktui-components
|
|
3
|
-
description: Guides correct use of KtUI (Keenthemes Tailwind UI) components—imports from @keenthemes/ktui, init pattern, and docs. Use this skill when building UI with KtUI, adding or customizing KtUI components, or when the user mentions KtUI, ktui, Keenthemes components, or Tailwind UI components from Keenthemes.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# KtUI Components
|
|
7
|
-
|
|
8
|
-
Use this skill when working with [KtUI](https://ktui.io)—free Tailwind UI components by Keenthemes. It ensures correct package usage, initialization, and reference to official docs.
|
|
9
|
-
|
|
10
|
-
## Package and imports
|
|
11
|
-
|
|
12
|
-
- **Package:** `@keenthemes/ktui` (npm install `@keenthemes/ktui`).
|
|
13
|
-
- **Import pattern (ESM):** Import components and/or the init helper from the package root:
|
|
14
|
-
- Single component: `import { KTModal } from '@keenthemes/ktui';`
|
|
15
|
-
- Init all: `import { KTComponents } from '@keenthemes/ktui';` then call `KTComponents.init()` after DOM ready.
|
|
16
|
-
- **Types (TypeScript):** Types are exported from the same entry; use the package root (e.g. `@keenthemes/ktui`) for types, not deep `lib/esm` paths.
|
|
17
|
-
- **Styles:** Include KtUI CSS (e.g. from the package or built `dist/styles.css`) and ensure Tailwind and CSS variables are set up as in the [installation docs](https://ktui.io/docs/getting-started/installation).
|
|
18
|
-
|
|
19
|
-
## Initialization
|
|
20
|
-
|
|
21
|
-
- Call `KTComponents.init()` once after the DOM is ready so that data-attribute–driven components (dropdowns, modals, tabs, etc.) are initialized.
|
|
22
|
-
- **Frameworks (React, Vue, Next):** Run init after the root is mounted (e.g. `useEffect` with empty deps, or `onMounted`) so the DOM with `data-kt-*` attributes exists before init.
|
|
23
|
-
- For dynamically added markup (e.g. after route change or lazy load), run `KTComponents.init()` again or use per-component init (e.g. `KTModal.init()` on the new container).
|
|
24
|
-
- **SSR:** Call init only on the client (e.g. inside `useEffect` or when `typeof document !== 'undefined'`), never during server render.
|
|
25
|
-
- Per-component init is also available (e.g. `KTModal.init()`) when not using the full init.
|
|
26
|
-
|
|
27
|
-
## Documentation
|
|
28
|
-
|
|
29
|
-
- **Official docs:** [https://ktui.io](https://ktui.io) — use for component APIs, examples, and markup.
|
|
30
|
-
- **Doc paths:** Getting started: `ktui.io/docs/installation`, `ktui.io/docs/theming`, `ktui.io/docs/typescript`, `ktui.io/docs/dark-mode`, `ktui.io/docs/rtl`, `ktui.io/docs/changelog`. Components: `ktui.io/docs/<component>` (e.g. `ktui.io/docs/modal`, `ktui.io/docs/tooltip`, `ktui.io/docs/datatable`, `ktui.io/docs/dropdown`, `ktui.io/docs/repeater`).
|
|
31
|
-
- Prefer docs and examples from ktui.io over inferring markup or options.
|
|
32
|
-
|
|
33
|
-
## Common pitfalls
|
|
34
|
-
|
|
35
|
-
- **Components not opening or reacting:** Ensure `KTComponents.init()` ran after the relevant DOM was in the page; for SPAs, run init after navigation or after injecting KtUI markup.
|
|
36
|
-
- **Styles missing or wrong:** Confirm KtUI CSS and CSS variables are loaded (see [installation](https://ktui.io/docs/installation), [theming](https://ktui.io/docs/theming)).
|
|
37
|
-
- **SSR/hydration:** Do not call init during server render; run it only in the browser after mount.
|
|
38
|
-
|
|
39
|
-
## Component list (reference)
|
|
40
|
-
|
|
41
|
-
Components exported from `@keenthemes/ktui` include: KTDropdown, KTModal, KTDrawer, KTCollapse, KTDismiss, KTTabs, KTAccordion, KTScrollspy, KTScrollable, KTScrollto, KTSticky, KTReparent, KTToggle, KTTooltip, KTStepper, KTThemeSwitch, KTImageInput, KTTogglePassword, KTDataTable, KTSelect, KTToast, KTRating, KTRepeater. See the package exports or ktui.io for the full list and usage.
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ktui-theming
|
|
3
|
-
description: Guides KtUI theming with CSS variables, light/dark mode, and semantic colors. Use this skill when the user asks about KtUI colors, theme, dark mode, customizing appearance, or CSS variables for KtUI.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# KtUI Theming
|
|
7
|
-
|
|
8
|
-
Use this skill when working with [KtUI](https://ktui.io) theming—colors, dark mode, and custom appearance. KtUI uses CSS variables and a background/foreground convention; prefer these over inventing custom classes.
|
|
9
|
-
|
|
10
|
-
## Convention
|
|
11
|
-
|
|
12
|
-
- **Background/foreground:** KtUI follows a Shadcn-inspired convention. Background colors omit the `-background` suffix (e.g. `bg-primary` uses `var(--primary)`). Foreground colors use `-foreground` (e.g. `text-primary-foreground`).
|
|
13
|
-
- **Semantic classes:** Use KtUI semantic utilities (e.g. `bg-background`, `text-foreground`, `bg-primary`, `text-card`, `kt-btn`, `kt-card`) so theming stays consistent.
|
|
14
|
-
|
|
15
|
-
## Key CSS variables
|
|
16
|
-
|
|
17
|
-
Define or override in your Tailwind entry (e.g. `style.css`). KtUI expects at least:
|
|
18
|
-
|
|
19
|
-
- **Surfaces:** `--background`, `--foreground`; `--card`, `--card-foreground`; `--popover`, `--popover-foreground`.
|
|
20
|
-
- **Brand:** `--primary`, `--primary-foreground`; `--secondary`, `--secondary-foreground`.
|
|
21
|
-
- **Muted / accent:** `--muted`, `--muted-foreground`; `--accent`, `--accent-foreground`.
|
|
22
|
-
- **Destructive:** `--destructive`, `--destructive-foreground`.
|
|
23
|
-
- **Borders and inputs:** `--border`, `--input`, `--ring`.
|
|
24
|
-
- **Radius:** `--radius` (e.g. `0.5rem`).
|
|
25
|
-
|
|
26
|
-
Use `oklch()` or any valid CSS color. Example:
|
|
27
|
-
|
|
28
|
-
```css
|
|
29
|
-
:root {
|
|
30
|
-
--background: oklch(1 0 0);
|
|
31
|
-
--foreground: oklch(14.1% 0.005 285.823);
|
|
32
|
-
--primary: oklch(62.3% 0.214 259.815);
|
|
33
|
-
--primary-foreground: oklch(1 0 0);
|
|
34
|
-
--radius: 0.5rem;
|
|
35
|
-
}
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Dark mode
|
|
39
|
-
|
|
40
|
-
- **Variables:** Override the same variables under a `.dark` scope (or your dark-mode class). KtUI docs use `.dark` for dark theme.
|
|
41
|
-
- **Tailwind:** Add the dark variant, e.g. `@custom-variant dark (&:is(.dark *));` in your Tailwind entry so utilities like `dark:bg-background` work.
|
|
42
|
-
- **Theme switch:** Use the KTThemeSwitch component or toggle the `.dark` class on `<html>` (or a wrapper) to switch between light and dark variables.
|
|
43
|
-
|
|
44
|
-
## Documentation
|
|
45
|
-
|
|
46
|
-
- **Theming:** [ktui.io/docs/theming](https://ktui.io/docs/theming)
|
|
47
|
-
- **Dark mode:** [ktui.io/docs/dark-mode](https://ktui.io/docs/dark-mode)
|
|
48
|
-
- **Installation** (variables setup): [ktui.io/docs/installation](https://ktui.io/docs/installation)
|
|
49
|
-
|
|
50
|
-
Prefer the official variable list and examples from these pages over guessing variable names or values.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
-
* Copyright 2025 by Keenthemes Inc
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
KTDataTableEmit,
|
|
8
|
-
KTDataTableEventAdapter,
|
|
9
|
-
} from './datatable-contracts';
|
|
10
|
-
|
|
11
|
-
export function createDataTableEventAdapter(
|
|
12
|
-
fireEvent: KTDataTableEmit,
|
|
13
|
-
dispatchEvent: KTDataTableEmit,
|
|
14
|
-
): KTDataTableEventAdapter {
|
|
15
|
-
return {
|
|
16
|
-
emit(eventName: string, eventData?: object): void {
|
|
17
|
-
fireEvent(eventName, eventData);
|
|
18
|
-
dispatchEvent(eventName, eventData);
|
|
19
|
-
},
|
|
20
|
-
};
|
|
21
|
-
}
|