@protobase/ui 0.53.4 → 0.53.6

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 (47) hide show
  1. package/dist/app/comments/comment-body.js +5 -8
  2. package/dist/app/comments/comment-composer.js +29 -114
  3. package/dist/app/comments/messages.js +0 -4
  4. package/dist/chat/chat-day.d.ts +6 -0
  5. package/dist/chat/chat-day.js +17 -0
  6. package/dist/chat/chat-divider.d.ts +15 -0
  7. package/dist/chat/chat-divider.js +25 -0
  8. package/dist/chat/chat-message.d.ts +26 -4
  9. package/dist/chat/chat-message.js +65 -5
  10. package/dist/chat/chat-thread.d.ts +13 -3
  11. package/dist/chat/chat-thread.js +85 -16
  12. package/dist/chat/composer.d.ts +12 -4
  13. package/dist/chat/composer.js +30 -15
  14. package/dist/chat/index.d.ts +4 -2
  15. package/dist/chat/messages.js +10 -2
  16. package/dist/chat/thread-scroll.js +15 -0
  17. package/dist/data/query-keys.js +0 -7
  18. package/dist/index.d.ts +10 -3
  19. package/dist/index.js +6 -1
  20. package/dist/mentions/index.d.ts +5 -0
  21. package/dist/mentions/mention-box.d.ts +27 -0
  22. package/dist/mentions/mention-box.js +125 -0
  23. package/dist/mentions/mention-text.d.ts +9 -0
  24. package/dist/{app/comments → mentions}/mention-text.js +1 -1
  25. package/dist/mentions/mentioned-text.d.ts +9 -0
  26. package/dist/mentions/mentioned-text.js +13 -0
  27. package/dist/mentions/messages.js +16 -0
  28. package/dist/mentions/people-list.js +26 -0
  29. package/dist/mentions/use-people-search.d.ts +5 -0
  30. package/dist/mentions/use-people-search.js +26 -0
  31. package/dist/package.js +1 -1
  32. package/dist/shell/assets/{app-KfucDRFg.js → app-lIH0RyO8.js} +8 -8
  33. package/dist/shell/assets/{index-DITOhzRs.js → index-BUHPj0Eg.js} +2 -2
  34. package/dist/shell/assets/{index-DFC2GwM_.css → index-BX1N2n2W.css} +1 -1
  35. package/dist/shell/assets/{records-map-DLdvIE4o.js → records-map-_qjIukY1.js} +2 -2
  36. package/dist/shell/assets/shared-protobase-ui-MwC2WreJ.js +2 -0
  37. package/dist/shell/index.html +4 -4
  38. package/dist/shell/protobase.shell.json +1 -1
  39. package/dist/shell/protobase.sw.js +2 -2
  40. package/dist/shell-source-maps/assets/app-lIH0RyO8.js.map +1 -0
  41. package/dist/shell-source-maps/assets/{index-DITOhzRs.js.map → index-BUHPj0Eg.js.map} +1 -1
  42. package/dist/shell-source-maps/assets/{records-map-DLdvIE4o.js.map → records-map-_qjIukY1.js.map} +1 -1
  43. package/dist/shell-source-maps/assets/{shared-protobase-ui-Ss_2gqqM.js.map → shared-protobase-ui-MwC2WreJ.js.map} +1 -1
  44. package/package.json +4 -4
  45. package/dist/app/comments/use-mentionable.js +0 -18
  46. package/dist/shell/assets/shared-protobase-ui-Ss_2gqqM.js +0 -2
  47. package/dist/shell-source-maps/assets/app-KfucDRFg.js.map +0 -1
@@ -0,0 +1,15 @@
1
+ /** Whether a scroll box shows its end. */
2
+ const isAtEnd = (box) => box.scrollHeight - box.scrollTop - box.clientHeight <= 24;
3
+ /**
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. Messages loaded before the first
6
+ * keep the reader on the message they were reading, moved by as much as it moved; otherwise a reader at the end follows
7
+ * the newest message, and one who scrolled back stays where they are.
8
+ */
9
+ const scrollAfterChange = (before, now) => {
10
+ if (now.followKey !== before.followKey) return now.scrollHeight;
11
+ if (before.first !== void 0 && now.first !== before.first && before.anchor && now.anchorTop !== void 0) return now.scrollTop + now.anchorTop - before.anchor.top;
12
+ if (before.atEnd) return now.scrollHeight;
13
+ };
14
+ //#endregion
15
+ export { isAtEnd, scrollAfterChange };
@@ -74,13 +74,6 @@ const keys = {
74
74
  "comments"
75
75
  ],
