@protobase/ui 0.33.4 → 0.34.0
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-security-page.js +16 -6
- package/dist/account/connected-apps-section.js +93 -0
- package/dist/app/app.js +20 -7
- package/dist/app/charts/chart-view.js +1 -1
- package/dist/app/create/create-page.js +2 -2
- package/dist/app/create/create-route.js +1 -1
- package/dist/app/delete/use-delete-flow.js +1 -1
- package/dist/app/displays/board-display.js +1 -1
- package/dist/app/displays/calendar-display.js +1 -1
- package/dist/app/displays/map-display.js +1 -1
- package/dist/app/displays/record-card.js +1 -1
- package/dist/app/displays/timeline-display.js +1 -1
- package/dist/app/displays/use-board-column.js +1 -1
- package/dist/app/displays/use-map-records.js +1 -1
- package/dist/app/embedded-list/embedded-list.js +1 -1
- package/dist/app/embedded-list/use-embedded-rows.js +1 -1
- package/dist/app/export/export-dialog.js +1 -1
- package/dist/app/file-field.js +1 -1
- package/dist/app/gallery/gallery-section.js +1 -1
- package/dist/app/gallery/lightbox.js +1 -1
- package/dist/app/gallery/use-gallery-photos.js +1 -1
- package/dist/app/hand-order/hand-sorted-list.js +1 -1
- package/dist/app/hand-order/use-hand-order.js +1 -1
- package/dist/app/import/import-dialog.js +1 -1
- package/dist/app/incoming-card.js +1 -1
- package/dist/app/list-page.js +3 -3
- package/dist/app/map/map-section.js +1 -1
- package/dist/app/meta-gate.js +2 -2
- package/dist/app/pages/action-inputs/action-inputs-dialog.js +2 -2
- package/dist/app/pages/blocks/card-row.js +1 -1
- package/dist/app/pages/blocks/field.js +1 -1
- package/dist/app/pages/blocks/modal-form.js +1 -1
- package/dist/app/pages/blocks/record-card.js +1 -1
- package/dist/app/pages/blocks/record-cards.js +1 -1
- package/dist/app/pages/blocks/stat.js +1 -1
- package/dist/app/pages/composed-page.js +1 -1
- package/dist/app/pages/field-value.js +1 -1
- package/dist/app/pages/use-run-action.js +1 -1
- package/dist/app/record-chart/record-chart-section.js +1 -1
- package/dist/app/record-lines.js +1 -1
- package/dist/app/record-page.js +5 -5
- package/dist/app/record-row/record-row-body.js +1 -1
- package/dist/app/record-row/saved-record-row.js +1 -1
- package/dist/app/related-cards/related-card.js +1 -1
- package/dist/app/related-section.js +2 -2
- package/dist/app/saved/use-favorite.js +1 -1
- package/dist/app/search/navigation-hits.js +1 -1
- package/dist/app/search/use-global-search.js +1 -1
- package/dist/app/searches/save-search-button.js +1 -1
- package/dist/app/searches/saved-search-rows.js +1 -1
- package/dist/app/searches/saved-searches.js +1 -1
- package/dist/app/searches/search-controls.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/searches/use-saved-searches.js +1 -1
- package/dist/app/source-notice.js +1 -1
- package/dist/app/use-app-address.js +1 -1
- package/dist/app/use-incoming-counts.js +1 -1
- package/dist/auth/auth-gate.js +1 -1
- package/dist/auth/auth-provider.js +4 -0
- package/dist/auth/sign-in-prompt.js +2 -2
- package/dist/connect/client-name.js +9 -0
- package/dist/connect/client-publisher.js +13 -0
- package/dist/connect/connect-page.js +236 -0
- package/dist/connect/interaction-guard.js +77 -0
- package/dist/connect/redirect-target.js +36 -0
- package/dist/connect/signed-request.js +14 -0
- package/dist/connect/use-connect-address.js +11 -0
- package/dist/data/use-live-updates.js +1 -1
- package/dist/forms/public-form-page.js +2 -2
- package/dist/index.js +3 -3
- package/dist/live/use-filter-config.js +1 -1
- package/dist/live/use-reference-search.js +1 -1
- package/dist/package.js +1 -1
- package/dist/plugins/plugin-gate.js +1 -1
- package/dist/shell/assets/app--vnZRzES.js +70 -0
- package/dist/shell/assets/index-BX850JXd.css +2 -0
- package/dist/shell/assets/{index-BAYZFH-l.js → index-DUcwNz5T.js} +2 -2
- package/dist/shell/assets/{records-map-D2XP8QkT.js → records-map-4gB4TBHO.js} +2 -2
- package/dist/shell/assets/shared-protobase-ui-Bj2wjoBo.js +2 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app--vnZRzES.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-BAYZFH-l.js.map → index-DUcwNz5T.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-D2XP8QkT.js.map → records-map-4gB4TBHO.js.map} +1 -1
- package/dist/shell-source-maps/assets/{shared-protobase-ui-C5AR2k3j.js.map → shared-protobase-ui-Bj2wjoBo.js.map} +1 -1
- package/package.json +4 -4
- package/dist/shell/assets/app-B9bnfmK5.js +0 -70
- package/dist/shell/assets/index-BrgzNBVN.css +0 -2
- package/dist/shell/assets/shared-protobase-ui-C5AR2k3j.js +0 -2
- package/dist/shell-source-maps/assets/app-B9bnfmK5.js.map +0 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
//#region src/connect/interaction-guard.ts
|
|
3
|
+
/** Whether the page runs inside another page's frame; comparing the two windows works across origins and in a sandbox. */
|
|
4
|
+
const isFramed = (win = window) => win.top !== win.self;
|
|
5
|
+
/**
|
|
6
|
+
* Calls `onChange(true)` once the document has been visible and focused for `delayMs` without a break, and
|
|
7
|
+
* `onChange(false)` as soon as the window loses focus or the document is hidden, waiting again from the next focus. A
|
|
8
|
+
* click aimed at another page, such as the second half of a double-click on a page that just gave way to this one,
|
|
9
|
+
* lands before then. Returns the cleanup that removes its listeners and timer.
|
|
10
|
+
*/
|
|
11
|
+
const armAfterFocus = (doc, delayMs, onChange) => {
|
|
12
|
+
let armed = false;
|
|
13
|
+
let timer;
|
|
14
|
+
const set = (value) => {
|
|
15
|
+
if (armed === value) return;
|
|
16
|
+
armed = value;
|
|
17
|
+
onChange(value);
|
|
18
|
+
};
|
|
19
|
+
const disarm = () => {
|
|
20
|
+
clearTimeout(timer);
|
|
21
|
+
set(false);
|
|
22
|
+
};
|
|
23
|
+
const wait = () => {
|
|
24
|
+
disarm();
|
|
25
|
+
if (doc.visibilityState === "visible" && doc.hasFocus()) timer = setTimeout(() => set(true), delayMs);
|
|
26
|
+
};
|
|
27
|
+
const visibility = () => doc.visibilityState === "visible" ? wait() : disarm();
|
|
28
|
+
doc.defaultView?.addEventListener("focus", wait);
|
|
29
|
+
doc.defaultView?.addEventListener("blur", disarm);
|
|
30
|
+
doc.addEventListener("visibilitychange", visibility);
|
|
31
|
+
wait();
|
|
32
|
+
return () => {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
doc.defaultView?.removeEventListener("focus", wait);
|
|
35
|
+
doc.defaultView?.removeEventListener("blur", disarm);
|
|
36
|
+
doc.removeEventListener("visibilitychange", visibility);
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Whether a click on a guarded button goes through: only one whose press began on it while it was armed (`down`), and
|
|
41
|
+
* not a key held down and repeating. A mouse button held across the moment it arms, or Enter held from before, does
|
|
42
|
+
* not count.
|
|
43
|
+
*/
|
|
44
|
+
const pressGate = () => {
|
|
45
|
+
let began = false;
|
|
46
|
+
return {
|
|
47
|
+
/** The press began: on `pointerdown` or `keydown`, with whether the button was armed then and the key repeats. */
|
|
48
|
+
down: (armed, repeat = false) => {
|
|
49
|
+
began = armed && !repeat;
|
|
50
|
+
},
|
|
51
|
+
/** The click: true once for a press that began armed. */
|
|
52
|
+
take: () => {
|
|
53
|
+
const counts = began;
|
|
54
|
+
began = false;
|
|
55
|
+
return counts;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
/** Whether the page has been in front for `delayMs` (`armAfterFocus`), as React state. */
|
|
60
|
+
const useArmedAfterFocus = (delayMs) => {
|
|
61
|
+
const [armed, setArmed] = useState(false);
|
|
62
|
+
useEffect(() => armAfterFocus(document, delayMs, setArmed), [delayMs]);
|
|
63
|
+
return armed;
|
|
64
|
+
};
|
|
65
|
+
/** The handlers of a button guarded by `armed`: its press must begin while armed for its click to act (`pressGate`). */
|
|
66
|
+
const useGuardedPress = (armed, onPress) => {
|
|
67
|
+
const gate = useRef(pressGate());
|
|
68
|
+
return {
|
|
69
|
+
onPointerDown: () => gate.current.down(armed),
|
|
70
|
+
onKeyDown: (event) => gate.current.down(armed, event.repeat),
|
|
71
|
+
onClick: () => {
|
|
72
|
+
if (gate.current.take()) onPress();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
//#endregion
|
|
77
|
+
export { armAfterFocus, isFramed, pressGate, useArmedAfterFocus, useGuardedPress };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/connect/redirect-target.ts
|
|
2
|
+
const loopbackHosts = [
|
|
3
|
+
"localhost",
|
|
4
|
+
"127.0.0.1",
|
|
5
|
+
"[::1]"
|
|
6
|
+
];
|
|
7
|
+
const unsafeSchemes = [
|
|
8
|
+
"javascript:",
|
|
9
|
+
"data:",
|
|
10
|
+
"vbscript:",
|
|
11
|
+
"file:",
|
|
12
|
+
"blob:",
|
|
13
|
+
"about:"
|
|
14
|
+
];
|
|
15
|
+
/** Where `redirectUri` sends the code, or `undefined` for one the page will not send it to. */
|
|
16
|
+
const redirectTarget = (redirectUri) => {
|
|
17
|
+
if (!URL.canParse(redirectUri)) return void 0;
|
|
18
|
+
const url = new URL(redirectUri);
|
|
19
|
+
if (url.protocol === "https:") return {
|
|
20
|
+
kind: "site",
|
|
21
|
+
host: url.host
|
|
22
|
+
};
|
|
23
|
+
if (url.protocol === "http:") return loopbackHosts.includes(url.hostname) ? {
|
|
24
|
+
kind: "this-computer",
|
|
25
|
+
port: url.port || "80"
|
|
26
|
+
} : void 0;
|
|
27
|
+
if (unsafeSchemes.includes(url.protocol)) return void 0;
|
|
28
|
+
return {
|
|
29
|
+
kind: "device-app",
|
|
30
|
+
uri: redirectUri.split("?")[0].split("#")[0]
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
/** Whether `url` is one the page may send the browser to: anything but a scheme that runs or shows content in the page. */
|
|
34
|
+
const isSafeTarget = (url) => !URL.canParse(url, "http://local") || !unsafeSchemes.includes(new URL(url, "http://local").protocol);
|
|
35
|
+
//#endregion
|
|
36
|
+
export { isSafeTarget, redirectTarget };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { readConnectAddress } from "@protobase/schema";
|
|
2
|
+
//#region src/connect/signed-request.ts
|
|
3
|
+
/**
|
|
4
|
+
* The signed request in `url` when the server sends the browser back to this page (`here`: its origin and path), else
|
|
5
|
+
* `undefined`. The origin counts as well as the path: a client's own redirect URI can have this page's path, on its
|
|
6
|
+
* host or a loopback port, and a query of its choosing; and only a query with the provider's signature is a request.
|
|
7
|
+
*/
|
|
8
|
+
const signedRequestFor = (url, here) => {
|
|
9
|
+
const target = new URL(url, here.origin);
|
|
10
|
+
if (target.origin !== here.origin || target.pathname !== here.pathname) return void 0;
|
|
11
|
+
return readConnectAddress(target.search).oauthQuery;
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { signedRequestFor };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useRouter } from "../app/router.js";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { readConnectAddress } from "@protobase/schema";
|
|
4
|
+
//#region src/connect/use-connect-address.ts
|
|
5
|
+
/** The connect page's address as logical values: the signed authorization request, when its query holds one. */
|
|
6
|
+
const useConnectAddress = () => {
|
|
7
|
+
const { params } = useRouter();
|
|
8
|
+
return useMemo(() => readConnectAddress(params.toString()), [params]);
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
export { useConnectAddress };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FocusRefreshContext } from "./focus-refresh.js";
|
|
2
2
|
import { useClient } from "./api-provider.js";
|
|
3
|
-
import { liveRefresh } from "./live-refresh.js";
|
|
4
3
|
import { sourceStatusOf } from "./with-sources.js";
|
|
4
|
+
import { liveRefresh } from "./live-refresh.js";
|
|
5
5
|
import { useContext, useEffect, useMemo } from "react";
|
|
6
6
|
import { useQueryClient } from "@tanstack/react-query";
|
|
7
7
|
//#region src/data/use-live-updates.ts
|
|
@@ -2,11 +2,11 @@ import { Spinner } from "../primitives/spinner.js";
|
|
|
2
2
|
import { Button } from "../primitives/button.js";
|
|
3
3
|
import { keys } from "../data/query-keys.js";
|
|
4
4
|
import { useClient } from "../data/api-provider.js";
|
|
5
|
+
import { text } from "../app/meta-gate-messages.js";
|
|
6
|
+
import { Notice } from "../app/meta-gate.js";
|
|
5
7
|
import { AuthLayout } from "../auth/auth-layout.js";
|
|
6
8
|
import { humanize } from "../live/naming.js";
|
|
7
9
|
import { Field } from "../record-view/field.js";
|
|
8
|
-
import { text } from "../app/meta-gate-messages.js";
|
|
9
|
-
import { Notice } from "../app/meta-gate.js";
|
|
10
10
|
import { createBody, hasDatabaseDefault, initialDraft, isRequired } from "../app/create/create-values.js";
|
|
11
11
|
import { useDocumentTitle } from "../app/document-title.js";
|
|
12
12
|
import { ErrorBanner } from "../app/error-banner.js";
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,10 @@ import { Spinner } from "./primitives/spinner.js";
|
|
|
6
6
|
import { Button, buttonClass } from "./primitives/button.js";
|
|
7
7
|
import { Dialog } from "./primitives/dialog.js";
|
|
8
8
|
import { OtpInput } from "./primitives/otp-input.js";
|
|
9
|
+
import { ResponsiveDialog } from "./primitives/responsive-dialog.js";
|
|
10
|
+
import { SignInPrompt } from "./auth/sign-in-prompt.js";
|
|
9
11
|
import { ApiProvider, useClient, useResourceClient } from "./data/api-provider.js";
|
|
12
|
+
import { useMeta } from "./data/use-meta.js";
|
|
10
13
|
import { AuthLayout } from "./auth/auth-layout.js";
|
|
11
14
|
import { ResendCodeButton } from "./auth/resend-code-button.js";
|
|
12
15
|
import { CodeForm } from "./auth/code-form.js";
|
|
@@ -18,7 +21,6 @@ import { useApiToken } from "./auth/use-api-token.js";
|
|
|
18
21
|
import { useRecord } from "./data/use-record.js";
|
|
19
22
|
import { Field } from "./record-view/field.js";
|
|
20
23
|
import { Section } from "./record-view/section.js";
|
|
21
|
-
import { useMeta } from "./data/use-meta.js";
|
|
22
24
|
import { AppLink, useAppAddress } from "./app/use-app-address.js";
|
|
23
25
|
import { Textarea } from "./primitives/textarea.js";
|
|
24
26
|
import { useList } from "./data/use-list.js";
|
|
@@ -38,11 +40,9 @@ import { useBackendData } from "./assistant-dock/use-backend-data.js";
|
|
|
38
40
|
import { AssistantDock } from "./assistant-dock/assistant-dock.js";
|
|
39
41
|
import { useAssistant } from "./assistant-dock/use-assistant.js";
|
|
40
42
|
import { useAssistantClient } from "./assistant-dock/use-assistant-client.js";
|
|
41
|
-
import { ResponsiveDialog } from "./primitives/responsive-dialog.js";
|
|
42
43
|
import { ActionInputError } from "./app/pages/action-inputs/input-errors.js";
|
|
43
44
|
import { useLayoutRecord } from "./app/pages/record-scope.js";
|
|
44
45
|
import { useFacets, useHistogram, useSeries } from "./data/use-aggregates.js";
|
|
45
|
-
import { SignInPrompt } from "./auth/sign-in-prompt.js";
|
|
46
46
|
import { useListDisplay } from "./app/displays/display-context.js";
|
|
47
47
|
import { MenuSelect } from "./primitives/menu-select.js";
|
|
48
48
|
import { Form } from "./app/create/form.js";
|
|
@@ -8,8 +8,8 @@ import { stateToText } from "./filter-string.js";
|
|
|
8
8
|
import { rangeFormat, rangeHistogram, widgetFields } from "./view-fields.js";
|
|
9
9
|
import { labelKeys, matchOptions } from "./reference-options.js";
|
|
10
10
|
import { useReferenceSearch } from "./use-reference-search.js";
|
|
11
|
-
import { useQueries, useQueryClient } from "@tanstack/react-query";
|
|
12
11
|
import { enumLabel } from "@protobase/schema";
|
|
12
|
+
import { useQueries, useQueryClient } from "@tanstack/react-query";
|
|
13
13
|
//#region src/live/use-filter-config.ts
|
|
14
14
|
const histogramBuckets = 24;
|
|
15
15
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useClient } from "../data/api-provider.js";
|
|
2
2
|
import { labelFieldOf } from "./model-helpers.js";
|
|
3
3
|
import { useState } from "react";
|
|
4
|
-
import { keepPreviousData, useQueries } from "@tanstack/react-query";
|
|
5
4
|
import { createWhere, printFilter } from "@protobase/schema";
|
|
5
|
+
import { keepPreviousData, useQueries } from "@tanstack/react-query";
|
|
6
6
|
//#region src/live/use-reference-search.ts
|
|
7
7
|
const where$1 = createWhere();
|
|
8
8
|
/**
|
package/dist/package.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Spinner } from "../primitives/spinner.js";
|
|
2
|
+
import { useAdminMeta } from "../app/meta-gate.js";
|
|
2
3
|
import { mergePlugins } from "./merge-plugins.js";
|
|
3
4
|
import { text } from "./messages.js";
|
|
4
5
|
import { usePluginSource } from "./plugin-source.js";
|
|
5
6
|
import { usePluginLoads } from "./use-plugin-loads.js";
|
|
6
|
-
import { useAdminMeta } from "../app/meta-gate.js";
|
|
7
7
|
import { ProjectUiContext } from "../app/pages/project-ui.js";
|
|
8
8
|
import { createContext, useContext, useMemo } from "react";
|
|
9
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|