@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.
@@ -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.js';
14
+ import type { CollectionLike } from './types';
15
15
  export interface RelationSelectProps {
16
16
  /** The collection the relation points at. */
17
17
  collection: CollectionLike;
@@ -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.js';
11
- import type { Action } from './types.js';
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
- export { Button, DataTable, Detail, Divider, Form, Grid, Heading, Stack, Text } from './components.js';
2
- export { RelationSelect } from './RelationSelect.js';
3
- export type { RelationSelectProps } from './RelationSelect.js';
4
- export { GroveUiProvider, formatValue, resolveHref, useAction, useGroveUi } from './runtime.js';
5
- export type { GroveUiContext } from './runtime.js';
6
- export type { Action, ButtonVariant, CollectionLike, Doc, FieldRef, Format, RowAction } from './types.js';
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 { jsxDEV } from "react/jsx-dev-runtime";
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__ */ jsxDEV("p", {
58
+ return /* @__PURE__ */ jsx("p", {
59
59
  className: "text-xs text-red-600",
60
60
  children: error
61
- }, undefined, false, undefined, this);
61
+ });
62
62
  }
63
63
  const buttonLabel = selected ? labelOf(selected, displayField) : value !== "" ? value : placeholder ?? "Select…";
64
- return /* @__PURE__ */ jsxDEV("div", {
64
+ return /* @__PURE__ */ jsxs("div", {
65
65
  ref: boxRef,
66
66
  className: "relative",
67
67
  children: [
68
- /* @__PURE__ */ jsxDEV("button", {
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__ */ jsxDEV("span", {
73
+ /* @__PURE__ */ jsx("span", {
74
74
  className: selected || value ? "" : "text-neutral-400",
75
75
  children: docs === null ? "Loading…" : buttonLabel
76
- }, undefined, false, undefined, this),
77
- /* @__PURE__ */ jsxDEV("span", {
76
+ }),
77
+ /* @__PURE__ */ jsx("span", {
78
78
  "aria-hidden": true,
79
79
  className: "ml-2 text-neutral-400",
80
80
  children: "▾"
81
- }, undefined, false, undefined, this)
81
+ })
82
82
  ]
83
- }, undefined, true, undefined, this),
84
- open ? /* @__PURE__ */ jsxDEV("div", {
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__ */ jsxDEV("input", {
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
- }, undefined, false, undefined, this),
94
- /* @__PURE__ */ jsxDEV("ul", {
93
+ }),
94
+ /* @__PURE__ */ jsxs("ul", {
95
95
  className: "max-h-56 overflow-auto py-1",
96
96
  children: [
97
- !required ? /* @__PURE__ */ jsxDEV("li", {
98
- children: /* @__PURE__ */ jsxDEV("button", {
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
- }, undefined, false, undefined, this)
108
- }, undefined, false, undefined, this) : null,
109
- matches.length === 0 ? /* @__PURE__ */ jsxDEV("li", {
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
- }, undefined, false, undefined, this) : matches.map((doc) => {
112
+ }) : matches.map((doc) => {
113
113
  const id = typeof doc["id"] === "string" ? doc["id"] : "";
114
- return /* @__PURE__ */ jsxDEV("li", {
115
- children: /* @__PURE__ */ jsxDEV("button", {
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
- }, undefined, false, undefined, this)
125
- }, id, false, undefined, this);
124
+ })
125
+ }, id);
126
126
  })
127
127
  ]
128
- }, undefined, true, undefined, this),
129
- truncated ? /* @__PURE__ */ jsxDEV("p", {
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
- }, undefined, true, undefined, this) : null
136
+ }) : null
137
137
  ]
138
- }, undefined, true, undefined, this) : null
138
+ }) : null
139
139
  ]
140
- }, undefined, true, undefined, this);
140
+ });
141
141
  }
142
142
 
143
143
  // src/runtime.tsx
144
144
  import { createContext, useContext, useCallback } from "react";
145
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
145
+ import { jsx as jsx2 } from "react/jsx-runtime";
146
146
  var Ctx = createContext(null);