76
76
  /** The people a comment on a record may mention whose name contains `query`. */
77
- mentionable: (resource, key, query) => [
78
- "record",
79
- resource,
80
- key,
81
- "mentionable",
82
- query
83
- ],
84
77
  facets: (resource, field, filter) => [
85
78
  "facets",
86
79
  resource,
package/dist/index.d.ts CHANGED
@@ -37,8 +37,16 @@ import { useAssistant } from "./assistant-dock/use-assistant.js";
37
37
  import { useAssistantClient } from "./assistant-dock/use-assistant-client.js";
38
38
  import { BackendData, BackendRefresh, useBackendData } from "./assistant-dock/use-backend-data.js";
39
39
  import "./assistant-dock/index.js";
40
- import { ChatMessage } from "./chat/chat-message.js";
40
+ import { chatDayLabel, sameChatDay } from "./chat/chat-day.js";
41
+ import { ChatDivider, ChatDividerProps } from "./chat/chat-divider.js";
42
+ import { ChatAuthor, ChatMessage, ChatMessageProps } from "./chat/chat-message.js";
41
43
  import { ChatThread, ChatThreadProps } from "./chat/chat-thread.js";
44
+ import { Textarea, TextareaProps } from "./primitives/textarea.js";
45
+ import { PickedUser, mentionedIds } from "./mentions/mention-text.js";
46
+ import { PeopleSearch } from "./mentions/use-people-search.js";
47
+ import { MentionBox, MentionBoxProps, MentionDraft, emptyDraft } from "./mentions/mention-box.js";
48
+ import { MentionedText } from "./mentions/mentioned-text.js";
49
+ import "./mentions/index.js";
42
50
  import { Composer, ComposerProps } from "./chat/composer.js";
43
51
  import "./chat/index.js";
44
52
  import { SidePanel, SidePanelProps } from "./side-panel/side-panel.js";
@@ -61,7 +69,6 @@ import { ResendCodeButton, ResendCodeButtonProps } from "./auth/resend-code-butt
61
69
  import { allowDelayMs, isFramed, useArmedAfterFocus, useGuardedPress } from "./connect/interaction-guard.js";
62
70
  import { Button, ButtonProps, buttonClass } from "./primitives/button.js";
63
71
  import { Input, InputProps } from "./primitives/input.js";
64
- import { Textarea, TextareaProps } from "./primitives/textarea.js";
65
72
  import { Segmented, SegmentedOption, SegmentedProps } from "./primitives/segmented.js";
66
73
  import { MenuSelect, MenuSelectProps } from "./primitives/menu-select.js";
67
74
  import { ChoiceOption, Choices, ChoicesProps } from "./primitives/choices.js";
@@ -72,4 +79,4 @@ import { Section } from "./record-view/section.js";
72
79
  import { Form, FormProps } from "./app/create/form.js";
73
80
  import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
74
81
  import { Spinner } from "./primitives/spinner.js";
75
- 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, ChatMessage, 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, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, 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, definePlugin, defineUi, diffStats, isConflict, isFramed, mergeUi, parseDiff, pluginApi, protobaseVersion, 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 };
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 };
package/dist/index.js CHANGED
@@ -32,8 +32,13 @@ import { usePlugin } from "./plugins/plugin-context.js";
32
32
  import { pluginApi, protobaseVersion } from "./plugins/plugin-api.js";
33
33
  import { useApiToken } from "./auth/use-api-token.js";
34
34
  import { useRecord } from "./data/use-record.js";
35
+ import { chatDayLabel, sameChatDay } from "./chat/chat-day.js";
36
+ import { ChatDivider } from "./chat/chat-divider.js";
35
37
  import { ChatMessage } from "./chat/chat-message.js";
36
38
  import { ChatThread } from "./chat/chat-thread.js";
39
+ import { mentionedIds } from "./mentions/mention-text.js";
40
+ import { MentionBox, emptyDraft } from "./mentions/mention-box.js";
41
+ import { MentionedText } from "./mentions/mentioned-text.js";
37
42
  import { Composer } from "./chat/composer.js";
38
43
  import { SidePanel } from "./side-panel/side-panel.js";
39
44
  import { ActionCard } from "./action-card/action-card.js";
@@ -56,4 +61,4 @@ import { RecordLink } from "./app/displays/record-link.js";
56
61
  import { Carousel } from "./carousel/carousel.js";
57
62
  import { Choices } from "./primitives/choices.js";
58
63
  import { RecordPicker } from "./app/record-picker.js";
