@groveback/ui 0.1.0 → 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/dist/RelationSelect.d.ts +1 -1
- package/dist/components.d.ts +2 -2
- package/dist/index.d.ts +19 -6
- package/dist/index.js +123 -123
- package/dist/runtime.d.ts +1 -1
- package/package.json +4 -3
package/dist/RelationSelect.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* is truncated rather than silently hiding matches — the data API's find has no text search
|
|
12
12
|
* to delegate to, so pretending otherwise would be worse than saying so.
|
|
13
13
|
*/
|
|
14
|
-
import type { CollectionLike } from './types
|
|
14
|
+
import type { CollectionLike } from './types';
|
|
15
15
|
export interface RelationSelectProps {
|
|
16
16
|
/** The collection the relation points at. */
|
|
17
17
|
collection: CollectionLike;
|
package/dist/components.d.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* could not reach by hand.
|
|
8
8
|
*/
|
|
9
9
|
import type { ReactNode } from 'react';
|
|
10
|
-
import type { ButtonVariant, CollectionLike, Doc, FieldRef, RowAction } from './types
|
|
11
|
-
import type { Action } from './types
|
|
10
|
+
import type { ButtonVariant, CollectionLike, Doc, FieldRef, RowAction } from './types';
|
|
11
|
+
import type { Action } from './types';
|
|
12
12
|
declare const WIDTH: {
|
|
13
13
|
full: string;
|
|
14
14
|
container: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Public surface of @groveback/ui.
|
|
3
|
+
*
|
|
4
|
+
* Values are IMPORTED and then re-exported rather than written as `export { x } from './y'`.
|
|
5
|
+
* That is verbose on purpose: it keeps the entry point's dependency on each module explicit,
|
|
6
|
+
* which is what a bundler's tree-shaker reasons about. See the `sideEffects` note in
|
|
7
|
+
* package.json for the failure this class of ambiguity produced.
|
|
8
|
+
*
|
|
9
|
+
* Relative specifiers here are extensionless rather than the repo's `./foo.js` ESM
|
|
10
|
+
* convention — this package is browser React bundled by `bun build`, not backend ESM, so the
|
|
11
|
+
* convention buys nothing and extensionless is what every bundler agrees on.
|
|
12
|
+
*/
|
|
13
|
+
import { Button, DataTable, Detail, Divider, Form, Grid, Heading, Stack, Text } from './components';
|
|
14
|
+
import { RelationSelect } from './RelationSelect';
|
|
15
|
+
import { GroveUiProvider, formatValue, resolveHref, useAction, useGroveUi } from './runtime';
|
|
16
|
+
export { Button, DataTable, Detail, Divider, Form, Grid, GroveUiProvider, Heading, RelationSelect, Stack, Text, formatValue, resolveHref, useAction, useGroveUi, };
|
|
17
|
+
export type { RelationSelectProps } from './RelationSelect';
|
|
18
|
+
export type { GroveUiContext } from './runtime';
|
|
19
|
+
export type { Action, ButtonVariant, CollectionLike, Doc, FieldRef, Format, RowAction } from './types';
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { useCallback as useCallback2, useEffect as useEffect2, useState as useSt
|
|
|
3
3
|
|
|
4
4
|
// src/RelationSelect.tsx
|
|
5
5
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
-
import {
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
var PAGE = 200;
|
|
8
8
|
function labelOf(doc, field) {
|
|
9
9
|
const raw = doc[field];
|
|
@@ -55,47 +55,47 @@ function RelationSelect(props) {
|
|
|
55
55
|
return all.filter((d) => labelOf(d, displayField).toLowerCase().includes(q));
|
|
56
56
|
}, [docs, displayField, query]);
|
|
57
57
|
if (error) {
|
|
58
|
-
return /* @__PURE__ */
|
|
58
|
+
return /* @__PURE__ */ jsx("p", {
|
|
59
59
|
className: "text-xs text-red-600",
|
|
60
60
|
children: error
|
|
61
|
-
}
|
|
61
|
+
});
|
|
62
62
|
}
|
|
63
63
|
const buttonLabel = selected ? labelOf(selected, displayField) : value !== "" ? value : placeholder ?? "Select…";
|
|
64
|
-
return /* @__PURE__ */
|
|
64
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
65
65
|
ref: boxRef,
|
|
66
66
|
className: "relative",
|
|
67
67
|
children: [
|
|
68
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ jsxs("button", {
|
|
69
69
|
type: "button",
|
|
70
70
|
onClick: () => setOpen((v) => !v),
|
|
71
71
|
className: "flex w-full items-center justify-between rounded-md border border-neutral-300 px-3 py-1.5 text-left text-sm dark:border-neutral-700 dark:bg-neutral-950",
|
|
72
72
|
children: [
|
|
73
|
-
/* @__PURE__ */
|
|
73
|
+
/* @__PURE__ */ jsx("span", {
|
|
74
74
|
className: selected || value ? "" : "text-neutral-400",
|
|
75
75
|
children: docs === null ? "Loading…" : buttonLabel
|
|
76
|
-
}
|
|
77
|
-
/* @__PURE__ */
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ jsx("span", {
|
|
78
78
|
"aria-hidden": true,
|
|
79
79
|
className: "ml-2 text-neutral-400",
|
|
80
80
|
children: "▾"
|
|
81
|
-
}
|
|
81
|
+
})
|
|
82
82
|
]
|
|
83
|
-
}
|
|
84
|
-
open ? /* @__PURE__ */
|
|
83
|
+
}),
|
|
84
|
+
open ? /* @__PURE__ */ jsxs("div", {
|
|
85
85
|
className: "absolute z-20 mt-1 w-full rounded-md border border-neutral-200 bg-white shadow-lg dark:border-neutral-800 dark:bg-neutral-950",
|
|
86
86
|
children: [
|
|
87
|
-
/* @__PURE__ */
|
|
87
|
+
/* @__PURE__ */ jsx("input", {
|
|
88
88
|
autoFocus: true,
|
|
89
89
|
value: query,
|
|
90
90
|
onChange: (e) => setQuery(e.target.value),
|
|
91
91
|
placeholder: "Search…",
|
|
92
92
|
className: "w-full rounded-t-md border-b border-neutral-200 px-3 py-1.5 text-sm outline-none dark:border-neutral-800 dark:bg-neutral-950"
|
|
93
|
-
}
|
|
94
|
-
/* @__PURE__ */
|
|
93
|
+
}),
|
|
94
|
+
/* @__PURE__ */ jsxs("ul", {
|
|
95
95
|
className: "max-h-56 overflow-auto py-1",
|
|
96
96
|
children: [
|
|
97
|
-
!required ? /* @__PURE__ */
|
|
98
|
-
children: /* @__PURE__ */
|
|
97
|
+
!required ? /* @__PURE__ */ jsx("li", {
|
|
98
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
99
99
|
type: "button",
|
|
100
100
|
onClick: () => {
|
|
101
101
|
onChange("");
|
|
@@ -104,15 +104,15 @@ function RelationSelect(props) {
|
|
|
104
104
|
},
|
|
105
105
|
className: "w-full px-3 py-1.5 text-left text-sm text-neutral-500 hover:bg-neutral-100 dark:hover:bg-neutral-900",
|
|
106
106
|
children: "None"
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
matches.length === 0 ? /* @__PURE__ */
|
|
107
|
+
})
|
|
108
|
+
}) : null,
|
|
109
|
+
matches.length === 0 ? /* @__PURE__ */ jsx("li", {
|
|
110
110
|
className: "px-3 py-2 text-xs text-neutral-500",
|
|
111
111
|
children: "No matches."
|
|
112
|
-
}
|
|
112
|
+
}) : matches.map((doc) => {
|
|
113
113
|
const id = typeof doc["id"] === "string" ? doc["id"] : "";
|
|
114
|
-
return /* @__PURE__ */
|
|
115
|
-
children: /* @__PURE__ */
|
|
114
|
+
return /* @__PURE__ */ jsx("li", {
|
|
115
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
116
116
|
type: "button",
|
|
117
117
|
onClick: () => {
|
|
118
118
|
onChange(id);
|
|
@@ -121,34 +121,34 @@ function RelationSelect(props) {
|
|
|
121
121
|
},
|
|
122
122
|
className: `w-full px-3 py-1.5 text-left text-sm hover:bg-neutral-100 dark:hover:bg-neutral-900 ${id === value ? "font-medium" : ""}`,
|
|
123
123
|
children: labelOf(doc, displayField)
|
|
124
|
-
}
|
|
125
|
-
}, id
|
|
124
|
+
})
|
|
125
|
+
}, id);
|
|
126
126
|
})
|
|
127
127
|
]
|
|
128
|
-
}
|
|
129
|
-
truncated ? /* @__PURE__ */
|
|
128
|
+
}),
|
|
129
|
+
truncated ? /* @__PURE__ */ jsxs("p", {
|
|
130
130
|
className: "border-t border-neutral-200 px-3 py-1.5 text-[11px] text-neutral-500 dark:border-neutral-800",
|
|
131
131
|
children: [
|
|
132
132
|
"Showing the first ",
|
|
133
133
|
PAGE,
|
|
134
134
|
". Type to filter within them."
|
|
135
135
|
]
|
|
136
|
-
}
|
|
136
|
+
}) : null
|
|
137
137
|
]
|
|
138
|
-
}
|
|
138
|
+
}) : null
|
|
139
139
|
]
|
|
140
|
-
}
|
|
140
|
+
});
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
// src/runtime.tsx
|
|
144
144
|
import { createContext, useContext, useCallback } from "react";
|
|
145
|
-
import {
|
|
145
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
146
146
|
var Ctx = createContext(null);
|
|
147
147
|
function GroveUiProvider({ value, children }) {
|
|
148
|
-
return /* @__PURE__ */
|
|
148
|
+
return /* @__PURE__ */ jsx2(Ctx.Provider, {
|
|
149
149
|
value,
|
|
150
150
|
children
|
|
151
|
-
}
|
|
151
|
+
});
|
|
152
152
|
}
|
|
153
153
|
var DEFAULT_CONTEXT = {
|
|
154
154
|
navigate: (href) => {
|
|
@@ -225,7 +225,7 @@ function formatValue(value, format, opts = {}) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// src/components.tsx
|
|
228
|
-
import {
|
|
228
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
229
229
|
var GAP = ["gap-0", "gap-1", "gap-2", "gap-3", "gap-4", "gap-5", "gap-6", "gap-7", "gap-8", "gap-9", "gap-10", "gap-11", "gap-12"];
|
|
230
230
|
var COLUMNS = ["", "md:grid-cols-1", "md:grid-cols-2", "md:grid-cols-3", "md:grid-cols-4", "md:grid-cols-5", "md:grid-cols-6"];
|
|
231
231
|
var WIDTH = { full: "w-full", container: "w-full max-w-5xl mx-auto", narrow: "w-full max-w-xl mx-auto" };
|
|
@@ -236,36 +236,36 @@ function cx(...parts) {
|
|
|
236
236
|
}
|
|
237
237
|
function Stack(props) {
|
|
238
238
|
const { direction = "vertical", gap = 4, align, justify, width, wrap, children } = props;
|
|
239
|
-
return /* @__PURE__ */
|
|
239
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
240
240
|
className: cx("flex", direction === "vertical" ? "flex-col" : "flex-row", GAP[Math.min(Math.max(gap, 0), 12)], align && ALIGN[align], justify && JUSTIFY[justify], width && WIDTH[width], wrap && "flex-wrap"),
|
|
241
241
|
children
|
|
242
|
-
}
|
|
242
|
+
});
|
|
243
243
|
}
|
|
244
244
|
function Grid(props) {
|
|
245
245
|
const { columns, gap = 4, width, children } = props;
|
|
246
|
-
return /* @__PURE__ */
|
|
246
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
247
247
|
className: cx("grid grid-cols-1", COLUMNS[Math.min(Math.max(columns, 1), 6)], GAP[Math.min(Math.max(gap, 0), 12)], width && WIDTH[width]),
|
|
248
248
|
children
|
|
249
|
-
}
|
|
249
|
+
});
|
|
250
250
|
}
|
|
251
251
|
function Divider() {
|
|
252
|
-
return /* @__PURE__ */
|
|
252
|
+
return /* @__PURE__ */ jsx3("hr", {
|
|
253
253
|
className: "border-t border-neutral-200 dark:border-neutral-800"
|
|
254
|
-
}
|
|
254
|
+
});
|
|
255
255
|
}
|
|
256
256
|
var HEADING = { 1: "text-3xl font-semibold tracking-tight", 2: "text-2xl font-semibold", 3: "text-lg font-medium" };
|
|
257
257
|
function Heading({ level = 1, children }) {
|
|
258
258
|
const Tag = ["h1", "h2", "h3"][level - 1] ?? "h1";
|
|
259
|
-
return /* @__PURE__ */
|
|
259
|
+
return /* @__PURE__ */ jsx3(Tag, {
|
|
260
260
|
className: HEADING[level],
|
|
261
261
|
children
|
|
262
|
-
}
|
|
262
|
+
});
|
|
263
263
|
}
|
|
264
264
|
function Text({ muted, children }) {
|
|
265
|
-
return /* @__PURE__ */
|
|
265
|
+
return /* @__PURE__ */ jsx3("p", {
|
|
266
266
|
className: muted ? "text-sm text-neutral-500" : "text-sm",
|
|
267
267
|
children
|
|
268
|
-
}
|
|
268
|
+
});
|
|
269
269
|
}
|
|
270
270
|
var VARIANT = {
|
|
271
271
|
default: "bg-neutral-900 text-white hover:bg-neutral-800 dark:bg-white dark:text-neutral-900",
|
|
@@ -283,13 +283,13 @@ function Button(props) {
|
|
|
283
283
|
setBusy(true);
|
|
284
284
|
dispatch(action, { doc, collection, onDone }).finally(() => setBusy(false));
|
|
285
285
|
}, [action, dispatch, doc, collection, onDone]);
|
|
286
|
-
return /* @__PURE__ */
|
|
286
|
+
return /* @__PURE__ */ jsx3("button", {
|
|
287
287
|
type: "button",
|
|
288
288
|
disabled: busy,
|
|
289
289
|
onClick,
|
|
290
290
|
className: cx(BUTTON_BASE, VARIANT[variant]),
|
|
291
291
|
children
|
|
292
|
-
}
|
|
292
|
+
});
|
|
293
293
|
}
|
|
294
294
|
function useAsync(load, deps) {
|
|
295
295
|
const [data, setData] = useState2(null);
|
|
@@ -318,10 +318,10 @@ function useAsync(load, deps) {
|
|
|
318
318
|
}
|
|
319
319
|
function Message({ tone, children }) {
|
|
320
320
|
const cls = tone === "error" ? "text-sm text-red-600" : "text-sm text-neutral-500";
|
|
321
|
-
return /* @__PURE__ */
|
|
321
|
+
return /* @__PURE__ */ jsx3("p", {
|
|
322
322
|
className: cls,
|
|
323
323
|
children
|
|
324
|
-
}
|
|
324
|
+
});
|
|
325
325
|
}
|
|
326
326
|
function useRelations(fields, docs, resolveCollection) {
|
|
327
327
|
const [map, setMap] = useState2({});
|
|
@@ -367,69 +367,69 @@ function DataTable(props) {
|
|
|
367
367
|
const { data, error, loading, reload } = useAsync(() => collection.find(options), [JSON.stringify(options)]);
|
|
368
368
|
const relations = useRelations(columns, data, resolveRelation);
|
|
369
369
|
if (loading)
|
|
370
|
-
return /* @__PURE__ */
|
|
370
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
371
371
|
tone: "muted",
|
|
372
372
|
children: "Loading…"
|
|
373
|
-
}
|
|
373
|
+
});
|
|
374
374
|
if (error)
|
|
375
|
-
return /* @__PURE__ */
|
|
375
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
376
376
|
tone: "error",
|
|
377
377
|
children: error
|
|
378
|
-
}
|
|
378
|
+
});
|
|
379
379
|
if (!data || data.length === 0)
|
|
380
|
-
return /* @__PURE__ */
|
|
380
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
381
381
|
tone: "muted",
|
|
382
382
|
children: emptyText ?? "Nothing here yet."
|
|
383
|
-
}
|
|
384
|
-
return /* @__PURE__ */
|
|
383
|
+
});
|
|
384
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
385
385
|
className: "w-full overflow-x-auto rounded-md border border-neutral-200 dark:border-neutral-800",
|
|
386
|
-
children: /* @__PURE__ */
|
|
386
|
+
children: /* @__PURE__ */ jsxs2("table", {
|
|
387
387
|
className: "w-full text-sm",
|
|
388
388
|
children: [
|
|
389
|
-
/* @__PURE__ */
|
|
389
|
+
/* @__PURE__ */ jsx3("thead", {
|
|
390
390
|
className: "bg-neutral-50 dark:bg-neutral-900",
|
|
391
|
-
children: /* @__PURE__ */
|
|
391
|
+
children: /* @__PURE__ */ jsxs2("tr", {
|
|
392
392
|
children: [
|
|
393
|
-
columns.map((c) => /* @__PURE__ */
|
|
393
|
+
columns.map((c) => /* @__PURE__ */ jsx3("th", {
|
|
394
394
|
className: "px-3 py-2 text-left font-medium text-neutral-600 dark:text-neutral-400",
|
|
395
395
|
children: c.label
|
|
396
|
-
}, c.field
|
|
397
|
-
rowActions && rowActions.length > 0 ? /* @__PURE__ */
|
|
396
|
+
}, c.field)),
|
|
397
|
+
rowActions && rowActions.length > 0 ? /* @__PURE__ */ jsx3("th", {
|
|
398
398
|
className: "px-3 py-2"
|
|
399
|
-
}
|
|
399
|
+
}) : null
|
|
400
400
|
]
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
/* @__PURE__ */
|
|
404
|
-
children: data.map((doc, i) => /* @__PURE__ */
|
|
401
|
+
})
|
|
402
|
+
}),
|
|
403
|
+
/* @__PURE__ */ jsx3("tbody", {
|
|
404
|
+
children: data.map((doc, i) => /* @__PURE__ */ jsxs2("tr", {
|
|
405
405
|
className: cx("border-t border-neutral-200 dark:border-neutral-800", rowHref && "cursor-pointer hover:bg-neutral-50 dark:hover:bg-neutral-900"),
|
|
406
406
|
onClick: rowHref ? () => ctx.navigate(resolveHref(rowHref, doc)) : undefined,
|
|
407
407
|
children: [
|
|
408
|
-
columns.map((c) => /* @__PURE__ */
|
|
408
|
+
columns.map((c) => /* @__PURE__ */ jsx3("td", {
|
|
409
409
|
className: "px-3 py-2",
|
|
410
410
|
children: cellValue(doc, c, relations, ctx.locale, ctx.currency)
|
|
411
|
-
}, c.field
|
|
412
|
-
rowActions && rowActions.length > 0 ? /* @__PURE__ */
|
|
411
|
+
}, c.field)),
|
|
412
|
+
rowActions && rowActions.length > 0 ? /* @__PURE__ */ jsx3("td", {
|
|
413
413
|
className: "px-3 py-2",
|
|
414
|
-
children: /* @__PURE__ */
|
|
414
|
+
children: /* @__PURE__ */ jsx3("div", {
|
|
415
415
|
className: "flex justify-end gap-2",
|
|
416
416
|
onClick: (e) => e.stopPropagation(),
|
|
417
|
-
children: rowActions.map((a) => /* @__PURE__ */
|
|
417
|
+
children: rowActions.map((a) => /* @__PURE__ */ jsx3(Button, {
|
|
418
418
|
action: a.action,
|
|
419
419
|
variant: a.variant,
|
|
420
420
|
doc,
|
|
421
421
|
collection,
|
|
422
422
|
onDone: reload,
|
|
423
423
|
children: a.label
|
|
424
|
-
}, a.label
|
|
425
|
-
}
|
|
426
|
-
}
|
|
424
|
+
}, a.label))
|
|
425
|
+
})
|
|
426
|
+
}) : null
|
|
427
427
|
]
|
|
428
|
-
}, typeof doc["id"] === "string" ? doc["id"] : i
|
|
429
|
-
}
|
|
428
|
+
}, typeof doc["id"] === "string" ? doc["id"] : i))
|
|
429
|
+
})
|
|
430
430
|
]
|
|
431
|
-
}
|
|
432
|
-
}
|
|
431
|
+
})
|
|
432
|
+
});
|
|
433
433
|
}
|
|
434
434
|
function Detail(props) {
|
|
435
435
|
const { collection, id, fields, resolveRelation } = props;
|
|
@@ -437,36 +437,36 @@ function Detail(props) {
|
|
|
437
437
|
const { data, error, loading } = useAsync(() => collection.get(id), [id]);
|
|
438
438
|
const relations = useRelations(fields, data ? [data] : null, resolveRelation);
|
|
439
439
|
if (loading)
|
|
440
|
-
return /* @__PURE__ */
|
|
440
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
441
441
|
tone: "muted",
|
|
442
442
|
children: "Loading…"
|
|
443
|
-
}
|
|
443
|
+
});
|
|
444
444
|
if (error)
|
|
445
|
-
return /* @__PURE__ */
|
|
445
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
446
446
|
tone: "error",
|
|
447
447
|
children: error
|
|
448
|
-
}
|
|
448
|
+
});
|
|
449
449
|
if (!data)
|
|
450
|
-
return /* @__PURE__ */
|
|
450
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
451
451
|
tone: "muted",
|
|
452
452
|
children: "Not found."
|
|
453
|
-
}
|
|
454
|
-
return /* @__PURE__ */
|
|
453
|
+
});
|
|
454
|
+
return /* @__PURE__ */ jsx3("dl", {
|
|
455
455
|
className: "divide-y divide-neutral-200 rounded-md border border-neutral-200 dark:divide-neutral-800 dark:border-neutral-800",
|
|
456
|
-
children: fields.map((f) => /* @__PURE__ */
|
|
456
|
+
children: fields.map((f) => /* @__PURE__ */ jsxs2("div", {
|
|
457
457
|
className: "grid grid-cols-3 gap-4 px-3 py-2",
|
|
458
458
|
children: [
|
|
459
|
-
/* @__PURE__ */
|
|
459
|
+
/* @__PURE__ */ jsx3("dt", {
|
|
460
460
|
className: "text-sm text-neutral-500",
|
|
461
461
|
children: f.label
|
|
462
|
-
}
|
|
463
|
-
/* @__PURE__ */
|
|
462
|
+
}),
|
|
463
|
+
/* @__PURE__ */ jsx3("dd", {
|
|
464
464
|
className: "col-span-2 text-sm",
|
|
465
465
|
children: cellValue(data, f, relations, ctx.locale, ctx.currency)
|
|
466
|
-
}
|
|
466
|
+
})
|
|
467
467
|
]
|
|
468
|
-
}, f.field
|
|
469
|
-
}
|
|
468
|
+
}, f.field))
|
|
469
|
+
});
|
|
470
470
|
}
|
|
471
471
|
function inputType(format) {
|
|
472
472
|
switch (format) {
|
|
@@ -546,68 +546,68 @@ function Form(props) {
|
|
|
546
546
|
}
|
|
547
547
|
}, [collection, dispatch, fields, id, mode, onSuccess, values]);
|
|
548
548
|
if (loading)
|
|
549
|
-
return /* @__PURE__ */
|
|
549
|
+
return /* @__PURE__ */ jsx3(Message, {
|
|
550
550
|
tone: "muted",
|
|
551
551
|
children: "Loading…"
|
|
552
|
-
}
|
|
553
|
-
return /* @__PURE__ */
|
|
552
|
+
});
|
|
553
|
+
return /* @__PURE__ */ jsxs2("form", {
|
|
554
554
|
onSubmit: submit,
|
|
555
555
|
className: "flex w-full max-w-xl flex-col gap-3",
|
|
556
556
|
children: [
|
|
557
|
-
fields.map((f) => /* @__PURE__ */
|
|
557
|
+
fields.map((f) => /* @__PURE__ */ jsxs2("label", {
|
|
558
558
|
className: "flex flex-col gap-1",
|
|
559
559
|
children: [
|
|
560
|
-
/* @__PURE__ */
|
|
560
|
+
/* @__PURE__ */ jsx3("span", {
|
|
561
561
|
className: "text-sm text-neutral-600 dark:text-neutral-400",
|
|
562
562
|
children: f.label
|
|
563
|
-
}
|
|
564
|
-
f.display && resolveRelation ? /* @__PURE__ */
|
|
563
|
+
}),
|
|
564
|
+
f.display && resolveRelation ? /* @__PURE__ */ jsx3(RelationSelect, {
|
|
565
565
|
collection: resolveRelation(f.field),
|
|
566
566
|
displayField: f.display.field,
|
|
567
567
|
value: String(values[f.field] ?? ""),
|
|
568
568
|
onChange: (next) => setValues((v) => ({ ...v, [f.field]: next }))
|
|
569
|
-
}
|
|
569
|
+
}) : f.format === "boolean" ? /* @__PURE__ */ jsx3("input", {
|
|
570
570
|
type: "checkbox",
|
|
571
571
|
className: "h-4 w-4",
|
|
572
572
|
checked: Boolean(values[f.field]),
|
|
573
573
|
onChange: (e) => setValues((v) => ({ ...v, [f.field]: e.target.checked }))
|
|
574
|
-
}
|
|
574
|
+
}) : /* @__PURE__ */ jsx3("input", {
|
|
575
575
|
type: inputType(f.format),
|
|
576
576
|
className: "rounded-md border border-neutral-300 px-3 py-1.5 text-sm dark:border-neutral-700 dark:bg-neutral-950",
|
|
577
577
|
value: String(values[f.field] ?? ""),
|
|
578
578
|
onChange: (e) => setValues((v) => ({ ...v, [f.field]: e.target.value }))
|
|
579
|
-
}
|
|
579
|
+
})
|
|
580
580
|
]
|
|
581
|
-
}, f.field
|
|
582
|
-
error ? /* @__PURE__ */
|
|
581
|
+
}, f.field)),
|
|
582
|
+
error ? /* @__PURE__ */ jsx3(Message, {
|
|
583
583
|
tone: "error",
|
|
584
584
|
children: error
|
|
585
|
-
}
|
|
586
|
-
/* @__PURE__ */
|
|
587
|
-
children: /* @__PURE__ */
|
|
585
|
+
}) : null,
|
|
586
|
+
/* @__PURE__ */ jsx3("div", {
|
|
587
|
+
children: /* @__PURE__ */ jsx3("button", {
|
|
588
588
|
type: "submit",
|
|
589
589
|
disabled: busy,
|
|
590
590
|
className: cx(BUTTON_BASE, VARIANT.default),
|
|
591
591
|
children: busy ? "Saving…" : submitLabel ?? "Save"
|
|
592
|
-
}
|
|
593
|
-
}
|
|
592
|
+
})
|
|
593
|
+
})
|
|
594
594
|
]
|
|
595
|
-
}
|
|
595
|
+
});
|
|
596
596
|
}
|
|
597
597
|
export {
|
|
598
|
-
|
|
599
|
-
useAction,
|
|
600
|
-
resolveHref,
|
|
601
|
-
formatValue,
|
|
602
|
-
Text,
|
|
603
|
-
Stack,
|
|
604
|
-
RelationSelect,
|
|
605
|
-
Heading,
|
|
606
|
-
GroveUiProvider,
|
|
607
|
-
Grid,
|
|
608
|
-
Form,
|
|
609
|
-
Divider,
|
|
610
|
-
Detail,
|
|
598
|
+
Button,
|
|
611
599
|
DataTable,
|
|
612
|
-
|
|
600
|
+
Detail,
|
|
601
|
+
Divider,
|
|
602
|
+
Form,
|
|
603
|
+
Grid,
|
|
604
|
+
GroveUiProvider,
|
|
605
|
+
Heading,
|
|
606
|
+
RelationSelect,
|
|
607
|
+
Stack,
|
|
608
|
+
Text,
|
|
609
|
+
formatValue,
|
|
610
|
+
resolveHref,
|
|
611
|
+
useAction,
|
|
612
|
+
useGroveUi
|
|
613
613
|
};
|
package/dist/runtime.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* under any of them (Next, React Router, plain history). The default uses the History API.
|
|
6
6
|
*/
|
|
7
7
|
import type { ReactNode } from 'react';
|
|
8
|
-
import type { Action, CollectionLike, Doc, Format } from './types
|
|
8
|
+
import type { Action, CollectionLike, Doc, Format } from './types';
|
|
9
9
|
export interface GroveUiContext {
|
|
10
10
|
/** Navigate to a route. */
|
|
11
11
|
navigate: (href: string) => void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@groveback/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "React primitives for Groveback Studio screens \u2014 tables, forms, detail views and relation pickers that read through the Groveback SDK, so every query passes the policy engine.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -15,9 +15,10 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
|
-
"sideEffects": false,
|
|
18
|
+
"//sideEffects": "NOT set to false: bun 1.4's bundler treats it as licence to drop the entire entry point, emitting the export names with nothing behind them \u2014 a 193-byte module exporting symbols that do not exist, reported as a successful build. The check-bundle script guards against a regression.",
|
|
19
19
|
"scripts": {
|
|
20
|
-
"build": "rm -rf dist && bun build ./src/index.ts --outdir dist --target browser --format esm --external react --external react-dom && bunx tsc -p tsconfig.json && cp ../../LICENSE ."
|
|
20
|
+
"build": "rm -rf dist && bun build ./src/index.ts --outdir dist --target browser --format esm --external react --external react-dom && bun run check-bundle && bunx tsc -p tsconfig.json && cp ../../LICENSE .",
|
|
21
|
+
"check-bundle": "bun -e \"const s=(await Bun.file('dist/index.js').text()); if(!s.includes('DataTable')||s.length<5000){console.error('dist/index.js looks empty ('+s.length+' bytes) \u2014 the bundler dropped modules');process.exit(1)}\""
|
|
21
22
|
},
|
|
22
23
|
"peerDependencies": {
|
|
23
24
|
"react": ">=18",
|