@protobase/ui 0.55.2 → 0.55.4
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/create/create-page.js +11 -1
- package/dist/app/marks/marks-hook.d.ts +10 -0
- package/dist/app/nav-from-meta.js +19 -3
- package/dist/app/nav-recent.js +10 -1
- package/dist/app/pages/merge-ui.d.ts +1 -1
- package/dist/app/pages/merge-ui.js +3 -1
- package/dist/app/pages/project-ui.d.ts +4 -1
- package/dist/app/use-live-save.js +11 -4
- package/dist/app/use-nav-recent.js +10 -6
- package/dist/app-shell/index.d.ts +1 -1
- package/dist/app-shell/nav.d.ts +13 -1
- package/dist/app-shell/record-marks.js +18 -0
- package/dist/app-shell/sidebar-create-link.js +26 -0
- package/dist/app-shell/sidebar-group.js +15 -30
- package/dist/app-shell/sidebar-records.js +54 -0
- package/dist/app-shell/sidebar-rows.js +13 -0
- package/dist/app-shell/sidebar.js +11 -5
- package/dist/auth/auth-provider.d.ts +4 -0
- package/dist/auth/use-signed-in-user.d.ts +13 -0
- package/dist/auth/use-signed-in-user.js +22 -0
- package/dist/chat/chat-thread.d.ts +10 -4
- package/dist/chat/chat-thread.js +20 -6
- package/dist/chat/thread-scroll.js +4 -1
- package/dist/chat/use-read-to-end.js +32 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +2 -1
- package/dist/notifications/bell/notification-bell.js +5 -8
- package/dist/notifications/bell/unread-label.js +1 -3
- package/dist/notifications/push/push-notifications.d.ts +1 -0
- package/dist/package.js +1 -1
- package/dist/plugins/plugin-ui.js +1 -0
- package/dist/primitives/count-badge.js +20 -0
- package/dist/shell/assets/app-NiljOxYe.js +9 -0
- package/dist/shell/assets/{index-CY05Rf2z.js → index-BIUTidjX.js} +2 -2
- package/dist/shell/assets/{records-map-DA9Zv2ST.js → records-map-BwX2z-di.js} +2 -2
- package/dist/shell/assets/shared-protobase-ui-BD_5D_Ot.js +2 -0
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-NiljOxYe.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-CY05Rf2z.js.map → index-BIUTidjX.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-DA9Zv2ST.js.map → records-map-BwX2z-di.js.map} +1 -1
- package/dist/shell-source-maps/assets/shared-protobase-ui-BD_5D_Ot.js.map +1 -0
- package/package.json +4 -4
- package/dist/shell/assets/app-CTbX0_B2.js +0 -9
- package/dist/shell/assets/shared-protobase-ui-DjAPl6MN.js +0 -2
- package/dist/shell-source-maps/assets/app-CTbX0_B2.js.map +0 -1
- package/dist/shell-source-maps/assets/shared-protobase-ui-DjAPl6MN.js.map +0 -1
package/dist/chat/chat-thread.js
CHANGED
|
@@ -3,15 +3,18 @@ import { Button } from "../primitives/button.js";
|
|
|
3
3
|
import { text } from "./messages.js";
|
|
4
4
|
import { scrollAfterChange } from "./thread-scroll.js";
|
|
5
5
|
import { readPlace } from "./read-place.js";
|
|
6
|
+
import { useReadToEnd } from "./use-read-to-end.js";
|
|
6
7
|
import { useEffect, useLayoutEffect, useRef } from "react";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
//#region src/chat/chat-thread.tsx
|
|
10
|
+
const newLineOf = (list) => list.querySelector("[data-chat-divider][data-tone=\"new\"]");
|
|
9
11
|
/**
|
|
10
|
-
* The scrolling list of a chat's messages.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* The scrolling list of a chat's messages. It opens on its `ChatDivider tone="new"` line when it has one, else at the
|
|
13
|
+
* end. A reader at the end follows new messages; one who scrolled back stays where they are until `followKey` changes,
|
|
14
|
+
* and earlier messages loaded above keep them on the message they were reading. It scrolls inside itself, not with the
|
|
15
|
+
* page, since a chat keeps its composer in view beneath it.
|
|
13
16
|
*/
|
|
14
|
-
const ChatThread = ({ children, empty, length, onLoadEarlier, loadingEarlier, followKey }) => {
|
|
17
|
+
const ChatThread = ({ children, empty, length, onLoadEarlier, loadingEarlier, followKey, onReadToEnd }) => {
|
|
15
18
|
const box = useRef(null);
|
|
16
19
|
const list = useRef(null);
|
|
17
20
|
const place = useRef({
|
|
@@ -22,20 +25,31 @@ const ChatThread = ({ children, empty, length, onLoadEarlier, loadingEarlier, fo
|
|
|
22
25
|
const remember = () => {
|
|
23
26
|
if (box.current && list.current) place.current = readPlace(box.current, list.current, followKey);
|
|
24
27
|
};
|
|
28
|
+
const readToEnd = () => place.current.atEnd && place.current.first !== void 0;
|
|
29
|
+
const tellRead = useReadToEnd(onReadToEnd, readToEnd);
|
|
30
|
+
const scrolled = () => {
|
|
31
|
+
const wasAtEnd = place.current.atEnd;
|
|
32
|
+
remember();
|
|
33
|
+
if (!wasAtEnd && readToEnd()) tellRead();
|
|
34
|
+
};
|
|
25
35
|
useLayoutEffect(() => {
|
|
26
36
|
const element = box.current;
|
|
27
37
|
if (!element || !list.current) return;
|
|
28
38
|
const before = place.current;
|
|
29
39
|
const anchorTop = before.anchor && before.anchor.element.isConnected ? before.anchor.element.getBoundingClientRect().top - element.getBoundingClientRect().top : void 0;
|
|
40
|
+
const newLine = newLineOf(list.current);
|
|
41
|
+
const newLineTop = newLine ? newLine.getBoundingClientRect().top - element.getBoundingClientRect().top : void 0;
|
|
30
42
|
const next = scrollAfterChange(before, {
|
|
31
43
|
first: list.current.firstElementChild ?? void 0,
|
|
32
44
|
anchorTop,
|
|
33
45
|
scrollTop: element.scrollTop,
|
|
34
46
|
scrollHeight: element.scrollHeight,
|
|
35
|
-
followKey
|
|
47
|
+
followKey,
|
|
48
|
+
newLineTop
|
|
36
49
|
});
|
|
37
50
|
if (next !== void 0) element.scrollTop = next;
|
|
38
51
|
remember();
|
|
52
|
+
if (readToEnd()) tellRead();
|
|
39
53
|
}, [length, followKey]);
|
|
40
54
|
useEffect(() => {
|
|
41
55
|
const element = box.current;
|
|
@@ -50,7 +64,7 @@ const ChatThread = ({ children, empty, length, onLoadEarlier, loadingEarlier, fo
|
|
|
50
64
|
const showsEmpty = length === 0 && Boolean(empty);
|
|
51
65
|
return /* @__PURE__ */ jsxs("div", {
|
|
52
66
|
ref: box,
|
|
53
|
-
onScroll:
|
|
67
|
+
onScroll: scrolled,
|
|
54
68
|
className: "flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto p-4 [overflow-anchor:none]",
|
|
55
69
|
children: [
|
|
56
70
|
onLoadEarlier && /* @__PURE__ */ jsx(Button, {
|
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
const isAtEnd = (box) => box.scrollHeight - box.scrollTop - box.clientHeight <= 24;
|
|
3
3
|
/**
|
|
4
4
|
* The scroll position a chat takes once its messages or follow key changed, or `undefined` to stay put. A changed follow
|
|
5
|
-
* key, such as the reader sending a message, goes to the end wherever the reader was.
|
|
5
|
+
* key, such as the reader sending a message, goes to the end wherever the reader was. A chat that gets its first
|
|
6
|
+
* messages opens with its "New messages" line near the top, when it has one at `newLineTop` below the top of the box,
|
|
7
|
+
* and at the end otherwise. Messages loaded before the first
|
|
6
8
|
* keep the reader on the message they were reading, moved by as much as it moved; otherwise a reader at the end follows
|
|
7
9
|
* the newest message, and one who scrolled back stays where they are.
|
|
8
10
|
*/
|
|
9
11
|
const scrollAfterChange = (before, now) => {
|
|
10
12
|
if (now.followKey !== before.followKey) return now.scrollHeight;
|
|
13
|
+
if (before.first === void 0 && now.first !== void 0 && now.newLineTop !== void 0) return Math.max(0, now.scrollTop + now.newLineTop - 16);
|
|
11
14
|
if (before.first !== void 0 && now.first !== before.first && before.anchor && now.anchorTop !== void 0) return now.scrollTop + now.anchorTop - before.anchor.top;
|
|
12
15
|
if (before.atEnd) return now.scrollHeight;
|
|
13
16
|
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useRef } from "react";
|
|
2
|
+
//#region src/chat/use-read-to-end.ts
|
|
3
|
+
/**
|
|
4
|
+
* Tells `onReadToEnd` that the reader is at the end of a chat, but only while the page shows: the returned function
|
|
5
|
+
* tells it now, and the page becoming visible again tells it when `atEnd()` holds then. Call it before the layout
|
|
6
|
+
* effects that tell, so they tell the latest `onReadToEnd`.
|
|
7
|
+
*/
|
|
8
|
+
const useReadToEnd = (onReadToEnd, atEnd) => {
|
|
9
|
+
const latest = useRef({
|
|
10
|
+
onReadToEnd,
|
|
11
|
+
atEnd
|
|
12
|
+
});
|
|
13
|
+
useLayoutEffect(() => {
|
|
14
|
+
latest.current = {
|
|
15
|
+
onReadToEnd,
|
|
16
|
+
atEnd
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
const tell = useCallback(() => {
|
|
20
|
+
if (document.visibilityState === "visible") latest.current.onReadToEnd?.();
|
|
21
|
+
}, []);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const shown = () => {
|
|
24
|
+
if (latest.current.atEnd()) tell();
|
|
25
|
+
};
|
|
26
|
+
document.addEventListener("visibilitychange", shown);
|
|
27
|
+
return () => document.removeEventListener("visibilitychange", shown);
|
|
28
|
+
}, [tell]);
|
|
29
|
+
return tell;
|
|
30
|
+
};
|
|
31
|
+
//#endregion
|
|
32
|
+
export { useReadToEnd };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { RecordMark } from "./app-shell/nav.js";
|
|
1
2
|
import { LogoMark, ShellLogoProps } from "./app-shell/logo.js";
|
|
2
3
|
import { FieldWidget, FieldWidgetEditorProps, FieldWidgetProps } from "./app/field-types/field-widget.js";
|
|
3
4
|
import { BoardDrop, BoardDropContext, BoardDropUndo, BoardDropWrite } from "./app/displays/board-drop.js";
|
|
4
5
|
import { Drop, DropRule } from "./app/hand-order/drop-rule.js";
|
|
5
6
|
import { TitleReader, TitleRule } from "./app/titles/title-rule.js";
|
|
7
|
+
import { MarksHook } from "./app/marks/marks-hook.js";
|
|
6
8
|
import { ActionContext, ActionHandler, BeforeSignInProps, ProjectUi, definePlugin, defineUi } from "./app/pages/project-ui.js";
|
|
7
9
|
import { PluginInfo, usePlugin } from "./plugins/plugin-context.js";
|
|
8
10
|
import { App, AppProps } from "./app/app.js";
|
|
@@ -26,8 +28,9 @@ import { ListDisplayRow, RecordCardProps } from "./app/displays/list-displays.js
|
|
|
26
28
|
import { ListDisplay, useListDisplay } from "./app/displays/display-context.js";
|
|
27
29
|
import { RecordLink } from "./app/displays/record-link.js";
|
|
28
30
|
import { useApiToken } from "./auth/use-api-token.js";
|
|
29
|
-
import { MembersResult, useMembers } from "./people/use-members.js";
|
|
30
31
|
import { PushState } from "./notifications/push/web-push.js";
|
|
32
|
+
import { SignedInUser, useSignedInUser } from "./auth/use-signed-in-user.js";
|
|
33
|
+
import { MembersResult, useMembers } from "./people/use-members.js";
|
|
31
34
|
import { usePushNotifications } from "./notifications/push/use-push-notifications.js";
|
|
32
35
|
import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./notifications/bell/use-inbox.js";
|
|
33
36
|
import { SignInPrompt, SignInPromptProps } from "./auth/sign-in-prompt.js";
|
|
@@ -81,4 +84,4 @@ import { Section } from "./record-view/section.js";
|
|
|
81
84
|
import { Form, FormProps } from "./app/create/form.js";
|
|
82
85
|
import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
|
|
83
86
|
import { Spinner } from "./primitives/spinner.js";
|
|
84
|
-
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, type TitleReader, type TitleRule, 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 };
|
|
87
|
+
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 MarksHook, 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, type RecordMark, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, type SignedInUser, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, type TitleReader, type TitleRule, 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, useSignedInUser, useUnreadNotifications, useUpdateRecord };
|
package/dist/index.js
CHANGED
|
@@ -59,7 +59,8 @@ import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./no
|
|
|
59
59
|
import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
|
|
60
60
|
import { App } from "./app/app.js";
|
|
61
61
|
import { RecordLink } from "./app/displays/record-link.js";
|
|
62
|
+
import { useSignedInUser } from "./auth/use-signed-in-user.js";
|
|
62
63
|
import { Carousel } from "./carousel/carousel.js";
|
|
63
64
|
import { Choices } from "./primitives/choices.js";
|
|
64
65
|
import { RecordPicker } from "./app/record-picker.js";
|
|
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 };
|
|
66
|
+
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, useSignedInUser, useUnreadNotifications, useUpdateRecord };
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Button } from "../../primitives/button.js";
|
|
2
|
-
import { Badge } from "../../primitives/badge.js";
|
|
3
2
|
import { useRouter } from "../../app/router.js";
|
|
4
3
|
import { usePopoverMenu } from "../../primitives/popover-menu.js";
|
|
4
|
+
import { CountBadge } from "../../primitives/count-badge.js";
|
|
5
5
|
import { usePushNotifications } from "../push/use-push-notifications.js";
|
|
6
6
|
import { NotificationPanel } from "./notification-panel.js";
|
|
7
7
|
import { notificationTarget } from "./notification-target.js";
|
|
8
8
|
import { PushControl } from "./push-control.js";
|
|
9
9
|
import { historyRefused, useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./use-inbox.js";
|
|
10
|
-
import { bellLabel
|
|
10
|
+
import { bellLabel } from "./unread-label.js";
|
|
11
11
|
import { useState } from "react";
|
|
12
12
|
import { Bell } from "lucide-react";
|
|
13
13
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -25,7 +25,6 @@ const NotificationBell = () => {
|
|
|
25
25
|
const push = usePushNotifications();
|
|
26
26
|
const { navigate, basePath } = useRouter();
|
|
27
27
|
const [now, setNow] = useState(Date.now);
|
|
28
|
-
const badge = unreadBadge(unread.data);
|
|
29
28
|
const label = bellLabel(unread.data);
|
|
30
29
|
const origin = window.location.origin;
|
|
31
30
|
if (historyRefused(unread.error)) return null;
|
|
@@ -47,11 +46,9 @@ const NotificationBell = () => {
|
|
|
47
46
|
"aria-expanded": open,
|
|
48
47
|
onClick: toggle,
|
|
49
48
|
className: "relative max-md:size-9",
|
|
50
|
-
children: [/* @__PURE__ */ jsx(Bell, { className: "size-4 max-md:size-5" }),
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
className: "pointer-events-none absolute -top-1 -right-1.5 h-4 min-w-4 justify-center px-1 text-[10px] font-semibold max-md:top-0 max-md:right-0",
|
|
54
|
-
children: badge
|
|
49
|
+
children: [/* @__PURE__ */ jsx(Bell, { className: "size-4 max-md:size-5" }), /* @__PURE__ */ jsx(CountBadge, {
|
|
50
|
+
count: unread.data,
|
|
51
|
+
className: "pointer-events-none absolute -top-1 -right-1.5 max-md:top-0 max-md:right-0"
|
|
55
52
|
})]
|
|
56
53
|
}), open && createPortal(/* @__PURE__ */ jsx("div", {
|
|
57
54
|
ref: menu,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { text } from "./messages.js";
|
|
2
2
|
//#region src/notifications/bell/unread-label.ts
|
|
3
3
|
const shownMax = 99;
|
|
4
|
-
/** The count over the bell, or none while nothing is unread. */
|
|
5
|
-
const unreadBadge = (count) => count === void 0 || count <= 0 ? void 0 : count > shownMax ? `${shownMax}+` : String(count);
|
|
6
4
|
/** The bell's name for a screen reader, with how many are unread. */
|
|
7
5
|
const bellLabel = (count) => {
|
|
8
6
|
if (count === void 0 || count <= 0) return text("notifications");
|
|
9
7
|
return count > shownMax ? text("notificationsManyUnread") : text("notificationsUnread", String(count));
|
|
10
8
|
};
|
|
11
9
|
//#endregion
|
|
12
|
-
export { bellLabel
|
|
10
|
+
export { bellLabel };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./web-push.js";
|
package/dist/package.js
CHANGED
|
@@ -43,6 +43,7 @@ const pluginUi = (plugin, module) => {
|
|
|
43
43
|
...ui.dropRules && { dropRules: ui.dropRules },
|
|
44
44
|
...ui.boardDrops && { boardDrops: ui.boardDrops },
|
|
45
45
|
...ui.titles && { titles: ui.titles },
|
|
46
|
+
...ui.marks && { marks: ui.marks },
|
|
46
47
|
...fields && { fields },
|
|
47
48
|
...shell && { shell },
|
|
48
49
|
...beforeSignIn && { beforeSignIn }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { Badge } from "./badge.js";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
//#region src/primitives/count-badge.tsx
|
|
5
|
+
const shownMax = 99;
|
|
6
|
+
/** A count as its badge shows it: up to 99, then 99+; none while it is zero or unknown. */
|
|
7
|
+
const countBadgeText = (count) => count === void 0 || count <= 0 ? void 0 : count > shownMax ? `${shownMax}+` : String(count);
|
|
8
|
+
/** The small red pill with a count of unread things, such as over the notification bell; nothing while the count is zero. */
|
|
9
|
+
const CountBadge = ({ count, className }) => {
|
|
10
|
+
const shown = countBadgeText(count);
|
|
11
|
+
if (!shown) return null;
|
|
12
|
+
return /* @__PURE__ */ jsx(Badge, {
|
|
13
|
+
tone: "red",
|
|
14
|
+
dot: false,
|
|
15
|
+
className: cn("h-4 min-w-4 justify-center px-1 text-[10px] font-semibold", className),
|
|
16
|
+
children: shown
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { CountBadge, countBadgeText };
|