59
- export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, Carousel, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, allowDelayMs, buttonClass, definePlugin, defineUi, diffStats, isConflict, isFramed, mergeUi, parseDiff, pluginApi, protobaseVersion, 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 };
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 };
@@ -0,0 +1,5 @@
1
+ import { PickedUser, mentionedIds } from "./mention-text.js";
2
+ import { PeopleSearch } from "./use-people-search.js";
3
+ import { MentionBox, MentionBoxProps, MentionDraft, emptyDraft } from "./mention-box.js";
4
+ import { MentionedText } from "./mentioned-text.js";
5
+ export { MentionBox, type MentionBoxProps, type MentionDraft, MentionedText, type PeopleSearch, type PickedUser, emptyDraft, mentionedIds };
@@ -0,0 +1,27 @@
1
+ import { TextareaProps } from "../primitives/textarea.js";
2
+ import { PickedUser } from "./mention-text.js";
3
+ import { PeopleSearch } from "./use-people-search.js";
4
+ import { KeyboardEvent } from "react";
5
+ //#region src/mentions/mention-box.d.ts
6
+ /** What is written in a mention box: the text, and the people picked from its list, whom `@{name}` in it mentions. */
7
+ export type MentionDraft = {
8
+ text: string;
9
+ picked: readonly PickedUser[];
10
+ };
11
+ export declare const emptyDraft: MentionDraft;
12
+ export type MentionBoxProps = Omit<TextareaProps, 'value' | 'onChange' | 'onKeyDown' | 'role'> & {
13
+ value: MentionDraft;
14
+ onChange: (draft: MentionDraft) => void;
15
+ /** Finds the people `@` lists by the name typed after it; without it, `@` is plain text. */
16
+ searchPeople?: PeopleSearch;
17
+ /** Where the list opens: under the box, or above it for a box at the bottom of a chat. */
18
+ listAt?: 'below' | 'above';
19
+ /** The keys the open list does not take, such as Enter to send. */
20
+ onKeyDown?: (event: KeyboardEvent<HTMLTextAreaElement>) => void;
21
+ };
22
+ /**
23
+ * A text box of several lines in which typing `@` lists the people `searchPeople` finds by the name typed after it:
24
+ * the arrows move, Enter or Tab picks, Escape closes. A picked name is written out as `@{name} `.
25
+ */
26
+ export declare const MentionBox: ({ value, onChange, searchPeople, listAt, onKeyDown, onFocus, onBlur, ref: given, ...rest }: MentionBoxProps) => import("react").JSX.Element;
27
+ //#endregion
@@ -0,0 +1,125 @@
1
+ import { Textarea } from "../primitives/textarea.js";
2
+ import { menuKeyTarget } from "../primitives/menu-keys.js";
3
+ import { activeMention, insertMention } from "./mention-text.js";
4
+ import { PeopleList } from "./people-list.js";
5
+ import { usePeopleSearch } from "./use-people-search.js";
6
+ import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/mentions/mention-box.tsx
9
+ const emptyDraft = {
10
+ text: "",
11
+ picked: []
12
+ };
13
+ /** The most people the list offers; few enough that it never needs a scrollbar of its own. */
14
+ const shownPeople = 8;
15
+ /**
16
+ * A text box of several lines in which typing `@` lists the people `searchPeople` finds by the name typed after it:
17
+ * the arrows move, Enter or Tab picks, Escape closes. A picked name is written out as `@{name} `.
18
+ */
19
+ const MentionBox = ({ value, onChange, searchPeople, listAt = "below", onKeyDown, onFocus, onBlur, ref: given, ...rest }) => {
20
+ const [caret, setCaret] = useState(0);
21
+ const [focused, setFocused] = useState(false);
22
+ const [active, setActive] = useState(0);
23
+ const [closed, setClosed] = useState();
24
+ const box = useRef(null);
25
+ const setBox = useCallback((element) => {
26
+ box.current = element;
27
+ if (typeof given === "function") given(element);
28
+ else if (given) given.current = element;
29
+ }, [given]);
30
+ const nextCaret = useRef(void 0);
31
+ const listId = useId();
32
+ const mention = searchPeople ? activeMention(value.text, caret) : void 0;
33
+ const open = focused && mention !== void 0 && mention.start !== closed;
34
+ const found = usePeopleSearch(searchPeople, open ? mention.query : void 0);
35
+ const options = open && found && "people" in found ? found.people.slice(0, shownPeople) : [];
36
+ const listed = open && found !== void 0;
37
+ const optionId = (index) => `${listId}-${index}`;
38
+ useEffect(() => setActive(0), [mention?.query]);
39
+ const mentionStart = mention?.start;
40
+ useEffect(() => {
41
+ if (mentionStart === void 0) setClosed(void 0);
42
+ }, [mentionStart]);
43
+ useLayoutEffect(() => {
44
+ if (nextCaret.current === void 0 || !box.current) return;
45
+ box.current.setSelectionRange(nextCaret.current, nextCaret.current);
46
+ setCaret(nextCaret.current);
47
+ nextCaret.current = void 0;
48
+ }, [value.text]);
49
+ const pick = (user) => {
50
+ if (!mention) return;
51
+ const next = insertMention(value.text, mention, user.name);
52
+ nextCaret.current = next.caret;
53
+ onChange({
54
+ text: next.text,
55
+ picked: [...value.picked, user]
56
+ });
57
+ };
58
+ const keyDown = (event) => {
59
+ if (open) {
60
+ const target = menuKeyTarget(event.key, active, options.length);
61
+ if (target !== void 0 && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
62
+ event.preventDefault();
63
+ setActive(target);
64
+ return;
65
+ }
66
+ if ((event.key === "Enter" || event.key === "Tab") && options[active]) {
67
+ event.preventDefault();
68
+ pick(options[active]);
69
+ return;
70
+ }
71
+ if (event.key === "Escape") {
72
+ event.preventDefault();
73
+ event.stopPropagation();
74
+ setClosed(mention.start);
75
+ return;
76
+ }
77
+ }
78
+ onKeyDown?.(event);
79
+ };
80
+ const combobox = searchPeople && {
81
+ role: "combobox",
82
+ "aria-multiline": true,
83
+ "aria-autocomplete": "list",
84
+ "aria-expanded": listed,
85
+ "aria-controls": listId,
86
+ "aria-activedescendant": listed && options[active] ? optionId(active) : void 0
87
+ };
88
+ return /* @__PURE__ */ jsxs("div", {
89
+ className: "relative",
90
+ children: [/* @__PURE__ */ jsx(Textarea, {
91
+ ...rest,
92
+ ...combobox,
93
+ ref: setBox,
94
+ value: value.text,
95
+ onChange: (event) => {
96
+ onChange({
97
+ ...value,
98
+ text: event.target.value
99
+ });
100
+ setCaret(event.target.selectionStart);
101
+ },
102
+ onSelect: (event) => setCaret(event.currentTarget.selectionStart),
103
+ onFocus: (event) => {
104
+ setFocused(true);
105
+ onFocus?.(event);
106
+ },
107
+ onBlur: (event) => {
108
+ setFocused(false);
109
+ onBlur?.(event);
110
+ },
111
+ onKeyDown: keyDown
112
+ }), listed && /* @__PURE__ */ jsx(PeopleList, {
113
+ id: listId,
114
+ optionId,
115
+ people: options,
116
+ active,
117
+ onActive: setActive,
118
+ onPick: pick,
119
+ failed: "failed" in found,
120
+ at: listAt
121
+ })]
122
+ });
123
+ };
124
+ //#endregion
125
+ export { MentionBox, emptyDraft };
@@ -0,0 +1,9 @@
1
+ //#region src/mentions/mention-text.d.ts
2
+ /** A user picked from the list while writing, whom `@{name}` in the text mentions. */
3
+ export type PickedUser = {
4
+ id: string;
5
+ name: string;
6
+ };
7
+ /** The ids of the picked users whose `@{name}` is still in the text, each once. */
8
+ export declare const mentionedIds: (text: string, picked: readonly PickedUser[]) => string[];
9
+ //#endregion
@@ -1,4 +1,4 @@
1
- //#region src/app/comments/mention-text.ts
1
+ //#region src/mentions/mention-text.ts
2
2
  /** The mention the caret is in: an `@` that starts the text or follows whitespace, with no whitespace up to the caret. */
