@comitor/ui 3.4.0 → 3.5.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/README.md +17 -5
- package/dist/components/ui/item.d.ts +1 -1
- package/dist/data-grid/use-data-grid.d.ts +2 -2
- package/dist/editor/bubble-toolbar.d.ts +34 -0
- package/dist/editor/bubble-toolbar.js +391 -0
- package/dist/editor/bubble-toolbar.js.map +1 -0
- package/dist/editor/image-upload.d.ts +57 -0
- package/dist/editor/image-upload.js +136 -0
- package/dist/editor/image-upload.js.map +1 -0
- package/dist/editor/index.d.ts +5 -0
- package/dist/editor/index.js +11 -0
- package/dist/editor/index.js.map +1 -0
- package/dist/editor/markdown-editor-classes.d.ts +16 -0
- package/dist/editor/markdown-editor-classes.js +117 -0
- package/dist/editor/markdown-editor-classes.js.map +1 -0
- package/dist/editor/markdown-editor-constants.d.ts +92 -0
- package/dist/editor/markdown-editor-constants.js +6 -0
- package/dist/editor/markdown-editor-constants.js.map +1 -0
- package/dist/editor/markdown-editor.d.ts +125 -0
- package/dist/editor/markdown-editor.js +325 -0
- package/dist/editor/markdown-editor.js.map +1 -0
- package/dist/editor/markdown-extensions.d.ts +71 -0
- package/dist/editor/markdown-extensions.js +172 -0
- package/dist/editor/markdown-extensions.js.map +1 -0
- package/dist/editor/markdown-syntax.d.ts +87 -0
- package/dist/editor/markdown-syntax.js +58 -0
- package/dist/editor/markdown-syntax.js.map +1 -0
- package/dist/editor/slash-commands.d.ts +62 -0
- package/dist/editor/slash-commands.js +186 -0
- package/dist/editor/slash-commands.js.map +1 -0
- package/dist/editor/suggestion-menu.d.ts +38 -0
- package/dist/editor/suggestion-menu.js +144 -0
- package/dist/editor/suggestion-menu.js.map +1 -0
- package/dist/editor/suggestion-store.d.ts +47 -0
- package/dist/editor/suggestion-store.js +49 -0
- package/dist/editor/suggestion-store.js.map +1 -0
- package/dist/i18n/locale-provider.d.ts +1 -0
- package/dist/i18n/messages.d.ts +2 -0
- package/dist/i18n/messages.js +3 -0
- package/dist/i18n/messages.js.map +1 -1
- package/dist/i18n/namespaces/markdown-editor.d.ts +6 -0
- package/dist/i18n/namespaces/markdown-editor.js +127 -0
- package/dist/i18n/namespaces/markdown-editor.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/package.json +79 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/editor/slash-commands.ts"],"sourcesContent":["/**\n * Menu `/` — danh sách khối chèn được, và extension Tiptap mở nó.\n *\n * Không `\"use client\"`: bảng dưới đây là dữ liệu (icon là component, `run` là hàm) — file thuần.\n * Chữ hiển thị KHÔNG nằm ở đây: tên khối và từ đồng nghĩa đi qua namespace `markdownEditor`; ở đây\n * chỉ có bí danh MÁY tiếng Anh (`h1`, `ul`, `todo`…), thứ người dùng gõ như nhau ở mọi ngôn ngữ.\n */\n\nimport { type Editor, Extension, type Range } from \"@tiptap/core\";\nimport { PluginKey } from \"@tiptap/pm/state\";\nimport { Suggestion, type SuggestionOptions } from \"@tiptap/suggestion\";\nimport {\n BetweenHorizontalEnd,\n BetweenVerticalEnd,\n Columns3,\n Heading1,\n Heading2,\n Heading3,\n ImagePlus,\n List,\n ListChecks,\n ListOrdered,\n type LucideIcon,\n Minus,\n Pilcrow,\n Rows3,\n SquareCode,\n Table,\n TextQuote,\n Trash2\n} from \"lucide-react\";\nimport { matchesSearch } from \"../lib/text-search.js\";\nimport type { MarkdownEditorBlockId, MarkdownEditorLabels } from \"./markdown-editor-constants.js\";\n\n/** Thứ menu cần mà chỉ component biết — vd mở hộp chọn tệp. */\nexport interface SlashCommandContext {\n /** Có = app đã truyền `onImageUpload`, khối \"Ảnh\" hiện ra. */\n pickImage?: () => void;\n}\n\nexport interface SlashCommand {\n id: MarkdownEditorBlockId;\n icon: LucideIcon;\n /**\n * Cú pháp gõ tắt, hiện mờ bên phải mục — kiểu Linear. Menu `/` vì vậy cũng là chỗ DẠY cú pháp:\n * lần sau người dùng gõ thẳng `## ` mà không cần mở menu.\n */\n hint?: string;\n /** Bí danh máy, cách nhau bằng dấu cách — luôn tìm được, ở mọi ngôn ngữ. */\n aliases: string;\n /** Mục có hiện ở vị trí con trỏ này không. Bỏ trống = hiện khi KHÔNG ở trong bảng. */\n when?: (editor: Editor, context: SlashCommandContext) => boolean;\n run: (editor: Editor, range: Range, context: SlashCommandContext) => void;\n}\n\n/** Mục hiện trong menu: lệnh + chữ đã dịch, ngữ cảnh đã buộc sẵn. */\nexport interface SlashMenuItem {\n id: MarkdownEditorBlockId;\n icon: LucideIcon;\n hint?: string;\n label: string;\n execute: (editor: Editor, range: Range) => void;\n}\n\nconst inTable = (editor: Editor) => editor.isActive(\"table\");\n\n/*\n * Thứ tự theo tần suất dùng của một mô tả công việc (văn bản → tiêu đề → danh sách → khối), không\n * theo bảng chữ cái: menu lọc theo chữ gõ, nên thứ tự chỉ có nghĩa ở lượt mở ĐẦU, khi chưa gõ gì.\n *\n * Trong bảng chỉ còn thao tác bảng: ô bảng chỉ chứa một đoạn văn (xem `ComitorTableCell`), nên mọi\n * khối khác có hiện cũng không chèn được.\n */\nexport const SLASH_COMMANDS: readonly SlashCommand[] = [\n {\n id: \"text\",\n icon: Pilcrow,\n aliases: \"p text paragraph\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).setParagraph().run()\n },\n {\n id: \"heading1\",\n icon: Heading1,\n hint: \"#\",\n aliases: \"h1 heading\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).setHeading({ level: 1 }).run()\n },\n {\n id: \"heading2\",\n icon: Heading2,\n hint: \"##\",\n aliases: \"h2 heading\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).setHeading({ level: 2 }).run()\n },\n {\n id: \"heading3\",\n icon: Heading3,\n hint: \"###\",\n aliases: \"h3 heading\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).setHeading({ level: 3 }).run()\n },\n {\n id: \"bulletList\",\n icon: List,\n hint: \"-\",\n aliases: \"ul bullet list\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).toggleBulletList().run()\n },\n {\n id: \"orderedList\",\n icon: ListOrdered,\n hint: \"1.\",\n aliases: \"ol ordered number list\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).toggleOrderedList().run()\n },\n {\n id: \"taskList\",\n icon: ListChecks,\n hint: \"[]\",\n aliases: \"todo task checklist checkbox\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).toggleTaskList().run()\n },\n {\n id: \"blockquote\",\n icon: TextQuote,\n hint: \">\",\n aliases: \"quote blockquote\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).toggleBlockquote().run()\n },\n {\n id: \"codeBlock\",\n icon: SquareCode,\n hint: \"```\",\n aliases: \"code pre codeblock\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).setCodeBlock().run()\n },\n {\n id: \"table\",\n icon: Table,\n aliases: \"table spreadsheet\",\n run: (editor, range) =>\n editor.chain().focus().deleteRange(range).insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()\n },\n {\n id: \"divider\",\n icon: Minus,\n hint: \"---\",\n aliases: \"hr divider rule separator\",\n run: (editor, range) => editor.chain().focus().deleteRange(range).setHorizontalRule().run()\n },\n {\n id: \"image\",\n icon: ImagePlus,\n aliases: \"img image picture upload\",\n when: (editor, context) => !inTable(editor) && Boolean(context.pickImage),\n run: (editor, range, context) => {\n editor.chain().focus().deleteRange(range).run();\n context.pickImage?.();\n }\n },\n {\n id: \"tableAddRow\",\n icon: BetweenHorizontalEnd,\n aliases: \"row add insert\",\n when: inTable,\n run: (editor, range) => editor.chain().focus().deleteRange(range).addRowAfter().run()\n },\n {\n id: \"tableAddColumn\",\n icon: BetweenVerticalEnd,\n aliases: \"column col add insert\",\n when: inTable,\n run: (editor, range) => editor.chain().focus().deleteRange(range).addColumnAfter().run()\n },\n {\n id: \"tableDeleteRow\",\n icon: Rows3,\n aliases: \"row delete remove\",\n when: inTable,\n run: (editor, range) => editor.chain().focus().deleteRange(range).deleteRow().run()\n },\n {\n id: \"tableDeleteColumn\",\n icon: Columns3,\n aliases: \"column col delete remove\",\n when: inTable,\n run: (editor, range) => editor.chain().focus().deleteRange(range).deleteColumn().run()\n },\n {\n id: \"tableDelete\",\n icon: Trash2,\n aliases: \"table delete remove\",\n when: inTable,\n run: (editor, range) => editor.chain().focus().deleteRange(range).deleteTable().run()\n }\n];\n\n/**\n * Các mục hiện được ở vị trí con trỏ, khớp với chữ đã gõ.\n *\n * Khớp = MỌI từ của chữ gõ nằm trong (tên đã dịch + từ đồng nghĩa đã dịch + bí danh máy) — không\n * dấu, không phân biệt hoa thường, đúng luật `matchesSearch` của `Combobox`: \"/tieu\" ra \"Tiêu đề\",\n * \"/h2\" ra \"Tiêu đề 2\" ở mọi ngôn ngữ.\n */\nexport function filterSlashCommands(\n editor: Editor,\n query: string,\n labels: MarkdownEditorLabels,\n context: SlashCommandContext\n): SlashMenuItem[] {\n const words = query.trim().split(/\\s+/).filter(Boolean);\n return SLASH_COMMANDS.filter((command) => (command.when ? command.when(editor, context) : !inTable(editor)))\n .filter((command) => {\n const haystack = `${labels[command.id]} ${labels.slashKeywords[command.id]} ${command.aliases}`;\n return words.every((word) => matchesSearch(haystack, word));\n })\n .map((command) => ({\n id: command.id,\n icon: command.icon,\n hint: command.hint,\n label: labels[command.id],\n execute: (target, range) => command.run(target, range, context)\n }));\n}\n\nexport const SLASH_PLUGIN_KEY = new PluginKey(\"comitorSlashMenu\");\n\n/**\n * Extension mở menu `/`. Phần GIAO DIỆN (danh sách, bàn phím) đến qua `render` — xem\n * `SuggestionStore`.\n *\n * `/` chỉ kích hoạt ở đầu dòng hoặc sau dấu cách (`allowedPrefixes` mặc định của Tiptap), nên gõ\n * đường dẫn `a/b` hay ngày `02/10` không bật menu. Không bật trong khối mã và mã nội dòng: ở đó `/`\n * là chữ của mã.\n */\nexport function createSlashCommandExtension(\n options: Pick<SuggestionOptions<SlashMenuItem, SlashMenuItem>, \"items\" | \"render\">\n): Extension {\n return Extension.create({\n name: \"comitorSlashMenu\",\n addProseMirrorPlugins() {\n return [\n Suggestion<SlashMenuItem, SlashMenuItem>({\n editor: this.editor,\n pluginKey: SLASH_PLUGIN_KEY,\n char: \"/\",\n allowSpaces: false,\n items: options.items,\n render: options.render,\n command: ({ editor, range, props }) => props.execute(editor, range),\n allow: ({ editor }) => editor.isEditable && !editor.isActive(\"codeBlock\") && !editor.isActive(\"code\")\n })\n ];\n }\n });\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { SuggestionStore } from './suggestion-store.js';
|
|
4
|
+
import '@tiptap/pm/state';
|
|
5
|
+
import '@tiptap/suggestion';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Danh sách gợi ý nổi của trình soạn — DÙNG CHUNG cho menu `/` (khối) và `@` (người).
|
|
9
|
+
*
|
|
10
|
+
* Con trỏ KHÔNG rời vùng soạn: người dùng vẫn đang gõ chữ lọc, nên focus ở lại ProseMirror và bàn
|
|
11
|
+
* phím đi đường plugin → `SuggestionStore.keyHandler` → đây. Vì vậy danh sách là `role="listbox"`
|
|
12
|
+
* điều khiển bằng `aria-activedescendant` đặt lên CHÍNH vùng soạn (khuôn combobox của ARIA), không
|
|
13
|
+
* phải một danh sách nhận focus.
|
|
14
|
+
*
|
|
15
|
+
* Mở bằng `Popover` của gói neo vào một phần tử ẢO (khung của đoạn `/que…` đang gõ) — lý do không
|
|
16
|
+
* gắn thẳng vào `document.body` ở đầu `suggestion-store.ts`.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface SuggestionMenuProps<Item, Selected> {
|
|
20
|
+
editor: Editor;
|
|
21
|
+
store: SuggestionStore<Item, Selected>;
|
|
22
|
+
/** Tên của listbox cho trình đọc màn hình */
|
|
23
|
+
label: string;
|
|
24
|
+
emptyLabel: string;
|
|
25
|
+
loadingLabel?: string;
|
|
26
|
+
getKey: (item: Item) => string;
|
|
27
|
+
renderItem: (item: Item) => React.ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Ẩn hẳn menu (và thoát chế độ gợi ý) khi KHÔNG có kết quả. Dùng cho `@`: tên người có dấu cách
|
|
30
|
+
* nên chữ lọc được phép chứa dấu cách — khi không còn ai khớp, người dùng đang viết câu thường
|
|
31
|
+
* chứ không còn tìm người, và một ô "Không tìm thấy" bám theo từng chữ là phiền.
|
|
32
|
+
*/
|
|
33
|
+
exitWhenEmpty?: (query: string) => boolean;
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function SuggestionMenu<Item, Selected>({ editor, store, label, emptyLabel, loadingLabel, getKey, renderItem, exitWhenEmpty, className }: SuggestionMenuProps<Item, Selected>): React.JSX.Element;
|
|
37
|
+
|
|
38
|
+
export { SuggestionMenu, type SuggestionMenuProps };
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Popover, PopoverAnchor, PopoverContent } from "../components/ui/popover.js";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { useAsRef } from "../lib/use-as-ref.js";
|
|
7
|
+
function SuggestionMenu({
|
|
8
|
+
editor,
|
|
9
|
+
store,
|
|
10
|
+
label,
|
|
11
|
+
emptyLabel,
|
|
12
|
+
loadingLabel,
|
|
13
|
+
getKey,
|
|
14
|
+
renderItem,
|
|
15
|
+
exitWhenEmpty,
|
|
16
|
+
className
|
|
17
|
+
}) {
|
|
18
|
+
const snapshot = React.useSyncExternalStore(
|
|
19
|
+
store.subscribe,
|
|
20
|
+
store.getSnapshot,
|
|
21
|
+
store.getServerSnapshot
|
|
22
|
+
);
|
|
23
|
+
const [lastSnapshot, setLastSnapshot] = React.useState(snapshot);
|
|
24
|
+
if (snapshot && snapshot !== lastSnapshot) setLastSnapshot(snapshot);
|
|
25
|
+
const shown = snapshot ?? lastSnapshot;
|
|
26
|
+
const [activeIndex, setActiveIndex] = React.useState(0);
|
|
27
|
+
const activeIndexRef = useAsRef(activeIndex);
|
|
28
|
+
const listId = React.useId();
|
|
29
|
+
const optionId = (index) => `${listId}-option-${index}`;
|
|
30
|
+
const items = shown?.items ?? [];
|
|
31
|
+
const query = shown?.query ?? "";
|
|
32
|
+
const loading = Boolean(shown?.loading);
|
|
33
|
+
const hidden = Boolean(snapshot && !loading && items.length === 0 && exitWhenEmpty?.(snapshot.query));
|
|
34
|
+
React.useEffect(() => setActiveIndex(0), [query, items.length]);
|
|
35
|
+
React.useEffect(() => {
|
|
36
|
+
if (hidden) snapshot?.exit();
|
|
37
|
+
}, [hidden, snapshot]);
|
|
38
|
+
React.useEffect(() => {
|
|
39
|
+
store.keyHandler = (event) => {
|
|
40
|
+
const current = store.getSnapshot();
|
|
41
|
+
const count = current?.items.length ?? 0;
|
|
42
|
+
if (!current || count === 0) return false;
|
|
43
|
+
if (event.key === "ArrowDown") {
|
|
44
|
+
setActiveIndex((index) => (index + 1) % count);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (event.key === "ArrowUp") {
|
|
48
|
+
setActiveIndex((index) => (index - 1 + count) % count);
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
52
|
+
const item = current.items[Math.min(activeIndexRef.current, count - 1)];
|
|
53
|
+
if (item === void 0) return false;
|
|
54
|
+
current.select(item);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
};
|
|
59
|
+
return () => {
|
|
60
|
+
store.keyHandler = null;
|
|
61
|
+
};
|
|
62
|
+
}, [store, activeIndexRef]);
|
|
63
|
+
const open = Boolean(snapshot) && !hidden;
|
|
64
|
+
const activeOption = open && items.length > 0 ? optionId(Math.min(activeIndex, items.length - 1)) : null;
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
const dom = editor.view.dom;
|
|
67
|
+
if (!open) return;
|
|
68
|
+
dom.setAttribute("aria-controls", listId);
|
|
69
|
+
if (activeOption) dom.setAttribute("aria-activedescendant", activeOption);
|
|
70
|
+
else dom.removeAttribute("aria-activedescendant");
|
|
71
|
+
return () => {
|
|
72
|
+
dom.removeAttribute("aria-controls");
|
|
73
|
+
dom.removeAttribute("aria-activedescendant");
|
|
74
|
+
};
|
|
75
|
+
}, [editor, open, listId, activeOption]);
|
|
76
|
+
React.useEffect(() => {
|
|
77
|
+
if (activeOption) document.getElementById(activeOption)?.scrollIntoView({ block: "nearest" });
|
|
78
|
+
}, [activeOption]);
|
|
79
|
+
const latest = useAsRef(shown);
|
|
80
|
+
const anchorRect = React.useRef(null);
|
|
81
|
+
const anchor = React.useRef({
|
|
82
|
+
getBoundingClientRect: () => {
|
|
83
|
+
const rect = latest.current?.clientRect();
|
|
84
|
+
if (rect) anchorRect.current = rect;
|
|
85
|
+
return anchorRect.current ?? new DOMRect();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return /* @__PURE__ */ jsxs(
|
|
89
|
+
Popover,
|
|
90
|
+
{
|
|
91
|
+
open,
|
|
92
|
+
onOpenChange: (next) => {
|
|
93
|
+
if (!next) snapshot?.exit();
|
|
94
|
+
},
|
|
95
|
+
children: [
|
|
96
|
+
/* @__PURE__ */ jsx(PopoverAnchor, { virtualRef: anchor }),
|
|
97
|
+
/* @__PURE__ */ jsx(
|
|
98
|
+
PopoverContent,
|
|
99
|
+
{
|
|
100
|
+
align: "start",
|
|
101
|
+
side: "bottom",
|
|
102
|
+
sideOffset: 6,
|
|
103
|
+
updatePositionStrategy: "always",
|
|
104
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
105
|
+
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
106
|
+
className: cn("max-h-80 w-72 overflow-y-auto p-1", className),
|
|
107
|
+
children: /* @__PURE__ */ jsx("div", { role: "listbox", id: listId, "aria-label": label, children: items.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-2 py-6 text-center text-sm text-muted-foreground", children: loading && loadingLabel ? loadingLabel : emptyLabel }) : items.map((item, index) => {
|
|
108
|
+
const active = index === activeIndex;
|
|
109
|
+
return (
|
|
110
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: bàn phím đi qua vùng soạn (aria-activedescendant), xem đầu file
|
|
111
|
+
/* @__PURE__ */ jsx(
|
|
112
|
+
"div",
|
|
113
|
+
{
|
|
114
|
+
id: optionId(index),
|
|
115
|
+
role: "option",
|
|
116
|
+
tabIndex: -1,
|
|
117
|
+
"aria-selected": active,
|
|
118
|
+
"data-selected": active || void 0,
|
|
119
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
120
|
+
onPointerMove: () => setActiveIndex(index),
|
|
121
|
+
onClick: () => snapshot?.select(item),
|
|
122
|
+
className: cn(
|
|
123
|
+
// Bộ lớp của `CommandItem`: mục sáng `bg-accent`, đệm theo trục mật độ, icon mặc định 16px.
|
|
124
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none",
|
|
125
|
+
"mt-1 first:mt-0 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground",
|
|
126
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
127
|
+
"[&_svg:not([class*='text-'])]:text-muted-foreground"
|
|
128
|
+
),
|
|
129
|
+
children: renderItem(item)
|
|
130
|
+
},
|
|
131
|
+
getKey(item)
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
}) })
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
SuggestionMenu
|
|
143
|
+
};
|
|
144
|
+
//# sourceMappingURL=suggestion-menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/editor/suggestion-menu.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * Danh sách gợi ý nổi của trình soạn — DÙNG CHUNG cho menu `/` (khối) và `@` (người).\n *\n * Con trỏ KHÔNG rời vùng soạn: người dùng vẫn đang gõ chữ lọc, nên focus ở lại ProseMirror và bàn\n * phím đi đường plugin → `SuggestionStore.keyHandler` → đây. Vì vậy danh sách là `role=\"listbox\"`\n * điều khiển bằng `aria-activedescendant` đặt lên CHÍNH vùng soạn (khuôn combobox của ARIA), không\n * phải một danh sách nhận focus.\n *\n * Mở bằng `Popover` của gói neo vào một phần tử ẢO (khung của đoạn `/que…` đang gõ) — lý do không\n * gắn thẳng vào `document.body` ở đầu `suggestion-store.ts`.\n */\n\nimport type { Editor } from \"@tiptap/core\";\nimport * as React from \"react\";\nimport { Popover, PopoverAnchor, PopoverContent } from \"../components/ui/popover.js\";\nimport { cn } from \"../lib/cn.js\";\nimport { useAsRef } from \"../lib/use-as-ref.js\";\nimport type { SuggestionSnapshot, SuggestionStore } from \"./suggestion-store.js\";\n\nexport interface SuggestionMenuProps<Item, Selected> {\n editor: Editor;\n store: SuggestionStore<Item, Selected>;\n /** Tên của listbox cho trình đọc màn hình */\n label: string;\n emptyLabel: string;\n loadingLabel?: string;\n getKey: (item: Item) => string;\n renderItem: (item: Item) => React.ReactNode;\n /**\n * Ẩn hẳn menu (và thoát chế độ gợi ý) khi KHÔNG có kết quả. Dùng cho `@`: tên người có dấu cách\n * nên chữ lọc được phép chứa dấu cách — khi không còn ai khớp, người dùng đang viết câu thường\n * chứ không còn tìm người, và một ô \"Không tìm thấy\" bám theo từng chữ là phiền.\n */\n exitWhenEmpty?: (query: string) => boolean;\n className?: string;\n}\n\nexport function SuggestionMenu<Item, Selected>({\n editor,\n store,\n label,\n emptyLabel,\n loadingLabel,\n getKey,\n renderItem,\n exitWhenEmpty,\n className\n}: SuggestionMenuProps<Item, Selected>) {\n const snapshot: SuggestionSnapshot<Item> | null = React.useSyncExternalStore(\n store.subscribe,\n store.getSnapshot,\n store.getServerSnapshot\n );\n\n /*\n * Giữ ảnh chụp CUỐI để vẽ trong lúc `Popover` chạy hiệu ứng đóng: plugin xoá trạng thái ngay khi\n * thoát, mà nội dung biến mất trước khung thì thấy một ô rỗng co lại.\n */\n const [lastSnapshot, setLastSnapshot] = React.useState(snapshot);\n if (snapshot && snapshot !== lastSnapshot) setLastSnapshot(snapshot);\n const shown = snapshot ?? lastSnapshot;\n\n const [activeIndex, setActiveIndex] = React.useState(0);\n // Bản ĐỌC của chỉ số cho bộ xử lý phím: nó sống trong một effect, nên state trong closure cũ một nhịp.\n const activeIndexRef = useAsRef(activeIndex);\n const listId = React.useId();\n const optionId = (index: number) => `${listId}-option-${index}`;\n\n const items = shown?.items ?? [];\n const query = shown?.query ?? \"\";\n const loading = Boolean(shown?.loading);\n const hidden = Boolean(snapshot && !loading && items.length === 0 && exitWhenEmpty?.(snapshot.query));\n\n // Danh sách đổi → về mục đầu. Theo CHỮ LỌC chứ không theo mảng (mảng mới mỗi lần plugin cập nhật).\n // biome-ignore lint/correctness/useExhaustiveDependencies: chủ đích chạy lại theo chữ lọc và số mục\n React.useEffect(() => setActiveIndex(0), [query, items.length]);\n\n React.useEffect(() => {\n if (hidden) snapshot?.exit();\n }, [hidden, snapshot]);\n\n // Bàn phím: plugin giữ focus ở vùng soạn và chuyển phím vào đây.\n React.useEffect(() => {\n store.keyHandler = (event) => {\n const current = store.getSnapshot();\n const count = current?.items.length ?? 0;\n // Không có mục nào: trả phím cho vùng soạn — Enter xuống dòng như thường.\n if (!current || count === 0) return false;\n if (event.key === \"ArrowDown\") {\n setActiveIndex((index) => (index + 1) % count);\n return true;\n }\n if (event.key === \"ArrowUp\") {\n setActiveIndex((index) => (index - 1 + count) % count);\n return true;\n }\n if (event.key === \"Enter\" || event.key === \"Tab\") {\n const item = current.items[Math.min(activeIndexRef.current, count - 1)];\n if (item === undefined) return false;\n current.select(item);\n return true;\n }\n return false;\n };\n return () => {\n store.keyHandler = null;\n };\n }, [store, activeIndexRef]);\n\n // Quan hệ combobox trên chính vùng soạn — xem đầu file. Gỡ sạch khi menu đóng.\n const open = Boolean(snapshot) && !hidden;\n const activeOption = open && items.length > 0 ? optionId(Math.min(activeIndex, items.length - 1)) : null;\n React.useEffect(() => {\n const dom = editor.view.dom;\n if (!open) return;\n dom.setAttribute(\"aria-controls\", listId);\n if (activeOption) dom.setAttribute(\"aria-activedescendant\", activeOption);\n else dom.removeAttribute(\"aria-activedescendant\");\n return () => {\n dom.removeAttribute(\"aria-controls\");\n dom.removeAttribute(\"aria-activedescendant\");\n };\n }, [editor, open, listId, activeOption]);\n\n // Mục đang sáng luôn trong tầm nhìn khi đi bằng phím.\n React.useEffect(() => {\n if (activeOption) document.getElementById(activeOption)?.scrollIntoView({ block: \"nearest\" });\n }, [activeOption]);\n\n /*\n * Neo ẢO: Radix đọc `getBoundingClientRect()` của nó mỗi lần tính vị trí. Đối tượng phải ỔN ĐỊNH\n * (Popper gọi `onAnchorChange` sau mỗi render), còn khung thì đọc từ ảnh chụp MỚI NHẤT qua ref.\n *\n * ⚠ Nhớ khung CUỐI còn đo được: lúc menu đóng, plugin gỡ decoration của đoạn `/…` trước, nên\n * `clientRect()` ra `null` đúng trong lúc `Popover` còn chạy hiệu ứng đóng — trả `DOMRect` rỗng\n * ở đó là menu văng lên góc trái trên khi đang mờ dần (đã thấy trên harness).\n */\n const latest = useAsRef(shown);\n const anchorRect = React.useRef<DOMRect | null>(null);\n const anchor = React.useRef({\n getBoundingClientRect: () => {\n const rect = latest.current?.clientRect();\n if (rect) anchorRect.current = rect;\n return anchorRect.current ?? new DOMRect();\n }\n });\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n // Đóng từ PHÍA MENU (Escape, bấm ra ngoài) → thoát chế độ gợi ý của plugin cho khớp.\n if (!next) snapshot?.exit();\n }}\n >\n <PopoverAnchor virtualRef={anchor} />\n <PopoverContent\n align=\"start\"\n side=\"bottom\"\n sideOffset={6}\n // Neo di chuyển theo chữ gõ (dòng xuống, khung cuộn) — đo lại mỗi khung hình khi đang mở.\n updatePositionStrategy=\"always\"\n onOpenAutoFocus={(event) => event.preventDefault()}\n onCloseAutoFocus={(event) => event.preventDefault()}\n className={cn(\"max-h-80 w-72 overflow-y-auto p-1\", className)}\n >\n {/* Không đặt `aria-multiselectable`: mỗi lần chọn là một lệnh, không phải tập chọn. */}\n <div role=\"listbox\" id={listId} aria-label={label}>\n {items.length === 0 ? (\n <div className=\"px-2 py-6 text-center text-sm text-muted-foreground\">\n {loading && loadingLabel ? loadingLabel : emptyLabel}\n </div>\n ) : (\n items.map((item, index) => {\n const active = index === activeIndex;\n return (\n // biome-ignore lint/a11y/useKeyWithClickEvents: bàn phím đi qua vùng soạn (aria-activedescendant), xem đầu file\n <div\n key={getKey(item)}\n id={optionId(index)}\n role=\"option\"\n // -1: tới được bằng mã (biome đòi `option` focus được), không vào thứ tự Tab — focus\n // thật vẫn ở vùng soạn, mục sáng đi theo `aria-activedescendant`.\n tabIndex={-1}\n aria-selected={active}\n data-selected={active || undefined}\n // Giữ focus ở vùng soạn: mousedown mặc định sẽ kéo focus ra khỏi ProseMirror.\n onMouseDown={(event) => event.preventDefault()}\n onPointerMove={() => setActiveIndex(index)}\n onClick={() => snapshot?.select(item)}\n className={cn(\n // Bộ lớp của `CommandItem`: mục sáng `bg-accent`, đệm theo trục mật độ, icon mặc định 16px.\n \"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-(--menu-item-py) text-sm outline-hidden select-none\",\n \"mt-1 first:mt-0 data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground\",\n \"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n \"[&_svg:not([class*='text-'])]:text-muted-foreground\"\n )}\n >\n {renderItem(item)}\n </div>\n );\n })\n )}\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { PluginKey } from '@tiptap/pm/state';
|
|
2
|
+
import { SuggestionOptions } from '@tiptap/suggestion';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Cầu nối giữa plugin gợi ý của Tiptap (`@tiptap/suggestion` — vòng đời onStart/onUpdate/onExit,
|
|
6
|
+
* sống trong ProseMirror) và một component React (`SuggestionMenu`).
|
|
7
|
+
*
|
|
8
|
+
* Không dùng `ReactRenderer` + `props.mount()` như ví dụ của Tiptap: cách đó gắn danh sách vào
|
|
9
|
+
* `document.body` ngoài mọi lớp Radix, và trong một `Dialog` (form tạo việc) thì Radix coi cú bấm
|
|
10
|
+
* vào danh sách là bấm RA NGOÀI hộp thoại — hộp thoại đóng ngay giữa lúc chọn khối. Ở đây plugin
|
|
11
|
+
* chỉ ĐẨY trạng thái vào một kho nhỏ; menu là cây React bình thường của trình soạn, mở bằng `Popover`
|
|
12
|
+
* của gói — một lớp Radix lồng đúng thứ bậc, nên Escape đóng menu chứ không đóng hộp thoại.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface SuggestionSnapshot<Item> {
|
|
16
|
+
items: Item[];
|
|
17
|
+
/** Chữ gõ sau ký tự kích hoạt */
|
|
18
|
+
query: string;
|
|
19
|
+
/** `true` khi `items()` bất đồng bộ chưa trả về */
|
|
20
|
+
loading: boolean;
|
|
21
|
+
/** Khung của đoạn đang gõ (`/que…`) — neo cho menu. `null` khi plugin chưa vẽ xong decoration. */
|
|
22
|
+
clientRect: () => DOMRect | null;
|
|
23
|
+
select: (item: Item) => void;
|
|
24
|
+
/** Thoát hẳn chế độ gợi ý (gỡ decoration) — dùng khi menu bị đóng từ PHÍA MENU (Escape, bấm ra ngoài). */
|
|
25
|
+
exit: () => void;
|
|
26
|
+
}
|
|
27
|
+
declare class SuggestionStore<Item, Selected = Item> {
|
|
28
|
+
private readonly pluginKey;
|
|
29
|
+
private readonly toSelected;
|
|
30
|
+
private snapshot;
|
|
31
|
+
private readonly listeners;
|
|
32
|
+
/**
|
|
33
|
+
* Bàn phím trong lúc menu mở: ProseMirror nhận phím (con trỏ vẫn ở trình soạn), plugin chuyển cho
|
|
34
|
+
* hàm này. Menu đăng ký nó khi mount. Trả `true` = đã xử lý, ProseMirror không làm gì thêm.
|
|
35
|
+
*/
|
|
36
|
+
keyHandler: ((event: KeyboardEvent) => boolean) | null;
|
|
37
|
+
constructor(pluginKey: PluginKey, toSelected: (item: Item) => Selected);
|
|
38
|
+
subscribe: (listener: () => void) => () => void;
|
|
39
|
+
getSnapshot: () => SuggestionSnapshot<Item> | null;
|
|
40
|
+
getServerSnapshot: () => null;
|
|
41
|
+
private set;
|
|
42
|
+
private fromProps;
|
|
43
|
+
/** Truyền thẳng vào `render` của `Suggestion` / `Mention.suggestion`. */
|
|
44
|
+
render: NonNullable<SuggestionOptions<Item, Selected>["render"]>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { type SuggestionSnapshot, SuggestionStore };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { exitSuggestion } from "@tiptap/suggestion";
|
|
2
|
+
class SuggestionStore {
|
|
3
|
+
constructor(pluginKey, toSelected) {
|
|
4
|
+
this.pluginKey = pluginKey;
|
|
5
|
+
this.toSelected = toSelected;
|
|
6
|
+
}
|
|
7
|
+
pluginKey;
|
|
8
|
+
toSelected;
|
|
9
|
+
snapshot = null;
|
|
10
|
+
listeners = /* @__PURE__ */ new Set();
|
|
11
|
+
/**
|
|
12
|
+
* Bàn phím trong lúc menu mở: ProseMirror nhận phím (con trỏ vẫn ở trình soạn), plugin chuyển cho
|
|
13
|
+
* hàm này. Menu đăng ký nó khi mount. Trả `true` = đã xử lý, ProseMirror không làm gì thêm.
|
|
14
|
+
*/
|
|
15
|
+
keyHandler = null;
|
|
16
|
+
subscribe = (listener) => {
|
|
17
|
+
this.listeners.add(listener);
|
|
18
|
+
return () => {
|
|
19
|
+
this.listeners.delete(listener);
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
getSnapshot = () => this.snapshot;
|
|
23
|
+
getServerSnapshot = () => null;
|
|
24
|
+
set(next) {
|
|
25
|
+
this.snapshot = next;
|
|
26
|
+
for (const listener of this.listeners) listener();
|
|
27
|
+
}
|
|
28
|
+
fromProps(props) {
|
|
29
|
+
return {
|
|
30
|
+
items: props.items,
|
|
31
|
+
query: props.query,
|
|
32
|
+
loading: props.loading,
|
|
33
|
+
clientRect: () => props.clientRect?.() ?? null,
|
|
34
|
+
select: (item) => props.command(this.toSelected(item)),
|
|
35
|
+
exit: () => exitSuggestion(props.editor.view, this.pluginKey)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Truyền thẳng vào `render` của `Suggestion` / `Mention.suggestion`. */
|
|
39
|
+
render = () => ({
|
|
40
|
+
onStart: (props) => this.set(this.fromProps(props)),
|
|
41
|
+
onUpdate: (props) => this.set(this.fromProps(props)),
|
|
42
|
+
onExit: () => this.set(null),
|
|
43
|
+
onKeyDown: ({ event }) => this.keyHandler?.(event) ?? false
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
SuggestionStore
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=suggestion-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/editor/suggestion-store.ts"],"sourcesContent":["/**\n * Cầu nối giữa plugin gợi ý của Tiptap (`@tiptap/suggestion` — vòng đời onStart/onUpdate/onExit,\n * sống trong ProseMirror) và một component React (`SuggestionMenu`).\n *\n * Không dùng `ReactRenderer` + `props.mount()` như ví dụ của Tiptap: cách đó gắn danh sách vào\n * `document.body` ngoài mọi lớp Radix, và trong một `Dialog` (form tạo việc) thì Radix coi cú bấm\n * vào danh sách là bấm RA NGOÀI hộp thoại — hộp thoại đóng ngay giữa lúc chọn khối. Ở đây plugin\n * chỉ ĐẨY trạng thái vào một kho nhỏ; menu là cây React bình thường của trình soạn, mở bằng `Popover`\n * của gói — một lớp Radix lồng đúng thứ bậc, nên Escape đóng menu chứ không đóng hộp thoại.\n */\n\nimport type { PluginKey } from \"@tiptap/pm/state\";\nimport { exitSuggestion, type SuggestionOptions, type SuggestionProps } from \"@tiptap/suggestion\";\n\nexport interface SuggestionSnapshot<Item> {\n items: Item[];\n /** Chữ gõ sau ký tự kích hoạt */\n query: string;\n /** `true` khi `items()` bất đồng bộ chưa trả về */\n loading: boolean;\n /** Khung của đoạn đang gõ (`/que…`) — neo cho menu. `null` khi plugin chưa vẽ xong decoration. */\n clientRect: () => DOMRect | null;\n select: (item: Item) => void;\n /** Thoát hẳn chế độ gợi ý (gỡ decoration) — dùng khi menu bị đóng từ PHÍA MENU (Escape, bấm ra ngoài). */\n exit: () => void;\n}\n\nexport class SuggestionStore<Item, Selected = Item> {\n private snapshot: SuggestionSnapshot<Item> | null = null;\n private readonly listeners = new Set<() => void>();\n\n /**\n * Bàn phím trong lúc menu mở: ProseMirror nhận phím (con trỏ vẫn ở trình soạn), plugin chuyển cho\n * hàm này. Menu đăng ký nó khi mount. Trả `true` = đã xử lý, ProseMirror không làm gì thêm.\n */\n keyHandler: ((event: KeyboardEvent) => boolean) | null = null;\n\n constructor(\n private readonly pluginKey: PluginKey,\n private readonly toSelected: (item: Item) => Selected\n ) {}\n\n subscribe = (listener: () => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n getSnapshot = () => this.snapshot;\n\n getServerSnapshot = () => null;\n\n private set(next: SuggestionSnapshot<Item> | null) {\n this.snapshot = next;\n for (const listener of this.listeners) listener();\n }\n\n private fromProps(props: SuggestionProps<Item, Selected>): SuggestionSnapshot<Item> {\n return {\n items: props.items,\n query: props.query,\n loading: props.loading,\n clientRect: () => props.clientRect?.() ?? null,\n select: (item) => props.command(this.toSelected(item)),\n exit: () => exitSuggestion(props.editor.view, this.pluginKey)\n };\n }\n\n /** Truyền thẳng vào `render` của `Suggestion` / `Mention.suggestion`. */\n render: NonNullable<SuggestionOptions<Item, Selected>[\"render\"]> = () => ({\n onStart: (props) => this.set(this.fromProps(props)),\n onUpdate: (props) => this.set(this.fromProps(props)),\n onExit: () => this.set(null),\n onKeyDown: ({ event }) => this.keyHandler?.(event) ?? false\n });\n}\n"],"mappings":"AAYA,SAAS,sBAAoE;AAetE,MAAM,gBAAuC;AAAA,EAUlD,YACmB,WACA,YACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EAXX,WAA4C;AAAA,EACnC,YAAY,oBAAI,IAAgB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMjD,aAAyD;AAAA,EAOzD,YAAY,CAAC,aAAyB;AACpC,SAAK,UAAU,IAAI,QAAQ;AAC3B,WAAO,MAAM;AACX,WAAK,UAAU,OAAO,QAAQ;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,cAAc,MAAM,KAAK;AAAA,EAEzB,oBAAoB,MAAM;AAAA,EAElB,IAAI,MAAuC;AACjD,SAAK,WAAW;AAChB,eAAW,YAAY,KAAK,UAAW,UAAS;AAAA,EAClD;AAAA,EAEQ,UAAU,OAAkE;AAClF,WAAO;AAAA,MACL,OAAO,MAAM;AAAA,MACb,OAAO,MAAM;AAAA,MACb,SAAS,MAAM;AAAA,MACf,YAAY,MAAM,MAAM,aAAa,KAAK;AAAA,MAC1C,QAAQ,CAAC,SAAS,MAAM,QAAQ,KAAK,WAAW,IAAI,CAAC;AAAA,MACrD,MAAM,MAAM,eAAe,MAAM,OAAO,MAAM,KAAK,SAAS;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA,EAGA,SAAmE,OAAO;AAAA,IACxE,SAAS,CAAC,UAAU,KAAK,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,IAClD,UAAU,CAAC,UAAU,KAAK,IAAI,KAAK,UAAU,KAAK,CAAC;AAAA,IACnD,QAAQ,MAAM,KAAK,IAAI,IAAI;AAAA,IAC3B,WAAW,CAAC,EAAE,MAAM,MAAM,KAAK,aAAa,KAAK,KAAK;AAAA,EACxD;AACF;","names":[]}
|
|
@@ -13,6 +13,7 @@ import './namespaces/route-tabs.js';
|
|
|
13
13
|
import './namespaces/rating.js';
|
|
14
14
|
import './namespaces/pagination.js';
|
|
15
15
|
import './namespaces/mention.js';
|
|
16
|
+
import '../editor/markdown-editor-constants.js';
|
|
16
17
|
import './namespaces/inline-edit.js';
|
|
17
18
|
import './namespaces/filter-chips.js';
|
|
18
19
|
import '../uploader/file-upload-constants.js';
|
package/dist/i18n/messages.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { RouteTabsMessages } from './namespaces/route-tabs.js';
|
|
|
8
8
|
import { RatingMessages } from './namespaces/rating.js';
|
|
9
9
|
import { PaginationMessages } from './namespaces/pagination.js';
|
|
10
10
|
import { MentionMessages } from './namespaces/mention.js';
|
|
11
|
+
import { MarkdownEditorLabels } from '../editor/markdown-editor-constants.js';
|
|
11
12
|
import { InlineEditMessages } from './namespaces/inline-edit.js';
|
|
12
13
|
import { FilterChipsMessages } from './namespaces/filter-chips.js';
|
|
13
14
|
import { FileUploadLabels, ImageUploadFieldLabels } from '../uploader/file-upload-constants.js';
|
|
@@ -72,6 +73,7 @@ declare const comitorMessagesEn: {
|
|
|
72
73
|
inlineEdit: InlineEditMessages;
|
|
73
74
|
kanban: KanbanLabels;
|
|
74
75
|
keyValue: KeyValueLabels;
|
|
76
|
+
markdownEditor: MarkdownEditorLabels;
|
|
75
77
|
mention: MentionMessages;
|
|
76
78
|
pagination: PaginationMessages;
|
|
77
79
|
phoneInput: PhoneInputLabels;
|
package/dist/i18n/messages.js
CHANGED
|
@@ -23,6 +23,7 @@ import { imageUploadFieldEn, imageUploadFieldVi } from "./namespaces/image-uploa
|
|
|
23
23
|
import { inlineEditEn, inlineEditVi } from "./namespaces/inline-edit.js";
|
|
24
24
|
import { kanbanEn, kanbanVi } from "./namespaces/kanban.js";
|
|
25
25
|
import { keyValueEn, keyValueVi } from "./namespaces/key-value.js";
|
|
26
|
+
import { markdownEditorEn, markdownEditorVi } from "./namespaces/markdown-editor.js";
|
|
26
27
|
import { mentionEn, mentionVi } from "./namespaces/mention.js";
|
|
27
28
|
import { paginationEn, paginationVi } from "./namespaces/pagination.js";
|
|
28
29
|
import { phoneInputEn, phoneInputVi } from "./namespaces/phone-input.js";
|
|
@@ -64,6 +65,7 @@ const comitorMessagesEn = {
|
|
|
64
65
|
inlineEdit: inlineEditEn,
|
|
65
66
|
kanban: kanbanEn,
|
|
66
67
|
keyValue: keyValueEn,
|
|
68
|
+
markdownEditor: markdownEditorEn,
|
|
67
69
|
mention: mentionEn,
|
|
68
70
|
pagination: paginationEn,
|
|
69
71
|
phoneInput: phoneInputEn,
|
|
@@ -106,6 +108,7 @@ const comitorMessagesVi = {
|
|
|
106
108
|
inlineEdit: inlineEditVi,
|
|
107
109
|
kanban: kanbanVi,
|
|
108
110
|
keyValue: keyValueVi,
|
|
111
|
+
markdownEditor: markdownEditorVi,
|
|
109
112
|
mention: mentionVi,
|
|
110
113
|
pagination: paginationVi,
|
|
111
114
|
phoneInput: phoneInputVi,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/i18n/messages.ts"],"sourcesContent":["/**\n * Bảng CHỮ của cả gói — module THUẦN (không directive, không hook), Server Component đọc thẳng\n * được (`verify:directives`, phép kiểm 2 và 3).\n *\n * Cùng khuôn với bảng chuỗi của `DataGrid` (`data-grid/messages.ts`), nay áp cho MỌI component:\n * · `comitorMessagesEn` là BẢN GỐC định nghĩa hình dạng ({@link ComitorMessages}). Tiếng Anh\n * là mặc định từ `3.0.0` — gói phục vụ cả app quốc tế, không chỉ app Comitor.\n * · `comitorMessagesVi` chú thích `: ComitorMessages`, nên thiếu một khoá là typecheck đỏ\n * ngay — không có đường nào để một chuỗi `undefined` lặng lẽ lọt ra giao diện.\n *\n * Mỗi namespace là MỘT component (hoặc một cụm component dùng chung chữ, vd `shell`). Chuỗi có\n * tham số viết thành HÀM, không phải mẫu có chỗ trống — trật tự từ quanh con số khác nhau theo ngôn\n * ngữ, nên mọi cách để gói tự nối chuỗi đều là chỗ chắc chắn dịch sai.\n *\n * Thêm ngôn ngữ thứ ba: app KHÔNG phải sửa gói — truyền cả một bảng qua prop `messages` của\n * `ComitorLocaleProvider` (trải `comitorMessagesEn` ra rồi dịch từng namespace).\n */\nimport { type ComitorLanguage, getComitorLanguage } from \"./locale.js\";\nimport { actionBarEn, actionBarVi } from \"./namespaces/action-bar.js\";\nimport { avatarGroupEn, avatarGroupVi } from \"./namespaces/avatar-group.js\";\nimport { badgeOverflowEn, badgeOverflowVi } from \"./namespaces/badge-overflow.js\";\nimport { bannerEn, bannerVi } from \"./namespaces/banner.js\";\nimport { breadcrumbEn, breadcrumbVi } from \"./namespaces/breadcrumb.js\";\nimport { calendarEn, calendarVi } from \"./namespaces/calendar.js\";\nimport { carouselEn, carouselVi } from \"./namespaces/carousel.js\";\nimport { comboboxEn, comboboxVi } from \"./namespaces/combobox.js\";\nimport { commandEn, commandVi } from \"./namespaces/command.js\";\nimport { commandPaletteEn, commandPaletteVi } from \"./namespaces/command-palette.js\";\nimport { confirmDialogEn, confirmDialogVi } from \"./namespaces/confirm-dialog.js\";\nimport { contrastEn, contrastVi } from \"./namespaces/contrast.js\";\nimport { copyButtonEn, copyButtonVi } from \"./namespaces/copy-button.js\";\nimport { cropperEn, cropperVi } from \"./namespaces/cropper.js\";\nimport { dataTableEn, dataTableVi } from \"./namespaces/data-table.js\";\nimport { datePickerEn, datePickerVi } from \"./namespaces/date-picker.js\";\nimport { dialogEn, dialogVi } from \"./namespaces/dialog.js\";\nimport { displayAxisEn, displayAxisVi } from \"./namespaces/display-axis.js\";\nimport { fileUploadEn, fileUploadVi } from \"./namespaces/file-upload.js\";\nimport { filterChipsEn, filterChipsVi } from \"./namespaces/filter-chips.js\";\nimport { imageUploadFieldEn, imageUploadFieldVi } from \"./namespaces/image-upload-field.js\";\nimport { inlineEditEn, inlineEditVi } from \"./namespaces/inline-edit.js\";\nimport { kanbanEn, kanbanVi } from \"./namespaces/kanban.js\";\nimport { keyValueEn, keyValueVi } from \"./namespaces/key-value.js\";\nimport { mentionEn, mentionVi } from \"./namespaces/mention.js\";\nimport { paginationEn, paginationVi } from \"./namespaces/pagination.js\";\nimport { phoneInputEn, phoneInputVi } from \"./namespaces/phone-input.js\";\nimport { ratingEn, ratingVi } from \"./namespaces/rating.js\";\nimport { routeTabsEn, routeTabsVi } from \"./namespaces/route-tabs.js\";\nimport { scrollSpyEn, scrollSpyVi } from \"./namespaces/scroll-spy.js\";\nimport { searchFilterBarEn, searchFilterBarVi } from \"./namespaces/search-filter-bar.js\";\nimport { sheetEn, sheetVi } from \"./namespaces/sheet.js\";\nimport { shellEn, shellVi } from \"./namespaces/shell.js\";\nimport { spinnerEn, spinnerVi } from \"./namespaces/spinner.js\";\nimport { stepperEn, stepperVi } from \"./namespaces/stepper.js\";\nimport { tablePaginationEn, tablePaginationVi } from \"./namespaces/table-pagination.js\";\nimport { tagsInputEn, tagsInputVi } from \"./namespaces/tags-input.js\";\nimport { timePickerEn, timePickerVi } from \"./namespaces/time-picker.js\";\nimport { toastEn, toastVi } from \"./namespaces/toast.js\";\nimport { tourEn, tourVi } from \"./namespaces/tour.js\";\n\n/** Bảng tiếng Anh — BẢN GỐC, định nghĩa hình dạng của mọi bảng khác. */\nexport const comitorMessagesEn = {\n actionBar: actionBarEn,\n avatarGroup: avatarGroupEn,\n badgeOverflow: badgeOverflowEn,\n banner: bannerEn,\n breadcrumb: breadcrumbEn,\n calendar: calendarEn,\n carousel: carouselEn,\n combobox: comboboxEn,\n command: commandEn,\n commandPalette: commandPaletteEn,\n confirmDialog: confirmDialogEn,\n contrast: contrastEn,\n copyButton: copyButtonEn,\n cropper: cropperEn,\n dataTable: dataTableEn,\n datePicker: datePickerEn,\n dialog: dialogEn,\n displayAxis: displayAxisEn,\n fileUpload: fileUploadEn,\n filterChips: filterChipsEn,\n imageUploadField: imageUploadFieldEn,\n inlineEdit: inlineEditEn,\n kanban: kanbanEn,\n keyValue: keyValueEn,\n mention: mentionEn,\n pagination: paginationEn,\n phoneInput: phoneInputEn,\n rating: ratingEn,\n routeTabs: routeTabsEn,\n scrollSpy: scrollSpyEn,\n searchFilterBar: searchFilterBarEn,\n sheet: sheetEn,\n shell: shellEn,\n spinner: spinnerEn,\n stepper: stepperEn,\n tablePagination: tablePaginationEn,\n tagsInput: tagsInputEn,\n timePicker: timePickerEn,\n toast: toastEn,\n tour: tourEn\n};\n\n/** Hình dạng của một bảng chữ đầy đủ. Suy từ bảng tiếng Anh, không viết tay lần hai. */\nexport type ComitorMessages = typeof comitorMessagesEn;\n\n/** Bảng tiếng Việt — phải phủ ĐỦ mọi khoá của bảng tiếng Anh. */\nexport const comitorMessagesVi: ComitorMessages = {\n actionBar: actionBarVi,\n avatarGroup: avatarGroupVi,\n badgeOverflow: badgeOverflowVi,\n banner: bannerVi,\n breadcrumb: breadcrumbVi,\n calendar: calendarVi,\n carousel: carouselVi,\n combobox: comboboxVi,\n command: commandVi,\n commandPalette: commandPaletteVi,\n confirmDialog: confirmDialogVi,\n contrast: contrastVi,\n copyButton: copyButtonVi,\n cropper: cropperVi,\n dataTable: dataTableVi,\n datePicker: datePickerVi,\n dialog: dialogVi,\n displayAxis: displayAxisVi,\n fileUpload: fileUploadVi,\n filterChips: filterChipsVi,\n imageUploadField: imageUploadFieldVi,\n inlineEdit: inlineEditVi,\n kanban: kanbanVi,\n keyValue: keyValueVi,\n mention: mentionVi,\n pagination: paginationVi,\n phoneInput: phoneInputVi,\n rating: ratingVi,\n routeTabs: routeTabsVi,\n scrollSpy: scrollSpyVi,\n searchFilterBar: searchFilterBarVi,\n sheet: sheetVi,\n shell: shellVi,\n spinner: spinnerVi,\n stepper: stepperVi,\n tablePagination: tablePaginationVi,\n tagsInput: tagsInputVi,\n timePicker: timePickerVi,\n toast: toastVi,\n tour: tourVi\n};\n\n/** Mọi bảng dựng sẵn, tra theo ngôn ngữ. */\nexport const COMITOR_MESSAGES: Record<ComitorLanguage, ComitorMessages> = {\n en: comitorMessagesEn,\n vi: comitorMessagesVi\n};\n\n/**\n * Ghi đè MỘT PHẦN: theo namespace, và trong mỗi namespace theo khoá. Dùng để đổi vài chữ theo\n * nghiệp vụ (\"Không gian làm việc\" → \"Công ty\"), hoặc để cấp nguyên một bảng cho ngôn ngữ thứ ba.\n */\nexport type ComitorMessagesOverride = {\n [K in keyof ComitorMessages]?: Partial<ComitorMessages[K]>;\n};\n\n/**\n * Trộn phần ghi đè vào một bảng đầy đủ — từng namespace một, nông ở cấp khoá. Không đụng tới bảng\n * gốc (trả về object mới), và namespace không được ghi đè giữ nguyên tham chiếu.\n */\nexport function mergeComitorMessages(base: ComitorMessages, override?: ComitorMessagesOverride): ComitorMessages {\n if (!override) return base;\n const merged = { ...base } as Record<string, object>;\n for (const key of Object.keys(override) as Array<keyof ComitorMessages>) {\n const part = override[key];\n if (part) merged[key] = { ...base[key], ...part };\n }\n return merged as ComitorMessages;\n}\n\n/**\n * Bảng chữ cho một `locale` (+ phần ghi đè) — bản THUẦN của những gì `ComitorLocaleProvider` làm,\n * dành cho nơi không có context: Server Component, email, toast bắn ngoài cây React.\n */\nexport function getComitorMessages(locale?: string | null, override?: ComitorMessagesOverride): ComitorMessages {\n return mergeComitorMessages(COMITOR_MESSAGES[getComitorLanguage(locale)], override);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/i18n/messages.ts"],"sourcesContent":["/**\n * Bảng CHỮ của cả gói — module THUẦN (không directive, không hook), Server Component đọc thẳng\n * được (`verify:directives`, phép kiểm 2 và 3).\n *\n * Cùng khuôn với bảng chuỗi của `DataGrid` (`data-grid/messages.ts`), nay áp cho MỌI component:\n * · `comitorMessagesEn` là BẢN GỐC định nghĩa hình dạng ({@link ComitorMessages}). Tiếng Anh\n * là mặc định từ `3.0.0` — gói phục vụ cả app quốc tế, không chỉ app Comitor.\n * · `comitorMessagesVi` chú thích `: ComitorMessages`, nên thiếu một khoá là typecheck đỏ\n * ngay — không có đường nào để một chuỗi `undefined` lặng lẽ lọt ra giao diện.\n *\n * Mỗi namespace là MỘT component (hoặc một cụm component dùng chung chữ, vd `shell`). Chuỗi có\n * tham số viết thành HÀM, không phải mẫu có chỗ trống — trật tự từ quanh con số khác nhau theo ngôn\n * ngữ, nên mọi cách để gói tự nối chuỗi đều là chỗ chắc chắn dịch sai.\n *\n * Thêm ngôn ngữ thứ ba: app KHÔNG phải sửa gói — truyền cả một bảng qua prop `messages` của\n * `ComitorLocaleProvider` (trải `comitorMessagesEn` ra rồi dịch từng namespace).\n */\nimport { type ComitorLanguage, getComitorLanguage } from \"./locale.js\";\nimport { actionBarEn, actionBarVi } from \"./namespaces/action-bar.js\";\nimport { avatarGroupEn, avatarGroupVi } from \"./namespaces/avatar-group.js\";\nimport { badgeOverflowEn, badgeOverflowVi } from \"./namespaces/badge-overflow.js\";\nimport { bannerEn, bannerVi } from \"./namespaces/banner.js\";\nimport { breadcrumbEn, breadcrumbVi } from \"./namespaces/breadcrumb.js\";\nimport { calendarEn, calendarVi } from \"./namespaces/calendar.js\";\nimport { carouselEn, carouselVi } from \"./namespaces/carousel.js\";\nimport { comboboxEn, comboboxVi } from \"./namespaces/combobox.js\";\nimport { commandEn, commandVi } from \"./namespaces/command.js\";\nimport { commandPaletteEn, commandPaletteVi } from \"./namespaces/command-palette.js\";\nimport { confirmDialogEn, confirmDialogVi } from \"./namespaces/confirm-dialog.js\";\nimport { contrastEn, contrastVi } from \"./namespaces/contrast.js\";\nimport { copyButtonEn, copyButtonVi } from \"./namespaces/copy-button.js\";\nimport { cropperEn, cropperVi } from \"./namespaces/cropper.js\";\nimport { dataTableEn, dataTableVi } from \"./namespaces/data-table.js\";\nimport { datePickerEn, datePickerVi } from \"./namespaces/date-picker.js\";\nimport { dialogEn, dialogVi } from \"./namespaces/dialog.js\";\nimport { displayAxisEn, displayAxisVi } from \"./namespaces/display-axis.js\";\nimport { fileUploadEn, fileUploadVi } from \"./namespaces/file-upload.js\";\nimport { filterChipsEn, filterChipsVi } from \"./namespaces/filter-chips.js\";\nimport { imageUploadFieldEn, imageUploadFieldVi } from \"./namespaces/image-upload-field.js\";\nimport { inlineEditEn, inlineEditVi } from \"./namespaces/inline-edit.js\";\nimport { kanbanEn, kanbanVi } from \"./namespaces/kanban.js\";\nimport { keyValueEn, keyValueVi } from \"./namespaces/key-value.js\";\nimport { markdownEditorEn, markdownEditorVi } from \"./namespaces/markdown-editor.js\";\nimport { mentionEn, mentionVi } from \"./namespaces/mention.js\";\nimport { paginationEn, paginationVi } from \"./namespaces/pagination.js\";\nimport { phoneInputEn, phoneInputVi } from \"./namespaces/phone-input.js\";\nimport { ratingEn, ratingVi } from \"./namespaces/rating.js\";\nimport { routeTabsEn, routeTabsVi } from \"./namespaces/route-tabs.js\";\nimport { scrollSpyEn, scrollSpyVi } from \"./namespaces/scroll-spy.js\";\nimport { searchFilterBarEn, searchFilterBarVi } from \"./namespaces/search-filter-bar.js\";\nimport { sheetEn, sheetVi } from \"./namespaces/sheet.js\";\nimport { shellEn, shellVi } from \"./namespaces/shell.js\";\nimport { spinnerEn, spinnerVi } from \"./namespaces/spinner.js\";\nimport { stepperEn, stepperVi } from \"./namespaces/stepper.js\";\nimport { tablePaginationEn, tablePaginationVi } from \"./namespaces/table-pagination.js\";\nimport { tagsInputEn, tagsInputVi } from \"./namespaces/tags-input.js\";\nimport { timePickerEn, timePickerVi } from \"./namespaces/time-picker.js\";\nimport { toastEn, toastVi } from \"./namespaces/toast.js\";\nimport { tourEn, tourVi } from \"./namespaces/tour.js\";\n\n/** Bảng tiếng Anh — BẢN GỐC, định nghĩa hình dạng của mọi bảng khác. */\nexport const comitorMessagesEn = {\n actionBar: actionBarEn,\n avatarGroup: avatarGroupEn,\n badgeOverflow: badgeOverflowEn,\n banner: bannerEn,\n breadcrumb: breadcrumbEn,\n calendar: calendarEn,\n carousel: carouselEn,\n combobox: comboboxEn,\n command: commandEn,\n commandPalette: commandPaletteEn,\n confirmDialog: confirmDialogEn,\n contrast: contrastEn,\n copyButton: copyButtonEn,\n cropper: cropperEn,\n dataTable: dataTableEn,\n datePicker: datePickerEn,\n dialog: dialogEn,\n displayAxis: displayAxisEn,\n fileUpload: fileUploadEn,\n filterChips: filterChipsEn,\n imageUploadField: imageUploadFieldEn,\n inlineEdit: inlineEditEn,\n kanban: kanbanEn,\n keyValue: keyValueEn,\n markdownEditor: markdownEditorEn,\n mention: mentionEn,\n pagination: paginationEn,\n phoneInput: phoneInputEn,\n rating: ratingEn,\n routeTabs: routeTabsEn,\n scrollSpy: scrollSpyEn,\n searchFilterBar: searchFilterBarEn,\n sheet: sheetEn,\n shell: shellEn,\n spinner: spinnerEn,\n stepper: stepperEn,\n tablePagination: tablePaginationEn,\n tagsInput: tagsInputEn,\n timePicker: timePickerEn,\n toast: toastEn,\n tour: tourEn\n};\n\n/** Hình dạng của một bảng chữ đầy đủ. Suy từ bảng tiếng Anh, không viết tay lần hai. */\nexport type ComitorMessages = typeof comitorMessagesEn;\n\n/** Bảng tiếng Việt — phải phủ ĐỦ mọi khoá của bảng tiếng Anh. */\nexport const comitorMessagesVi: ComitorMessages = {\n actionBar: actionBarVi,\n avatarGroup: avatarGroupVi,\n badgeOverflow: badgeOverflowVi,\n banner: bannerVi,\n breadcrumb: breadcrumbVi,\n calendar: calendarVi,\n carousel: carouselVi,\n combobox: comboboxVi,\n command: commandVi,\n commandPalette: commandPaletteVi,\n confirmDialog: confirmDialogVi,\n contrast: contrastVi,\n copyButton: copyButtonVi,\n cropper: cropperVi,\n dataTable: dataTableVi,\n datePicker: datePickerVi,\n dialog: dialogVi,\n displayAxis: displayAxisVi,\n fileUpload: fileUploadVi,\n filterChips: filterChipsVi,\n imageUploadField: imageUploadFieldVi,\n inlineEdit: inlineEditVi,\n kanban: kanbanVi,\n keyValue: keyValueVi,\n markdownEditor: markdownEditorVi,\n mention: mentionVi,\n pagination: paginationVi,\n phoneInput: phoneInputVi,\n rating: ratingVi,\n routeTabs: routeTabsVi,\n scrollSpy: scrollSpyVi,\n searchFilterBar: searchFilterBarVi,\n sheet: sheetVi,\n shell: shellVi,\n spinner: spinnerVi,\n stepper: stepperVi,\n tablePagination: tablePaginationVi,\n tagsInput: tagsInputVi,\n timePicker: timePickerVi,\n toast: toastVi,\n tour: tourVi\n};\n\n/** Mọi bảng dựng sẵn, tra theo ngôn ngữ. */\nexport const COMITOR_MESSAGES: Record<ComitorLanguage, ComitorMessages> = {\n en: comitorMessagesEn,\n vi: comitorMessagesVi\n};\n\n/**\n * Ghi đè MỘT PHẦN: theo namespace, và trong mỗi namespace theo khoá. Dùng để đổi vài chữ theo\n * nghiệp vụ (\"Không gian làm việc\" → \"Công ty\"), hoặc để cấp nguyên một bảng cho ngôn ngữ thứ ba.\n */\nexport type ComitorMessagesOverride = {\n [K in keyof ComitorMessages]?: Partial<ComitorMessages[K]>;\n};\n\n/**\n * Trộn phần ghi đè vào một bảng đầy đủ — từng namespace một, nông ở cấp khoá. Không đụng tới bảng\n * gốc (trả về object mới), và namespace không được ghi đè giữ nguyên tham chiếu.\n */\nexport function mergeComitorMessages(base: ComitorMessages, override?: ComitorMessagesOverride): ComitorMessages {\n if (!override) return base;\n const merged = { ...base } as Record<string, object>;\n for (const key of Object.keys(override) as Array<keyof ComitorMessages>) {\n const part = override[key];\n if (part) merged[key] = { ...base[key], ...part };\n }\n return merged as ComitorMessages;\n}\n\n/**\n * Bảng chữ cho một `locale` (+ phần ghi đè) — bản THUẦN của những gì `ComitorLocaleProvider` làm,\n * dành cho nơi không có context: Server Component, email, toast bắn ngoài cây React.\n */\nexport function getComitorMessages(locale?: string | null, override?: ComitorMessagesOverride): ComitorMessages {\n return mergeComitorMessages(COMITOR_MESSAGES[getComitorLanguage(locale)], override);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const markdownEditorEn = {
|
|
2
|
+
placeholder: "Write something, or type \u201C/\u201D for blocks\u2026",
|
|
3
|
+
editorLabel: "Editor",
|
|
4
|
+
toolbar: "Formatting",
|
|
5
|
+
bold: "Bold",
|
|
6
|
+
italic: "Italic",
|
|
7
|
+
strike: "Strikethrough",
|
|
8
|
+
code: "Inline code",
|
|
9
|
+
link: "Link",
|
|
10
|
+
text: "Text",
|
|
11
|
+
heading1: "Heading 1",
|
|
12
|
+
heading2: "Heading 2",
|
|
13
|
+
heading3: "Heading 3",
|
|
14
|
+
bulletList: "Bulleted list",
|
|
15
|
+
orderedList: "Numbered list",
|
|
16
|
+
taskList: "Checklist",
|
|
17
|
+
blockquote: "Quote",
|
|
18
|
+
codeBlock: "Code block",
|
|
19
|
+
table: "Table",
|
|
20
|
+
divider: "Divider",
|
|
21
|
+
image: "Image",
|
|
22
|
+
tableAddRow: "Add row below",
|
|
23
|
+
tableAddColumn: "Add column right",
|
|
24
|
+
tableDeleteRow: "Delete row",
|
|
25
|
+
tableDeleteColumn: "Delete column",
|
|
26
|
+
tableDelete: "Delete table",
|
|
27
|
+
slashMenu: "Insert block",
|
|
28
|
+
slashEmpty: "No matching blocks",
|
|
29
|
+
slashKeywords: {
|
|
30
|
+
text: "paragraph plain",
|
|
31
|
+
heading1: "title big",
|
|
32
|
+
heading2: "subtitle medium",
|
|
33
|
+
heading3: "small section",
|
|
34
|
+
bulletList: "unordered bullets",
|
|
35
|
+
orderedList: "ordered numbers",
|
|
36
|
+
taskList: "checkbox todo tasks",
|
|
37
|
+
blockquote: "citation callout",
|
|
38
|
+
codeBlock: "snippet programming",
|
|
39
|
+
table: "columns rows cells",
|
|
40
|
+
divider: "separator rule line",
|
|
41
|
+
image: "picture photo upload",
|
|
42
|
+
tableAddRow: "insert row",
|
|
43
|
+
tableAddColumn: "insert column",
|
|
44
|
+
tableDeleteRow: "remove row",
|
|
45
|
+
tableDeleteColumn: "remove column",
|
|
46
|
+
tableDelete: "remove table"
|
|
47
|
+
},
|
|
48
|
+
linkInput: "Link address",
|
|
49
|
+
linkPlaceholder: "Paste or type a link\u2026",
|
|
50
|
+
linkApply: "Apply link",
|
|
51
|
+
linkEdit: "Edit link",
|
|
52
|
+
linkOpen: "Open link",
|
|
53
|
+
linkRemove: "Remove link",
|
|
54
|
+
linkInvalid: "Enter a web, email or phone link",
|
|
55
|
+
mentionMenu: "People",
|
|
56
|
+
mentionEmpty: "No people found",
|
|
57
|
+
mentionLoading: "Searching\u2026",
|
|
58
|
+
imageUploading: "Uploading image\u2026",
|
|
59
|
+
imageUploadFailed: "Couldn\u2019t upload the image",
|
|
60
|
+
taskCheckbox: (text) => text ? `Task: ${text}` : "Empty task"
|
|
61
|
+
};
|
|
62
|
+
const markdownEditorVi = {
|
|
63
|
+
placeholder: "Vi\u1EBFt n\u1ED9i dung, ho\u1EB7c g\xF5 \u201C/\u201D \u0111\u1EC3 ch\xE8n kh\u1ED1i\u2026",
|
|
64
|
+
editorLabel: "Tr\xECnh so\u1EA1n th\u1EA3o",
|
|
65
|
+
toolbar: "\u0110\u1ECBnh d\u1EA1ng",
|
|
66
|
+
bold: "In \u0111\u1EADm",
|
|
67
|
+
italic: "In nghi\xEAng",
|
|
68
|
+
strike: "G\u1EA1ch ngang",
|
|
69
|
+
code: "M\xE3 n\u1ED9i d\xF2ng",
|
|
70
|
+
link: "Li\xEAn k\u1EBFt",
|
|
71
|
+
text: "V\u0103n b\u1EA3n",
|
|
72
|
+
heading1: "Ti\xEAu \u0111\u1EC1 1",
|
|
73
|
+
heading2: "Ti\xEAu \u0111\u1EC1 2",
|
|
74
|
+
heading3: "Ti\xEAu \u0111\u1EC1 3",
|
|
75
|
+
bulletList: "Danh s\xE1ch d\u1EA5u ch\u1EA5m",
|
|
76
|
+
orderedList: "Danh s\xE1ch \u0111\xE1nh s\u1ED1",
|
|
77
|
+
taskList: "Danh s\xE1ch vi\u1EC7c",
|
|
78
|
+
blockquote: "Tr\xEDch d\u1EABn",
|
|
79
|
+
codeBlock: "Kh\u1ED1i m\xE3",
|
|
80
|
+
table: "B\u1EA3ng",
|
|
81
|
+
divider: "\u0110\u01B0\u1EDDng k\u1EBB ngang",
|
|
82
|
+
image: "\u1EA2nh",
|
|
83
|
+
tableAddRow: "Th\xEAm h\xE0ng b\xEAn d\u01B0\u1EDBi",
|
|
84
|
+
tableAddColumn: "Th\xEAm c\u1ED9t b\xEAn ph\u1EA3i",
|
|
85
|
+
tableDeleteRow: "Xo\xE1 h\xE0ng",
|
|
86
|
+
tableDeleteColumn: "Xo\xE1 c\u1ED9t",
|
|
87
|
+
tableDelete: "Xo\xE1 b\u1EA3ng",
|
|
88
|
+
slashMenu: "Ch\xE8n kh\u1ED1i",
|
|
89
|
+
slashEmpty: "Kh\xF4ng c\xF3 kh\u1ED1i ph\xF9 h\u1EE3p",
|
|
90
|
+
slashKeywords: {
|
|
91
|
+
text: "\u0111o\u1EA1n v\u0103n ch\u1EEF th\u01B0\u1EDDng",
|
|
92
|
+
heading1: "\u0111\u1EC1 m\u1EE5c l\u1EDBn",
|
|
93
|
+
heading2: "\u0111\u1EC1 m\u1EE5c v\u1EEBa",
|
|
94
|
+
heading3: "\u0111\u1EC1 m\u1EE5c nh\u1ECF",
|
|
95
|
+
bulletList: "g\u1EA1ch \u0111\u1EA7u d\xF2ng li\u1EC7t k\xEA",
|
|
96
|
+
orderedList: "li\u1EC7t k\xEA s\u1ED1 th\u1EE9 t\u1EF1",
|
|
97
|
+
taskList: "vi\u1EC7c c\u1EA7n l\xE0m \xF4 tick",
|
|
98
|
+
blockquote: "d\u1EABn l\u1EDDi",
|
|
99
|
+
codeBlock: "m\xE3 ngu\u1ED3n \u0111o\u1EA1n m\xE3",
|
|
100
|
+
table: "l\u01B0\u1EDBi c\u1ED9t h\xE0ng",
|
|
101
|
+
divider: "ph\xE2n c\xE1ch v\u1EA1ch k\u1EBB",
|
|
102
|
+
image: "h\xECnh \u1EA3nh t\u1EA3i l\xEAn",
|
|
103
|
+
tableAddRow: "ch\xE8n h\xE0ng d\xF2ng",
|
|
104
|
+
tableAddColumn: "ch\xE8n c\u1ED9t",
|
|
105
|
+
tableDeleteRow: "b\u1ECF h\xE0ng d\xF2ng",
|
|
106
|
+
tableDeleteColumn: "b\u1ECF c\u1ED9t",
|
|
107
|
+
tableDelete: "b\u1ECF b\u1EA3ng"
|
|
108
|
+
},
|
|
109
|
+
linkInput: "\u0110\u1ECBa ch\u1EC9 li\xEAn k\u1EBFt",
|
|
110
|
+
linkPlaceholder: "D\xE1n ho\u1EB7c nh\u1EADp li\xEAn k\u1EBFt\u2026",
|
|
111
|
+
linkApply: "\xC1p d\u1EE5ng li\xEAn k\u1EBFt",
|
|
112
|
+
linkEdit: "S\u1EEDa li\xEAn k\u1EBFt",
|
|
113
|
+
linkOpen: "M\u1EDF li\xEAn k\u1EBFt",
|
|
114
|
+
linkRemove: "G\u1EE1 li\xEAn k\u1EBFt",
|
|
115
|
+
linkInvalid: "Nh\u1EADp li\xEAn k\u1EBFt web, email ho\u1EB7c s\u1ED1 \u0111i\u1EC7n tho\u1EA1i",
|
|
116
|
+
mentionMenu: "Th\xE0nh vi\xEAn",
|
|
117
|
+
mentionEmpty: "Kh\xF4ng t\xECm th\u1EA5y th\xE0nh vi\xEAn",
|
|
118
|
+
mentionLoading: "\u0110ang t\xECm\u2026",
|
|
119
|
+
imageUploading: "\u0110ang t\u1EA3i \u1EA3nh l\xEAn\u2026",
|
|
120
|
+
imageUploadFailed: "Kh\xF4ng t\u1EA3i \u0111\u01B0\u1EE3c \u1EA3nh l\xEAn",
|
|
121
|
+
taskCheckbox: (text) => text ? `Vi\u1EC7c: ${text}` : "Vi\u1EC7c ch\u01B0a c\xF3 n\u1ED9i dung"
|
|
122
|
+
};
|
|
123
|
+
export {
|
|
124
|
+
markdownEditorEn,
|
|
125
|
+
markdownEditorVi
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=markdown-editor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/i18n/namespaces/markdown-editor.ts"],"sourcesContent":["import type { MarkdownEditorLabels } from \"../../editor/markdown-editor-constants.js\";\n\n/*\n * Chữ của `MarkdownEditor` (`@comitor/ui/editor`). Hình dạng + chú thích từng khoá:\n * `MarkdownEditorLabels`.\n *\n * ⚠ Import ở dòng đầu là `import type` — bị xoá lúc biên dịch. Bảng chữ là tầng của entry CHÍNH,\n * còn trình soạn kéo Tiptap (peer tuỳ chọn); một import CHẠY ĐƯỢC từ đây sang `src/editor/` là kéo\n * Tiptap vào mọi app chỉ dùng `Button`.\n *\n * Tên khối giữ đúng chữ quen thuộc của các trình soạn markdown; bí danh máy (`h1`, `ul`, `todo`…)\n * không nằm ở đây mà ở `slash-commands.ts`, vì chúng không đổi theo ngôn ngữ.\n */\nexport const markdownEditorEn: MarkdownEditorLabels = {\n placeholder: \"Write something, or type “/” for blocks…\",\n editorLabel: \"Editor\",\n\n toolbar: \"Formatting\",\n bold: \"Bold\",\n italic: \"Italic\",\n strike: \"Strikethrough\",\n code: \"Inline code\",\n link: \"Link\",\n\n text: \"Text\",\n heading1: \"Heading 1\",\n heading2: \"Heading 2\",\n heading3: \"Heading 3\",\n bulletList: \"Bulleted list\",\n orderedList: \"Numbered list\",\n taskList: \"Checklist\",\n blockquote: \"Quote\",\n codeBlock: \"Code block\",\n table: \"Table\",\n divider: \"Divider\",\n image: \"Image\",\n tableAddRow: \"Add row below\",\n tableAddColumn: \"Add column right\",\n tableDeleteRow: \"Delete row\",\n tableDeleteColumn: \"Delete column\",\n tableDelete: \"Delete table\",\n\n slashMenu: \"Insert block\",\n slashEmpty: \"No matching blocks\",\n slashKeywords: {\n text: \"paragraph plain\",\n heading1: \"title big\",\n heading2: \"subtitle medium\",\n heading3: \"small section\",\n bulletList: \"unordered bullets\",\n orderedList: \"ordered numbers\",\n taskList: \"checkbox todo tasks\",\n blockquote: \"citation callout\",\n codeBlock: \"snippet programming\",\n table: \"columns rows cells\",\n divider: \"separator rule line\",\n image: \"picture photo upload\",\n tableAddRow: \"insert row\",\n tableAddColumn: \"insert column\",\n tableDeleteRow: \"remove row\",\n tableDeleteColumn: \"remove column\",\n tableDelete: \"remove table\"\n },\n\n linkInput: \"Link address\",\n linkPlaceholder: \"Paste or type a link…\",\n linkApply: \"Apply link\",\n linkEdit: \"Edit link\",\n linkOpen: \"Open link\",\n linkRemove: \"Remove link\",\n linkInvalid: \"Enter a web, email or phone link\",\n\n mentionMenu: \"People\",\n mentionEmpty: \"No people found\",\n mentionLoading: \"Searching…\",\n\n imageUploading: \"Uploading image…\",\n imageUploadFailed: \"Couldn’t upload the image\",\n\n taskCheckbox: (text) => (text ? `Task: ${text}` : \"Empty task\")\n};\n\nexport const markdownEditorVi: MarkdownEditorLabels = {\n placeholder: \"Viết nội dung, hoặc gõ “/” để chèn khối…\",\n editorLabel: \"Trình soạn thảo\",\n\n toolbar: \"Định dạng\",\n bold: \"In đậm\",\n italic: \"In nghiêng\",\n strike: \"Gạch ngang\",\n code: \"Mã nội dòng\",\n link: \"Liên kết\",\n\n text: \"Văn bản\",\n heading1: \"Tiêu đề 1\",\n heading2: \"Tiêu đề 2\",\n heading3: \"Tiêu đề 3\",\n bulletList: \"Danh sách dấu chấm\",\n orderedList: \"Danh sách đánh số\",\n taskList: \"Danh sách việc\",\n blockquote: \"Trích dẫn\",\n codeBlock: \"Khối mã\",\n table: \"Bảng\",\n divider: \"Đường kẻ ngang\",\n image: \"Ảnh\",\n tableAddRow: \"Thêm hàng bên dưới\",\n tableAddColumn: \"Thêm cột bên phải\",\n tableDeleteRow: \"Xoá hàng\",\n tableDeleteColumn: \"Xoá cột\",\n tableDelete: \"Xoá bảng\",\n\n slashMenu: \"Chèn khối\",\n slashEmpty: \"Không có khối phù hợp\",\n slashKeywords: {\n text: \"đoạn văn chữ thường\",\n heading1: \"đề mục lớn\",\n heading2: \"đề mục vừa\",\n heading3: \"đề mục nhỏ\",\n bulletList: \"gạch đầu dòng liệt kê\",\n orderedList: \"liệt kê số thứ tự\",\n taskList: \"việc cần làm ô tick\",\n blockquote: \"dẫn lời\",\n codeBlock: \"mã nguồn đoạn mã\",\n table: \"lưới cột hàng\",\n divider: \"phân cách vạch kẻ\",\n image: \"hình ảnh tải lên\",\n tableAddRow: \"chèn hàng dòng\",\n tableAddColumn: \"chèn cột\",\n tableDeleteRow: \"bỏ hàng dòng\",\n tableDeleteColumn: \"bỏ cột\",\n tableDelete: \"bỏ bảng\"\n },\n\n linkInput: \"Địa chỉ liên kết\",\n linkPlaceholder: \"Dán hoặc nhập liên kết…\",\n linkApply: \"Áp dụng liên kết\",\n linkEdit: \"Sửa liên kết\",\n linkOpen: \"Mở liên kết\",\n linkRemove: \"Gỡ liên kết\",\n linkInvalid: \"Nhập liên kết web, email hoặc số điện thoại\",\n\n mentionMenu: \"Thành viên\",\n mentionEmpty: \"Không tìm thấy thành viên\",\n mentionLoading: \"Đang tìm…\",\n\n imageUploading: \"Đang tải ảnh lên…\",\n imageUploadFailed: \"Không tải được ảnh lên\",\n\n taskCheckbox: (text) => (text ? `Việc: ${text}` : \"Việc chưa có nội dung\")\n};\n"],"mappings":"AAaO,MAAM,mBAAyC;AAAA,EACpD,aAAa;AAAA,EACb,aAAa;AAAA,EAEb,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EAEN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EAEb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf;AAAA,EAEA,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EAEb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,gBAAgB;AAAA,EAEhB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EAEnB,cAAc,CAAC,SAAU,OAAO,SAAS,IAAI,KAAK;AACpD;AAEO,MAAM,mBAAyC;AAAA,EACpD,aAAa;AAAA,EACb,aAAa;AAAA,EAEb,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,MAAM;AAAA,EAEN,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,OAAO;AAAA,EACP,SAAS;AAAA,EACT,OAAO;AAAA,EACP,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,aAAa;AAAA,EAEb,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,OAAO;AAAA,IACP,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf;AAAA,EAEA,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,WAAW;AAAA,EACX,UAAU;AAAA,EACV,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAAa;AAAA,EAEb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,gBAAgB;AAAA,EAEhB,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EAEnB,cAAc,CAAC,SAAU,OAAO,cAAS,IAAI,KAAK;AACpD;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -165,6 +165,7 @@ import './i18n/namespaces/route-tabs.js';
|
|
|
165
165
|
import './i18n/namespaces/rating.js';
|
|
166
166
|
import './i18n/namespaces/pagination.js';
|
|
167
167
|
import './i18n/namespaces/mention.js';
|
|
168
|
+
import './editor/markdown-editor-constants.js';
|
|
168
169
|
import './i18n/namespaces/inline-edit.js';
|
|
169
170
|
import './i18n/namespaces/filter-chips.js';
|
|
170
171
|
import './uploader/file-upload-constants.js';
|