@keenthemes/ktui 1.2.4 → 1.2.6
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/dist/ktui.js +2551 -2817
- package/dist/ktui.min.js +1 -1
- package/dist/ktui.min.js.map +1 -1
- package/dist/styles.css +136 -40
- package/lib/cjs/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-checkbox.js +34 -15
- package/lib/cjs/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-contracts.d.ts +3 -3
- package/lib/cjs/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js +328 -0
- package/lib/cjs/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/cjs/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-local-provider.js +18 -10
- 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 +40 -25
- package/lib/cjs/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-remote-provider.js +3 -0
- package/lib/cjs/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/cjs/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable-table-renderer.js +14 -6
- package/lib/cjs/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/cjs/components/datatable/datatable.d.ts +9 -0
- package/lib/cjs/components/datatable/datatable.d.ts.map +1 -1
- package/lib/cjs/components/datatable/datatable.js +200 -61
- 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 +27 -0
- package/lib/cjs/components/datatable/types.d.ts.map +1 -1
- package/lib/cjs/components/dropdown/dropdown.d.ts +2 -2
- package/lib/cjs/components/dropdown/dropdown.d.ts.map +1 -1
- package/lib/cjs/components/dropdown/dropdown.js +68 -31
- package/lib/cjs/components/dropdown/dropdown.js.map +1 -1
- package/lib/cjs/components/input-number/index.d.ts +7 -0
- package/lib/cjs/components/input-number/index.d.ts.map +1 -0
- package/lib/cjs/components/input-number/index.js +10 -0
- package/lib/cjs/components/input-number/index.js.map +1 -0
- package/lib/cjs/components/input-number/input-number.d.ts +40 -0
- package/lib/cjs/components/input-number/input-number.d.ts.map +1 -0
- package/lib/cjs/components/input-number/input-number.js +248 -0
- package/lib/cjs/components/input-number/input-number.js.map +1 -0
- package/lib/cjs/components/input-number/types.d.ts +30 -0
- package/lib/cjs/components/input-number/types.d.ts.map +1 -0
- package/lib/cjs/components/input-number/types.js +7 -0
- package/lib/cjs/components/input-number/types.js.map +1 -0
- package/lib/cjs/components/select/config.d.ts +1 -0
- package/lib/cjs/components/select/config.d.ts.map +1 -1
- package/lib/cjs/components/select/config.js +2 -1
- package/lib/cjs/components/select/config.js.map +1 -1
- package/lib/cjs/components/select/select.d.ts +8 -1
- package/lib/cjs/components/select/select.d.ts.map +1 -1
- package/lib/cjs/components/select/select.js +14 -1
- package/lib/cjs/components/select/select.js.map +1 -1
- package/lib/cjs/components/select/tags.d.ts.map +1 -1
- package/lib/cjs/components/select/tags.js +10 -0
- package/lib/cjs/components/select/tags.js.map +1 -1
- package/lib/cjs/index.d.ts +5 -1
- package/lib/cjs/index.d.ts.map +1 -1
- package/lib/cjs/index.js +5 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-checkbox.js +34 -15
- package/lib/esm/components/datatable/datatable-checkbox.js.map +1 -1
- package/lib/esm/components/datatable/datatable-contracts.d.ts +3 -3
- package/lib/esm/components/datatable/datatable-contracts.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts +7 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.d.ts.map +1 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js +324 -0
- package/lib/esm/components/datatable/datatable-layout-plugin.js.map +1 -0
- package/lib/esm/components/datatable/datatable-local-provider.d.ts +2 -2
- package/lib/esm/components/datatable/datatable-local-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-local-provider.js +18 -10
- 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 +40 -25
- package/lib/esm/components/datatable/datatable-pagination-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable-remote-provider.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-remote-provider.js +3 -0
- package/lib/esm/components/datatable/datatable-remote-provider.js.map +1 -1
- package/lib/esm/components/datatable/datatable-table-renderer.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable-table-renderer.js +14 -6
- package/lib/esm/components/datatable/datatable-table-renderer.js.map +1 -1
- package/lib/esm/components/datatable/datatable.d.ts +9 -0
- package/lib/esm/components/datatable/datatable.d.ts.map +1 -1
- package/lib/esm/components/datatable/datatable.js +200 -61
- 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 +27 -0
- package/lib/esm/components/datatable/types.d.ts.map +1 -1
- package/lib/esm/components/dropdown/dropdown.d.ts +2 -2
- package/lib/esm/components/dropdown/dropdown.d.ts.map +1 -1
- package/lib/esm/components/dropdown/dropdown.js +68 -31
- package/lib/esm/components/dropdown/dropdown.js.map +1 -1
- package/lib/esm/components/input-number/index.d.ts +7 -0
- package/lib/esm/components/input-number/index.d.ts.map +1 -0
- package/lib/esm/components/input-number/index.js +6 -0
- package/lib/esm/components/input-number/index.js.map +1 -0
- package/lib/esm/components/input-number/input-number.d.ts +40 -0
- package/lib/esm/components/input-number/input-number.d.ts.map +1 -0
- package/lib/esm/components/input-number/input-number.js +245 -0
- package/lib/esm/components/input-number/input-number.js.map +1 -0
- package/lib/esm/components/input-number/types.d.ts +30 -0
- package/lib/esm/components/input-number/types.d.ts.map +1 -0
- package/lib/esm/components/input-number/types.js +6 -0
- package/lib/esm/components/input-number/types.js.map +1 -0
- package/lib/esm/components/select/config.d.ts +1 -0
- package/lib/esm/components/select/config.d.ts.map +1 -1
- package/lib/esm/components/select/config.js +2 -1
- package/lib/esm/components/select/config.js.map +1 -1
- package/lib/esm/components/select/select.d.ts +8 -1
- package/lib/esm/components/select/select.d.ts.map +1 -1
- package/lib/esm/components/select/select.js +14 -1
- package/lib/esm/components/select/select.js.map +1 -1
- package/lib/esm/components/select/tags.d.ts.map +1 -1
- package/lib/esm/components/select/tags.js +11 -1
- package/lib/esm/components/select/tags.js.map +1 -1
- package/lib/esm/index.d.ts +5 -1
- package/lib/esm/index.d.ts.map +1 -1
- package/lib/esm/index.js +3 -0
- package/lib/esm/index.js.map +1 -1
- package/package.json +5 -11
- package/src/components/datatable/__tests__/locked-layout.test.ts +257 -0
- package/src/components/datatable/__tests__/pagination-reset.test.ts +18 -0
- package/src/components/datatable/datatable-checkbox.ts +35 -27
- package/src/components/datatable/datatable-contracts.ts +3 -3
- package/src/components/datatable/datatable-layout-plugin.ts +449 -0
- package/src/components/datatable/datatable-local-provider.ts +21 -14
- package/src/components/datatable/datatable-pagination-renderer.ts +40 -29
- package/src/components/datatable/datatable-remote-provider.ts +3 -0
- package/src/components/datatable/datatable-table-renderer.ts +40 -32
- package/src/components/datatable/datatable.css +98 -0
- package/src/components/datatable/datatable.ts +223 -86
- package/src/components/datatable/index.ts +5 -0
- package/src/components/datatable/types.ts +33 -0
- package/src/components/dropdown/dropdown.ts +86 -58
- package/src/components/input/input-group.css +14 -1
- package/src/components/input-number/__tests__/input-number.test.ts +278 -0
- package/src/components/input-number/index.ts +11 -0
- package/src/components/input-number/input-number.ts +267 -0
- package/src/components/input-number/types.ts +32 -0
- package/src/components/select/__tests__/ux-behaviors.test.ts +72 -0
- package/src/components/select/config.ts +3 -1
- package/src/components/select/select.css +23 -20
- package/src/components/select/select.ts +15 -1
- package/src/components/select/tags.ts +14 -1
- package/src/index.ts +14 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import KTData from '../../helpers/data';
|
|
7
|
+
import KTComponent from '../component';
|
|
8
|
+
import {
|
|
9
|
+
KTInputNumberConfigInterface,
|
|
10
|
+
KTInputNumberEventPayloadInterface,
|
|
11
|
+
KTInputNumberInterface,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
declare global {
|
|
15
|
+
interface Window {
|
|
16
|
+
KTInputNumber: typeof KTInputNumber;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class KTInputNumber
|
|
21
|
+
extends KTComponent
|
|
22
|
+
implements KTInputNumberInterface
|
|
23
|
+
{
|
|
24
|
+
protected override _name: string = 'input-number';
|
|
25
|
+
protected override _defaultConfig: KTInputNumberConfigInterface = {};
|
|
26
|
+
protected override _config: KTInputNumberConfigInterface =
|
|
27
|
+
this._defaultConfig;
|
|
28
|
+
protected _numberInput: HTMLInputElement | null = null;
|
|
29
|
+
protected _onNativeInput: ((e: Event) => void) | null = null;
|
|
30
|
+
protected _onNativeChange: ((e: Event) => void) | null = null;
|
|
31
|
+
protected _onDecrementClick: ((e: Event) => void) | null = null;
|
|
32
|
+
protected _onIncrementClick: ((e: Event) => void) | null = null;
|
|
33
|
+
protected _decrementElement: HTMLElement | null = null;
|
|
34
|
+
protected _incrementElement: HTMLElement | null = null;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
element: HTMLElement,
|
|
38
|
+
config: KTInputNumberConfigInterface | null = null,
|
|
39
|
+
) {
|
|
40
|
+
super();
|
|
41
|
+
|
|
42
|
+
const input = KTInputNumber.findNumberInput(element);
|
|
43
|
+
if (!input) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (this._shouldSkipInit(element)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this._numberInput = input;
|
|
52
|
+
this._init(element);
|
|
53
|
+
this._buildConfig(config);
|
|
54
|
+
|
|
55
|
+
this._onNativeInput = this._handleNativeInput.bind(this);
|
|
56
|
+
this._onNativeChange = this._handleNativeChange.bind(this);
|
|
57
|
+
this._element?.addEventListener('input', this._onNativeInput);
|
|
58
|
+
this._element?.addEventListener('change', this._onNativeChange);
|
|
59
|
+
|
|
60
|
+
this._decrementElement =
|
|
61
|
+
this._element?.querySelector<HTMLElement>(
|
|
62
|
+
'[data-kt-input-number-decrement]',
|
|
63
|
+
) ?? null;
|
|
64
|
+
this._incrementElement =
|
|
65
|
+
this._element?.querySelector<HTMLElement>(
|
|
66
|
+
'[data-kt-input-number-increment]',
|
|
67
|
+
) ?? null;
|
|
68
|
+
|
|
69
|
+
this._onDecrementClick = (e: Event) => {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
if (!this._numberInput || this._numberInput.disabled) return;
|
|
72
|
+
if (typeof this._numberInput.stepDown === 'function') {
|
|
73
|
+
this._numberInput.stepDown();
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
this._onIncrementClick = (e: Event) => {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
if (!this._numberInput || this._numberInput.disabled) return;
|
|
79
|
+
if (typeof this._numberInput.stepUp === 'function') {
|
|
80
|
+
this._numberInput.stepUp();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (this._decrementElement && this._onDecrementClick) {
|
|
85
|
+
this._decrementElement.addEventListener('click', this._onDecrementClick);
|
|
86
|
+
}
|
|
87
|
+
if (this._incrementElement && this._onIncrementClick) {
|
|
88
|
+
this._incrementElement.addEventListener('click', this._onIncrementClick);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private static findNumberInput(root: HTMLElement): HTMLInputElement | null {
|
|
93
|
+
if (root instanceof HTMLInputElement && root.type === 'number') {
|
|
94
|
+
return root;
|
|
95
|
+
}
|
|
96
|
+
return root.querySelector<HTMLInputElement>('input[type="number"]');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
protected _getNumericMin(): number | undefined {
|
|
100
|
+
const input = this._numberInput;
|
|
101
|
+
if (!input) return undefined;
|
|
102
|
+
if (typeof input.min === 'string' && input.min !== '') {
|
|
103
|
+
const n = parseFloat(input.min);
|
|
104
|
+
if (Number.isFinite(n)) return n;
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
protected _getNumericMax(): number | undefined {
|
|
110
|
+
const input = this._numberInput;
|
|
111
|
+
if (!input) return undefined;
|
|
112
|
+
if (typeof input.max === 'string' && input.max !== '') {
|
|
113
|
+
const n = parseFloat(input.max);
|
|
114
|
+
if (Number.isFinite(n)) return n;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected _getStepForPayload(): number | undefined {
|
|
120
|
+
const input = this._numberInput;
|
|
121
|
+
if (!input) return undefined;
|
|
122
|
+
const raw = input.getAttribute('step');
|
|
123
|
+
if (raw === 'any') return undefined;
|
|
124
|
+
if (raw === null || raw === '') return 1;
|
|
125
|
+
const n = parseFloat(raw);
|
|
126
|
+
return Number.isFinite(n) && n > 0 ? n : 1;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
protected _getCurrentNumericValue(): number | null {
|
|
130
|
+
const input = this._numberInput;
|
|
131
|
+
if (!input) return null;
|
|
132
|
+
if (input.value === '') {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const n =
|
|
136
|
+
typeof input.valueAsNumber === 'number' &&
|
|
137
|
+
!Number.isNaN(input.valueAsNumber)
|
|
138
|
+
? input.valueAsNumber
|
|
139
|
+
: parseFloat(input.value);
|
|
140
|
+
return Number.isFinite(n) ? n : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
protected _buildEventPayload(): KTInputNumberEventPayloadInterface {
|
|
144
|
+
const input = this._numberInput;
|
|
145
|
+
const min = this._getNumericMin();
|
|
146
|
+
const max = this._getNumericMax();
|
|
147
|
+
const step = this._getStepForPayload();
|
|
148
|
+
const value = this._getCurrentNumericValue();
|
|
149
|
+
const valueAsString = input?.value ?? '';
|
|
150
|
+
return {
|
|
151
|
+
value,
|
|
152
|
+
valueAsString,
|
|
153
|
+
...(min !== undefined ? { min } : {}),
|
|
154
|
+
...(max !== undefined ? { max } : {}),
|
|
155
|
+
...(step !== undefined ? { step } : {}),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
protected _handleNativeInput(_event: Event): void {
|
|
160
|
+
const event = _event as Event;
|
|
161
|
+
const target = event.target;
|
|
162
|
+
if (!(target instanceof HTMLInputElement) || target.type !== 'number') {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this._numberInput = target;
|
|
167
|
+
const payload = this._buildEventPayload();
|
|
168
|
+
this._fireEvent('input', payload);
|
|
169
|
+
this._dispatchEvent('kt.input-number.input', payload);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
protected _handleNativeChange(_event: Event): void {
|
|
173
|
+
const event = _event as Event;
|
|
174
|
+
const target = event.target;
|
|
175
|
+
if (!(target instanceof HTMLInputElement) || target.type !== 'number') {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
this._numberInput = target;
|
|
180
|
+
const payload = this._buildEventPayload();
|
|
181
|
+
this._fireEvent('change', payload);
|
|
182
|
+
this._dispatchEvent('kt.input-number.change', payload);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public getNumberInput(): HTMLInputElement | null {
|
|
186
|
+
return this._numberInput;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
public getValue(): number | null {
|
|
190
|
+
return this._getCurrentNumericValue();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
public override dispose(): void {
|
|
194
|
+
if (this._element) {
|
|
195
|
+
if (this._onNativeInput) {
|
|
196
|
+
this._element.removeEventListener('input', this._onNativeInput);
|
|
197
|
+
}
|
|
198
|
+
if (this._onNativeChange) {
|
|
199
|
+
this._element.removeEventListener('change', this._onNativeChange);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (this._decrementElement && this._onDecrementClick) {
|
|
203
|
+
this._decrementElement.removeEventListener(
|
|
204
|
+
'click',
|
|
205
|
+
this._onDecrementClick,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (this._incrementElement && this._onIncrementClick) {
|
|
209
|
+
this._incrementElement.removeEventListener(
|
|
210
|
+
'click',
|
|
211
|
+
this._onIncrementClick,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
this._onNativeInput = null;
|
|
215
|
+
this._onNativeChange = null;
|
|
216
|
+
this._onDecrementClick = null;
|
|
217
|
+
this._onIncrementClick = null;
|
|
218
|
+
this._decrementElement = null;
|
|
219
|
+
this._incrementElement = null;
|
|
220
|
+
this._numberInput = null;
|
|
221
|
+
super.dispose();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
public static getInstance(element: HTMLElement): KTInputNumber | null {
|
|
225
|
+
if (!element) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
if (KTData.has(element, 'input-number')) {
|
|
229
|
+
return KTData.get(element, 'input-number') as KTInputNumber;
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
public static getOrCreateInstance(
|
|
235
|
+
element: HTMLElement,
|
|
236
|
+
config?: KTInputNumberConfigInterface,
|
|
237
|
+
): KTInputNumber | null {
|
|
238
|
+
const existing = this.getInstance(element);
|
|
239
|
+
if (existing) {
|
|
240
|
+
return existing;
|
|
241
|
+
}
|
|
242
|
+
if (!this.findNumberInput(element)) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
new KTInputNumber(element, config ?? undefined);
|
|
246
|
+
return this.getInstance(element);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
public static createInstances(): void {
|
|
250
|
+
document
|
|
251
|
+
.querySelectorAll<HTMLElement>('[data-kt-input-number]')
|
|
252
|
+
.forEach((el) => {
|
|
253
|
+
if (el.getAttribute('data-kt-input-number-lazy') === 'true') {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
new KTInputNumber(el);
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
public static init(): void {
|
|
261
|
+
KTInputNumber.createInstances();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (typeof window !== 'undefined') {
|
|
266
|
+
window.KTInputNumber = KTInputNumber;
|
|
267
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KTUI - Free & Open-Source Tailwind UI Components by Keenthemes
|
|
3
|
+
* Copyright 2025 by Keenthemes Inc
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface KTInputNumberConfigInterface {
|
|
7
|
+
/** Reserved for future options merged from data attributes. */
|
|
8
|
+
[key: string]: string | number | boolean | undefined;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface KTInputNumberEventPayloadInterface {
|
|
12
|
+
/** Parsed numeric value, or `null` when the input is empty or not a finite number. */
|
|
13
|
+
value: number | null;
|
|
14
|
+
/** Raw `value` attribute string of the controlled input. */
|
|
15
|
+
valueAsString: string;
|
|
16
|
+
/** Effective minimum when set on the input. */
|
|
17
|
+
min?: number;
|
|
18
|
+
/** Effective maximum when set on the input. */
|
|
19
|
+
max?: number;
|
|
20
|
+
/** Effective step when numeric; omitted when `step="any"` or missing. */
|
|
21
|
+
step?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface KTInputNumberInterface {
|
|
25
|
+
getNumberInput(): HTMLInputElement | null;
|
|
26
|
+
getValue(): number | null;
|
|
27
|
+
getOption(name: string): unknown;
|
|
28
|
+
getElement(): HTMLElement | null;
|
|
29
|
+
on(eventType: string, callback: CallableFunction): string;
|
|
30
|
+
off(eventType: string, eventId: string): void;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
}
|
|
@@ -1279,4 +1279,76 @@ describe('KTSelect UX Behaviors', () => {
|
|
|
1279
1279
|
expect(dropdownOption?.getAttribute('aria-selected')).toBe('true');
|
|
1280
1280
|
});
|
|
1281
1281
|
});
|
|
1282
|
+
|
|
1283
|
+
describe('getValue()', () => {
|
|
1284
|
+
it('returns null for single-select with no explicit selection after clear', async () => {
|
|
1285
|
+
const selectEl = createSelectElement([
|
|
1286
|
+
{ value: '1', text: 'Option 1' },
|
|
1287
|
+
{ value: '2', text: 'Option 2' },
|
|
1288
|
+
]);
|
|
1289
|
+
container.appendChild(selectEl);
|
|
1290
|
+
|
|
1291
|
+
const select = new KTSelect(selectEl, { height: 250 });
|
|
1292
|
+
await waitForInit(select);
|
|
1293
|
+
|
|
1294
|
+
const option1 = selectEl.querySelector(
|
|
1295
|
+
'option[value="1"]',
|
|
1296
|
+
) as HTMLOptionElement;
|
|
1297
|
+
select.setSelectedOptions([option1]);
|
|
1298
|
+
await waitFor(50);
|
|
1299
|
+
select.setSelectedOptions([]);
|
|
1300
|
+
await waitFor(50);
|
|
1301
|
+
|
|
1302
|
+
expect(select.getValue()).toBe(null);
|
|
1303
|
+
expect(select.getSelectedOptions()).toEqual([]);
|
|
1304
|
+
});
|
|
1305
|
+
|
|
1306
|
+
it('returns the selected value for single-select', async () => {
|
|
1307
|
+
const selectEl = createSelectElement([
|
|
1308
|
+
{ value: '1', text: 'Option 1' },
|
|
1309
|
+
{ value: '2', text: 'Option 2' },
|
|
1310
|
+
]);
|
|
1311
|
+
container.appendChild(selectEl);
|
|
1312
|
+
|
|
1313
|
+
const select = new KTSelect(selectEl, { height: 250 });
|
|
1314
|
+
await waitForInit(select);
|
|
1315
|
+
|
|
1316
|
+
const option2 = selectEl.querySelector(
|
|
1317
|
+
'option[value="2"]',
|
|
1318
|
+
) as HTMLOptionElement;
|
|
1319
|
+
select.setSelectedOptions([option2]);
|
|
1320
|
+
await waitFor(50);
|
|
1321
|
+
|
|
1322
|
+
expect(select.getValue()).toBe('2');
|
|
1323
|
+
expect(select.getSelectedOptions()).toEqual(['2']);
|
|
1324
|
+
});
|
|
1325
|
+
|
|
1326
|
+
it('returns null for multiple-select even when options are selected', async () => {
|
|
1327
|
+
const selectEl = createSelectElement([
|
|
1328
|
+
{ value: 'a', text: 'A' },
|
|
1329
|
+
{ value: 'b', text: 'B' },
|
|
1330
|
+
]);
|
|
1331
|
+
selectEl.setAttribute('multiple', 'multiple');
|
|
1332
|
+
container.appendChild(selectEl);
|
|
1333
|
+
|
|
1334
|
+
const select = new KTSelect(selectEl, {
|
|
1335
|
+
multiple: true,
|
|
1336
|
+
height: 250,
|
|
1337
|
+
});
|
|
1338
|
+
await waitForInit(select);
|
|
1339
|
+
|
|
1340
|
+
const optionA = selectEl.querySelector(
|
|
1341
|
+
'option[value="a"]',
|
|
1342
|
+
) as HTMLOptionElement;
|
|
1343
|
+
const optionB = selectEl.querySelector(
|
|
1344
|
+
'option[value="b"]',
|
|
1345
|
+
) as HTMLOptionElement;
|
|
1346
|
+
select.setSelectedOptions([optionA, optionB]);
|
|
1347
|
+
await waitFor(50);
|
|
1348
|
+
|
|
1349
|
+
expect(select.getValue()).toBe(null);
|
|
1350
|
+
expect(select.getSelectedOptions()).toContain('a');
|
|
1351
|
+
expect(select.getSelectedOptions()).toContain('b');
|
|
1352
|
+
});
|
|
1353
|
+
});
|
|
1282
1354
|
});
|
|
@@ -59,7 +59,8 @@ export const DefaultConfig: KTSelectConfigInterface = {
|
|
|
59
59
|
selectAllText: 'Select all', // Text for the "Select All" option (if implemented)
|
|
60
60
|
clearAllText: 'Clear all', // Text for the "Clear All" option (if implemented)
|
|
61
61
|
enableSelectAll: false, // Enable/disable "Select All" button for multi-select
|
|
62
|
-
showSelectedCount:
|
|
62
|
+
showSelectedCount: false, // Tags mode: show "N selected" before chips when true
|
|
63
|
+
selectedCountText: '{{count}} selected', // Tags mode; use {{count}} placeholder
|
|
63
64
|
renderSelected: undefined, // Custom function to render the selected value(s) in the display area
|
|
64
65
|
|
|
65
66
|
// Accessibility & Usability
|
|
@@ -112,6 +113,7 @@ export interface KTSelectConfigInterface {
|
|
|
112
113
|
clearAllText?: string;
|
|
113
114
|
enableSelectAll?: boolean;
|
|
114
115
|
showSelectedCount?: boolean;
|
|
116
|
+
selectedCountText?: string;
|
|
115
117
|
renderSelected?: (selectedOptions: string[]) => string;
|
|
116
118
|
|
|
117
119
|
// Accessibility & Usability
|
|
@@ -137,34 +137,37 @@
|
|
|
137
137
|
|
|
138
138
|
/* Enhanced Tag Styles */
|
|
139
139
|
.kt-select-tag {
|
|
140
|
-
@apply inline-flex items-center gap-
|
|
141
|
-
@apply bg-accent/10 text-accent-foreground border border-border;
|
|
140
|
+
@apply inline-flex items-center gap-1 px-2 py-1 rounded-none text-xs font-medium leading-tight;
|
|
141
|
+
@apply bg-accent/10 text-accent-foreground border-[0.5px] border-border/80;
|
|
142
142
|
@apply max-w-[200px] truncate flex-shrink-0;
|
|
143
|
-
@apply shadow-sm;
|
|
144
|
-
@apply leading-tight;
|
|
145
143
|
}
|
|
146
144
|
|
|
147
145
|
.kt-select-tag-remove {
|
|
148
|
-
@apply flex items-center justify-center
|
|
146
|
+
@apply flex items-center justify-center size-4 rounded-full;
|
|
147
|
+
@apply border-0 bg-transparent p-0 shadow-none;
|
|
149
148
|
@apply hover:bg-accent/20 text-muted-foreground hover:text-accent-foreground;
|
|
150
|
-
@apply transition-
|
|
149
|
+
@apply transition-[color,background-color,transform] duration-200 cursor-pointer;
|
|
151
150
|
@apply flex-shrink-0 opacity-100;
|
|
152
|
-
@apply hover:scale-
|
|
151
|
+
@apply hover:scale-105;
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
.kt-select-tag-remove svg {
|
|
156
|
-
@apply
|
|
155
|
+
@apply size-3;
|
|
157
156
|
@apply transition-transform duration-200;
|
|
158
157
|
}
|
|
159
158
|
|
|
160
159
|
.kt-select-tag-remove:hover svg {
|
|
161
|
-
@apply scale-
|
|
160
|
+
@apply scale-105;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.kt-select-selected-count {
|
|
164
|
+
@apply inline-flex items-center text-xs font-medium leading-tight text-muted-foreground flex-shrink-0;
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
/* Enhanced Multi-select display container */
|
|
165
168
|
.kt-select-display[data-multiple='true'] {
|
|
166
|
-
@apply flex flex-wrap items-center gap-
|
|
167
|
-
@apply w-full;
|
|
169
|
+
@apply flex flex-wrap items-center gap-1;
|
|
170
|
+
@apply w-full min-h-0;
|
|
168
171
|
@apply bg-background border border-input rounded-md;
|
|
169
172
|
@apply focus-within:border-ring focus-within:ring-2 focus-within:ring-ring/20;
|
|
170
173
|
@apply transition-all duration-200;
|
|
@@ -178,8 +181,8 @@
|
|
|
178
181
|
background-position: right 0.5rem center;
|
|
179
182
|
|
|
180
183
|
&[data-multiple='true'] {
|
|
181
|
-
@apply h-auto min-h-8.5 py-
|
|
182
|
-
background-position: right 0.5rem
|
|
184
|
+
@apply h-auto min-h-8.5 py-0 flex-wrap;
|
|
185
|
+
background-position: right 0.5rem center;
|
|
183
186
|
}
|
|
184
187
|
}
|
|
185
188
|
|
|
@@ -188,8 +191,8 @@
|
|
|
188
191
|
background-position: right 0.5rem center;
|
|
189
192
|
|
|
190
193
|
&[data-multiple='true'] {
|
|
191
|
-
@apply h-auto min-h-7;
|
|
192
|
-
background-position: right 0.5rem
|
|
194
|
+
@apply h-auto min-h-7 py-0 flex-wrap;
|
|
195
|
+
background-position: right 0.5rem center;
|
|
193
196
|
}
|
|
194
197
|
}
|
|
195
198
|
|
|
@@ -198,8 +201,8 @@
|
|
|
198
201
|
background-position: right 0.6rem center;
|
|
199
202
|
|
|
200
203
|
&[data-multiple='true'] {
|
|
201
|
-
@apply h-auto min-h-10 py-
|
|
202
|
-
background-position: right 0.6rem
|
|
204
|
+
@apply h-auto min-h-10 py-0 flex-wrap;
|
|
205
|
+
background-position: right 0.6rem center;
|
|
203
206
|
}
|
|
204
207
|
}
|
|
205
208
|
}
|
|
@@ -211,7 +214,7 @@
|
|
|
211
214
|
background-position: left 0.5rem center;
|
|
212
215
|
|
|
213
216
|
&[data-multiple='true'] {
|
|
214
|
-
background-position: left 0.5rem
|
|
217
|
+
background-position: left 0.5rem center;
|
|
215
218
|
}
|
|
216
219
|
}
|
|
217
220
|
|
|
@@ -219,7 +222,7 @@
|
|
|
219
222
|
background-position: left 0.5rem center;
|
|
220
223
|
|
|
221
224
|
&[data-multiple='true'] {
|
|
222
|
-
background-position: left 0.5rem
|
|
225
|
+
background-position: left 0.5rem center;
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
228
|
|
|
@@ -227,7 +230,7 @@
|
|
|
227
230
|
background-position: left 0.75rem center;
|
|
228
231
|
|
|
229
232
|
&[data-multiple='true'] {
|
|
230
|
-
background-position: left 0.75rem
|
|
233
|
+
background-position: left 0.75rem center;
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
}
|
|
@@ -1627,12 +1627,26 @@ export class KTSelect extends KTComponent {
|
|
|
1627
1627
|
}
|
|
1628
1628
|
|
|
1629
1629
|
/**
|
|
1630
|
-
*
|
|
1630
|
+
* Returns the current selection as **option value** strings (not labels, not DOM nodes).
|
|
1631
|
+
* For single-select, the array has zero or one entry; use {@link getValue} for a scalar.
|
|
1632
|
+
* For multiple-select, the array may contain multiple values in arbitrary order.
|
|
1631
1633
|
*/
|
|
1632
1634
|
public getSelectedOptions() {
|
|
1633
1635
|
return this._state.getSelectedOptions();
|
|
1634
1636
|
}
|
|
1635
1637
|
|
|
1638
|
+
/**
|
|
1639
|
+
* Returns the selected option **value** for single-select, or `null` when nothing is selected.
|
|
1640
|
+
* When the select is configured with `multiple: true`, always returns `null` — use {@link getSelectedOptions} instead.
|
|
1641
|
+
*/
|
|
1642
|
+
public getValue(): string | null {
|
|
1643
|
+
if (this._config.multiple) {
|
|
1644
|
+
return null;
|
|
1645
|
+
}
|
|
1646
|
+
const values = this._state.getSelectedOptions();
|
|
1647
|
+
return values.length > 0 ? values[0] : null;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1636
1650
|
/**
|
|
1637
1651
|
* Get configuration
|
|
1638
1652
|
*/
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { KTSelectConfigInterface } from './config';
|
|
7
7
|
import { KTSelect } from './select';
|
|
8
8
|
import { defaultTemplates } from './templates';
|
|
9
|
-
import { EventManager } from './utils';
|
|
9
|
+
import { EventManager, renderTemplateString } from './utils';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* KTSelectTags - Handles tags-specific functionality for KTSelect
|
|
@@ -50,6 +50,19 @@ export class KTSelectTags {
|
|
|
50
50
|
// Clear all existing content before adding tags
|
|
51
51
|
valueDisplayElement.innerHTML = '';
|
|
52
52
|
|
|
53
|
+
// Optional summary count (config exists for tags mode; non-tag display uses templates instead)
|
|
54
|
+
if (this._config.showSelectedCount && selectedOptions.length > 0) {
|
|
55
|
+
const countEl = document.createElement('span');
|
|
56
|
+
countEl.setAttribute('data-kt-select-selected-count', 'true');
|
|
57
|
+
countEl.className = 'kt-select-selected-count';
|
|
58
|
+
countEl.setAttribute('aria-live', 'polite');
|
|
59
|
+
countEl.textContent = renderTemplateString(
|
|
60
|
+
this._config.selectedCountText ?? '{{count}} selected',
|
|
61
|
+
{ count: selectedOptions.length },
|
|
62
|
+
);
|
|
63
|
+
valueDisplayElement.appendChild(countEl);
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
// Insert each tag before the display element
|
|
54
67
|
selectedOptions.forEach((optionValue) => {
|
|
55
68
|
// Find the original option element (in dropdown or select)
|
package/src/index.ts
CHANGED
|
@@ -34,6 +34,7 @@ import { KTRepeater } from './components/repeater';
|
|
|
34
34
|
import { KTClipboard } from './components/clipboard';
|
|
35
35
|
import { KTRangeSlider } from './components/range-slider';
|
|
36
36
|
import { KTPinInput } from './components/pin-input';
|
|
37
|
+
import { KTInputNumber } from './components/input-number';
|
|
37
38
|
import { KTCarousel } from './components/carousel';
|
|
38
39
|
|
|
39
40
|
export { KTDropdown } from './components/dropdown';
|
|
@@ -63,6 +64,7 @@ export { KTRepeater } from './components/repeater';
|
|
|
63
64
|
export { KTClipboard } from './components/clipboard';
|
|
64
65
|
export { KTRangeSlider } from './components/range-slider';
|
|
65
66
|
export { KTPinInput } from './components/pin-input';
|
|
67
|
+
export { KTInputNumber } from './components/input-number';
|
|
66
68
|
export { KTCarousel } from './components/carousel';
|
|
67
69
|
|
|
68
70
|
export type {
|
|
@@ -85,6 +87,11 @@ export type {
|
|
|
85
87
|
KTDataTableCheckConfigInterface,
|
|
86
88
|
KTDataTableCheckInterface,
|
|
87
89
|
KTDataTableCheckChangePayloadInterface,
|
|
90
|
+
KTDataTableLockedRowsConfigInterface,
|
|
91
|
+
KTDataTableLockedColumnsConfigInterface,
|
|
92
|
+
KTDataTableLockedLayoutConfigInterface,
|
|
93
|
+
KTDataTableLayoutPluginContextInterface,
|
|
94
|
+
KTDataTableLayoutPluginInterface,
|
|
88
95
|
} from './components/datatable';
|
|
89
96
|
export type {
|
|
90
97
|
KTDismissConfigInterface,
|
|
@@ -137,6 +144,11 @@ export type {
|
|
|
137
144
|
KTPinInputEventPayloadInterface,
|
|
138
145
|
KTPinInputInterface,
|
|
139
146
|
} from './components/pin-input';
|
|
147
|
+
export type {
|
|
148
|
+
KTInputNumberConfigInterface,
|
|
149
|
+
KTInputNumberEventPayloadInterface,
|
|
150
|
+
KTInputNumberInterface,
|
|
151
|
+
} from './components/input-number';
|
|
140
152
|
export type {
|
|
141
153
|
KTCarouselConfigInterface,
|
|
142
154
|
KTCarouselChangePayloadInterface,
|
|
@@ -217,6 +229,7 @@ export const KTComponents = {
|
|
|
217
229
|
KTClipboard.init();
|
|
218
230
|
KTRangeSlider.init();
|
|
219
231
|
KTPinInput.init();
|
|
232
|
+
KTInputNumber.init();
|
|
220
233
|
KTCarousel.init();
|
|
221
234
|
},
|
|
222
235
|
};
|
|
@@ -254,6 +267,7 @@ declare global {
|
|
|
254
267
|
KTClipboard: typeof KTClipboard;
|
|
255
268
|
KTRangeSlider: typeof KTRangeSlider;
|
|
256
269
|
KTPinInput: typeof KTPinInput;
|
|
270
|
+
KTInputNumber: typeof KTInputNumber;
|
|
257
271
|
KTCarousel: typeof KTCarousel;
|
|
258
272
|
KTComponents: typeof KTComponents;
|
|
259
273
|
}
|