@tokis/react 1.0.1 → 1.2.0
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 +8 -6
- package/dist/__tests__/Accordion.test.d.ts +2 -0
- package/dist/__tests__/Accordion.test.d.ts.map +1 -0
- package/dist/__tests__/Accordion.test.js +144 -0
- package/dist/__tests__/Accordion.test.js.map +1 -0
- package/dist/__tests__/Button.test.d.ts +2 -0
- package/dist/__tests__/Button.test.d.ts.map +1 -0
- package/dist/__tests__/Button.test.js +104 -0
- package/dist/__tests__/Button.test.js.map +1 -0
- package/dist/__tests__/Checkbox.test.d.ts +2 -0
- package/dist/__tests__/Checkbox.test.d.ts.map +1 -0
- package/dist/__tests__/Checkbox.test.js +90 -0
- package/dist/__tests__/Checkbox.test.js.map +1 -0
- package/dist/__tests__/Select.test.d.ts +2 -0
- package/dist/__tests__/Select.test.d.ts.map +1 -0
- package/dist/__tests__/Select.test.js +119 -0
- package/dist/__tests__/Select.test.js.map +1 -0
- package/dist/__tests__/datagrid-utils.test.d.ts +2 -0
- package/dist/__tests__/datagrid-utils.test.d.ts.map +1 -0
- package/dist/__tests__/datagrid-utils.test.js +190 -0
- package/dist/__tests__/datagrid-utils.test.js.map +1 -0
- package/dist/__tests__/date-utils.test.d.ts +2 -0
- package/dist/__tests__/date-utils.test.d.ts.map +1 -0
- package/dist/__tests__/date-utils.test.js +180 -0
- package/dist/__tests__/date-utils.test.js.map +1 -0
- package/dist/cjs/components/accordion/index.js +1 -1
- package/dist/cjs/components/checkbox/index.js +2 -1
- package/dist/cjs/components/datagrid/index.js +161 -0
- package/dist/cjs/components/datagrid/types.js +2 -0
- package/dist/cjs/components/datagrid/utils.js +87 -0
- package/dist/cjs/components/datepicker/Calendar.js +117 -0
- package/dist/cjs/components/datepicker/date-utils.js +121 -0
- package/dist/cjs/components/datepicker/index.js +134 -0
- package/dist/cjs/components/extended/index.js +39 -31
- package/dist/cjs/components/layout/index.js +5 -4
- package/dist/cjs/components/treeview/index.js +1 -1
- package/dist/cjs/hooks/useId.js +18 -5
- package/dist/cjs/index.js +2 -0
- package/dist/components/accordion/index.js +1 -1
- package/dist/components/accordion/index.js.map +1 -1
- package/dist/components/checkbox/index.js +2 -1
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/datagrid/index.d.ts +20 -0
- package/dist/components/datagrid/index.d.ts.map +1 -0
- package/dist/components/datagrid/index.js +159 -0
- package/dist/components/datagrid/index.js.map +1 -0
- package/dist/components/datagrid/types.d.ts +92 -0
- package/dist/components/datagrid/types.d.ts.map +1 -0
- package/dist/components/datagrid/types.js +2 -0
- package/dist/components/datagrid/types.js.map +1 -0
- package/dist/components/datagrid/utils.d.ts +14 -0
- package/dist/components/datagrid/utils.d.ts.map +1 -0
- package/dist/components/datagrid/utils.js +80 -0
- package/dist/components/datagrid/utils.js.map +1 -0
- package/dist/components/datepicker/Calendar.d.ts +30 -0
- package/dist/components/datepicker/Calendar.d.ts.map +1 -0
- package/dist/components/datepicker/Calendar.js +115 -0
- package/dist/components/datepicker/Calendar.js.map +1 -0
- package/dist/components/datepicker/date-utils.d.ts +34 -0
- package/dist/components/datepicker/date-utils.d.ts.map +1 -0
- package/dist/components/datepicker/date-utils.js +107 -0
- package/dist/components/datepicker/date-utils.js.map +1 -0
- package/dist/components/datepicker/index.d.ts +52 -0
- package/dist/components/datepicker/index.d.ts.map +1 -0
- package/dist/components/datepicker/index.js +128 -0
- package/dist/components/datepicker/index.js.map +1 -0
- package/dist/components/extended/index.d.ts +14 -29
- package/dist/components/extended/index.d.ts.map +1 -1
- package/dist/components/extended/index.js +40 -27
- package/dist/components/extended/index.js.map +1 -1
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +5 -4
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/treeview/index.js +1 -1
- package/dist/components/treeview/index.js.map +1 -1
- package/dist/hooks/useId.d.ts +13 -3
- package/dist/hooks/useId.d.ts.map +1 -1
- package/dist/hooks/useId.js +19 -6
- package/dist/hooks/useId.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCellValue = getCellValue;
|
|
4
|
+
exports.sortRows = sortRows;
|
|
5
|
+
exports.filterRows = filterRows;
|
|
6
|
+
exports.nextSortDirection = nextSortDirection;
|
|
7
|
+
exports.paginateRows = paginateRows;
|
|
8
|
+
exports.computeColumnWidths = computeColumnWidths;
|
|
9
|
+
/** Extract a comparable value from a row cell. */
|
|
10
|
+
function getCellValue(row, col) {
|
|
11
|
+
return col.valueGetter ? col.valueGetter(row) : row[col.field];
|
|
12
|
+
}
|
|
13
|
+
/** Sort rows by a single column. Returns a new array. */
|
|
14
|
+
function sortRows(rows, sortModel, columns) {
|
|
15
|
+
if (!sortModel.direction)
|
|
16
|
+
return rows;
|
|
17
|
+
const col = columns.find((c) => c.field === sortModel.field);
|
|
18
|
+
if (!col)
|
|
19
|
+
return rows;
|
|
20
|
+
return [...rows].sort((a, b) => {
|
|
21
|
+
const av = getCellValue(a, col);
|
|
22
|
+
const bv = getCellValue(b, col);
|
|
23
|
+
let cmp = 0;
|
|
24
|
+
if (typeof av === 'number' && typeof bv === 'number') {
|
|
25
|
+
cmp = av - bv;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
cmp = String(av ?? '').localeCompare(String(bv ?? ''), undefined, { numeric: true, sensitivity: 'base' });
|
|
29
|
+
}
|
|
30
|
+
return sortModel.direction === 'asc' ? cmp : -cmp;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** Filter rows based on the FilterModel. Returns a new array. */
|
|
34
|
+
function filterRows(rows, filterModel, columns) {
|
|
35
|
+
let result = rows;
|
|
36
|
+
// Global quick filter
|
|
37
|
+
const q = filterModel.quickFilter?.trim().toLowerCase();
|
|
38
|
+
if (q) {
|
|
39
|
+
const filterableCols = columns.filter((c) => c.filterable !== false);
|
|
40
|
+
result = result.filter((row) => filterableCols.some((col) => {
|
|
41
|
+
const v = getCellValue(row, col);
|
|
42
|
+
return String(v ?? '').toLowerCase().includes(q);
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
// Per-column filters
|
|
46
|
+
const colFilters = filterModel.columnFilters ?? {};
|
|
47
|
+
const activeColFilters = Object.entries(colFilters).filter(([, v]) => v.trim() !== '');
|
|
48
|
+
if (activeColFilters.length > 0) {
|
|
49
|
+
result = result.filter((row) => activeColFilters.every(([field, filterStr]) => {
|
|
50
|
+
const col = columns.find((c) => c.field === field);
|
|
51
|
+
if (!col)
|
|
52
|
+
return true;
|
|
53
|
+
const v = getCellValue(row, col);
|
|
54
|
+
return String(v ?? '').toLowerCase().includes(filterStr.toLowerCase());
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
/** Cycle sort direction: null → asc → desc → null */
|
|
60
|
+
function nextSortDirection(currentField, currentModel, clickedField) {
|
|
61
|
+
if (currentModel.field !== clickedField || currentModel.direction === null) {
|
|
62
|
+
return { field: clickedField, direction: 'asc' };
|
|
63
|
+
}
|
|
64
|
+
if (currentModel.direction === 'asc') {
|
|
65
|
+
return { field: clickedField, direction: 'desc' };
|
|
66
|
+
}
|
|
67
|
+
return { field: clickedField, direction: null };
|
|
68
|
+
}
|
|
69
|
+
/** Paginate an array. Returns the slice for the given page. */
|
|
70
|
+
function paginateRows(rows, page, pageSize) {
|
|
71
|
+
const start = page * pageSize;
|
|
72
|
+
return rows.slice(start, start + pageSize);
|
|
73
|
+
}
|
|
74
|
+
/** Compute column widths from fixed + flex definitions. */
|
|
75
|
+
function computeColumnWidths(columns, totalWidth) {
|
|
76
|
+
const fixed = columns.map((c) => c.width ?? 0);
|
|
77
|
+
const totalFixed = fixed.reduce((s, w) => s + w, 0);
|
|
78
|
+
const totalFlex = columns.reduce((s, c) => s + (c.flex ?? 0), 0);
|
|
79
|
+
const remaining = Math.max(0, totalWidth - totalFixed);
|
|
80
|
+
return columns.map((col, i) => {
|
|
81
|
+
if (col.width)
|
|
82
|
+
return col.width;
|
|
83
|
+
if (totalFlex > 0 && col.flex)
|
|
84
|
+
return (col.flex / totalFlex) * remaining;
|
|
85
|
+
return fixed[i] || 120; // fallback
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Calendar = Calendar;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* Calendar — standalone month-grid date picker widget.
|
|
7
|
+
*
|
|
8
|
+
* Implements WAI-ARIA Calendar / Date Grid pattern:
|
|
9
|
+
* - role="grid" on the day grid
|
|
10
|
+
* - role="gridcell" on each day
|
|
11
|
+
* - Arrow keys navigate within the grid
|
|
12
|
+
* - Enter/Space selects the focused date
|
|
13
|
+
* - Page Up/Down → prev/next month
|
|
14
|
+
* - Ctrl+Page Up/Down → prev/next year
|
|
15
|
+
* - Home/End → first/last day of current week
|
|
16
|
+
*/
|
|
17
|
+
const react_1 = require("react");
|
|
18
|
+
const date_utils_js_1 = require("./date-utils");
|
|
19
|
+
const cn_js_1 = require("../../utils/cn");
|
|
20
|
+
function Calendar({ value, onChange, min, max, defaultMonth, className }) {
|
|
21
|
+
const selectedDate = (0, date_utils_js_1.parseDate)(value);
|
|
22
|
+
const minDate = (0, date_utils_js_1.parseDate)(min);
|
|
23
|
+
const maxDate = (0, date_utils_js_1.parseDate)(max);
|
|
24
|
+
const todayDate = (0, date_utils_js_1.today)();
|
|
25
|
+
const initialYear = defaultMonth?.year ?? selectedDate?.getFullYear() ?? todayDate.getFullYear();
|
|
26
|
+
const initialMonth = defaultMonth?.month ?? selectedDate?.getMonth() ?? todayDate.getMonth();
|
|
27
|
+
const [viewYear, setViewYear] = (0, react_1.useState)(initialYear);
|
|
28
|
+
const [viewMonth, setViewMonth] = (0, react_1.useState)(initialMonth);
|
|
29
|
+
const [focusedDate, setFocusedDate] = (0, react_1.useState)(selectedDate ?? todayDate);
|
|
30
|
+
const gridRef = (0, react_1.useRef)(null);
|
|
31
|
+
const grid = (0, date_utils_js_1.buildCalendarGrid)(viewYear, viewMonth);
|
|
32
|
+
const navigateMonth = (delta) => {
|
|
33
|
+
setViewMonth((m) => {
|
|
34
|
+
const next = m + delta;
|
|
35
|
+
if (next < 0) {
|
|
36
|
+
setViewYear((y) => y - 1);
|
|
37
|
+
return 11;
|
|
38
|
+
}
|
|
39
|
+
if (next > 11) {
|
|
40
|
+
setViewYear((y) => y + 1);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
return next;
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
const navigateYear = (delta) => {
|
|
47
|
+
setViewYear((y) => y + delta);
|
|
48
|
+
};
|
|
49
|
+
const selectDate = (0, react_1.useCallback)((date) => {
|
|
50
|
+
if (minDate && (0, date_utils_js_1.isBeforeDay)(date, minDate))
|
|
51
|
+
return;
|
|
52
|
+
if (maxDate && (0, date_utils_js_1.isAfterDay)(date, maxDate))
|
|
53
|
+
return;
|
|
54
|
+
onChange?.((0, date_utils_js_1.formatDate)(date));
|
|
55
|
+
}, [onChange, minDate, maxDate]);
|
|
56
|
+
const handleKeyDown = (e) => {
|
|
57
|
+
let next = new Date(focusedDate);
|
|
58
|
+
switch (e.key) {
|
|
59
|
+
case 'ArrowRight':
|
|
60
|
+
next.setDate(next.getDate() + 1);
|
|
61
|
+
break;
|
|
62
|
+
case 'ArrowLeft':
|
|
63
|
+
next.setDate(next.getDate() - 1);
|
|
64
|
+
break;
|
|
65
|
+
case 'ArrowDown':
|
|
66
|
+
next.setDate(next.getDate() + 7);
|
|
67
|
+
break;
|
|
68
|
+
case 'ArrowUp':
|
|
69
|
+
next.setDate(next.getDate() - 7);
|
|
70
|
+
break;
|
|
71
|
+
case 'Home':
|
|
72
|
+
next = new Date(next.getFullYear(), next.getMonth(), 1);
|
|
73
|
+
break;
|
|
74
|
+
case 'End':
|
|
75
|
+
next = new Date(next.getFullYear(), next.getMonth(), (0, date_utils_js_1.daysInMonth)(next.getFullYear(), next.getMonth()));
|
|
76
|
+
break;
|
|
77
|
+
case 'PageUp':
|
|
78
|
+
e.ctrlKey ? navigateYear(-1) : navigateMonth(-1);
|
|
79
|
+
return;
|
|
80
|
+
case 'PageDown':
|
|
81
|
+
e.ctrlKey ? navigateYear(1) : navigateMonth(1);
|
|
82
|
+
return;
|
|
83
|
+
case 'Enter':
|
|
84
|
+
case ' ':
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
selectDate(focusedDate);
|
|
87
|
+
return;
|
|
88
|
+
default: return;
|
|
89
|
+
}
|
|
90
|
+
e.preventDefault();
|
|
91
|
+
setFocusedDate(next);
|
|
92
|
+
// Auto-navigate view if the focused date is outside the current view month
|
|
93
|
+
if (next.getFullYear() !== viewYear || next.getMonth() !== viewMonth) {
|
|
94
|
+
setViewYear(next.getFullYear());
|
|
95
|
+
setViewMonth(next.getMonth());
|
|
96
|
+
}
|
|
97
|
+
// Re-focus the cell after state update
|
|
98
|
+
requestAnimationFrame(() => {
|
|
99
|
+
const cell = gridRef.current?.querySelector('[tabindex="0"]');
|
|
100
|
+
cell?.focus();
|
|
101
|
+
});
|
|
102
|
+
};
|
|
103
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)('tokis-calendar', className), role: "group", "aria-label": "Calendar", children: [(0, jsx_runtime_1.jsxs)("div", { className: "tokis-calendar-nav", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: "tokis-calendar-nav-btn", onClick: () => navigateYear(-1), "aria-label": "Previous year", children: "\u00AB" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tokis-calendar-nav-btn", onClick: () => navigateMonth(-1), "aria-label": "Previous month", children: "\u2039" }), (0, jsx_runtime_1.jsxs)("span", { className: "tokis-calendar-title", "aria-live": "polite", "aria-atomic": "true", children: [date_utils_js_1.MONTHS[viewMonth], " ", viewYear] }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tokis-calendar-nav-btn", onClick: () => navigateMonth(1), "aria-label": "Next month", children: "\u203A" }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tokis-calendar-nav-btn", onClick: () => navigateYear(1), "aria-label": "Next year", children: "\u00BB" })] }), (0, jsx_runtime_1.jsxs)("table", { ref: gridRef, className: "tokis-calendar-grid", role: "grid", "aria-label": `${date_utils_js_1.MONTHS[viewMonth]} ${viewYear}`, onKeyDown: handleKeyDown, children: [(0, jsx_runtime_1.jsx)("thead", { children: (0, jsx_runtime_1.jsx)("tr", { role: "row", children: date_utils_js_1.DAYS_OF_WEEK.map((d) => ((0, jsx_runtime_1.jsx)("th", { role: "columnheader", abbr: d, className: "tokis-calendar-weekday", scope: "col", children: d }, d))) }) }), (0, jsx_runtime_1.jsx)("tbody", { children: grid.map((week, wi) => ((0, jsx_runtime_1.jsx)("tr", { role: "row", children: week.map((day) => {
|
|
104
|
+
const isCurrentMonth = day.getMonth() === viewMonth;
|
|
105
|
+
const isSelected = selectedDate ? (0, date_utils_js_1.isSameDay)(day, selectedDate) : false;
|
|
106
|
+
const isToday = (0, date_utils_js_1.isSameDay)(day, todayDate);
|
|
107
|
+
const isFocused = (0, date_utils_js_1.isSameDay)(day, focusedDate);
|
|
108
|
+
const isDisabled = Boolean((minDate && (0, date_utils_js_1.isBeforeDay)(day, minDate)) ||
|
|
109
|
+
(maxDate && (0, date_utils_js_1.isAfterDay)(day, maxDate)));
|
|
110
|
+
return ((0, jsx_runtime_1.jsx)("td", { role: "gridcell", "aria-selected": isSelected, "aria-disabled": isDisabled, "aria-label": day.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' }), "aria-current": isToday ? 'date' : undefined, tabIndex: isFocused ? 0 : -1, className: (0, cn_js_1.cn)('tokis-calendar-day', !isCurrentMonth && 'tokis-calendar-day--outside', isSelected && 'tokis-calendar-day--selected', isToday && 'tokis-calendar-day--today', isDisabled && 'tokis-calendar-day--disabled', isFocused && 'tokis-calendar-day--focused'), onClick: () => !isDisabled && selectDate(day), onFocus: () => setFocusedDate(day), children: day.getDate() }, day.toISOString()));
|
|
111
|
+
}) }, wi))) })] }), (0, jsx_runtime_1.jsx)("div", { className: "tokis-calendar-footer", children: (0, jsx_runtime_1.jsx)("button", { type: "button", className: "tokis-calendar-today-btn", onClick: () => {
|
|
112
|
+
setViewYear(todayDate.getFullYear());
|
|
113
|
+
setViewMonth(todayDate.getMonth());
|
|
114
|
+
setFocusedDate(todayDate);
|
|
115
|
+
selectDate(todayDate);
|
|
116
|
+
}, children: "Today" }) })] }));
|
|
117
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Zero-dependency date utilities for the DatePicker calendar.
|
|
4
|
+
* All functions operate on plain JS Date objects or ISO YYYY-MM-DD strings.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.MONTHS = exports.DAYS_OF_WEEK = void 0;
|
|
8
|
+
exports.parseDate = parseDate;
|
|
9
|
+
exports.formatDate = formatDate;
|
|
10
|
+
exports.formatDisplayDate = formatDisplayDate;
|
|
11
|
+
exports.startOfMonth = startOfMonth;
|
|
12
|
+
exports.daysInMonth = daysInMonth;
|
|
13
|
+
exports.buildCalendarGrid = buildCalendarGrid;
|
|
14
|
+
exports.isSameDay = isSameDay;
|
|
15
|
+
exports.isBeforeDay = isBeforeDay;
|
|
16
|
+
exports.isAfterDay = isAfterDay;
|
|
17
|
+
exports.today = today;
|
|
18
|
+
exports.parseTime = parseTime;
|
|
19
|
+
exports.formatTime = formatTime;
|
|
20
|
+
exports.DAYS_OF_WEEK = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
|
|
21
|
+
exports.MONTHS = [
|
|
22
|
+
'January', 'February', 'March', 'April', 'May', 'June',
|
|
23
|
+
'July', 'August', 'September', 'October', 'November', 'December',
|
|
24
|
+
];
|
|
25
|
+
/** Parse a YYYY-MM-DD string → local Date (midnight). Returns null if invalid. */
|
|
26
|
+
function parseDate(iso) {
|
|
27
|
+
if (!iso)
|
|
28
|
+
return null;
|
|
29
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(iso);
|
|
30
|
+
if (!m)
|
|
31
|
+
return null;
|
|
32
|
+
const [, y, mo, d] = m.map(Number);
|
|
33
|
+
const date = new Date(y, mo - 1, d);
|
|
34
|
+
// Guard against e.g. "2024-02-30" auto-rolling to March
|
|
35
|
+
if (date.getFullYear() !== y || date.getMonth() !== mo - 1 || date.getDate() !== d)
|
|
36
|
+
return null;
|
|
37
|
+
return date;
|
|
38
|
+
}
|
|
39
|
+
/** Format a Date → YYYY-MM-DD string. */
|
|
40
|
+
function formatDate(date) {
|
|
41
|
+
const y = date.getFullYear();
|
|
42
|
+
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
43
|
+
const d = String(date.getDate()).padStart(2, '0');
|
|
44
|
+
return `${y}-${m}-${d}`;
|
|
45
|
+
}
|
|
46
|
+
/** Format a Date for the input display label (e.g., "Jan 15, 2025"). */
|
|
47
|
+
function formatDisplayDate(date) {
|
|
48
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
|
|
49
|
+
}
|
|
50
|
+
/** Return the first day of the month for a given year/month (0-indexed). */
|
|
51
|
+
function startOfMonth(year, month) {
|
|
52
|
+
return new Date(year, month, 1);
|
|
53
|
+
}
|
|
54
|
+
/** Return the number of days in a month. */
|
|
55
|
+
function daysInMonth(year, month) {
|
|
56
|
+
return new Date(year, month + 1, 0).getDate();
|
|
57
|
+
}
|
|
58
|
+
/** Build the 6×7 calendar grid for a given year/month. Returns Date[][] (some days in prev/next month). */
|
|
59
|
+
function buildCalendarGrid(year, month) {
|
|
60
|
+
const firstDay = startOfMonth(year, month).getDay(); // 0=Sun
|
|
61
|
+
const totalDays = daysInMonth(year, month);
|
|
62
|
+
const cells = [];
|
|
63
|
+
// Pad with days from previous month
|
|
64
|
+
const prevMonthDays = daysInMonth(year, month - 1 < 0 ? 11 : month - 1);
|
|
65
|
+
const prevYear = month === 0 ? year - 1 : year;
|
|
66
|
+
const prevMonth = month === 0 ? 11 : month - 1;
|
|
67
|
+
for (let i = firstDay - 1; i >= 0; i--) {
|
|
68
|
+
cells.push(new Date(prevYear, prevMonth, prevMonthDays - i));
|
|
69
|
+
}
|
|
70
|
+
// Current month days
|
|
71
|
+
for (let d = 1; d <= totalDays; d++) {
|
|
72
|
+
cells.push(new Date(year, month, d));
|
|
73
|
+
}
|
|
74
|
+
// Pad to complete 6 rows (42 cells)
|
|
75
|
+
const nextYear = month === 11 ? year + 1 : year;
|
|
76
|
+
const nextMonth = month === 11 ? 0 : month + 1;
|
|
77
|
+
let nextDay = 1;
|
|
78
|
+
while (cells.length < 42) {
|
|
79
|
+
cells.push(new Date(nextYear, nextMonth, nextDay++));
|
|
80
|
+
}
|
|
81
|
+
// Split into 6 rows of 7
|
|
82
|
+
const rows = [];
|
|
83
|
+
for (let r = 0; r < 6; r++) {
|
|
84
|
+
rows.push(cells.slice(r * 7, r * 7 + 7));
|
|
85
|
+
}
|
|
86
|
+
return rows;
|
|
87
|
+
}
|
|
88
|
+
/** Returns true if two dates represent the same calendar day. */
|
|
89
|
+
function isSameDay(a, b) {
|
|
90
|
+
return a.getFullYear() === b.getFullYear()
|
|
91
|
+
&& a.getMonth() === b.getMonth()
|
|
92
|
+
&& a.getDate() === b.getDate();
|
|
93
|
+
}
|
|
94
|
+
/** Returns true if a date is before the given min date (by day). */
|
|
95
|
+
function isBeforeDay(date, min) {
|
|
96
|
+
const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
97
|
+
const m = new Date(min.getFullYear(), min.getMonth(), min.getDate());
|
|
98
|
+
return d < m;
|
|
99
|
+
}
|
|
100
|
+
/** Returns true if a date is after the given max date (by day). */
|
|
101
|
+
function isAfterDay(date, max) {
|
|
102
|
+
const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
103
|
+
const m = new Date(max.getFullYear(), max.getMonth(), max.getDate());
|
|
104
|
+
return d > m;
|
|
105
|
+
}
|
|
106
|
+
/** Returns today as a local midnight Date. */
|
|
107
|
+
function today() {
|
|
108
|
+
const t = new Date();
|
|
109
|
+
return new Date(t.getFullYear(), t.getMonth(), t.getDate());
|
|
110
|
+
}
|
|
111
|
+
/** Parse a HH:MM string into { hours, minutes }. */
|
|
112
|
+
function parseTime(t) {
|
|
113
|
+
if (!t)
|
|
114
|
+
return { hours: 0, minutes: 0 };
|
|
115
|
+
const [h, m] = t.split(':').map(Number);
|
|
116
|
+
return { hours: h ?? 0, minutes: m ?? 0 };
|
|
117
|
+
}
|
|
118
|
+
/** Format { hours, minutes } → HH:MM string. */
|
|
119
|
+
function formatTime(hours, minutes) {
|
|
120
|
+
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
|
121
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Calendar = void 0;
|
|
4
|
+
exports.DatePicker = DatePicker;
|
|
5
|
+
exports.TimePicker = TimePicker;
|
|
6
|
+
exports.DateTimePicker = DateTimePicker;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
/**
|
|
9
|
+
* DatePicker, TimePicker, DateTimePicker
|
|
10
|
+
*
|
|
11
|
+
* All three share a pattern: a trigger input that opens a Portal-mounted popup.
|
|
12
|
+
* DatePicker uses the Calendar grid widget.
|
|
13
|
+
* TimePicker uses scroll-select columns for hours/minutes.
|
|
14
|
+
* DateTimePicker combines both.
|
|
15
|
+
*/
|
|
16
|
+
const react_1 = require("react");
|
|
17
|
+
const index_js_1 = require("../portal/index");
|
|
18
|
+
const useId_js_1 = require("../../hooks/useId");
|
|
19
|
+
const cn_js_1 = require("../../utils/cn");
|
|
20
|
+
const Calendar_js_1 = require("./Calendar");
|
|
21
|
+
const date_utils_js_1 = require("./date-utils");
|
|
22
|
+
// ─── Shared floating panel hook ───────────────────────────────────────────────
|
|
23
|
+
function useFloatingPanel(onClose) {
|
|
24
|
+
const [open, setOpen] = (0, react_1.useState)(false);
|
|
25
|
+
const triggerRef = (0, react_1.useRef)(null);
|
|
26
|
+
const panelRef = (0, react_1.useRef)(null);
|
|
27
|
+
const [pos, setPos] = (0, react_1.useState)({ top: 0, left: 0 });
|
|
28
|
+
const updatePos = (0, react_1.useCallback)(() => {
|
|
29
|
+
const el = triggerRef.current;
|
|
30
|
+
if (!el)
|
|
31
|
+
return;
|
|
32
|
+
const rect = el.getBoundingClientRect();
|
|
33
|
+
setPos({
|
|
34
|
+
top: rect.bottom + window.scrollY + 4,
|
|
35
|
+
left: rect.left + window.scrollX,
|
|
36
|
+
});
|
|
37
|
+
}, []);
|
|
38
|
+
const openPanel = () => {
|
|
39
|
+
updatePos();
|
|
40
|
+
setOpen(true);
|
|
41
|
+
};
|
|
42
|
+
const closePanel = (0, react_1.useCallback)(() => {
|
|
43
|
+
setOpen(false);
|
|
44
|
+
onClose?.();
|
|
45
|
+
triggerRef.current?.focus();
|
|
46
|
+
}, [onClose]);
|
|
47
|
+
// Close on outside click
|
|
48
|
+
(0, react_1.useEffect)(() => {
|
|
49
|
+
if (!open)
|
|
50
|
+
return;
|
|
51
|
+
const handler = (e) => {
|
|
52
|
+
if (!triggerRef.current?.contains(e.target) &&
|
|
53
|
+
!panelRef.current?.contains(e.target)) {
|
|
54
|
+
closePanel();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
document.addEventListener('mousedown', handler);
|
|
58
|
+
return () => document.removeEventListener('mousedown', handler);
|
|
59
|
+
}, [open, closePanel]);
|
|
60
|
+
// Close on Escape
|
|
61
|
+
(0, react_1.useEffect)(() => {
|
|
62
|
+
if (!open)
|
|
63
|
+
return;
|
|
64
|
+
const handler = (e) => { if (e.key === 'Escape')
|
|
65
|
+
closePanel(); };
|
|
66
|
+
document.addEventListener('keydown', handler);
|
|
67
|
+
return () => document.removeEventListener('keydown', handler);
|
|
68
|
+
}, [open, closePanel]);
|
|
69
|
+
return { open, openPanel, closePanel, triggerRef, panelRef, pos };
|
|
70
|
+
}
|
|
71
|
+
function DatePicker({ value: valueProp, defaultValue, onChange, label, placeholder = 'Pick a date', disabled = false, required = false, error = false, min, max, className, }) {
|
|
72
|
+
const [internalValue, setInternalValue] = (0, react_1.useState)(defaultValue);
|
|
73
|
+
const value = valueProp !== undefined ? valueProp : internalValue;
|
|
74
|
+
const id = (0, useId_js_1.useId)('datepicker');
|
|
75
|
+
const parsedDate = (0, date_utils_js_1.parseDate)(value);
|
|
76
|
+
const { open, openPanel, closePanel, triggerRef, panelRef, pos } = useFloatingPanel();
|
|
77
|
+
const handleSelect = (iso) => {
|
|
78
|
+
if (valueProp === undefined)
|
|
79
|
+
setInternalValue(iso);
|
|
80
|
+
onChange?.(iso);
|
|
81
|
+
closePanel();
|
|
82
|
+
};
|
|
83
|
+
const handleClear = (e) => {
|
|
84
|
+
e.stopPropagation();
|
|
85
|
+
if (valueProp === undefined)
|
|
86
|
+
setInternalValue(undefined);
|
|
87
|
+
onChange?.(undefined);
|
|
88
|
+
};
|
|
89
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)('tokis-datepicker', error && 'tokis-datepicker--error', disabled && 'tokis-datepicker--disabled', className), children: [label && ((0, jsx_runtime_1.jsxs)("label", { className: "tokis-label", htmlFor: id, children: [label, required && (0, jsx_runtime_1.jsx)("span", { className: "tokis-label-required", "aria-hidden": "true", children: " *" })] })), (0, jsx_runtime_1.jsxs)("div", { className: "tokis-datepicker-input-wrap", children: [(0, jsx_runtime_1.jsxs)("button", { ref: triggerRef, id: id, type: "button", className: (0, cn_js_1.cn)('tokis-datepicker-trigger', open && 'tokis-datepicker-trigger--open'), disabled: disabled, "aria-haspopup": "dialog", "aria-expanded": open, "aria-label": label ? `${label}: ${parsedDate ? (0, date_utils_js_1.formatDisplayDate)(parsedDate) : 'not set'}` : undefined, onClick: () => (open ? closePanel() : openPanel()), children: [(0, jsx_runtime_1.jsx)("span", { className: (0, cn_js_1.cn)('tokis-datepicker-value', !parsedDate && 'tokis-datepicker-placeholder'), children: parsedDate ? (0, date_utils_js_1.formatDisplayDate)(parsedDate) : placeholder }), (0, jsx_runtime_1.jsx)("span", { className: "tokis-datepicker-icon", "aria-hidden": "true", children: "\uD83D\uDCC5" })] }), parsedDate && !disabled && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "tokis-datepicker-clear", "aria-label": "Clear date", onClick: handleClear, children: "\u2715" }))] }), open && ((0, jsx_runtime_1.jsx)(index_js_1.Portal, { children: (0, jsx_runtime_1.jsx)("div", { ref: panelRef, className: "tokis-datepicker-panel", role: "dialog", "aria-modal": "false", "aria-label": label ?? 'Date picker', style: { top: pos.top, left: pos.left }, children: (0, jsx_runtime_1.jsx)(Calendar_js_1.Calendar, { value: value, onChange: handleSelect, min: min, max: max }) }) }))] }));
|
|
90
|
+
}
|
|
91
|
+
function TimePicker({ value: valueProp, defaultValue, onChange, label, disabled = false, required = false, error = false, hourFormat = 24, minuteStep = 1, className, }) {
|
|
92
|
+
const [internalValue, setInternalValue] = (0, react_1.useState)(defaultValue ?? '00:00');
|
|
93
|
+
const value = valueProp !== undefined ? valueProp : internalValue;
|
|
94
|
+
const { hours, minutes } = (0, date_utils_js_1.parseTime)(value);
|
|
95
|
+
const id = (0, useId_js_1.useId)('timepicker');
|
|
96
|
+
const setTime = (h, m) => {
|
|
97
|
+
const next = (0, date_utils_js_1.formatTime)(h, m);
|
|
98
|
+
if (valueProp === undefined)
|
|
99
|
+
setInternalValue(next);
|
|
100
|
+
onChange?.(next);
|
|
101
|
+
};
|
|
102
|
+
const hourOptions = Array.from({ length: hourFormat === 12 ? 12 : 24 }, (_, i) => hourFormat === 12 ? i + 1 : i);
|
|
103
|
+
const minuteOptions = [];
|
|
104
|
+
for (let m = 0; m < 60; m += minuteStep)
|
|
105
|
+
minuteOptions.push(m);
|
|
106
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_js_1.cn)('tokis-timepicker', error && 'tokis-timepicker--error', disabled && 'tokis-timepicker--disabled', className), children: [label && ((0, jsx_runtime_1.jsxs)("label", { className: "tokis-label", htmlFor: `${id}-hours`, children: [label, required && (0, jsx_runtime_1.jsx)("span", { className: "tokis-label-required", "aria-hidden": "true", children: " *" })] })), (0, jsx_runtime_1.jsxs)("div", { className: "tokis-timepicker-inputs", children: [(0, jsx_runtime_1.jsx)("select", { id: `${id}-hours`, className: "tokis-timepicker-select", value: hours, disabled: disabled, "aria-label": "Hours", onChange: (e) => setTime(Number(e.target.value), minutes), children: hourOptions.map((h) => ((0, jsx_runtime_1.jsx)("option", { value: h, children: String(h).padStart(2, '0') }, h))) }), (0, jsx_runtime_1.jsx)("span", { className: "tokis-timepicker-sep", "aria-hidden": "true", children: ":" }), (0, jsx_runtime_1.jsx)("select", { id: `${id}-minutes`, className: "tokis-timepicker-select", value: minutes, disabled: disabled, "aria-label": "Minutes", onChange: (e) => setTime(hours, Number(e.target.value)), children: minuteOptions.map((m) => ((0, jsx_runtime_1.jsx)("option", { value: m, children: String(m).padStart(2, '0') }, m))) }), hourFormat === 12 && ((0, jsx_runtime_1.jsxs)("select", { className: "tokis-timepicker-select tokis-timepicker-ampm", value: hours < 12 ? 'AM' : 'PM', disabled: disabled, "aria-label": "AM/PM", onChange: (e) => {
|
|
107
|
+
const ampm = e.target.value;
|
|
108
|
+
let h = hours % 12;
|
|
109
|
+
if (ampm === 'PM')
|
|
110
|
+
h += 12;
|
|
111
|
+
setTime(h, minutes);
|
|
112
|
+
}, children: [(0, jsx_runtime_1.jsx)("option", { value: "AM", children: "AM" }), (0, jsx_runtime_1.jsx)("option", { value: "PM", children: "PM" })] }))] })] }));
|
|
113
|
+
}
|
|
114
|
+
function DateTimePicker({ value: valueProp, defaultValue, onChange, minuteStep = 1, hourFormat = 24, ...dateProps }) {
|
|
115
|
+
const split = (v) => {
|
|
116
|
+
if (!v)
|
|
117
|
+
return { date: undefined, time: '00:00' };
|
|
118
|
+
const [date, time] = v.split('T');
|
|
119
|
+
return { date, time: time ?? '00:00' };
|
|
120
|
+
};
|
|
121
|
+
const [internalValue, setInternalValue] = (0, react_1.useState)(defaultValue);
|
|
122
|
+
const value = valueProp !== undefined ? valueProp : internalValue;
|
|
123
|
+
const { date, time } = split(value);
|
|
124
|
+
const emit = (0, react_1.useCallback)((d, t) => {
|
|
125
|
+
const next = d ? `${d}T${t ?? '00:00'}` : undefined;
|
|
126
|
+
if (valueProp === undefined)
|
|
127
|
+
setInternalValue(next);
|
|
128
|
+
onChange?.(next);
|
|
129
|
+
}, [valueProp, onChange]);
|
|
130
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-datetimepicker", children: [(0, jsx_runtime_1.jsx)(DatePicker, { ...dateProps, value: date, onChange: (d) => emit(d, time) }), (0, jsx_runtime_1.jsx)(TimePicker, { value: time, disabled: dateProps.disabled, minuteStep: minuteStep, hourFormat: hourFormat, onChange: (t) => emit(date, t) })] }));
|
|
131
|
+
}
|
|
132
|
+
// Re-export Calendar for standalone use
|
|
133
|
+
var Calendar_js_2 = require("./Calendar");
|
|
134
|
+
Object.defineProperty(exports, "Calendar", { enumerable: true, get: function () { return Calendar_js_2.Calendar; } });
|
|
@@ -41,7 +41,6 @@ exports.Rating = Rating;
|
|
|
41
41
|
exports.StarRating = Rating;
|
|
42
42
|
exports.TransferList = TransferList;
|
|
43
43
|
exports.Icon = Icon;
|
|
44
|
-
exports.MaterialIcon = MaterialIcon;
|
|
45
44
|
exports.Backdrop = Backdrop;
|
|
46
45
|
exports.Paper = Paper;
|
|
47
46
|
exports.SpeedDial = SpeedDial;
|
|
@@ -58,19 +57,15 @@ exports.Popper = Popper;
|
|
|
58
57
|
exports.TextareaAutosize = TextareaAutosize;
|
|
59
58
|
exports.Fade = Fade;
|
|
60
59
|
exports.useMediaQuery = useMediaQuery;
|
|
61
|
-
exports.DataGrid = DataGrid;
|
|
62
|
-
exports.DatePicker = DatePicker;
|
|
63
|
-
exports.TimePicker = TimePicker;
|
|
64
|
-
exports.DateTimePicker = DateTimePicker;
|
|
65
60
|
exports.Charts = Charts;
|
|
66
61
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
67
62
|
const react_1 = __importStar(require("react"));
|
|
63
|
+
const core_1 = require("@tokis/core");
|
|
68
64
|
const cn_js_1 = require("../../utils/cn");
|
|
69
65
|
const index_js_1 = require("../input/index");
|
|
70
66
|
const index_js_2 = require("../button/index");
|
|
71
67
|
const index_js_3 = require("../portal/index");
|
|
72
68
|
const index_js_4 = require("../card/index");
|
|
73
|
-
const index_js_5 = require("../table/index");
|
|
74
69
|
function Autocomplete({ options, value, onChange, label, placeholder = 'Search...', ...props }) {
|
|
75
70
|
const [query, setQuery] = (0, react_1.useState)(value ?? '');
|
|
76
71
|
const [open, setOpen] = (0, react_1.useState)(false);
|
|
@@ -128,19 +123,12 @@ function TransferList({ left, right, onChange, leftTitle = 'Available', rightTit
|
|
|
128
123
|
const renderPane = (title, items, checked, setChecked) => ((0, jsx_runtime_1.jsxs)(index_js_4.Card, { className: "tokis-transfer-pane", children: [(0, jsx_runtime_1.jsx)(index_js_4.CardHeader, { children: title }), (0, jsx_runtime_1.jsx)(index_js_4.CardBody, { children: items.map((item) => ((0, jsx_runtime_1.jsxs)("label", { className: "tokis-transfer-item", children: [(0, jsx_runtime_1.jsx)("input", { type: "checkbox", checked: checked.includes(item.id), onChange: (e) => setChecked(e.target.checked ? [...checked, item.id] : checked.filter((id) => id !== item.id)) }), item.label] }, item.id))) })] }));
|
|
129
124
|
return ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-transfer-list", children: [renderPane(leftTitle, left, leftChecked, setLeftChecked), (0, jsx_runtime_1.jsxs)("div", { className: "tokis-transfer-actions", children: [(0, jsx_runtime_1.jsx)(index_js_2.ButtonRoot, { size: "sm", onClick: moveToRight, children: '>' }), (0, jsx_runtime_1.jsx)(index_js_2.ButtonRoot, { size: "sm", onClick: moveToLeft, children: '<' })] }), renderPane(rightTitle, right, rightChecked, setRightChecked)] }));
|
|
130
125
|
}
|
|
131
|
-
const
|
|
132
|
-
search: '⌕',
|
|
133
|
-
close: '×',
|
|
134
|
-
menu: '☰',
|
|
135
|
-
check: '✓',
|
|
136
|
-
star: '★',
|
|
137
|
-
'arrow-right': '→',
|
|
126
|
+
const _emojiIconMap = {
|
|
127
|
+
search: '⌕', close: '×', menu: '☰', check: '✓', star: '★', 'arrow-right': '→',
|
|
138
128
|
};
|
|
129
|
+
/** @deprecated Use named SVG icons from `@tokis/icons`. */
|
|
139
130
|
function Icon({ name, className, ...props }) {
|
|
140
|
-
return (0, jsx_runtime_1.jsx)("span", { className: (0, cn_js_1.cn)('tokis-icon', className), "aria-hidden": "true", ...props, children:
|
|
141
|
-
}
|
|
142
|
-
function MaterialIcon({ icon, className, ...props }) {
|
|
143
|
-
return (0, jsx_runtime_1.jsx)("span", { className: (0, cn_js_1.cn)('tokis-material-icon', className), ...props, children: icon });
|
|
131
|
+
return (0, jsx_runtime_1.jsx)("span", { className: (0, cn_js_1.cn)('tokis-icon', className), "aria-hidden": "true", ...props, children: _emojiIconMap[name] });
|
|
144
132
|
}
|
|
145
133
|
function Backdrop({ open, onClick, className, children, ...props }) {
|
|
146
134
|
if (!open)
|
|
@@ -252,10 +240,42 @@ function InitColorSchemeScript() {
|
|
|
252
240
|
})();`;
|
|
253
241
|
return (0, jsx_runtime_1.jsx)("script", { dangerouslySetInnerHTML: { __html: script } });
|
|
254
242
|
}
|
|
255
|
-
function Modal({ open, onClose, children }) {
|
|
243
|
+
function Modal({ open, onClose, children, title, description, closeOnBackdropClick = true, closeOnEscape = true, }) {
|
|
244
|
+
const contentRef = (0, react_1.useRef)(null);
|
|
245
|
+
const previousFocusRef = (0, react_1.useRef)(null);
|
|
246
|
+
const titleId = (0, react_1.useId)();
|
|
247
|
+
const descriptionId = (0, react_1.useId)();
|
|
248
|
+
// Lock scroll on open
|
|
249
|
+
(0, react_1.useEffect)(() => {
|
|
250
|
+
if (!open)
|
|
251
|
+
return;
|
|
252
|
+
const prev = document.body.style.overflow;
|
|
253
|
+
document.body.style.overflow = 'hidden';
|
|
254
|
+
return () => { document.body.style.overflow = prev; };
|
|
255
|
+
}, [open]);
|
|
256
|
+
// Trap focus when open
|
|
257
|
+
(0, react_1.useEffect)(() => {
|
|
258
|
+
if (!open || !contentRef.current)
|
|
259
|
+
return;
|
|
260
|
+
previousFocusRef.current = document.activeElement;
|
|
261
|
+
const release = (0, core_1.trapFocus)(contentRef.current);
|
|
262
|
+
return () => {
|
|
263
|
+
release?.();
|
|
264
|
+
previousFocusRef.current?.focus();
|
|
265
|
+
};
|
|
266
|
+
}, [open]);
|
|
267
|
+
// Close on Escape
|
|
268
|
+
(0, react_1.useEffect)(() => {
|
|
269
|
+
if (!open || !closeOnEscape)
|
|
270
|
+
return;
|
|
271
|
+
const handler = (e) => { if (e.key === 'Escape')
|
|
272
|
+
onClose(); };
|
|
273
|
+
document.addEventListener('keydown', handler);
|
|
274
|
+
return () => document.removeEventListener('keydown', handler);
|
|
275
|
+
}, [open, closeOnEscape, onClose]);
|
|
256
276
|
if (!open)
|
|
257
277
|
return null;
|
|
258
|
-
return ((0, jsx_runtime_1.jsx)(index_js_3.Portal, { children: (0, jsx_runtime_1.jsx)("div", { className: "tokis-modal-root", role: "presentation", onClick: onClose, children: (0, jsx_runtime_1.
|
|
278
|
+
return ((0, jsx_runtime_1.jsx)(index_js_3.Portal, { children: (0, jsx_runtime_1.jsx)("div", { className: "tokis-modal-root", role: "presentation", onClick: closeOnBackdropClick ? onClose : undefined, children: (0, jsx_runtime_1.jsxs)("div", { ref: contentRef, className: "tokis-modal-content", role: "dialog", "aria-modal": "true", "aria-labelledby": title ? titleId : undefined, "aria-describedby": description ? descriptionId : undefined, onClick: (e) => e.stopPropagation(), children: [title && (0, jsx_runtime_1.jsx)("span", { id: titleId, className: "tokis-modal-title", children: title }), description && (0, jsx_runtime_1.jsx)("span", { id: descriptionId, className: "tokis-modal-desc", children: description }), children] }) }) }));
|
|
259
279
|
}
|
|
260
280
|
function NoSsr({ children, fallback = null }) {
|
|
261
281
|
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
@@ -311,18 +331,6 @@ function useMediaQuery(query) {
|
|
|
311
331
|
}, [query]);
|
|
312
332
|
return matches;
|
|
313
333
|
}
|
|
314
|
-
function DataGrid({ columns, rows }) {
|
|
315
|
-
return ((0, jsx_runtime_1.jsxs)(index_js_5.Table, { children: [(0, jsx_runtime_1.jsx)(index_js_5.TableHead, { children: (0, jsx_runtime_1.jsx)(index_js_5.TableRow, { children: columns.map((column) => ((0, jsx_runtime_1.jsx)(index_js_5.TableHeaderCell, { children: column.headerName }, column.field))) }) }), (0, jsx_runtime_1.jsx)(index_js_5.TableBody, { children: rows.map((row, rowIndex) => ((0, jsx_runtime_1.jsx)(index_js_5.TableRow, { children: columns.map((column) => ((0, jsx_runtime_1.jsx)(index_js_5.TableCell, { children: row[column.field] }, column.field))) }, rowIndex))) })] }));
|
|
316
|
-
}
|
|
317
|
-
function DatePicker({ label, ...props }) {
|
|
318
|
-
return (0, jsx_runtime_1.jsx)(index_js_1.TextField, { type: "date", label: label, ...props });
|
|
319
|
-
}
|
|
320
|
-
function TimePicker({ label, ...props }) {
|
|
321
|
-
return (0, jsx_runtime_1.jsx)(index_js_1.TextField, { type: "time", label: label, ...props });
|
|
322
|
-
}
|
|
323
|
-
function DateTimePicker({ label, ...props }) {
|
|
324
|
-
return (0, jsx_runtime_1.jsx)(index_js_1.TextField, { type: "datetime-local", label: label, ...props });
|
|
325
|
-
}
|
|
326
334
|
function Charts({ data, labels = [] }) {
|
|
327
335
|
const max = Math.max(...data, 1);
|
|
328
336
|
return ((0, jsx_runtime_1.jsx)("div", { className: "tokis-chart", role: "img", "aria-label": "Bar chart", children: data.map((value, index) => ((0, jsx_runtime_1.jsxs)("div", { className: "tokis-chart-bar-wrap", children: [(0, jsx_runtime_1.jsx)("div", { className: "tokis-chart-bar", style: { height: `${(value / max) * 100}%` }, title: `${labels[index] ?? index}: ${value}` }), (0, jsx_runtime_1.jsx)("span", { className: "tokis-chart-label", children: labels[index] ?? index + 1 })] }, index))) }));
|
|
@@ -22,10 +22,11 @@ exports.Box = (0, react_1.forwardRef)(({ as: Component = 'div', display, flex, g
|
|
|
22
22
|
flex,
|
|
23
23
|
gap: gap !== undefined ? gapVar(gap) : undefined,
|
|
24
24
|
padding: p !== undefined ? gapVar(p) : undefined,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
// Logical properties for RTL support
|
|
26
|
+
paddingInlineStart: px !== undefined ? gapVar(px) : undefined,
|
|
27
|
+
paddingInlineEnd: px !== undefined ? gapVar(px) : undefined,
|
|
28
|
+
paddingBlockStart: py !== undefined ? gapVar(py) : undefined,
|
|
29
|
+
paddingBlockEnd: py !== undefined ? gapVar(py) : undefined,
|
|
29
30
|
margin: m !== undefined ? gapVar(m) : undefined,
|
|
30
31
|
width,
|
|
31
32
|
height,
|
|
@@ -53,7 +53,7 @@ function TreeItem({ node, level, selected, expanded, onSelect, onExpand, multiSe
|
|
|
53
53
|
break;
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
|
-
return ((0, jsx_runtime_1.jsxs)("li", { role: "none", children: [(0, jsx_runtime_1.jsxs)("div", { role: "treeitem", "aria-expanded": hasChildren ? isExpanded : undefined, "aria-selected": isSelected, "aria-disabled": node.disabled, "aria-level": level, tabIndex: node.disabled ? -1 : 0, className: (0, cn_js_1.cn)('tokis-treeview__item', isSelected && 'tokis-treeview__item--selected', node.disabled && 'tokis-treeview__item--disabled'), style: {
|
|
56
|
+
return ((0, jsx_runtime_1.jsxs)("li", { role: "none", children: [(0, jsx_runtime_1.jsxs)("div", { role: "treeitem", "aria-expanded": hasChildren ? isExpanded : undefined, "aria-selected": isSelected, "aria-disabled": node.disabled, "aria-level": level, tabIndex: node.disabled ? -1 : 0, className: (0, cn_js_1.cn)('tokis-treeview__item', isSelected && 'tokis-treeview__item--selected', node.disabled && 'tokis-treeview__item--disabled'), style: { paddingInlineStart: `calc(${(level - 1) * 20}px + var(--tokis-spacing-2))` }, onClick: () => {
|
|
57
57
|
if (node.disabled)
|
|
58
58
|
return;
|
|
59
59
|
if (hasChildren)
|
package/dist/cjs/hooks/useId.js
CHANGED
|
@@ -2,12 +2,25 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.useId = useId;
|
|
4
4
|
const react_1 = require("react");
|
|
5
|
-
const core_1 = require("@tokis/core");
|
|
6
5
|
/**
|
|
7
|
-
* Returns a stable, unique ID for the lifetime of the component.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* Returns a stable, SSR-safe, unique ID for the lifetime of the component.
|
|
7
|
+
* Delegates to React 18's built-in `useId` to guarantee consistent IDs
|
|
8
|
+
* across server and client renders — no hydration mismatch.
|
|
9
|
+
*
|
|
10
|
+
* The raw React `useId` value (e.g. `:r3:`) has colons stripped so it is safe
|
|
11
|
+
* for use as an HTML `id` attribute and inside class name strings.
|
|
12
|
+
*
|
|
13
|
+
* @param prefix Optional string prepended to the generated ID.
|
|
14
|
+
* Defaults to `'tokis'`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const id = useId(); // → 'tokis-r3'
|
|
18
|
+
* const id = useId('dialog'); // → 'dialog-r3'
|
|
10
19
|
*/
|
|
11
20
|
function useId(prefix = 'tokis') {
|
|
12
|
-
|
|
21
|
+
// React 18 useId is SSR-safe: server and client produce matching values.
|
|
22
|
+
const reactId = (0, react_1.useId)();
|
|
23
|
+
// Strip the leading/trailing colons React adds (e.g. ':r3:' → 'r3')
|
|
24
|
+
const clean = reactId.replace(/:/g, '');
|
|
25
|
+
return prefix ? `${prefix}-${clean}` : clean;
|
|
13
26
|
}
|