@protobase/ui 0.40.6 → 0.40.8

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.
Files changed (43) hide show
  1. package/dist/app/app.js +12 -3
  2. package/dist/app/comments/comment-composer.js +2 -0
  3. package/dist/app/create/create-page.js +10 -2
  4. package/dist/app/messages.js +12 -0
  5. package/dist/app/new-version/messages.js +24 -0
  6. package/dist/app/new-version/new-version-banner.js +37 -0
  7. package/dist/app/new-version/new-version-button.js +27 -0
  8. package/dist/app/new-version/new-version-toast.js +43 -0
  9. package/dist/app/new-version/use-modal-open.js +19 -0
  10. package/dist/app/pages/blocks/modal-form.js +4 -0
  11. package/dist/app/pages/blocks/record-cards.js +14 -4
  12. package/dist/app/record-page.js +1 -1
  13. package/dist/app/related-cards/related-cards.js +18 -13
  14. package/dist/app/router.js +68 -26
  15. package/dist/app-shell/app-shell.js +6 -3
  16. package/dist/app-shell/sidebar-notice.js +12 -0
  17. package/dist/app-shell/sidebar.d.ts +1 -1
  18. package/dist/app-shell/sidebar.js +2 -1
  19. package/dist/data/api-provider.js +1 -0
  20. package/dist/data/meta-data.d.ts +9 -0
  21. package/dist/data/use-meta.d.ts +3 -2
  22. package/dist/data/use-meta.js +8 -4
  23. package/dist/data/version-freeze.js +17 -0
  24. package/dist/package.js +1 -1
  25. package/dist/primitives/snap-row-width.js +13 -0
  26. package/dist/primitives/snap-row.js +21 -0
  27. package/dist/shell/assets/app-DXbtlR1O.js +9 -0
  28. package/dist/shell/assets/{index-BX850JXd.css → index-DT92y8N1.css} +1 -1
  29. package/dist/shell/assets/{index-GTJA-4i3.js → index-hbOjGIrj.js} +2 -2
  30. package/dist/shell/assets/{records-map-DVCcd765.js → records-map-DW4-2p9A.js} +2 -2
  31. package/dist/shell/assets/{shared-protobase-ui-D1hRi4LJ.js → shared-protobase-ui-DOLAGMZi.js} +2 -2
  32. package/dist/shell/index.html +4 -4
  33. package/dist/shell/protobase.shell.json +1 -1
  34. package/dist/shell/protobase.sw.js +2 -2
  35. package/dist/shell-source-maps/assets/app-DXbtlR1O.js.map +1 -0
  36. package/dist/shell-source-maps/assets/{index-GTJA-4i3.js.map → index-hbOjGIrj.js.map} +1 -1
  37. package/dist/shell-source-maps/assets/{records-map-DVCcd765.js.map → records-map-DW4-2p9A.js.map} +1 -1
  38. package/dist/shell-source-maps/assets/{shared-protobase-ui-D1hRi4LJ.js.map → shared-protobase-ui-DOLAGMZi.js.map} +1 -1
  39. package/dist/toasts/toast-provider.js +11 -2
  40. package/dist/toasts/toast.js +3 -2
  41. package/package.json +4 -4
  42. package/dist/shell/assets/app-Di5oi8xq.js +0 -9
  43. package/dist/shell-source-maps/assets/app-Di5oi8xq.js.map +0 -1
@@ -11,7 +11,7 @@ import { SidebarGroup } from "./sidebar-group.js";
11
11
  import { LogIn } from "lucide-react";
12
12
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
13
  //#region src/app-shell/sidebar.tsx
14
- const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky, logo: SidebarLogo = Logo }) => {
14
+ const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExpanded, profileMenuOpen, groups = navGroups, userMenu, onNavigate, onSignOut, signIn, organizations, roles, theme, sticky, logo: SidebarLogo = Logo, notice }) => {
15
15
  const expanding = mode === "icon-expand";
16
16
  const expanded = expanding && forceExpanded;
17
17
  const inline = isTextMode(mode) || expanding;
@@ -51,6 +51,7 @@ const Sidebar = ({ mode, activeItem, user, workspace, appName, appIcon, forceExp
51
51
  }, item.id))]
