@kupola/kupola 2.0.0-beta.2 → 2.0.0-beta.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/dist/kupola-core-datepicker.cjs.js +9 -9
- package/dist/kupola-core-datepicker.esm.js +9 -9
- package/dist/kupola-core-dialog.cjs.js +8 -8
- package/dist/kupola-core-dialog.esm.js +8 -8
- package/dist/kupola-core-drawer.cjs.js +5 -5
- package/dist/kupola-core-drawer.esm.js +5 -5
- package/dist/kupola-core-dropdown.cjs.js +4 -4
- package/dist/kupola-core-dropdown.esm.js +4 -4
- package/dist/kupola-core-i18n.cjs.js +1 -1
- package/dist/kupola-core-i18n.esm.js +1 -1
- package/dist/kupola-core-modal.cjs.js +2 -2
- package/dist/kupola-core-modal.esm.js +2 -2
- package/dist/kupola-core-numberinput.cjs.js +6 -5
- package/dist/kupola-core-numberinput.esm.js +6 -5
- package/dist/kupola-core-select.cjs.js +8 -7
- package/dist/kupola-core-select.esm.js +8 -7
- package/dist/kupola-core-table.cjs.js +1 -1
- package/dist/kupola-core-table.esm.js +1 -1
- package/dist/kupola-core-timepicker.cjs.js +3 -3
- package/dist/kupola-core-timepicker.esm.js +3 -3
- package/dist/kupola-core.cjs.js +1 -1
- package/dist/kupola-core.esm.js +1 -1
- package/package.json +1 -1
- package/packages/core/src/components/datepicker.js +11 -6
- package/packages/core/src/components/dialog.js +14 -7
- package/packages/core/src/components/drawer.js +5 -3
- package/packages/core/src/components/dropdown.js +15 -3
- package/packages/core/src/components/modal.js +20 -1
- package/packages/core/src/components/numberinput.js +12 -3
- package/packages/core/src/components/select.js +22 -5
- package/packages/core/src/components/table.js +3 -1
- package/packages/core/src/components/timepicker.js +2 -1
- package/packages/core/src/errors.js +80 -0
- package/packages/core/src/i18n.js +5 -0
- package/packages/core/src/index.js +3 -0
|
@@ -24,11 +24,13 @@
|
|
|
24
24
|
|
|
25
25
|
import { html } from '../template.js';
|
|
26
26
|
import { render } from '../render.js';
|
|
27
|
+
import { t } from '../i18n.js';
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Create a Select component instance.
|
|
30
31
|
*
|
|
31
32
|
* @param {Object} [options]
|
|
33
|
+
* @param {string} [options.label] Label text (creates <label> linked to select)
|
|
32
34
|
* @param {Array<{value:string, text:string}>} [options.items] Option list
|
|
33
35
|
* @param {string} [options.placeholder] Placeholder text
|
|
34
36
|
* @param {boolean} [options.searchable] Enable search filter
|
|
@@ -41,8 +43,9 @@ import { render } from '../render.js';
|
|
|
41
43
|
*/
|
|
42
44
|
export function Select(options = {}) {
|
|
43
45
|
const {
|
|
46
|
+
label = '',
|
|
44
47
|
items = [],
|
|
45
|
-
placeholder =
|
|
48
|
+
placeholder = null,
|
|
46
49
|
searchable = false,
|
|
47
50
|
clearable = false,
|
|
48
51
|
multiple = false,
|
|
@@ -51,6 +54,10 @@ export function Select(options = {}) {
|
|
|
51
54
|
onChange = null,
|
|
52
55
|
} = options;
|
|
53
56
|
|
|
57
|
+
const _placeholder = placeholder || t('select.placeholder');
|
|
58
|
+
const _id = label ? `ds-select-${Math.random().toString(36).slice(2, 8)}` : '';
|
|
59
|
+
const _hasLabel = !!label;
|
|
60
|
+
|
|
54
61
|
let _isOpen = false;
|
|
55
62
|
let _focusIndex = -1;
|
|
56
63
|
let _searchQuery = '';
|
|
@@ -158,7 +165,7 @@ export function Select(options = {}) {
|
|
|
158
165
|
valueEl.textContent = `${_selectedValues.size} selected`;
|
|
159
166
|
valueEl.classList.remove('ds-select__value--placeholder');
|
|
160
167
|
} else {
|
|
161
|
-
valueEl.textContent =
|
|
168
|
+
valueEl.textContent = _placeholder;
|
|
162
169
|
valueEl.classList.add('ds-select__value--placeholder');
|
|
163
170
|
}
|
|
164
171
|
}
|
|
@@ -230,6 +237,14 @@ export function Select(options = {}) {
|
|
|
230
237
|
e.preventDefault();
|
|
231
238
|
_setFocus(_focusIndex - 1);
|
|
232
239
|
break;
|
|
240
|
+
case 'Home':
|
|
241
|
+
e.preventDefault();
|
|
242
|
+
_setFocus(0);
|
|
243
|
+
break;
|
|
244
|
+
case 'End':
|
|
245
|
+
e.preventDefault();
|
|
246
|
+
_setFocus(_filteredItems().length - 1);
|
|
247
|
+
break;
|
|
233
248
|
case 'Enter':
|
|
234
249
|
e.preventDefault();
|
|
235
250
|
{
|
|
@@ -240,6 +255,7 @@ export function Select(options = {}) {
|
|
|
240
255
|
}
|
|
241
256
|
break;
|
|
242
257
|
case 'Escape':
|
|
258
|
+
case 'Tab':
|
|
243
259
|
e.preventDefault();
|
|
244
260
|
close();
|
|
245
261
|
break;
|
|
@@ -274,9 +290,10 @@ export function Select(options = {}) {
|
|
|
274
290
|
// are serialized as static text by the render system.
|
|
275
291
|
|
|
276
292
|
const tpl = html`
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
293
|
+
${_hasLabel ? html`<label class="ds-form-label" for="${_id}">${label}</label>` : ''}
|
|
294
|
+
<div class="ds-select" ${!label ? 'aria-label="Select"' : ''}>
|
|
295
|
+
<div class="ds-select__trigger" id="${_id}">
|
|
296
|
+
<span class="ds-select__value${!displayText ? ' ds-select__value--placeholder' : ''}">${displayText || _placeholder}</span>
|
|
280
297
|
${clearable ? html`<button class="ds-select__clear">×</button>` : ''}
|
|
281
298
|
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
282
299
|
<polyline points="6 9 12 15 18 9"/>
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @module Table
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { t } from '../i18n.js';
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* @typedef {Object} TableColumn
|
|
13
15
|
* @property {string} key - Column data key
|
|
@@ -382,7 +384,7 @@ export function Table(options = {}) {
|
|
|
382
384
|
const td = document.createElement('td');
|
|
383
385
|
td.colSpan = _getTotalColCount();
|
|
384
386
|
td.className = 'kupola-table-empty';
|
|
385
|
-
td.textContent = options.emptyText || '
|
|
387
|
+
td.textContent = options.emptyText || t('table.empty');
|
|
386
388
|
tr.appendChild(td);
|
|
387
389
|
tbody.appendChild(tr);
|
|
388
390
|
} else {
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
|
|
21
21
|
import { html } from '../template.js';
|
|
22
22
|
import { render } from '../render.js';
|
|
23
|
+
import { t } from '../i18n.js';
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
26
|
* Create a Timepicker component instance.
|
|
@@ -126,7 +127,7 @@ export function Timepicker(options = {}) {
|
|
|
126
127
|
const tpl = html`
|
|
127
128
|
<div class="ds-timepicker">
|
|
128
129
|
<div class="ds-timepicker__input-wrap">
|
|
129
|
-
<input class="ds-timepicker__input" type="text" readonly placeholder="
|
|
130
|
+
<input class="ds-timepicker__input" type="text" readonly placeholder="${t('timepicker.placeholder')}" />
|
|
130
131
|
<span class="ds-timepicker__icon">
|
|
131
132
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
132
133
|
</span>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
/**
|
|
3
|
+
* @kupola/core — Error boundary utility for graceful error handling.
|
|
4
|
+
*
|
|
5
|
+
* Wraps component rendering so that exceptions within a component
|
|
6
|
+
* do not crash the entire application. On error, a fallback element
|
|
7
|
+
* is rendered in place of the failed component.
|
|
8
|
+
*
|
|
9
|
+
* ```js
|
|
10
|
+
* import { ErrorBoundary } from '@kupola/core/errors';
|
|
11
|
+
*
|
|
12
|
+
* const view = ErrorBoundary(
|
|
13
|
+
* () => SomeRiskyComponent({ data: badData }),
|
|
14
|
+
* { fallback: (err) => html`<div class="ds-alert ds-alert--error">Error: ${err.message}</div>` }
|
|
15
|
+
* );
|
|
16
|
+
* container.appendChild(view.element);
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @module errors
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { html } from './template.js';
|
|
23
|
+
import { render } from './render.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Wrap a component factory with error handling.
|
|
27
|
+
*
|
|
28
|
+
* @param {Function} factory - Component factory function that returns { element, ... }
|
|
29
|
+
* @param {Object} [options]
|
|
30
|
+
* @param {Function} [options.fallback] - Fallback factory: (error) => TemplateResult|string
|
|
31
|
+
* @param {Function} [options.onError] - Error callback: (error) => void
|
|
32
|
+
* @returns {{ element: DocumentFragment|HTMLElement, error: Error|null }}
|
|
33
|
+
*/
|
|
34
|
+
export function ErrorBoundary(factory, options = {}) {
|
|
35
|
+
const {
|
|
36
|
+
fallback = null,
|
|
37
|
+
onError = null,
|
|
38
|
+
} = options;
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const result = factory();
|
|
42
|
+
return result;
|
|
43
|
+
} catch (err) {
|
|
44
|
+
if (onError) {
|
|
45
|
+
try { onError(err); } catch (_e) { /* swallow callback errors */ }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Log to console for debugging
|
|
49
|
+
console.error('[Kupola ErrorBoundary]', err);
|
|
50
|
+
|
|
51
|
+
// Create fallback element
|
|
52
|
+
const container = document.createDocumentFragment();
|
|
53
|
+
if (fallback) {
|
|
54
|
+
try {
|
|
55
|
+
const fallbackContent = typeof fallback === 'function' ? fallback(err) : fallback;
|
|
56
|
+
if (typeof fallbackContent === 'string') {
|
|
57
|
+
const el = document.createElement('div');
|
|
58
|
+
el.className = 'ds-error-boundary';
|
|
59
|
+
el.textContent = fallbackContent;
|
|
60
|
+
container.appendChild(el);
|
|
61
|
+
} else {
|
|
62
|
+
render(fallbackContent, container);
|
|
63
|
+
}
|
|
64
|
+
} catch (_fallbackErr) {
|
|
65
|
+
// If fallback also fails, show a minimal error
|
|
66
|
+
const el = document.createElement('div');
|
|
67
|
+
el.className = 'ds-error-boundary';
|
|
68
|
+
el.textContent = `Component error: ${err.message}`;
|
|
69
|
+
container.appendChild(el);
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
const el = document.createElement('div');
|
|
73
|
+
el.className = 'ds-error-boundary';
|
|
74
|
+
el.textContent = `Component error: ${err.message}`;
|
|
75
|
+
container.appendChild(el);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return { element: container, error: err };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -19,6 +19,9 @@ const _messages = {
|
|
|
19
19
|
'fileupload.drag': 'Drag files here or',
|
|
20
20
|
'fileupload.browse': 'Browse',
|
|
21
21
|
'empty.text': 'No data',
|
|
22
|
+
'datepicker.months': 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec',
|
|
23
|
+
'datepicker.weekdays': 'Mo,Tu,We,Th,Fr,Sa,Su',
|
|
24
|
+
'timepicker.placeholder': 'Select time',
|
|
22
25
|
},
|
|
23
26
|
'zh-CN': {
|
|
24
27
|
'modal.close': '关闭',
|
|
@@ -29,6 +32,8 @@ const _messages = {
|
|
|
29
32
|
'pagination.of': '/',
|
|
30
33
|
'select.placeholder': '请选择',
|
|
31
34
|
'datepicker.placeholder': '选择日期',
|
|
35
|
+
'datepicker.months': '1月,2月,3月,4月,5月,6月,7月,8月,9月,10月,11月,12月',
|
|
36
|
+
'datepicker.weekdays': '一,二,三,四,五,六,日',
|
|
32
37
|
'timepicker.placeholder': '选择时间',
|
|
33
38
|
'fileupload.drag': '拖拽文件到此处或',
|
|
34
39
|
'fileupload.browse': '浏览',
|
|
@@ -40,3 +40,6 @@ export { flushJobs, resetScheduler } from './scheduler.js';
|
|
|
40
40
|
|
|
41
41
|
// ── i18n ─────────────────────────────────────────────────────────────────────
|
|
42
42
|
export { setLocale, getLocale, t, addMessages } from './i18n.js';
|
|
43
|
+
|
|
44
|
+
// ── Error Boundary ───────────────────────────────────────────────────────────
|
|
45
|
+
export { ErrorBoundary } from './errors.js';
|