@g4rcez/components 4.0.2 → 4.1.2
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/ai/SKILL.md +120 -153
- package/ai/docs/Alert.md +47 -48
- package/ai/docs/AnimatedList.md +88 -87
- package/ai/docs/Autocomplete.md +102 -106
- package/ai/docs/Button.md +33 -33
- package/ai/docs/Calendar.md +67 -101
- package/ai/docs/Card.md +59 -55
- package/ai/docs/Checkbox.md +77 -82
- package/ai/docs/CommandPalette.md +154 -172
- package/ai/docs/DatePicker.md +45 -64
- package/ai/docs/Dropdown.md +98 -126
- package/ai/docs/Empty.md +41 -44
- package/ai/docs/Expand.md +40 -54
- package/ai/docs/FileUpload.md +102 -109
- package/ai/docs/Form.md +35 -29
- package/ai/docs/FormReset.md +37 -37
- package/ai/docs/Heading.md +4 -6
- package/ai/docs/Input.md +73 -98
- package/ai/docs/InputField.md +67 -67
- package/ai/docs/List.md +88 -85
- package/ai/docs/Menu.md +62 -60
- package/ai/docs/Modal.md +129 -136
- package/ai/docs/MultiSelect.md +65 -84
- package/ai/docs/Notifications.md +96 -96
- package/ai/docs/PageCalendar.md +88 -97
- package/ai/docs/Polymorph.md +25 -41
- package/ai/docs/Progress.md +55 -51
- package/ai/docs/Radiobox.md +49 -41
- package/ai/docs/RenderOnView.md +26 -32
- package/ai/docs/Resizable.md +60 -65
- package/ai/docs/Select.md +158 -158
- package/ai/docs/Shortcut.md +14 -14
- package/ai/docs/Skeleton.md +56 -60
- package/ai/docs/Slider.md +26 -49
- package/ai/docs/Slot.md +15 -17
- package/ai/docs/Spinner.md +17 -15
- package/ai/docs/Stats.md +44 -42
- package/ai/docs/Step.md +60 -60
- package/ai/docs/Switch.md +44 -54
- package/ai/docs/Table.md +124 -136
- package/ai/docs/Tabs.md +90 -90
- package/ai/docs/Tag.md +63 -65
- package/ai/docs/TaskList.md +40 -50
- package/ai/docs/Textarea.md +47 -70
- package/ai/docs/Timeline.md +98 -96
- package/ai/docs/Toolbar.md +46 -46
- package/ai/docs/Tooltip.md +103 -103
- package/ai/docs/Typography.md +55 -55
- package/ai/docs/Wizard.md +104 -109
- package/ai/docs/index.md +116 -116
- package/dist/autocomplete-DcTNOwyg.js +393 -0
- package/dist/autocomplete-DcTNOwyg.js.map +1 -0
- package/dist/{calendar-B5lSd0ID.js → calendar-BJMHRoy2.js} +104 -105
- package/dist/calendar-BJMHRoy2.js.map +1 -0
- package/dist/components/core/button.d.ts.map +1 -1
- package/dist/components/core/button.js +10 -10
- package/dist/components/core/button.js.map +1 -1
- package/dist/components/core/button.jsx +79 -0
- package/dist/components/core/heading.jsx +4 -0
- package/dist/components/core/polymorph.d.ts +1 -1
- package/dist/components/core/polymorph.d.ts.map +1 -1
- package/dist/components/core/polymorph.jsx +5 -0
- package/dist/components/core/render-on-view.jsx +31 -0
- package/dist/components/core/resizable.jsx +51 -0
- package/dist/components/core/slot.d.ts.map +1 -1
- package/dist/components/core/slot.jsx +156 -0
- package/dist/components/core/tag.d.ts +1 -1
- package/dist/components/core/tag.d.ts.map +1 -1
- package/dist/components/core/tag.js +4 -4
- package/dist/components/core/tag.js.map +1 -1
- package/dist/components/core/tag.jsx +51 -0
- package/dist/components/core/typography.jsx +18 -0
- package/dist/components/display/alert.d.ts +1 -1
- package/dist/components/display/alert.d.ts.map +1 -1
- package/dist/components/display/alert.js +48 -46
- package/dist/components/display/alert.js.map +1 -1
- package/dist/components/display/alert.jsx +61 -0
- package/dist/components/display/calendar.js +6 -6
- package/dist/components/display/calendar.jsx +299 -0
- package/dist/components/display/card.js +2 -2
- package/dist/components/display/card.js.map +1 -1
- package/dist/components/display/card.jsx +43 -0
- package/dist/components/display/empty.jsx +11 -0
- package/dist/components/display/list.js +1 -1
- package/dist/components/display/list.js.map +1 -1
- package/dist/components/display/list.jsx +81 -0
- package/dist/components/display/notifications.d.ts +1 -1
- package/dist/components/display/notifications.d.ts.map +1 -1
- package/dist/components/display/notifications.js +3 -3
- package/dist/components/display/notifications.jsx +126 -0
- package/dist/components/display/progress.jsx +11 -0
- package/dist/components/display/shortcut.jsx +23 -0
- package/dist/components/display/skeleton.d.ts.map +1 -1
- package/dist/components/display/skeleton.jsx +19 -0
- package/dist/components/display/spinner.d.ts.map +1 -1
- package/dist/components/display/spinner.jsx +11 -0
- package/dist/components/display/stats.js +1 -1
- package/dist/components/display/stats.jsx +20 -0
- package/dist/components/display/step.jsx +131 -0
- package/dist/components/display/tabs.d.ts +1 -1
- package/dist/components/display/tabs.d.ts.map +1 -1
- package/dist/components/display/tabs.js +2 -2
- package/dist/components/display/tabs.jsx +125 -0
- package/dist/components/display/timeline.js +1 -1
- package/dist/components/display/timeline.jsx +25 -0
- package/dist/components/floating/command-palette.d.ts.map +1 -1
- package/dist/components/floating/command-palette.jsx +197 -0
- package/dist/components/floating/dropdown.d.ts.map +1 -1
- package/dist/components/floating/dropdown.js +25 -17
- package/dist/components/floating/dropdown.js.map +1 -1
- package/dist/components/floating/dropdown.jsx +56 -0
- package/dist/components/floating/expand.js +7 -7
- package/dist/components/floating/expand.js.map +1 -1
- package/dist/components/floating/expand.jsx +44 -0
- package/dist/components/floating/menu.js +10 -10
- package/dist/components/floating/menu.js.map +1 -1
- package/dist/components/floating/menu.jsx +147 -0
- package/dist/components/floating/modal.d.ts +4 -4
- package/dist/components/floating/modal.d.ts.map +1 -1
- package/dist/components/floating/modal.js +106 -102
- package/dist/components/floating/modal.js.map +1 -1
- package/dist/components/floating/modal.jsx +301 -0
- package/dist/components/floating/toolbar.jsx +5 -0
- package/dist/components/floating/tooltip.d.ts.map +1 -1
- package/dist/components/floating/tooltip.js +14 -14
- package/dist/components/floating/tooltip.js.map +1 -1
- package/dist/components/floating/tooltip.jsx +58 -0
- package/dist/components/floating/wizard.jsx +161 -0
- package/dist/components/form/autocomplete.d.ts.map +1 -1
- package/dist/components/form/autocomplete.js +6 -6
- package/dist/components/form/autocomplete.jsx +278 -0
- package/dist/components/form/checkbox.js +1 -1
- package/dist/components/form/checkbox.jsx +12 -0
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/date-picker.js +5 -5
- package/dist/components/form/date-picker.jsx +115 -0
- package/dist/components/form/file-upload.d.ts.map +1 -1
- package/dist/components/form/file-upload.js +2 -2
- package/dist/components/form/file-upload.jsx +134 -0
- package/dist/components/form/form.js.map +1 -1
- package/dist/components/form/form.jsx +10 -0
- package/dist/components/form/formReset.jsx +17 -0
- package/dist/components/form/free-text.d.ts.map +1 -1
- package/dist/components/form/free-text.jsx +41 -0
- package/dist/components/form/input-field.d.ts +11 -11
- package/dist/components/form/input-field.d.ts.map +1 -1
- package/dist/components/form/input-field.jsx +58 -0
- package/dist/components/form/input.js +1 -1
- package/dist/components/form/input.jsx +36 -0
- package/dist/components/form/multi-select.d.ts.map +1 -1
- package/dist/components/form/multi-select.jsx +336 -0
- package/dist/components/form/radiobox.js +1 -1
- package/dist/components/form/radiobox.jsx +6 -0
- package/dist/components/form/select.js +3 -3
- package/dist/components/form/select.jsx +42 -0
- package/dist/components/form/slider.jsx +45 -0
- package/dist/components/form/switch.js +2 -2
- package/dist/components/form/switch.js.map +1 -1
- package/dist/components/form/switch.jsx +46 -0
- package/dist/components/form/task-list.jsx +26 -0
- package/dist/components/form/textarea.jsx +12 -0
- package/dist/components/index.d.ts +0 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +44 -0
- package/dist/components/page-calendar/calendar-header.jsx +83 -0
- package/dist/components/page-calendar/day-view.jsx +94 -0
- package/dist/components/page-calendar/event-pill.d.ts.map +1 -1
- package/dist/components/page-calendar/event-pill.jsx +25 -0
- package/dist/components/page-calendar/index.js +2 -0
- package/dist/components/page-calendar/month-view.jsx +46 -0
- package/dist/components/page-calendar/page-calendar.jsx +41 -0
- package/dist/components/page-calendar/page-calendar.types.js +1 -0
- package/dist/components/page-calendar/page-calendar.utils.js +93 -0
- package/dist/components/page-calendar/week-view.jsx +71 -0
- package/dist/components/table/filter.jsx +141 -0
- package/dist/components/table/group.jsx +68 -0
- package/dist/components/table/index.d.ts +2 -3
- package/dist/components/table/index.d.ts.map +1 -1
- package/dist/components/table/index.jsx +60 -0
- package/dist/components/table/inner-table.d.ts +2 -3
- package/dist/components/table/inner-table.d.ts.map +1 -1
- package/dist/components/table/inner-table.jsx +102 -0
- package/dist/components/table/metadata.jsx +36 -0
- package/dist/components/table/pagination.d.ts +3 -1
- package/dist/components/table/pagination.d.ts.map +1 -1
- package/dist/components/table/pagination.jsx +74 -0
- package/dist/components/table/row.d.ts +4 -5
- package/dist/components/table/row.d.ts.map +1 -1
- package/dist/components/table/row.jsx +49 -0
- package/dist/components/table/sort.d.ts.map +1 -1
- package/dist/components/table/sort.jsx +109 -0
- package/dist/components/table/table-lib.d.ts +1 -1
- package/dist/components/table/table-lib.d.ts.map +1 -1
- package/dist/components/table/table-lib.js +83 -0
- package/dist/components/table/table.context.jsx +4 -0
- package/dist/components/table/table.js +3 -3
- package/dist/components/table/thead.jsx +103 -0
- package/dist/config/context.js +12 -0
- package/dist/config/default-translations.d.ts +4 -0
- package/dist/config/default-translations.d.ts.map +1 -1
- package/dist/config/default-translations.jsx +87 -0
- package/dist/config/default-tweaks.js +4 -0
- package/dist/constants.js +2 -0
- package/dist/{date-picker-DwNo22lx.js → date-picker-Bnl07nd8.js} +8 -6
- package/dist/date-picker-Bnl07nd8.js.map +1 -0
- package/dist/{dom-Dl8XH0CK.js → dom-CyQHY7ID.js} +66 -61
- package/dist/dom-CyQHY7ID.js.map +1 -0
- package/dist/{file-upload-Brf2NkLr.js → file-upload-93d5DR4q.js} +139 -129
- package/dist/file-upload-93d5DR4q.js.map +1 -0
- package/dist/{floating-ui.react-QNHG9W4N.js → floating-ui.react-M2PGXLDp.js} +8 -8
- package/dist/floating-ui.react-M2PGXLDp.js.map +1 -0
- package/dist/{fns-D2eyJKd5.js → fns-C7Dt27Qd.js} +2 -2
- package/dist/fns-C7Dt27Qd.js.map +1 -0
- package/dist/hooks/use-click-outside.js +17 -0
- package/dist/hooks/use-color-parser.js +9 -0
- package/dist/hooks/use-components-provider.jsx +22 -0
- package/dist/hooks/use-debounce.d.ts +3 -3
- package/dist/hooks/use-debounce.d.ts.map +1 -1
- package/dist/hooks/use-debounce.js +12 -0
- package/dist/hooks/use-floating-ref.js +6 -0
- package/dist/hooks/use-form.d.ts +14 -13
- package/dist/hooks/use-form.d.ts.map +1 -1
- package/dist/hooks/use-form.js +563 -0
- package/dist/hooks/use-hover.js +18 -0
- package/dist/hooks/use-input-id.js +5 -0
- package/dist/hooks/use-is-coarse-device.js +12 -0
- package/dist/hooks/use-locale.js +10 -0
- package/dist/hooks/use-media-query.js +25 -0
- package/dist/hooks/use-on-event.js +7 -0
- package/dist/hooks/use-parent.js +21 -0
- package/dist/hooks/use-preferences.js +23 -0
- package/dist/hooks/use-previous.js +9 -0
- package/dist/hooks/use-reactive.js +9 -0
- package/dist/hooks/use-remove-scroll.d.ts.map +1 -1
- package/dist/hooks/use-remove-scroll.js +48 -0
- package/dist/hooks/use-resize-observer.js +17 -0
- package/dist/hooks/use-stable-ref.js +9 -0
- package/dist/hooks/use-swipe.js +17 -0
- package/dist/hooks/use-translations.d.ts +4 -0
- package/dist/hooks/use-translations.d.ts.map +1 -1
- package/dist/hooks/use-translations.js +9 -0
- package/dist/hooks/use-tweaks.js +9 -0
- package/dist/hooks/use-window-size.js +14 -0
- package/dist/index-BJ1ayTam.js +126 -0
- package/dist/index-BJ1ayTam.js.map +1 -0
- package/dist/index-C-PuFUZi.js.map +1 -1
- package/dist/{index-BelDtX5M.js → index-DDeQW0JW.js} +399 -404
- package/dist/index-DDeQW0JW.js.map +1 -0
- package/dist/index-DE4shK8D.js +215 -0
- package/dist/index-DE4shK8D.js.map +1 -0
- package/dist/{index-DJSMaZR4.js → index-DlJ_2RBL.js} +2 -2
- package/dist/index-DlJ_2RBL.js.map +1 -0
- package/dist/index.css +1 -1
- package/dist/index.esm-BaIwleSE.js.map +1 -1
- package/dist/index.js +20 -8177
- package/dist/index.js.map +1 -1
- package/dist/{input-CrGrSnUt.js → input-_MVxmIpu.js} +116 -114
- package/dist/input-_MVxmIpu.js.map +1 -0
- package/dist/{input-field-5vYcz5tT.js → input-field-CDCOODOl.js} +63 -47
- package/dist/input-field-CDCOODOl.js.map +1 -0
- package/dist/lib/combi-keys.js +60 -0
- package/dist/lib/dict.d.ts +1 -4
- package/dist/lib/dict.d.ts.map +1 -1
- package/dist/lib/dict.js +28 -0
- package/dist/lib/dom.d.ts +3 -2
- package/dist/lib/dom.d.ts.map +1 -1
- package/dist/lib/dom.js +66 -0
- package/dist/lib/fns.js +46 -0
- package/dist/lib/fzf.d.ts.map +1 -1
- package/dist/lib/fzf.js +115 -0
- package/dist/lib/keyboard-area.js +14 -0
- package/dist/notifications-Dp0ydKJS.js +2576 -0
- package/dist/notifications-Dp0ydKJS.js.map +1 -0
- package/dist/polymorph-B5n9fs_K.js.map +1 -1
- package/dist/preset/plugin.tailwind.d.ts.map +1 -1
- package/dist/preset/plugin.tailwind.js +20 -18
- package/dist/preset/preset.tailwind.js +24 -25
- package/dist/preset/src/styles/common.js +6 -3
- package/dist/preset/src/styles/dark.d.ts.map +1 -1
- package/dist/preset/src/styles/dark.js +9 -5
- package/dist/preset/src/styles/design-tokens.js +68 -89
- package/dist/preset/src/styles/light.d.ts.map +1 -1
- package/dist/preset/src/styles/light.js +9 -5
- package/dist/preset/src/styles/theme.js +7 -4
- package/dist/preset/src/styles/theme.types.d.ts +2 -1
- package/dist/preset/src/styles/theme.types.d.ts.map +1 -1
- package/dist/preset/src/styles/theme.types.js +2 -1
- package/dist/skeleton-BjJobYYf.js +24 -0
- package/dist/skeleton-BjJobYYf.js.map +1 -0
- package/dist/slot-CRyweuj0.js.map +1 -1
- package/dist/styles/common.js +6 -3
- package/dist/styles/dark.d.ts.map +1 -1
- package/dist/styles/dark.js +9 -5
- package/dist/styles/design-tokens.js +68 -89
- package/dist/styles/light.d.ts.map +1 -1
- package/dist/styles/light.js +9 -5
- package/dist/styles/theme.js +7 -4
- package/dist/styles/theme.types.d.ts +2 -1
- package/dist/styles/theme.types.d.ts.map +1 -1
- package/dist/styles/theme.types.js +2 -1
- package/dist/tabs-B0g7YtQv.js +123 -0
- package/dist/tabs-B0g7YtQv.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -0
- package/dist/{context-BFXNJVn2.js → use-translations-DSltA7H_.js} +38 -30
- package/dist/use-translations-DSltA7H_.js.map +1 -0
- package/package.json +99 -86
- package/ai/docs/TransferList.md +0 -142
- package/dist/autocomplete-Cn9Z2hLq.js +0 -375
- package/dist/autocomplete-Cn9Z2hLq.js.map +0 -1
- package/dist/calendar-B5lSd0ID.js.map +0 -1
- package/dist/components/form/transfer-list.d.ts +0 -14
- package/dist/components/form/transfer-list.d.ts.map +0 -1
- package/dist/components/form/transfer-list.js +0 -55
- package/dist/components/form/transfer-list.js.map +0 -1
- package/dist/context-BFXNJVn2.js.map +0 -1
- package/dist/date-picker-DwNo22lx.js.map +0 -1
- package/dist/dom-Dl8XH0CK.js.map +0 -1
- package/dist/file-upload-Brf2NkLr.js.map +0 -1
- package/dist/floating-ui.react-QNHG9W4N.js.map +0 -1
- package/dist/fns-D2eyJKd5.js.map +0 -1
- package/dist/index-BelDtX5M.js.map +0 -1
- package/dist/index-DJSMaZR4.js.map +0 -1
- package/dist/index-Z-fZHxfJ.js +0 -335
- package/dist/index-Z-fZHxfJ.js.map +0 -1
- package/dist/input-CrGrSnUt.js.map +0 -1
- package/dist/input-field-5vYcz5tT.js.map +0 -1
- package/dist/notifications-cUdVPs-B.js +0 -2786
- package/dist/notifications-cUdVPs-B.js.map +0 -1
- package/dist/skeleton-D75GFBV6.js +0 -10
- package/dist/skeleton-D75GFBV6.js.map +0 -1
- package/dist/tabs-S00a8qq8.js +0 -106
- package/dist/tabs-S00a8qq8.js.map +0 -1
- package/dist/use-translations-BwLKTrZv.js +0 -10
- package/dist/use-translations-BwLKTrZv.js.map +0 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { addDays, startOfMonth, startOfWeek, getISOWeek } from "date-fns";
|
|
2
|
+
export function toDateKey(d) {
|
|
3
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
4
|
+
}
|
|
5
|
+
export function groupEventsByDate(events) {
|
|
6
|
+
const map = new Map();
|
|
7
|
+
for (const event of events) {
|
|
8
|
+
const key = toDateKey(event.date);
|
|
9
|
+
const existing = map.get(key);
|
|
10
|
+
if (existing) {
|
|
11
|
+
existing.push(event);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
map.set(key, [event]);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return map;
|
|
18
|
+
}
|
|
19
|
+
export function getMonthDays(date) {
|
|
20
|
+
const monthStart = startOfMonth(date);
|
|
21
|
+
const gridStart = startOfWeek(monthStart, { weekStartsOn: 1 });
|
|
22
|
+
const days = [];
|
|
23
|
+
for (let i = 0; i < 42; i++) {
|
|
24
|
+
days.push(addDays(gridStart, i));
|
|
25
|
+
}
|
|
26
|
+
return days;
|
|
27
|
+
}
|
|
28
|
+
export function getWeekDays(date) {
|
|
29
|
+
const weekStart = startOfWeek(date, { weekStartsOn: 1 });
|
|
30
|
+
const days = [];
|
|
31
|
+
for (let i = 0; i < 7; i++) {
|
|
32
|
+
days.push(addDays(weekStart, i));
|
|
33
|
+
}
|
|
34
|
+
return days;
|
|
35
|
+
}
|
|
36
|
+
export function getHourSlots() {
|
|
37
|
+
return Array.from({ length: 24 }, (_, i) => i);
|
|
38
|
+
}
|
|
39
|
+
export function formatEventTime(date, locale) {
|
|
40
|
+
return new Intl.DateTimeFormat(locale, { hour: "2-digit", minute: "2-digit", hour12: false }).format(new Date(date));
|
|
41
|
+
}
|
|
42
|
+
export function formatDay(date, locale) {
|
|
43
|
+
return new Intl.DateTimeFormat(locale, { day: "numeric" }).format(date);
|
|
44
|
+
}
|
|
45
|
+
export function formatWeekDay(date, locale) {
|
|
46
|
+
return new Intl.DateTimeFormat(locale, { weekday: "short" }).format(date);
|
|
47
|
+
}
|
|
48
|
+
export function formatWeekdayLong(date, locale) {
|
|
49
|
+
return new Intl.DateTimeFormat(locale, { weekday: "long" }).format(date);
|
|
50
|
+
}
|
|
51
|
+
export function formatWeekdayShort(date, locale) {
|
|
52
|
+
return new Intl.DateTimeFormat(locale, { weekday: "short" }).format(date);
|
|
53
|
+
}
|
|
54
|
+
export function formatMonthYear(date, locale) {
|
|
55
|
+
return new Intl.DateTimeFormat(locale, { month: "long", year: "numeric" }).format(date);
|
|
56
|
+
}
|
|
57
|
+
export function formatMonthShort(date, locale) {
|
|
58
|
+
return new Intl.DateTimeFormat(locale, { month: "short" }).format(date);
|
|
59
|
+
}
|
|
60
|
+
export function formatHourLabel(hour, locale) {
|
|
61
|
+
return new Intl.DateTimeFormat(locale, { hour: "2-digit", minute: "2-digit", hour12: false }).format(new Date(0, 0, 0, hour));
|
|
62
|
+
}
|
|
63
|
+
export function formatFullDate(date, locale) {
|
|
64
|
+
return new Intl.DateTimeFormat(locale, { weekday: "long", month: "short", day: "numeric", year: "numeric" }).format(date);
|
|
65
|
+
}
|
|
66
|
+
export function formatTime(date, locale) {
|
|
67
|
+
return new Intl.DateTimeFormat(locale, { hour: "2-digit", minute: "2-digit", hour12: false }).format(date);
|
|
68
|
+
}
|
|
69
|
+
export function getWeekNumber(date) {
|
|
70
|
+
return getISOWeek(date);
|
|
71
|
+
}
|
|
72
|
+
export function computeEventColumns(events) {
|
|
73
|
+
const sorted = [...events].sort((a, b) => a.date.getTime() - b.date.getTime());
|
|
74
|
+
const hourGroups = new Map();
|
|
75
|
+
for (const event of sorted) {
|
|
76
|
+
const hour = event.date.getHours();
|
|
77
|
+
const group = hourGroups.get(hour);
|
|
78
|
+
if (group) {
|
|
79
|
+
group.push(event);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
hourGroups.set(hour, [event]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const result = [];
|
|
86
|
+
for (const group of hourGroups.values()) {
|
|
87
|
+
const columnCount = group.length;
|
|
88
|
+
group.forEach((event, columnIndex) => {
|
|
89
|
+
result.push({ event, columnIndex, columnCount });
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { isToday } from "date-fns";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useLocale } from "../../hooks/use-locale";
|
|
4
|
+
import { EventPill } from "./event-pill";
|
|
5
|
+
import { getHourSlots, toDateKey, formatWeekdayShort, formatDay, formatHourLabel, formatFullDate, computeEventColumns } from "./page-calendar.utils";
|
|
6
|
+
const HOUR_HEIGHT = 48;
|
|
7
|
+
function getTopOffset(event) {
|
|
8
|
+
const hour = event.date.getHours();
|
|
9
|
+
const minutes = event.date.getMinutes();
|
|
10
|
+
return hour * HOUR_HEIGHT + (minutes / 60) * HOUR_HEIGHT;
|
|
11
|
+
}
|
|
12
|
+
export function WeekView({ days, eventsByDate, onEventClick, onSlotClick }) {
|
|
13
|
+
const locale = useLocale();
|
|
14
|
+
const currentHourRef = useRef(null);
|
|
15
|
+
const scrollBodyRef = useRef(null);
|
|
16
|
+
const hours = getHourSlots();
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (scrollBodyRef.current && currentHourRef.current) {
|
|
19
|
+
const top = currentHourRef.current.offsetTop;
|
|
20
|
+
scrollBodyRef.current.scrollTop = top - scrollBodyRef.current.clientHeight / 2;
|
|
21
|
+
}
|
|
22
|
+
}, []);
|
|
23
|
+
return (<div className="flex flex-1 flex-col overflow-hidden">
|
|
24
|
+
<div className="flex flex-shrink-0 border-b border-border">
|
|
25
|
+
<div className="w-[60px] flex-shrink-0"/>
|
|
26
|
+
{days.map((day, idx) => {
|
|
27
|
+
const isCurrentDay = isToday(day);
|
|
28
|
+
return (<div key={idx} aria-label={formatFullDate(day, locale)} className="flex-1 py-2 text-center text-xs font-medium text-muted-foreground">
|
|
29
|
+
<span className="block">{formatWeekdayShort(day, locale)}</span>
|
|
30
|
+
<span className={`inline-flex h-6 w-6 items-center justify-center rounded-full text-sm font-bold ${isCurrentDay ? "bg-primary text-primary-foreground" : "text-foreground"}`}>
|
|
31
|
+
{formatDay(day, locale)}
|
|
32
|
+
</span>
|
|
33
|
+
</div>);
|
|
34
|
+
})}
|
|
35
|
+
</div>
|
|
36
|
+
<div ref={scrollBodyRef} className="flex flex-1 items-start overflow-y-auto">
|
|
37
|
+
<div className="w-[60px] flex-shrink-0">
|
|
38
|
+
{hours.map((hour) => (<div key={hour} className="relative" style={{ height: HOUR_HEIGHT }}>
|
|
39
|
+
<span className="absolute -top-2.5 right-2 text-[10px] text-muted-foreground">
|
|
40
|
+
{hour === 0 ? "" : formatHourLabel(hour, locale)}
|
|
41
|
+
</span>
|
|
42
|
+
{hour === new Date().getHours() && <div ref={currentHourRef}/>}
|
|
43
|
+
</div>))}
|
|
44
|
+
</div>
|
|
45
|
+
{days.map((day, dayIdx) => {
|
|
46
|
+
const key = toDateKey(day);
|
|
47
|
+
const events = eventsByDate.get(key) || [];
|
|
48
|
+
return (<div key={dayIdx} className="relative flex-1 border-l border-card-border">
|
|
49
|
+
{hours.map((hour) => {
|
|
50
|
+
const slotDate = new Date(day);
|
|
51
|
+
slotDate.setHours(hour, 0, 0, 0);
|
|
52
|
+
return (<div key={hour} role="button" tabIndex={0} aria-label={formatHourLabel(hour, locale)} className="cursor-pointer border-b border-border/50 hover:bg-muted/20" style={{ height: HOUR_HEIGHT }} onClick={() => onSlotClick?.(slotDate)} onKeyDown={(e) => {
|
|
53
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
onSlotClick?.(slotDate);
|
|
56
|
+
}
|
|
57
|
+
}}/>);
|
|
58
|
+
})}
|
|
59
|
+
{computeEventColumns(events).map(({ event, columnIndex, columnCount }) => (<div key={event.id} className="absolute" style={{
|
|
60
|
+
top: getTopOffset(event),
|
|
61
|
+
height: HOUR_HEIGHT,
|
|
62
|
+
left: `calc(${(columnIndex / columnCount) * 100}% + 1px)`,
|
|
63
|
+
width: `calc(${100 / columnCount}% - 2px)`,
|
|
64
|
+
}} onClick={(e) => e.stopPropagation()}>
|
|
65
|
+
<EventPill event={event} onClick={() => onEventClick(event)}/>
|
|
66
|
+
</div>))}
|
|
67
|
+
</div>);
|
|
68
|
+
})}
|
|
69
|
+
</div>
|
|
70
|
+
</div>);
|
|
71
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { FunnelIcon, PlusIcon, TrashIcon } from "@phosphor-icons/react";
|
|
2
|
+
import React, { Fragment, useMemo } from "react";
|
|
3
|
+
import { useTranslations } from "../../hooks/use-translations";
|
|
4
|
+
import { uuid } from "../../lib/fns";
|
|
5
|
+
import { Dropdown } from "../floating/dropdown";
|
|
6
|
+
import { Input } from "../form/input";
|
|
7
|
+
import { Select } from "../form/select";
|
|
8
|
+
import { ColType, getLabel, valueFromType } from "./table-lib";
|
|
9
|
+
export const createFilterFromCol = (f, options, operations, rest = {}) => {
|
|
10
|
+
const name = f.id;
|
|
11
|
+
const type = f.type ?? ColType.Text;
|
|
12
|
+
const typeOptions = options[type] ?? [];
|
|
13
|
+
const operatorId = (typeOptions.find((x) => x["data-default"])?.value ?? typeOptions[0]?.value);
|
|
14
|
+
const operation = operations[operatorId];
|
|
15
|
+
return { id: uuid(), operation, label: getLabel(f), name, type, value: "", ...rest };
|
|
16
|
+
};
|
|
17
|
+
export const useOperators = () => {
|
|
18
|
+
const translation = useTranslations();
|
|
19
|
+
const operations = useMemo(() => ({
|
|
20
|
+
contains: {
|
|
21
|
+
value: "contains",
|
|
22
|
+
label: translation.tableFilterTypeContains,
|
|
23
|
+
symbol: "includes",
|
|
24
|
+
"data-default": "true",
|
|
25
|
+
},
|
|
26
|
+
is: { value: "is", label: translation.tableFilterTypeIs, symbol: "is" },
|
|
27
|
+
isNot: { value: "isNot", label: translation.tableFilterTypeIsNot, symbol: "!==" },
|
|
28
|
+
notContains: {
|
|
29
|
+
value: "notContains",
|
|
30
|
+
label: translation.tableFilterTypeNotContains,
|
|
31
|
+
symbol: "notIncludes",
|
|
32
|
+
},
|
|
33
|
+
lessThan: { value: "lessThan", label: translation.tableFilterTypeLessThan, symbol: "<=" },
|
|
34
|
+
greaterThan: { value: "greaterThan", label: translation.tableFilterTypeGreaterThan, symbol: ">=" },
|
|
35
|
+
startsWith: { value: "startsWith", label: translation.tableFilterTypeStartsWith, symbol: "startsWith" },
|
|
36
|
+
endsWith: { value: "endsWith", label: translation.tableFilterTypeEndsWith, symbol: "endsWith" },
|
|
37
|
+
}), [translation]);
|
|
38
|
+
const options = useMemo(() => ({
|
|
39
|
+
[ColType.Text]: [
|
|
40
|
+
operations.is,
|
|
41
|
+
operations.isNot,
|
|
42
|
+
operations.contains,
|
|
43
|
+
operations.notContains,
|
|
44
|
+
operations.startsWith,
|
|
45
|
+
operations.endsWith,
|
|
46
|
+
],
|
|
47
|
+
[ColType.Boolean]: [operations.is, operations.isNot],
|
|
48
|
+
[ColType.Number]: [operations.is, operations.isNot, operations.greaterThan, operations.lessThan],
|
|
49
|
+
}), [operations]);
|
|
50
|
+
return { options, operations };
|
|
51
|
+
};
|
|
52
|
+
export const Filter = (props) => {
|
|
53
|
+
const translation = useTranslations();
|
|
54
|
+
const operators = useOperators();
|
|
55
|
+
const onAddFilter = () => {
|
|
56
|
+
const col = props.cols.at(0);
|
|
57
|
+
props.set((prev) => [...prev, createFilterFromCol(col, operators.options, operators.operations)]);
|
|
58
|
+
};
|
|
59
|
+
const onSelectProperty = (e) => {
|
|
60
|
+
const changedId = e.target.dataset.id || "";
|
|
61
|
+
const newId = e.target.value;
|
|
62
|
+
props.set((prev) => prev.map((x) => {
|
|
63
|
+
if (changedId !== x.id)
|
|
64
|
+
return x;
|
|
65
|
+
const col = props.cols.find((x) => newId === x.id);
|
|
66
|
+
return createFilterFromCol(col, operators.options, operators.operations, { value: "" });
|
|
67
|
+
}));
|
|
68
|
+
};
|
|
69
|
+
const onSelectOperation = (e) => {
|
|
70
|
+
const id = e.target.dataset.id || "";
|
|
71
|
+
const operator = e.target.value;
|
|
72
|
+
props.set((prev) => prev.map((x) => x.id === id
|
|
73
|
+
? {
|
|
74
|
+
...x,
|
|
75
|
+
operation: operators.operations[operator],
|
|
76
|
+
}
|
|
77
|
+
: x));
|
|
78
|
+
};
|
|
79
|
+
const onDelete = (e) => {
|
|
80
|
+
const id = e.currentTarget.dataset.id || "";
|
|
81
|
+
props.set((prev) => prev.filter((x) => x.id !== id));
|
|
82
|
+
};
|
|
83
|
+
const onChangeValue = (e) => {
|
|
84
|
+
const id = e.target.dataset.id || "";
|
|
85
|
+
const value = valueFromType(e.target);
|
|
86
|
+
props.set((prev) => prev.map((x) => (x.id === id ? { ...x, value } : x)));
|
|
87
|
+
};
|
|
88
|
+
return (<Fragment>
|
|
89
|
+
<Dropdown arrow title={translation.tableFilterDropdownTitle} trigger={<span className="flex items-center gap-1 proportional-nums">
|
|
90
|
+
<FunnelIcon size={14}/>
|
|
91
|
+
{translation.tableFilterLabel} {props.filters.length === 0 ? "" : ` (${props.filters.length})`}
|
|
92
|
+
</span>}>
|
|
93
|
+
<ul className="mt-4 space-y-2">
|
|
94
|
+
{props.filters.map((filter) => {
|
|
95
|
+
const options = operators.options[filter.type];
|
|
96
|
+
return (<li key={`filter-select-${filter.id}`} className="flex flex-nowrap gap-3">
|
|
97
|
+
<Select options={props.options} title={translation.tableFilterColumnTitle} placeholder={translation.tableFilterColumnPlaceholder} value={filter.name} data-id={filter.id} onChange={onSelectProperty}/>
|
|
98
|
+
<Select data-id={filter.id} onChange={onSelectOperation} value={filter.operation.value} options={options} title={translation.tableFilterOperatorTitle} placeholder={translation.tableFilterOperatorPlaceholder}/>
|
|
99
|
+
<Input optionalText="" data-id={filter.id} onChange={onChangeValue} value={filter.value} type={filter.type} title={translation.tableFilterValueTitle} placeholder={translation.tableFilterValuePlaceholder}/>
|
|
100
|
+
<div className="mt-5 flex items-center justify-center">
|
|
101
|
+
<button data-id={filter.id} type="button" onClick={onDelete}>
|
|
102
|
+
<TrashIcon className="text-danger" size={16}/>
|
|
103
|
+
</button>
|
|
104
|
+
</div>
|
|
105
|
+
</li>);
|
|
106
|
+
})}
|
|
107
|
+
<li>
|
|
108
|
+
<button type="button" onClick={onAddFilter} className="flex items-center gap-1 text-primary">
|
|
109
|
+
<PlusIcon size={14}/> {translation.tableFilterNewFilter}
|
|
110
|
+
</button>
|
|
111
|
+
</li>
|
|
112
|
+
</ul>
|
|
113
|
+
</Dropdown>
|
|
114
|
+
</Fragment>);
|
|
115
|
+
};
|
|
116
|
+
export const ColumnHeaderFilter = ({ filter, onDelete, set }) => {
|
|
117
|
+
const translation = useTranslations();
|
|
118
|
+
const operators = useOperators();
|
|
119
|
+
const onSelectOperation = (e) => {
|
|
120
|
+
const operator = e.target.value;
|
|
121
|
+
const id = e.target.dataset.id || "";
|
|
122
|
+
set((prev) => prev.map((x) => x.id === id
|
|
123
|
+
? {
|
|
124
|
+
...x,
|
|
125
|
+
operation: operators.operations[operator],
|
|
126
|
+
}
|
|
127
|
+
: x));
|
|
128
|
+
};
|
|
129
|
+
const onChangeValue = (e) => {
|
|
130
|
+
const id = e.target.dataset.id || "";
|
|
131
|
+
const value = valueFromType(e.target);
|
|
132
|
+
set((prev) => prev.map((x) => (x.id === id ? { ...x, value } : x)));
|
|
133
|
+
};
|
|
134
|
+
return (<div className="flex flex-nowrap items-center gap-4 py-2">
|
|
135
|
+
<Select data-id={filter.id} onChange={onSelectOperation} value={filter.operation.value} options={operators.options[filter.type]} title={translation.tableFilterColumnTitle} placeholder={translation.tableFilterColumnPlaceholder}/>
|
|
136
|
+
<Input optionalText=" " data-id={filter.id} onChange={onChangeValue} value={filter.value} type={filter.type} title={translation.tableFilterValueTitle} placeholder={translation.tableFilterValueTitle}/>
|
|
137
|
+
<button onClick={onDelete} data-id={filter.id} type="button" className="mt-4">
|
|
138
|
+
<TrashIcon className="text-danger" size={14}/>
|
|
139
|
+
</button>
|
|
140
|
+
</div>);
|
|
141
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { LayoutGroup, Reorder, useDragControls, useMotionValue } from "motion/react";
|
|
3
|
+
import Linq from "linq-arrays";
|
|
4
|
+
import { DotsSixVerticalIcon, TrashIcon, StackMinusIcon } from "@phosphor-icons/react";
|
|
5
|
+
import React, { Fragment, useState } from "react";
|
|
6
|
+
import { keys } from "sidekicker";
|
|
7
|
+
import { useTranslations } from "../../hooks/use-translations";
|
|
8
|
+
import { uuid } from "../../lib/fns";
|
|
9
|
+
import { Button } from "../core/button";
|
|
10
|
+
import { Dropdown } from "../floating/dropdown";
|
|
11
|
+
import { Select } from "../form/select";
|
|
12
|
+
import { createOptionCols } from "./table-lib";
|
|
13
|
+
const Item = ({ item, onPointerDown }) => {
|
|
14
|
+
const y = useMotionValue(0);
|
|
15
|
+
return (<Reorder.Item onPointerDown={onPointerDown} id={item.groupId} className="flex flex-row items-center gap-2" key={item.groupId} value={item} style={{ y }}>
|
|
16
|
+
<button type="button" className="cursor-grab">
|
|
17
|
+
<DotsSixVerticalIcon size={14}/>
|
|
18
|
+
</button>
|
|
19
|
+
<span>{item.groupName}</span>
|
|
20
|
+
</Reorder.Item>);
|
|
21
|
+
};
|
|
22
|
+
export const Group = (props) => {
|
|
23
|
+
const translations = useTranslations();
|
|
24
|
+
const options = createOptionCols(props.cols);
|
|
25
|
+
const controls = useDragControls();
|
|
26
|
+
const [group, setGroup] = useState(props.groups[0]?.thead || "");
|
|
27
|
+
const onChange = (e) => {
|
|
28
|
+
const select = e.target;
|
|
29
|
+
const key = select.value;
|
|
30
|
+
const index = select.options.selectedIndex;
|
|
31
|
+
const label = select.options.item(index)?.label || "";
|
|
32
|
+
setGroup(label);
|
|
33
|
+
const groupBy = new Linq(props.rows).GroupBy(key);
|
|
34
|
+
const col = props.cols.find((x) => x.id === key);
|
|
35
|
+
props.setGroups(keys(groupBy).map((groupName, index) => {
|
|
36
|
+
const rows = groupBy[groupName];
|
|
37
|
+
return { ...col, groupId: uuid(), groupKey: key, index, rows, groupName: groupName };
|
|
38
|
+
}));
|
|
39
|
+
};
|
|
40
|
+
const onDelete = () => props.setGroups([]);
|
|
41
|
+
return (<Fragment>
|
|
42
|
+
<Dropdown arrow={false} title={translations.tableGroupLabel} trigger={<span className="flex items-center gap-1 proportional-nums">
|
|
43
|
+
<StackMinusIcon size={14}/>
|
|
44
|
+
{translations.tableGroupLabelWithCount}
|
|
45
|
+
{props.groups.length > 0 ? ` - ${group}(${props.groups.length})` : ""}
|
|
46
|
+
</span>}>
|
|
47
|
+
<div className="flex flex-nowrap items-center">
|
|
48
|
+
<Select value={group} title="Tipo de agrupamento" onChange={onChange} options={options} placeholder="Agrupar por..."/>
|
|
49
|
+
<Button className="mt-4" onClick={onDelete} theme="raw" data-id={group}>
|
|
50
|
+
<TrashIcon size={16} className="text-danger"/>
|
|
51
|
+
</Button>
|
|
52
|
+
</div>
|
|
53
|
+
{props.groups.length > 0 ? (<section className="my-4">
|
|
54
|
+
<header>
|
|
55
|
+
<h2 className="text-xl font-medium">Order groups</h2>
|
|
56
|
+
</header>
|
|
57
|
+
<LayoutGroup>
|
|
58
|
+
<Reorder.Group axis="y" className="relative space-y-2" drag dragControls={controls} dragListener={false} layoutScroll onReorder={props.setGroups} values={props.groups}>
|
|
59
|
+
{props.groups.map((item) => (<Item key={item.groupId} item={item} onPointerDown={(e) => {
|
|
60
|
+
controls.start(e);
|
|
61
|
+
props.setGroups([...props.groups]);
|
|
62
|
+
}}/>))}
|
|
63
|
+
</Reorder.Group>
|
|
64
|
+
</LayoutGroup>
|
|
65
|
+
</section>) : null}
|
|
66
|
+
</Dropdown>
|
|
67
|
+
</Fragment>);
|
|
68
|
+
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ComponentProps } from "react";
|
|
2
|
-
import { Any } from "../../types";
|
|
3
2
|
import { InnerTableProps } from "./inner-table";
|
|
4
3
|
import { TableOperationProps } from "./table-lib";
|
|
5
|
-
export type TableProps<T extends
|
|
4
|
+
export type TableProps<T extends Record<string, unknown>> = Pick<InnerTableProps<T>, "cols" | "rows" | "loadingMore" | "border" | "Aside"> & {
|
|
6
5
|
name: string;
|
|
7
6
|
} & Partial<TableOperationProps<T> & {
|
|
8
7
|
loading: boolean;
|
|
@@ -16,5 +15,5 @@ export type TableProps<T extends Any> = Pick<InnerTableProps<T>, "cols" | "rows"
|
|
|
16
15
|
getScrollRef?: () => HTMLElement | undefined;
|
|
17
16
|
getRowProps: (row: T) => ComponentProps<"tr">;
|
|
18
17
|
}>;
|
|
19
|
-
export declare const Table: <T extends
|
|
18
|
+
export declare const Table: <T extends Record<string, unknown>>(props: TableProps<T>) => import("react").JSX.Element;
|
|
20
19
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/table/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAsB,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/table/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAsB,MAAM,OAAO,CAAC;AAK3D,OAAO,EAAc,eAAe,EAAE,MAAM,eAAe,CAAC;AAG5D,OAAO,EAAuC,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAGvF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,GAAG,OAAO,CAAC,GAAG;IACzI,IAAI,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CACH,mBAAmB,CAAC,CAAC,CAAC,GAAG;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IAC7C,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,CAAC;CACjD,CACJ,CAAC;AASN,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,UAAU,CAAC,CAAC,CAAC,gCAwH5E,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { AnimatePresence } from "motion/react";
|
|
3
|
+
import { useEffect, useMemo } from "react";
|
|
4
|
+
import { useReducer } from "use-typed-reducer";
|
|
5
|
+
import { useTweaks } from "../../hooks/use-tweaks";
|
|
6
|
+
import { InnerTable } from "./inner-table";
|
|
7
|
+
import { Metadata } from "./metadata";
|
|
8
|
+
import { createOptionCols } from "./table-lib";
|
|
9
|
+
import { TableProvider } from "./table.context";
|
|
10
|
+
const dispatcherFun = (prev, setter) => typeof setter === "function" ? setter(prev) : setter;
|
|
11
|
+
const compareAndExec = (prev, state, exec) => (prev === state ? undefined : exec?.(state));
|
|
12
|
+
export const Table = (props) => {
|
|
13
|
+
const tweaks = useTweaks();
|
|
14
|
+
const contextState = useMemo(() => ({
|
|
15
|
+
sticky: props.sticky === undefined ? (tweaks.table.sticky ?? undefined) : (props.sticky ?? undefined),
|
|
16
|
+
}), [props.sticky, tweaks.table.sticky]);
|
|
17
|
+
const operations = props.operations ?? tweaks.table.operations ?? true;
|
|
18
|
+
const optionCols = useMemo(() => createOptionCols(props.cols), [props.cols]);
|
|
19
|
+
const [state, dispatch] = useReducer({
|
|
20
|
+
cols: props.cols,
|
|
21
|
+
sorters: (props.sorters ?? []),
|
|
22
|
+
groups: (props.groups ?? []),
|
|
23
|
+
filters: (props.filters ?? []),
|
|
24
|
+
}, (get) => {
|
|
25
|
+
const create = (key) => (arg) => {
|
|
26
|
+
const state = get.state();
|
|
27
|
+
return { ...state, [key]: dispatcherFun(state[key], arg) };
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
cols: create("cols"),
|
|
31
|
+
sorters: create("sorters"),
|
|
32
|
+
groups: create("groups"),
|
|
33
|
+
filters: create("filters"),
|
|
34
|
+
};
|
|
35
|
+
}, {
|
|
36
|
+
postMiddleware: [
|
|
37
|
+
(state, prev) => {
|
|
38
|
+
props.set?.(state);
|
|
39
|
+
compareAndExec(prev?.filters ?? [], state.filters ?? [], props.setFilters);
|
|
40
|
+
compareAndExec(prev?.sorters ?? [], state.sorters ?? [], props.setSorters);
|
|
41
|
+
compareAndExec(prev?.groups ?? [], state.groups ?? [], props.setGroups);
|
|
42
|
+
compareAndExec(prev?.cols ?? [], state.cols ?? [], props.setCols);
|
|
43
|
+
return state;
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
dispatch.cols(props.cols);
|
|
49
|
+
}, [props.cols]);
|
|
50
|
+
return (<TableProvider value={contextState}>
|
|
51
|
+
<AnimatePresence propagate initial={false}>
|
|
52
|
+
{operations ? (<Metadata cols={state.cols} rows={props.rows} options={optionCols} groups={state.groups} filters={state.filters} setCols={dispatch.cols} sorters={state.sorters} setGroups={dispatch.groups} setFilters={dispatch.filters} setSorters={dispatch.sorters} pagination={props.pagination ?? null} inlineFilter={props.inlineFilter ?? true} inlineSorter={props.inlineSorter ?? true}/>) : null}
|
|
53
|
+
{state.groups.length === 0 ? (<InnerTable {...props} index={0} cols={state.cols} options={optionCols} groups={state.groups} filters={state.filters} optionCols={optionCols} setCols={dispatch.cols} sorters={state.sorters} setGroups={dispatch.groups} setFilters={dispatch.filters} setSorters={dispatch.sorters} onScrollEnd={props.onScrollEnd} pagination={props.pagination ?? null} inlineFilter={props.inlineFilter ?? true} inlineSorter={props.inlineSorter ?? true}/>) : (<div className="flex flex-wrap gap-4">
|
|
54
|
+
{state.groups.map((group, index) => (<div className="min-w-full" key={`group-${group.groupId}`}>
|
|
55
|
+
<InnerTable {...props} group={group} index={index} cols={state.cols} pagination={null} rows={group.rows} options={optionCols} groups={state.groups} filters={state.filters} optionCols={optionCols} setCols={dispatch.cols} sorters={state.sorters} setGroups={dispatch.groups} setFilters={dispatch.filters} setSorters={dispatch.sorters} onScrollEnd={props.onScrollEnd} inlineFilter={props.inlineFilter ?? true} inlineSorter={props.inlineSorter ?? true}/>
|
|
56
|
+
</div>))}
|
|
57
|
+
</div>)}
|
|
58
|
+
</AnimatePresence>
|
|
59
|
+
</TableProvider>);
|
|
60
|
+
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import React, { ComponentProps, HTMLAttributes } from "react";
|
|
2
|
-
import { Any } from "../../types";
|
|
3
2
|
import { OptionProps } from "../form/select";
|
|
4
3
|
import { FilterConfig } from "./filter";
|
|
5
4
|
import { GroupItem } from "./group";
|
|
6
5
|
import { Sorter } from "./sort";
|
|
7
6
|
import { CellAsideElement, Col, TableOperationProps } from "./table-lib";
|
|
8
|
-
export type InnerTableProps<T extends
|
|
7
|
+
export type InnerTableProps<T extends Record<string, unknown>> = HTMLAttributes<HTMLTableElement> & TableOperationProps<T> & {
|
|
9
8
|
rows: T[];
|
|
10
9
|
index: number;
|
|
11
10
|
cols: Col<T>[];
|
|
@@ -25,5 +24,5 @@ export type InnerTableProps<T extends Any> = HTMLAttributes<HTMLTableElement> &
|
|
|
25
24
|
getRowProps?: (_: T) => ComponentProps<"tr">;
|
|
26
25
|
setGroups: React.Dispatch<React.SetStateAction<GroupItem<T>[]>>;
|
|
27
26
|
};
|
|
28
|
-
export declare const InnerTable: <T extends
|
|
27
|
+
export declare const InnerTable: <T extends Record<string, unknown>>({ cols, filters, setCols, sorters, setFilters, setSorters, onScrollEnd, getScrollRef, pagination, useControl, ...props }: InnerTableProps<T>) => React.JSX.Element;
|
|
29
28
|
//# sourceMappingURL=inner-table.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inner-table.d.ts","sourceRoot":"","sources":["../../../src/components/table/inner-table.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"inner-table.d.ts","sourceRoot":"","sources":["../../../src/components/table/inner-table.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,cAAc,EAA2B,cAAc,EAAwC,MAAM,OAAO,CAAC;AAM7H,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,EAAa,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAczE,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,cAAc,CAAC,gBAAgB,CAAC,GAC7F,mBAAmB,CAAC,CAAC,CAAC,GAAG;IACrB,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,WAAW,GAAG,SAAS,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,cAAc,CAAC,IAAI,CAAC,CAAC;IAC7C,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACnE,CAAC;AAmFN,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,0HAY3D,eAAe,CAAC,CAAC,CAAC,sBA0EpB,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import Linq from "linq-arrays";
|
|
2
|
+
import { AnimatePresence } from "motion/react";
|
|
3
|
+
import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { TableVirtuoso } from "react-virtuoso";
|
|
5
|
+
import { Is } from "sidekicker";
|
|
6
|
+
import { useStableRef } from "../../hooks/use-stable-ref";
|
|
7
|
+
import { Empty } from "../display/empty";
|
|
8
|
+
import { SkeletonCell } from "../display/skeleton";
|
|
9
|
+
import { Pagination } from "./pagination";
|
|
10
|
+
import { Row } from "./row";
|
|
11
|
+
import { multiSort } from "./sort";
|
|
12
|
+
import { useTable } from "./table.context";
|
|
13
|
+
import { TableHeader } from "./thead";
|
|
14
|
+
const TableBody = React.forwardRef(({ context: _context, className = "", ...props }, ref) => (<tbody {...props} role="rowgroup" className={`divide-y divide-table-border ${className}`} ref={ref}>
|
|
15
|
+
<AnimatePresence>{props.children}</AnimatePresence>
|
|
16
|
+
</tbody>));
|
|
17
|
+
const VirtualTable = React.forwardRef(({ context: _context, className = "", ...props }, ref) => (<table {...props} role="table" ref={ref} style={{ ...props.style, "--table-cell-padding": "0.75rem" }} className={`table w-full table-fixed border-separate border-spacing-0 text-left ${className ?? ""}`}/>));
|
|
18
|
+
const Thead = React.forwardRef(({ context: _context, ...props }, ref) => {
|
|
19
|
+
const ctx = useTable();
|
|
20
|
+
const style = {
|
|
21
|
+
...props.style,
|
|
22
|
+
top: Is.number(ctx.sticky) ? `${ctx.sticky}px` : undefined,
|
|
23
|
+
};
|
|
24
|
+
return <thead {...props} ref={ref} style={style} role="rowgroup" className="group:sticky top-0 hidden bg-transparent md:table-header-group"/>;
|
|
25
|
+
});
|
|
26
|
+
const TRow = React.forwardRef(({ context, item, className, ...props }, ref) => {
|
|
27
|
+
const contextProps = item ? context?.getRowProps?.(item) : undefined;
|
|
28
|
+
const innerProps = { ...props, ...contextProps };
|
|
29
|
+
return (<tr {...innerProps} role="row" ref={ref} className={`group-table-row flex h-fit flex-col flex-wrap justify-center gap-1 pb-4 md:table-row ${[className, contextProps?.className].filter(Boolean).join(" ")}`}/>);
|
|
30
|
+
});
|
|
31
|
+
const TFoot = React.forwardRef(({ context, ...props }, ref) => {
|
|
32
|
+
if (context?.loadingMore) {
|
|
33
|
+
return (<tfoot {...props} ref={ref} className="bg-card-background">
|
|
34
|
+
<tr role="row" className="bg-card-background">
|
|
35
|
+
<td colSpan={999} className="h-14 bg-card-background px-2">
|
|
36
|
+
<span className="block h-2 w-full animate-pulse rounded bg-foreground opacity-60"/>
|
|
37
|
+
</td>
|
|
38
|
+
</tr>
|
|
39
|
+
</tfoot>);
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
});
|
|
43
|
+
const components = {
|
|
44
|
+
TableRow: TRow,
|
|
45
|
+
TableFoot: TFoot,
|
|
46
|
+
TableHead: Thead,
|
|
47
|
+
Table: VirtualTable,
|
|
48
|
+
TableBody: TableBody,
|
|
49
|
+
};
|
|
50
|
+
const loadingArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
51
|
+
const EmptyContent = (props) => (<div className="flex h-48 w-full items-center justify-center px-2">{props.loading ? SkeletonCell : <Empty />}</div>);
|
|
52
|
+
const EmptyCell = () => <Fragment />;
|
|
53
|
+
const emptyRows = [];
|
|
54
|
+
export const InnerTable = ({ cols, filters, setCols, sorters, setFilters, setSorters, onScrollEnd, getScrollRef, pagination = null, useControl = false, ...props }) => {
|
|
55
|
+
const ref = useRef(null);
|
|
56
|
+
const [, setShowLoadingFooter] = useState(false);
|
|
57
|
+
const onScrollEndRef = useStableRef(onScrollEnd);
|
|
58
|
+
const loadingMoreRef = useStableRef(props.loadingMore);
|
|
59
|
+
const rows = useMemo(() => {
|
|
60
|
+
if (props.loading)
|
|
61
|
+
return loadingArray;
|
|
62
|
+
if (useControl)
|
|
63
|
+
return props.rows;
|
|
64
|
+
const linq = new Linq(props.rows);
|
|
65
|
+
if (filters.length > 0) {
|
|
66
|
+
filters.forEach((x) => x.value === "" || Number.isNaN(x.value) ? undefined : linq.Where(x.name, x.operation.symbol, x.value));
|
|
67
|
+
}
|
|
68
|
+
if (sorters.length === 0)
|
|
69
|
+
return linq.Select();
|
|
70
|
+
return multiSort(linq.Select(), sorters);
|
|
71
|
+
}, [props.loading, props.rows, useControl, filters, sorters]);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (ref.current === null)
|
|
74
|
+
return () => { };
|
|
75
|
+
const div = ref.current;
|
|
76
|
+
const observer = new IntersectionObserver((entries) => {
|
|
77
|
+
const endOfPage = entries[entries.length - 1];
|
|
78
|
+
const condition = endOfPage.isIntersecting && loadingMoreRef.current;
|
|
79
|
+
if (condition) {
|
|
80
|
+
onScrollEndRef.current?.();
|
|
81
|
+
return void setShowLoadingFooter(true);
|
|
82
|
+
}
|
|
83
|
+
return setShowLoadingFooter(false);
|
|
84
|
+
});
|
|
85
|
+
observer.observe(div);
|
|
86
|
+
return () => observer.disconnect();
|
|
87
|
+
}, [loadingMoreRef, onScrollEndRef]);
|
|
88
|
+
const empty = rows.length === 0;
|
|
89
|
+
const context = {
|
|
90
|
+
cols: cols,
|
|
91
|
+
Aside: props.Aside,
|
|
92
|
+
loading: props.loading,
|
|
93
|
+
getRowProps: props.getRowProps,
|
|
94
|
+
loadingMore: props.loadingMore,
|
|
95
|
+
};
|
|
96
|
+
return (<div className="group relative flex w-full flex-col whitespace-nowrap rounded-lg bg-table-background">
|
|
97
|
+
<TableVirtuoso components={components} context={context} totalCount={rows.length} itemContent={empty ? EmptyCell : Row} data={empty ? emptyRows : rows} useWindowScroll={getScrollRef ? false : true} customScrollParent={getScrollRef ? getScrollRef() : undefined} fixedHeaderContent={() => (<TableHeader headers={cols} filters={filters} setCols={setCols} sorters={sorters} setFilters={setFilters} setSorters={setSorters} loading={!!props.loading} inlineFilter={props.inlineFilter} inlineSorter={props.inlineSorter}/>)}/>
|
|
98
|
+
{empty ? <EmptyContent loading={props.loading}/> : null}
|
|
99
|
+
<div aria-hidden="true" ref={ref} className="h-0.5 w-full"/>
|
|
100
|
+
{pagination !== null ? <Pagination {...pagination}/> : null}
|
|
101
|
+
</div>);
|
|
102
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Filter } from "./filter";
|
|
2
|
+
import { Group } from "./group";
|
|
3
|
+
import { Sort } from "./sort";
|
|
4
|
+
import { valueFromType } from "./table-lib";
|
|
5
|
+
export const Metadata = (props) => (<header className="mb-1 min-w-full">
|
|
6
|
+
<div className="flex min-w-full flex-wrap items-center justify-between gap-x-4 gap-y-1">
|
|
7
|
+
<div className="items-centeend flex w-fit gap-4 whitespace-nowrap py-2">
|
|
8
|
+
<span>
|
|
9
|
+
<Filter cols={props.cols} options={props.options} filters={props.filters} set={props.setFilters}/>
|
|
10
|
+
</span>
|
|
11
|
+
<span>
|
|
12
|
+
<Sort options={props.options} cols={props.cols} sorters={props.sorters} set={props.setSorters}/>
|
|
13
|
+
</span>
|
|
14
|
+
<span>
|
|
15
|
+
<Group rows={props.rows} groups={props.groups} setGroups={props.setGroups} options={props.options} cols={props.cols}/>
|
|
16
|
+
</span>
|
|
17
|
+
</div>
|
|
18
|
+
<ul className="flex w-full flex-1 flex-grow flex-row flex-wrap items-center gap-4 md:justify-end">
|
|
19
|
+
{props.filters.map((x) => (<li key={`filter-table-${x.id}`} className="flex items-center gap-1 rounded-xl border border-card-border px-4 py-0.5">
|
|
20
|
+
<span>
|
|
21
|
+
<span className="mr-2 inline-block aspect-square size-3 rounded-full bg-primary" aria-hidden="true"/>
|
|
22
|
+
{x.label} {x.operation.label.toLowerCase()}:
|
|
23
|
+
</span>
|
|
24
|
+
<div className="relative w-min min-w-[1ch]">
|
|
25
|
+
<span aria-hidden="true" className="invisible whitespace-pre p-0">
|
|
26
|
+
{x.value || " "}
|
|
27
|
+
</span>
|
|
28
|
+
<input type={x.type} value={x.value} className="absolute left-0 top-0 m-0 inline-block w-full bg-transparent p-0 placeholder-primary/70 outline-none [appearance:textfield] after:empty:text-primary/70 [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none" onChange={(e) => {
|
|
29
|
+
const value = valueFromType(e.target);
|
|
30
|
+
props.setFilters((prev) => prev.map((item) => (x.id === item.id ? { ...item, value } : item)));
|
|
31
|
+
}}/>
|
|
32
|
+
</div>
|
|
33
|
+
</li>))}
|
|
34
|
+
</ul>
|
|
35
|
+
</div>
|
|
36
|
+
</header>);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from "react";
|
|
1
2
|
import { TablePagination } from "./table-lib";
|
|
2
|
-
export declare
|
|
3
|
+
export declare function createPaginationItems(current: number, max: number): (string | number)[];
|
|
4
|
+
export declare const Pagination: (pagination: TablePagination) => React.JSX.Element;
|
|
3
5
|
//# sourceMappingURL=pagination.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/components/table/pagination.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../../src/components/table/pagination.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,uBAmBjE;AAED,eAAO,MAAM,UAAU,GAAI,YAAY,eAAe,sBA4ErD,CAAC"}
|