52
52
  }, group.label))
53
53
  }),
54
+ notice,
54
55
  /* @__PURE__ */ jsx("div", {
55
56
  className: cn("shrink-0 border-t p-2", mode !== "text-large" && mode !== "text-small" && !expanding && "flex justify-center"),
56
57
  children: user ? /* @__PURE__ */ jsx(ProfileMenu, {
@@ -66,6 +66,7 @@ const ApiProvider = ({ baseUrl, client: given, sources, fetch, queryClient: give
66
66
  const previous = metaVersion.current;
67
67
  metaVersion.current = version;
68
68
  if (previous === void 0 || previous === version) return;
69
+ if (queryClient.getQueryData(keys.meta)?.versionChange) return;
69
70
  queryClient.invalidateQueries({ queryKey: keys.meta }, { cancelRefetch: false });
70
71
  }
71
72
  }), [
@@ -7,6 +7,15 @@ export type MetaData = {
7
7
  etag: string;
8
8
  /** The app's name, which ends the tab titles. */
9
9
  appName: string;
10
+ /** The Protobase version the server ran when this `/meta` was read. */
11
+ protobaseVersion?: string;
12
+ /**
13
+ * Set once the server runs another Protobase version than the page started with: `to` is that version, when the server
14
+ * names one. The page keeps this `/meta` and stops reading it until it is reloaded (see `keepOnVersionChange`).
15
+ */
16
+ versionChange?: {
17
+ to?: string;
18
+ };
10
19
  /** The path of the app's own icon, when the project has one, which the sidebar's logo draws. */
11
20
  appIcon?: string;
12
21
  resources: Record<string, ResourceModel>;
@@ -3,8 +3,9 @@ import { UseQueryResult } from "@tanstack/react-query";
3
3
  //#region src/data/use-meta.d.ts
4
4
  /**
5
5
  * The resource and view models. Refetches on window focus with `If-None-Match`, so an unchanged
6
- * `/meta` costs a 304, and the cached object keeps its identity. Below the app's `MetaGate`, `data` holds the resources
7
- * of its sources too, as every page of the app reads them.
6
+ * `/meta` costs a 304, and the cached object keeps its identity. Once the server runs another Protobase version than
7
+ * the page started with, it keeps the `/meta` it has and stops reading it (see `keepOnVersionChange`). Below the app's
8
+ * `MetaGate`, `data` holds the resources of its sources too, as every page of the app reads them.
8
9
  */
9
10
  export declare const useMeta: () => UseQueryResult<MetaData>;
10
11
  //#endregion
@@ -1,13 +1,15 @@
1
1
  import { keys } from "./query-keys.js";
2
2
  import { useClient } from "./api-provider.js";
3
3
  import { MetaContext } from "./meta-context.js";
4
+ import { keepOnVersionChange } from "./version-freeze.js";
4
5
  import { useContext, useMemo } from "react";
5
6
  import { useQuery, useQueryClient } from "@tanstack/react-query";
6
7
  //#region src/data/use-meta.ts
7
8
  /**
8
9
  * The resource and view models. Refetches on window focus with `If-None-Match`, so an unchanged
9
- * `/meta` costs a 304, and the cached object keeps its identity. Below the app's `MetaGate`, `data` holds the resources
10
- * of its sources too, as every page of the app reads them.
10
+ * `/meta` costs a 304, and the cached object keeps its identity. Once the server runs another Protobase version than
11
+ * the page started with, it keeps the `/meta` it has and stops reading it (see `keepOnVersionChange`). Below the app's
12
+ * `MetaGate`, `data` holds the resources of its sources too, as every page of the app reads them.
11
13
  */
12
14
  const useMeta = () => {
13
15
  const client = useClient();
@@ -17,6 +19,7 @@ const useMeta = () => {
17
19
  queryKey: keys.meta,
18
20
  staleTime: 0,
19
21
  refetchOnWindowFocus: "always",
22
+ enabled: (current) => !current.state.data?.versionChange,
20
23
  queryFn: async () => {
21
24
  const previous = queryClient.getQueryData(keys.meta);
22
25
  const result = await client.meta(previous?.etag);
@@ -24,9 +27,10 @@ const useMeta = () => {
24
27
  if (!previous) throw new Error("The server answered 304 to a request without If-None-Match");
25
28
  return previous;
26
29
  }
27
- return {
30
+ return keepOnVersionChange(previous, {
28
31
  etag: result.etag,
29
32
  appName: result.meta.appName,
33
+ ...result.meta.protobaseVersion && { protobaseVersion: result.meta.protobaseVersion },
30
34
  ...result.meta.appIcon && { appIcon: result.meta.appIcon },
31
35
  resources: Object.fromEntries(result.meta.resources.map((model) => [model.name, model])),
32
36
  views: Object.fromEntries(result.meta.views.map((model) => [model.resource, model])),
@@ -46,7 +50,7 @@ const useMeta = () => {
46
50
  source,
47
51
  state: "loading"
48
52
  })) }
49
- };
53
+ });
50
54
  }
51
55
  });
52
56
  return useMemo(() => merged && query.data ? {
@@ -0,0 +1,17 @@
1
+ //#region src/data/version-freeze.ts
2
+ /**
3
+ * The `/meta` the page keeps after reading it again: `next`, unless the server now runs another Protobase version than
4
+ * the page started with. The page then keeps the `/meta` it has, marked with the version the server moved to, and
5
+ * `useMeta` stops reading it, so nothing on the page changes until it is reloaded.
6
+ */
7
+ const keepOnVersionChange = (previous, next) => {
8
+ if (!previous) return next;
9
+ if (previous.versionChange) return previous;
10
+ if (next.protobaseVersion === previous.protobaseVersion) return next;
11
+ return {
12
+ ...previous,
13
+ versionChange: { ...next.protobaseVersion && { to: next.protobaseVersion } }
14
+ };
15
+ };
16
+ //#endregion
17
+ export { keepOnVersionChange };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.40.6";
2
+ var version = "0.40.8";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };
@@ -0,0 +1,13 @@
1
+ //#region src/primitives/snap-row-width.ts
2
+ /** The space between the items of a `SnapRow`, Tailwind's `gap-3`. */
3
+ const snapRowGap = "0.75rem";
4
+ /**
5
+ * How wide an item of a row is at `share` of the row's width, the gaps taken off so that `1 / share` items fill it
6
+ * exactly: a third leaves room for three items and the two gaps between them.
7
+ */
8
+ const snapItemWidth = (share) => {
9
+ if (!(share > 0 && share <= 1)) throw new Error(`An item's share of a row is above 0 and at most 1, not ${share}`);
10
+ return `calc((100% + ${snapRowGap}) * ${share} - ${snapRowGap})`;
11
+ };
12
+ //#endregion
13
+ export { snapItemWidth, snapRowGap };
@@ -0,0 +1,21 @@
1
+ import { snapItemWidth, snapRowGap } from "./snap-row-width.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ //#region src/primitives/snap-row.tsx
4
+ /**
5
+ * A list laid out as one row that scrolls sideways, by touch, trackpad, wheel or its scrollbar, and snaps each item to
6
+ * its left edge. Each item is `share` of the row wide, but never narrower than 12rem or the row, so narrow shares stay
7
+ * readable on a phone; the items are top aligned. The row scrolls on its own because the page cannot scroll sideways,
8
+ * and only sideways: it is as tall as its tallest item. A swipe past its last item stops there.
9
+ */
10
+ const SnapRow = ({ share, label, children }) => /* @__PURE__ */ jsx("ul", {
11
+ "aria-label": label,
12
+ className: "flex snap-x snap-mandatory items-start overflow-x-auto overflow-y-hidden overscroll-x-contain pb-1 *:w-(--snap-item-width) *:min-w-[min(100%,12rem)] *:shrink-0 *:snap-start",
13
+ style: {
14
+ gap: snapRowGap,
15
+ scrollbarWidth: "thin",
16
+ "--snap-item-width": snapItemWidth(share)
17
+ },
18
+ children
19
+ });
20
+ //#endregion
21
+ export { SnapRow };