@rebasepro/ui 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/icons/index.d.ts +1 -1
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.es.js +2058 -16
  4. package/dist/index.es.js.map +1 -1
  5. package/dist/styles.d.ts +1 -1
  6. package/dist/views/CardView.d.ts +32 -0
  7. package/dist/views/CollectionView/CollectionCardView.d.ts +30 -0
  8. package/dist/views/CollectionView/CollectionKanbanView.d.ts +31 -0
  9. package/dist/views/CollectionView/CollectionListView.d.ts +30 -0
  10. package/dist/views/CollectionView/CollectionTableView.d.ts +37 -0
  11. package/dist/views/CollectionView/CollectionView.d.ts +121 -0
  12. package/dist/views/CollectionView/CollectionViewToolbar.d.ts +31 -0
  13. package/dist/views/CollectionView/CollectionViewTypes.d.ts +106 -0
  14. package/dist/views/CollectionView/DefaultCellRenderer.d.ts +9 -0
  15. package/dist/views/CollectionView/index.d.ts +24 -0
  16. package/dist/views/CollectionView/utils.d.ts +5 -0
  17. package/dist/views/Kanban/Board.d.ts +3 -0
  18. package/dist/views/Kanban/BoardColumn.d.ts +20 -0
  19. package/dist/views/Kanban/BoardColumnTitle.d.ts +8 -0
  20. package/dist/views/Kanban/BoardSortableList.d.ts +14 -0
  21. package/dist/views/Kanban/board_types.d.ts +61 -0
  22. package/dist/views/Kanban/index.d.ts +5 -0
  23. package/dist/views/KanbanView.d.ts +24 -0
  24. package/dist/views/ListView.d.ts +40 -0
  25. package/dist/views/TableView.d.ts +6 -0
  26. package/dist/views/index.d.ts +5 -0
  27. package/package.json +1 -1
  28. package/src/components/Sheet.tsx +8 -4
  29. package/src/icons/index.ts +3 -0
  30. package/src/index.ts +2 -0
  31. package/src/styles.ts +1 -1
  32. package/src/views/CardView.tsx +281 -0
  33. package/src/views/CollectionView/CollectionCardView.tsx +270 -0
  34. package/src/views/CollectionView/CollectionKanbanView.tsx +251 -0
  35. package/src/views/CollectionView/CollectionListView.tsx +245 -0
  36. package/src/views/CollectionView/CollectionTableView.tsx +229 -0
  37. package/src/views/CollectionView/CollectionView.tsx +388 -0
  38. package/src/views/CollectionView/CollectionViewToolbar.tsx +229 -0
  39. package/src/views/CollectionView/CollectionViewTypes.ts +137 -0
  40. package/src/views/CollectionView/DefaultCellRenderer.tsx +404 -0
  41. package/src/views/CollectionView/index.ts +44 -0
  42. package/src/views/CollectionView/utils.ts +14 -0
  43. package/src/views/Kanban/Board.tsx +421 -0
  44. package/src/views/Kanban/BoardColumn.tsx +135 -0
  45. package/src/views/Kanban/BoardColumnTitle.tsx +47 -0
  46. package/src/views/Kanban/BoardSortableList.tsx +170 -0
  47. package/src/views/Kanban/board_types.ts +70 -0
  48. package/src/views/Kanban/index.tsx +5 -0
  49. package/src/views/KanbanView.tsx +32 -0
  50. package/src/views/ListView.tsx +281 -0
  51. package/src/views/TableView.tsx +7 -0
  52. package/src/views/index.ts +5 -0
