@protobase/ui 0.23.10 → 0.23.11
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/create/create-page.js +3 -2
- package/dist/app/create/create-record-form.js +4 -3
- package/dist/app/create/create-route.js +5 -8
- package/dist/app/create/create-values.js +2 -1
- package/dist/app/create/form.js +3 -2
- package/dist/app/create/messages.js +32 -0
- package/dist/app/delete/blocked-delete.js +6 -5
- package/dist/app/delete/dialogs.js +21 -20
- package/dist/app/delete/messages.js +66 -0
- package/dist/app/delete/use-delete-flow.js +7 -6
- package/dist/app/displays/display-controls.js +5 -4
- package/dist/app/displays/list-displays.js +2 -1
- package/dist/app/displays/messages.js +20 -0
- package/dist/app/embedded-list/cut-off-note.js +2 -1
- package/dist/app/embedded-list/embedded-list.js +11 -6
- package/dist/app/embedded-list/embedded-row-targets.js +4 -3
- package/dist/app/embedded-list/messages.js +24 -0
- package/dist/app/error-banner.js +2 -1
- package/dist/app/field-errors.js +19 -19
- package/dist/app/field-messages.js +48 -0
- package/dist/app/hand-order/hand-sorted-list.js +6 -11
- package/dist/app/hand-order/messages.js +22 -0
- package/dist/app/hand-order/sort-button.js +2 -1
- package/dist/app/hand-order/use-hand-order.js +2 -1
- package/dist/app/incoming-card.js +2 -1
- package/dist/app/list-edit/messages.js +14 -0
- package/dist/app/list-edit/use-list-edits.js +2 -1
- package/dist/app/list-empty-state.js +5 -5
- package/dist/app/list-empty.js +3 -2
- package/dist/app/list-messages.js +54 -0
- package/dist/app/list-page.js +24 -38
- package/dist/app/list-search.js +8 -7
- package/dist/app/messages.js +16 -0
- package/dist/app/pages/record-actions.js +4 -3
- package/dist/app/record-lines.js +3 -2
- package/dist/app/record-messages.js +78 -0
- package/dist/app/record-page.js +31 -32
- package/dist/app/related-section.js +5 -4
- package/dist/app/use-live-save.js +4 -3
- package/dist/data-grid/cell-text.js +2 -1
- package/dist/data-grid/cells.js +15 -14
- package/dist/data-grid/grid-footer.js +3 -2
- package/dist/data-grid/grid-header.js +4 -3
- package/dist/data-grid/grid-row.js +2 -1
- package/dist/data-grid/header-menu.js +5 -4
- package/dist/data-grid/messages.js +50 -0
- package/dist/data-grid/row-menu.js +2 -1
- package/dist/data-grid/row-reorder.js +8 -8
- package/dist/filter-panel/active-filter-chips.js +3 -2
- package/dist/filter-panel/date-presets.js +5 -4
- package/dist/filter-panel/facet-combobox.js +3 -2
- package/dist/filter-panel/facet-group.js +3 -2
- package/dist/filter-panel/filter-config.js +15 -12
- package/dist/filter-panel/filter-panel.js +4 -3
- package/dist/filter-panel/filter-state.js +2 -2
- package/dist/filter-panel/messages.js +52 -0
- package/dist/filter-panel/more-filters-menu.js +3 -2
- package/dist/filter-panel/range-filter.js +2 -1
- package/dist/filter-panel/secondary-filters.js +2 -1
- package/dist/forms/messages.js +24 -0
- package/dist/forms/prove-human.js +2 -1
- package/dist/forms/public-form-page.js +14 -8
- package/dist/live/display-value.js +3 -2
- package/dist/live/file-values.js +2 -1
- package/dist/live/messages.js +18 -0
- package/dist/package.js +1 -1
- package/dist/record-view/action-menu.js +2 -1
- package/dist/record-view/action-rail.js +2 -1
- package/dist/record-view/field.js +12 -7
- package/dist/record-view/messages.js +38 -0
- package/dist/record-view/record-header.js +8 -10
- package/dist/shell/assets/app-Bc3JCvDX.js +69 -0
- package/dist/shell/assets/export-worker-Bz57Pf5g.js +2 -0
- package/dist/shell/assets/{index-CPnp4jVD.js → index-DMPV9_11.js} +1 -1
- package/dist/shell/assets/{maplibre-CJlfYYmB.js → maplibre-CqykHSB6.js} +1 -1
- package/dist/shell/assets/{markdown-content-BDgnIKSJ.js → markdown-content-BmKfQOZ6.js} +1 -1
- package/dist/shell/assets/{record-map-BCWeSTdZ.js → record-map-D2ccAykf.js} +1 -1
- package/dist/shell/assets/{records-map-BhNGzm42.js → records-map-DKQG53I0.js} +1 -1
- package/dist/shell/assets/{shared-protobase-ui-C6_QptrK.js → shared-protobase-ui-BCsFQ2PD.js} +1 -1
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/package.json +4 -4
- package/dist/shell/assets/app-BPh2QhzJ.js +0 -69
- package/dist/shell/assets/export-worker-O1HF9R4j.js +0 -2
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
import { DataRow } from "./grid-row.js";
|
|
2
3
|
import { GripVertical } from "lucide-react";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
import { DndContext, KeyboardSensor, MouseSensor, TouchSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
|
|
5
6
|
import { SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
6
7
|
//#region src/data-grid/row-reorder.tsx
|
|
7
|
-
const instructions = "To move a row, press Space on its handle, move it with the up and down arrow keys, and press Space again to drop it there. Escape puts it back.";
|
|
8
8
|
/** The rows of a grid that are moved by hand, `ids` in their order, each loaded or not, so the drop's index is the row's place in the whole list. */
|
|
9
9
|
const ReorderRows = ({ ids, rowAt, reorder, children }) => {
|
|
10
10
|
const sensors = useSensors(useSensor(MouseSensor, { activationConstraint: { distance: 4 } }), useSensor(TouchSensor, { activationConstraint: {
|
|
@@ -14,13 +14,13 @@ const ReorderRows = ({ ids, rowAt, reorder, children }) => {
|
|
|
14
14
|
const place = (id) => ids.indexOf(String(id));
|
|
15
15
|
const name = (id) => {
|
|
16
16
|
const row = rowAt(place(id));
|
|
17
|
-
return row ? reorder.label(row) : "row";
|
|
17
|
+
return row ? reorder.label(row) : text("row");
|
|
18
18
|
};
|
|
19
19
|
const announcements = {
|
|
20
|
-
onDragStart: ({ active }) =>
|
|
21
|
-
onDragOver: ({ active, over }) => over ?
|
|
22
|
-
onDragEnd: ({ active, over }) => over ?
|
|
23
|
-
onDragCancel: ({ active }) =>
|
|
20
|
+
onDragStart: ({ active }) => text("pickedUp", name(active.id), place(active.id) + 1, ids.length),
|
|
21
|
+
onDragOver: ({ active, over }) => over ? text("isOver", name(active.id), place(over.id) + 1, ids.length) : void 0,
|
|
22
|
+
onDragEnd: ({ active, over }) => over ? text("droppedAt", name(active.id), place(over.id) + 1, ids.length) : text("putBack", name(active.id)),
|
|
23
|
+
onDragCancel: ({ active }) => text("cancelled", name(active.id), place(active.id) + 1)
|
|
24
24
|
};
|
|
25
25
|
const end = ({ active, over }) => {
|
|
26
26
|
if (!over || active.id === over.id) return;
|
|
@@ -32,7 +32,7 @@ const ReorderRows = ({ ids, rowAt, reorder, children }) => {
|
|
|
32
32
|
onDragEnd: end,
|
|
33
33
|
accessibility: {
|
|
34
34
|
announcements,
|
|
35
|
-
screenReaderInstructions: { draggable:
|
|
35
|
+
screenReaderInstructions: { draggable: text("reorderInstructions") }
|
|
36
36
|
},
|
|
37
37
|
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
38
38
|
items: ids,
|
|
@@ -47,7 +47,7 @@ const SortableRow = ({ row, label, ...props }) => {
|
|
|
47
47
|
const handle = /* @__PURE__ */ jsx("button", {
|
|
48
48
|
ref: setActivatorNodeRef,
|
|
49
49
|
type: "button",
|
|
50
|
-
"aria-label":
|
|
50
|
+
"aria-label": text("move", label),
|
|
51
51
|
...attributes,
|
|
52
52
|
...listeners,
|
|
53
53
|
onClick: (event) => event.stopPropagation(),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
import { Lock, X } from "lucide-react";
|
|
2
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
//#region src/filter-panel/active-filter-chips.tsx
|
|
@@ -15,7 +16,7 @@ const ActiveFilterChips = ({ chips, lockedLabels = [], onRemove, onClear, scroll
|
|
|
15
16
|
className: chipClass,
|
|
16
17
|
children: [chip.label, /* @__PURE__ */ jsx("button", {
|
|
17
18
|
type: "button",
|
|
18
|
-
"aria-label":
|
|
19
|
+
"aria-label": text("remove", chip.label),
|
|
19
20
|
onClick: () => onRemove(chip.key),
|
|
20
21
|
className: "rounded-full p-0.5 hover:bg-primary-border/60",
|
|
21
22
|
children: /* @__PURE__ */ jsx(X, { className: "size-3" })
|
|
@@ -25,7 +26,7 @@ const ActiveFilterChips = ({ chips, lockedLabels = [], onRemove, onClear, scroll
|
|
|
25
26
|
type: "button",
|
|
26
27
|
onClick: onClear,
|
|
27
28
|
className: "ml-1 text-xs font-medium text-muted-foreground hover:text-foreground",
|
|
28
|
-
children: "
|
|
29
|
+
children: text("clearAll")
|
|
29
30
|
})
|
|
30
31
|
]
|
|
31
32
|
});
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
-
import {
|
|
2
|
+
import { text } from "./messages.js";
|
|
3
|
+
import { datePresetLabel, defaultDatePresets } from "./filter-config.js";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
//#region src/filter-panel/date-presets.tsx
|
|
5
6
|
const DatePresets = ({ value, presets = defaultDatePresets, onChange }) => /* @__PURE__ */ jsx("div", {
|
|
6
7
|
className: "flex flex-wrap gap-1.5",
|
|
7
8
|
children: presets.map((id) => {
|
|
8
|
-
const label =
|
|
9
|
+
const label = datePresetLabel(id);
|
|
9
10
|
return /* @__PURE__ */ jsx("button", {
|
|
10
11
|
type: "button",
|
|
11
12
|
disabled: !label,
|
|
12
|
-
title: label ? void 0 :
|
|
13
|
+
title: label ? void 0 : text("unknownPreset", id),
|
|
13
14
|
onClick: () => onChange(value === id ? void 0 : id),
|
|
14
15
|
className: cn("h-7 rounded-md border px-2.5 text-xs font-medium transition-colors", !label && "border-dashed text-danger-text", value === id ? "border-primary-border bg-primary-soft text-primary-text" : "border-border-strong bg-background text-muted-foreground hover:bg-muted"),
|
|
15
|
-
children: label ??
|
|
16
|
+
children: label ?? text("unsupported", id)
|
|
16
17
|
}, id);
|
|
17
18
|
})
|
|
18
19
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { formatInt } from "../format/number.js";
|
|
3
|
+
import { text } from "./messages.js";
|
|
3
4
|
import { Token } from "../primitives/token.js";
|
|
4
5
|
import { facetLabel } from "./filter-state.js";
|
|
5
6
|
import { comboboxOptions, moveActive } from "./combobox-options.js";
|
|
@@ -64,7 +65,7 @@ const FacetCombobox = ({ config, selected, onToggle, inline = false }) => {
|
|
|
64
65
|
"aria-controls": `${id}-options`,
|
|
65
66
|
"aria-autocomplete": "list",
|
|
66
67
|
"aria-activedescendant": options[active] ? `${id}-${active}` : void 0,
|
|
67
|
-
placeholder: selected.length > 0 ? "" :
|
|
68
|
+
placeholder: selected.length > 0 ? "" : text("searchIn", config.label),
|
|
68
69
|
className: "h-6 min-w-16 flex-1 bg-transparent text-foreground outline-none placeholder:text-faint-foreground",
|
|
69
70
|
value: query,
|
|
70
71
|
onChange: (event) => search(event.target.value),
|
|
@@ -113,7 +114,7 @@ const FacetCombobox = ({ config, selected, onToggle, inline = false }) => {
|
|
|
113
114
|
}, option.value);
|
|
114
115
|
}), options.length === 0 && /* @__PURE__ */ jsx("li", {
|
|
115
116
|
className: "px-1.5 py-1 text-xs text-faint-foreground",
|
|
116
|
-
children: "
|
|
117
|
+
children: text("noMatches")
|
|
117
118
|
})]
|
|
118
119
|
})]
|
|
119
120
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Input } from "../primitives/input.js";
|
|
2
2
|
import { formatInt } from "../format/number.js";
|
|
3
3
|
import { Checkbox } from "../primitives/checkbox.js";
|
|
4
|
+
import { text } from "./messages.js";
|
|
4
5
|
import { matchesSearch } from "./filter-state.js";
|
|
5
6
|
import { useState } from "react";
|
|
6
7
|
import { Search } from "lucide-react";
|
|
@@ -15,7 +16,7 @@ const FacetGroup = ({ config, selected, onToggle }) => {
|
|
|
15
16
|
config.searchable && /* @__PURE__ */ jsx(Input, {
|
|
16
17
|
wrapperClassName: "mb-1 h-7",
|
|
17
18
|
leading: /* @__PURE__ */ jsx(Search, { className: "size-3.5" }),
|
|
18
|
-
placeholder:
|
|
19
|
+
placeholder: text("searchIn", config.label),
|
|
19
20
|
value: query,
|
|
20
21
|
onChange: (event) => setQuery(event.target.value)
|
|
21
22
|
}),
|
|
@@ -39,7 +40,7 @@ const FacetGroup = ({ config, selected, onToggle }) => {
|
|
|
39
40
|
}, option.value)),
|
|
40
41
|
options.length === 0 && /* @__PURE__ */ jsx("span", {
|
|
41
42
|
className: "px-1 py-1 text-xs text-faint-foreground",
|
|
42
|
-
children: "
|
|
43
|
+
children: text("noMatches")
|
|
43
44
|
})
|
|
44
45
|
]
|
|
45
46
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
//#region src/filter-panel/filter-config.ts
|
|
2
3
|
const defaultDatePresets = [
|
|
3
4
|
"today",
|
|
@@ -5,16 +6,18 @@ const defaultDatePresets = [
|
|
|
5
6
|
"30d",
|
|
6
7
|
"year"
|
|
7
8
|
];
|
|
8
|
-
const
|
|
9
|
-
today
|
|
10
|
-
yesterday
|
|
11
|
-
"24h"
|
|
12
|
-
"7d"
|
|
13
|
-
"30d"
|
|
14
|
-
"90d"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
const knownPresets = /* @__PURE__ */ new Set([
|
|
10
|
+
"today",
|
|
11
|
+
"yesterday",
|
|
12
|
+
"24h",
|
|
13
|
+
"7d",
|
|
14
|
+
"30d",
|
|
15
|
+
"90d",
|
|
16
|
+
"month",
|
|
17
|
+
"quarter",
|
|
18
|
+
"year"
|
|
19
|
+
]);
|
|
20
|
+
/** A date preset's label in the screens' language; undefined for one the server offers that this UI does not know. */
|
|
21
|
+
const datePresetLabel = (id) => knownPresets.has(id) ? text(id) : void 0;
|
|
19
22
|
//#endregion
|
|
20
|
-
export {
|
|
23
|
+
export { datePresetLabel, defaultDatePresets };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
import { ActiveFilterChips } from "./active-filter-chips.js";
|
|
2
3
|
import { DatePresets } from "./date-presets.js";
|
|
3
4
|
import { activeChips, emptyFilterState, removeChip, setRange, toggleFacetValue, toggleFlag } from "./filter-state.js";
|
|
@@ -67,7 +68,7 @@ const FilterPanel = ({ config, layout, value, defaultValue, onChange, lockedLabe
|
|
|
67
68
|
className: "flex items-center justify-between pb-1",
|
|
68
69
|
children: /* @__PURE__ */ jsx("h2", {
|
|
69
70
|
className: "font-semibold",
|
|
70
|
-
children: "
|
|
71
|
+
children: text("filters")
|
|
71
72
|
})
|
|
72
73
|
}),
|
|
73
74
|
(chips.length > 0 || (lockedLabels?.length ?? 0) > 0) && /* @__PURE__ */ jsx("div", {
|
|
@@ -84,11 +85,11 @@ const FilterPanel = ({ config, layout, value, defaultValue, onChange, lockedLabe
|
|
|
84
85
|
children: rangeView(range)
|
|
85
86
|
}, range.id)),
|
|
86
87
|
dateView && /* @__PURE__ */ jsx(FilterSection, {
|
|
87
|
-
title: "
|
|
88
|
+
title: text("date"),
|
|
88
89
|
children: dateView
|
|
89
90
|
}),
|
|
90
91
|
toggleViews.length > 0 && /* @__PURE__ */ jsx(FilterSection, {
|
|
91
|
-
title: "
|
|
92
|
+
title: text("showOnly"),
|
|
92
93
|
children: toggleViews
|
|
93
94
|
})
|
|
94
95
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { datePresetLabel } from "./filter-config.js";
|
|
2
2
|
//#region src/filter-panel/filter-state.ts
|
|
3
3
|
const emptyFilterState = {
|
|
4
4
|
facets: {},
|
|
@@ -46,7 +46,7 @@ const activeChips = (config, state) => {
|
|
|
46
46
|
});
|
|
47
47
|
const dateChips = state.date ? [{
|
|
48
48
|
key: "date",
|
|
49
|
-
label:
|
|
49
|
+
label: datePresetLabel(state.date) ?? state.date
|
|
50
50
|
}] : [];
|
|
51
51
|
const toggleChips = state.toggles.map((id) => ({
|
|
52
52
|
key: `toggle:${id}`,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/filter-panel/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
today: "Today",
|
|
6
|
+
yesterday: "Yesterday",
|
|
7
|
+
"24h": "Last 24 hours",
|
|
8
|
+
"7d": "Last 7 days",
|
|
9
|
+
"30d": "Last 30 days",
|
|
10
|
+
"90d": "Last 90 days",
|
|
11
|
+
month: "This month",
|
|
12
|
+
quarter: "This quarter",
|
|
13
|
+
year: "This year",
|
|
14
|
+
unknownPreset: (id) => `The server offers the preset "${id}", which this UI does not know`,
|
|
15
|
+
unsupported: (id) => `${id} (unsupported)`,
|
|
16
|
+
remove: (label) => `Remove ${label}`,
|
|
17
|
+
clearAll: "Clear all",
|
|
18
|
+
searchIn: (label) => `Search ${label.toLowerCase()}`,
|
|
19
|
+
noMatches: "No matches",
|
|
20
|
+
moreFilters: "More filters",
|
|
21
|
+
filters: "Filters",
|
|
22
|
+
date: "Date",
|
|
23
|
+
showOnly: "Show only",
|
|
24
|
+
minimum: (label) => `${label} minimum`,
|
|
25
|
+
maximum: (label) => `${label} maximum`
|
|
26
|
+
},
|
|
27
|
+
nl: {
|
|
28
|
+
today: "Vandaag",
|
|
29
|
+
yesterday: "Gisteren",
|
|
30
|
+
"24h": "Afgelopen 24 uur",
|
|
31
|
+
"7d": "Afgelopen 7 dagen",
|
|
32
|
+
"30d": "Afgelopen 30 dagen",
|
|
33
|
+
"90d": "Afgelopen 90 dagen",
|
|
34
|
+
month: "Deze maand",
|
|
35
|
+
quarter: "Dit kwartaal",
|
|
36
|
+
year: "Dit jaar",
|
|
37
|
+
unknownPreset: (id) => `De server biedt de periode "${id}", die deze UI niet kent`,
|
|
38
|
+
unsupported: (id) => `${id} (niet ondersteund)`,
|
|
39
|
+
remove: (label) => `${label} verwijderen`,
|
|
40
|
+
clearAll: "Alles wissen",
|
|
41
|
+
searchIn: (label) => `Zoeken in ${label.toLowerCase()}`,
|
|
42
|
+
noMatches: "Niets gevonden",
|
|
43
|
+
moreFilters: "Meer filters",
|
|
44
|
+
filters: "Filters",
|
|
45
|
+
date: "Datum",
|
|
46
|
+
showOnly: "Alleen tonen",
|
|
47
|
+
minimum: (label) => `${label} minimum`,
|
|
48
|
+
maximum: (label) => `${label} maximum`
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
//#endregion
|
|
52
|
+
export { text };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { menuItemClass } from "../primitives/menu-item.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { FilterMenu } from "./filter-menu.js";
|
|
3
4
|
import { menuKeyTarget } from "../primitives/menu-keys.js";
|
|
4
5
|
import { useEffect, useRef } from "react";
|
|
@@ -16,7 +17,7 @@ const Items = ({ items, onPick, close }) => {
|
|
|
16
17
|
};
|
|
17
18
|
return /* @__PURE__ */ jsx("ul", {
|
|
18
19
|
role: "menu",
|
|
19
|
-
"aria-label": "
|
|
20
|
+
"aria-label": text("moreFilters"),
|
|
20
21
|
onKeyDown,
|
|
21
22
|
className: "-mx-3 -my-3",
|
|
22
23
|
children: items.map((item, index) => /* @__PURE__ */ jsx("li", {
|
|
@@ -41,7 +42,7 @@ const Items = ({ items, onPick, close }) => {
|
|
|
41
42
|
const MoreFiltersMenu = ({ items, onPick }) => {
|
|
42
43
|
if (items.length === 0) return null;
|
|
43
44
|
return /* @__PURE__ */ jsx(FilterMenu, {
|
|
44
|
-
label: "
|
|
45
|
+
label: text("moreFilters"),
|
|
45
46
|
activeCount: 0,
|
|
46
47
|
children: (close) => /* @__PURE__ */ jsx(Items, {
|
|
47
48
|
items,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { useDebounced } from "../lib/use-debounced.js";
|
|
3
4
|
import { barInRange, fraction, moveThumb } from "./range-math.js";
|
|
4
5
|
import { useEffect, useState } from "react";
|
|
@@ -48,7 +49,7 @@ const RangeFilter = ({ config, value, onChange }) => {
|
|
|
48
49
|
children: [0, 1].map((thumb) => /* @__PURE__ */ jsx("input", {
|
|
49
50
|
type: "range",
|
|
50
51
|
className: "pb-range",
|
|
51
|
-
"aria-label":
|
|
52
|
+
"aria-label": thumb === 0 ? text("minimum", config.label) : text("maximum", config.label),
|
|
52
53
|
min,
|
|
53
54
|
max,
|
|
54
55
|
step,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
//#region src/filter-panel/secondary-filters.ts
|
|
2
3
|
/** The filter a chip belongs to: `facet:<id>`, `range:<id>`, `toggle:<id>` or `date`. A filter is active exactly when it has a chip. */
|
|
3
4
|
const activeFilterKeys = (chips) => [...new Set(chips.map((chip) => chip.key.split(":").slice(0, 2).join(":")))];
|
|
@@ -21,7 +22,7 @@ const barFilters = (config, revealed) => {
|
|
|
21
22
|
})),
|
|
22
23
|
...config.datePresets ? [{
|
|
23
24
|
key: "date",
|
|
24
|
-
label: "
|
|
25
|
+
label: text("date"),
|
|
25
26
|
secondary: config.dateSecondary
|
|
26
27
|
}] : [],
|
|
27
28
|
...(config.toggles ?? []).map((toggle) => ({
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/forms/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
loading: "Loading",
|
|
6
|
+
notFound: "Not found",
|
|
7
|
+
noForm: "There is no form at this address.",
|
|
8
|
+
setByDatabase: "Set by the database",
|
|
9
|
+
notSent: "Not sent",
|
|
10
|
+
send: "Send",
|
|
11
|
+
checkTooLong: "The check that you are not a bot took too long; send the form again"
|
|
12
|
+
},
|
|
13
|
+
nl: {
|
|
14
|
+
loading: "Laden",
|
|
15
|
+
notFound: "Niet gevonden",
|
|
16
|
+
noForm: "Er is geen formulier op dit adres.",
|
|
17
|
+
setByDatabase: "Ingevuld door de database",
|
|
18
|
+
notSent: "Niet verzonden",
|
|
19
|
+
send: "Verzenden",
|
|
20
|
+
checkTooLong: "De controle of je geen bot bent duurde te lang; verzend het formulier opnieuw"
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { text };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
import { solveChallenge } from "altcha-lib";
|
|
2
3
|
import { deriveKey } from "altcha-lib/algorithms/web/sha";
|
|
3
4
|
//#region src/forms/prove-human.ts
|
|
@@ -11,7 +12,7 @@ const proveHuman = async (client, name) => {
|
|
|
11
12
|
challenge,
|
|
12
13
|
deriveKey
|
|
13
14
|
});
|
|
14
|
-
if (!solution) throw new Error("
|
|
15
|
+
if (!solution) throw new Error(text("checkTooLong"));
|
|
15
16
|
return {
|
|
16
17
|
challenge,
|
|
17
18
|
solution: {
|
|
@@ -5,6 +5,7 @@ import { useClient } from "../data/api-provider.js";
|
|
|
5
5
|
import { AuthLayout } from "../auth/auth-layout.js";
|
|
6
6
|
import { humanize } from "../live/naming.js";
|
|
7
7
|
import { Field } from "../record-view/field.js";
|
|
8
|
+
import { text } from "../app/meta-gate-messages.js";
|
|
8
9
|
import { Notice } from "../app/meta-gate.js";
|
|
9
10
|
import { createBody, hasDatabaseDefault, initialDraft, isRequired } from "../app/create/create-values.js";
|
|
10
11
|
import { useDocumentTitle } from "../app/document-title.js";
|
|
@@ -12,6 +13,7 @@ import { ErrorBanner } from "../app/error-banner.js";
|
|
|
12
13
|
import { FieldEditor } from "../app/field-editor.js";
|
|
13
14
|
import { fieldErrors, fieldMessage } from "../app/field-errors.js";
|
|
14
15
|
import { useFocusFirstInvalid } from "../app/use-focus-first-invalid.js";
|
|
16
|
+
import { text as text$1 } from "./messages.js";
|
|
15
17
|
import { proveHuman } from "./prove-human.js";
|
|
16
18
|
import { useState } from "react";
|
|
17
19
|
import { isApiError } from "@protobase/client";
|
|
@@ -30,16 +32,20 @@ const PublicFormPage = ({ name }) => {
|
|
|
30
32
|
});
|
|
31
33
|
if (form.isPending) return /* @__PURE__ */ jsxs("div", {
|
|
32
34
|
className: "flex h-full min-h-48 items-center justify-center gap-2 text-muted-foreground",
|
|
33
|
-
children: [
|
|
35
|
+
children: [
|
|
36
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
37
|
+
" ",
|
|
38
|
+
text$1("loading")
|
|
39
|
+
]
|
|
34
40
|
});
|
|
35
41
|
if (form.error) {
|
|
36
42
|
if (isApiError(form.error) && form.error.status === 404) return /* @__PURE__ */ jsx(Notice, {
|
|
37
|
-
title: "
|
|
38
|
-
children: "
|
|
43
|
+
title: text$1("notFound"),
|
|
44
|
+
children: text$1("noForm")
|
|
39
45
|
});
|
|
40
46
|
return /* @__PURE__ */ jsx(Notice, {
|
|
41
|
-
title: "
|
|
42
|
-
children: isApiError(form.error) ? form.error.message : "
|
|
47
|
+
title: text("cannotReach"),
|
|
48
|
+
children: isApiError(form.error) ? form.error.message : text("noAnswer")
|
|
43
49
|
});
|
|
44
50
|
}
|
|
45
51
|
return /* @__PURE__ */ jsx(LoadedForm, { form: form.data });
|
|
@@ -117,14 +123,14 @@ const LoadedForm = ({ form }) => {
|
|
|
117
123
|
hints,
|
|
118
124
|
value: draft[name] ?? (field.type === "boolean" ? false : ""),
|
|
119
125
|
stored: void 0,
|
|
120
|
-
placeholder: hasDatabaseDefault(field) ? "
|
|
126
|
+
placeholder: hasDatabaseDefault(field) ? text$1("setByDatabase") : void 0,
|
|
121
127
|
invalid: Boolean(problem),
|
|
122
128
|
onChange: (next) => change(name, next)
|
|
123
129
|
})
|
|
124
130
|
}, name);
|
|
125
131
|
}),
|
|
126
132
|
failure !== void 0 && /* @__PURE__ */ jsx(ErrorBanner, {
|
|
127
|
-
title: "
|
|
133
|
+
title: text$1("notSent"),
|
|
128
134
|
error: failure
|
|
129
135
|
}),
|
|
130
136
|
/* @__PURE__ */ jsx(Button, {
|
|
@@ -132,7 +138,7 @@ const LoadedForm = ({ form }) => {
|
|
|
132
138
|
type: "submit",
|
|
133
139
|
loading: sending,
|
|
134
140
|
className: "min-h-10",
|
|
135
|
-
children: "
|
|
141
|
+
children: text$1("send")
|
|
136
142
|
})
|
|
137
143
|
]
|
|
138
144
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { formatTag } from "../locale/active-locale.js";
|
|
2
2
|
import { viewerZone, zoneLabel } from "../format/zone.js";
|
|
3
3
|
import { formatDate, formatDateTime, formatRelativeTime } from "../format/date.js";
|
|
4
|
+
import { text } from "./messages.js";
|
|
4
5
|
import { fileShown } from "./file-values.js";
|
|
5
6
|
import { enumLabel } from "@protobase/schema";
|
|
6
7
|
//#region src/live/display-value.ts
|
|
@@ -12,7 +13,7 @@ const affixed = (text, hints) => `${hints.prefix ?? ""}${text}${hints.suffix ??
|
|
|
12
13
|
const displayValue = (field, value, hints = {}, zone = viewerZone(), now = Date.now()) => {
|
|
13
14
|
if (value === null || value === void 0 || value === "") return "—";
|
|
14
15
|
switch (field.type) {
|
|
15
|
-
case "boolean": return value ? "
|
|
16
|
+
case "boolean": return value ? text("yes") : text("no");
|
|
16
17
|
case "file": return fileShown(value)?.name ?? "—";
|
|
17
18
|
case "enum": return enumLabel(String(value), hints.valueLabels);
|
|
18
19
|
case "timestamp": {
|
|
@@ -39,7 +40,7 @@ const valueTitle = (field, value, zone = viewerZone(), hints = {}) => {
|
|
|
39
40
|
const ms = Date.parse(value);
|
|
40
41
|
if (Number.isNaN(ms)) return void 0;
|
|
41
42
|
if (hints.format === "relative") return formatDateTime(ms, zone);
|
|
42
|
-
return
|
|
43
|
+
return text("shownIn", zoneLabel(ms, zone));
|
|
43
44
|
};
|
|
44
45
|
//#endregion
|
|
45
46
|
export { displayValue, valueTitle };
|
package/dist/live/file-values.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
//#region src/live/file-values.ts
|
|
2
3
|
const isStoredFile = (value) => typeof value === "object" && value !== null && typeof value.uri === "string";
|
|
3
4
|
const isFileDraft = (value) => typeof value === "object" && value !== null && typeof value.uploaded?.value === "string";
|
|
@@ -10,7 +11,7 @@ const fileShown = (value) => {
|
|
|
10
11
|
};
|
|
11
12
|
if (!isStoredFile(value)) return void 0;
|
|
12
13
|
if ("missing" in value) return {
|
|
13
|
-
name: "
|
|
14
|
+
name: text("fileMissing"),
|
|
14
15
|
type: "",
|
|
15
16
|
missing: true
|
|
16
17
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/live/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
yes: "Yes",
|
|
6
|
+
no: "No",
|
|
7
|
+
shownIn: (zone) => `Shown in ${zone}`,
|
|
8
|
+
fileMissing: "File missing"
|
|
9
|
+
},
|
|
10
|
+
nl: {
|
|
11
|
+
yes: "Ja",
|
|
12
|
+
no: "Nee",
|
|
13
|
+
shownIn: (zone) => `Getoond in ${zone}`,
|
|
14
|
+
fileMissing: "Bestand ontbreekt"
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
export { text };
|
package/dist/package.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import "../primitives/menu-item.js";
|
|
3
4
|
import { usePopoverMenu } from "../primitives/popover-menu.js";
|
|
4
5
|
import { MoreVertical } from "lucide-react";
|
|
@@ -9,7 +10,7 @@ import { createPortal } from "react-dom";
|
|
|
9
10
|
* The actions of an action rail as a menu behind a "..." button, as a mobile screen's top bar holds them: each item the
|
|
10
11
|
* rail's, a link where the rail draws one, disabled with its reason when it cannot run. `className` styles the button.
|
|
11
12
|
*/
|
|
12
|
-
const ActionMenu = ({ actions, label = "
|
|
13
|
+
const ActionMenu = ({ actions, label = text("actions"), className }) => {
|
|
13
14
|
const { open, setOpen, place, button, menu } = usePopoverMenu();
|
|
14
15
|
if (actions.length === 0) return null;
|
|
15
16
|
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
4
|
//#region src/record-view/action-rail.tsx
|
|
4
5
|
const itemClass = "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";
|
|
@@ -7,7 +8,7 @@ const ActionRail = ({ actions }) => /* @__PURE__ */ jsxs("div", {
|
|
|
7
8
|
className: "overflow-hidden rounded-lg border border-border bg-background",
|
|
8
9
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
9
10
|
className: "hidden border-b border-border px-4 py-2.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground md:block",
|
|
10
|
-
children: "
|
|
11
|
+
children: text("actions")
|
|
11
12
|
}), /* @__PURE__ */ jsx("ul", {
|
|
12
13
|
className: "flex flex-wrap gap-2 p-2 md:block md:divide-y md:divide-border md:p-0",
|
|
13
14
|
children: actions.map(({ name, description, icon: Icon, onRun, blocked, running, tone, render }) => {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Avatar } from "../primitives/avatar.js";
|
|
2
2
|
import { FieldControlContext } from "../primitives/field-control.js";
|
|
3
3
|
import { Button } from "../primitives/button.js";
|
|
4
|
+
import { text } from "./messages.js";
|
|
4
5
|
import { useId } from "react";
|
|
5
6
|
import { TriangleAlert } from "lucide-react";
|
|
6
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -31,7 +32,7 @@ const Field = ({ name, label, required, help, example, error, conflict, wide, ch
|
|
|
31
32
|
})]
|
|
32
33
|
}), conflict && /* @__PURE__ */ jsxs("span", {
|
|
33
34
|
className: "inline-flex items-center gap-1.5 text-warning-text",
|
|
34
|
-
title:
|
|
35
|
+
title: text("savedDifferent", conflict.user.name),
|
|
35
36
|
children: [/* @__PURE__ */ jsx(TriangleAlert, { className: "size-3.5" }), /* @__PURE__ */ jsx(Avatar, {
|
|
36
37
|
initials: conflict.user.initials,
|
|
37
38
|
hue: conflict.user.hue,
|
|
@@ -57,7 +58,11 @@ const Field = ({ name, label, required, help, example, error, conflict, wide, ch
|
|
|
57
58
|
example && /* @__PURE__ */ jsxs("p", {
|
|
58
59
|
id: `${id}-example`,
|
|
59
60
|
className: "mt-0.5 text-xs text-faint-foreground",
|
|
60
|
-
children: [
|
|
61
|
+
children: [
|
|
62
|
+
text("example"),
|
|
63
|
+
" ",
|
|
64
|
+
example
|
|
65
|
+
]
|
|
61
66
|
}),
|
|
62
67
|
error && /* @__PURE__ */ jsx("p", {
|
|
63
68
|
id: `${id}-error`,
|
|
@@ -68,20 +73,20 @@ const Field = ({ name, label, required, help, example, error, conflict, wide, ch
|
|
|
68
73
|
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",
|
|
69
74
|
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
70
75
|
/* @__PURE__ */ jsx("strong", { children: conflict.user.name }),
|
|
71
|
-
"
|
|
72
|
-
conflict.ago,
|
|
73
|
-
"
|
|
76
|
+
" ",
|
|
77
|
+
text("savedThis", conflict.ago),
|
|
78
|
+
" ",
|
|
74
79
|
/* @__PURE__ */ jsx("strong", { children: conflict.theirValue })
|
|
75
80
|
] }), /* @__PURE__ */ jsxs("span", {
|
|
76
81
|
className: "flex gap-1.5",
|
|
77
82
|
children: [/* @__PURE__ */ jsx(Button, {
|
|
78
83
|
size: "sm",
|
|
79
84
|
onClick: conflict.onKeepMine,
|
|
80
|
-
children: "
|
|
85
|
+
children: text("keepMine")
|
|
81
86
|
}), /* @__PURE__ */ jsx(Button, {
|
|
82
87
|
size: "sm",
|
|
83
88
|
onClick: conflict.onUseTheirs,
|
|
84
|
-
children: "
|
|
89
|
+
children: text("useTheirs")
|
|
85
90
|
})]
|
|
86
91
|
})]
|
|
87
92
|
})
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/record-view/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
savedDifferent: (name) => `${name} saved a different value`,
|
|
6
|
+
example: "Example:",
|
|
7
|
+
savedThis: (ago) => `saved this ${ago}:`,
|
|
8
|
+
keepMine: "Keep mine",
|
|
9
|
+
useTheirs: "Use theirs",
|
|
10
|
+
actions: "Actions",
|
|
11
|
+
saving: "Saving...",
|
|
12
|
+
savedAgo: (ago) => `Saved ${ago}`,
|
|
13
|
+
notSavedRetrying: "Not saved, retrying",
|
|
14
|
+
lastEditedBy: "Last edited by",
|
|
15
|
+
lastUpdated: (ago) => `Last updated ${ago}`,
|
|
16
|
+
viewing: (count) => `${count} viewing`,
|
|
17
|
+
savingSubject: (subject) => `Saving ${subject}...`,
|
|
18
|
+
subjectSaved: (subject) => `${subject} saved`
|
|
19
|
+
},
|
|
20
|
+
nl: {
|
|
21
|
+
savedDifferent: (name) => `${name} bewaarde een andere waarde`,
|
|
22
|
+
example: "Voorbeeld:",
|
|
23
|
+
savedThis: (ago) => `bewaarde dit ${ago}:`,
|
|
24
|
+
keepMine: "Mijn waarde houden",
|
|
25
|
+
useTheirs: "Hun waarde gebruiken",
|
|
26
|
+
actions: "Acties",
|
|
27
|
+
saving: "Bewaren...",
|
|
28
|
+
savedAgo: (ago) => `Bewaard ${ago}`,
|
|
29
|
+
notSavedRetrying: "Niet bewaard, opnieuw proberen",
|
|
30
|
+
lastEditedBy: "Laatst bewerkt door",
|
|
31
|
+
lastUpdated: (ago) => `Laatst bijgewerkt ${ago}`,
|
|
32
|
+
viewing: (count) => `${count} kijken mee`,
|
|
33
|
+
savingSubject: (subject) => `${subject} bewaren...`,
|
|
34
|
+
subjectSaved: (subject) => `${subject} bewaard`
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//#endregion
|
|
38
|
+
export { text };
|