@rebasepro/ui 0.24.0 → 0.24.1-canary.gcb918fc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Alert.d.ts +6 -0
- package/dist/components/Alert.es.js +82 -0
- package/dist/components/Alert.es.js.map +1 -0
- package/dist/components/Autocomplete.es.js +53 -0
- package/dist/components/Autocomplete.es.js.map +1 -0
- package/dist/components/Avatar.d.ts +3 -1
- package/dist/components/Avatar.es.js +37 -0
- package/dist/components/Avatar.es.js.map +1 -0
- package/dist/components/Badge.es.js +27 -0
- package/dist/components/Badge.es.js.map +1 -0
- package/dist/components/BooleanSwitch.es.js +75 -0
- package/dist/components/BooleanSwitch.es.js.map +1 -0
- package/dist/components/BooleanSwitchWithLabel.es.js +55 -0
- package/dist/components/BooleanSwitchWithLabel.es.js.map +1 -0
- package/dist/components/Button.es.js +58 -0
- package/dist/components/Button.es.js.map +1 -0
- package/dist/components/Card.es.js +26 -0
- package/dist/components/Card.es.js.map +1 -0
- package/dist/components/CenteredView.es.js +23 -0
- package/dist/components/CenteredView.es.js.map +1 -0
- package/dist/components/Checkbox.es.js +62 -0
- package/dist/components/Checkbox.es.js.map +1 -0
- package/dist/components/Chip.es.js +72 -0
- package/dist/components/Chip.es.js.map +1 -0
- package/dist/components/CircularProgress.es.js +28 -0
- package/dist/components/CircularProgress.es.js.map +1 -0
- package/dist/components/CircularProgressCenter.es.js +30 -0
- package/dist/components/CircularProgressCenter.es.js.map +1 -0
- package/dist/components/Collapse.es.js +57 -0
- package/dist/components/Collapse.es.js.map +1 -0
- package/dist/components/ColorPicker.es.js +89 -0
- package/dist/components/ColorPicker.es.js.map +1 -0
- package/dist/components/Container.es.js +31 -0
- package/dist/components/Container.es.js.map +1 -0
- package/dist/components/DateTimeField.d.ts +11 -1
- package/dist/components/DateTimeField.es.js +253 -0
- package/dist/components/DateTimeField.es.js.map +1 -0
- package/dist/components/DebouncedTextField.es.js +93 -0
- package/dist/components/DebouncedTextField.es.js.map +1 -0
- package/dist/components/Dialog.es.js +98 -0
- package/dist/components/Dialog.es.js.map +1 -0
- package/dist/components/DialogActions.es.js +15 -0
- package/dist/components/DialogActions.es.js.map +1 -0
- package/dist/components/DialogContent.es.js +18 -0
- package/dist/components/DialogContent.es.js.map +1 -0
- package/dist/components/DialogTitle.es.js +26 -0
- package/dist/components/DialogTitle.es.js.map +1 -0
- package/dist/components/ErrorBoundary.es.js +158 -0
- package/dist/components/ErrorBoundary.es.js.map +1 -0
- package/dist/components/ExpandablePanel.es.js +91 -0
- package/dist/components/ExpandablePanel.es.js.map +1 -0
- package/dist/components/FileUpload.es.js +58 -0
- package/dist/components/FileUpload.es.js.map +1 -0
- package/dist/components/FilterChip.es.js +31 -0
- package/dist/components/FilterChip.es.js.map +1 -0
- package/dist/components/IconButton.es.js +52 -0
- package/dist/components/IconButton.es.js.map +1 -0
- package/dist/components/InfoLabel.es.js +18 -0
- package/dist/components/InfoLabel.es.js.map +1 -0
- package/dist/components/InputLabel.es.js +27 -0
- package/dist/components/InputLabel.es.js.map +1 -0
- package/dist/components/Label.es.js +18 -0
- package/dist/components/Label.es.js.map +1 -0
- package/dist/components/LoadingButton.es.js +21 -0
- package/dist/components/LoadingButton.es.js.map +1 -0
- package/dist/components/Markdown.es.js +88 -0
- package/dist/components/Markdown.es.js.map +1 -0
- package/dist/components/Menu.es.js +46 -0
- package/dist/components/Menu.es.js.map +1 -0
- package/dist/components/Menubar.es.js +142 -0
- package/dist/components/Menubar.es.js.map +1 -0
- package/dist/components/MultiSelect.d.ts +1 -7
- package/dist/components/MultiSelect.es.js +271 -0
- package/dist/components/MultiSelect.es.js.map +1 -0
- package/dist/components/Paper.es.js +16 -0
- package/dist/components/Paper.es.js.map +1 -0
- package/dist/components/Popover.es.js +113 -0
- package/dist/components/Popover.es.js.map +1 -0
- package/dist/components/RadioGroup.es.js +30 -0
- package/dist/components/RadioGroup.es.js.map +1 -0
- package/dist/components/ResizablePanels.d.ts +3 -1
- package/dist/components/ResizablePanels.es.js +137 -0
- package/dist/components/ResizablePanels.es.js.map +1 -0
- package/dist/components/SearchBar.es.js +82 -0
- package/dist/components/SearchBar.es.js.map +1 -0
- package/dist/components/Select.d.ts +8 -1
- package/dist/components/Select.es.js +300 -0
- package/dist/components/Select.es.js.map +1 -0
- package/dist/components/Separator.es.js +20 -0
- package/dist/components/Separator.es.js.map +1 -0
- package/dist/components/Sheet.es.js +73 -0
- package/dist/components/Sheet.es.js.map +1 -0
- package/dist/components/Skeleton.es.js +33 -0
- package/dist/components/Skeleton.es.js.map +1 -0
- package/dist/components/Slider.es.js +56 -0
- package/dist/components/Slider.es.js.map +1 -0
- package/dist/components/Table.d.ts +18 -3
- package/dist/components/Table.es.js +69 -0
- package/dist/components/Table.es.js.map +1 -0
- package/dist/components/Tabs.es.js +101 -0
- package/dist/components/Tabs.es.js.map +1 -0
- package/dist/components/TextField.d.ts +0 -8
- package/dist/components/TextField.es.js +111 -0
- package/dist/components/TextField.es.js.map +1 -0
- package/dist/components/TextareaAutosize.es.js +187 -0
- package/dist/components/TextareaAutosize.es.js.map +1 -0
- package/dist/components/ToggleButtonGroup.d.ts +6 -1
- package/dist/components/ToggleButtonGroup.es.js +35 -0
- package/dist/components/ToggleButtonGroup.es.js.map +1 -0
- package/dist/components/Tooltip.d.ts +24 -0
- package/dist/components/Tooltip.es.js +165 -0
- package/dist/components/Tooltip.es.js.map +1 -0
- package/dist/components/Typography.es.js +85 -0
- package/dist/components/Typography.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTable.es.js +516 -0
- package/dist/components/VirtualTable/VirtualTable.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableCell.d.ts +1 -1
- package/dist/components/VirtualTable/VirtualTableCell.es.js +40 -0
- package/dist/components/VirtualTable/VirtualTableCell.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableHeader.es.js +209 -0
- package/dist/components/VirtualTable/VirtualTableHeader.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js +166 -0
- package/dist/components/VirtualTable/VirtualTableHeaderRow.es.js.map +1 -0
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +13 -2
- package/dist/components/VirtualTable/VirtualTableRow.d.ts +6 -7
- package/dist/components/VirtualTable/VirtualTableRow.es.js +41 -0
- package/dist/components/VirtualTable/VirtualTableRow.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableDateField.d.ts +2 -1
- package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js +71 -0
- package/dist/components/VirtualTable/fields/VirtualTableDateField.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableInput.es.js +73 -0
- package/dist/components/VirtualTable/fields/VirtualTableInput.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js +47 -0
- package/dist/components/VirtualTable/fields/VirtualTableNumberInput.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js +83 -0
- package/dist/components/VirtualTable/fields/VirtualTableSelect.es.js.map +1 -0
- package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js +22 -0
- package/dist/components/VirtualTable/fields/VirtualTableSwitch.es.js.map +1 -0
- package/dist/components/VirtualTable/selection/SelectionContext.es.js +23 -0
- package/dist/components/VirtualTable/selection/SelectionContext.es.js.map +1 -0
- package/dist/components/VirtualTable/selection/SelectionStore.es.js +43 -0
- package/dist/components/VirtualTable/selection/SelectionStore.es.js.map +1 -0
- package/dist/components/VirtualTable/types.d.ts +1 -1
- package/dist/components/common/SelectInputLabel.es.js +28 -0
- package/dist/components/common/SelectInputLabel.es.js.map +1 -0
- package/dist/components/index.es.js +64 -0
- package/dist/hooks/PortalContainerContext.es.js +40 -0
- package/dist/hooks/PortalContainerContext.es.js.map +1 -0
- package/dist/hooks/UiLabelsContext.d.ts +63 -0
- package/dist/hooks/UiLabelsContext.es.js +59 -0
- package/dist/hooks/UiLabelsContext.es.js.map +1 -0
- package/dist/hooks/useDebounceCallback.es.js +15 -0
- package/dist/hooks/useDebounceCallback.es.js.map +1 -0
- package/dist/hooks/useDebounceValue.es.js +19 -0
- package/dist/hooks/useDebounceValue.es.js.map +1 -0
- package/dist/hooks/useDebouncedCallback.es.js +36 -0
- package/dist/hooks/useDebouncedCallback.es.js.map +1 -0
- package/dist/hooks/useInjectStyles.es.js +27 -0
- package/dist/hooks/useInjectStyles.es.js.map +1 -0
- package/dist/hooks/useIsDarkMode.es.js +61 -0
- package/dist/hooks/useIsDarkMode.es.js.map +1 -0
- package/dist/hooks/useModalFocus.es.js +81 -0
- package/dist/hooks/useModalFocus.es.js.map +1 -0
- package/dist/hooks/useOutsideAlerter.es.js +41 -0
- package/dist/hooks/useOutsideAlerter.es.js.map +1 -0
- package/dist/hooks/useRestoreInterruptedFocus.es.js +61 -0
- package/dist/hooks/useRestoreInterruptedFocus.es.js.map +1 -0
- package/dist/icons/GitHubIcon.es.js +23 -0
- package/dist/icons/GitHubIcon.es.js.map +1 -0
- package/dist/icons/HandleIcon.es.js +60 -0
- package/dist/icons/HandleIcon.es.js.map +1 -0
- package/dist/icons/Icon.es.js +25 -0
- package/dist/icons/Icon.es.js.map +1 -0
- package/dist/icons/LucideIconByName.d.ts +12 -9
- package/dist/icons/LucideIconByName.es.js +83 -0
- package/dist/icons/LucideIconByName.es.js.map +1 -0
- package/dist/icons/cool_icon_keys.es.js +55 -0
- package/dist/icons/cool_icon_keys.es.js.map +1 -0
- package/dist/icons/icon_keys.es.js +1941 -0
- package/dist/icons/icon_keys.es.js.map +1 -0
- package/dist/icons/index.d.ts +4 -3
- package/dist/icons/index.es.js +8 -0
- package/dist/icons/lucide_icon_map.d.ts +12 -0
- package/dist/icons/lucide_icon_map.es.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.es.js +87 -9563
- package/dist/styles.es.js +158 -0
- package/dist/styles.es.js.map +1 -0
- package/dist/util/chip_colors.es.js +290 -0
- package/dist/util/chip_colors.es.js.map +1 -0
- package/dist/util/cls.es.js +10 -0
- package/dist/util/cls.es.js.map +1 -0
- package/dist/util/debounce.es.js +22 -0
- package/dist/util/debounce.es.js.map +1 -0
- package/dist/util/hash.es.js +16 -0
- package/dist/util/hash.es.js.map +1 -0
- package/dist/util/keys.es.js +37 -0
- package/dist/util/keys.es.js.map +1 -0
- package/dist/util/lazy_chunk.es.js +96 -0
- package/dist/util/lazy_chunk.es.js.map +1 -0
- package/dist/views/CardView.d.ts +26 -2
- package/dist/views/CardView.es.js +435 -0
- package/dist/views/CardView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionCardView.es.js +179 -0
- package/dist/views/CollectionView/CollectionCardView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionKanbanView.es.js +172 -0
- package/dist/views/CollectionView/CollectionKanbanView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionListView.es.js +156 -0
- package/dist/views/CollectionView/CollectionListView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionTableView.es.js +149 -0
- package/dist/views/CollectionView/CollectionTableView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionView.es.js +197 -0
- package/dist/views/CollectionView/CollectionView.es.js.map +1 -0
- package/dist/views/CollectionView/CollectionViewToolbar.es.js +155 -0
- package/dist/views/CollectionView/CollectionViewToolbar.es.js.map +1 -0
- package/dist/views/CollectionView/DefaultCellRenderer.es.js +302 -0
- package/dist/views/CollectionView/DefaultCellRenderer.es.js.map +1 -0
- package/dist/views/CollectionView/resolveTitleProperty.es.js +56 -0
- package/dist/views/CollectionView/resolveTitleProperty.es.js.map +1 -0
- package/dist/views/CollectionView/utils.es.js +19 -0
- package/dist/views/CollectionView/utils.es.js.map +1 -0
- package/dist/views/Kanban/Board.es.js +237 -0
- package/dist/views/Kanban/Board.es.js.map +1 -0
- package/dist/views/Kanban/BoardColumn.es.js +81 -0
- package/dist/views/Kanban/BoardColumn.es.js.map +1 -0
- package/dist/views/Kanban/BoardColumnTitle.es.js +27 -0
- package/dist/views/Kanban/BoardColumnTitle.es.js.map +1 -0
- package/dist/views/Kanban/BoardSortableList.es.js +98 -0
- package/dist/views/Kanban/BoardSortableList.es.js.map +1 -0
- package/dist/views/Kanban/placement.es.js +27 -0
- package/dist/views/Kanban/placement.es.js.map +1 -0
- package/dist/views/KanbanView.es.js +11 -0
- package/dist/views/KanbanView.es.js.map +1 -0
- package/dist/views/ListView.d.ts +15 -2
- package/dist/views/ListView.es.js +245 -0
- package/dist/views/ListView.es.js.map +1 -0
- package/dist/views/useScrollParentRestoration.d.ts +8 -1
- package/dist/views/useScrollParentRestoration.es.js +75 -0
- package/dist/views/useScrollParentRestoration.es.js.map +1 -0
- package/package.json +2 -2
- package/dist/index.es.js.map +0 -1
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "../../styles.es.js";
|
|
3
|
+
import { cls } from "../../util/cls.es.js";
|
|
4
|
+
import { Card } from "../../components/Card.es.js";
|
|
5
|
+
import { Typography } from "../../components/Typography.es.js";
|
|
6
|
+
import { Checkbox } from "../../components/Checkbox.es.js";
|
|
7
|
+
import { useUiLabels } from "../../hooks/UiLabelsContext.es.js";
|
|
8
|
+
import { CardView } from "../CardView.es.js";
|
|
9
|
+
import { DefaultCellRenderer } from "./DefaultCellRenderer.es.js";
|
|
10
|
+
import { getValueInPath } from "./utils.es.js";
|
|
11
|
+
import { resolveTitlePropertyKey } from "./resolveTitleProperty.es.js";
|
|
12
|
+
import { useCallback, useMemo } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
//#region src/views/CollectionView/CollectionCardView.tsx
|
|
15
|
+
/**
|
|
16
|
+
* Headless card grid view that wraps {@link CardView}.
|
|
17
|
+
*
|
|
18
|
+
* Renders each item as a card with an optional image, title,
|
|
19
|
+
* preview properties, and selection support.
|
|
20
|
+
*
|
|
21
|
+
* @group Collection View
|
|
22
|
+
*/
|
|
23
|
+
function CollectionCardView({ dataController, properties, propertiesOrder, idProperty = "id", titleProperty, onRowClick, cellRenderer, size = "m", selectionEnabled, selectionController, highlightedItems, emptyComponent, className }) {
|
|
24
|
+
const labels = useUiLabels();
|
|
25
|
+
const order = useMemo(() => propertiesOrder ?? Object.keys(properties), [propertiesOrder, properties]);
|
|
26
|
+
const titlePropertyKey = useMemo(() => resolveTitlePropertyKey(order, properties, idProperty, titleProperty), [
|
|
27
|
+
titleProperty,
|
|
28
|
+
order,
|
|
29
|
+
properties,
|
|
30
|
+
idProperty
|
|
31
|
+
]);
|
|
32
|
+
const imageProperty = useMemo(() => {
|
|
33
|
+
for (const key of order) {
|
|
34
|
+
const prop = properties[key];
|
|
35
|
+
if (prop && prop.type === "string" && prop.storage) return key;
|
|
36
|
+
}
|
|
37
|
+
}, [order, properties]);
|
|
38
|
+
const previewProperties = useMemo(() => {
|
|
39
|
+
const result = [];
|
|
40
|
+
for (const key of order) {
|
|
41
|
+
if (key === titlePropertyKey || key === imageProperty) continue;
|
|
42
|
+
const prop = properties[key];
|
|
43
|
+
if (prop && !prop.hideFromCollection) {
|
|
44
|
+
result.push({
|
|
45
|
+
key,
|
|
46
|
+
property: prop
|
|
47
|
+
});
|
|
48
|
+
if (result.length >= 3) break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return result;
|
|
52
|
+
}, [
|
|
53
|
+
order,
|
|
54
|
+
properties,
|
|
55
|
+
titlePropertyKey,
|
|
56
|
+
imageProperty
|
|
57
|
+
]);
|
|
58
|
+
const selectedIds = useMemo(() => {
|
|
59
|
+
if (!selectionController?.selectedItems) return void 0;
|
|
60
|
+
const ids = /* @__PURE__ */ new Set();
|
|
61
|
+
for (const item of selectionController.selectedItems) {
|
|
62
|
+
const id = getValueInPath(item, idProperty);
|
|
63
|
+
if (id !== void 0 && id !== null) ids.add(id);
|
|
64
|
+
}
|
|
65
|
+
return ids;
|
|
66
|
+
}, [selectionController?.selectedItems, idProperty]);
|
|
67
|
+
const highlightedIds = useMemo(() => {
|
|
68
|
+
if (!highlightedItems) return void 0;
|
|
69
|
+
const ids = /* @__PURE__ */ new Set();
|
|
70
|
+
for (const item of highlightedItems) {
|
|
71
|
+
const id = getValueInPath(item, idProperty);
|
|
72
|
+
if (id !== void 0 && id !== null) ids.add(id);
|
|
73
|
+
}
|
|
74
|
+
return ids;
|
|
75
|
+
}, [highlightedItems, idProperty]);
|
|
76
|
+
const handleSelectionChange = useCallback((item, selected) => {
|
|
77
|
+
if (!selectionController) return;
|
|
78
|
+
selectionController.setSelectedItems((prev) => {
|
|
79
|
+
if (selected) return [...prev, item];
|
|
80
|
+
const itemId = getValueInPath(item, idProperty);
|
|
81
|
+
return prev.filter((existing) => {
|
|
82
|
+
return getValueInPath(existing, idProperty) !== itemId;
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}, [selectionController, idProperty]);
|
|
86
|
+
const renderCard = useCallback((item, extra) => {
|
|
87
|
+
const titleValue = getValueInPath(item, titlePropertyKey);
|
|
88
|
+
const imageValue = imageProperty ? getValueInPath(item, imageProperty) : void 0;
|
|
89
|
+
const hasImage = typeof imageValue === "string" && imageValue.length > 0;
|
|
90
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
91
|
+
onClick: (e) => {
|
|
92
|
+
if (e) extra.onClick(e);
|
|
93
|
+
},
|
|
94
|
+
className: cls("overflow-hidden relative", extra.selected && "bg-primary-bg/30 dark:bg-primary-bg/10 ring-1 ring-primary/75", extra.highlighted && !extra.selected && "ring-1 ring-primary/50"),
|
|
95
|
+
children: [
|
|
96
|
+
imageProperty && (hasImage ? /* @__PURE__ */ jsx("img", {
|
|
97
|
+
src: imageValue,
|
|
98
|
+
alt: "",
|
|
99
|
+
className: "w-full h-32 object-cover rounded-t-lg"
|
|
100
|
+
}) : /* @__PURE__ */ jsx("div", { className: cls("h-24 rounded-t-lg", "bg-surface-field border border-hairline") })),
|
|
101
|
+
selectionEnabled && /* @__PURE__ */ jsx("div", {
|
|
102
|
+
className: "absolute top-2 right-2",
|
|
103
|
+
onClick: (e) => e.stopPropagation(),
|
|
104
|
+
children: /* @__PURE__ */ jsx(Checkbox, {
|
|
105
|
+
"aria-label": labels.select,
|
|
106
|
+
checked: extra.selected,
|
|
107
|
+
onCheckedChange: extra.onSelectionChange,
|
|
108
|
+
size: "small"
|
|
109
|
+
})
|
|
110
|
+
}),
|
|
111
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
112
|
+
variant: "subtitle2",
|
|
113
|
+
noWrap: true,
|
|
114
|
+
className: "px-3 pt-2 pb-1",
|
|
115
|
+
children: String(titleValue ?? "")
|
|
116
|
+
}),
|
|
117
|
+
previewProperties.map(({ key, property }) => {
|
|
118
|
+
const value = getValueInPath(item, key);
|
|
119
|
+
if (cellRenderer) {
|
|
120
|
+
const custom = cellRenderer({
|
|
121
|
+
row: item,
|
|
122
|
+
propertyKey: key,
|
|
123
|
+
property,
|
|
124
|
+
value,
|
|
125
|
+
size: "small"
|
|
126
|
+
});
|
|
127
|
+
if (custom !== void 0) return /* @__PURE__ */ jsx("div", {
|
|
128
|
+
className: "px-3 py-0.5",
|
|
129
|
+
children: custom
|
|
130
|
+
}, key);
|
|
131
|
+
}
|
|
132
|
+
return /* @__PURE__ */ jsx("div", {
|
|
133
|
+
className: "px-3 py-0.5",
|
|
134
|
+
children: /* @__PURE__ */ jsx(DefaultCellRenderer, {
|
|
135
|
+
row: item,
|
|
136
|
+
propertyKey: key,
|
|
137
|
+
property,
|
|
138
|
+
value,
|
|
139
|
+
size: "small"
|
|
140
|
+
})
|
|
141
|
+
}, key);
|
|
142
|
+
}),
|
|
143
|
+
/* @__PURE__ */ jsx("div", { className: "pb-2" })
|
|
144
|
+
]
|
|
145
|
+
});
|
|
146
|
+
}, [
|
|
147
|
+
titlePropertyKey,
|
|
148
|
+
imageProperty,
|
|
149
|
+
previewProperties,
|
|
150
|
+
cellRenderer,
|
|
151
|
+
selectionEnabled,
|
|
152
|
+
labels
|
|
153
|
+
]);
|
|
154
|
+
return /* @__PURE__ */ jsx("div", {
|
|
155
|
+
className: cls("w-full h-full overflow-auto", className),
|
|
156
|
+
children: /* @__PURE__ */ jsx(CardView, {
|
|
157
|
+
data: dataController.data,
|
|
158
|
+
dataLoading: dataController.loading,
|
|
159
|
+
noMoreToLoad: dataController.noMoreToLoad,
|
|
160
|
+
dataLoadingError: dataController.error,
|
|
161
|
+
itemCount: dataController.itemCount,
|
|
162
|
+
setItemCount: dataController.setItemCount,
|
|
163
|
+
pageSize: dataController.pageSize,
|
|
164
|
+
paginationEnabled: dataController.paginationEnabled,
|
|
165
|
+
onItemClick: onRowClick,
|
|
166
|
+
selectedIds,
|
|
167
|
+
highlightedIds,
|
|
168
|
+
selectionEnabled: !!selectionEnabled,
|
|
169
|
+
onSelectionChange: handleSelectionChange,
|
|
170
|
+
size,
|
|
171
|
+
renderCard,
|
|
172
|
+
emptyComponent
|
|
173
|
+
})
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
//#endregion
|
|
177
|
+
export { CollectionCardView };
|
|
178
|
+
|
|
179
|
+
//# sourceMappingURL=CollectionCardView.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectionCardView.es.js","names":[],"sources":["../../../src/views/CollectionView/CollectionCardView.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useMemo } from \"react\";\nimport { CardView } from \"../CardView\";\nimport { Card } from \"../../components/Card\";\nimport { Typography } from \"../../components/Typography\";\nimport { Checkbox } from \"../../components/Checkbox\";\nimport { DefaultCellRenderer } from \"./DefaultCellRenderer\";\nimport { cls } from \"../../util\";\nimport { useUiLabels } from \"../../hooks/UiLabelsContext\";\nimport { cardSelectedMixin, fieldBackgroundMixin } from \"../../styles\";\nimport type {\n CollectionPropertyConfig,\n CollectionDataController,\n CellRendererOverride,\n CollectionViewSize,\n CollectionSelectionController,\n} from \"./CollectionViewTypes\";\nimport { resolveTitlePropertyKey } from \"./resolveTitleProperty\";\n\nimport { getValueInPath } from \"./utils\";\n\n/**\n * Props for the {@link CollectionCardView} component.\n * @group Collection View\n */\nexport type CollectionCardViewProps<T = Record<string, unknown>> = {\n dataController: CollectionDataController<T>;\n properties: Record<string, CollectionPropertyConfig>;\n propertiesOrder?: string[];\n idProperty?: string;\n titleProperty?: string;\n onRowClick?: (row: T) => void;\n cellRenderer?: CellRendererOverride<T>;\n size?: CollectionViewSize;\n selectionEnabled?: boolean;\n selectionController?: CollectionSelectionController<T>;\n highlightedItems?: T[];\n emptyComponent?: React.ReactNode;\n className?: string;\n};\n\n/**\n * Headless card grid view that wraps {@link CardView}.\n *\n * Renders each item as a card with an optional image, title,\n * preview properties, and selection support.\n *\n * @group Collection View\n */\nexport function CollectionCardView<T extends Record<string, unknown> = Record<string, unknown>>({\n dataController,\n properties,\n propertiesOrder,\n idProperty = \"id\",\n titleProperty,\n onRowClick,\n cellRenderer,\n size = \"m\",\n selectionEnabled,\n selectionController,\n highlightedItems,\n emptyComponent,\n className,\n}: CollectionCardViewProps<T>) {\n const labels = useUiLabels();\n const order = useMemo(\n () => propertiesOrder ?? Object.keys(properties),\n [propertiesOrder, properties]\n );\n\n // ── Resolve title property ───────────────────────────────────────\n const titlePropertyKey = useMemo(\n () => resolveTitlePropertyKey(order, properties, idProperty, titleProperty),\n [titleProperty, order, properties, idProperty]\n );\n\n // ── Resolve image property ───────────────────────────────────────\n const imageProperty = useMemo(() => {\n for (const key of order) {\n const prop = properties[key];\n if (prop && prop.type === \"string\" && prop.storage) {\n return key;\n }\n }\n return undefined;\n }, [order, properties]);\n\n // ── Preview properties (up to 3) ─────────────────────────────────\n const previewProperties = useMemo(() => {\n const result: { key: string; property: CollectionPropertyConfig }[] = [];\n for (const key of order) {\n if (key === titlePropertyKey || key === imageProperty) continue;\n const prop = properties[key];\n if (prop && !prop.hideFromCollection) {\n result.push({ key, property: prop });\n if (result.length >= 3) break;\n }\n }\n return result;\n }, [order, properties, titlePropertyKey, imageProperty]);\n\n // ── Selection IDs ────────────────────────────────────────────────\n const selectedIds = useMemo(() => {\n if (!selectionController?.selectedItems) return undefined;\n const ids = new Set<string | number>();\n for (const item of selectionController.selectedItems) {\n const id = getValueInPath(item as Record<string, unknown>, idProperty);\n if (id !== undefined && id !== null) {\n ids.add(id as string | number);\n }\n }\n return ids;\n }, [selectionController?.selectedItems, idProperty]);\n\n // ── Highlighted IDs ──────────────────────────────────────────────\n const highlightedIds = useMemo(() => {\n if (!highlightedItems) return undefined;\n const ids = new Set<string | number>();\n for (const item of highlightedItems) {\n const id = getValueInPath(item as Record<string, unknown>, idProperty);\n if (id !== undefined && id !== null) {\n ids.add(id as string | number);\n }\n }\n return ids;\n }, [highlightedItems, idProperty]);\n\n // ── Selection handler ────────────────────────────────────────────\n const handleSelectionChange = useCallback(\n (item: T, selected: boolean) => {\n if (!selectionController) return;\n selectionController.setSelectedItems((prev) => {\n if (selected) {\n return [...prev, item];\n }\n const itemId = getValueInPath(item as Record<string, unknown>, idProperty);\n return prev.filter((existing) => {\n const existingId = getValueInPath(existing as Record<string, unknown>, idProperty);\n return existingId !== itemId;\n });\n });\n },\n [selectionController, idProperty]\n );\n\n // ── Render card ──────────────────────────────────────────────────\n const renderCard = useCallback(\n (\n item: T,\n extra: {\n selected: boolean;\n highlighted: boolean;\n onSelectionChange: (selected: boolean) => void;\n onClick: (e: React.MouseEvent) => void;\n }\n ): React.ReactNode => {\n const titleValue = getValueInPath(item as Record<string, unknown>, titlePropertyKey);\n const imageValue = imageProperty\n ? getValueInPath(item as Record<string, unknown>, imageProperty)\n : undefined;\n const hasImage = typeof imageValue === \"string\" && imageValue.length > 0;\n\n return (\n <Card\n onClick={(e) => { if (e) extra.onClick(e); }}\n className={cls(\n \"overflow-hidden relative\",\n extra.selected && cardSelectedMixin,\n extra.highlighted && !extra.selected && \"ring-1 ring-primary/50\"\n )}\n >\n {/* Image */}\n {imageProperty && (\n hasImage ? (\n <img\n src={imageValue as string}\n alt=\"\"\n className=\"w-full h-32 object-cover rounded-t-lg\"\n />\n ) : (\n <div className={cls(\"h-24 rounded-t-lg\", fieldBackgroundMixin)} />\n )\n )}\n\n {/* Selection checkbox */}\n {selectionEnabled && (\n <div\n className=\"absolute top-2 right-2\"\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n aria-label={labels.select}\n checked={extra.selected}\n onCheckedChange={extra.onSelectionChange}\n size=\"small\"\n />\n </div>\n )}\n\n {/* Title */}\n <Typography variant=\"subtitle2\" noWrap className=\"px-3 pt-2 pb-1\">\n {String(titleValue ?? \"\")}\n </Typography>\n\n {/* Preview properties */}\n {previewProperties.map(({ key, property }) => {\n const value = getValueInPath(item as Record<string, unknown>, key);\n\n if (cellRenderer) {\n const custom = cellRenderer({\n row: item,\n propertyKey: key,\n property,\n value,\n size: \"small\",\n });\n if (custom !== undefined) {\n return (\n <div key={key} className=\"px-3 py-0.5\">\n {custom}\n </div>\n );\n }\n }\n\n return (\n <div key={key} className=\"px-3 py-0.5\">\n <DefaultCellRenderer\n row={item}\n propertyKey={key}\n property={property}\n value={value}\n size=\"small\"\n />\n </div>\n );\n })}\n\n <div className=\"pb-2\" />\n </Card>\n );\n },\n [titlePropertyKey, imageProperty, previewProperties, cellRenderer, selectionEnabled, labels]\n );\n\n return (\n <div className={cls(\"w-full h-full overflow-auto\", className)}>\n <CardView<T>\n data={dataController.data}\n dataLoading={dataController.loading}\n noMoreToLoad={dataController.noMoreToLoad}\n dataLoadingError={dataController.error}\n itemCount={dataController.itemCount}\n setItemCount={dataController.setItemCount}\n pageSize={dataController.pageSize}\n paginationEnabled={dataController.paginationEnabled}\n onItemClick={onRowClick}\n selectedIds={selectedIds}\n highlightedIds={highlightedIds}\n selectionEnabled={!!selectionEnabled}\n onSelectionChange={handleSelectionChange}\n size={size}\n renderCard={renderCard}\n emptyComponent={emptyComponent}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cardClickableMixin } from "../../styles.es.js";
|
|
3
|
+
import { cls } from "../../util/cls.es.js";
|
|
4
|
+
import { Card } from "../../components/Card.es.js";
|
|
5
|
+
import { Typography } from "../../components/Typography.es.js";
|
|
6
|
+
import { KanbanView } from "../KanbanView.es.js";
|
|
7
|
+
import { DefaultCellRenderer } from "./DefaultCellRenderer.es.js";
|
|
8
|
+
import { getValueInPath } from "./utils.es.js";
|
|
9
|
+
import { resolveTitlePropertyKey } from "./resolveTitleProperty.es.js";
|
|
10
|
+
import { useCallback, useMemo } from "react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
//#region src/views/CollectionView/CollectionKanbanView.tsx
|
|
13
|
+
function getEnumLabel(enumConfig, key) {
|
|
14
|
+
if (!enumConfig) return key;
|
|
15
|
+
if (enumConfig instanceof Map) {
|
|
16
|
+
const cfg = enumConfig.get(key);
|
|
17
|
+
if (!cfg) return key;
|
|
18
|
+
return typeof cfg === "string" ? cfg : cfg.label;
|
|
19
|
+
}
|
|
20
|
+
const cfg = enumConfig[key];
|
|
21
|
+
if (!cfg) return key;
|
|
22
|
+
return typeof cfg === "string" ? cfg : cfg.label;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Headless kanban view that wraps {@link KanbanView}.
|
|
26
|
+
*
|
|
27
|
+
* Groups data by a kanban property's enum values into columns.
|
|
28
|
+
* Each card displays a title and 1–2 preview properties.
|
|
29
|
+
*
|
|
30
|
+
* @group Collection View
|
|
31
|
+
*/
|
|
32
|
+
function CollectionKanbanView({ dataController, properties, propertiesOrder, idProperty = "id", titleProperty, kanbanProperty, onRowClick, cellRenderer, emptyComponent, onRowCreate, className }) {
|
|
33
|
+
const order = useMemo(() => propertiesOrder ?? Object.keys(properties), [propertiesOrder, properties]);
|
|
34
|
+
const columns = useMemo(() => {
|
|
35
|
+
const kanbanConfig = properties[kanbanProperty];
|
|
36
|
+
if (kanbanConfig?.enum) {
|
|
37
|
+
if (kanbanConfig.enum instanceof Map) return Array.from(kanbanConfig.enum.keys()).map(String);
|
|
38
|
+
return Object.keys(kanbanConfig.enum);
|
|
39
|
+
}
|
|
40
|
+
const seen = /* @__PURE__ */ new Set();
|
|
41
|
+
for (const row of dataController.data) {
|
|
42
|
+
const val = getValueInPath(row, kanbanProperty);
|
|
43
|
+
if (val !== null && val !== void 0) seen.add(String(val));
|
|
44
|
+
}
|
|
45
|
+
return Array.from(seen);
|
|
46
|
+
}, [
|
|
47
|
+
properties,
|
|
48
|
+
kanbanProperty,
|
|
49
|
+
dataController.data
|
|
50
|
+
]);
|
|
51
|
+
const columnLabels = useMemo(() => {
|
|
52
|
+
const kanbanConfig = properties[kanbanProperty];
|
|
53
|
+
const labels = {};
|
|
54
|
+
for (const col of columns) labels[col] = getEnumLabel(kanbanConfig?.enum, col);
|
|
55
|
+
return labels;
|
|
56
|
+
}, [
|
|
57
|
+
properties,
|
|
58
|
+
kanbanProperty,
|
|
59
|
+
columns
|
|
60
|
+
]);
|
|
61
|
+
const titlePropertyKey = useMemo(() => resolveTitlePropertyKey(order, properties, idProperty, titleProperty), [
|
|
62
|
+
titleProperty,
|
|
63
|
+
order,
|
|
64
|
+
properties,
|
|
65
|
+
idProperty
|
|
66
|
+
]);
|
|
67
|
+
const previewProperties = useMemo(() => {
|
|
68
|
+
const result = [];
|
|
69
|
+
for (const key of order) {
|
|
70
|
+
if (key === titlePropertyKey || key === kanbanProperty) continue;
|
|
71
|
+
const prop = properties[key];
|
|
72
|
+
if (prop && !prop.hideFromCollection) {
|
|
73
|
+
result.push({
|
|
74
|
+
key,
|
|
75
|
+
property: prop
|
|
76
|
+
});
|
|
77
|
+
if (result.length >= 2) break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}, [
|
|
82
|
+
order,
|
|
83
|
+
properties,
|
|
84
|
+
titlePropertyKey,
|
|
85
|
+
kanbanProperty
|
|
86
|
+
]);
|
|
87
|
+
const boardItems = useMemo(() => {
|
|
88
|
+
return dataController.data.map((row, index) => {
|
|
89
|
+
const id = getValueInPath(row, idProperty);
|
|
90
|
+
return {
|
|
91
|
+
id: id !== null && id !== void 0 ? String(id) : String(index),
|
|
92
|
+
data: row
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
}, [dataController.data, idProperty]);
|
|
96
|
+
const assignColumn = useCallback((item) => {
|
|
97
|
+
const val = getValueInPath(item.data, kanbanProperty);
|
|
98
|
+
const strVal = val !== null && val !== void 0 ? String(val) : "";
|
|
99
|
+
if (columns.includes(strVal)) return strVal;
|
|
100
|
+
return columns[0] ?? "";
|
|
101
|
+
}, [kanbanProperty, columns]);
|
|
102
|
+
const ItemComponent = useMemo(() => {
|
|
103
|
+
function KanbanCard(props) {
|
|
104
|
+
const { item } = props;
|
|
105
|
+
const titleValue = getValueInPath(item.data, titlePropertyKey);
|
|
106
|
+
return /* @__PURE__ */ jsxs(Card, {
|
|
107
|
+
onClick: () => onRowClick?.(item.data),
|
|
108
|
+
className: cls("p-3", cardClickableMixin, props.isDragging && "bg-primary-bg/30 dark:bg-primary-bg/10 ring-1 ring-primary/75", props.isDragging && "shadow-lg opacity-75"),
|
|
109
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
110
|
+
variant: "subtitle2",
|
|
111
|
+
noWrap: true,
|
|
112
|
+
children: String(titleValue ?? "")
|
|
113
|
+
}), previewProperties.map(({ key, property }) => {
|
|
114
|
+
const value = getValueInPath(item.data, key);
|
|
115
|
+
if (cellRenderer) {
|
|
116
|
+
const custom = cellRenderer({
|
|
117
|
+
row: item.data,
|
|
118
|
+
propertyKey: key,
|
|
119
|
+
property,
|
|
120
|
+
value,
|
|
121
|
+
size: "tiny"
|
|
122
|
+
});
|
|
123
|
+
if (custom !== void 0) return /* @__PURE__ */ jsx("div", {
|
|
124
|
+
className: "mt-1",
|
|
125
|
+
children: custom
|
|
126
|
+
}, key);
|
|
127
|
+
}
|
|
128
|
+
return /* @__PURE__ */ jsx("div", {
|
|
129
|
+
className: "mt-1",
|
|
130
|
+
children: /* @__PURE__ */ jsx(DefaultCellRenderer, {
|
|
131
|
+
row: item.data,
|
|
132
|
+
propertyKey: key,
|
|
133
|
+
property,
|
|
134
|
+
value,
|
|
135
|
+
size: "tiny"
|
|
136
|
+
})
|
|
137
|
+
}, key);
|
|
138
|
+
})]
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return KanbanCard;
|
|
142
|
+
}, [
|
|
143
|
+
titlePropertyKey,
|
|
144
|
+
previewProperties,
|
|
145
|
+
cellRenderer,
|
|
146
|
+
onRowClick
|
|
147
|
+
]);
|
|
148
|
+
const handleAddItemToColumn = useCallback((column) => {
|
|
149
|
+
onRowCreate?.({ [kanbanProperty]: column });
|
|
150
|
+
}, [onRowCreate, kanbanProperty]);
|
|
151
|
+
if (columns.length === 0 && !dataController.loading) return /* @__PURE__ */ jsx("div", {
|
|
152
|
+
className: "flex items-center justify-center p-8",
|
|
153
|
+
children: emptyComponent ?? /* @__PURE__ */ jsx(Typography, {
|
|
154
|
+
variant: "body2",
|
|
155
|
+
color: "secondary",
|
|
156
|
+
children: "No columns available for kanban view."
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
return /* @__PURE__ */ jsx(KanbanView, {
|
|
160
|
+
columns,
|
|
161
|
+
columnLabels,
|
|
162
|
+
data: boardItems,
|
|
163
|
+
assignColumn,
|
|
164
|
+
ItemComponent,
|
|
165
|
+
onAddItemToColumn: onRowCreate ? handleAddItemToColumn : void 0,
|
|
166
|
+
className
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { CollectionKanbanView };
|
|
171
|
+
|
|
172
|
+
//# sourceMappingURL=CollectionKanbanView.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectionKanbanView.es.js","names":[],"sources":["../../../src/views/CollectionView/CollectionKanbanView.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useMemo } from \"react\";\nimport { KanbanView } from \"../KanbanView\";\nimport type { BoardItem, BoardItemViewProps } from \"../KanbanView\";\nimport { Card } from \"../../components/Card\";\nimport { Typography } from \"../../components/Typography\";\nimport { DefaultCellRenderer } from \"./DefaultCellRenderer\";\nimport { cls } from \"../../util\";\nimport { cardClickableMixin, cardSelectedMixin } from \"../../styles\";\nimport type {\n CollectionPropertyConfig,\n CollectionDataController,\n CellRendererOverride,\n CollectionSelectionController,\n CollectionEnumValueConfig,\n} from \"./CollectionViewTypes\";\nimport { resolveTitlePropertyKey } from \"./resolveTitleProperty\";\n\nimport { getValueInPath } from \"./utils\";\n\nfunction getEnumLabel(enumConfig: CollectionPropertyConfig[\"enum\"], key: string): string {\n if (!enumConfig) return key;\n if (enumConfig instanceof Map) {\n const cfg: CollectionEnumValueConfig | undefined = enumConfig.get(key);\n if (!cfg) return key;\n return typeof cfg === \"string\" ? cfg : cfg.label;\n }\n const cfg: CollectionEnumValueConfig | undefined = enumConfig[key];\n if (!cfg) return key;\n return typeof cfg === \"string\" ? cfg : cfg.label;\n}\n\n/**\n * Props for the {@link CollectionKanbanView} component.\n * @group Collection View\n */\nexport type CollectionKanbanViewProps<T = Record<string, unknown>> = {\n dataController: CollectionDataController<T>;\n properties: Record<string, CollectionPropertyConfig>;\n propertiesOrder?: string[];\n idProperty?: string;\n titleProperty?: string;\n kanbanProperty: string;\n onRowClick?: (row: T) => void;\n cellRenderer?: CellRendererOverride<T>;\n selectionEnabled?: boolean;\n selectionController?: CollectionSelectionController<T>;\n highlightedItems?: T[];\n emptyComponent?: React.ReactNode;\n onRowCreate?: (defaults?: Record<string, unknown>) => void;\n className?: string;\n};\n\n/**\n * Headless kanban view that wraps {@link KanbanView}.\n *\n * Groups data by a kanban property's enum values into columns.\n * Each card displays a title and 1–2 preview properties.\n *\n * @group Collection View\n */\nexport function CollectionKanbanView<T extends Record<string, unknown> = Record<string, unknown>>({\n dataController,\n properties,\n propertiesOrder,\n idProperty = \"id\",\n titleProperty,\n kanbanProperty,\n onRowClick,\n cellRenderer,\n emptyComponent,\n onRowCreate,\n className,\n}: CollectionKanbanViewProps<T>) {\n const order = useMemo(\n () => propertiesOrder ?? Object.keys(properties),\n [propertiesOrder, properties]\n );\n\n // ── Resolve kanban columns ───────────────────────────────────────\n const columns = useMemo<string[]>(() => {\n const kanbanConfig = properties[kanbanProperty];\n if (kanbanConfig?.enum) {\n if (kanbanConfig.enum instanceof Map) {\n return Array.from(kanbanConfig.enum.keys()).map(String);\n }\n return Object.keys(kanbanConfig.enum);\n }\n // Deduce from data\n const seen = new Set<string>();\n for (const row of dataController.data) {\n const val = getValueInPath(row as Record<string, unknown>, kanbanProperty);\n if (val !== null && val !== undefined) {\n seen.add(String(val));\n }\n }\n return Array.from(seen);\n }, [properties, kanbanProperty, dataController.data]);\n\n // ── Column labels ────────────────────────────────────────────────\n const columnLabels = useMemo(() => {\n const kanbanConfig = properties[kanbanProperty];\n const labels: Record<string, string> = {};\n for (const col of columns) {\n labels[col] = getEnumLabel(kanbanConfig?.enum, col);\n }\n return labels;\n }, [properties, kanbanProperty, columns]);\n\n // ── Resolve title property ───────────────────────────────────────\n const titlePropertyKey = useMemo(\n () => resolveTitlePropertyKey(order, properties, idProperty, titleProperty),\n [titleProperty, order, properties, idProperty]\n );\n\n // ── Preview properties (up to 2) ─────────────────────────────────\n const previewProperties = useMemo(() => {\n const result: { key: string; property: CollectionPropertyConfig }[] = [];\n for (const key of order) {\n if (key === titlePropertyKey || key === kanbanProperty) continue;\n const prop = properties[key];\n if (prop && !prop.hideFromCollection) {\n result.push({ key, property: prop });\n if (result.length >= 2) break;\n }\n }\n return result;\n }, [order, properties, titlePropertyKey, kanbanProperty]);\n\n // ── Convert data to BoardItem<T>[] ───────────────────────────────\n const boardItems = useMemo<BoardItem<T>[]>(() => {\n return dataController.data.map((row, index) => {\n const id = getValueInPath(row as Record<string, unknown>, idProperty);\n return {\n id: id !== null && id !== undefined ? String(id) : String(index),\n data: row,\n };\n });\n }, [dataController.data, idProperty]);\n\n // ── Assign column ────────────────────────────────────────────────\n const assignColumn = useCallback(\n (item: BoardItem<T>): string => {\n const val = getValueInPath(item.data as Record<string, unknown>, kanbanProperty);\n const strVal = val !== null && val !== undefined ? String(val) : \"\";\n if (columns.includes(strVal)) return strVal;\n return columns[0] ?? \"\";\n },\n [kanbanProperty, columns]\n );\n\n // ── Item component ───────────────────────────────────────────────\n const ItemComponent = useMemo(() => {\n function KanbanCard(props: BoardItemViewProps<T>) {\n const { item } = props;\n const titleValue = getValueInPath(\n item.data as Record<string, unknown>,\n titlePropertyKey\n );\n\n return (\n <Card\n onClick={() => onRowClick?.(item.data)}\n className={cls(\n \"p-3\",\n cardClickableMixin,\n props.isDragging && cardSelectedMixin,\n props.isDragging && \"shadow-lg opacity-75\"\n )}\n >\n <Typography variant=\"subtitle2\" noWrap>\n {String(titleValue ?? \"\")}\n </Typography>\n {previewProperties.map(({ key, property }) => {\n const value = getValueInPath(\n item.data as Record<string, unknown>,\n key\n );\n\n if (cellRenderer) {\n const custom = cellRenderer({\n row: item.data,\n propertyKey: key,\n property,\n value,\n size: \"tiny\",\n });\n if (custom !== undefined) {\n return (\n <div key={key} className=\"mt-1\">\n {custom}\n </div>\n );\n }\n }\n\n return (\n <div key={key} className=\"mt-1\">\n <DefaultCellRenderer\n row={item.data}\n propertyKey={key}\n property={property}\n value={value}\n size=\"tiny\"\n />\n </div>\n );\n })}\n </Card>\n );\n }\n return KanbanCard;\n }, [titlePropertyKey, previewProperties, cellRenderer, onRowClick]);\n\n // ── Add item to column ───────────────────────────────────────────\n const handleAddItemToColumn = useCallback(\n (column: string) => {\n onRowCreate?.({ [kanbanProperty]: column });\n },\n [onRowCreate, kanbanProperty]\n );\n\n if (columns.length === 0 && !dataController.loading) {\n return (\n <div className=\"flex items-center justify-center p-8\">\n {emptyComponent ?? (\n <Typography variant=\"body2\" color=\"secondary\">\n No columns available for kanban view.\n </Typography>\n )}\n </div>\n );\n }\n\n return (\n <KanbanView<T, string>\n columns={columns}\n columnLabels={columnLabels as Record<string, string>}\n data={boardItems}\n assignColumn={assignColumn}\n ItemComponent={ItemComponent}\n onAddItemToColumn={onRowCreate ? handleAddItemToColumn : undefined}\n className={className}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cls } from "../../util/cls.es.js";
|
|
3
|
+
import { Typography } from "../../components/Typography.es.js";
|
|
4
|
+
import { Checkbox } from "../../components/Checkbox.es.js";
|
|
5
|
+
import { useUiLabels } from "../../hooks/UiLabelsContext.es.js";
|
|
6
|
+
import { ListView } from "../ListView.es.js";
|
|
7
|
+
import { DefaultCellRenderer } from "./DefaultCellRenderer.es.js";
|
|
8
|
+
import { getValueInPath } from "./utils.es.js";
|
|
9
|
+
import { resolveTitlePropertyKey } from "./resolveTitleProperty.es.js";
|
|
10
|
+
import React, { useCallback, useMemo } from "react";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
//#region src/views/CollectionView/CollectionListView.tsx
|
|
13
|
+
/**
|
|
14
|
+
* Headless list view that wraps {@link ListView}.
|
|
15
|
+
*
|
|
16
|
+
* Each row displays a title (bold), subtitle with 1–2 secondary property values,
|
|
17
|
+
* and optional selection support.
|
|
18
|
+
*
|
|
19
|
+
* @group Collection View
|
|
20
|
+
*/
|
|
21
|
+
function CollectionListView({ dataController, properties, propertiesOrder, idProperty = "id", titleProperty, onRowClick, cellRenderer, size = "m", selectionEnabled, selectionController, highlightedItems, emptyComponent, className }) {
|
|
22
|
+
const labels = useUiLabels();
|
|
23
|
+
const order = useMemo(() => propertiesOrder ?? Object.keys(properties), [propertiesOrder, properties]);
|
|
24
|
+
const titlePropertyKey = useMemo(() => resolveTitlePropertyKey(order, properties, idProperty, titleProperty), [
|
|
25
|
+
titleProperty,
|
|
26
|
+
order,
|
|
27
|
+
properties,
|
|
28
|
+
idProperty
|
|
29
|
+
]);
|
|
30
|
+
const subtitleProperties = useMemo(() => {
|
|
31
|
+
const result = [];
|
|
32
|
+
for (const key of order) {
|
|
33
|
+
if (key === titlePropertyKey) continue;
|
|
34
|
+
const prop = properties[key];
|
|
35
|
+
if (prop && !prop.hideFromCollection) {
|
|
36
|
+
result.push({
|
|
37
|
+
key,
|
|
38
|
+
property: prop
|
|
39
|
+
});
|
|
40
|
+
if (result.length >= 2) break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}, [
|
|
45
|
+
order,
|
|
46
|
+
properties,
|
|
47
|
+
titlePropertyKey
|
|
48
|
+
]);
|
|
49
|
+
const selectedIds = useMemo(() => {
|
|
50
|
+
if (!selectionController?.selectedItems) return void 0;
|
|
51
|
+
const ids = /* @__PURE__ */ new Set();
|
|
52
|
+
for (const item of selectionController.selectedItems) {
|
|
53
|
+
const id = getValueInPath(item, idProperty);
|
|
54
|
+
if (id !== void 0 && id !== null) ids.add(id);
|
|
55
|
+
}
|
|
56
|
+
return ids;
|
|
57
|
+
}, [selectionController?.selectedItems, idProperty]);
|
|
58
|
+
const highlightedIds = useMemo(() => {
|
|
59
|
+
if (!highlightedItems) return void 0;
|
|
60
|
+
const ids = /* @__PURE__ */ new Set();
|
|
61
|
+
for (const item of highlightedItems) {
|
|
62
|
+
const id = getValueInPath(item, idProperty);
|
|
63
|
+
if (id !== void 0 && id !== null) ids.add(id);
|
|
64
|
+
}
|
|
65
|
+
return ids;
|
|
66
|
+
}, [highlightedItems, idProperty]);
|
|
67
|
+
const handleSelectionChange = useCallback((item, selected) => {
|
|
68
|
+
if (!selectionController) return;
|
|
69
|
+
selectionController.setSelectedItems((prev) => {
|
|
70
|
+
if (selected) return [...prev, item];
|
|
71
|
+
const itemId = getValueInPath(item, idProperty);
|
|
72
|
+
return prev.filter((existing) => {
|
|
73
|
+
return getValueInPath(existing, idProperty) !== itemId;
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
}, [selectionController, idProperty]);
|
|
77
|
+
const renderRow = useCallback((params) => {
|
|
78
|
+
const { item, style, className: rowClassName, selected, highlighted, onClick, onSelectionChange } = params;
|
|
79
|
+
const titleValue = getValueInPath(item, titlePropertyKey);
|
|
80
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
81
|
+
style,
|
|
82
|
+
className: cls("flex items-center gap-3 px-4 cursor-pointer", "hover:bg-surface-hover", "transition-colors duration-100", selected && "bg-primary-bg", highlighted && "bg-surface-raised", rowClassName),
|
|
83
|
+
onClick,
|
|
84
|
+
children: [selectionEnabled && /* @__PURE__ */ jsx("div", {
|
|
85
|
+
className: "flex-shrink-0",
|
|
86
|
+
onClick: (e) => e.stopPropagation(),
|
|
87
|
+
children: /* @__PURE__ */ jsx(Checkbox, {
|
|
88
|
+
"aria-label": labels.select,
|
|
89
|
+
checked: selected,
|
|
90
|
+
onCheckedChange: onSelectionChange,
|
|
91
|
+
size: "small"
|
|
92
|
+
})
|
|
93
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
94
|
+
className: "flex-1 min-w-0 py-1",
|
|
95
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
96
|
+
variant: "subtitle2",
|
|
97
|
+
noWrap: true,
|
|
98
|
+
children: String(titleValue ?? "")
|
|
99
|
+
}), subtitleProperties.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
100
|
+
className: "flex items-center gap-2",
|
|
101
|
+
children: subtitleProperties.map(({ key, property }) => {
|
|
102
|
+
const value = getValueInPath(item, key);
|
|
103
|
+
if (cellRenderer) {
|
|
104
|
+
const custom = cellRenderer({
|
|
105
|
+
row: item,
|
|
106
|
+
propertyKey: key,
|
|
107
|
+
property,
|
|
108
|
+
value,
|
|
109
|
+
size: "tiny"
|
|
110
|
+
});
|
|
111
|
+
if (custom !== void 0) return /* @__PURE__ */ jsx(React.Fragment, { children: custom }, key);
|
|
112
|
+
}
|
|
113
|
+
return /* @__PURE__ */ jsx(React.Fragment, { children: /* @__PURE__ */ jsx(DefaultCellRenderer, {
|
|
114
|
+
row: item,
|
|
115
|
+
propertyKey: key,
|
|
116
|
+
property,
|
|
117
|
+
value,
|
|
118
|
+
size: "tiny"
|
|
119
|
+
}) }, key);
|
|
120
|
+
})
|
|
121
|
+
})]
|
|
122
|
+
})]
|
|
123
|
+
});
|
|
124
|
+
}, [
|
|
125
|
+
titlePropertyKey,
|
|
126
|
+
subtitleProperties,
|
|
127
|
+
cellRenderer,
|
|
128
|
+
selectionEnabled,
|
|
129
|
+
labels
|
|
130
|
+
]);
|
|
131
|
+
return /* @__PURE__ */ jsx("div", {
|
|
132
|
+
className: cls("w-full h-full overflow-auto", className),
|
|
133
|
+
children: /* @__PURE__ */ jsx(ListView, {
|
|
134
|
+
data: dataController.data,
|
|
135
|
+
dataLoading: dataController.loading,
|
|
136
|
+
noMoreToLoad: dataController.noMoreToLoad,
|
|
137
|
+
dataLoadingError: dataController.error,
|
|
138
|
+
itemCount: dataController.itemCount,
|
|
139
|
+
setItemCount: dataController.setItemCount,
|
|
140
|
+
pageSize: dataController.pageSize,
|
|
141
|
+
paginationEnabled: dataController.paginationEnabled,
|
|
142
|
+
onItemClick: onRowClick,
|
|
143
|
+
selectedIds,
|
|
144
|
+
highlightedIds,
|
|
145
|
+
selectionEnabled: !!selectionEnabled,
|
|
146
|
+
onSelectionChange: handleSelectionChange,
|
|
147
|
+
size,
|
|
148
|
+
renderRow,
|
|
149
|
+
emptyComponent
|
|
150
|
+
})
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
export { CollectionListView };
|
|
155
|
+
|
|
156
|
+
//# sourceMappingURL=CollectionListView.es.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectionListView.es.js","names":[],"sources":["../../../src/views/CollectionView/CollectionListView.tsx"],"sourcesContent":["\"use client\";\nimport React, { useCallback, useMemo } from \"react\";\nimport { ListView } from \"../ListView\";\nimport { Typography } from \"../../components/Typography\";\nimport { Checkbox } from \"../../components/Checkbox\";\nimport { DefaultCellRenderer } from \"./DefaultCellRenderer\";\nimport { cls } from \"../../util\";\nimport { useUiLabels } from \"../../hooks/UiLabelsContext\";\nimport type {\n CollectionPropertyConfig,\n CollectionDataController,\n CellRendererOverride,\n CollectionViewSize,\n CollectionSelectionController,\n} from \"./CollectionViewTypes\";\nimport { resolveTitlePropertyKey } from \"./resolveTitleProperty\";\n\nimport { getValueInPath } from \"./utils\";\n\n/**\n * Props for the {@link CollectionListView} component.\n * @group Collection View\n */\nexport type CollectionListViewProps<T = Record<string, unknown>> = {\n dataController: CollectionDataController<T>;\n properties: Record<string, CollectionPropertyConfig>;\n propertiesOrder?: string[];\n idProperty?: string;\n titleProperty?: string;\n onRowClick?: (row: T) => void;\n cellRenderer?: CellRendererOverride<T>;\n size?: CollectionViewSize;\n selectionEnabled?: boolean;\n selectionController?: CollectionSelectionController<T>;\n highlightedItems?: T[];\n emptyComponent?: React.ReactNode;\n className?: string;\n};\n\n/**\n * Headless list view that wraps {@link ListView}.\n *\n * Each row displays a title (bold), subtitle with 1–2 secondary property values,\n * and optional selection support.\n *\n * @group Collection View\n */\nexport function CollectionListView<T extends Record<string, unknown> = Record<string, unknown>>({\n dataController,\n properties,\n propertiesOrder,\n idProperty = \"id\",\n titleProperty,\n onRowClick,\n cellRenderer,\n size = \"m\",\n selectionEnabled,\n selectionController,\n highlightedItems,\n emptyComponent,\n className,\n}: CollectionListViewProps<T>) {\n const labels = useUiLabels();\n const order = useMemo(\n () => propertiesOrder ?? Object.keys(properties),\n [propertiesOrder, properties]\n );\n\n // ── Resolve title property ───────────────────────────────────────\n const titlePropertyKey = useMemo(\n () => resolveTitlePropertyKey(order, properties, idProperty, titleProperty),\n [titleProperty, order, properties, idProperty]\n );\n\n // ── Subtitle properties (up to 2) ────────────────────────────────\n const subtitleProperties = useMemo(() => {\n const result: { key: string; property: CollectionPropertyConfig }[] = [];\n for (const key of order) {\n if (key === titlePropertyKey) continue;\n const prop = properties[key];\n if (prop && !prop.hideFromCollection) {\n result.push({ key, property: prop });\n if (result.length >= 2) break;\n }\n }\n return result;\n }, [order, properties, titlePropertyKey]);\n\n // ── Selection IDs ────────────────────────────────────────────────\n const selectedIds = useMemo(() => {\n if (!selectionController?.selectedItems) return undefined;\n const ids = new Set<string | number>();\n for (const item of selectionController.selectedItems) {\n const id = getValueInPath(item as Record<string, unknown>, idProperty);\n if (id !== undefined && id !== null) {\n ids.add(id as string | number);\n }\n }\n return ids;\n }, [selectionController?.selectedItems, idProperty]);\n\n // ── Highlighted IDs ──────────────────────────────────────────────\n const highlightedIds = useMemo(() => {\n if (!highlightedItems) return undefined;\n const ids = new Set<string | number>();\n for (const item of highlightedItems) {\n const id = getValueInPath(item as Record<string, unknown>, idProperty);\n if (id !== undefined && id !== null) {\n ids.add(id as string | number);\n }\n }\n return ids;\n }, [highlightedItems, idProperty]);\n\n // ── Selection handler ────────────────────────────────────────────\n const handleSelectionChange = useCallback(\n (item: T, selected: boolean) => {\n if (!selectionController) return;\n selectionController.setSelectedItems((prev) => {\n if (selected) {\n return [...prev, item];\n }\n const itemId = getValueInPath(item as Record<string, unknown>, idProperty);\n return prev.filter((existing) => {\n const existingId = getValueInPath(existing as Record<string, unknown>, idProperty);\n return existingId !== itemId;\n });\n });\n },\n [selectionController, idProperty]\n );\n\n // ── Render row ───────────────────────────────────────────────────\n const renderRow = useCallback(\n (params: {\n item: T;\n index: number;\n style: React.CSSProperties;\n className: string;\n selected: boolean;\n highlighted: boolean;\n isLast: boolean;\n onClick: (e: React.MouseEvent) => void;\n onSelectionChange: (selected: boolean) => void;\n }): React.ReactNode => {\n const { item, style, className: rowClassName, selected, highlighted, onClick, onSelectionChange } = params;\n const titleValue = getValueInPath(item as Record<string, unknown>, titlePropertyKey);\n\n return (\n <div\n style={style}\n className={cls(\n \"flex items-center gap-3 px-4 cursor-pointer\",\n \"hover:bg-surface-hover\",\n \"transition-colors duration-100\",\n selected && \"bg-primary-bg\",\n highlighted && \"bg-surface-raised\",\n rowClassName\n )}\n onClick={onClick}\n >\n {/* Selection checkbox */}\n {selectionEnabled && (\n <div className=\"flex-shrink-0\" onClick={(e) => e.stopPropagation()}>\n <Checkbox\n aria-label={labels.select}\n checked={selected}\n onCheckedChange={onSelectionChange}\n size=\"small\"\n />\n </div>\n )}\n\n {/* Content */}\n <div className=\"flex-1 min-w-0 py-1\">\n {/* Title */}\n <Typography variant=\"subtitle2\" noWrap>\n {String(titleValue ?? \"\")}\n </Typography>\n\n {/* Subtitle properties */}\n {subtitleProperties.length > 0 && (\n <div className=\"flex items-center gap-2\">\n {subtitleProperties.map(({ key, property }) => {\n const value = getValueInPath(item as Record<string, unknown>, key);\n\n if (cellRenderer) {\n const custom = cellRenderer({\n row: item,\n propertyKey: key,\n property,\n value,\n size: \"tiny\",\n });\n if (custom !== undefined) {\n return <React.Fragment key={key}>{custom}</React.Fragment>;\n }\n }\n\n return (\n <React.Fragment key={key}>\n <DefaultCellRenderer\n row={item}\n propertyKey={key}\n property={property}\n value={value}\n size=\"tiny\"\n />\n </React.Fragment>\n );\n })}\n </div>\n )}\n </div>\n </div>\n );\n },\n [titlePropertyKey, subtitleProperties, cellRenderer, selectionEnabled, labels]\n );\n\n return (\n <div className={cls(\"w-full h-full overflow-auto\", className)}>\n <ListView<T>\n data={dataController.data}\n dataLoading={dataController.loading}\n noMoreToLoad={dataController.noMoreToLoad}\n dataLoadingError={dataController.error}\n itemCount={dataController.itemCount}\n setItemCount={dataController.setItemCount}\n pageSize={dataController.pageSize}\n paginationEnabled={dataController.paginationEnabled}\n onItemClick={onRowClick}\n selectedIds={selectedIds}\n highlightedIds={highlightedIds}\n selectionEnabled={!!selectionEnabled}\n onSelectionChange={handleSelectionChange}\n size={size}\n renderRow={renderRow}\n emptyComponent={emptyComponent}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|