@protobase/ui 0.23.4 → 0.23.5
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.d.ts +4 -1
- package/dist/app/app.js +22 -18
- package/dist/app/charts/chart-groups.js +3 -2
- package/dist/app/charts/period-chart.js +3 -2
- package/dist/app/create/create-values.js +1 -1
- package/dist/app/custom-section.js +1 -1
- package/dist/app/displays/board-column.js +2 -1
- package/dist/app/displays/custom-display.js +1 -1
- package/dist/app/displays/display-frame.js +3 -2
- package/dist/app/displays/map-display.js +4 -3
- package/dist/app/embedded-list/cut-off-note.js +2 -1
- package/dist/app/hand-order/hand-sorted-list.js +2 -1
- package/dist/app/hand-order/use-hand-order.js +1 -1
- package/dist/app/import/import-check-step.js +3 -2
- package/dist/app/import/import-counts.js +2 -1
- package/dist/app/import/import-dialog.js +2 -1
- package/dist/app/import/import-result-step.js +2 -1
- package/dist/app/incoming-relations.js +1 -1
- package/dist/app/list-edit/use-list-edits.js +1 -1
- package/dist/app/list-page.js +1 -1
- package/dist/app/main.js +3 -1
- package/dist/app/map/records-map.js +4 -3
- package/dist/app/meta-gate-messages.js +16 -0
- package/dist/app/meta-gate.js +13 -4
- package/dist/app/nav-recent.js +1 -1
- package/dist/app/pages/blocks/link.js +1 -1
- package/dist/app/pages/blocks/progress.js +3 -2
- package/dist/app/pages/blocks/stat.js +2 -1
- package/dist/app/pages/render-node.js +1 -1
- package/dist/app/pages/use-layout-data.js +1 -1
- package/dist/app/record-preview.js +1 -1
- package/dist/app/record-row/record-row-body.js +2 -1
- package/dist/app/saved/saved-records.js +1 -1
- package/dist/app/search/messages.js +18 -0
- package/dist/app/search/search-model.js +6 -5
- package/dist/app/searches/saved-search-rows.js +1 -1
- package/dist/app/searches/saved-searches.js +1 -1
- package/dist/app/searches/update-search-dialog.js +1 -1
- package/dist/app/searches/use-save-search.js +1 -1
- package/dist/app-shell/bottom-bar.js +3 -2
- package/dist/app-shell/breadcrumb.js +2 -1
- package/dist/app-shell/drawer.js +2 -1
- package/dist/app-shell/global-search.js +5 -4
- package/dist/app-shell/messages.js +64 -0
- package/dist/app-shell/profile-menu.js +5 -4
- package/dist/app-shell/sidebar-group.js +6 -5
- package/dist/app-shell/sidebar.js +4 -3
- package/dist/app-shell/theme-switch.js +8 -7
- package/dist/app-shell/top-bar.js +5 -4
- package/dist/data/meta-data.d.ts +3 -0
- package/dist/data/use-meta.js +1 -0
- package/dist/data/use-record.js +1 -1
- package/dist/data/use-update-record.js +1 -1
- package/dist/format/date.js +42 -50
- package/dist/format/intl-cache.js +12 -0
- package/dist/format/messages.js +24 -0
- package/dist/format/money.js +9 -16
- package/dist/format/number-text.js +3 -2
- package/dist/format/number.js +7 -5
- package/dist/format/zone.js +3 -5
- package/dist/invitations/pending-invitation-row.js +2 -1
- package/dist/live/display-value.js +2 -1
- package/dist/locale/active-locale.js +24 -0
- package/dist/locale/app-locale.js +18 -0
- package/dist/locale/messages.js +12 -0
- package/dist/organizations/organizations-page.js +2 -1
- package/dist/package.js +1 -1
- package/dist/shell/assets/app-Di4_EOe7.js +69 -0
- package/dist/shell/assets/export-worker-O1HF9R4j.js +2 -0
- package/dist/shell/assets/{index-Dxk5GdSQ.js → index-DIcL_2pR.js} +2 -2
- package/dist/shell/assets/{maplibre-CzKsADHj.js → maplibre-B8VhXCMb.js} +2 -2
- package/dist/shell/assets/{markdown-content-pV-6NJbB.js → markdown-content-CDcQeWQm.js} +1 -1
- package/dist/shell/assets/{record-map-CVhzR4LM.js → record-map-Bd0yjDXL.js} +1 -1
- package/dist/shell/assets/records-map-QR_MUceu.js +1 -0
- package/dist/shell/assets/{shared-protobase-ui-BxPtBpOH.js → shared-protobase-ui-CWBBcLfz.js} +1 -1
- 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-CZR9_391.js +0 -69
- package/dist/shell/assets/export-worker-C1djGgjD.js +0 -2
- package/dist/shell/assets/records-map-CJLKyY62.js +0 -1
|
@@ -8,8 +8,8 @@ import { InlineFilterChips } from "./filter-chip-list.js";
|
|
|
8
8
|
import { RecordRowBody } from "../record-row/record-row-body.js";
|
|
9
9
|
import { useSavedSearches } from "./use-saved-searches.js";
|
|
10
10
|
import { useSearchRow } from "./use-search-row.js";
|
|
11
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
11
|
import { encodeKey } from "@protobase/schema/internal";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
//#region src/app/searches/saved-search-rows.tsx
|
|
14
14
|
const SearchRowItem = ({ model, view, target, targetView, row }) => {
|
|
15
15
|
const key = encodeKey(recordKey(model, row));
|
|
@@ -9,8 +9,8 @@ import { can } from "../permissions.js";
|
|
|
9
9
|
import { useSavedSearches } from "./use-saved-searches.js";
|
|
10
10
|
import { useDeleteFlow } from "../delete/use-delete-flow.js";
|
|
11
11
|
import { SavedSearchSection } from "./saved-search-section.js";
|
|
12
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
12
|
import { encodeKey } from "@protobase/schema/internal";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
14
|
//#region src/app/searches/saved-searches.tsx
|
|
15
15
|
/**
|
|
16
16
|
* The list page of a view with `searchesOf`: each of the user's saved searches, with what it finds (`SavedSearchSection`),
|
|
@@ -9,9 +9,9 @@ import { useFilterChips } from "./use-filter-chips.js";
|
|
|
9
9
|
import { EmbeddedList } from "../embedded-list/embedded-list.js";
|
|
10
10
|
import { addedFilter, filterChanges, netChange, removedFilter } from "./search-diff.js";
|
|
11
11
|
import { useMemo } from "react";
|
|
12
|
+
import { tableDisplay } from "@protobase/schema/internal";
|
|
12
13
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { useQuery } from "@tanstack/react-query";
|
|
14
|
-
import { tableDisplay } from "@protobase/schema/internal";
|
|
15
15
|
//#region src/app/searches/update-search-dialog.tsx
|
|
16
16
|
/** How many records each side of the diff shows; its count says how many there are in all. */
|
|
17
17
|
const shownLimit = 12;
|
|
@@ -8,8 +8,8 @@ import { can } from "../permissions.js";
|
|
|
8
8
|
import { useRefreshResource } from "../pages/use-layout-data.js";
|
|
9
9
|
import { useSavedSearches } from "./use-saved-searches.js";
|
|
10
10
|
import { useOpenedSearch } from "./use-opened-search.js";
|
|
11
|
-
import { useMutation } from "@tanstack/react-query";
|
|
12
11
|
import { encodeKey } from "@protobase/schema/internal";
|
|
12
|
+
import { useMutation } from "@tanstack/react-query";
|
|
13
13
|
//#region src/app/searches/use-save-search.ts
|
|
14
14
|
const message = (error) => error instanceof Error ? error.message : "Something went wrong";
|
|
15
15
|
/**
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { Ellipsis } from "lucide-react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/app-shell/bottom-bar.tsx
|
|
@@ -13,7 +14,7 @@ const BottomBar = ({ tabs, onNavigate, onMore, moreOpen, moreControls, sticky, r
|
|
|
13
14
|
const moreCurrent = !tabs.some((item) => item.active);
|
|
14
15
|
return /* @__PURE__ */ jsxs("nav", {
|
|
15
16
|
ref,
|
|
16
|
-
"aria-label": "
|
|
17
|
+
"aria-label": text("tabs"),
|
|
17
18
|
className: cn("flex shrink-0 border-t bg-background pb-[env(safe-area-inset-bottom)] md:hidden", sticky && "sticky bottom-0 z-30"),
|
|
18
19
|
children: [tabs.map((item) => {
|
|
19
20
|
const Icon = item.icon;
|
|
@@ -50,7 +51,7 @@ const BottomBar = ({ tabs, onNavigate, onMore, moreOpen, moreControls, sticky, r
|
|
|
50
51
|
className: "size-6",
|
|
51
52
|
strokeWidth: moreCurrent ? 2.2 : 1.9,
|
|
52
53
|
"aria-hidden": true
|
|
53
|
-
}), "
|
|
54
|
+
}), text("more")]
|
|
54
55
|
})]
|
|
55
56
|
});
|
|
56
57
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { Skeleton } from "../primitives/skeleton.js";
|
|
3
4
|
import { ChevronRight } from "lucide-react";
|
|
4
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -7,7 +8,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
7
8
|
const inApp = (event) => event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
|
|
8
9
|
/** The trail of where you are; the last crumb, the current page, shows on phones too. */
|
|
9
10
|
const Breadcrumb = ({ parts, onNavigate }) => /* @__PURE__ */ jsx("nav", {
|
|
10
|
-
"aria-label": "
|
|
11
|
+
"aria-label": text("breadcrumb"),
|
|
11
12
|
className: "flex min-w-0 items-center gap-1.5",
|
|
12
13
|
children: parts.map((part, index) => {
|
|
13
14
|
const last = index === parts.length - 1;
|
package/dist/app-shell/drawer.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
import { useFocusTrap } from "../lib/use-focus-trap.js";
|
|
2
3
|
import { useRef } from "react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -18,7 +19,7 @@ const Drawer = ({ id, open, onClose, children }) => {
|
|
|
18
19
|
id,
|
|
19
20
|
role: "dialog",
|
|
20
21
|
"aria-modal": "true",
|
|
21
|
-
"aria-label": "
|
|
22
|
+
"aria-label": text("navigation"),
|
|
22
23
|
tabIndex: -1,
|
|
23
24
|
className: "absolute inset-y-0 left-0 flex w-60 max-w-[85vw] bg-sidebar shadow-pop outline-none",
|
|
24
25
|
onClick: (event) => event.target.closest("a") && onClose(),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { Input } from "../primitives/input.js";
|
|
3
4
|
import { Kbd } from "../primitives/kbd.js";
|
|
4
5
|
import { Spinner } from "../primitives/spinner.js";
|
|
@@ -65,7 +66,7 @@ const GlobalSearch = ({ search, className = "w-96", autoFocus, showShortcut = tr
|
|
|
65
66
|
leading: search.loading ? /* @__PURE__ */ jsx(Spinner, { className: "size-4" }) : /* @__PURE__ */ jsx(Search, { className: "size-4" }),
|
|
66
67
|
trailing: search.text ? /* @__PURE__ */ jsx("button", {
|
|
67
68
|
type: "button",
|
|
68
|
-
"aria-label": "
|
|
69
|
+
"aria-label": text("clearSearch"),
|
|
69
70
|
className: "-mr-1 inline-flex size-6 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
70
71
|
onMouseDown: (event) => event.preventDefault(),
|
|
71
72
|
onClick: clear,
|
|
@@ -75,7 +76,7 @@ const GlobalSearch = ({ search, className = "w-96", autoFocus, showShortcut = tr
|
|
|
75
76
|
children: /* @__PURE__ */ jsx(Kbd, { children: "⌘K" })
|
|
76
77
|
}) : void 0,
|
|
77
78
|
placeholder: search.placeholder,
|
|
78
|
-
"aria-label": "
|
|
79
|
+
"aria-label": text("globalSearch"),
|
|
79
80
|
role: "combobox",
|
|
80
81
|
"aria-autocomplete": "list",
|
|
81
82
|
"aria-expanded": open,
|
|
@@ -93,11 +94,11 @@ const GlobalSearch = ({ search, className = "w-96", autoFocus, showShortcut = tr
|
|
|
93
94
|
}), open && /* @__PURE__ */ jsxs("div", {
|
|
94
95
|
id: listId,
|
|
95
96
|
role: "listbox",
|
|
96
|
-
"aria-label": "
|
|
97
|
+
"aria-label": text("searchResults"),
|
|
97
98
|
className: "absolute right-0 top-full z-40 mt-1 max-h-96 w-full min-w-72 overflow-y-auto rounded-lg border bg-background py-1 shadow-pop",
|
|
98
99
|
children: [search.groups.length === 0 && /* @__PURE__ */ jsx("p", {
|
|
99
100
|
className: "px-3 py-2 text-muted-foreground",
|
|
100
|
-
children: search.loading ? "
|
|
101
|
+
children: search.loading ? text("searching") : text("noMatches", search.query)
|
|
101
102
|
}), search.groups.map((group) => /* @__PURE__ */ jsxs("div", {
|
|
102
103
|
role: "group",
|
|
103
104
|
"aria-label": group.label,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/app-shell/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
navigation: "Navigation",
|
|
6
|
+
resources: "Resources",
|
|
7
|
+
breadcrumb: "Breadcrumb",
|
|
8
|
+
tabs: "Tabs",
|
|
9
|
+
more: "More",
|
|
10
|
+
openNavigation: "Open navigation",
|
|
11
|
+
backTo: (label) => `Back to ${label}`,
|
|
12
|
+
search: "Search",
|
|
13
|
+
closeSearch: "Close search",
|
|
14
|
+
clearSearch: "Clear search",
|
|
15
|
+
globalSearch: "Global search",
|
|
16
|
+
searchResults: "Search results",
|
|
17
|
+
searching: "Searching",
|
|
18
|
+
noMatches: (query) => `No matches for “${query}”`,
|
|
19
|
+
profileMenu: "Profile menu",
|
|
20
|
+
organizations: "Organizations",
|
|
21
|
+
roles: "Roles",
|
|
22
|
+
signIn: "Sign in",
|
|
23
|
+
signOut: "Sign out",
|
|
24
|
+
theme: "Theme",
|
|
25
|
+
light: "Light",
|
|
26
|
+
dark: "Dark",
|
|
27
|
+
system: "System",
|
|
28
|
+
recent: (plural) => `Recent ${plural}`,
|
|
29
|
+
loading: "Loading...",
|
|
30
|
+
couldNotLoad: "Could not load",
|
|
31
|
+
viewAll: "View all"
|
|
32
|
+
},
|
|
33
|
+
nl: {
|
|
34
|
+
navigation: "Navigatie",
|
|
35
|
+
resources: "Onderdelen",
|
|
36
|
+
breadcrumb: "Kruimelpad",
|
|
37
|
+
tabs: "Tabbladen",
|
|
38
|
+
more: "Meer",
|
|
39
|
+
openNavigation: "Navigatie openen",
|
|
40
|
+
backTo: (label) => `Terug naar ${label}`,
|
|
41
|
+
search: "Zoeken",
|
|
42
|
+
closeSearch: "Zoeken sluiten",
|
|
43
|
+
clearSearch: "Zoekopdracht wissen",
|
|
44
|
+
globalSearch: "Overal zoeken",
|
|
45
|
+
searchResults: "Zoekresultaten",
|
|
46
|
+
searching: "Zoeken",
|
|
47
|
+
noMatches: (query) => `Niets gevonden voor “${query}”`,
|
|
48
|
+
profileMenu: "Profielmenu",
|
|
49
|
+
organizations: "Organisaties",
|
|
50
|
+
roles: "Rollen",
|
|
51
|
+
signIn: "Inloggen",
|
|
52
|
+
signOut: "Uitloggen",
|
|
53
|
+
theme: "Thema",
|
|
54
|
+
light: "Licht",
|
|
55
|
+
dark: "Donker",
|
|
56
|
+
system: "Systeem",
|
|
57
|
+
recent: (plural) => `Recente ${plural}`,
|
|
58
|
+
loading: "Laden...",
|
|
59
|
+
couldNotLoad: "Kon niet laden",
|
|
60
|
+
viewAll: "Alles bekijken"
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
//#endregion
|
|
64
|
+
export { text };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { Avatar } from "../primitives/avatar.js";
|
|
3
4
|
import { Badge } from "../primitives/badge.js";
|
|
4
5
|
import { SwitchGroup } from "./switch-group.js";
|
|
@@ -41,7 +42,7 @@ const ProfileMenu = ({ user, compact, open: controlled, items = [], onNavigate,
|
|
|
41
42
|
children: [/* @__PURE__ */ jsxs("button", {
|
|
42
43
|
type: "button",
|
|
43
44
|
className: "flex min-h-11 w-full items-center gap-2.5 rounded-md p-1 text-left hover:bg-muted md:min-h-0",
|
|
44
|
-
"aria-label": "
|
|
45
|
+
"aria-label": text("profileMenu"),
|
|
45
46
|
"aria-haspopup": "menu",
|
|
46
47
|
"aria-expanded": open,
|
|
47
48
|
onClick: toggle,
|
|
@@ -81,7 +82,7 @@ const ProfileMenu = ({ user, compact, open: controlled, items = [], onNavigate,
|
|
|
81
82
|
})]
|
|
82
83
|
}),
|
|
83
84
|
organizations && (organizations.current || organizations.others.length > 0) && /* @__PURE__ */ jsx(SwitchGroup, {
|
|
84
|
-
label: "
|
|
85
|
+
label: text("organizations"),
|
|
85
86
|
...organizations.current && { current: {
|
|
86
87
|
id: organizations.current.id,
|
|
87
88
|
label: organizations.current.name
|
|
@@ -97,7 +98,7 @@ const ProfileMenu = ({ user, compact, open: controlled, items = [], onNavigate,
|
|
|
97
98
|
}
|
|
98
99
|
}),
|
|
99
100
|
roles && roles.roles.length > 1 && /* @__PURE__ */ jsx(SwitchGroup, {
|
|
100
|
-
label: "
|
|
101
|
+
label: text("roles"),
|
|
101
102
|
...activeRole && { current: {
|
|
102
103
|
id: activeRole.name,
|
|
103
104
|
label: activeRole.label
|
|
@@ -147,7 +148,7 @@ const ProfileMenu = ({ user, compact, open: controlled, items = [], onNavigate,
|
|
|
147
148
|
role: "menuitem",
|
|
148
149
|
onClick: onSignOut,
|
|
149
150
|
className: menuRow,
|
|
150
|
-
children: [/* @__PURE__ */ jsx(LogOut, { className: "size-4" }), "
|
|
151
|
+
children: [/* @__PURE__ */ jsx(LogOut, { className: "size-4" }), text("signOut")]
|
|
151
152
|
})
|
|
152
153
|
]
|
|
153
154
|
})]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { navOpenStorageKey, readNavOpen, writeNavOpen } from "./nav-open-storage.js";
|
|
3
4
|
import { SidebarItem } from "./sidebar-item.js";
|
|
4
5
|
import { StatusDot } from "./status-dot.js";
|
|
@@ -49,23 +50,23 @@ const SidebarGroup = ({ item, recent, mode, active, onNavigate }) => {
|
|
|
49
50
|
type: "button",
|
|
50
51
|
"aria-expanded": open,
|
|
51
52
|
"aria-controls": listId,
|
|
52
|
-
"aria-label":
|
|
53
|
+
"aria-label": text("recent", item.label.toLowerCase()),
|
|
53
54
|
onClick: toggle,
|
|
54
55
|
className: "absolute right-1 top-1/2 inline-flex size-6 -translate-y-1/2 items-center justify-center rounded text-faint-foreground hover:bg-muted hover:text-foreground",
|
|
55
56
|
children: /* @__PURE__ */ jsx(ChevronRight, { className: cn("size-3.5 transition-transform", open && "rotate-90") })
|
|
56
57
|
})]
|
|
57
58
|
}), open && /* @__PURE__ */ jsxs("ul", {
|
|
58
59
|
id: listId,
|
|
59
|
-
"aria-label":
|
|
60
|
+
"aria-label": text("recent", item.label.toLowerCase()),
|
|
60
61
|
className: cn("flex flex-col gap-0.5 border-l pl-1.5", indent[mode]),
|
|
61
62
|
children: [
|
|
62
63
|
recent.state === "loading" && /* @__PURE__ */ jsx("li", {
|
|
63
64
|
className: cn("flex items-center text-faint-foreground", row[mode]),
|
|
64
|
-
children: "
|
|
65
|
+
children: text("loading")
|
|
65
66
|
}),
|
|
66
67
|
recent.state === "error" && /* @__PURE__ */ jsx("li", {
|
|
67
68
|
className: cn("flex items-center text-faint-foreground", row[mode]),
|
|
68
|
-
children: "
|
|
69
|
+
children: text("couldNotLoad")
|
|
69
70
|
}),
|
|
70
71
|
recent.state === "ready" && recent.records.map((record) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
|
|
71
72
|
...link(record.href),
|
|
@@ -84,7 +85,7 @@ const SidebarGroup = ({ item, recent, mode, active, onNavigate }) => {
|
|
|
84
85
|
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", {
|
|
85
86
|
...link(href),
|
|
86
87
|
className: cn("flex items-center rounded-md text-faint-foreground transition-colors hover:bg-muted hover:text-foreground", row[mode]),
|
|
87
|
-
children: "
|
|
88
|
+
children: text("viewAll")
|
|
88
89
|
}) })
|
|
89
90
|
]
|
|
90
91
|
})]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { RouteLink } from "../primitives/route-link.js";
|
|
3
4
|
import { Logo } from "./logo.js";
|
|
4
5
|
import { navGroups } from "./nav.js";
|
|
@@ -15,7 +16,7 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, forceExpanded, pr
|
|
|
15
16
|
const expanded = expanding && forceExpanded;
|
|
16
17
|
const inline = isTextMode(mode) || expanding;
|
|
17
18
|
return /* @__PURE__ */ jsx("nav", {
|
|
18
|
-
"aria-label": "
|
|
19
|
+
"aria-label": text("resources"),
|
|
19
20
|
className: cn(sticky ? "sticky top-0 h-dvh self-start" : "relative", "z-30 shrink-0", sidebarWidth[mode]),
|
|
20
21
|
children: /* @__PURE__ */ jsxs("div", {
|
|
21
22
|
className: cn("group/side absolute inset-y-0 left-0 flex flex-col overflow-hidden border-r bg-sidebar", expanding ? "w-14 transition-[width,box-shadow] duration-150 hover:w-60 hover:shadow-pop focus-within:w-60 focus-within:shadow-pop" : "w-full", expanded && "w-60 shadow-pop"),
|
|
@@ -66,11 +67,11 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, forceExpanded, pr
|
|
|
66
67
|
className: "flex min-h-11 w-full items-center gap-2.5 whitespace-nowrap p-1.5 text-muted-foreground md:min-h-0"
|
|
67
68
|
}), signIn && /* @__PURE__ */ jsxs(RouteLink, {
|
|
68
69
|
to: signIn,
|
|
69
|
-
"aria-label": "
|
|
70
|
+
"aria-label": text("signIn"),
|
|
70
71
|
className: "flex min-h-11 w-full items-center gap-2.5 rounded-md p-1.5 text-left font-medium text-muted-foreground hover:bg-muted hover:text-foreground md:min-h-0",
|
|
71
72
|
children: [/* @__PURE__ */ jsx(LogIn, { className: "size-4 shrink-0" }), inline && /* @__PURE__ */ jsx("span", {
|
|
72
73
|
className: "whitespace-nowrap",
|
|
73
|
-
children: "
|
|
74
|
+
children: text("signIn")
|
|
74
75
|
})]
|
|
75
76
|
})] })
|
|
76
77
|
})
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
+
import { text } from "./messages.js";
|
|
1
2
|
import { Segmented } from "../primitives/segmented.js";
|
|
2
3
|
import { Monitor, Moon, Sun, SunMoon } from "lucide-react";
|
|
3
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/app-shell/theme-switch.tsx
|
|
5
|
-
const options = [
|
|
6
|
+
const options = () => [
|
|
6
7
|
{
|
|
7
8
|
value: "light",
|
|
8
|
-
label: "
|
|
9
|
+
label: text("light"),
|
|
9
10
|
icon: Sun
|
|
10
11
|
},
|
|
11
12
|
{
|
|
12
13
|
value: "dark",
|
|
13
|
-
label: "
|
|
14
|
+
label: text("dark"),
|
|
14
15
|
icon: Moon
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
value: "system",
|
|
18
|
-
label: "
|
|
19
|
+
label: text("system"),
|
|
19
20
|
icon: Monitor
|
|
20
21
|
}
|
|
21
22
|
];
|
|
@@ -26,13 +27,13 @@ const ThemeSwitch = ({ theme, className }) => /* @__PURE__ */ jsxs("div", {
|
|
|
26
27
|
/* @__PURE__ */ jsx(SunMoon, { className: "size-4" }),
|
|
27
28
|
/* @__PURE__ */ jsx("span", {
|
|
28
29
|
className: "flex-1",
|
|
29
|
-
children: "
|
|
30
|
+
children: text("theme")
|
|
30
31
|
}),
|
|
31
32
|
/* @__PURE__ */ jsx(Segmented, {
|
|
32
|
-
label: "
|
|
33
|
+
label: text("theme"),
|
|
33
34
|
iconOnly: true,
|
|
34
35
|
value: theme.preference,
|
|
35
|
-
options,
|
|
36
|
+
options: options(),
|
|
36
37
|
onChange: theme.onChange
|
|
37
38
|
})
|
|
38
39
|
]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
2
3
|
import { Breadcrumb, inApp } from "./breadcrumb.js";
|
|
3
4
|
import { backTarget } from "./back-target.js";
|
|
4
5
|
import { GlobalSearch } from "./global-search.js";
|
|
@@ -30,7 +31,7 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
30
31
|
children: [
|
|
31
32
|
onMenu && /* @__PURE__ */ jsx("button", {
|
|
32
33
|
type: "button",
|
|
33
|
-
"aria-label": "
|
|
34
|
+
"aria-label": text("openNavigation"),
|
|
34
35
|
"aria-expanded": Boolean(menuOpen),
|
|
35
36
|
"aria-controls": menuControls,
|
|
36
37
|
onClick: onMenu,
|
|
@@ -39,7 +40,7 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
39
40
|
}),
|
|
40
41
|
back && /* @__PURE__ */ jsx("a", {
|
|
41
42
|
href: back.href,
|
|
42
|
-
"aria-label":
|
|
43
|
+
"aria-label": text("backTo", back.label),
|
|
43
44
|
onClick: (event) => {
|
|
44
45
|
if (!onNavigate || !inApp(event)) return;
|
|
45
46
|
event.preventDefault();
|
|
@@ -63,7 +64,7 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
63
64
|
})
|
|
64
65
|
}), /* @__PURE__ */ jsx("button", {
|
|
65
66
|
type: "button",
|
|
66
|
-
"aria-label": "
|
|
67
|
+
"aria-label": text("search"),
|
|
67
68
|
"aria-expanded": searchOpen,
|
|
68
69
|
onClick: () => setSearchOpen(true),
|
|
69
70
|
className: "inline-flex size-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted md:hidden",
|
|
@@ -91,7 +92,7 @@ const TopBar = ({ breadcrumb, onNavigate, actions, onMenu, menuOpen, menuControl
|
|
|
91
92
|
}
|
|
92
93
|
}), /* @__PURE__ */ jsx("button", {
|
|
93
94
|
type: "button",
|
|
94
|
-
"aria-label": "
|
|
95
|
+
"aria-label": text("closeSearch"),
|
|
95
96
|
onClick: () => setSearchOpen(false),
|
|
96
97
|
className: "inline-flex size-11 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted md:hidden",
|
|
97
98
|
children: /* @__PURE__ */ jsx(X, { className: "size-5" })
|
package/dist/data/meta-data.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MapLayerModel, PluginRef, ResourcePermissions, SourceRef } from "@protobase/client";
|
|
2
|
+
import { Locale } from "@protobase/schema/internal";
|
|
2
3
|
import { BottomBarModel, ResourceModel, SignInViewModel, UserMenuModel, ViewModel } from "@protobase/schema";
|
|
3
4
|
import { PageModel } from "@protobase/layout";
|
|
4
5
|
//#region src/data/meta-data.d.ts
|
|
@@ -34,6 +35,8 @@ export type MetaData = {
|
|
|
34
35
|
style: string;
|
|
35
36
|
layers?: MapLayerModel[];
|
|
36
37
|
};
|
|
38
|
+
/** The language the app's screens are in for everyone, when it sets one (see `AppLocale`). */
|
|
39
|
+
locale?: Locale;
|
|
37
40
|
/** The hosts besides the app's own whose images are drawn with the page's referrer (see `FileImage`). */
|
|
38
41
|
files?: {
|
|
39
42
|
trustedHosts: string[];
|
package/dist/data/use-meta.js
CHANGED
|
@@ -38,6 +38,7 @@ const useMeta = () => {
|
|
|
38
38
|
...result.meta.runtime && { runtime: result.meta.runtime },
|
|
39
39
|
...result.meta.errorReporting && { errorReporting: result.meta.errorReporting },
|
|
40
40
|
...result.meta.maps && { maps: result.meta.maps },
|
|
41
|
+
...result.meta.locale && { locale: result.meta.locale },
|
|
41
42
|
...result.meta.files && { files: result.meta.files },
|
|
42
43
|
...result.meta.plugins && { plugins: result.meta.plugins },
|
|
43
44
|
...result.meta.sources && { sources: result.meta.sources.map((source) => ({
|
package/dist/data/use-record.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keys } from "./query-keys.js";
|
|
2
2
|
import { useClient } from "./api-provider.js";
|
|
3
|
-
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
4
3
|
import { encodeKey } from "@protobase/schema/internal";
|
|
4
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
5
|
//#region src/data/use-record.ts
|
|
6
6
|
const useRecord = (resource, key, enabled = true) => {
|
|
7
7
|
const client = useClient();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { keys } from "./query-keys.js";
|
|
2
2
|
import { useClient } from "./api-provider.js";
|
|
3
3
|
import { PreconditionFailedError } from "@protobase/client";
|
|
4
|
-
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
4
|
import { encodeKey } from "@protobase/schema/internal";
|
|
5
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
6
6
|
//#region src/data/use-update-record.ts
|
|
7
7
|
/**
|
|
8
8
|
* PATCH with `If-Match`. On success the cached record gets the new ETag and lists refetch;
|
package/dist/format/date.js
CHANGED
|
@@ -1,77 +1,69 @@
|
|
|
1
|
+
import { cachedFormat } from "./intl-cache.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
1
3
|
import { viewerZone } from "./zone.js";
|
|
2
4
|
//#region src/format/date.ts
|
|
3
|
-
const
|
|
5
|
+
const dateFormat = (name, options) => () => cachedFormat(`date:${name}`, (tag) => new Intl.DateTimeFormat(tag, {
|
|
6
|
+
...options,
|
|
7
|
+
timeZone: "UTC"
|
|
8
|
+
}));
|
|
9
|
+
const day = dateFormat("day", {
|
|
4
10
|
day: "numeric",
|
|
5
11
|
month: "short",
|
|
6
|
-
year: "numeric"
|
|
7
|
-
timeZone: "UTC"
|
|
12
|
+
year: "numeric"
|
|
8
13
|
});
|
|
9
|
-
const dayShort =
|
|
14
|
+
const dayShort = dateFormat("dayShort", {
|
|
10
15
|
day: "numeric",
|
|
11
|
-
month: "short"
|
|
12
|
-
timeZone: "UTC"
|
|
16
|
+
month: "short"
|
|
13
17
|
});
|
|
14
|
-
const month =
|
|
18
|
+
const month = dateFormat("month", {
|
|
15
19
|
month: "long",
|
|
16
|
-
year: "numeric"
|
|
17
|
-
timeZone: "UTC"
|
|
20
|
+
year: "numeric"
|
|
18
21
|
});
|
|
19
|
-
const weekday =
|
|
20
|
-
|
|
21
|
-
timeZone: "UTC"
|
|
22
|
-
});
|
|
23
|
-
const weekdayDate = new Intl.DateTimeFormat("en-GB", {
|
|
22
|
+
const weekday = dateFormat("weekday", { weekday: "short" });
|
|
23
|
+
const weekdayDate = dateFormat("weekdayDate", {
|
|
24
24
|
weekday: "long",
|
|
25
25
|
day: "numeric",
|
|
26
26
|
month: "short",
|
|
27
|
-
year: "numeric"
|
|
28
|
-
timeZone: "UTC"
|
|
27
|
+
year: "numeric"
|
|
29
28
|
});
|
|
30
|
-
const formatDate = (ms) => day.format(ms);
|
|
31
|
-
const formatDayShort = (ms) => dayShort.format(ms);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
hour12: false,
|
|
47
|
-
timeZone: zone
|
|
48
|
-
})
|
|
49
|
-
};
|
|
50
|
-
zoned.set(zone, formats);
|
|
51
|
-
return formats;
|
|
52
|
-
};
|
|
29
|
+
const formatDate = (ms) => day().format(ms);
|
|
30
|
+
const formatDayShort = (ms) => dayShort().format(ms);
|
|
31
|
+
const zonedFormats = (zone) => cachedFormat(`zoned:${zone}`, (tag) => ({
|
|
32
|
+
day: new Intl.DateTimeFormat(tag, {
|
|
33
|
+
day: "numeric",
|
|
34
|
+
month: "short",
|
|
35
|
+
year: "numeric",
|
|
36
|
+
timeZone: zone
|
|
37
|
+
}),
|
|
38
|
+
time: new Intl.DateTimeFormat(tag, {
|
|
39
|
+
hour: "2-digit",
|
|
40
|
+
minute: "2-digit",
|
|
41
|
+
hour12: false,
|
|
42
|
+
timeZone: zone
|
|
43
|
+
})
|
|
44
|
+
}));
|
|
53
45
|
/** "6 Oct 2026, 16:32": an instant on the clock of `zone`, the viewer's unless given. */
|
|
54
46
|
const formatDateTime = (ms, zone = viewerZone()) => {
|
|
55
47
|
const formats = zonedFormats(zone);
|
|
56
48
|
return `${formats.day.format(ms)}, ${formats.time.format(ms)}`;
|
|
57
49
|
};
|
|
58
50
|
/** "October 2026". */
|
|
59
|
-
const formatMonth = (ms) => month.format(ms);
|
|
51
|
+
const formatMonth = (ms) => month().format(ms);
|
|
60
52
|
/** "Mon". */
|
|
61
|
-
const formatWeekday = (ms) => weekday.format(ms);
|
|
53
|
+
const formatWeekday = (ms) => weekday().format(ms);
|
|
62
54
|
/** "Monday, 12 Oct 2026". */
|
|
63
|
-
const formatWeekdayDate = (ms) => weekdayDate.format(ms);
|
|
55
|
+
const formatWeekdayDate = (ms) => weekdayDate().format(ms);
|
|
64
56
|
/** "12–18 Oct 2026", "28 Dec 2026 – 3 Jan 2027": the days from `from` to `to`, both included. */
|
|
65
|
-
const formatDayRange = (from, to) => day.formatRange(from, to);
|
|
57
|
+
const formatDayRange = (from, to) => day().formatRange(from, to);
|
|
66
58
|
/** "09:30": an instant's time on the clock of `zone`, the viewer's unless given. */
|
|
67
59
|
const formatTime = (ms, zone = viewerZone()) => zonedFormats(zone).time.format(ms);
|
|
68
60
|
const formatAgo = (seconds) => {
|
|
69
|
-
if (seconds < 60) return
|
|
70
|
-
if (seconds < 3600) return
|
|
71
|
-
if (seconds < 86400) return
|
|
72
|
-
return
|
|
61
|
+
if (seconds < 60) return text("secondsAgo", seconds);
|
|
62
|
+
if (seconds < 3600) return text("minutesAgo", Math.floor(seconds / 60));
|
|
63
|
+
if (seconds < 86400) return text("hoursAgo", Math.floor(seconds / 3600));
|
|
64
|
+
return text("daysAgo", Math.floor(seconds / 86400));
|
|
73
65
|
};
|
|
74
|
-
const relative = new Intl.RelativeTimeFormat(
|
|
66
|
+
const relative = () => cachedFormat("relative", (tag) => new Intl.RelativeTimeFormat(tag, { numeric: "auto" }));
|
|
75
67
|
const units = [
|
|
76
68
|
["year", 31536e3],
|
|
77
69
|
["month", 2592e3],
|
|
@@ -83,8 +75,8 @@ const units = [
|
|
|
83
75
|
const formatRelativeTime = (seconds) => {
|
|
84
76
|
const away = Math.abs(seconds);
|
|
85
77
|
const [unit, size] = units.find(([, size]) => away >= size) ?? ["second", 1];
|
|
86
|
-
if (unit === "second" && away < 45) return "
|
|
87
|
-
return relative.format(-Math.sign(seconds) * Math.floor(away / size), unit);
|
|
78
|
+
if (unit === "second" && away < 45) return text("justNow");
|
|
79
|
+
return relative().format(-Math.sign(seconds) * Math.floor(away / size), unit);
|
|
88
80
|
};
|
|
89
81
|
//#endregion
|
|
90
82
|
export { formatAgo, formatDate, formatDateTime, formatDayRange, formatDayShort, formatMonth, formatRelativeTime, formatTime, formatWeekday, formatWeekdayDate };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { formatTag } from "../locale/active-locale.js";
|
|
2
|
+
//#region src/format/intl-cache.ts
|
|
3
|
+
const cache = /* @__PURE__ */ new Map();
|
|
4
|
+
/** One `Intl` formatter per language tag and `key`, made with `make` for the active locale's tag the first time. */
|
|
5
|
+
const cachedFormat = (key, make) => {
|
|
6
|
+
const tag = formatTag();
|
|
7
|
+
const id = `${tag}|${key}`;
|
|
8
|
+
if (!cache.has(id)) cache.set(id, make(tag));
|
|
9
|
+
return cache.get(id);
|
|
10
|
+
};
|
|
11
|
+
//#endregion
|
|
12
|
+
export { cachedFormat };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/format/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
rows: (count) => `${count} rows`,
|
|
6
|
+
aboutRows: (count) => `about ${count} rows`,
|
|
7
|
+
secondsAgo: (count) => `${count} s ago`,
|
|
8
|
+
minutesAgo: (count) => `${count} min ago`,
|
|
9
|
+
hoursAgo: (count) => `${count} h ago`,
|
|
10
|
+
daysAgo: (count) => `${count} d ago`,
|
|
11
|
+
justNow: "just now"
|
|
12
|
+
},
|
|
13
|
+
nl: {
|
|
14
|
+
rows: (count) => `${count} rijen`,
|
|
15
|
+
aboutRows: (count) => `ongeveer ${count} rijen`,
|
|
16
|
+
secondsAgo: (count) => `${count} s geleden`,
|
|
17
|
+
minutesAgo: (count) => `${count} min geleden`,
|
|
18
|
+
hoursAgo: (count) => `${count} u geleden`,
|
|
19
|
+
daysAgo: (count) => `${count} d geleden`,
|
|
20
|
+
justNow: "zojuist"
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
//#endregion
|
|
24
|
+
export { text };
|
package/dist/format/money.js
CHANGED
|
@@ -1,20 +1,13 @@
|
|
|
1
|
+
import { cachedFormat } from "./intl-cache.js";
|
|
1
2
|
//#region src/format/money.ts
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
...decimals !== void 0 && {
|
|
11
|
-
minimumFractionDigits: decimals,
|
|
12
|
-
maximumFractionDigits: decimals
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
formatters.set(key, created);
|
|
16
|
-
return created;
|
|
17
|
-
};
|
|
3
|
+
const formatterFor = (currency, decimals) => cachedFormat(`money:${currency}:${decimals ?? ""}`, (tag) => new Intl.NumberFormat(tag, {
|
|
4
|
+
style: "currency",
|
|
5
|
+
currency,
|
|
6
|
+
...decimals !== void 0 && {
|
|
7
|
+
minimumFractionDigits: decimals,
|
|
8
|
+
maximumFractionDigits: decimals
|
|
9
|
+
}
|
|
10
|
+
}));
|
|
18
11
|
/** An amount in cents in the currency, with its usual decimals or `decimals` of them. */
|
|
19
12
|
const formatMoney = (cents, currency = "EUR", decimals) => formatterFor(currency, decimals).format(cents / 100);
|
|
20
13
|
//#endregion
|