@devalok/shilp-sutra 0.43.0 → 0.44.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/MIGRATION.md +46 -0
- package/dist/_chunks/chat.js +163 -162
- package/dist/_chunks/chat.js.map +1 -1
- package/dist/_chunks/mention-suggestion.js +34 -32
- package/dist/_chunks/mention-suggestion.js.map +1 -1
- package/dist/_chunks/oauth-button.js +102 -97
- package/dist/_chunks/oauth-button.js.map +1 -1
- package/dist/_chunks/tree-view.js +1 -1
- package/dist/_chunks/tree-view.js.map +1 -1
- package/dist/ai/command-bar.js.map +1 -1
- package/dist/composed/command-palette.js +3 -3
- package/dist/composed/command-palette.js.map +1 -1
- package/dist/composed/content-card.d.ts +6 -0
- package/dist/composed/content-card.d.ts.map +1 -1
- package/dist/composed/content-card.js.map +1 -1
- package/dist/composed/extensions/file-attachment.d.ts.map +1 -1
- package/dist/composed/file-preview/audio-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.d.ts.map +1 -1
- package/dist/composed/file-preview.js +146 -142
- package/dist/composed/file-preview.js.map +1 -1
- package/dist/composed/filter-bar.js +5 -2
- package/dist/composed/filter-bar.js.map +1 -1
- package/dist/composed/master-detail.js +1 -1
- package/dist/composed/master-detail.js.map +1 -1
- package/dist/composed/page-header.js +1 -1
- package/dist/composed/page-header.js.map +1 -1
- package/dist/composed/rich-chat-input/attachment-strip.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/audio-player.d.ts.map +1 -1
- package/dist/composed/rich-chat-input/schedule-send.d.ts.map +1 -1
- package/dist/composed/rich-chat-input.js +595 -586
- package/dist/composed/rich-chat-input.js.map +1 -1
- package/dist/composed/rich-text-editor.d.ts.map +1 -1
- package/dist/composed/rich-text-editor.js +164 -162
- package/dist/composed/rich-text-editor.js.map +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/shell/notification-center.d.ts.map +1 -1
- package/dist/shell/notification-center.js +101 -97
- package/dist/shell/notification-center.js.map +1 -1
- package/dist/shell/sidebar.d.ts.map +1 -1
- package/dist/shell/sidebar.js +137 -122
- package/dist/shell/sidebar.js.map +1 -1
- package/dist/shell/top-bar.d.ts.map +1 -1
- package/dist/shell/top-bar.js +86 -83
- package/dist/shell/top-bar.js.map +1 -1
- package/dist/tokens/semantic.css +1 -0
- package/dist/ui/avatar.js +1 -1
- package/dist/ui/avatar.js.map +1 -1
- package/dist/ui/breadcrumb.js +2 -2
- package/dist/ui/breadcrumb.js.map +1 -1
- package/dist/ui/button.js.map +1 -1
- package/dist/ui/card.d.ts +82 -7
- package/dist/ui/card.d.ts.map +1 -1
- package/dist/ui/card.js +74 -108
- package/dist/ui/card.js.map +1 -1
- package/dist/ui/chat/message.d.ts.map +1 -1
- package/dist/ui/combobox.js +2 -2
- package/dist/ui/combobox.js.map +1 -1
- package/dist/ui/dropdown-menu.js +9 -3
- package/dist/ui/dropdown-menu.js.map +1 -1
- package/dist/ui/index.d.ts +2 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +58 -57
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/menubar.js +9 -3
- package/dist/ui/menubar.js.map +1 -1
- package/dist/ui/oauth-button/oauth-button.d.ts.map +1 -1
- package/dist/ui/select.js +5 -2
- package/dist/ui/select.js.map +1 -1
- package/dist/ui/sidebar.js +73 -73
- package/dist/ui/sidebar.js.map +1 -1
- package/dist/ui/stat-card.d.ts +8 -5
- package/dist/ui/stat-card.d.ts.map +1 -1
- package/dist/ui/stat-card.js +168 -150
- package/dist/ui/stat-card.js.map +1 -1
- package/dist/ui/tabs.d.ts.map +1 -1
- package/dist/ui/tabs.js +5 -2
- package/dist/ui/tabs.js.map +1 -1
- package/dist/ui/truncated-text.d.ts +37 -0
- package/dist/ui/truncated-text.d.ts.map +1 -0
- package/dist/ui/truncated-text.js +53 -0
- package/dist/ui/truncated-text.js.map +1 -0
- package/docs/components/ui/card.md +13 -4
- package/docs/components/ui/stat-card.md +7 -2
- package/docs/components/ui/truncated-text.md +49 -0
- package/llms-full.txt +70 -7
- package/make-kit/components/card.md +14 -4
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
- package/skill/references/components-full.md +70 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../src/composed/rich-text-editor.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlignCenter,\n IconAlignLeft,\n IconAlignRight,\n IconArrowBackUp,\n IconArrowForwardUp,\n IconBlockquote,\n IconBold,\n IconCode,\n IconH2,\n IconH3,\n IconHighlight,\n IconItalic,\n IconLineDashed,\n IconLink,\n IconList,\n IconListCheck,\n IconListNumbers,\n IconMoodSmile,\n IconPaperclip,\n IconPhoto,\n IconStrikethrough,\n IconUnderline,\n} from '@tabler/icons-react'\nimport Highlight from '@tiptap/extension-highlight'\nimport Image from '@tiptap/extension-image'\nimport { ListKit } from '@tiptap/extension-list'\nimport Mention from '@tiptap/extension-mention'\nimport TextAlign from '@tiptap/extension-text-align'\nimport { Placeholder } from '@tiptap/extensions'\nimport { type Editor,EditorContent, useEditor, useEditorState } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport * as React from 'react'\n\nimport { useColorMode } from '../hooks/use-color-mode'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport type { EmojiSet } from './emoji-picker'\nimport { loadEmojiData, lookupEmoji } from './extensions/emoji-data'\nimport { EmojiNode } from './extensions/emoji-node'\nimport { createEmojiSuggestion } from './extensions/emoji-suggestion'\nimport { FileAttachment } from './extensions/file-attachment'\nimport { createSuggestionRenderer } from './extensions/mention-suggestion'\n\nconst PROSE_CLASSES = [\n 'prose prose-sm max-w-none',\n 'font-body text-ds-md leading-relaxed text-surface-fg',\n '[&_h2]:text-ds-xl [&_h2]:mb-ds-03 [&_h2]:mt-ds-05 [&_h2]:text-surface-fg',\n '[&_h3]:text-ds-base [&_h3]:font-semibold [&_h3]:mb-ds-02b [&_h3]:mt-ds-04 [&_h3]:text-surface-fg',\n '[&_p]:mb-ds-02b [&_p]:text-surface-fg-muted',\n '[&_ul]:ml-ds-05 [&_ul]:list-disc [&_ol]:ml-ds-05 [&_ol]:list-decimal',\n '[&_li]:text-surface-fg-muted',\n '[&_code]:rounded [&_code]:bg-surface-raised [&_code]:px-ds-02b [&_code]:py-ds-01 [&_code]:text-ds-md [&_code]:text-accent-11',\n '[&_pre]:rounded-surface [&_pre]:bg-surface-raised [&_pre]:p-ds-04',\n '[&_strong]:font-semibold [&_strong]:text-surface-fg',\n '[&_blockquote]:border-l-[3px] [&_blockquote]:border-accent-6 [&_blockquote]:pl-ds-04 [&_blockquote]:italic [&_blockquote]:text-surface-fg-subtle',\n '[&_mark]:rounded-xs [&_mark]:bg-warning-3 [&_mark]:px-[2px]',\n '[&_ul[data-type=\"taskList\"]]:ml-0 [&_ul[data-type=\"taskList\"]]:list-none [&_li[data-type=\"taskItem\"]]:flex [&_li[data-type=\"taskItem\"]]:items-start [&_li[data-type=\"taskItem\"]]:gap-ds-02',\n '[&_hr]:my-ds-04 [&_hr]:border-surface-border-strong',\n '[&_a]:text-accent-11 [&_a]:underline [&_a]:decoration-accent-6 hover:[&_a]:decoration-accent-11',\n '[&_img]:max-w-full [&_img]:rounded-control [&_img]:my-ds-03',\n '[&_.mention]:rounded-control-inner [&_.mention]:bg-accent-2 [&_.mention]:px-ds-02 [&_.mention]:py-[1px] [&_.mention]:font-medium [&_.mention]:text-accent-11',\n] as const\n\ninterface ToolbarButtonProps {\n onClick: () => void\n isActive?: boolean\n disabled?: boolean\n title: string\n children: React.ReactNode\n}\n\nfunction ToolbarButton({\n onClick,\n isActive = false,\n disabled = false,\n title,\n children,\n}: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n className={cn(\n 'inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:pointer-events-none disabled:opacity-action-disabled',\n isActive\n ? 'bg-surface-raised-hover text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n {children}\n </button>\n )\n}\n\nfunction LinkButton({ editor }: { editor: Editor }) {\n const [showInput, setShowInput] = React.useState(false)\n const [url, setUrl] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n const { isLink } = useEditorState({\n editor,\n selector: ({ editor: e }) => ({ isLink: e.isActive('link') }),\n })\n\n const handleToggle = () => {\n if (isLink) {\n editor.chain().focus().unsetLink().run()\n return\n }\n const previousUrl = editor.getAttributes('link').href || ''\n setUrl(previousUrl)\n setShowInput(true)\n setTimeout(() => inputRef.current?.focus(), 0)\n }\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault()\n if (url.trim()) {\n editor.chain().focus().setLink({ href: url.trim() }).run()\n }\n setShowInput(false)\n setUrl('')\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n setShowInput(false)\n setUrl('')\n editor.commands.focus()\n }\n }\n\n return (\n <div className=\"relative\">\n <ToolbarButton onClick={handleToggle} isActive={isLink} title=\"Link\">\n <Icon icon={IconLink} size=\"sm\" />\n </ToolbarButton>\n {showInput && (\n <form\n onSubmit={handleSubmit}\n aria-label=\"Edit link URL\"\n className=\"absolute left-0 top-full z-popover mt-ds-01 flex items-center gap-ds-02 rounded-control bg-surface-overlay p-ds-02 shadow-raised-hover\"\n >\n <input\n ref={inputRef}\n type=\"url\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"https://...\"\n className=\"h-ds-sm w-[240px] rounded-control-inner border border-surface-border-strong bg-surface-overlay px-ds-03 text-ds-sm text-surface-fg focus:border-accent-7 focus:outline-hidden\"\n />\n <button type=\"submit\" className=\"h-ds-sm rounded-control-inner bg-accent-9 px-ds-03 text-ds-sm text-accent-fg hover:bg-accent-10\">\n Apply\n </button>\n </form>\n )}\n </div>\n )\n}\n\nfunction ToolbarDivider() {\n return <div className=\"mx-ds-02 h-[16px] w-px bg-surface-border\" />\n}\n\nfunction Toolbar({ editor, toolbar, onImageClick, onFileClick, onEmojiClick }: {\n editor: Editor\n toolbar?: ToolbarItem[]\n onImageClick?: () => void\n onFileClick?: () => void\n onEmojiClick?: () => void\n}) {\n // v3: useEditorState subscribes to specific state slices so the toolbar\n // re-renders only when active-state or can-undo/redo actually changes.\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n isBold: e.isActive('bold'),\n isItalic: e.isActive('italic'),\n isUnderline: e.isActive('underline'),\n isStrike: e.isActive('strike'),\n isHighlight: e.isActive('highlight'),\n isH2: e.isActive('heading', { level: 2 }),\n isH3: e.isActive('heading', { level: 3 }),\n isBlockquote: e.isActive('blockquote'),\n isBulletList: e.isActive('bulletList'),\n isOrderedList: e.isActive('orderedList'),\n isTaskList: e.isActive('taskList'),\n isCodeBlock: e.isActive('codeBlock'),\n isLink: e.isActive('link'),\n isAlignLeft: e.isActive({ textAlign: 'left' }),\n isAlignCenter: e.isActive({ textAlign: 'center' }),\n isAlignRight: e.isActive({ textAlign: 'right' }),\n canUndo: e.can().undo(),\n canRedo: e.can().redo(),\n }),\n })\n\n const show = (item: ToolbarItem) => !toolbar || toolbar.includes(item)\n\n // Each group is an array of ToolbarItem names that belong to it.\n // A divider renders between two adjacent groups only if both have at least one visible item.\n const hasInline = show('bold') || show('italic') || show('underline') || show('strike') || show('highlight')\n const hasBlock = show('h2') || show('h3') || show('blockquote')\n const hasList = show('bulletList') || show('orderedList') || show('taskList') || show('codeBlock')\n const hasMedia = show('link') || (!!onImageClick && show('image')) || (!!onFileClick && show('file')) || show('hr')\n const hasAlign = show('alignLeft') || show('alignCenter') || show('alignRight')\n const hasEmojiGroup = !!onEmojiClick && show('emoji')\n const hasHistory = show('undo') || show('redo')\n\n return (\n <div role=\"toolbar\" aria-label=\"Text formatting\" className=\"flex flex-wrap items-center gap-ds-01 border-b border-surface-border-strong px-ds-04 py-ds-02b\">\n {/* Inline formatting */}\n {show('bold') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBold().run()} isActive={state.isBold} title=\"Bold\">\n <Icon icon={IconBold} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('italic') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleItalic().run()} isActive={state.isItalic} title=\"Italic\">\n <Icon icon={IconItalic} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('underline') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleUnderline().run()} isActive={state.isUnderline} title=\"Underline\">\n <Icon icon={IconUnderline} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('strike') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleStrike().run()} isActive={state.isStrike} title=\"Strikethrough\">\n <Icon icon={IconStrikethrough} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('highlight') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHighlight().run()} isActive={state.isHighlight} title=\"Highlight\">\n <Icon icon={IconHighlight} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasInline && hasBlock && <ToolbarDivider />}\n\n {/* Block formatting */}\n {show('h2') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} isActive={state.isH2} title=\"Heading 2\">\n <Icon icon={IconH2} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('h3') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} isActive={state.isH3} title=\"Heading 3\">\n <Icon icon={IconH3} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('blockquote') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBlockquote().run()} isActive={state.isBlockquote} title=\"Blockquote\">\n <Icon icon={IconBlockquote} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasBlock && hasList && <ToolbarDivider />}\n\n {/* Lists */}\n {show('bulletList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBulletList().run()} isActive={state.isBulletList} title=\"Bullet list\">\n <Icon icon={IconList} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('orderedList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleOrderedList().run()} isActive={state.isOrderedList} title=\"Ordered list\">\n <Icon icon={IconListNumbers} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('taskList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleTaskList().run()} isActive={state.isTaskList} title=\"Task list\">\n <Icon icon={IconListCheck} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('codeBlock') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleCodeBlock().run()} isActive={state.isCodeBlock} title=\"Code block\">\n <Icon icon={IconCode} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasList && hasMedia && <ToolbarDivider />}\n\n {/* Media & Links */}\n {show('link') && <LinkButton editor={editor} />}\n {onImageClick && show('image') && (\n <ToolbarButton onClick={onImageClick} title=\"Insert image\">\n <Icon icon={IconPhoto} size=\"sm\" />\n </ToolbarButton>\n )}\n {onFileClick && show('file') && (\n <ToolbarButton onClick={onFileClick} title=\"Attach file\">\n <Icon icon={IconPaperclip} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('hr') && (\n <ToolbarButton onClick={() => editor.chain().focus().setHorizontalRule().run()} title=\"Horizontal rule\">\n <Icon icon={IconLineDashed} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasMedia && hasAlign && <ToolbarDivider />}\n\n {/* Alignment */}\n {show('alignLeft') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('left').run()} isActive={state.isAlignLeft} title=\"Align left\">\n <Icon icon={IconAlignLeft} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignCenter') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('center').run()} isActive={state.isAlignCenter} title=\"Align center\">\n <Icon icon={IconAlignCenter} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignRight') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('right').run()} isActive={state.isAlignRight} title=\"Align right\">\n <Icon icon={IconAlignRight} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasAlign && (hasEmojiGroup || hasHistory) && <ToolbarDivider />}\n\n {/* Emoji */}\n {onEmojiClick && show('emoji') && (\n <ToolbarButton onClick={onEmojiClick} title=\"Emoji\">\n <Icon icon={IconMoodSmile} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {/* History */}\n {show('undo') && (\n <ToolbarButton onClick={() => editor.chain().focus().undo().run()} disabled={!state.canUndo} title=\"Undo\">\n <Icon icon={IconArrowBackUp} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('redo') && (\n <ToolbarButton onClick={() => editor.chain().focus().redo().run()} disabled={!state.canRedo} title=\"Redo\">\n <Icon icon={IconArrowForwardUp} size=\"sm\" />\n </ToolbarButton>\n )}\n </div>\n )\n}\n\nconst LazyPicker = React.lazy(() => import('@emoji-mart/react'))\n\nfunction EmojiPickerLazy({ set = 'native', onSelect }: { set?: string; onSelect: (emoji: { id: string; native: string }) => void }) {\n const [data, setData] = React.useState<unknown>(null)\n const { colorMode } = useColorMode()\n\n React.useEffect(() => {\n loadEmojiData(set).then((d) => setData(d))\n }, [set])\n\n const fallback = <div className=\"flex h-[350px] w-[352px] items-center justify-center rounded-surface bg-surface-overlay shadow-raised-hover\"><span className=\"text-ds-sm text-surface-fg-subtle\">Loading...</span></div>\n\n if (!data) return fallback\n\n return (\n <React.Suspense fallback={fallback}>\n <LazyPicker\n data={data}\n set={set}\n onEmojiSelect={(emoji: { native: string; id: string }) => onSelect({ id: emoji.id, native: emoji.native })}\n theme={colorMode === 'dark' ? 'dark' : 'light'}\n previewPosition=\"none\"\n skinTonePosition=\"none\"\n />\n </React.Suspense>\n )\n}\n\nexport type ToolbarItem =\n | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight'\n | 'h2' | 'h3' | 'blockquote'\n | 'bulletList' | 'orderedList' | 'taskList' | 'codeBlock'\n | 'link' | 'image' | 'file' | 'hr'\n | 'alignLeft' | 'alignCenter' | 'alignRight'\n | 'emoji' | 'undo' | 'redo'\n\nexport interface MentionItem {\n id: string\n label: string\n avatar?: string\n}\n\n/**\n * A Tiptap-powered rich text editor with a configurable toolbar, @mentions,\n * emoji suggestions, file/image uploads, and task lists.\n * Outputs sanitized HTML via `onChange`.\n */\nexport interface RichTextEditorProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange' | 'content'> {\n /** Initial HTML content. Updates are NOT reactive — use `onChange` for controlled state. */\n content?: string\n placeholder?: string\n /** Called with the editor's HTML string whenever content changes. */\n onChange?: (html: string) => void\n className?: string\n /** Set to false to render in read-only mode (e.g. for previewing). */\n editable?: boolean\n /** Called when an image is pasted/dropped. Return a URL. If not provided, images inline as base64. */\n onImageUpload?: (file: File) => Promise<string>\n /** Called when a non-image file is dropped/pasted. If not provided, non-image files are ignored. */\n onFileUpload?: (file: File) => Promise<{ url: string; name: string; size: number }>\n /** Whitelist of toolbar items to display. Omit to show all. */\n toolbar?: ToolbarItem[]\n /** Static list of mentionable items (shown when user types @). */\n mentions?: MentionItem[]\n /** Async mention search. Takes precedence over static `mentions` list. */\n onMentionSearch?: (query: string) => Promise<MentionItem[]>\n /** Called when a mention is selected from the suggestion dropdown. */\n onMentionSelect?: (item: MentionItem) => void\n /** Emoji art style. @default 'native' */\n emojiSet?: EmojiSet\n}\n\nconst RichTextEditor = React.forwardRef<HTMLDivElement, RichTextEditorProps>(\n function RichTextEditor({\n content = '',\n placeholder = 'Start writing...',\n onChange,\n className,\n editable = true,\n toolbar,\n onImageUpload,\n onFileUpload,\n mentions,\n onMentionSearch,\n onMentionSelect,\n emojiSet = 'native',\n ...props\n}, ref) {\n const editorRef = React.useRef<ReturnType<typeof useEditor>>(null)\n const isInternalChangeRef = React.useRef(false)\n const [showEmojiPicker, setShowEmojiPicker] = React.useState(false)\n const emojiPickerRef = React.useRef<HTMLDivElement>(null)\n const imageInputRef = React.useRef<HTMLInputElement>(null)\n const fileInputRef = React.useRef<HTMLInputElement>(null)\n\n const handleImageInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed) return\n if (onImageUpload) {\n const url = await onImageUpload(file)\n if (url && /^https?:\\/\\//i.test(url)) {\n ed.chain().focus().setImage({ src: url }).run()\n }\n } else {\n const reader = new FileReader()\n reader.onload = () => {\n ed.chain().focus().setImage({ src: reader.result as string }).run()\n }\n reader.readAsDataURL(file)\n }\n }\n\n const handleFileInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed || !onFileUpload) return\n const result = await onFileUpload(file)\n ed.chain().focus().insertContent({\n type: 'fileAttachment',\n attrs: { url: result.url, name: result.name, size: result.size },\n }).run()\n }\n\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n // v3: Underline + Link are now included in StarterKit\n link: {\n openOnClick: false,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Placeholder.configure({\n placeholder,\n emptyEditorClass:\n 'before:content-[attr(data-placeholder)] before:text-surface-fg-subtle before:float-left before:h-0 before:pointer-events-none',\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false, // Already in StarterKit\n orderedList: false, // Already in StarterKit\n listItem: false, // Already in StarterKit\n listKeymap: false, // Already in StarterKit\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n ...(mentions || onMentionSearch ? [\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n suggestion: {\n items: async ({ query }: { query: string }) => {\n if (onMentionSearch) return await onMentionSearch(query)\n if (mentions) return mentions.filter(m => m.label.toLowerCase().includes(query.toLowerCase())).slice(0, 8)\n return []\n },\n render: createSuggestionRenderer(onMentionSelect),\n },\n }),\n ] : []),\n EmojiNode,\n createEmojiSuggestion(emojiSet),\n ],\n content,\n editable,\n editorProps: {\n handleDrop: (_view, event, _slice, moved) => {\n if (moved || !event.dataTransfer?.files.length) return false\n const file = event.dataTransfer.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n handlePaste: (_view, event) => {\n const file = event.clipboardData?.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n attributes: {\n class: cn(\n ...PROSE_CLASSES,\n 'focus:outline-hidden',\n 'min-h-[120px] px-ds-04 py-ds-04',\n ),\n },\n },\n onUpdate: ({ editor: ed }) => {\n isInternalChangeRef.current = true\n onChange?.(ed.getHTML())\n queueMicrotask(() => {\n isInternalChangeRef.current = false\n })\n },\n })\n\n React.useEffect(() => {\n editorRef.current = editor\n }, [editor])\n\n // Close emoji picker on click outside\n React.useEffect(() => {\n if (!showEmojiPicker) return\n const handleClickOutside = (e: MouseEvent) => {\n if (emojiPickerRef.current && !emojiPickerRef.current.contains(e.target as Node)) {\n setShowEmojiPicker(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [showEmojiPicker])\n\n React.useEffect(() => {\n if (isInternalChangeRef.current) return\n if (editor && content !== editor.getHTML()) {\n editor.commands.setContent(content, { emitUpdate: false })\n }\n }, [editor, content])\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={cn('relative', className)}>\n {/* Emoji picker rendered outside the overflow-hidden box so it isn't clipped */}\n {showEmojiPicker && (\n <div\n ref={emojiPickerRef}\n className=\"absolute bottom-full right-0 z-popover mb-ds-02\"\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n setShowEmojiPicker(false)\n }\n }}\n >\n <EmojiPickerLazy\n set={emojiSet}\n onSelect={async ({ id, native }) => {\n const data = await loadEmojiData(emojiSet)\n const resolved = lookupEmoji(data, id)\n if (resolved) {\n editor.chain().focus().insertContent({\n type: 'emojiNode',\n attrs: { id, native: resolved.native, set: emojiSet, x: resolved.x, y: resolved.y },\n }).run()\n } else {\n editor.chain().focus().insertContent(native).run()\n }\n setShowEmojiPicker(false)\n }}\n />\n </div>\n )}\n <div\n className={cn(\n 'overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised',\n 'transition-colors ease-productive-standard focus-within:border-surface-border-strong',\n )}\n >\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n aria-label=\"Upload image\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleImageInsert(file)\n e.target.value = ''\n }}\n />\n {onFileUpload && (\n <input\n ref={fileInputRef}\n type=\"file\"\n aria-label=\"Upload file\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleFileInsert(file)\n e.target.value = ''\n }}\n />\n )}\n {editable && (\n <Toolbar\n editor={editor}\n toolbar={toolbar}\n onImageClick={() => imageInputRef.current?.click()}\n onFileClick={onFileUpload ? () => fileInputRef.current?.click() : undefined}\n onEmojiClick={() => setShowEmojiPicker((prev) => !prev)}\n />\n )}\n <EditorContent editor={editor} />\n </div>\n </div>\n )\n},\n)\n\nRichTextEditor.displayName = 'RichTextEditor'\n\nexport interface RichTextViewerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'content'> {\n content: string\n className?: string\n}\n\nconst RichTextViewer = React.forwardRef<HTMLDivElement, RichTextViewerProps>(\n function RichTextViewer({ content, className, ...props }, ref) {\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n link: {\n openOnClick: true,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false,\n orderedList: false,\n listItem: false,\n listKeymap: false,\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n }),\n ],\n content,\n editable: false,\n editorProps: {\n attributes: {\n class: cn(...PROSE_CLASSES),\n },\n },\n })\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={className}>\n <EditorContent editor={editor} />\n </div>\n )\n},\n)\n\nRichTextViewer.displayName = 'RichTextViewer'\n\nexport { RichTextEditor, RichTextViewer }\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rich-text-editor.js","names":[],"sources":["../../src/composed/rich-text-editor.tsx"],"sourcesContent":["'use client'\n\nimport {\n IconAlignCenter,\n IconAlignLeft,\n IconAlignRight,\n IconArrowBackUp,\n IconArrowForwardUp,\n IconBlockquote,\n IconBold,\n IconCode,\n IconH2,\n IconH3,\n IconHighlight,\n IconItalic,\n IconLineDashed,\n IconLink,\n IconList,\n IconListCheck,\n IconListNumbers,\n IconMoodSmile,\n IconPaperclip,\n IconPhoto,\n IconStrikethrough,\n IconUnderline,\n} from '@tabler/icons-react'\nimport Highlight from '@tiptap/extension-highlight'\nimport Image from '@tiptap/extension-image'\nimport { ListKit } from '@tiptap/extension-list'\nimport Mention from '@tiptap/extension-mention'\nimport TextAlign from '@tiptap/extension-text-align'\nimport { Placeholder } from '@tiptap/extensions'\nimport { type Editor,EditorContent, useEditor, useEditorState } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport * as React from 'react'\n\nimport { useColorMode } from '../hooks/use-color-mode'\nimport { Button } from '../ui/button'\nimport { Icon } from '../ui/icon'\nimport { cn } from '../ui/lib/utils'\nimport type { EmojiSet } from './emoji-picker'\nimport { loadEmojiData, lookupEmoji } from './extensions/emoji-data'\nimport { EmojiNode } from './extensions/emoji-node'\nimport { createEmojiSuggestion } from './extensions/emoji-suggestion'\nimport { FileAttachment } from './extensions/file-attachment'\nimport { createSuggestionRenderer } from './extensions/mention-suggestion'\n\nconst PROSE_CLASSES = [\n 'prose prose-sm max-w-none',\n 'font-body text-ds-md leading-relaxed text-surface-fg',\n '[&_h2]:text-ds-xl [&_h2]:mb-ds-03 [&_h2]:mt-ds-05 [&_h2]:text-surface-fg',\n '[&_h3]:text-ds-base [&_h3]:font-semibold [&_h3]:mb-ds-02b [&_h3]:mt-ds-04 [&_h3]:text-surface-fg',\n '[&_p]:mb-ds-02b [&_p]:text-surface-fg-muted',\n '[&_ul]:ml-ds-05 [&_ul]:list-disc [&_ol]:ml-ds-05 [&_ol]:list-decimal',\n '[&_li]:text-surface-fg-muted',\n '[&_code]:rounded [&_code]:bg-surface-raised [&_code]:px-ds-02b [&_code]:py-ds-01 [&_code]:text-ds-md [&_code]:text-accent-11',\n '[&_pre]:rounded-surface [&_pre]:bg-surface-raised [&_pre]:p-ds-04',\n '[&_strong]:font-semibold [&_strong]:text-surface-fg',\n '[&_blockquote]:border-l-[3px] [&_blockquote]:border-accent-6 [&_blockquote]:pl-ds-04 [&_blockquote]:italic [&_blockquote]:text-surface-fg-subtle',\n '[&_mark]:rounded-xs [&_mark]:bg-warning-3 [&_mark]:px-[2px]',\n '[&_ul[data-type=\"taskList\"]]:ml-0 [&_ul[data-type=\"taskList\"]]:list-none [&_li[data-type=\"taskItem\"]]:flex [&_li[data-type=\"taskItem\"]]:items-start [&_li[data-type=\"taskItem\"]]:gap-ds-02',\n '[&_hr]:my-ds-04 [&_hr]:border-surface-border-strong',\n '[&_a]:text-accent-11 [&_a]:underline [&_a]:decoration-accent-6 hover:[&_a]:decoration-accent-11',\n '[&_img]:max-w-full [&_img]:rounded-control [&_img]:my-ds-03',\n '[&_.mention]:rounded-control-inner [&_.mention]:bg-accent-2 [&_.mention]:px-ds-02 [&_.mention]:py-[1px] [&_.mention]:font-medium [&_.mention]:text-accent-11',\n] as const\n\ninterface ToolbarButtonProps {\n onClick: () => void\n isActive?: boolean\n disabled?: boolean\n title: string\n children: React.ReactNode\n}\n\nfunction ToolbarButton({\n onClick,\n isActive = false,\n disabled = false,\n title,\n children,\n}: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={title}\n aria-label={title}\n aria-pressed={isActive}\n className={cn(\n 'inline-flex h-ds-xs-plus w-ds-xs-plus items-center justify-center rounded-control transition-colors duration-fast-01 ease-productive-standard',\n 'hover:bg-surface-raised-hover',\n 'disabled:pointer-events-none disabled:opacity-action-disabled',\n isActive\n ? 'bg-surface-raised-hover text-accent-11'\n : 'text-surface-fg-subtle',\n )}\n >\n {children}\n </button>\n )\n}\n\nfunction LinkButton({ editor }: { editor: Editor }) {\n const [showInput, setShowInput] = React.useState(false)\n const [url, setUrl] = React.useState('')\n const inputRef = React.useRef<HTMLInputElement>(null)\n const { isLink } = useEditorState({\n editor,\n selector: ({ editor: e }) => ({ isLink: e.isActive('link') }),\n })\n\n const handleToggle = () => {\n if (isLink) {\n editor.chain().focus().unsetLink().run()\n return\n }\n const previousUrl = editor.getAttributes('link').href || ''\n setUrl(previousUrl)\n setShowInput(true)\n setTimeout(() => inputRef.current?.focus(), 0)\n }\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault()\n if (url.trim()) {\n editor.chain().focus().setLink({ href: url.trim() }).run()\n }\n setShowInput(false)\n setUrl('')\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Escape') {\n setShowInput(false)\n setUrl('')\n editor.commands.focus()\n }\n }\n\n return (\n <div className=\"relative\">\n <ToolbarButton onClick={handleToggle} isActive={isLink} title=\"Link\">\n <Icon icon={IconLink} size=\"sm\" />\n </ToolbarButton>\n {showInput && (\n <form\n onSubmit={handleSubmit}\n aria-label=\"Edit link URL\"\n className=\"absolute left-0 top-full z-popover mt-ds-01 flex items-center gap-ds-02 rounded-control bg-surface-overlay p-ds-02 shadow-raised-hover\"\n >\n <input\n ref={inputRef}\n type=\"url\"\n value={url}\n onChange={(e) => setUrl(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"https://...\"\n className=\"h-ds-sm w-[240px] rounded-control-inner border border-surface-border-strong bg-surface-overlay px-ds-03 text-ds-sm text-surface-fg focus:border-accent-7 focus:outline-hidden\"\n />\n <Button type=\"submit\" variant=\"solid\" size=\"sm\">\n Apply\n </Button>\n </form>\n )}\n </div>\n )\n}\n\nfunction ToolbarDivider() {\n return <div className=\"mx-ds-02 h-[16px] w-px bg-surface-border\" />\n}\n\nfunction Toolbar({ editor, toolbar, onImageClick, onFileClick, onEmojiClick }: {\n editor: Editor\n toolbar?: ToolbarItem[]\n onImageClick?: () => void\n onFileClick?: () => void\n onEmojiClick?: () => void\n}) {\n // v3: useEditorState subscribes to specific state slices so the toolbar\n // re-renders only when active-state or can-undo/redo actually changes.\n const state = useEditorState({\n editor,\n selector: ({ editor: e }) => ({\n isBold: e.isActive('bold'),\n isItalic: e.isActive('italic'),\n isUnderline: e.isActive('underline'),\n isStrike: e.isActive('strike'),\n isHighlight: e.isActive('highlight'),\n isH2: e.isActive('heading', { level: 2 }),\n isH3: e.isActive('heading', { level: 3 }),\n isBlockquote: e.isActive('blockquote'),\n isBulletList: e.isActive('bulletList'),\n isOrderedList: e.isActive('orderedList'),\n isTaskList: e.isActive('taskList'),\n isCodeBlock: e.isActive('codeBlock'),\n isLink: e.isActive('link'),\n isAlignLeft: e.isActive({ textAlign: 'left' }),\n isAlignCenter: e.isActive({ textAlign: 'center' }),\n isAlignRight: e.isActive({ textAlign: 'right' }),\n canUndo: e.can().undo(),\n canRedo: e.can().redo(),\n }),\n })\n\n const show = (item: ToolbarItem) => !toolbar || toolbar.includes(item)\n\n // Each group is an array of ToolbarItem names that belong to it.\n // A divider renders between two adjacent groups only if both have at least one visible item.\n const hasInline = show('bold') || show('italic') || show('underline') || show('strike') || show('highlight')\n const hasBlock = show('h2') || show('h3') || show('blockquote')\n const hasList = show('bulletList') || show('orderedList') || show('taskList') || show('codeBlock')\n const hasMedia = show('link') || (!!onImageClick && show('image')) || (!!onFileClick && show('file')) || show('hr')\n const hasAlign = show('alignLeft') || show('alignCenter') || show('alignRight')\n const hasEmojiGroup = !!onEmojiClick && show('emoji')\n const hasHistory = show('undo') || show('redo')\n\n return (\n <div role=\"toolbar\" aria-label=\"Text formatting\" className=\"flex flex-wrap items-center gap-ds-01 border-b border-surface-border-strong px-ds-04 py-ds-02b\">\n {/* Inline formatting */}\n {show('bold') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBold().run()} isActive={state.isBold} title=\"Bold\">\n <Icon icon={IconBold} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('italic') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleItalic().run()} isActive={state.isItalic} title=\"Italic\">\n <Icon icon={IconItalic} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('underline') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleUnderline().run()} isActive={state.isUnderline} title=\"Underline\">\n <Icon icon={IconUnderline} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('strike') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleStrike().run()} isActive={state.isStrike} title=\"Strikethrough\">\n <Icon icon={IconStrikethrough} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('highlight') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHighlight().run()} isActive={state.isHighlight} title=\"Highlight\">\n <Icon icon={IconHighlight} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasInline && hasBlock && <ToolbarDivider />}\n\n {/* Block formatting */}\n {show('h2') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} isActive={state.isH2} title=\"Heading 2\">\n <Icon icon={IconH2} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('h3') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} isActive={state.isH3} title=\"Heading 3\">\n <Icon icon={IconH3} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('blockquote') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBlockquote().run()} isActive={state.isBlockquote} title=\"Blockquote\">\n <Icon icon={IconBlockquote} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasBlock && hasList && <ToolbarDivider />}\n\n {/* Lists */}\n {show('bulletList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleBulletList().run()} isActive={state.isBulletList} title=\"Bullet list\">\n <Icon icon={IconList} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('orderedList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleOrderedList().run()} isActive={state.isOrderedList} title=\"Ordered list\">\n <Icon icon={IconListNumbers} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('taskList') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleTaskList().run()} isActive={state.isTaskList} title=\"Task list\">\n <Icon icon={IconListCheck} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n {show('codeBlock') && (\n <ToolbarButton onClick={() => editor.chain().focus().toggleCodeBlock().run()} isActive={state.isCodeBlock} title=\"Code block\">\n <Icon icon={IconCode} size=\"sm\" stroke=\"bold\" />\n </ToolbarButton>\n )}\n\n {hasList && hasMedia && <ToolbarDivider />}\n\n {/* Media & Links */}\n {show('link') && <LinkButton editor={editor} />}\n {onImageClick && show('image') && (\n <ToolbarButton onClick={onImageClick} title=\"Insert image\">\n <Icon icon={IconPhoto} size=\"sm\" />\n </ToolbarButton>\n )}\n {onFileClick && show('file') && (\n <ToolbarButton onClick={onFileClick} title=\"Attach file\">\n <Icon icon={IconPaperclip} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('hr') && (\n <ToolbarButton onClick={() => editor.chain().focus().setHorizontalRule().run()} title=\"Horizontal rule\">\n <Icon icon={IconLineDashed} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasMedia && hasAlign && <ToolbarDivider />}\n\n {/* Alignment */}\n {show('alignLeft') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('left').run()} isActive={state.isAlignLeft} title=\"Align left\">\n <Icon icon={IconAlignLeft} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignCenter') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('center').run()} isActive={state.isAlignCenter} title=\"Align center\">\n <Icon icon={IconAlignCenter} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('alignRight') && (\n <ToolbarButton onClick={() => editor.chain().focus().setTextAlign('right').run()} isActive={state.isAlignRight} title=\"Align right\">\n <Icon icon={IconAlignRight} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {hasAlign && (hasEmojiGroup || hasHistory) && <ToolbarDivider />}\n\n {/* Emoji */}\n {onEmojiClick && show('emoji') && (\n <ToolbarButton onClick={onEmojiClick} title=\"Emoji\">\n <Icon icon={IconMoodSmile} size=\"sm\" />\n </ToolbarButton>\n )}\n\n {/* History */}\n {show('undo') && (\n <ToolbarButton onClick={() => editor.chain().focus().undo().run()} disabled={!state.canUndo} title=\"Undo\">\n <Icon icon={IconArrowBackUp} size=\"sm\" />\n </ToolbarButton>\n )}\n {show('redo') && (\n <ToolbarButton onClick={() => editor.chain().focus().redo().run()} disabled={!state.canRedo} title=\"Redo\">\n <Icon icon={IconArrowForwardUp} size=\"sm\" />\n </ToolbarButton>\n )}\n </div>\n )\n}\n\nconst LazyPicker = React.lazy(() => import('@emoji-mart/react'))\n\nfunction EmojiPickerLazy({ set = 'native', onSelect }: { set?: string; onSelect: (emoji: { id: string; native: string }) => void }) {\n const [data, setData] = React.useState<unknown>(null)\n const { colorMode } = useColorMode()\n\n React.useEffect(() => {\n loadEmojiData(set).then((d) => setData(d))\n }, [set])\n\n const fallback = <div className=\"flex h-[350px] w-[352px] items-center justify-center rounded-surface bg-surface-overlay shadow-raised-hover\"><span className=\"text-ds-sm text-surface-fg-subtle\">Loading...</span></div>\n\n if (!data) return fallback\n\n return (\n <React.Suspense fallback={fallback}>\n <LazyPicker\n data={data}\n set={set}\n onEmojiSelect={(emoji: { native: string; id: string }) => onSelect({ id: emoji.id, native: emoji.native })}\n theme={colorMode === 'dark' ? 'dark' : 'light'}\n previewPosition=\"none\"\n skinTonePosition=\"none\"\n />\n </React.Suspense>\n )\n}\n\nexport type ToolbarItem =\n | 'bold' | 'italic' | 'underline' | 'strike' | 'highlight'\n | 'h2' | 'h3' | 'blockquote'\n | 'bulletList' | 'orderedList' | 'taskList' | 'codeBlock'\n | 'link' | 'image' | 'file' | 'hr'\n | 'alignLeft' | 'alignCenter' | 'alignRight'\n | 'emoji' | 'undo' | 'redo'\n\nexport interface MentionItem {\n id: string\n label: string\n avatar?: string\n}\n\n/**\n * A Tiptap-powered rich text editor with a configurable toolbar, @mentions,\n * emoji suggestions, file/image uploads, and task lists.\n * Outputs sanitized HTML via `onChange`.\n */\nexport interface RichTextEditorProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'onChange' | 'content'> {\n /** Initial HTML content. Updates are NOT reactive — use `onChange` for controlled state. */\n content?: string\n placeholder?: string\n /** Called with the editor's HTML string whenever content changes. */\n onChange?: (html: string) => void\n className?: string\n /** Set to false to render in read-only mode (e.g. for previewing). */\n editable?: boolean\n /** Called when an image is pasted/dropped. Return a URL. If not provided, images inline as base64. */\n onImageUpload?: (file: File) => Promise<string>\n /** Called when a non-image file is dropped/pasted. If not provided, non-image files are ignored. */\n onFileUpload?: (file: File) => Promise<{ url: string; name: string; size: number }>\n /** Whitelist of toolbar items to display. Omit to show all. */\n toolbar?: ToolbarItem[]\n /** Static list of mentionable items (shown when user types @). */\n mentions?: MentionItem[]\n /** Async mention search. Takes precedence over static `mentions` list. */\n onMentionSearch?: (query: string) => Promise<MentionItem[]>\n /** Called when a mention is selected from the suggestion dropdown. */\n onMentionSelect?: (item: MentionItem) => void\n /** Emoji art style. @default 'native' */\n emojiSet?: EmojiSet\n}\n\nconst RichTextEditor = React.forwardRef<HTMLDivElement, RichTextEditorProps>(\n function RichTextEditor({\n content = '',\n placeholder = 'Start writing...',\n onChange,\n className,\n editable = true,\n toolbar,\n onImageUpload,\n onFileUpload,\n mentions,\n onMentionSearch,\n onMentionSelect,\n emojiSet = 'native',\n ...props\n}, ref) {\n const editorRef = React.useRef<ReturnType<typeof useEditor>>(null)\n const isInternalChangeRef = React.useRef(false)\n const [showEmojiPicker, setShowEmojiPicker] = React.useState(false)\n const emojiPickerRef = React.useRef<HTMLDivElement>(null)\n const imageInputRef = React.useRef<HTMLInputElement>(null)\n const fileInputRef = React.useRef<HTMLInputElement>(null)\n\n const handleImageInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed) return\n if (onImageUpload) {\n const url = await onImageUpload(file)\n if (url && /^https?:\\/\\//i.test(url)) {\n ed.chain().focus().setImage({ src: url }).run()\n }\n } else {\n const reader = new FileReader()\n reader.onload = () => {\n ed.chain().focus().setImage({ src: reader.result as string }).run()\n }\n reader.readAsDataURL(file)\n }\n }\n\n const handleFileInsert = async (file: File) => {\n const ed = editorRef.current\n if (!ed || !onFileUpload) return\n const result = await onFileUpload(file)\n ed.chain().focus().insertContent({\n type: 'fileAttachment',\n attrs: { url: result.url, name: result.name, size: result.size },\n }).run()\n }\n\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n // v3: Underline + Link are now included in StarterKit\n link: {\n openOnClick: false,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Placeholder.configure({\n placeholder,\n emptyEditorClass:\n 'before:content-[attr(data-placeholder)] before:text-surface-fg-subtle before:float-left before:h-0 before:pointer-events-none',\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false, // Already in StarterKit\n orderedList: false, // Already in StarterKit\n listItem: false, // Already in StarterKit\n listKeymap: false, // Already in StarterKit\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n ...(mentions || onMentionSearch ? [\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n suggestion: {\n items: async ({ query }: { query: string }) => {\n if (onMentionSearch) return await onMentionSearch(query)\n if (mentions) return mentions.filter(m => m.label.toLowerCase().includes(query.toLowerCase())).slice(0, 8)\n return []\n },\n render: createSuggestionRenderer(onMentionSelect),\n },\n }),\n ] : []),\n EmojiNode,\n createEmojiSuggestion(emojiSet),\n ],\n content,\n editable,\n editorProps: {\n handleDrop: (_view, event, _slice, moved) => {\n if (moved || !event.dataTransfer?.files.length) return false\n const file = event.dataTransfer.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n handlePaste: (_view, event) => {\n const file = event.clipboardData?.files[0]\n if (!file) return false\n if (file.type.startsWith('image/')) {\n handleImageInsert(file)\n return true\n }\n if (onFileUpload) {\n handleFileInsert(file)\n return true\n }\n return false\n },\n attributes: {\n class: cn(\n ...PROSE_CLASSES,\n 'focus:outline-hidden',\n 'min-h-[120px] px-ds-04 py-ds-04',\n ),\n },\n },\n onUpdate: ({ editor: ed }) => {\n isInternalChangeRef.current = true\n onChange?.(ed.getHTML())\n queueMicrotask(() => {\n isInternalChangeRef.current = false\n })\n },\n })\n\n React.useEffect(() => {\n editorRef.current = editor\n }, [editor])\n\n // Close emoji picker on click outside\n React.useEffect(() => {\n if (!showEmojiPicker) return\n const handleClickOutside = (e: MouseEvent) => {\n if (emojiPickerRef.current && !emojiPickerRef.current.contains(e.target as Node)) {\n setShowEmojiPicker(false)\n }\n }\n document.addEventListener('mousedown', handleClickOutside)\n return () => document.removeEventListener('mousedown', handleClickOutside)\n }, [showEmojiPicker])\n\n React.useEffect(() => {\n if (isInternalChangeRef.current) return\n if (editor && content !== editor.getHTML()) {\n editor.commands.setContent(content, { emitUpdate: false })\n }\n }, [editor, content])\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={cn('relative', className)}>\n {/* Emoji picker rendered outside the overflow-hidden box so it isn't clipped */}\n {showEmojiPicker && (\n <div\n ref={emojiPickerRef}\n className=\"absolute bottom-full right-0 z-popover mb-ds-02\"\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n e.stopPropagation()\n setShowEmojiPicker(false)\n }\n }}\n >\n <EmojiPickerLazy\n set={emojiSet}\n onSelect={async ({ id, native }) => {\n const data = await loadEmojiData(emojiSet)\n const resolved = lookupEmoji(data, id)\n if (resolved) {\n editor.chain().focus().insertContent({\n type: 'emojiNode',\n attrs: { id, native: resolved.native, set: emojiSet, x: resolved.x, y: resolved.y },\n }).run()\n } else {\n editor.chain().focus().insertContent(native).run()\n }\n setShowEmojiPicker(false)\n }}\n />\n </div>\n )}\n <div\n className={cn(\n 'overflow-hidden rounded-surface border border-surface-border-strong bg-surface-raised',\n 'transition-colors ease-productive-standard focus-within:border-surface-border-strong',\n )}\n >\n <input\n ref={imageInputRef}\n type=\"file\"\n accept=\"image/*\"\n aria-label=\"Upload image\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleImageInsert(file)\n e.target.value = ''\n }}\n />\n {onFileUpload && (\n <input\n ref={fileInputRef}\n type=\"file\"\n aria-label=\"Upload file\"\n className=\"hidden\"\n onChange={(e) => {\n const file = e.target.files?.[0]\n if (file) handleFileInsert(file)\n e.target.value = ''\n }}\n />\n )}\n {editable && (\n <Toolbar\n editor={editor}\n toolbar={toolbar}\n onImageClick={() => imageInputRef.current?.click()}\n onFileClick={onFileUpload ? () => fileInputRef.current?.click() : undefined}\n onEmojiClick={() => setShowEmojiPicker((prev) => !prev)}\n />\n )}\n <EditorContent editor={editor} />\n </div>\n </div>\n )\n},\n)\n\nRichTextEditor.displayName = 'RichTextEditor'\n\nexport interface RichTextViewerProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'content'> {\n content: string\n className?: string\n}\n\nconst RichTextViewer = React.forwardRef<HTMLDivElement, RichTextViewerProps>(\n function RichTextViewer({ content, className, ...props }, ref) {\n const editor = useEditor({\n immediatelyRender: false, // SSR-safe — prevents hydration mismatch in Next.js\n extensions: [\n StarterKit.configure({\n heading: { levels: [2, 3] },\n link: {\n openOnClick: true,\n protocols: ['http', 'https', 'mailto'],\n validate: (href: string) => /^(https?:\\/\\/|mailto:)/i.test(href),\n HTMLAttributes: {\n rel: 'noopener noreferrer',\n target: '_blank',\n },\n },\n }),\n Highlight.configure({ multicolor: false }),\n ListKit.configure({\n bulletList: false,\n orderedList: false,\n listItem: false,\n listKeymap: false,\n taskList: {},\n taskItem: { nested: true },\n }),\n TextAlign.configure({ types: ['heading', 'paragraph'] }),\n Image.configure({\n allowBase64: true,\n HTMLAttributes: {\n class: 'max-w-full rounded-control',\n },\n }),\n FileAttachment,\n Mention.configure({\n HTMLAttributes: { class: 'mention' },\n }),\n ],\n content,\n editable: false,\n editorProps: {\n attributes: {\n class: cn(...PROSE_CLASSES),\n },\n },\n })\n\n if (!editor) return null\n\n return (\n <div ref={ref} {...props} className={className}>\n <EditorContent editor={editor} />\n </div>\n )\n},\n)\n\nRichTextViewer.displayName = 'RichTextViewer'\n\nexport { RichTextEditor, RichTextViewer }\n"],"mappings":"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"}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
export { toast } from '../ui/toast';
|
|
3
|
+
export type { ToastActionOptions, ToastOptions, ToastType, ToastUndoOptions, ToastUploadOptions, UploadFile, } from '../ui/toast-types';
|
|
2
4
|
export { type ColorMode, useColorMode } from './use-color-mode';
|
|
3
5
|
export { useIsMobile } from './use-mobile';
|
|
4
|
-
export type { ToastActionOptions, ToastOptions, ToastType, ToastUndoOptions, ToastUploadOptions, UploadFile, } from '../ui/toast-types';
|
|
5
|
-
export { toast } from '../ui/toast';
|
|
6
6
|
export { useTouchDevice } from './use-touch-device';
|
|
7
7
|
export { useViewportHeight } from './use-viewport-height';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,YAAY,EACV,kBAAkB,EAClB,YAAY,EACZ,SAAS,EACT,gBAAgB,EAChB,kBAAkB,EAClB,UAAU,GACX,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAC,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-center.d.ts","sourceRoot":"","sources":["../../src/shell/notification-center.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"notification-center.d.ts","sourceRoot":"","sources":["../../src/shell/notification-center.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAgC9B,MAAM,WAAW,kBAAkB;IACjC,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;IAC1C,8EAA8E;IAC9E,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IACvC,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAClC,wEAAwE;IACxE,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAA;CAC/B;AAED,MAAM,WAAW,uBACf,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACrD,uCAAuC;IACvC,aAAa,CAAC,EAAE,YAAY,EAAE,CAAA;IAC9B,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oDAAoD;IACpD,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,iDAAiD;IACjD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,6CAA6C;IAC7C,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,oDAAoD;IACpD,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,mDAAmD;IACnD,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,+CAA+C;IAC/C,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;IAC1B,4EAA4E;IAC5E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC,iFAAiF;IACjF,oBAAoB,CAAC,EAAE,CAAC,YAAY,EAAE,YAAY,KAAK,MAAM,GAAG,IAAI,CAAA;IACpE,wEAAwE;IACxE,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,+DAA+D;IAC/D,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,gGAAgG;IAChG,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,2BAA2B;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAuKD,QAAA,MAAM,kBAAkB,mGAqPvB,CAAA;AAGD,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -4,159 +4,163 @@ import { cn as t } from "../ui/lib/utils.js";
|
|
|
4
4
|
import { Spinner as n } from "../ui/spinner.js";
|
|
5
5
|
import { Icon as r } from "../ui/icon.js";
|
|
6
6
|
import { useIsMobile as i } from "../hooks/use-mobile.js";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { Tooltip as
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
7
|
+
import { Button as a } from "../ui/button.js";
|
|
8
|
+
import { i as o, r as s, t as c } from "../_chunks/popover.js";
|
|
9
|
+
import { Tooltip as l, TooltipContent as u, TooltipTrigger as d } from "../ui/tooltip.js";
|
|
10
|
+
import { TruncatedText as f } from "../ui/truncated-text.js";
|
|
11
|
+
import { Sheet as p, SheetContent as ee, SheetTitle as m, SheetTrigger as h } from "../ui/sheet.js";
|
|
12
|
+
import { formatRelativeTime as g } from "../ui/lib/date-utils.js";
|
|
13
|
+
import * as _ from "react";
|
|
14
|
+
import { useCallback as v, useRef as y } from "react";
|
|
15
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
16
|
+
import { IconBell as C, IconChecks as w, IconInbox as T, IconX as E } from "@tabler/icons-react";
|
|
17
|
+
import { motion as D, useReducedMotion as O } from "framer-motion";
|
|
16
18
|
//#region src/shell/notification-center.tsx
|
|
17
|
-
function
|
|
19
|
+
function k(e, t) {
|
|
18
20
|
return e.getFullYear() === t.getFullYear() && e.getMonth() === t.getMonth() && e.getDate() === t.getDate();
|
|
19
21
|
}
|
|
20
|
-
function
|
|
22
|
+
function A(e) {
|
|
21
23
|
let t = new Date(e), n = /* @__PURE__ */ new Date(), r = /* @__PURE__ */ new Date();
|
|
22
|
-
return r.setDate(n.getDate() - 1),
|
|
24
|
+
return r.setDate(n.getDate() - 1), k(t, n) ? "Today" : k(t, r) ? "Yesterday" : "Earlier";
|
|
23
25
|
}
|
|
24
|
-
var
|
|
26
|
+
var j = {
|
|
25
27
|
INFO: "bg-info-9",
|
|
26
28
|
IMPORTANT: "bg-warning-9",
|
|
27
29
|
CRITICAL: "bg-error-9"
|
|
28
30
|
};
|
|
29
|
-
function
|
|
30
|
-
let
|
|
31
|
-
e.isRead || n(e.id),
|
|
31
|
+
function te({ notification: e, onRead: n, onNavigate: i, getRoute: o, onDismiss: s }) {
|
|
32
|
+
let c = o(e), l = () => {
|
|
33
|
+
e.isRead || n(e.id), c && i(c);
|
|
32
34
|
};
|
|
33
|
-
return /* @__PURE__ */
|
|
35
|
+
return /* @__PURE__ */ S("div", {
|
|
34
36
|
role: "button",
|
|
35
37
|
tabIndex: 0,
|
|
36
|
-
onClick:
|
|
38
|
+
onClick: l,
|
|
37
39
|
onKeyDown: (e) => {
|
|
38
|
-
(e.key === "Enter" || e.key === " ") && (e.preventDefault(),
|
|
40
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), l());
|
|
39
41
|
},
|
|
40
42
|
className: t("group relative flex w-full cursor-pointer items-start gap-ds-04 px-ds-05 py-ds-04 text-left transition-colors duration-fast-02 ease-productive-standard", "hover:bg-surface-raised", !e.isRead && "bg-accent-1"),
|
|
41
43
|
children: [
|
|
42
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ x("div", {
|
|
43
45
|
className: "mt-ds-02b flex shrink-0",
|
|
44
|
-
children: /* @__PURE__ */
|
|
46
|
+
children: /* @__PURE__ */ x("span", { className: t("h-[8px] w-[8px] rounded-pill transition-opacity duration-fast-02 ease-productive-standard", j[e.tier] || j.INFO, e.isRead ? "opacity-20" : "opacity-100") })
|
|
45
47
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ S("div", {
|
|
47
49
|
className: "min-w-0 flex-1",
|
|
48
50
|
children: [
|
|
49
|
-
/* @__PURE__ */
|
|
50
|
-
|
|
51
|
+
/* @__PURE__ */ x(f, {
|
|
52
|
+
as: "p",
|
|
53
|
+
className: t("text-ds-md text-surface-fg", !e.isRead && "font-semibold"),
|
|
51
54
|
children: e.title
|
|
52
55
|
}),
|
|
53
|
-
e.body && /* @__PURE__ */
|
|
56
|
+
e.body && /* @__PURE__ */ x("p", {
|
|
54
57
|
className: "mt-ds-01 line-clamp-2 text-ds-sm text-surface-fg-subtle",
|
|
55
58
|
children: e.body
|
|
56
59
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ S("div", {
|
|
58
61
|
className: "mt-ds-02 flex items-center gap-ds-03",
|
|
59
|
-
children: [/* @__PURE__ */
|
|
62
|
+
children: [/* @__PURE__ */ x("span", {
|
|
60
63
|
className: "text-ds-sm text-surface-fg-subtle",
|
|
61
|
-
children:
|
|
62
|
-
}), e.project && /* @__PURE__ */
|
|
64
|
+
children: g(e.createdAt)
|
|
65
|
+
}), e.project && /* @__PURE__ */ S(b, { children: [/* @__PURE__ */ x("span", {
|
|
63
66
|
className: "text-surface-fg-subtle",
|
|
64
67
|
children: "·"
|
|
65
|
-
}), /* @__PURE__ */
|
|
66
|
-
className: "
|
|
68
|
+
}), /* @__PURE__ */ x(f, {
|
|
69
|
+
className: "text-ds-sm text-surface-fg-subtle",
|
|
67
70
|
children: e.project.title
|
|
68
71
|
})] })]
|
|
69
72
|
}),
|
|
70
|
-
e.actions && e.actions.length > 0 && /* @__PURE__ */
|
|
73
|
+
e.actions && e.actions.length > 0 && /* @__PURE__ */ x("div", {
|
|
71
74
|
className: "mt-ds-03 flex items-center gap-ds-02",
|
|
72
|
-
children: e.actions.map((
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
75
|
+
children: e.actions.map((t) => /* @__PURE__ */ x(a, {
|
|
76
|
+
size: "sm",
|
|
77
|
+
variant: t.variant === "primary" ? "solid" : "ghost",
|
|
78
|
+
color: t.variant === "danger" ? "error" : "accent",
|
|
79
|
+
onClick: (n) => {
|
|
80
|
+
n.stopPropagation(), t.onClick(e.id);
|
|
76
81
|
},
|
|
77
82
|
onKeyDown: (e) => e.stopPropagation(),
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}, n.label))
|
|
83
|
+
children: t.label
|
|
84
|
+
}, t.label))
|
|
81
85
|
})
|
|
82
86
|
]
|
|
83
87
|
}),
|
|
84
|
-
|
|
88
|
+
s && /* @__PURE__ */ x("button", {
|
|
85
89
|
type: "button",
|
|
86
90
|
"aria-label": `Dismiss notification: ${e.title}`,
|
|
87
91
|
onClick: (t) => {
|
|
88
|
-
t.stopPropagation(),
|
|
92
|
+
t.stopPropagation(), s(e.id);
|
|
89
93
|
},
|
|
90
94
|
onKeyDown: (e) => e.stopPropagation(),
|
|
91
95
|
className: "absolute right-ds-03 top-ds-03 hidden rounded-control-inner p-ds-01 text-surface-fg-subtle transition-[color,background-color,opacity] duration-fast-01 ease-productive-standard hover:bg-surface-raised-hover hover:text-surface-fg-muted group-hover:flex group-focus-within:flex",
|
|
92
|
-
children: /* @__PURE__ */
|
|
93
|
-
icon:
|
|
96
|
+
children: /* @__PURE__ */ x(r, {
|
|
97
|
+
icon: E,
|
|
94
98
|
size: "sm"
|
|
95
99
|
})
|
|
96
100
|
})
|
|
97
101
|
]
|
|
98
102
|
});
|
|
99
103
|
}
|
|
100
|
-
var
|
|
101
|
-
let W =
|
|
102
|
-
|
|
103
|
-
}, [P,
|
|
104
|
+
var M = _.forwardRef(({ notifications: a = [], unreadCount: f, open: g, onOpenChange: _, isLoading: E = !1, hasMore: k = !1, onFetchMore: j, onMarkRead: M, onMarkAllRead: N, onNavigate: P, getNotificationRoute: F, onDismiss: I, headerActions: L, emptyState: R, footerSlot: z, popoverClassName: B, className: V, ...H }, U) => {
|
|
105
|
+
let W = y(null), G = O(), K = i(), q = f ?? a.filter((e) => !e.isRead).length, J = F ?? (() => null), Y = v((e) => {
|
|
106
|
+
_?.(!1), P?.(e);
|
|
107
|
+
}, [P, _]), X = v((e) => {
|
|
104
108
|
M?.(e);
|
|
105
|
-
}, [M]), Z =
|
|
109
|
+
}, [M]), Z = v(() => {
|
|
106
110
|
let e = W.current;
|
|
107
|
-
!e ||
|
|
111
|
+
!e || E || !k || e.scrollTop + e.clientHeight >= e.scrollHeight - 50 && j?.();
|
|
108
112
|
}, [
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
E,
|
|
114
|
+
k,
|
|
111
115
|
j
|
|
112
|
-
]), ne =
|
|
113
|
-
let n =
|
|
116
|
+
]), ne = a.reduce((e, t) => {
|
|
117
|
+
let n = A(t.createdAt);
|
|
114
118
|
return e[n] || (e[n] = []), e[n].push(t), e;
|
|
115
|
-
}, {}), Q = /* @__PURE__ */
|
|
116
|
-
/* @__PURE__ */
|
|
119
|
+
}, {}), Q = /* @__PURE__ */ S(b, { children: [
|
|
120
|
+
/* @__PURE__ */ S("div", {
|
|
117
121
|
className: "flex items-center justify-between border-b border-surface-border-strong px-ds-05 py-ds-04",
|
|
118
|
-
children: [/* @__PURE__ */
|
|
122
|
+
children: [/* @__PURE__ */ S("div", {
|
|
119
123
|
className: "flex items-center gap-ds-03",
|
|
120
|
-
children: [!K && /* @__PURE__ */
|
|
124
|
+
children: [!K && /* @__PURE__ */ x("h3", {
|
|
121
125
|
className: "text-ds-md font-semibold text-surface-fg",
|
|
122
126
|
children: "Notifications"
|
|
123
|
-
}), q > 0 && /* @__PURE__ */
|
|
127
|
+
}), q > 0 && /* @__PURE__ */ x("span", {
|
|
124
128
|
className: "flex h-5 min-w-5 items-center justify-center rounded-pill bg-accent-2 px-ds-02b text-ds-sm font-semibold text-accent-11",
|
|
125
129
|
children: q
|
|
126
130
|
})]
|
|
127
|
-
}), /* @__PURE__ */
|
|
131
|
+
}), /* @__PURE__ */ S("div", {
|
|
128
132
|
className: "flex items-center gap-ds-03",
|
|
129
|
-
children: [q > 0 && N && /* @__PURE__ */
|
|
133
|
+
children: [q > 0 && N && /* @__PURE__ */ S("button", {
|
|
130
134
|
type: "button",
|
|
131
135
|
onClick: N,
|
|
132
136
|
className: "flex items-center gap-ds-02 text-ds-sm text-surface-fg-subtle transition-colors hover:text-accent-11",
|
|
133
|
-
children: [/* @__PURE__ */
|
|
134
|
-
icon:
|
|
137
|
+
children: [/* @__PURE__ */ x(r, {
|
|
138
|
+
icon: w,
|
|
135
139
|
size: "sm"
|
|
136
140
|
}), "Mark all read"]
|
|
137
141
|
}), L]
|
|
138
142
|
})]
|
|
139
143
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ S("div", {
|
|
141
145
|
ref: W,
|
|
142
146
|
onScroll: Z,
|
|
143
147
|
className: t("overflow-y-auto", K ? "max-h-[60vh]" : "max-h-[420px]"),
|
|
144
|
-
children: [
|
|
148
|
+
children: [a.length === 0 ? R || /* @__PURE__ */ S("div", {
|
|
145
149
|
className: "flex flex-col items-center justify-center px-ds-05 py-ds-09",
|
|
146
150
|
children: [
|
|
147
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ x("div", {
|
|
148
152
|
className: "flex h-ds-lg w-ds-lg items-center justify-center rounded-pill bg-surface-raised",
|
|
149
|
-
children: /* @__PURE__ */
|
|
150
|
-
icon:
|
|
153
|
+
children: /* @__PURE__ */ x(r, {
|
|
154
|
+
icon: T,
|
|
151
155
|
size: "lg",
|
|
152
156
|
className: "text-surface-fg-subtle"
|
|
153
157
|
})
|
|
154
158
|
}),
|
|
155
|
-
/* @__PURE__ */
|
|
159
|
+
/* @__PURE__ */ x("p", {
|
|
156
160
|
className: "mt-ds-04 text-ds-md text-surface-fg-subtle",
|
|
157
161
|
children: "No notifications yet"
|
|
158
162
|
}),
|
|
159
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ x("p", {
|
|
160
164
|
className: "mt-ds-02 text-ds-sm text-surface-fg-subtle",
|
|
161
165
|
children: "You're all caught up!"
|
|
162
166
|
})
|
|
@@ -167,17 +171,17 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
|
|
|
167
171
|
"Earlier"
|
|
168
172
|
].map((e) => {
|
|
169
173
|
let t = ne[e];
|
|
170
|
-
return !t || t.length === 0 ? null : /* @__PURE__ */
|
|
174
|
+
return !t || t.length === 0 ? null : /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("div", {
|
|
171
175
|
className: "sticky top-0 z-raised bg-surface-overlay px-ds-05 py-ds-02b",
|
|
172
|
-
children: /* @__PURE__ */
|
|
176
|
+
children: /* @__PURE__ */ x("span", {
|
|
173
177
|
className: "text-ds-sm font-medium text-surface-fg-subtle",
|
|
174
178
|
children: e
|
|
175
179
|
})
|
|
176
|
-
}), t.map((e, t) => /* @__PURE__ */
|
|
180
|
+
}), t.map((e, t) => /* @__PURE__ */ x(D.div, {
|
|
177
181
|
initial: G ? void 0 : { opacity: 0 },
|
|
178
182
|
animate: { opacity: 1 },
|
|
179
183
|
transition: G ? { duration: 0 } : { delay: t * .03 },
|
|
180
|
-
children: /* @__PURE__ */
|
|
184
|
+
children: /* @__PURE__ */ x(te, {
|
|
181
185
|
notification: e,
|
|
182
186
|
onRead: X,
|
|
183
187
|
onNavigate: Y,
|
|
@@ -185,24 +189,24 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
|
|
|
185
189
|
onDismiss: I
|
|
186
190
|
})
|
|
187
191
|
}, e.id))] }, e);
|
|
188
|
-
}),
|
|
192
|
+
}), E && /* @__PURE__ */ x("div", {
|
|
189
193
|
className: "flex items-center justify-center py-ds-05",
|
|
190
|
-
children: /* @__PURE__ */
|
|
194
|
+
children: /* @__PURE__ */ x(n, {})
|
|
191
195
|
})]
|
|
192
196
|
}),
|
|
193
|
-
z && /* @__PURE__ */
|
|
197
|
+
z && /* @__PURE__ */ x("div", {
|
|
194
198
|
className: "border-t border-surface-border-strong px-ds-05 py-ds-03",
|
|
195
199
|
children: z
|
|
196
200
|
})
|
|
197
|
-
] }), $ = /* @__PURE__ */
|
|
201
|
+
] }), $ = /* @__PURE__ */ S("button", {
|
|
198
202
|
...H,
|
|
199
203
|
ref: U,
|
|
200
204
|
"aria-label": q > 0 ? `Notifications, ${q} unread` : "Notifications",
|
|
201
205
|
className: t("relative flex h-ds-sm-plus w-ds-sm-plus items-center justify-center rounded-pill border border-surface-border-strong bg-surface-raised text-surface-fg-muted transition-colors ease-productive-standard hover:bg-surface-raised-hover", V),
|
|
202
|
-
children: [/* @__PURE__ */
|
|
203
|
-
icon:
|
|
206
|
+
children: [/* @__PURE__ */ x(r, {
|
|
207
|
+
icon: C,
|
|
204
208
|
size: "sm"
|
|
205
|
-
}), q > 0 && /* @__PURE__ */
|
|
209
|
+
}), q > 0 && /* @__PURE__ */ x(D.span, {
|
|
206
210
|
initial: { rotate: 0 },
|
|
207
211
|
animate: G ? void 0 : { rotate: [
|
|
208
212
|
0,
|
|
@@ -220,42 +224,42 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
|
|
|
220
224
|
children: q > 99 ? "99+" : q
|
|
221
225
|
})]
|
|
222
226
|
});
|
|
223
|
-
return K ? /* @__PURE__ */
|
|
224
|
-
open:
|
|
225
|
-
onOpenChange:
|
|
226
|
-
children: [/* @__PURE__ */
|
|
227
|
+
return K ? /* @__PURE__ */ S(p, {
|
|
228
|
+
open: g,
|
|
229
|
+
onOpenChange: _,
|
|
230
|
+
children: [/* @__PURE__ */ S(l, { children: [/* @__PURE__ */ x(d, {
|
|
227
231
|
asChild: !0,
|
|
228
|
-
children: /* @__PURE__ */
|
|
232
|
+
children: /* @__PURE__ */ x(h, {
|
|
229
233
|
asChild: !0,
|
|
230
234
|
children: $
|
|
231
235
|
})
|
|
232
|
-
}), /* @__PURE__ */
|
|
236
|
+
}), /* @__PURE__ */ x(u, {
|
|
233
237
|
side: "bottom",
|
|
234
238
|
align: "center",
|
|
235
239
|
children: "Notifications"
|
|
236
|
-
})] }), /* @__PURE__ */
|
|
240
|
+
})] }), /* @__PURE__ */ S(ee, {
|
|
237
241
|
side: "bottom",
|
|
238
242
|
className: t("p-0", B),
|
|
239
243
|
responsive: !1,
|
|
240
|
-
children: [/* @__PURE__ */
|
|
244
|
+
children: [/* @__PURE__ */ x(m, {
|
|
241
245
|
className: "px-ds-05 pt-ds-04 text-ds-md font-semibold text-surface-fg",
|
|
242
246
|
children: "Notifications"
|
|
243
247
|
}), Q]
|
|
244
248
|
})]
|
|
245
|
-
}) : /* @__PURE__ */
|
|
246
|
-
open:
|
|
247
|
-
onOpenChange:
|
|
248
|
-
children: [/* @__PURE__ */
|
|
249
|
+
}) : /* @__PURE__ */ S(c, {
|
|
250
|
+
open: g,
|
|
251
|
+
onOpenChange: _,
|
|
252
|
+
children: [/* @__PURE__ */ S(l, { children: [/* @__PURE__ */ x(d, {
|
|
249
253
|
asChild: !0,
|
|
250
|
-
children: /* @__PURE__ */
|
|
254
|
+
children: /* @__PURE__ */ x(o, {
|
|
251
255
|
asChild: !0,
|
|
252
256
|
children: $
|
|
253
257
|
})
|
|
254
|
-
}), /* @__PURE__ */
|
|
258
|
+
}), /* @__PURE__ */ x(u, {
|
|
255
259
|
side: "bottom",
|
|
256
260
|
align: "center",
|
|
257
261
|
children: "Notifications"
|
|
258
|
-
})] }), /* @__PURE__ */
|
|
262
|
+
})] }), /* @__PURE__ */ x(s, {
|
|
259
263
|
className: t("w-[380px] rounded-overlay-lg bg-surface-overlay p-0 shadow-floating", B),
|
|
260
264
|
sideOffset: 8,
|
|
261
265
|
align: "end",
|
|
@@ -263,6 +267,6 @@ var A = g.forwardRef(({ notifications: h = [], unreadCount: g, open: w, onOpenCh
|
|
|
263
267
|
})]
|
|
264
268
|
});
|
|
265
269
|
});
|
|
266
|
-
|
|
270
|
+
M.displayName = "NotificationCenter";
|
|
267
271
|
//#endregion
|
|
268
|
-
export {
|
|
272
|
+
export { M as NotificationCenter };
|