147
147
  function GroveUiProvider({ value, children }) {
148
- return /* @__PURE__ */ jsxDEV2(Ctx.Provider, {
148
+ return /* @__PURE__ */ jsx2(Ctx.Provider, {
149
149
  value,
150
150
  children
151
- }, undefined, false, undefined, this);
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 { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
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__ */ jsxDEV3("div", {
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
- }, undefined, false, undefined, this);
242
+ });
243
243
  }
244
244
  function Grid(props) {
245
245
  const { columns, gap = 4, width, children } = props;
246
- return /* @__PURE__ */ jsxDEV3("div", {
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
- }, undefined, false, undefined, this);
249
+ });
250
250
  }
251
251
  function Divider() {
252
- return /* @__PURE__ */ jsxDEV3("hr", {
252
+ return /* @__PURE__ */ jsx3("hr", {
253
253
  className: "border-t border-neutral-200 dark:border-neutral-800"
254
- }, undefined, false, undefined, this);
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__ */ jsxDEV3(Tag, {
259
+ return /* @__PURE__ */ jsx3(Tag, {
260
260
  className: HEADING[level],
261
261
  children
262
- }, undefined, false, undefined, this);
262
+ });
263
263
  }
264
264
  function Text({ muted, children }) {
265
- return /* @__PURE__ */ jsxDEV3("p", {
265
+ return /* @__PURE__ */ jsx3("p", {
266
266
  className: muted ? "text-sm text-neutral-500" : "text-sm",
267
267
  children
268
- }, undefined, false, undefined, this);
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__ */ jsxDEV3("button", {
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
- }, undefined, false, undefined, this);
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__ */ jsxDEV3("p", {
321
+ return /* @__PURE__ */ jsx3("p", {
322
322
  className: cls,
323
323
  children
324
- }, undefined, false, undefined, this);
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__ */ jsxDEV3(Message, {
370
+ return /* @__PURE__ */ jsx3(Message, {
371
371
  tone: "muted",
372
372
  children: "Loading…"
373
- }, undefined, false, undefined, this);
373
+ });
374
374
  if (error)
375
- return /* @__PURE__ */ jsxDEV3(Message, {
375
+ return /* @__PURE__ */ jsx3(Message, {
376
376
  tone: "error",
377
377
  children: error
378
- }, undefined, false, undefined, this);
378
+ });
379
379
  if (!data || data.length === 0)
380
- return /* @__PURE__ */ jsxDEV3(Message, {
380
+ return /* @__PURE__ */ jsx3(Message, {
381
381
  tone: "muted",
382
382
  children: emptyText ?? "Nothing here yet."
383
- }, undefined, false, undefined, this);
384
- return /* @__PURE__ */ jsxDEV3("div", {
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__ */ jsxDEV3("table", {
386
+ children: /* @__PURE__ */ jsxs2("table", {
387
387
  className: "w-full text-sm",
388
388
  children: [
389
- /* @__PURE__ */ jsxDEV3("thead", {
389
+ /* @__PURE__ */ jsx3("thead", {
390
390
  className: "bg-neutral-50 dark:bg-neutral-900",
391
- children: /* @__PURE__ */ jsxDEV3("tr", {
391
+ children: /* @__PURE__ */ jsxs2("tr", {
392
392
  children: [
393
- columns.map((c) => /* @__PURE__ */ jsxDEV3("th", {
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, false, undefined, this)),
397
- rowActions && rowActions.length > 0 ? /* @__PURE__ */ jsxDEV3("th", {
396
+ }, c.field)),
397
+ rowActions && rowActions.length > 0 ? /* @__PURE__ */ jsx3("th", {
398
398
  className: "px-3 py-2"
399
- }, undefined, false, undefined, this) : null
399
+ }) : null
400
400
  ]
401
- }, undefined, true, undefined, this)
402
- }, undefined, false, undefined, this),
403
- /* @__PURE__ */ jsxDEV3("tbody", {
404
- children: data.map((doc, i) => /* @__PURE__ */ jsxDEV3("tr", {
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__ */ jsxDEV3("td", {
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, false, undefined, this)),
412
- rowActions && rowActions.length > 0 ? /* @__PURE__ */ jsxDEV3("td", {
411
+ }, c.field)),
412
+ rowActions && rowActions.length > 0 ? /* @__PURE__ */ jsx3("td", {
413
413
  className: "px-3 py-2",
414
- children: /* @__PURE__ */ jsxDEV3("div", {
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__ */ jsxDEV3(Button, {
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, false, undefined, this))
425
- }, undefined, false, undefined, this)
426
- }, undefined, false, undefined, this) : null
424
+ }, a.label))
425
+ })
426
+ }) : null
427
427
  ]
428
- }, typeof doc["id"] === "string" ? doc["id"] : i, true, undefined, this))
429
- }, undefined, false, undefined, this)
428
+ }, typeof doc["id"] === "string" ? doc["id"] : i))
429
+ })
430
430
  ]
431
- }, undefined, true, undefined, this)
432
- }, undefined, false, undefined, this);
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__ */ jsxDEV3(Message, {
440
+ return /* @__PURE__ */ jsx3(Message, {
441
441
  tone: "muted",
442
442
  children: "Loading…"
443
- }, undefined, false, undefined, this);
443
+ });
444
444
  if (error)
445
- return /* @__PURE__ */ jsxDEV3(Message, {
445
+ return /* @__PURE__ */ jsx3(Message, {
446
446
  tone: "error",
447
447
  children: error
448
- }, undefined, false, undefined, this);
448
+ });
449
449
  if (!data)
450
- return /* @__PURE__ */ jsxDEV3(Message, {
450
+ return /* @__PURE__ */ jsx3(Message, {
451
451
  tone: "muted",
452
452
  children: "Not found."
453
- }, undefined, false, undefined, this);
454
- return /* @__PURE__ */ jsxDEV3("dl", {
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__ */ jsxDEV3("div", {
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__ */ jsxDEV3("dt", {
459
+ /* @__PURE__ */ jsx3("dt", {
460
460
  className: "text-sm text-neutral-500",
461
461
  children: f.label
462
- }, undefined, false, undefined, this),
463
- /* @__PURE__ */ jsxDEV3("dd", {
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
- }, undefined, false, undefined, this)
466
+ })
467
467
  ]
468
- }, f.field, true, undefined, this))
469
- }, undefined, false, undefined, this);
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__ */ jsxDEV3(Message, {
549
+ return /* @__PURE__ */ jsx3(Message, {
550
550
  tone: "muted",
551
551
  children: "Loading…"
552
- }, undefined, false, undefined, this);
553
- return /* @__PURE__ */ jsxDEV3("form", {
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__ */ jsxDEV3("label", {
557
+ fields.map((f) => /* @__PURE__ */ jsxs2("label", {
558
558
  className: "flex flex-col gap-1",
559
559
  children: [
560
- /* @__PURE__ */ jsxDEV3("span", {
560
+ /* @__PURE__ */ jsx3("span", {
561
561
  className: "text-sm text-neutral-600 dark:text-neutral-400",
562
562
  children: f.label
563
- }, undefined, false, undefined, this),
564
- f.display && resolveRelation ? /* @__PURE__ */ jsxDEV3(RelationSelect, {
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
- }, undefined, false, undefined, this) : f.format === "boolean" ? /* @__PURE__ */ jsxDEV3("input", {
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
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3("input", {
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
- }, undefined, false, undefined, this)
579
+ })
580
580
  ]
581
- }, f.field, true, undefined, this)),
582
- error ? /* @__PURE__ */ jsxDEV3(Message, {
581
+ }, f.field)),
582
+ error ? /* @__PURE__ */ jsx3(Message, {
583
583
  tone: "error",
584
584
  children: error
585
- }, undefined, false, undefined, this) : null,
586
- /* @__PURE__ */ jsxDEV3("div", {
587
- children: /* @__PURE__ */ jsxDEV3("button", {
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
- }, undefined, false, undefined, this)
593
- }, undefined, false, undefined, this)
592
+ })
593
+ })
594
594
  ]
595
- }, undefined, true, undefined, this);
595
+ });
596
596
  }
597
597
  export {
598
- useGroveUi,
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
- Button
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.js';
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.0",
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",