@protobase/ui 0.3.4 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account/account-pages.js +20 -0
- package/dist/account/account-section.js +31 -0
- package/dist/account/account-security-page.js +59 -0
- package/dist/account/backup-codes.js +44 -0
- package/dist/account/passkeys-section.js +141 -0
- package/dist/account/password-dialog.js +58 -0
- package/dist/account/sign-in-policy-form.js +108 -0
- package/dist/account/sign-in-policy-labels.js +76 -0
- package/dist/account/sign-in-policy-page.js +72 -0
- package/dist/account/staff-sign-in-log.js +62 -0
- package/dist/account/totp-qr.js +37 -0
- package/dist/account/two-factor-section.js +123 -0
- package/dist/account/two-factor-setup.js +188 -0
- package/dist/activity-chart/activity-chart.js +1 -1
- package/dist/activity-chart/activity-tooltip.js +1 -1
- package/dist/app/app.d.ts +3 -1
- package/dist/app/app.js +50 -18
- package/dist/app/assistant-shell.js +1 -1
- package/dist/app/create/create-page.js +1 -1
- package/dist/app/delete/use-delete-flow.js +1 -1
- package/dist/app/error-banner.js +1 -1
- package/dist/app/list-page.js +1 -1
- package/dist/app/list-search.js +1 -1
- package/dist/app/meta-gate.js +1 -1
- package/dist/app/pages/blocks/modal-form.js +3 -3
- package/dist/app/pages/record-table.js +1 -1
- package/dist/app/record-page.js +5 -5
- package/dist/app/runtime/runtime-status.js +25 -0
- package/dist/app/runtime/runtime-update.js +77 -0
- package/dist/app/runtime/use-runtime-status.js +59 -0
- package/dist/app/sensitive-field.js +1 -1
- package/dist/app/staff-banner.js +67 -0
- package/dist/app/use-record-editor.js +1 -1
- package/dist/app-shell/breadcrumb.js +1 -1
- package/dist/app-shell/global-search.js +1 -1
- package/dist/app-shell/profile-menu.js +1 -1
- package/dist/app-shell/sidebar-group.js +1 -1
- package/dist/app-shell/top-bar.js +1 -1
- package/dist/assistant-dock/assistant-dock.js +1 -1
- package/dist/auth/auth-gate.js +42 -4
- package/dist/auth/auth-messages.js +48 -0
- package/dist/auth/auth-provider.js +40 -14
- package/dist/auth/code-input.js +17 -0
- package/dist/auth/email-code-form.js +81 -0
- package/dist/auth/email-code-page.js +40 -0
- package/dist/auth/first-run-page.js +1 -1
- package/dist/auth/forgot-password-page.js +1 -1
- package/dist/auth/reset-password-page.js +1 -1
- package/dist/auth/setup-required-form.js +43 -0
- package/dist/auth/setup-required-page.js +51 -0
- package/dist/auth/sign-in-form.js +53 -20
- package/dist/auth/sign-in-page.js +14 -13
- package/dist/auth/staff-link.js +30 -0
- package/dist/auth/staff-sign-in-form.js +93 -0
- package/dist/auth/staff-sign-in-page.js +41 -0
- package/dist/auth/two-factor-form.js +137 -0
- package/dist/auth/two-factor-page.js +39 -0
- package/dist/auth/user-to-shell.js +5 -2
- package/dist/chat/composer.js +1 -1
- package/dist/data/api-provider.d.ts +1 -1
- package/dist/data/api-provider.js +1 -1
- package/dist/data/meta-data.d.ts +4 -0
- package/dist/data/use-aggregates.js +1 -1
- package/dist/data/use-list.js +1 -1
- package/dist/data/use-meta.js +2 -1
- package/dist/data/use-update-record.js +1 -1
- package/dist/data-grid/cells.js +3 -3
- package/dist/data-grid/grid-header.js +2 -2
- package/dist/data-grid/grid-row.js +1 -1
- package/dist/data-grid/header-menu.js +1 -1
- package/dist/data-grid/row-menu.js +1 -1
- package/dist/diff-view/diff-view.js +1 -1
- package/dist/filter-panel/active-filter-chips.js +1 -1
- package/dist/filter-panel/facet-group.js +1 -1
- package/dist/filter-panel/filter-menu.js +1 -1
- package/dist/inline-child-table/inline-child-table.js +1 -1
- package/dist/inline-child-table/line-menu.js +1 -1
- package/dist/inline-child-table/line-row.js +2 -2
- package/dist/inline-child-table/totals-footer.js +1 -1
- package/dist/lib/use-visual-viewport.js +27 -0
- package/dist/live/use-filter-config.js +1 -1
- package/dist/primitives/checkbox.js +1 -1
- package/dist/primitives/sheet.js +1 -1
- package/dist/primitives/spinner.js +1 -1
- package/dist/record-view/field.js +1 -1
- package/dist/record-view/record-sidebar.js +1 -1
- package/dist/side-panel/side-panel.d.ts +5 -1
- package/dist/side-panel/side-panel.js +49 -35
- package/dist/step-list/step-list.js +1 -1
- package/dist/toasts/toast.js +1 -1
- package/package.json +10 -8
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { authMessage } from "./auth-messages.js";
|
|
2
|
+
import { useAuth } from "./auth-provider.js";
|
|
3
|
+
import { TwoFactorForm } from "./two-factor-form.js";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { AuthError } from "@protobase/client";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
//#region src/auth/two-factor-page.tsx
|
|
8
|
+
/** The second step after a password or an emailed code, with the methods the server named. */
|
|
9
|
+
const TwoFactorPage = ({ methods, onBack }) => {
|
|
10
|
+
const { verifyTwoFactor, session } = useAuth();
|
|
11
|
+
const [busy, setBusy] = useState(false);
|
|
12
|
+
const [error, setError] = useState();
|
|
13
|
+
const [codeSent, setCodeSent] = useState(false);
|
|
14
|
+
const attempt = async (step) => {
|
|
15
|
+
setBusy(true);
|
|
16
|
+
setError(void 0);
|
|
17
|
+
try {
|
|
18
|
+
await step();
|
|
19
|
+
} catch (failure) {
|
|
20
|
+
if (!(failure instanceof AuthError)) throw failure;
|
|
21
|
+
setError(authMessage(failure));
|
|
22
|
+
}
|
|
23
|
+
setBusy(false);
|
|
24
|
+
};
|
|
25
|
+
return /* @__PURE__ */ jsx(TwoFactorForm, {
|
|
26
|
+
methods,
|
|
27
|
+
busy,
|
|
28
|
+
error,
|
|
29
|
+
codeSent,
|
|
30
|
+
onBack,
|
|
31
|
+
onVerify: (input) => void attempt(() => verifyTwoFactor(input)),
|
|
32
|
+
onSendCode: () => void attempt(async () => {
|
|
33
|
+
await session.sendTwoFactorCode();
|
|
34
|
+
setCodeSent(true);
|
|
35
|
+
})
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
//#endregion
|
|
39
|
+
export { TwoFactorPage };
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
//#region src/auth/user-to-shell.ts
|
|
2
2
|
const hueOf = (text) => [...text].reduce((sum, char) => (sum * 31 + char.charCodeAt(0)) % 360, 7);
|
|
3
3
|
const initialsOf = (name) => name.split(/[\s@.]+/).filter(Boolean).slice(0, 2).map((part) => part[0].toUpperCase()).join("");
|
|
4
|
+
const rolesOf = (user) => (user.role ?? "user").split(",").map((role) => role.trim());
|
|
5
|
+
/** The user holds the `admin` role, among others perhaps. */
|
|
6
|
+
const isAdmin = (user) => rolesOf(user).includes("admin");
|
|
4
7
|
/** What the sidebar's profile menu shows for a signed-in user. */
|
|
5
8
|
const userToShell = (user) => {
|
|
6
|
-
const roles = (user
|
|
9
|
+
const roles = rolesOf(user);
|
|
7
10
|
const admin = roles.includes("admin");
|
|
8
11
|
return {
|
|
9
12
|
name: user.name || user.email,
|
|
@@ -15,4 +18,4 @@ const userToShell = (user) => {
|
|
|
15
18
|
};
|
|
16
19
|
};
|
|
17
20
|
//#endregion
|
|
18
|
-
export { userToShell };
|
|
21
|
+
export { isAdmin, userToShell };
|
package/dist/chat/composer.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Input } from "../primitives/input.js";
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
4
|
import { SendHorizontal } from "lucide-react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/chat/composer.tsx
|
|
7
7
|
/** The text box under a chat. Enter or the button sends the trimmed text and clears the box. */
|
|
8
8
|
const Composer = ({ onSend, busy, placeholder, inputRef }) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import { QueryClient } from "@tanstack/react-query";
|
|
3
2
|
import { Client, ClientOptions } from "@protobase/client";
|
|
3
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
4
4
|
//#region src/data/api-provider.d.ts
|
|
5
5
|
export declare const useClient: () => {
|
|
6
6
|
list: <R extends import("@protobase/client").ResourceRef>(resource: R, params?: import("@protobase/client").ListParams) => Promise<import("@protobase/client").ListPage<import("@protobase/client").RecordOf<R>>>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { keys } from "./query-keys.js";
|
|
2
2
|
import { createContext, useContext, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createClient } from "@protobase/client";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
|
-
import { createClient } from "@protobase/client";
|
|
6
6
|
//#region src/data/api-provider.tsx
|
|
7
7
|
const ApiContext = createContext(null);
|
|
8
8
|
const useClient = () => {
|
package/dist/data/meta-data.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keys } from "./query-keys.js";
|
|
2
2
|
import { useClient } from "./api-provider.js";
|
|
3
|
-
import { useQuery } from "@tanstack/react-query";
|
|
4
3
|
import { filterText } from "@protobase/client";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
5
5
|
//#region src/data/use-aggregates.ts
|
|
6
6
|
const useFacets = (resource, field, filter, enabled = true) => {
|
|
7
7
|
const client = useClient();
|
package/dist/data/use-list.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keys } from "./query-keys.js";
|
|
2
2
|
import { useClient } from "./api-provider.js";
|
|
3
|
-
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
4
3
|
import { filterText } from "@protobase/client";
|
|
4
|
+
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
5
5
|
//#region src/data/use-list.ts
|
|
6
6
|
/** Infinite pages of a resource, following `next_page_token`. */
|
|
7
7
|
const useList = (resource, { filter, orderBy, pageSize = 50, fields, startToken = "", enabled = true } = {}) => {
|
package/dist/data/use-meta.js
CHANGED
|
@@ -27,7 +27,8 @@ const useMeta = () => {
|
|
|
27
27
|
pages: Object.fromEntries((result.meta.pages ?? []).map((model) => [model.name, model])),
|
|
28
28
|
permissions: result.meta.permissions ?? {},
|
|
29
29
|
...result.meta.userMenu && { userMenu: result.meta.userMenu },
|
|
30
|
-
...result.meta.assistant && { assistant: result.meta.assistant }
|
|
30
|
+
...result.meta.assistant && { assistant: result.meta.assistant },
|
|
31
|
+
...result.meta.runtime && { runtime: result.meta.runtime }
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { keys } from "./query-keys.js";
|
|
2
2
|
import { useClient } from "./api-provider.js";
|
|
3
|
-
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
4
3
|
import { PreconditionFailedError } from "@protobase/client";
|
|
4
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
5
|
import { encodeKey } from "@protobase/schema";
|
|
6
6
|
//#region src/data/use-update-record.ts
|
|
7
7
|
/**
|
package/dist/data-grid/cells.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { Avatar } from "../primitives/avatar.js";
|
|
3
3
|
import { Badge } from "../primitives/badge.js";
|
|
4
|
-
import { formatDate, formatDateTime } from "../format/date.js";
|
|
5
|
-
import { userById } from "../mocks/users.js";
|
|
6
4
|
import { formatMoney } from "../format/money.js";
|
|
7
5
|
import { formatInt, formatPercent, formatSigned } from "../format/number.js";
|
|
8
|
-
import {
|
|
6
|
+
import { formatDate, formatDateTime } from "../format/date.js";
|
|
7
|
+
import { userById } from "../mocks/users.js";
|
|
9
8
|
import { Check } from "lucide-react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
10
|
import { enumLabel } from "@protobase/schema";
|
|
11
11
|
//#region src/data-grid/cells.tsx
|
|
12
12
|
const muted = (text) => /* @__PURE__ */ jsx("span", {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
-
import { gridTemplate } from "./grid-template.js";
|
|
3
2
|
import { Checkbox } from "../primitives/checkbox.js";
|
|
3
|
+
import { gridTemplate } from "./grid-template.js";
|
|
4
4
|
import { isRightAligned } from "./column-spec.js";
|
|
5
5
|
import { HeaderMenu } from "./header-menu.js";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
6
|
import { ArrowDown, ArrowUp, ChevronsUpDown } from "lucide-react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/data-grid/grid-header.tsx
|
|
9
9
|
const SortIcon = ({ sorted }) => {
|
|
10
10
|
if (sorted === "asc") return /* @__PURE__ */ jsx(ArrowUp, { className: "size-3 text-foreground" });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { formatInt } from "../format/number.js";
|
|
3
|
-
import { gridTemplate } from "./grid-template.js";
|
|
4
3
|
import { Checkbox } from "../primitives/checkbox.js";
|
|
4
|
+
import { gridTemplate } from "./grid-template.js";
|
|
5
5
|
import { isRightAligned } from "./column-spec.js";
|
|
6
6
|
import { Skeleton } from "../primitives/skeleton.js";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
2
|
import { MoreHorizontal } from "lucide-react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/data-grid/header-menu.tsx
|
|
5
5
|
/** The "..." in a column header: for now, pinning the column to the left edge. */
|
|
6
6
|
const HeaderMenu = ({ header, pinned, onTogglePin }) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { MoreHorizontal } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
6
|
//#region src/data-grid/row-menu.tsx
|
|
7
7
|
/** The actions that apply to a row; a row with none gets no menu at all. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { diffStats, parseDiff } from "./diff.js";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { FileCode2 } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/diff-view/diff-view.tsx
|
|
6
6
|
const row = {
|
|
7
7
|
add: "bg-success-soft text-success-text",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
1
|
import { Lock, X } from "lucide-react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/filter-panel/active-filter-chips.tsx
|
|
4
4
|
const chipClass = "inline-flex h-6 items-center gap-1 rounded-full border border-primary-border bg-primary-soft pl-2.5 pr-1.5 text-xs font-medium text-primary-text";
|
|
5
5
|
const ActiveFilterChips = ({ chips, lockedLabels = [], onRemove, onClear, scroll }) => {
|
|
@@ -3,8 +3,8 @@ import { formatInt } from "../format/number.js";
|
|
|
3
3
|
import { Checkbox } from "../primitives/checkbox.js";
|
|
4
4
|
import { matchesSearch } from "./filter-state.js";
|
|
5
5
|
import { useState } from "react";
|
|
6
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
6
|
import { Search } from "lucide-react";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
//#region src/filter-panel/facet-group.tsx
|
|
9
9
|
const FacetGroup = ({ config, selected, onToggle }) => {
|
|
10
10
|
const [query, setQuery] = useState("");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { ChevronDown } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/filter-panel/filter-menu.tsx
|
|
6
6
|
const FilterMenu = ({ label, activeCount, width = 240, defaultOpen = false, children }) => {
|
|
7
7
|
const [open, setOpen] = useState(defaultOpen);
|
|
@@ -4,8 +4,8 @@ import { lineGrid } from "./columns.js";
|
|
|
4
4
|
import { LineRow } from "./line-row.js";
|
|
5
5
|
import { reorder } from "./reorder.js";
|
|
6
6
|
import { TotalsFooter } from "./totals-footer.js";
|
|
7
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { Plus } from "lucide-react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { DndContext, KeyboardSensor, MouseSensor, TouchSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
|
|
10
10
|
import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
11
11
|
//#region src/inline-child-table/inline-child-table.tsx
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { MoreHorizontal } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/inline-child-table/line-menu.tsx
|
|
6
6
|
/** The "..." at the end of a line: the keyboard-and-screen-reader way to reorder, besides the drag handle. */
|
|
7
7
|
const LineMenu = ({ items, label }) => {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { formatMoney } from "../format/money.js";
|
|
2
3
|
import { lineTotalCents } from "../mocks/invoices.js";
|
|
3
4
|
import { lineGrid } from "./columns.js";
|
|
4
|
-
import { formatMoney } from "../format/money.js";
|
|
5
5
|
import { LineMenu } from "./line-menu.js";
|
|
6
6
|
import { NumberCell } from "./number-cell.js";
|
|
7
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { GripVertical } from "lucide-react";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { useSortable } from "@dnd-kit/sortable";
|
|
10
10
|
//#region src/inline-child-table/line-row.tsx
|
|
11
11
|
const parseEuros = (text) => Math.round(Number(text.replace(",", ".").replace(/[^0-9.]/g, "")) * 100);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { invoiceTotals } from "../mocks/invoices.js";
|
|
2
1
|
import { formatMoney } from "../format/money.js";
|
|
2
|
+
import { invoiceTotals } from "../mocks/invoices.js";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/inline-child-table/totals-footer.tsx
|
|
5
5
|
const TotalsFooter = ({ lines, vatRate }) => {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
//#region src/lib/use-visual-viewport.ts
|
|
3
|
+
/**
|
|
4
|
+
* While `active`: the part of the page that is on screen, which shrinks when a phone's on-screen keyboard opens;
|
|
5
|
+
* undefined otherwise, or where the browser has no `visualViewport`.
|
|
6
|
+
*/
|
|
7
|
+
const useVisualViewport = (active) => {
|
|
8
|
+
const [box, setBox] = useState();
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const viewport = window.visualViewport;
|
|
11
|
+
if (!active || !viewport) return setBox(void 0);
|
|
12
|
+
const update = () => setBox({
|
|
13
|
+
top: viewport.offsetTop,
|
|
14
|
+
height: viewport.height
|
|
15
|
+
});
|
|
16
|
+
update();
|
|
17
|
+
viewport.addEventListener("resize", update);
|
|
18
|
+
viewport.addEventListener("scroll", update);
|
|
19
|
+
return () => {
|
|
20
|
+
viewport.removeEventListener("resize", update);
|
|
21
|
+
viewport.removeEventListener("scroll", update);
|
|
22
|
+
};
|
|
23
|
+
}, [active]);
|
|
24
|
+
return active ? box : void 0;
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { useVisualViewport };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { formatMoney } from "../format/money.js";
|
|
1
2
|
import { keys } from "../data/query-keys.js";
|
|
2
3
|
import { useClient } from "../data/api-provider.js";
|
|
3
4
|
import { humanize } from "./naming.js";
|
|
4
|
-
import { formatMoney } from "../format/money.js";
|
|
5
5
|
import { stateToText } from "./filter-string.js";
|
|
6
6
|
import { useHistogram } from "../data/use-aggregates.js";
|
|
7
7
|
import { fetchLabels } from "./relation-labels.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
2
|
import { Check, Minus } from "lucide-react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/primitives/checkbox.tsx
|
|
5
5
|
const Checkbox = ({ checked, indeterminate, onChange, label, className }) => /* @__PURE__ */ jsx("button", {
|
|
6
6
|
type: "button",
|
package/dist/primitives/sheet.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useFocusTrap } from "../lib/use-focus-trap.js";
|
|
2
2
|
import { useRef } from "react";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { X } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/primitives/sheet.tsx
|
|
6
6
|
/** A panel that rises from the bottom of the screen, for the filter panel on phones. */
|
|
7
7
|
const Sheet = ({ title, open, onClose, children }) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
3
2
|
import { Loader2 } from "lucide-react";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
4
|
//#region src/primitives/spinner.tsx
|
|
5
5
|
const Spinner = ({ className }) => /* @__PURE__ */ jsx(Loader2, {
|
|
6
6
|
"aria-label": "Loading",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Avatar } from "../primitives/avatar.js";
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { TriangleAlert } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/record-view/field.tsx
|
|
6
6
|
const Field = ({ label, required, help, example, error, conflict, children }) => /* @__PURE__ */ jsxs("div", { children: [
|
|
7
7
|
/* @__PURE__ */ jsxs("div", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
1
|
import { ChevronRight } from "lucide-react";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/record-view/record-sidebar.tsx
|
|
4
4
|
const MetadataList = ({ rows }) => /* @__PURE__ */ jsx("dl", {
|
|
5
5
|
className: "grid grid-cols-[auto_1fr] gap-x-4 gap-y-2 text-[13px]",
|
|
@@ -12,6 +12,10 @@ export type SidePanelProps = {
|
|
|
12
12
|
/** The content; it scrolls on its own, so give it `min-h-0 flex-1`. */
|
|
13
13
|
children: ReactNode;
|
|
14
14
|
};
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* A full-height panel beside the page, for the shell's `rightPanel`: a header with a close button, content and a footer.
|
|
17
|
+
* Below `md` there is no room beside the page, so it covers the screen instead, shrinking with the on-screen keyboard
|
|
18
|
+
* so the footer stays in view.
|
|
19
|
+
*/
|
|
16
20
|
export declare const SidePanel: ({ title, icon, status, onClose, footer, children }: SidePanelProps) => import("react").JSX.Element;
|
|
17
21
|
//#endregion
|
|
@@ -1,40 +1,54 @@
|
|
|
1
|
+
import { useIsDesktop } from "../lib/use-media-query.js";
|
|
1
2
|
import { Button } from "../primitives/button.js";
|
|
2
|
-
import {
|
|
3
|
+
import { useVisualViewport } from "../lib/use-visual-viewport.js";
|
|
3
4
|
import { X } from "lucide-react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
6
|
//#region src/side-panel/side-panel.tsx
|
|
5
|
-
/**
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A full-height panel beside the page, for the shell's `rightPanel`: a header with a close button, content and a footer.
|
|
9
|
+
* Below `md` there is no room beside the page, so it covers the screen instead, shrinking with the on-screen keyboard
|
|
10
|
+
* so the footer stays in view.
|
|
11
|
+
*/
|
|
12
|
+
const SidePanel = ({ title, icon, status, onClose, footer, children }) => {
|
|
13
|
+
const screen = useVisualViewport(!useIsDesktop());
|
|
14
|
+
return /* @__PURE__ */ jsxs("aside", {
|
|
15
|
+
"aria-label": title,
|
|
16
|
+
className: "flex h-full w-[380px] max-w-full shrink-0 flex-col border-l border-border bg-surface max-md:fixed max-md:inset-0 max-md:z-40 max-md:w-full max-md:border-l-0",
|
|
17
|
+
style: screen && {
|
|
18
|
+
top: screen.top,
|
|
19
|
+
bottom: "auto",
|
|
20
|
+
height: screen.height
|
|
21
|
+
},
|
|
22
|
+
children: [
|
|
23
|
+
/* @__PURE__ */ jsxs("header", {
|
|
24
|
+
className: "flex h-12 shrink-0 items-center gap-2 border-b border-border bg-background px-4",
|
|
25
|
+
children: [
|
|
26
|
+
icon,
|
|
27
|
+
/* @__PURE__ */ jsx("h2", {
|
|
28
|
+
className: "flex-1 text-[13px] font-semibold",
|
|
29
|
+
children: title
|
|
30
|
+
}),
|
|
31
|
+
status && /* @__PURE__ */ jsx("span", {
|
|
32
|
+
className: "truncate text-xs font-medium text-muted-foreground",
|
|
33
|
+
children: status
|
|
34
|
+
}),
|
|
35
|
+
onClose && /* @__PURE__ */ jsx(Button, {
|
|
36
|
+
variant: "ghost",
|
|
37
|
+
size: "sm",
|
|
38
|
+
className: "w-7 px-0",
|
|
39
|
+
"aria-label": "Close",
|
|
40
|
+
onClick: onClose,
|
|
41
|
+
children: /* @__PURE__ */ jsx(X, { className: "size-4" })
|
|
42
|
+
})
|
|
43
|
+
]
|
|
44
|
+
}),
|
|
45
|
+
children,
|
|
46
|
+
footer && /* @__PURE__ */ jsx("footer", {
|
|
47
|
+
className: "shrink-0 space-y-2 border-t border-border bg-background p-3",
|
|
48
|
+
children: footer
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
};
|
|
39
53
|
//#endregion
|
|
40
54
|
export { SidePanel };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { Spinner } from "../primitives/spinner.js";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { Check, Circle, X } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/step-list/step-list.tsx
|
|
6
6
|
const icons = {
|
|
7
7
|
pending: /* @__PURE__ */ jsx(Circle, { className: "size-3 text-faint-foreground" }),
|
package/dist/toasts/toast.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cn } from "../lib/cn.js";
|
|
2
2
|
import { Spinner } from "../primitives/spinner.js";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
3
|
import { CheckCircle2, X, XCircle } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
//#region src/toasts/toast.tsx
|
|
6
6
|
const icons = {
|
|
7
7
|
loading: /* @__PURE__ */ jsx(Spinner, { className: "text-primary" }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "The Protobase admin app and the React hooks and components for custom UI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -24,12 +24,19 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist"
|
|
26
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsdown",
|
|
29
|
+
"prepack": "tsdown"
|
|
30
|
+
},
|
|
27
31
|
"publishConfig": {
|
|
28
32
|
"access": "public"
|
|
29
33
|
},
|
|
30
34
|
"dependencies": {
|
|
31
35
|
"@dnd-kit/core": "^6.3.1",
|
|
32
36
|
"@dnd-kit/sortable": "^10.0.0",
|
|
37
|
+
"@protobase/client": "0.4.1",
|
|
38
|
+
"@protobase/layout": "0.4.1",
|
|
39
|
+
"@protobase/schema": "0.4.1",
|
|
33
40
|
"@tanstack/react-query": "^5.104.1",
|
|
34
41
|
"@tanstack/react-table": "^9.2.6",
|
|
35
42
|
"@tanstack/react-virtual": "^3.14.13",
|
|
@@ -38,11 +45,6 @@
|
|
|
38
45
|
"react-dom": "^19.3.0",
|
|
39
46
|
"recharts": "^3.10.1",
|
|
40
47
|
"tailwindcss": "^4.3.3",
|
|
41
|
-
"
|
|
42
|
-
"@protobase/layout": "0.3.4",
|
|
43
|
-
"@protobase/schema": "0.3.4"
|
|
44
|
-
},
|
|
45
|
-
"scripts": {
|
|
46
|
-
"build": "tsdown"
|
|
48
|
+
"uqr": "^0.1.3"
|
|
47
49
|
}
|
|
48
|
-
}
|
|
50
|
+
}
|