@protobase/ui 0.23.2 → 0.23.4
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/app/app.js +3 -1
- package/dist/app/meta-gate.js +1 -1
- package/dist/app/record-picker.d.ts +25 -0
- package/dist/app/record-picker.js +65 -0
- package/dist/app/relation-picker.js +6 -3
- package/dist/app/use-picked-labels.js +19 -0
- package/dist/data/api-provider.d.ts +2 -0
- package/dist/data/focus-refresh.js +2 -0
- package/dist/data/live-refresh.js +29 -0
- package/dist/data/query-keys.js +6 -0
- package/dist/data/use-live-updates.js +36 -0
- package/dist/filter-panel/facet-combobox.js +5 -14
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/package.js +1 -1
- package/dist/primitives/token.js +20 -0
- package/dist/shell/assets/app-CZR9_391.js +69 -0
- package/dist/shell/assets/{index-COUvd4Y8.js → index-Dxk5GdSQ.js} +1 -1
- package/dist/shell/assets/{maplibre--7-Evlih.js → maplibre-CzKsADHj.js} +2 -2
- package/dist/shell/assets/{markdown-content-BvbCIheC.js → markdown-content-pV-6NJbB.js} +1 -1
- package/dist/shell/assets/{record-map-B56wGcbR.js → record-map-CVhzR4LM.js} +1 -1
- package/dist/shell/assets/{records-map-BStfyJ5v.js → records-map-CJLKyY62.js} +1 -1
- package/dist/shell/assets/shared-protobase-ui-BxPtBpOH.js +1 -0
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-etWQCzpG.js +0 -69
- package/dist/shell/assets/shared-protobase-ui-OubT4zPz.js +0 -1
package/dist/app/app.js
CHANGED
|
@@ -12,11 +12,12 @@ import { CustomBoundary } from "./pages/custom-boundary.js";
|
|
|
12
12
|
import { PluginSourceProvider } from "../plugins/plugin-source.js";
|
|
13
13
|
import { AuthGate } from "../auth/auth-gate.js";
|
|
14
14
|
import { isAdmin, userToShell } from "../auth/user-to-shell.js";
|
|
15
|
+
import { sourceStatusOf } from "../data/with-sources.js";
|
|
16
|
+
import { useLiveUpdates } from "../data/use-live-updates.js";
|
|
15
17
|
import { useOpenedRecord } from "../data/use-record.js";
|
|
16
18
|
import { humanize } from "../live/naming.js";
|
|
17
19
|
import { ToastProvider } from "../toasts/toast-provider.js";
|
|
18
20
|
import { recordTitle } from "../live/model-helpers.js";
|
|
19
|
-
import { sourceStatusOf } from "../data/with-sources.js";
|
|
20
21
|
import { MetaGate, Notice, useAdminMeta } from "./meta-gate.js";
|
|
21
22
|
import { useAppAddress } from "./use-app-address.js";
|
|
22
23
|
import { documentTitle, titleParts, useDocumentTitle } from "./document-title.js";
|
|
@@ -75,6 +76,7 @@ const Routes = ({ workspace, sidebarMode, assistant, runtime }) => {
|
|
|
75
76
|
resource: shown?.resource,
|
|
76
77
|
key: shown?.key
|
|
77
78
|
});
|
|
79
|
+
useLiveUpdates(meta);
|
|
78
80
|
const groups = navFromMeta(meta, basePath, recent);
|
|
79
81
|
const firstPage = Object.keys(meta.pages)[0];
|
|
80
82
|
const firstResource = Object.keys(meta.resources)[0];
|
package/dist/app/meta-gate.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Spinner } from "../primitives/spinner.js";
|
|
2
2
|
import { ClientProvider, useClient, useSourceClients } from "../data/api-provider.js";
|
|
3
|
+
import { withSources } from "../data/with-sources.js";
|
|
3
4
|
import { MetaContext } from "../data/meta-context.js";
|
|
4
5
|
import { allowEverything } from "../data/meta-data.js";
|
|
5
6
|
import { useMeta } from "../data/use-meta.js";
|
|
6
7
|
import { useSourceMetas } from "../data/use-source-metas.js";
|
|
7
|
-
import { withSources } from "../data/with-sources.js";
|
|
8
8
|
import { useContext, useMemo } from "react";
|
|
9
9
|
import { isApiError, routedClient } from "@protobase/client";
|
|
10
10
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/app/record-picker.d.ts
|
|
2
|
+
type One = {
|
|
3
|
+
multiple?: false;
|
|
4
|
+
/** The chosen record's key, as text; `undefined` for none. */
|
|
5
|
+
value: string | undefined;
|
|
6
|
+
onChange: (key: string) => void;
|
|
7
|
+
};
|
|
8
|
+
type Several = {
|
|
9
|
+
multiple: true;
|
|
10
|
+
/** The chosen records' keys, as text. */
|
|
11
|
+
value: string[];
|
|
12
|
+
onChange: (keys: string[]) => void;
|
|
13
|
+
};
|
|
14
|
+
export type RecordPickerProps = (One | Several) & {
|
|
15
|
+
/** The name of the resource whose records it picks. */
|
|
16
|
+
resource: string;
|
|
17
|
+
invalid?: boolean;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Picks records of a resource of the app with the create form's relation picker: type to narrow them by their search
|
|
21
|
+
* fields or title, scroll for more, choose with the mouse or the keyboard. With `multiple`, each choice is added as a
|
|
22
|
+
* token that can be taken off again, and choosing a chosen record takes it off.
|
|
23
|
+
*/
|
|
24
|
+
export declare const RecordPicker: (props: RecordPickerProps) => import("react").JSX.Element;
|
|
25
|
+
//#endregion
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { useAdminMeta } from "./meta-gate.js";
|
|
2
|
+
import { RelationPicker } from "./relation-picker.js";
|
|
3
|
+
import { Token } from "../primitives/token.js";
|
|
4
|
+
import { usePickedLabels } from "./use-picked-labels.js";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/app/record-picker.tsx
|
|
8
|
+
/**
|
|
9
|
+
* Picks records of a resource of the app with the create form's relation picker: type to narrow them by their search
|
|
10
|
+
* fields or title, scroll for more, choose with the mouse or the keyboard. With `multiple`, each choice is added as a
|
|
11
|
+
* token that can be taken off again, and choosing a chosen record takes it off.
|
|
12
|
+
*/
|
|
13
|
+
const RecordPicker = (props) => {
|
|
14
|
+
const { resources, views } = useAdminMeta();
|
|
15
|
+
const target = resources[props.resource];
|
|
16
|
+
if (!target) throw new Error(`RecordPicker: the app has no resource named "${props.resource}"`);
|
|
17
|
+
if (target.primaryKey.length !== 1) throw new Error(`RecordPicker: "${props.resource}" has a composite key, which a picker cannot name`);
|
|
18
|
+
const view = views[props.resource];
|
|
19
|
+
const keys = props.multiple ? props.value : props.value === void 0 ? [] : [props.value];
|
|
20
|
+
const [known, setKnown] = useState({});
|
|
21
|
+
const fetched = usePickedLabels(target, view, keys);
|
|
22
|
+
const labelOf = (key) => known[key] ?? fetched.get(key) ?? key;
|
|
23
|
+
const remember = (key, label) => setKnown((current) => ({
|
|
24
|
+
...current,
|
|
25
|
+
[key]: label
|
|
26
|
+
}));
|
|
27
|
+
if (!props.multiple) {
|
|
28
|
+
const { value, onChange } = props;
|
|
29
|
+
return /* @__PURE__ */ jsx(RelationPicker, {
|
|
30
|
+
target,
|
|
31
|
+
view,
|
|
32
|
+
value: value ?? "",
|
|
33
|
+
label: value === void 0 ? void 0 : labelOf(value),
|
|
34
|
+
invalid: props.invalid,
|
|
35
|
+
onChange: (key, label) => {
|
|
36
|
+
remember(key, label);
|
|
37
|
+
onChange(key);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const { value, onChange } = props;
|
|
42
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
43
|
+
className: "flex flex-col gap-2",
|
|
44
|
+
children: [/* @__PURE__ */ jsx(RelationPicker, {
|
|
45
|
+
target,
|
|
46
|
+
view,
|
|
47
|
+
value: "",
|
|
48
|
+
chosen: value,
|
|
49
|
+
several: true,
|
|
50
|
+
invalid: props.invalid,
|
|
51
|
+
onChange: (key, label) => {
|
|
52
|
+
remember(key, label);
|
|
53
|
+
onChange(value.includes(key) ? value.filter((chosen) => chosen !== key) : [...value, key]);
|
|
54
|
+
}
|
|
55
|
+
}), value.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
56
|
+
className: "flex flex-wrap gap-1",
|
|
57
|
+
children: value.map((key) => /* @__PURE__ */ jsx(Token, {
|
|
58
|
+
label: labelOf(key),
|
|
59
|
+
onRemove: () => onChange(value.filter((chosen) => chosen !== key))
|
|
60
|
+
}, key))
|
|
61
|
+
})]
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { RecordPicker };
|
|
@@ -26,7 +26,7 @@ const useLoadMoreInView = (list, sentinel, loaded, loadMore) => {
|
|
|
26
26
|
* Picks a record of another resource: type to narrow the records by their search fields or title, scroll for more, and
|
|
27
27
|
* choose with the mouse or with the arrow keys and Enter; Escape closes the list.
|
|
28
28
|
*/
|
|
29
|
-
const RelationPicker = ({ target, view, value, label, invalid, onChange }) => {
|
|
29
|
+
const RelationPicker = ({ target, view, value, label, chosen = [value], several = false, invalid, onChange }) => {
|
|
30
30
|
const [text, setText] = useState("");
|
|
31
31
|
const [open, setOpen] = useState(false);
|
|
32
32
|
const [active, setActive] = useState(-1);
|
|
@@ -66,7 +66,9 @@ const RelationPicker = ({ target, view, value, label, invalid, onChange }) => {
|
|
|
66
66
|
};
|
|
67
67
|
const choose = (item) => {
|
|
68
68
|
onChange(String(item[keyField]), String(item[labelField]));
|
|
69
|
-
close();
|
|
69
|
+
if (!several) return close();
|
|
70
|
+
setText("");
|
|
71
|
+
setActive(-1);
|
|
70
72
|
};
|
|
71
73
|
const onKeyDown = (event) => {
|
|
72
74
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
@@ -111,6 +113,7 @@ const RelationPicker = ({ target, view, value, label, invalid, onChange }) => {
|
|
|
111
113
|
ref: setList,
|
|
112
114
|
id: listId,
|
|
113
115
|
role: "listbox",
|
|
116
|
+
"aria-multiselectable": several || void 0,
|
|
114
117
|
className: "absolute z-30 mt-1 max-h-60 w-full overflow-y-auto rounded-md border bg-background py-1 shadow-pop",
|
|
115
118
|
onMouseDown: (event) => event.preventDefault(),
|
|
116
119
|
children: [
|
|
@@ -118,7 +121,7 @@ const RelationPicker = ({ target, view, value, label, invalid, onChange }) => {
|
|
|
118
121
|
id: optionId(index),
|
|
119
122
|
role: "option",
|
|
120
123
|
"aria-selected": index === active,
|
|
121
|
-
className: cn("cursor-pointer truncate px-3 py-1.5", index === active && "bg-muted", String(item[keyField])
|
|
124
|
+
className: cn("cursor-pointer truncate px-3 py-1.5", index === active && "bg-muted", chosen.includes(String(item[keyField])) && "font-medium"),
|
|
122
125
|
onMouseMove: () => index !== active && setActive(index),
|
|
123
126
|
onClick: () => choose(item),
|
|
124
127
|
children: String(item[labelField])
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { keys } from "../data/query-keys.js";
|
|
2
|
+
import { useClient } from "../data/api-provider.js";
|
|
3
|
+
import { fetchLabels } from "../live/relation-labels.js";
|
|
4
|
+
import { keepPreviousData, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
//#region src/app/use-picked-labels.ts
|
|
6
|
+
/** The names of the chosen records, fetched in one list call; the previous names stay while the next ones load. */
|
|
7
|
+
const usePickedLabels = (target, view, ids) => {
|
|
8
|
+
const client = useClient();
|
|
9
|
+
const queryClient = useQueryClient();
|
|
10
|
+
const sorted = [...new Set(ids)].sort();
|
|
11
|
+
return useQuery({
|
|
12
|
+
queryKey: keys.pickedLabels(target.name, sorted.join(",")),
|
|
13
|
+
enabled: sorted.length > 0,
|
|
14
|
+
placeholderData: keepPreviousData,
|
|
15
|
+
queryFn: () => fetchLabels(queryClient, client, target, sorted, view)
|
|
16
|
+
}).data ?? /* @__PURE__ */ new Map();
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { usePickedLabels };
|
|
@@ -56,6 +56,7 @@ export declare const useClient: () => {
|
|
|
56
56
|
form: (name: string) => Promise<import("@protobase/client").PublicForm>;
|
|
57
57
|
formChallenge: (name: string) => Promise<import("@protobase/client").FormChallenge>;
|
|
58
58
|
submitForm: (name: string, values: Record<string, unknown>, proof: import("@protobase/client").FormProof) => Promise<void>;
|
|
59
|
+
live: (handlers: import("@protobase/client").LiveHandlers) => () => void;
|
|
59
60
|
};
|
|
60
61
|
/**
|
|
61
62
|
* The client of the API that serves `resource`: a source's for a resource the app draws from one, the app's own
|
|
@@ -114,6 +115,7 @@ export declare const useResourceClient: (resource: string) => {
|
|
|
114
115
|
form: (name: string) => Promise<import("@protobase/client").PublicForm>;
|
|
115
116
|
formChallenge: (name: string) => Promise<import("@protobase/client").FormChallenge>;
|
|
116
117
|
submitForm: (name: string, values: Record<string, unknown>, proof: import("@protobase/client").FormProof) => Promise<void>;
|
|
118
|
+
live: (handlers: import("@protobase/client").LiveHandlers) => () => void;
|
|
117
119
|
};
|
|
118
120
|
export type ApiProviderProps = {
|
|
119
121
|
/** Defaults to `/api/v1` on the current origin. */
|
|
@@ -9,6 +9,8 @@ const createFocusRefresh = () => {
|
|
|
9
9
|
let paused = 0;
|
|
10
10
|
return {
|
|
11
11
|
refetchOnWindowFocus: () => paused > 0 ? false : "always",
|
|
12
|
+
/** Whether a form on screen holds unsaved changes, so its record must not be read again under it. */
|
|
13
|
+
paused: () => paused > 0,
|
|
12
14
|
/** Holds the refetches until the returned function is called. */
|
|
13
15
|
pause: () => {
|
|
14
16
|
paused += 1;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads again what the page shows of the resources whose records changed: every query keyed by the resource, as after
|
|
3
|
+
* a write of the page's own. While a form holds unsaved changes (`paused`), the records stay as they are, only marked
|
|
4
|
+
* stale, so the form's Save still finds a conflict.
|
|
5
|
+
*/
|
|
6
|
+
const liveRefresh = (queryClient, paused) => {
|
|
7
|
+
const pending = /* @__PURE__ */ new Set();
|
|
8
|
+
let timer;
|
|
9
|
+
const flush = () => {
|
|
10
|
+
timer = void 0;
|
|
11
|
+
const resources = new Set(pending);
|
|
12
|
+
pending.clear();
|
|
13
|
+
const shown = (key) => resources.has(key[1]);
|
|
14
|
+
queryClient.invalidateQueries({ predicate: (query) => shown(query.queryKey) && query.queryKey[0] !== "record" });
|
|
15
|
+
queryClient.invalidateQueries({
|
|
16
|
+
predicate: (query) => shown(query.queryKey) && query.queryKey[0] === "record",
|
|
17
|
+
refetchType: paused() ? "none" : "active"
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
return {
|
|
21
|
+
changed: (resources) => {
|
|
22
|
+
for (const resource of resources) pending.add(resource);
|
|
23
|
+
if (pending.size > 0 && !timer) timer = setTimeout(flush, 300);
|
|
24
|
+
},
|
|
25
|
+
stop: () => clearTimeout(timer)
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { liveRefresh };
|
package/dist/data/query-keys.js
CHANGED
|
@@ -126,6 +126,12 @@ const keys = {
|
|
|
126
126
|
resource,
|
|
127
127
|
ids
|
|
128
128
|
],
|
|
129
|
+
/** The names of the records a `RecordPicker` holds. */
|
|
130
|
+
pickedLabels: (resource, ids) => [
|
|
131
|
+
"picked-labels",
|
|
132
|
+
resource,
|
|
133
|
+
ids
|
|
134
|
+
],
|
|
129
135
|
count: (resource, filter) => [
|
|
130
136
|
"count",
|
|
131
137
|
resource,
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FocusRefreshContext } from "./focus-refresh.js";
|
|
2
|
+
import { useClient } from "./api-provider.js";
|
|
3
|
+
import { liveRefresh } from "./live-refresh.js";
|
|
4
|
+
import { sourceStatusOf } from "./with-sources.js";
|
|
5
|
+
import { useContext, useEffect, useMemo } from "react";
|
|
6
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
7
|
+
//#region src/data/use-live-updates.ts
|
|
8
|
+
/**
|
|
9
|
+
* Keeps what the page shows of the app's `.live()` resources current: hears which records change (`client.live`) and
|
|
10
|
+
* reads again the lists, records and counts of their resources. Connects only when the caller reads a live resource.
|
|
11
|
+
*/
|
|
12
|
+
const useLiveUpdates = (meta) => {
|
|
13
|
+
const client = useClient();
|
|
14
|
+
const queryClient = useQueryClient();
|
|
15
|
+
const focusRefresh = useContext(FocusRefreshContext);
|
|
16
|
+
const live = useMemo(() => Object.values(meta.resources).filter((model) => model.live && !sourceStatusOf(meta, model.name)).map((model) => model.name).sort().join(","), [meta]);
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!live) return;
|
|
19
|
+
const refresh = liveRefresh(queryClient, () => focusRefresh?.paused() ?? false);
|
|
20
|
+
const stop = client.live({
|
|
21
|
+
onChanges: (changes) => refresh.changed(changes.map((change) => change.resource)),
|
|
22
|
+
onResync: () => refresh.changed(live.split(","))
|
|
23
|
+
});
|
|
24
|
+
return () => {
|
|
25
|
+
stop();
|
|
26
|
+
refresh.stop();
|
|
27
|
+
};
|
|
28
|
+
}, [
|
|
29
|
+
client,
|
|
30
|
+
queryClient,
|
|
31
|
+
focusRefresh,
|
|
32
|
+
live
|
|
33
|
+
]);
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { useLiveUpdates };
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { formatInt } from "../format/number.js";
|
|
3
|
+
import { Token } from "../primitives/token.js";
|
|
3
4
|
import { facetLabel } from "./filter-state.js";
|
|
4
5
|
import { comboboxOptions, moveActive } from "./combobox-options.js";
|
|
5
6
|
import { useEffect, useId, useRef, useState } from "react";
|
|
6
|
-
import { Check, Search
|
|
7
|
+
import { Check, Search } from "lucide-react";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
//#region src/filter-panel/facet-combobox.tsx
|
|
9
10
|
/** Chooses several values of a facet: the choices sit in the box as removable tokens, typing searches the options. */
|
|
@@ -50,19 +51,9 @@ const FacetCombobox = ({ config, selected, onToggle, inline = false }) => {
|
|
|
50
51
|
onClick: () => input.current?.focus(),
|
|
51
52
|
children: [
|
|
52
53
|
/* @__PURE__ */ jsx(Search, { className: "size-3.5 shrink-0 text-muted-foreground" }),
|
|
53
|
-
selected.map((value) => /* @__PURE__ */
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
className: "truncate",
|
|
57
|
-
children: facetLabel(config, value)
|
|
58
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
59
|
-
type: "button",
|
|
60
|
-
"aria-label": `Remove ${facetLabel(config, value)}`,
|
|
61
|
-
className: "rounded p-0.5 hover:bg-primary-border/60",
|
|
62
|
-
onMouseDown: (event) => event.preventDefault(),
|
|
63
|
-
onClick: () => onToggle(value),
|
|
64
|
-
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
65
|
-
})]
|
|
54
|
+
selected.map((value) => /* @__PURE__ */ jsx(Token, {
|
|
55
|
+
label: facetLabel(config, value),
|
|
56
|
+
onRemove: () => onToggle(value)
|
|
66
57
|
}, value)),
|
|
67
58
|
/* @__PURE__ */ jsx("input", {
|
|
68
59
|
ref: input,
|
package/dist/index.d.ts
CHANGED
|
@@ -59,5 +59,6 @@ import { Field, FieldProps } from "./record-view/field.js";
|
|
|
59
59
|
import { useFieldControl } from "./primitives/field-control.js";
|
|
60
60
|
import { Section } from "./record-view/section.js";
|
|
61
61
|
import { Form, FormProps } from "./app/create/form.js";
|
|
62
|
+
import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
|
|
62
63
|
import { Spinner } from "./primitives/spinner.js";
|
|
63
|
-
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type RecordCardProps, RecordLink, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
|
|
64
|
+
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
|
package/dist/index.js
CHANGED
|
@@ -50,4 +50,5 @@ import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
|
|
|
50
50
|
import { App } from "./app/app.js";
|
|
51
51
|
import { RecordLink } from "./app/displays/record-link.js";
|
|
52
52
|
import { Choices } from "./primitives/choices.js";
|
|
53
|
-
|
|
53
|
+
import { RecordPicker } from "./app/record-picker.js";
|
|
54
|
+
export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
|
package/dist/package.js
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { X } from "lucide-react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
//#region src/primitives/token.tsx
|
|
4
|
+
/** A chosen value as a small token with a button that takes it off; the button keeps the focus where it is. */
|
|
5
|
+
const Token = ({ label, onRemove }) => /* @__PURE__ */ jsxs("span", {
|
|
6
|
+
className: "inline-flex h-5 max-w-full items-center gap-0.5 rounded bg-primary-soft pl-1.5 pr-0.5 text-xs font-medium text-primary-text",
|
|
7
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
8
|
+
className: "truncate",
|
|
9
|
+
children: label
|
|
10
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
11
|
+
type: "button",
|
|
12
|
+
"aria-label": `Remove ${label}`,
|
|
13
|
+
className: "rounded p-0.5 hover:bg-primary-border/60",
|
|
14
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
15
|
+
onClick: onRemove,
|
|
16
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
17
|
+
})]
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { Token };
|