3
3
  const activeMention = (text, caret) => {
4
4
  const match = /(?:^|\s)@([^\s@]*)$/.exec(text.slice(0, caret));
@@ -0,0 +1,9 @@
1
+ //#region src/mentions/mentioned-text.d.ts
2
+ /** Text as written, line breaks kept, each `@{name}` of someone it mentions picked out; a name not in `mentions` stays plain. */
3
+ export declare const MentionedText: ({ text, mentions }: {
4
+ text: string;
5
+ mentions: readonly {
6
+ name?: string;
7
+ }[];
8
+ }) => import("react").JSX.Element;
9
+ //#endregion
@@ -0,0 +1,13 @@
1
+ import { bodyParts } from "./mention-text.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/mentions/mentioned-text.tsx
4
+ /** Text as written, line breaks kept, each `@{name}` of someone it mentions picked out; a name not in `mentions` stays plain. */
5
+ const MentionedText = ({ text, mentions }) => /* @__PURE__ */ jsx("p", {
6
+ className: "whitespace-pre-wrap break-words",
7
+ children: bodyParts(text, mentions).map((part, index) => "mention" in part ? /* @__PURE__ */ jsxs("span", {
8
+ className: "font-medium text-primary",
9
+ children: ["@", part.mention]
10
+ }, index) : part.text)
11
+ });
12
+ //#endregion
13
+ export { MentionedText };
@@ -0,0 +1,16 @@
1
+ import { defineMessages } from "../locale/messages.js";
2
+ //#region src/mentions/messages.ts
3
+ const text = defineMessages({
4
+ en: {
5
+ peopleToMention: "People to mention",
6
+ noOneFound: "No one found",
7
+ couldNotSearch: "Could not search for people"
8
+ },
9
+ nl: {
10
+ peopleToMention: "Mensen om te noemen",
11
+ noOneFound: "Niemand gevonden",
12
+ couldNotSearch: "Kon niet naar mensen zoeken"
13
+ }
14
+ });
15
+ //#endregion
16
+ export { text };
@@ -0,0 +1,26 @@
1
+ import { cn } from "../lib/cn.js";
2
+ import { text } from "./messages.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/mentions/people-list.tsx
5
+ /** The people a mention box offers for the `@` being typed; a press keeps the focus in the box. */
6
+ const PeopleList = ({ id, optionId, people, active, onActive, onPick, failed, at }) => /* @__PURE__ */ jsxs("ul", {
7
+ id,
8
+ role: "listbox",
9
+ "aria-label": text("peopleToMention"),
10
+ className: cn("absolute z-30 w-full max-w-sm rounded-md border bg-background py-1 shadow-pop", at === "above" ? "bottom-full mb-1" : "mt-1"),
11
+ onMouseDown: (event) => event.preventDefault(),
12
+ children: [people.map((user, index) => /* @__PURE__ */ jsx("li", {
13
+ id: optionId(index),
14
+ role: "option",
15
+ "aria-selected": index === active,
16
+ className: cn("cursor-pointer truncate px-3 py-1.5", index === active && "bg-muted"),
17
+ onMouseMove: () => index !== active && onActive(index),
18
+ onClick: () => onPick(user),
19
+ children: user.name
20
+ }, user.id)), people.length === 0 && /* @__PURE__ */ jsx("li", {
21
+ className: "px-3 py-1.5 text-muted-foreground",
22
+ children: failed ? text("couldNotSearch") : text("noOneFound")
23
+ })]
24
+ });
25
+ //#endregion
26
+ export { PeopleList };
@@ -0,0 +1,5 @@
1
+ import { PickedUser } from "./mention-text.js";
2
+ //#region src/mentions/use-people-search.d.ts
3
+ /** Finds the people whose name matches the text typed after `@`, such as the members of a channel. */
4
+ export type PeopleSearch = (query: string) => Promise<readonly PickedUser[]>;
5
+ //#endregion
@@ -0,0 +1,26 @@
1
+ import { useDebounced } from "../lib/use-debounced.js";
2
+ import { useEffect, useRef, useState } from "react";
3
+ //#region src/mentions/use-people-search.ts
4
+ /**
5
+ * The people `search` finds for `query`, asked 150 ms after it last changed; `undefined` until the first answer and
6
+ * while `query` is `undefined`. The last answer stays up while the next one is on its way, so the list does not flicker
7
+ * as a name is typed, and an answer to a query since changed is dropped.
8
+ */
9
+ const usePeopleSearch = (search, query) => {
10
+ const settled = useDebounced(query, 150);
11
+ const [found, setFound] = useState();
12
+ const latest = useRef(search);
13
+ latest.current = search;
14
+ useEffect(() => {
15
+ const ask = latest.current;
16
+ if (settled === void 0 || !ask) return;
17
+ let current = true;
18
+ ask(settled).then((people) => current && setFound({ people }), () => current && setFound({ failed: true }));
19
+ return () => {
20
+ current = false;
21
+ };
22
+ }, [settled]);
23
+ return query === void 0 ? void 0 : found;
24
+ };
25
+ //#endregion
26
+ export { usePeopleSearch };
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.53.4";
2
+ var version = "0.53.6";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };