@protobase/ui 0.0.0-stage → 0.1.1
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/LICENSE +202 -0
- package/dist/activity-chart/activity-chart.js +125 -0
- package/dist/activity-chart/activity-math.js +11 -0
- package/dist/activity-chart/activity-tooltip.js +24 -0
- package/dist/app/action-icon.js +10 -0
- package/dist/app/app.d.ts +24 -0
- package/dist/app/app.js +123 -0
- package/dist/app/batch-failure.js +21 -0
- package/dist/app/breadcrumb-for.js +27 -0
- package/dist/app/create/create-page.js +118 -0
- package/dist/app/create/create-record-form.js +68 -0
- package/dist/app/create/create-values.js +52 -0
- package/dist/app/delete/dialogs.js +109 -0
- package/dist/app/delete/record-diff.js +17 -0
- package/dist/app/delete/use-delete-flow.js +193 -0
- package/dist/app/delete/use-restore.js +9 -0
- package/dist/app/draft-storage.js +18 -0
- package/dist/app/error-banner.js +28 -0
- package/dist/app/field-editor.js +88 -0
- package/dist/app/icons.js +27 -0
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.html +12 -0
- package/dist/app/list-chart.js +36 -0
- package/dist/app/list-order.js +15 -0
- package/dist/app/list-page.js +213 -0
- package/dist/app/list-search.js +30 -0
- package/dist/app/main.d.ts +1 -0
- package/dist/app/main.js +14 -0
- package/dist/app/menu-icons.js +21 -0
- package/dist/app/meta-gate.js +46 -0
- package/dist/app/nav-from-meta.js +65 -0
- package/dist/app/nav-recent.js +33 -0
- package/dist/app/pages/action-href.js +7 -0
- package/dist/app/pages/blocks/action.js +38 -0
- package/dist/app/pages/blocks/block-props.js +5 -0
- package/dist/app/pages/blocks/card-frame.js +34 -0
- package/dist/app/pages/blocks/card-row.js +55 -0
- package/dist/app/pages/blocks/card.js +19 -0
- package/dist/app/pages/blocks/field.js +45 -0
- package/dist/app/pages/blocks/grid.js +34 -0
- package/dist/app/pages/blocks/link.js +27 -0
- package/dist/app/pages/blocks/modal-form.js +149 -0
- package/dist/app/pages/blocks/page-block.js +20 -0
- package/dist/app/pages/blocks/progress.js +54 -0
- package/dist/app/pages/blocks/record-card.js +56 -0
- package/dist/app/pages/blocks/show.js +43 -0
- package/dist/app/pages/blocks/stat.js +78 -0
- package/dist/app/pages/blocks/table.js +65 -0
- package/dist/app/pages/composed-page.js +19 -0
- package/dist/app/pages/confirm-action.js +20 -0
- package/dist/app/pages/field-value.js +58 -0
- package/dist/app/pages/project-ui.d.ts +30 -0
- package/dist/app/pages/project-ui.js +13 -0
- package/dist/app/pages/record-actions.js +40 -0
- package/dist/app/pages/record-scope.d.ts +16 -0
- package/dist/app/pages/record-scope.js +12 -0
- package/dist/app/pages/record-table.js +110 -0
- package/dist/app/pages/render-node.js +84 -0
- package/dist/app/pages/use-layout-data.js +73 -0
- package/dist/app/pages/use-path-value.js +65 -0
- package/dist/app/pages/use-relation-labels.js +30 -0
- package/dist/app/pages/use-run-action.js +88 -0
- package/dist/app/permissions.js +15 -0
- package/dist/app/record-lines.js +39 -0
- package/dist/app/record-page.js +318 -0
- package/dist/app/related-section.js +126 -0
- package/dist/app/relation-picker.js +69 -0
- package/dist/app/router.js +155 -0
- package/dist/app/search/search-model.js +70 -0
- package/dist/app/search/use-global-search.js +68 -0
- package/dist/app/sensitive-field.js +65 -0
- package/dist/app/use-lines-draft.js +45 -0
- package/dist/app/use-lines.js +19 -0
- package/dist/app/use-live-save.js +54 -0
- package/dist/app/use-nav-recent.js +41 -0
- package/dist/app/use-record-editor.js +154 -0
- package/dist/app/use-related-records.js +31 -0
- package/dist/app/user-menu-from-meta.js +36 -0
- package/dist/app-shell/app-shell.d.ts +4 -0
- package/dist/app-shell/app-shell.js +57 -0
- package/dist/app-shell/breadcrumb.js +37 -0
- package/dist/app-shell/drawer.js +30 -0
- package/dist/app-shell/global-search.d.ts +1 -0
- package/dist/app-shell/global-search.js +145 -0
- package/dist/app-shell/index.d.ts +9 -0
- package/dist/app-shell/logo.js +46 -0
- package/dist/app-shell/nav-open-storage.js +10 -0
- package/dist/app-shell/nav.d.ts +2 -0
- package/dist/app-shell/nav.js +85 -0
- package/dist/app-shell/page-header.d.ts +1 -0
- package/dist/app-shell/page-header.js +17 -0
- package/dist/app-shell/profile-menu.d.ts +2 -0
- package/dist/app-shell/profile-menu.js +115 -0
- package/dist/app-shell/sidebar-group.js +94 -0
- package/dist/app-shell/sidebar-item.js +46 -0
- package/dist/app-shell/sidebar-mode.d.ts +4 -0
- package/dist/app-shell/sidebar-mode.js +13 -0
- package/dist/app-shell/sidebar.d.ts +2 -0
- package/dist/app-shell/sidebar.js +64 -0
- package/dist/app-shell/status-dot.d.ts +1 -0
- package/dist/app-shell/status-dot.js +20 -0
- package/dist/app-shell/top-bar.js +82 -0
- package/dist/assistant-dock/assistant-dock.d.ts +15 -0
- package/dist/assistant-dock/assistant-dock.js +81 -0
- package/dist/assistant-dock/chat-message.js +9 -0
- package/dist/assistant-dock/composer.js +39 -0
- package/dist/assistant-dock/dock-item.d.ts +4 -0
- package/dist/assistant-dock/dock-item.js +37 -0
- package/dist/assistant-dock/model.d.ts +84 -0
- package/dist/assistant-dock/model.js +26 -0
- package/dist/assistant-dock/plan-card.d.ts +13 -0
- package/dist/assistant-dock/plan-card.js +133 -0
- package/dist/assistant-dock/progress-card.js +18 -0
- package/dist/assistant-dock/query-card.js +52 -0
- package/dist/assistant-dock/queue-card.js +31 -0
- package/dist/assistant-dock/result-card.js +77 -0
- package/dist/assistant-panel/content.d.ts +7 -0
- package/dist/assistant-panel/step-list.js +30 -0
- package/dist/auth/auth-gate.js +70 -0
- package/dist/auth/auth-layout.js +29 -0
- package/dist/auth/auth-provider.js +71 -0
- package/dist/auth/first-run-page.js +51 -0
- package/dist/auth/forgot-password-form.js +75 -0
- package/dist/auth/forgot-password-page.js +35 -0
- package/dist/auth/reset-link.js +29 -0
- package/dist/auth/reset-password-form.js +88 -0
- package/dist/auth/reset-password-page.js +43 -0
- package/dist/auth/sign-in-form.js +90 -0
- package/dist/auth/sign-in-page.js +39 -0
- package/dist/auth/user-to-shell.js +18 -0
- package/dist/data/api-provider.d.ts +54 -0
- package/dist/data/api-provider.js +53 -0
- package/dist/data/meta-data.d.ts +14 -0
- package/dist/data/meta-data.js +9 -0
- package/dist/data/query-keys.js +95 -0
- package/dist/data/use-aggregates.d.ts +8 -0
- package/dist/data/use-aggregates.js +43 -0
- package/dist/data/use-list.d.ts +231 -0
- package/dist/data/use-list.js +31 -0
- package/dist/data/use-meta.d.ts +8 -0
- package/dist/data/use-meta.js +35 -0
- package/dist/data/use-record.d.ts +6 -0
- package/dist/data/use-record.js +16 -0
- package/dist/data/use-update-record.d.ts +15 -0
- package/dist/data/use-update-record.js +26 -0
- package/dist/data-grid/cells.js +85 -0
- package/dist/data-grid/column-spec.js +4 -0
- package/dist/data-grid/columns.js +16 -0
- package/dist/data-grid/data-grid.js +238 -0
- package/dist/data-grid/geometry.js +32 -0
- package/dist/data-grid/grid-footer.js +33 -0
- package/dist/data-grid/grid-header.js +69 -0
- package/dist/data-grid/grid-row.js +83 -0
- package/dist/data-grid/grid-template.js +12 -0
- package/dist/data-grid/header-menu.js +52 -0
- package/dist/data-grid/page-cache.js +27 -0
- package/dist/data-grid/pinned-storage.js +13 -0
- package/dist/data-grid/row-menu.js +88 -0
- package/dist/data-grid/use-grid-scroll.js +80 -0
- package/dist/data-grid/use-horizontal-scroll.js +38 -0
- package/dist/data-grid/use-paged-rows.js +50 -0
- package/dist/data-grid/use-pinned-columns.js +16 -0
- package/dist/filter-panel/active-filter-chips.js +34 -0
- package/dist/filter-panel/date-presets.js +20 -0
- package/dist/filter-panel/facet-group.js +48 -0
- package/dist/filter-panel/filter-config.js +18 -0
- package/dist/filter-panel/filter-menu.js +41 -0
- package/dist/filter-panel/filter-panel.js +112 -0
- package/dist/filter-panel/filter-section.js +11 -0
- package/dist/filter-panel/filter-state.js +63 -0
- package/dist/filter-panel/range-filter.js +54 -0
- package/dist/filter-panel/range-math.js +11 -0
- package/dist/filter-panel/range-slider.css +32 -0
- package/dist/filter-panel/toggle-row.js +13 -0
- package/dist/filter-panel/use-filter-state.js +15 -0
- package/dist/format/date.js +37 -0
- package/dist/format/money.js +15 -0
- package/dist/format/number.js +12 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +11 -0
- package/dist/inline-child-table/columns.js +4 -0
- package/dist/inline-child-table/inline-child-table.js +120 -0
- package/dist/inline-child-table/line-menu.js +54 -0
- package/dist/inline-child-table/line-row.js +91 -0
- package/dist/inline-child-table/number-cell.js +25 -0
- package/dist/inline-child-table/reorder.js +10 -0
- package/dist/inline-child-table/totals-footer.js +42 -0
- package/dist/lib/cn.js +4 -0
- package/dist/lib/guarded.js +12 -0
- package/dist/lib/use-debounced.js +13 -0
- package/dist/lib/use-focus-trap.js +46 -0
- package/dist/lib/use-media-query.js +12 -0
- package/dist/live/columns-from-model.js +93 -0
- package/dist/live/display-value.js +24 -0
- package/dist/live/field-values.js +32 -0
- package/dist/live/filter-string.js +135 -0
- package/dist/live/grid-loader.js +63 -0
- package/dist/live/model-helpers.js +30 -0
- package/dist/live/naming.js +9 -0
- package/dist/live/relation-labels.js +53 -0
- package/dist/live/use-filter-config.js +88 -0
- package/dist/live/use-live-rows.js +45 -0
- package/dist/live/view-fields.js +35 -0
- package/dist/mocks/activity.js +24 -0
- package/dist/mocks/invoices.js +13 -0
- package/dist/mocks/products.js +167 -0
- package/dist/mocks/rng.js +20 -0
- package/dist/mocks/users.js +63 -0
- package/dist/primitives/avatar.js +22 -0
- package/dist/primitives/badge.d.ts +1 -0
- package/dist/primitives/badge.js +25 -0
- package/dist/primitives/button.js +23 -0
- package/dist/primitives/checkbox.js +21 -0
- package/dist/primitives/dialog.js +41 -0
- package/dist/primitives/input.js +16 -0
- package/dist/primitives/kbd.js +8 -0
- package/dist/primitives/segmented.js +14 -0
- package/dist/primitives/sheet.js +44 -0
- package/dist/primitives/skeleton.js +6 -0
- package/dist/primitives/spinner.js +10 -0
- package/dist/primitives/switch.js +14 -0
- package/dist/primitives/tooltip.js +13 -0
- package/dist/record-view/action-rail.js +28 -0
- package/dist/record-view/field.js +62 -0
- package/dist/record-view/record-header.js +50 -0
- package/dist/record-view/record-sidebar.js +58 -0
- package/dist/record-view/section.js +21 -0
- package/dist/styles.css +132 -0
- package/dist/toasts/toast-provider.js +69 -0
- package/dist/toasts/toast.js +45 -0
- package/package.json +45 -3
- package/README.md +0 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
//#region src/mocks/products.ts
|
|
2
|
+
const catalog = [
|
|
3
|
+
[
|
|
4
|
+
"M8 hex bolt A2, 100 pcs",
|
|
5
|
+
"FST",
|
|
6
|
+
1890
|
|
7
|
+
],
|
|
8
|
+
[
|
|
9
|
+
"M10 hex nut A2, 100 pcs",
|
|
10
|
+
"FST",
|
|
11
|
+
1240
|
|
12
|
+
],
|
|
13
|
+
[
|
|
14
|
+
"Stainless washer 8 mm, 250 pcs",
|
|
15
|
+
"FST",
|
|
16
|
+
990
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
"Cable tie 300 mm, 100 pcs",
|
|
20
|
+
"ELC",
|
|
21
|
+
650
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"Cable gland M20, 25 pcs",
|
|
25
|
+
"ELC",
|
|
26
|
+
2340
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
"Junction box IP65",
|
|
30
|
+
"ELC",
|
|
31
|
+
1480
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
"Hydraulic hose 1/2 in, 5 m",
|
|
35
|
+
"HYD",
|
|
36
|
+
8650
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
"Hydraulic coupling 3/8 in",
|
|
40
|
+
"HYD",
|
|
41
|
+
1895
|
|
42
|
+
],
|
|
43
|
+
[
|
|
44
|
+
"Pallet wrap 500 mm",
|
|
45
|
+
"PKG",
|
|
46
|
+
740
|
|
47
|
+
],
|
|
48
|
+
[
|
|
49
|
+
"Corrugated box 400 x 300",
|
|
50
|
+
"PKG",
|
|
51
|
+
215
|
|
52
|
+
],
|
|
53
|
+
[
|
|
54
|
+
"Bubble wrap roll 50 m",
|
|
55
|
+
"PKG",
|
|
56
|
+
1990
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
"Safety gloves, size L",
|
|
60
|
+
"PPE",
|
|
61
|
+
395
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
"Safety glasses, clear",
|
|
65
|
+
"PPE",
|
|
66
|
+
450
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
"Ear defenders SNR 31",
|
|
70
|
+
"PPE",
|
|
71
|
+
1295
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
"Ball bearing 6204-2RS",
|
|
75
|
+
"BRG",
|
|
76
|
+
685
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
"Taper roller bearing 30205",
|
|
80
|
+
"BRG",
|
|
81
|
+
1420
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
"Timing belt HTD 5M, 600 mm",
|
|
85
|
+
"BLT",
|
|
86
|
+
2780
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"V-belt SPA 1250",
|
|
90
|
+
"BLT",
|
|
91
|
+
1360
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"Grease cartridge, 400 g",
|
|
95
|
+
"LUB",
|
|
96
|
+
920
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
"Machine oil ISO 68, 5 l",
|
|
100
|
+
"LUB",
|
|
101
|
+
3950
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
"Angle grinder disc 125 mm, 10 pcs",
|
|
105
|
+
"ABR",
|
|
106
|
+
1880
|
|
107
|
+
],
|
|
108
|
+
[
|
|
109
|
+
"Drill bit set HSS, 19 pcs",
|
|
110
|
+
"TLS",
|
|
111
|
+
3490
|
|
112
|
+
],
|
|
113
|
+
[
|
|
114
|
+
"Torque wrench 20-100 Nm",
|
|
115
|
+
"TLS",
|
|
116
|
+
14900
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
"Pneumatic cylinder 40 x 100",
|
|
120
|
+
"PNE",
|
|
121
|
+
6120
|
|
122
|
+
],
|
|
123
|
+
[
|
|
124
|
+
"Air hose PU 8 mm, 20 m",
|
|
125
|
+
"PNE",
|
|
126
|
+
2475
|
|
127
|
+
],
|
|
128
|
+
[
|
|
129
|
+
"LED panel 600 x 600, 40 W",
|
|
130
|
+
"LGT",
|
|
131
|
+
3290
|
|
132
|
+
],
|
|
133
|
+
[
|
|
134
|
+
"Pallet label roll 100 x 150",
|
|
135
|
+
"LBL",
|
|
136
|
+
1175
|
|
137
|
+
],
|
|
138
|
+
[
|
|
139
|
+
"Thermal printer ribbon",
|
|
140
|
+
"LBL",
|
|
141
|
+
890
|
|
142
|
+
],
|
|
143
|
+
[
|
|
144
|
+
"Euro pallet EPAL, used",
|
|
145
|
+
"PLT",
|
|
146
|
+
1150
|
|
147
|
+
],
|
|
148
|
+
[
|
|
149
|
+
"Roll container, galvanised",
|
|
150
|
+
"PLT",
|
|
151
|
+
9800
|
|
152
|
+
]
|
|
153
|
+
];
|
|
154
|
+
catalog.length;
|
|
155
|
+
const productAt = (index) => {
|
|
156
|
+
const [name, category, priceCents] = catalog[index % catalog.length];
|
|
157
|
+
const slot = index % catalog.length;
|
|
158
|
+
return {
|
|
159
|
+
id: `P-${200 + slot}`,
|
|
160
|
+
sku: `${category}-${String(1e3 + slot * 7).padStart(4, "0")}`,
|
|
161
|
+
name,
|
|
162
|
+
category,
|
|
163
|
+
priceCents
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
//#endregion
|
|
167
|
+
export { productAt };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/mocks/rng.ts
|
|
2
|
+
const mockNow = Date.UTC(2026, 9, 6, 9, 41);
|
|
3
|
+
const hash = (seed, index) => {
|
|
4
|
+
let h = (seed ^ Math.imul(index + 2654435769, 2246822507)) >>> 0;
|
|
5
|
+
h = Math.imul(h ^ h >>> 16, 2246822507);
|
|
6
|
+
h = Math.imul(h ^ h >>> 13, 3266489909);
|
|
7
|
+
return (h ^ h >>> 16) >>> 0;
|
|
8
|
+
};
|
|
9
|
+
const rngAt = (seed, index) => {
|
|
10
|
+
let state = hash(seed, index);
|
|
11
|
+
return () => {
|
|
12
|
+
state = state + 1831565813 >>> 0;
|
|
13
|
+
let t = state;
|
|
14
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
15
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
16
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { hash, mockNow, rngAt };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/mocks/users.ts
|
|
2
|
+
const users = [
|
|
3
|
+
{
|
|
4
|
+
id: "u1",
|
|
5
|
+
name: "Sanne M.",
|
|
6
|
+
initials: "SM",
|
|
7
|
+
hue: 250,
|
|
8
|
+
role: "Admin"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "u2",
|
|
12
|
+
name: "Jonas K.",
|
|
13
|
+
initials: "JK",
|
|
14
|
+
hue: 160,
|
|
15
|
+
role: "Sales"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "u3",
|
|
19
|
+
name: "Lieke D.",
|
|
20
|
+
initials: "LD",
|
|
21
|
+
hue: 20,
|
|
22
|
+
role: "Finance"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "u4",
|
|
26
|
+
name: "Tobias R.",
|
|
27
|
+
initials: "TR",
|
|
28
|
+
hue: 310,
|
|
29
|
+
role: "Warehouse"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "u5",
|
|
33
|
+
name: "Imke V.",
|
|
34
|
+
initials: "IV",
|
|
35
|
+
hue: 200,
|
|
36
|
+
role: "Sales"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "u6",
|
|
40
|
+
name: "Marek W.",
|
|
41
|
+
initials: "MW",
|
|
42
|
+
hue: 80,
|
|
43
|
+
role: "Warehouse"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: "u7",
|
|
47
|
+
name: "Hanna B.",
|
|
48
|
+
initials: "HB",
|
|
49
|
+
hue: 350,
|
|
50
|
+
role: "Finance"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "u8",
|
|
54
|
+
name: "Daan P.",
|
|
55
|
+
initials: "DP",
|
|
56
|
+
hue: 120,
|
|
57
|
+
role: "Sales"
|
|
58
|
+
}
|
|
59
|
+
];
|
|
60
|
+
const userById = (id) => users.find((user) => user.id === id) ?? users[0];
|
|
61
|
+
users.filter((user) => user.role === "Sales");
|
|
62
|
+
//#endregion
|
|
63
|
+
export { userById, users };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/avatar.tsx
|
|
4
|
+
const sizes = {
|
|
5
|
+
xs: "size-5 text-[9px]",
|
|
6
|
+
sm: "size-6 text-[10px]",
|
|
7
|
+
md: "size-8 text-xs"
|
|
8
|
+
};
|
|
9
|
+
const Avatar = ({ initials, hue, size = "sm", className }) => /* @__PURE__ */ jsx("span", {
|
|
10
|
+
className: cn("inline-flex shrink-0 select-none items-center justify-center rounded-full font-semibold text-white ring-2 ring-background", sizes[size], className),
|
|
11
|
+
style: { background: `oklch(0.58 0.13 ${hue})` },
|
|
12
|
+
children: initials
|
|
13
|
+
});
|
|
14
|
+
const AvatarStack = ({ people, size = "sm" }) => /* @__PURE__ */ jsx("span", {
|
|
15
|
+
className: "inline-flex -space-x-1",
|
|
16
|
+
children: people.map((person) => /* @__PURE__ */ jsx(Avatar, {
|
|
17
|
+
...person,
|
|
18
|
+
size
|
|
19
|
+
}, person.initials))
|
|
20
|
+
});
|
|
21
|
+
//#endregion
|
|
22
|
+
export { Avatar, AvatarStack };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/badge.tsx
|
|
4
|
+
const tones = {
|
|
5
|
+
neutral: "bg-muted text-muted-foreground",
|
|
6
|
+
blue: "bg-primary-soft text-primary-text",
|
|
7
|
+
green: "bg-success-soft text-success-text",
|
|
8
|
+
amber: "bg-warning-soft text-warning-text",
|
|
9
|
+
red: "bg-danger-soft text-danger-text",
|
|
10
|
+
violet: "bg-violet-soft text-violet-text"
|
|
11
|
+
};
|
|
12
|
+
const dots = {
|
|
13
|
+
neutral: "bg-faint-foreground",
|
|
14
|
+
blue: "bg-primary",
|
|
15
|
+
green: "bg-success",
|
|
16
|
+
amber: "bg-warning",
|
|
17
|
+
red: "bg-danger",
|
|
18
|
+
violet: "bg-violet"
|
|
19
|
+
};
|
|
20
|
+
const Badge = ({ tone = "neutral", dot = true, children, className }) => /* @__PURE__ */ jsxs("span", {
|
|
21
|
+
className: cn("inline-flex h-5 items-center gap-1.5 rounded-full px-2 text-xs font-medium whitespace-nowrap", tones[tone], className),
|
|
22
|
+
children: [dot && /* @__PURE__ */ jsx("span", { className: cn("size-1.5 rounded-full", dots[tone]) }), children]
|
|
23
|
+
});
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Badge };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { Spinner } from "./spinner.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/primitives/button.tsx
|
|
5
|
+
const variants = {
|
|
6
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary-hover shadow-sm",
|
|
7
|
+
secondary: "bg-background text-foreground border border-border-strong hover:bg-muted shadow-sm",
|
|
8
|
+
ghost: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
9
|
+
danger: "bg-background text-danger-text border border-border-strong hover:bg-danger-soft shadow-sm"
|
|
10
|
+
};
|
|
11
|
+
const sizes = {
|
|
12
|
+
sm: "h-7 px-2.5 text-xs gap-1.5",
|
|
13
|
+
md: "h-8 px-3 text-[13px] gap-2"
|
|
14
|
+
};
|
|
15
|
+
const Button = ({ variant = "secondary", size = "md", loading, className, children, disabled, ...rest }) => /* @__PURE__ */ jsxs("button", {
|
|
16
|
+
type: "button",
|
|
17
|
+
disabled: disabled || loading,
|
|
18
|
+
className: cn("inline-flex shrink-0 items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:opacity-60 disabled:cursor-default", variants[variant], sizes[size], className),
|
|
19
|
+
...rest,
|
|
20
|
+
children: [loading && /* @__PURE__ */ jsx(Spinner, { className: "size-3.5" }), children]
|
|
21
|
+
});
|
|
22
|
+
//#endregion
|
|
23
|
+
export { Button };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Check, Minus } from "lucide-react";
|
|
4
|
+
//#region src/primitives/checkbox.tsx
|
|
5
|
+
const Checkbox = ({ checked, indeterminate, onChange, label, className }) => /* @__PURE__ */ jsx("button", {
|
|
6
|
+
type: "button",
|
|
7
|
+
role: "checkbox",
|
|
8
|
+
"aria-checked": indeterminate ? "mixed" : checked,
|
|
9
|
+
"aria-label": label,
|
|
10
|
+
onClick: () => onChange?.(!checked),
|
|
11
|
+
className: cn("relative inline-flex size-4 shrink-0 items-center justify-center rounded border transition-colors after:absolute after:-inset-1 after:content-[\"\"] pointer-coarse:after:-inset-3.5", checked || indeterminate ? "border-primary bg-primary text-primary-foreground" : "border-border-strong bg-background hover:border-faint-foreground", className),
|
|
12
|
+
children: indeterminate ? /* @__PURE__ */ jsx(Minus, {
|
|
13
|
+
className: "size-3",
|
|
14
|
+
strokeWidth: 3
|
|
15
|
+
}) : checked && /* @__PURE__ */ jsx(Check, {
|
|
16
|
+
className: "size-3",
|
|
17
|
+
strokeWidth: 3
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { Checkbox };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useFocusTrap } from "../lib/use-focus-trap.js";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/primitives/dialog.tsx
|
|
5
|
+
/** A modal with a title, optional body and action buttons. Closes on Escape and on a click outside. */
|
|
6
|
+
const Dialog = ({ title, description, onClose, actions, children }) => {
|
|
7
|
+
const panel = useRef(null);
|
|
8
|
+
useFocusTrap(panel, true, onClose);
|
|
9
|
+
return /* @__PURE__ */ jsx("div", {
|
|
10
|
+
className: "fixed inset-0 z-[90] flex items-center justify-center bg-foreground/30 p-4",
|
|
11
|
+
onMouseDown: (event) => event.target === event.currentTarget && onClose(),
|
|
12
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
13
|
+
ref: panel,
|
|
14
|
+
tabIndex: -1,
|
|
15
|
+
role: "dialog",
|
|
16
|
+
"aria-modal": "true",
|
|
17
|
+
"aria-label": title,
|
|
18
|
+
className: "outline-none max-h-[90vh] w-full max-w-md overflow-y-auto rounded-lg border bg-background p-4 shadow-pop sm:p-5",
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsx("h2", {
|
|
21
|
+
className: "text-[15px] font-semibold",
|
|
22
|
+
children: title
|
|
23
|
+
}),
|
|
24
|
+
description && /* @__PURE__ */ jsx("p", {
|
|
25
|
+
className: "mt-1.5 text-[13px] text-muted-foreground",
|
|
26
|
+
children: description
|
|
27
|
+
}),
|
|
28
|
+
children && /* @__PURE__ */ jsx("div", {
|
|
29
|
+
className: "mt-4",
|
|
30
|
+
children
|
|
31
|
+
}),
|
|
32
|
+
/* @__PURE__ */ jsx("div", {
|
|
33
|
+
className: "mt-5 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end [&>button]:min-h-11 sm:[&>button]:min-h-0",
|
|
34
|
+
children: actions
|
|
35
|
+
})
|
|
36
|
+
]
|
|
37
|
+
})
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { Dialog };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/input.tsx
|
|
4
|
+
const Input = ({ leading, trailing, invalid, className, wrapperClassName, ...rest }) => /* @__PURE__ */ jsxs("span", {
|
|
5
|
+
className: cn("flex h-8 items-center gap-2 rounded-md border bg-background px-2.5 text-muted-foreground shadow-sm focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20", invalid ? "border-danger" : "border-border-strong", wrapperClassName),
|
|
6
|
+
children: [
|
|
7
|
+
leading,
|
|
8
|
+
/* @__PURE__ */ jsx("input", {
|
|
9
|
+
className: cn("min-w-0 flex-1 bg-transparent text-[13px] text-foreground outline-none placeholder:text-faint-foreground", className),
|
|
10
|
+
...rest
|
|
11
|
+
}),
|
|
12
|
+
trailing
|
|
13
|
+
]
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { Input };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
//#region src/primitives/kbd.tsx
|
|
3
|
+
const Kbd = ({ children }) => /* @__PURE__ */ jsx("kbd", {
|
|
4
|
+
className: "inline-flex h-5 items-center rounded border border-border-strong bg-background px-1.5 font-sans text-[11px] font-medium text-muted-foreground",
|
|
5
|
+
children
|
|
6
|
+
});
|
|
7
|
+
//#endregion
|
|
8
|
+
export { Kbd };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/segmented.tsx
|
|
4
|
+
const Segmented = ({ value, options, onChange }) => /* @__PURE__ */ jsx("span", {
|
|
5
|
+
className: "inline-flex h-7 rounded-md bg-muted p-0.5",
|
|
6
|
+
children: options.map((option) => /* @__PURE__ */ jsx("button", {
|
|
7
|
+
type: "button",
|
|
8
|
+
onClick: () => onChange(option.value),
|
|
9
|
+
className: cn("rounded px-2.5 text-xs font-medium transition-colors", option.value === value ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"),
|
|
10
|
+
children: option.label
|
|
11
|
+
}, option.value))
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Segmented };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useFocusTrap } from "../lib/use-focus-trap.js";
|
|
2
|
+
import { useRef } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
//#region src/primitives/sheet.tsx
|
|
6
|
+
/** A panel that rises from the bottom of the screen, for the filter panel on phones. */
|
|
7
|
+
const Sheet = ({ title, open, onClose, children }) => {
|
|
8
|
+
const panel = useRef(null);
|
|
9
|
+
useFocusTrap(panel, open, onClose);
|
|
10
|
+
if (!open) return null;
|
|
11
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
12
|
+
className: "fixed inset-0 z-[80]",
|
|
13
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
14
|
+
className: "absolute inset-0 bg-foreground/40",
|
|
15
|
+
onClick: onClose,
|
|
16
|
+
"aria-hidden": true
|
|
17
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
18
|
+
ref: panel,
|
|
19
|
+
role: "dialog",
|
|
20
|
+
"aria-modal": "true",
|
|
21
|
+
"aria-label": title,
|
|
22
|
+
tabIndex: -1,
|
|
23
|
+
className: "absolute inset-x-0 bottom-0 flex max-h-[85vh] flex-col rounded-t-xl border-t bg-background shadow-pop outline-none",
|
|
24
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
25
|
+
className: "flex shrink-0 items-center justify-between border-b px-4",
|
|
26
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
27
|
+
className: "text-[15px] font-semibold",
|
|
28
|
+
children: title
|
|
29
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
30
|
+
type: "button",
|
|
31
|
+
"aria-label": "Close",
|
|
32
|
+
onClick: onClose,
|
|
33
|
+
className: "-mr-2 inline-flex size-11 items-center justify-center rounded-md text-muted-foreground hover:bg-muted",
|
|
34
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-5" })
|
|
35
|
+
})]
|
|
36
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
37
|
+
className: "min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 pb-6",
|
|
38
|
+
children
|
|
39
|
+
})]
|
|
40
|
+
})]
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
44
|
+
export { Sheet };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/skeleton.tsx
|
|
4
|
+
const Skeleton = ({ className }) => /* @__PURE__ */ jsx("span", { className: cn("block h-3 rounded bg-muted [animation:pb-shimmer_1.4s_ease-in-out_infinite]", className) });
|
|
5
|
+
//#endregion
|
|
6
|
+
export { Skeleton };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Loader2 } from "lucide-react";
|
|
4
|
+
//#region src/primitives/spinner.tsx
|
|
5
|
+
const Spinner = ({ className }) => /* @__PURE__ */ jsx(Loader2, {
|
|
6
|
+
"aria-label": "Loading",
|
|
7
|
+
className: cn("size-4 animate-spin", className)
|
|
8
|
+
});
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Spinner };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/switch.tsx
|
|
4
|
+
const Switch = ({ checked, onChange, label }) => /* @__PURE__ */ jsx("button", {
|
|
5
|
+
type: "button",
|
|
6
|
+
role: "switch",
|
|
7
|
+
"aria-checked": checked,
|
|
8
|
+
"aria-label": label,
|
|
9
|
+
onClick: () => onChange?.(!checked),
|
|
10
|
+
className: cn("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors", checked ? "bg-primary" : "bg-border-strong"),
|
|
11
|
+
children: /* @__PURE__ */ jsx("span", { className: cn("size-4 rounded-full bg-white shadow transition-transform", checked ? "translate-x-[18px]" : "translate-x-0.5") })
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Switch };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/tooltip.tsx
|
|
4
|
+
const Tooltip = ({ label, side = "right", visible, children }) => /* @__PURE__ */ jsxs("span", {
|
|
5
|
+
className: "group/tooltip relative inline-flex",
|
|
6
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
7
|
+
role: "tooltip",
|
|
8
|
+
className: cn("pointer-events-none absolute z-50 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background opacity-0 shadow-md transition-opacity group-hover/tooltip:opacity-100", visible && "opacity-100", side === "right" && "left-full top-1/2 ml-2 -translate-y-1/2", side === "top" && "bottom-full left-1/2 mb-2 -translate-x-1/2", side === "bottom" && "left-1/2 top-full mt-2 -translate-x-1/2"),
|
|
9
|
+
children: label
|
|
10
|
+
})]
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
export { Tooltip };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
//#region src/record-view/action-rail.tsx
|
|
3
|
+
/** Named actions with one-line descriptions; on phones a row of buttons, from `md` a list. */
|
|
4
|
+
const ActionRail = ({ actions }) => /* @__PURE__ */ jsxs("div", {
|
|
5
|
+
className: "overflow-hidden rounded-lg border border-border bg-background",
|
|
6
|
+
children: [/* @__PURE__ */ jsx("h3", {
|
|
7
|
+
className: "hidden border-b border-border px-4 py-2.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground md:block",
|
|
8
|
+
children: "Actions"
|
|
9
|
+
}), /* @__PURE__ */ jsx("ul", {
|
|
10
|
+
className: "flex flex-wrap gap-2 p-2 md:block md:divide-y md:divide-border md:p-0",
|
|
11
|
+
children: actions.map(({ name, description, icon: Icon, onRun, blocked, running }) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("button", {
|
|
12
|
+
type: "button",
|
|
13
|
+
onClick: onRun,
|
|
14
|
+
disabled: Boolean(blocked) || running,
|
|
15
|
+
title: blocked,
|
|
16
|
+
className: "flex min-h-11 items-center gap-2 rounded-md border border-border-strong px-3 text-left hover:bg-surface disabled:cursor-default disabled:opacity-60 md:min-h-0 md:w-full md:items-start md:gap-3 md:rounded-none md:border-0 md:px-4 md:py-3",
|
|
17
|
+
children: [/* @__PURE__ */ jsx(Icon, { className: "size-4 shrink-0 text-muted-foreground md:mt-0.5" }), /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsx("span", {
|
|
18
|
+
className: "block text-[13px] font-medium text-foreground",
|
|
19
|
+
children: name
|
|
20
|
+
}), description && /* @__PURE__ */ jsx("span", {
|
|
21
|
+
className: "mt-0.5 hidden text-xs leading-snug text-muted-foreground md:block",
|
|
22
|
+
children: description
|
|
23
|
+
})] })]
|
|
24
|
+
}) }, name))
|
|
25
|
+
})]
|
|
26
|
+
});
|
|
27
|
+
//#endregion
|
|
28
|
+
export { ActionRail };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Avatar } from "../primitives/avatar.js";
|
|
2
|
+
import { Button } from "../primitives/button.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { TriangleAlert } from "lucide-react";
|
|
5
|
+
//#region src/record-view/field.tsx
|
|
6
|
+
const Field = ({ label, required, help, example, error, conflict, children }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
7
|
+
/* @__PURE__ */ jsxs("div", {
|
|
8
|
+
className: "mb-1.5 flex items-center gap-2",
|
|
9
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
10
|
+
className: "text-[13px] font-medium text-foreground",
|
|
11
|
+
children: [label, required && /* @__PURE__ */ jsx("span", {
|
|
12
|
+
className: "ml-0.5 text-danger-text",
|
|
13
|
+
"aria-label": "required",
|
|
14
|
+
children: "*"
|
|
15
|
+
})]
|
|
16
|
+
}), conflict && /* @__PURE__ */ jsxs("span", {
|
|
17
|
+
className: "inline-flex items-center gap-1.5 text-warning-text",
|
|
18
|
+
title: `${conflict.user.name} saved a different value`,
|
|
19
|
+
children: [/* @__PURE__ */ jsx(TriangleAlert, { className: "size-3.5" }), /* @__PURE__ */ jsx(Avatar, {
|
|
20
|
+
initials: conflict.user.initials,
|
|
21
|
+
hue: conflict.user.hue,
|
|
22
|
+
size: "xs"
|
|
23
|
+
})]
|
|
24
|
+
})]
|
|
25
|
+
}),
|
|
26
|
+
children,
|
|
27
|
+
help && /* @__PURE__ */ jsx("p", {
|
|
28
|
+
className: "mt-1.5 text-xs text-muted-foreground",
|
|
29
|
+
children: help
|
|
30
|
+
}),
|
|
31
|
+
example && /* @__PURE__ */ jsxs("p", {
|
|
32
|
+
className: "mt-0.5 text-xs text-faint-foreground",
|
|
33
|
+
children: ["Example: ", example]
|
|
34
|
+
}),
|
|
35
|
+
error && /* @__PURE__ */ jsx("p", {
|
|
36
|
+
className: "mt-1.5 text-xs font-medium text-danger-text",
|
|
37
|
+
children: error
|
|
38
|
+
}),
|
|
39
|
+
conflict && /* @__PURE__ */ jsxs("div", {
|
|
40
|
+
className: "mt-2 flex items-center justify-between gap-3 rounded-md border border-warning/30 bg-warning-soft px-3 py-2 text-xs text-warning-text",
|
|
41
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
42
|
+
/* @__PURE__ */ jsx("strong", { children: conflict.user.name }),
|
|
43
|
+
" saved this ",
|
|
44
|
+
conflict.ago,
|
|
45
|
+
": ",
|
|
46
|
+
/* @__PURE__ */ jsx("strong", { children: conflict.theirValue })
|
|
47
|
+
] }), /* @__PURE__ */ jsxs("span", {
|
|
48
|
+
className: "flex gap-1.5",
|
|
49
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
50
|
+
size: "sm",
|
|
51
|
+
onClick: conflict.onKeepMine,
|
|
52
|
+
children: "Keep mine"
|
|
53
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
54
|
+
size: "sm",
|
|
55
|
+
onClick: conflict.onUseTheirs,
|
|
56
|
+
children: "Use theirs"
|
|
57
|
+
})]
|
|
58
|
+
})]
|
|
59
|
+
})
|
|
60
|
+
] });
|
|
61
|
+
//#endregion
|
|
62
|
+
export { Field };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AvatarStack } from "../primitives/avatar.js";
|
|
2
|
+
import { Badge } from "../primitives/badge.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/record-view/record-header.tsx
|
|
5
|
+
const RecordHeader = ({ collection, title, status, lastEditedBy, presence = [], actions }) => /* @__PURE__ */ jsxs("header", {
|
|
6
|
+
className: "flex flex-col gap-4 border-b border-border pb-6 md:flex-row md:items-end md:justify-between md:gap-6",
|
|
7
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [
|
|
8
|
+
/* @__PURE__ */ jsx("p", {
|
|
9
|
+
className: "text-xs text-muted-foreground",
|
|
10
|
+
children: collection
|
|
11
|
+
}),
|
|
12
|
+
/* @__PURE__ */ jsxs("div", {
|
|
13
|
+
className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-1",
|
|
14
|
+
children: [/* @__PURE__ */ jsx("h1", {
|
|
15
|
+
className: "text-xl font-semibold tracking-tight text-foreground md:text-2xl",
|
|
16
|
+
children: title
|
|
17
|
+
}), /* @__PURE__ */ jsx(Badge, {
|
|
18
|
+
tone: status.tone,
|
|
19
|
+
children: status.label
|
|
20
|
+
})]
|
|
21
|
+
}),
|
|
22
|
+
/* @__PURE__ */ jsx("p", {
|
|
23
|
+
className: "mt-1.5 text-xs text-muted-foreground",
|
|
24
|
+
children: lastEditedBy.name ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
"Last edited by ",
|
|
26
|
+
/* @__PURE__ */ jsx("span", {
|
|
27
|
+
className: "font-medium text-foreground",
|
|
28
|
+
children: lastEditedBy.name
|
|
29
|
+
}),
|
|
30
|
+
" ",
|
|
31
|
+
lastEditedBy.ago
|
|
32
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: ["Last updated ", lastEditedBy.ago] })
|
|
33
|
+
})
|
|
34
|
+
] }), /* @__PURE__ */ jsxs("div", {
|
|
35
|
+
className: "flex items-center gap-4",
|
|
36
|
+
children: [presence.length > 0 && /* @__PURE__ */ jsxs("span", {
|
|
37
|
+
className: "flex items-center gap-2 text-xs text-muted-foreground",
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ jsx(AvatarStack, {
|
|
40
|
+
people: presence,
|
|
41
|
+
size: "md"
|
|
42
|
+
}),
|
|
43
|
+
presence.length,
|
|
44
|
+
" viewing"
|
|
45
|
+
]
|
|
46
|
+
}), actions]
|
|
47
|
+
})]
|
|
48
|
+
});
|
|
49
|
+
//#endregion
|
|
50
|
+
export { RecordHeader };
|