@protobase/ui 0.53.8 → 0.54.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.
@@ -11,14 +11,23 @@ const recentRequest = (model, view, recent) => ({
11
11
  fields: [.../* @__PURE__ */ new Set([
12
12
  ...model.primaryKey,
13
13
  recordTitleField(model, view),
14
- recent.status
14
+ ...recent.status === void 0 ? [] : [recent.status]
15
15
  ])]
16
16
  });
17
- /** List rows as sidebar records: titled like the record page, dotted by their status, active while open. */
17
+ const recordStatus = (model, view, recent, row) => {
18
+ if (recent.status === void 0) return;
19
+ const value = String(row[recent.status] ?? "");
20
+ return {
21
+ label: model.fields[recent.status]?.type === "enum" && value ? enumLabel(value, view.fields[recent.status]?.valueLabels) : value,
22
+ tone: recent.tones?.[value] ?? "neutral",
23
+ pulse: recent.pulse?.includes(value) ?? false
24
+ };
25
+ };
26
+ /** List rows as sidebar records: titled like the record page, dotted by their status when the group has one, active while open. */
18
27
  const recentRecords = (model, view, recent, rows, basePath, activeKey) => {
19
28
  return rows.map((row) => {
20
29
  const key = encodeKey(recordKey(model, row));
21
- const status = String(row[recent.status] ?? "");
30
+ const status = recordStatus(model, view, recent, row);
22
31
  return {
23
32
  id: key,
24
33
  label: recordTitle(model, view, row, key),
@@ -27,9 +36,7 @@ const recentRecords = (model, view, recent, rows, basePath, activeKey) => {
27
36
  resource: model.name,
28
37
  key
29
38
  }, basePath),
30
- status: model.fields[recent.status]?.type === "enum" && status ? enumLabel(status, view.fields[recent.status]?.valueLabels) : status,
31
- tone: recent.tones[status] ?? "neutral",
32
- pulse: recent.pulse?.includes(status) ?? false,
39
+ ...status && { status },
33
40
  active: key === activeKey
34
41
  };
35
42
  });
@@ -70,13 +70,13 @@ const SidebarGroup = ({ item, recent, mode, active, onNavigate }) => {
70
70
  }),
71
71
  recent.state === "ready" && recent.records.map((record) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("a", {
72
72
  ...link(record.href),
73
- "aria-label": `${record.label}, ${record.status}`,
74
- title: `${record.label} (${record.status})`,
73
+ "aria-label": record.status ? `${record.label}, ${record.status.label}` : record.label,
74
+ title: record.status ? `${record.label} (${record.status.label})` : record.label,
75
75
  "aria-current": record.active ? "page" : void 0,
76
76
  className: cn("flex min-w-0 items-center rounded-md font-medium transition-colors", row[mode], record.active ? "bg-muted text-foreground" : "text-muted-foreground hover:bg-muted hover:text-foreground"),
77
- children: [/* @__PURE__ */ jsx(StatusDot, {
78
- tone: record.tone,
79
- pulse: record.pulse
77
+ children: [record.status && /* @__PURE__ */ jsx(StatusDot, {
78
+ tone: record.status.tone,
79
+ pulse: record.status.pulse
80
80
  }), /* @__PURE__ */ jsx("span", {
81
81
  className: "truncate",
82
82
  children: record.label
@@ -155,6 +155,8 @@ const keys = {
155
155
  inboxUnread: ["inbox", "unread"],
156
156
  /** The pages of the user's notification history. */
157
157
  inboxPages: ["inbox", "pages"],
158
+ /** The people of the organization the session of this user works in (`useMembers`). */
159
+ members: (userId) => ["members", userId],
158
160
  /** A public form, as `/-/forms/<name>` draws it. */
159
161
  publicForm: (name) => ["public-form", name]
160
162
  };
package/dist/index.d.ts CHANGED
@@ -25,6 +25,7 @@ import { ListDisplayRow, RecordCardProps } from "./app/displays/list-displays.js
25
25
  import { ListDisplay, useListDisplay } from "./app/displays/display-context.js";
26
26
  import { RecordLink } from "./app/displays/record-link.js";
27
27
  import { useApiToken } from "./auth/use-api-token.js";
28
+ import { MembersResult, useMembers } from "./people/use-members.js";
28
29
  import { PushState } from "./notifications/push/web-push.js";
29
30
  import { usePushNotifications } from "./notifications/push/use-push-notifications.js";
30
31
  import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./notifications/bell/use-inbox.js";
@@ -79,4 +80,4 @@ import { Section } from "./record-view/section.js";
79
80
  import { Form, FormProps } from "./app/create/form.js";
80
81
  import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
81
82
  import { Spinner } from "./primitives/spinner.js";
82
- export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, Carousel, type CarouselImage, type CarouselProps, type ChatAuthor, ChatDivider, type ChatDividerProps, ChatMessage, type ChatMessageProps, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, Markdown, type MarkdownContentProps as MarkdownProps, MentionBox, type MentionBoxProps, type MentionDraft, MentionedText, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PeopleSearch, type PickedUser, type PluginInfo, type ProjectUi, type PushState, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
83
+ export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, Carousel, type CarouselImage, type CarouselProps, type ChatAuthor, ChatDivider, type ChatDividerProps, ChatMessage, type ChatMessageProps, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, Markdown, type MarkdownContentProps as MarkdownProps, type MembersResult, MentionBox, type MentionBoxProps, type MentionDraft, MentionedText, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PeopleSearch, type PickedUser, type PluginInfo, type ProjectUi, type PushState, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMembers, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
package/dist/index.js CHANGED
@@ -58,7 +58,8 @@ import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./no
58
58
  import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
59
59
  import { App } from "./app/app.js";
60
60
  import { RecordLink } from "./app/displays/record-link.js";
61
+ import { useMembers } from "./people/use-members.js";
61
62
  import { Carousel } from "./carousel/carousel.js";
62
63
  import { Choices } from "./primitives/choices.js";
63
64
  import { RecordPicker } from "./app/record-picker.js";
64
- export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, Carousel, ChatDivider, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MentionBox, MentionedText, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
65
+ export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, Carousel, ChatDivider, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MentionBox, MentionedText, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, allowDelayMs, buttonClass, chatDayLabel, definePlugin, defineUi, diffStats, emptyDraft, isConflict, isFramed, mentionedIds, mergeUi, parseDiff, pluginApi, protobaseVersion, sameChatDay, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMembers, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.53.8";
2
+ var version = "0.54.1";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };
@@ -0,0 +1,15 @@
1
+ import { OrganizationPerson } from "@protobase/client";
2
+ import { UseQueryResult } from "@tanstack/react-query";
3
+ //#region src/people/use-members.d.ts
4
+ export type MembersResult = UseQueryResult<OrganizationPerson[]> & {
5
+ /** The member with this user id; an unknown one reads the list again (see `asksAgainFor`). */
6
+ member: (id: string) => OrganizationPerson | undefined;
7
+ };
8
+ /**
9
+ * The members of the organization the session works in, as `{ id, name, image? }` with `id` the user id that
10
+ * `.author()` stores and mentions hold, by name, readable by every member and without addresses. Read again on focus
11
+ * once 30 s old, and when `member` is asked for an id it lacks. Without organizations or the app's auth it fails
12
+ * saying so.
13
+ */
14
+ export declare const useMembers: () => MembersResult;
15
+ //#endregion
@@ -0,0 +1,44 @@
1
+ import { useOptionalAuth } from "../auth/auth-provider.js";
2
+ import { keys } from "../data/query-keys.js";
3
+ import { useCallback, useMemo } from "react";
4
+ import { useQuery } from "@tanstack/react-query";
5
+ //#region src/people/use-members.ts
6
+ /** How old the list must be before an id it lacks, such as a newcomer's, reads it again. */
7
+ const unknownIdRefetchMs = 1e4;
8
+ /** Whether an id the list lacks reads it again: once it has loaded, is not being read, and is old enough. */
9
+ const asksAgainFor = (query, now = Date.now()) => query.isSuccess && !query.isFetching && now - query.dataUpdatedAt >= 1e4;
10
+ /**
11
+ * The members of the organization the session works in, as `{ id, name, image? }` with `id` the user id that
12
+ * `.author()` stores and mentions hold, by name, readable by every member and without addresses. Read again on focus
13
+ * once 30 s old, and when `member` is asked for an id it lacks. Without organizations or the app's auth it fails
14
+ * saying so.
15
+ */
16
+ const useMembers = () => {
17
+ const auth = useOptionalAuth();
18
+ const user = auth?.state.kind === "signed-in" ? auth.state.user : void 0;
19
+ const query = useQuery({
20
+ queryKey: keys.members(user?.id ?? ""),
21
+ enabled: user !== void 0,
22
+ queryFn: () => {
23
+ if (!auth?.organizations) throw new Error("useMembers() needs organizations: turn them on in the auth config.");
24
+ return auth.session.organizations.people();
25
+ }
26
+ });
27
+ const byId = useMemo(() => new Map((query.data ?? []).map((person) => [person.id, person])), [query.data]);
28
+ const { refetch } = query;
29
+ const member = useCallback((id) => {
30
+ const found = byId.get(id);
31
+ if (!found && asksAgainFor(query)) setTimeout(() => void refetch(), 0);
32
+ return found;
33
+ }, [
34
+ byId,
35
+ query,
36
+ refetch
37
+ ]);
38
+ return {
39
+ ...query,
40
+ member
41
+ };
42
+ };
43
+ //#endregion
44
+ export { asksAgainFor, unknownIdRefetchMs, useMembers };