@@ -0,0 +1,229 @@
1
+ "use client";
2
+ import React, { useCallback, useState } from "react";
3
+ import { LayoutList, Table2, LayoutGrid, Kanban, Settings2 } from "lucide-react";
4
+ import { iconSize } from "../../icons/Icon";
5
+ import { SearchBar } from "../../components/SearchBar";
6
+ import { Popover } from "../../components/Popover";
7
+ import { ToggleButtonGroup } from "../../components/ToggleButtonGroup";
8
+ import type { ToggleButtonOption } from "../../components/ToggleButtonGroup";
9
+ import { Typography } from "../../components/Typography";
10
+ import { Select, SelectItem } from "../../components/Select";
11
+ import { IconButton } from "../../components/IconButton";
12
+ import { cls } from "../../util";
13
+ import type { CollectionViewMode, CollectionViewSize, KanbanPropertyOption } from "./CollectionViewTypes";
14
+
15
+ /**
16
+ * Props for the {@link CollectionViewToolbar} component.
17
+ * @group Collection View
18
+ */
19
+ export type CollectionViewToolbarProps = {
20
+ viewMode: CollectionViewMode;
21
+ onViewModeChange?: (mode: CollectionViewMode) => void;
22
+ enabledViews?: CollectionViewMode[];
23
+ size?: CollectionViewSize;
24
+ onSizeChange?: (size: CollectionViewSize) => void;
25
+ searchString?: string;
26
+ onSearchChange?: (search?: string) => void;
27
+ loading?: boolean;
28
+ actionsStart?: React.ReactNode;
29
+ actionsEnd?: React.ReactNode;
30
+ compact?: boolean;
31
+ kanbanPropertyOptions?: KanbanPropertyOption[];
32
+ selectedKanbanProperty?: string;
33
+ onKanbanPropertyChange?: (property: string) => void;
34
+ };
35
+
36
+ const VIEW_MODE_ICONS: Record<CollectionViewMode, React.ReactNode> = {
37
+ list: <LayoutList size={iconSize.small} />,
38
+ table: <Table2 size={iconSize.small} />,
39
+ cards: <LayoutGrid size={iconSize.small} />,
40
+ kanban: <Kanban size={iconSize.small} />,
41
+ };
42
+
43
+ const VIEW_MODE_LABELS: Record<CollectionViewMode, string> = {
44
+ list: "List",
45
+ table: "Table",
46
+ cards: "Cards",
47
+ kanban: "Board",
48
+ };
49
+
50
+ const SIZE_LABELS: Record<CollectionViewSize, string> = {
51
+ xs: "Extra Small",
52
+ s: "Small",
53
+ m: "Medium",
54
+ l: "Large",
55
+ xl: "Extra Large",
56
+ };
57
+
58
+ /**
59
+ * Toolbar for the headless CollectionView.
60
+ *
61
+ * Renders a search bar, view mode toggle (popover with toggle button group),
62
+ * optional size selector, optional kanban property selector, and action slots.
63
+ *
64
+ * @group Collection View
65
+ */
66
+ export function CollectionViewToolbar({
67
+ viewMode,
68
+ onViewModeChange,
69
+ enabledViews = ["list", "table", "cards", "kanban"],
70
+ size = "m",
71
+ onSizeChange,
72
+ searchString,
73
+ onSearchChange,
74
+ loading,
75
+ actionsStart,
76
+ actionsEnd,
77
+ compact,
78
+ kanbanPropertyOptions,
79
+ selectedKanbanProperty,
80
+ onKanbanPropertyChange,
81
+ }: CollectionViewToolbarProps) {
82
+ const [popoverOpen, setPopoverOpen] = useState(false);
83
+
84
+ const viewOptions: ToggleButtonOption<CollectionViewMode>[] = enabledViews.map((mode) => ({
85
+ value: mode,
86
+ label: VIEW_MODE_LABELS[mode],
87
+ icon: VIEW_MODE_ICONS[mode],
88
+ }));
89
+
90
+ const handleViewModeChange = useCallback(
91
+ (mode: CollectionViewMode) => {
92
+ onViewModeChange?.(mode);
93
+ },
94
+ [onViewModeChange]
95
+ );
96
+
97
+ const handleSizeChange = useCallback(
98
+ (value: string) => {
99
+ onSizeChange?.(value as CollectionViewSize);
100
+ },
101
+ [onSizeChange]
102
+ );
103
+
104
+ const handleKanbanPropertyChange = useCallback(
105
+ (value: string) => {
106
+ onKanbanPropertyChange?.(value);
107
+ },
108
+ [onKanbanPropertyChange]
109
+ );
110
+
111
+ const showViewToggle = onViewModeChange && enabledViews.length > 1;
112
+ const showSizeSelector = onSizeChange && viewMode !== "kanban";
113
+ const showKanbanSelector =
114
+ kanbanPropertyOptions &&
115
+ kanbanPropertyOptions.length > 0 &&
116
+ viewMode === "kanban" &&
117
+ onKanbanPropertyChange;
118
+ const showPopover = showViewToggle || showSizeSelector || showKanbanSelector;
119
+
120
+ return (
121
+ <div
122
+ className={cls(
123
+ "flex items-center gap-2 px-2 flex-shrink-0",
124
+ "border-b border-surface-200 dark:border-surface-800",
125
+ compact ? "py-0.5 gap-1" : "py-1"
126
+ )}
127
+ >
128
+ {actionsStart}
129
+
130
+ {onSearchChange && (
131
+ <SearchBar
132
+ onTextSearch={onSearchChange}
133
+ size="small"
134
+ initialValue={searchString}
135
+ loading={loading}
136
+ expandable
137
+ />
138
+ )}
139
+
140
+ <div className="flex-1" />
141
+
142
+ {actionsEnd}
143
+
144
+ {showPopover && (
145
+ <Popover
146
+ open={popoverOpen}
147
+ onOpenChange={setPopoverOpen}
148
+ side="bottom"
149
+ align="end"
150
+ trigger={
151
+ <IconButton
152
+ size="small"
153
+ onClick={() => setPopoverOpen((prev) => !prev)}
154
+ >
155
+ <Settings2 size={iconSize.small} />
156
+ </IconButton>
157
+ }
158
+ >
159
+ <div className="p-3 min-w-[200px] flex flex-col gap-3">
160
+ {showViewToggle && (
161
+ <div className="flex flex-col gap-1">
162
+ <Typography
163
+ variant="caption"
164
+ color="secondary"
165
+ className="font-medium uppercase tracking-wider"
166
+ >
167
+ View
168
+ </Typography>
169
+ <ToggleButtonGroup<CollectionViewMode>
170
+ value={viewMode}
171
+ onValueChange={handleViewModeChange}
172
+ options={viewOptions}
173
+ />
174
+ </div>
175
+ )}
176
+
177
+ {showSizeSelector && (
178
+ <div className="flex flex-col gap-1">
179
+ <Typography
180
+ variant="caption"
181
+ color="secondary"
182
+ className="font-medium uppercase tracking-wider"
183
+ >
184
+ Size
185
+ </Typography>
186
+ <Select
187
+ value={size}
188
+ onValueChange={handleSizeChange}
189
+ size="small"
190
+ >
191
+ {(Object.keys(SIZE_LABELS) as CollectionViewSize[]).map(
192
+ (sizeKey) => (
193
+ <SelectItem key={sizeKey} value={sizeKey}>
194
+ {SIZE_LABELS[sizeKey]}
195
+ </SelectItem>
196
+ )
197
+ )}
198
+ </Select>
199
+ </div>
200
+ )}
201
+
202
+ {showKanbanSelector && (
203
+ <div className="flex flex-col gap-1">
204
+ <Typography
205
+ variant="caption"
206
+ color="secondary"
207
+ className="font-medium uppercase tracking-wider"
208
+ >
209
+ Group by
210
+ </Typography>
211
+ <Select
212
+ value={selectedKanbanProperty}
213
+ onValueChange={handleKanbanPropertyChange}
214
+ size="small"
215
+ >
216
+ {kanbanPropertyOptions.map((option) => (
217
+ <SelectItem key={option.key} value={option.key}>
218
+ {option.label}
219
+ </SelectItem>
220
+ ))}
221
+ </Select>
222
+ </div>
223
+ )}
224
+ </div>
225
+ </Popover>
226
+ )}
227
+ </div>
228
+ );
229
+ }
@@ -0,0 +1,137 @@
1
+ import React from "react";
2
+
3
+ /** Supported view modes */
4
+ export type CollectionViewMode = "table" | "cards" | "kanban" | "list";
5
+
6
+ /** Supported collection sizes */
7
+ export type CollectionViewSize = "xs" | "s" | "m" | "l" | "xl";
8
+
9
+ /**
10
+ * Enum value configuration.
11
+ * Simple form: string label
12
+ * Extended form: object with label + optional color + disabled
13
+ */
14
+ export type CollectionEnumValueConfig = string | {
15
+ label: string;
16
+ color?: string;
17
+ disabled?: boolean;
18
+ };
19
+
20
+ /**
21
+ * A data-agnostic property definition for rendering columns/cells.
22
+ * This is a UI-only type — it describes HOW to render, not WHERE data lives.
23
+ *
24
+ * This shape is deliberately compatible with Rebase's Property type,
25
+ * so the connected wrapper can pass properties with minimal mapping.
26
+ */
27
+ export interface CollectionPropertyConfig {
28
+ /** Property data type — determines how cells are rendered */
29
+ type: "string" | "number" | "boolean" | "date" | "array" | "map" | "reference" | "relation" | "geopoint";
30
+
31
+ /** Display name shown in column headers and card labels */
32
+ name: string;
33
+
34
+ /** Optional description, shown as tooltip on column header */
35
+ description?: string;
36
+
37
+ // —— Display configuration ——
38
+ /** Column width in pixels */
39
+ columnWidth?: number;
40
+ /** If true, this property is not shown in collection views */
41
+ hideFromCollection?: boolean;
42
+
43
+ // —— Enum support ——
44
+ /** Map of value → label/config for enum properties */
45
+ enum?: Record<string, CollectionEnumValueConfig> | Map<string | number, CollectionEnumValueConfig>;
46
+
47
+ // —— String-specific ——
48
+ multiline?: boolean;
49
+ previewAsTag?: boolean;
50
+ url?: boolean | string;
51
+ markdown?: boolean;
52
+ storage?: boolean;
53
+ email?: boolean;
54
+
55
+ // —— Array-specific ——
56
+ of?: CollectionPropertyConfig;
57
+
58
+ // —— Map-specific ——
59
+ properties?: Record<string, CollectionPropertyConfig>;
60
+ propertiesOrder?: string[];
61
+
62
+ // —— Date-specific ——
63
+ mode?: "date" | "date_time";
64
+
65
+ // —— UI overrides ——
66
+ /** Custom preview component for this property */
67
+ Preview?: React.ComponentType<CellRendererProps>;
68
+ }
69
+
70
+ /**
71
+ * Data controller interface — the headless view reads data and state through this.
72
+ * Consumer apps can build their own implementation.
73
+ */
74
+ export interface CollectionDataController<T = Record<string, unknown>> {
75
+ data: T[];
76
+ loading: boolean;
77
+ noMoreToLoad: boolean;
78
+ error?: Error;
79
+
80
+ filterValues?: Record<string, [string, unknown]>;
81
+ setFilterValues?: (values: Record<string, [string, unknown]> | undefined) => void;
82
+
83
+ sortBy?: [string, "asc" | "desc"];
84
+ setSortBy?: (sort?: [string, "asc" | "desc"]) => void;
85
+
86
+ searchString?: string;
87
+ setSearchString?: (search?: string) => void;
88
+ clearFilter?: () => void;
89
+
90
+ initialScroll?: number;
91
+ onScroll?: (props: {
92
+ scrollDirection: "forward" | "backward";
93
+ scrollOffset: number;
94
+ scrollUpdateWasRequested: boolean;
95
+ }) => void;
96
+
97
+ paginationEnabled?: boolean;
98
+ pageSize?: number;
99
+ itemCount?: number;
100
+ setItemCount?: (count: number) => void;
101
+ }
102
+
103
+ /**
104
+ * Props passed to cell renderers.
105
+ */
106
+ export interface CellRendererProps<T = Record<string, unknown>> {
107
+ row: T;
108
+ propertyKey: string;
109
+ property: CollectionPropertyConfig;
110
+ value: unknown;
111
+ size?: "small" | "medium" | "large" | "tiny";
112
+ width?: number;
113
+ height?: number;
114
+ }
115
+
116
+ /**
117
+ * Custom cell renderer function type.
118
+ * Return undefined to fall through to the default renderer.
119
+ */
120
+ export type CellRendererOverride<T = Record<string, unknown>> =
121
+ (props: CellRendererProps<T>) => React.ReactNode | undefined;
122
+
123
+ /**
124
+ * Selection controller for the headless view.
125
+ */
126
+ export interface CollectionSelectionController<T = Record<string, unknown>> {
127
+ selectedItems: T[];
128
+ setSelectedItems: React.Dispatch<React.SetStateAction<T[]>>;
129
+ }
130
+
131
+ /**
132
+ * Kanban property option for the view mode toggle.
133
+ */
134
+ export interface KanbanPropertyOption {
135
+ key: string;
136
+ label: string;
137
+ }