@jbpark/ui-kit 7.0.0 → 8.0.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/dist/Button.d.mts +7 -2
- package/dist/Button.d.mts.map +1 -1
- package/dist/Button.mjs.map +1 -1
- package/dist/CodeEditor.d.mts +51 -0
- package/dist/CodeEditor.d.mts.map +1 -0
- package/dist/CodeEditor.mjs +87 -0
- package/dist/CodeEditor.mjs.map +1 -0
- package/dist/Tag.d.mts +8 -3
- package/dist/Tag.d.mts.map +1 -1
- package/dist/Tag.mjs.map +1 -1
- package/dist/components/atoms/index.d.mts +3 -1
- package/dist/components/atoms/rich-text-editor/content-classes.mjs +7 -0
- package/dist/components/atoms/rich-text-editor/content-classes.mjs.map +1 -0
- package/dist/components/atoms/rich-text-editor/index.d.mts +3 -0
- package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts +24 -0
- package/dist/components/atoms/rich-text-editor/rich-text-editor.d.mts.map +1 -0
- package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs +55 -0
- package/dist/components/atoms/rich-text-editor/rich-text-editor.mjs.map +1 -0
- package/dist/components/atoms/rich-text-editor/toolbar-button.mjs +16 -0
- package/dist/components/atoms/rich-text-editor/toolbar-button.mjs.map +1 -0
- package/dist/components/atoms/rich-text-editor/toolbar-presets.mjs +209 -0
- package/dist/components/atoms/rich-text-editor/toolbar-presets.mjs.map +1 -0
- package/dist/components/atoms/rich-text-editor/toolbar.mjs +21 -0
- package/dist/components/atoms/rich-text-editor/toolbar.mjs.map +1 -0
- package/dist/components/atoms/rich-text-editor/types.d.mts +14 -0
- package/dist/components/atoms/rich-text-editor/types.d.mts.map +1 -0
- package/dist/index.d.mts +2 -1
- package/dist/index.mjs +2 -1
- package/dist/lib/colors.d.mts +36 -3
- package/dist/lib/colors.d.mts.map +1 -1
- package/dist/providers/config/config.d.mts.map +1 -1
- package/dist/providers/config/config.mjs +5 -32
- package/dist/providers/config/config.mjs.map +1 -1
- package/dist/providers/config/dark-mode.mjs +39 -0
- package/dist/providers/config/dark-mode.mjs.map +1 -0
- package/dist/providers/config/types.d.mts +25 -18
- package/dist/providers/config/types.d.mts.map +1 -1
- package/dist/style.css +300 -88
- package/package.json +38 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import Button from "../../../Button.mjs";
|
|
3
|
+
import Popover from "../popover.mjs";
|
|
4
|
+
import ColorPicker from "../color-picker.mjs";
|
|
5
|
+
import Input from "../input/index.mjs";
|
|
6
|
+
import ToolbarButton from "./toolbar-button.mjs";
|
|
7
|
+
import Select from "../select.mjs";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { Bold, Code, Code2, Italic, Link, List, ListOrdered, Minus, Quote, Redo2, Strikethrough, Underline, Undo2 } from "lucide-react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/components/atoms/rich-text-editor/toolbar-presets.tsx
|
|
12
|
+
const FONT_SIZE_OPTIONS = [
|
|
13
|
+
{
|
|
14
|
+
label: "12",
|
|
15
|
+
value: "12px"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
label: "14",
|
|
19
|
+
value: "14px"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
label: "16",
|
|
23
|
+
value: "16px"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: "18",
|
|
27
|
+
value: "18px"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
label: "24",
|
|
31
|
+
value: "24px"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: "32",
|
|
35
|
+
value: "32px"
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
const HEADING_LEVELS = [
|
|
39
|
+
1,
|
|
40
|
+
2,
|
|
41
|
+
3,
|
|
42
|
+
4
|
|
43
|
+
];
|
|
44
|
+
const HEADING_OPTIONS = [{
|
|
45
|
+
label: "Paragraph",
|
|
46
|
+
value: "paragraph"
|
|
47
|
+
}, ...HEADING_LEVELS.map((level) => ({
|
|
48
|
+
label: `Heading ${level}`,
|
|
49
|
+
value: String(level)
|
|
50
|
+
}))];
|
|
51
|
+
const LinkControl = ({ editor }) => {
|
|
52
|
+
const [open, setOpen] = useState(false);
|
|
53
|
+
const [href, setHref] = useState("");
|
|
54
|
+
const apply = () => {
|
|
55
|
+
const next = href.trim();
|
|
56
|
+
if (next) editor.chain().focus().extendMarkRange("link").setLink({ href: next }).run();
|
|
57
|
+
else editor.chain().focus().unsetLink().run();
|
|
58
|
+
setOpen(false);
|
|
59
|
+
};
|
|
60
|
+
return /* @__PURE__ */ jsx(Popover, {
|
|
61
|
+
placement: "bottom",
|
|
62
|
+
open,
|
|
63
|
+
onOpenChange: (next) => {
|
|
64
|
+
if (next) setHref(editor.getAttributes("link").href ?? "");
|
|
65
|
+
setOpen(next);
|
|
66
|
+
},
|
|
67
|
+
content: /* @__PURE__ */ jsxs("div", {
|
|
68
|
+
className: "flex items-center gap-2",
|
|
69
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
70
|
+
value: href,
|
|
71
|
+
placeholder: "https://",
|
|
72
|
+
className: "w-48",
|
|
73
|
+
onChange: (e) => setHref(e.target.value),
|
|
74
|
+
onKeyDown: (e) => {
|
|
75
|
+
if (e.key === "Enter") {
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
apply();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
81
|
+
size: "small",
|
|
82
|
+
onClick: apply,
|
|
83
|
+
children: "Apply"
|
|
84
|
+
})]
|
|
85
|
+
}),
|
|
86
|
+
children: /* @__PURE__ */ jsx(ToolbarButton, {
|
|
87
|
+
icon: /* @__PURE__ */ jsx(Link, {}),
|
|
88
|
+
"aria-label": "Link",
|
|
89
|
+
active: editor.isActive("link")
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
const TOOLBAR_PRESET_RENDERERS = {
|
|
94
|
+
undo: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
95
|
+
icon: /* @__PURE__ */ jsx(Undo2, {}),
|
|
96
|
+
"aria-label": "Undo",
|
|
97
|
+
disabled: !editor.can().undo(),
|
|
98
|
+
onClick: () => editor.chain().focus().undo().run()
|
|
99
|
+
}, "undo"),
|
|
100
|
+
redo: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
101
|
+
icon: /* @__PURE__ */ jsx(Redo2, {}),
|
|
102
|
+
"aria-label": "Redo",
|
|
103
|
+
disabled: !editor.can().redo(),
|
|
104
|
+
onClick: () => editor.chain().focus().redo().run()
|
|
105
|
+
}, "redo"),
|
|
106
|
+
heading: (editor) => {
|
|
107
|
+
const activeLevel = HEADING_LEVELS.find((level) => editor.isActive("heading", { level }));
|
|
108
|
+
return /* @__PURE__ */ jsx(Select, {
|
|
109
|
+
className: "h-6 w-28",
|
|
110
|
+
placeholder: "Style",
|
|
111
|
+
options: HEADING_OPTIONS,
|
|
112
|
+
value: activeLevel ? String(activeLevel) : "paragraph",
|
|
113
|
+
onChange: (value) => value === "paragraph" ? editor.chain().setParagraph().run() : editor.chain().setHeading({ level: Number(value) }).run()
|
|
114
|
+
}, "heading");
|
|
115
|
+
},
|
|
116
|
+
bold: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
117
|
+
icon: /* @__PURE__ */ jsx(Bold, {}),
|
|
118
|
+
"aria-label": "Bold",
|
|
119
|
+
active: editor.isActive("bold"),
|
|
120
|
+
onClick: () => editor.chain().focus().toggleBold().run()
|
|
121
|
+
}, "bold"),
|
|
122
|
+
italic: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
123
|
+
icon: /* @__PURE__ */ jsx(Italic, {}),
|
|
124
|
+
"aria-label": "Italic",
|
|
125
|
+
active: editor.isActive("italic"),
|
|
126
|
+
onClick: () => editor.chain().focus().toggleItalic().run()
|
|
127
|
+
}, "italic"),
|
|
128
|
+
underline: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
129
|
+
icon: /* @__PURE__ */ jsx(Underline, {}),
|
|
130
|
+
"aria-label": "Underline",
|
|
131
|
+
active: editor.isActive("underline"),
|
|
132
|
+
onClick: () => editor.chain().focus().toggleUnderline().run()
|
|
133
|
+
}, "underline"),
|
|
134
|
+
strike: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
135
|
+
icon: /* @__PURE__ */ jsx(Strikethrough, {}),
|
|
136
|
+
"aria-label": "Strikethrough",
|
|
137
|
+
active: editor.isActive("strike"),
|
|
138
|
+
onClick: () => editor.chain().focus().toggleStrike().run()
|
|
139
|
+
}, "strike"),
|
|
140
|
+
code: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
141
|
+
icon: /* @__PURE__ */ jsx(Code, {}),
|
|
142
|
+
"aria-label": "Inline code",
|
|
143
|
+
active: editor.isActive("code"),
|
|
144
|
+
onClick: () => editor.chain().focus().toggleCode().run()
|
|
145
|
+
}, "code"),
|
|
146
|
+
bulletList: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
147
|
+
icon: /* @__PURE__ */ jsx(List, {}),
|
|
148
|
+
"aria-label": "Bullet list",
|
|
149
|
+
active: editor.isActive("bulletList"),
|
|
150
|
+
onClick: () => editor.chain().focus().toggleBulletList().run()
|
|
151
|
+
}, "bulletList"),
|
|
152
|
+
orderedList: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
153
|
+
icon: /* @__PURE__ */ jsx(ListOrdered, {}),
|
|
154
|
+
"aria-label": "Ordered list",
|
|
155
|
+
active: editor.isActive("orderedList"),
|
|
156
|
+
onClick: () => editor.chain().focus().toggleOrderedList().run()
|
|
157
|
+
}, "orderedList"),
|
|
158
|
+
blockquote: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
159
|
+
icon: /* @__PURE__ */ jsx(Quote, {}),
|
|
160
|
+
"aria-label": "Blockquote",
|
|
161
|
+
active: editor.isActive("blockquote"),
|
|
162
|
+
onClick: () => editor.chain().focus().toggleBlockquote().run()
|
|
163
|
+
}, "blockquote"),
|
|
164
|
+
codeBlock: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
165
|
+
icon: /* @__PURE__ */ jsx(Code2, {}),
|
|
166
|
+
"aria-label": "Code block",
|
|
167
|
+
active: editor.isActive("codeBlock"),
|
|
168
|
+
onClick: () => editor.chain().focus().toggleCodeBlock().run()
|
|
169
|
+
}, "codeBlock"),
|
|
170
|
+
horizontalRule: (editor) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
171
|
+
icon: /* @__PURE__ */ jsx(Minus, {}),
|
|
172
|
+
"aria-label": "Horizontal rule",
|
|
173
|
+
onClick: () => editor.chain().focus().setHorizontalRule().run()
|
|
174
|
+
}, "horizontalRule"),
|
|
175
|
+
link: (editor) => /* @__PURE__ */ jsx(LinkControl, { editor }, "link"),
|
|
176
|
+
fontSize: (editor) => /* @__PURE__ */ jsx(Select, {
|
|
177
|
+
className: "h-6 w-20",
|
|
178
|
+
placeholder: "Size",
|
|
179
|
+
options: FONT_SIZE_OPTIONS,
|
|
180
|
+
value: editor.getAttributes("textStyle").fontSize ?? void 0,
|
|
181
|
+
onChange: (value) => editor.chain().setFontSize(value).run()
|
|
182
|
+
}, "fontSize"),
|
|
183
|
+
color: (editor) => /* @__PURE__ */ jsx(ColorPicker, {
|
|
184
|
+
value: editor.getAttributes("textStyle").color || "#000000",
|
|
185
|
+
onChange: (value) => editor.chain().setColor(value).run()
|
|
186
|
+
}, "color")
|
|
187
|
+
};
|
|
188
|
+
const DEFAULT_TOOLBAR_PRESETS = [
|
|
189
|
+
"undo",
|
|
190
|
+
"redo",
|
|
191
|
+
"heading",
|
|
192
|
+
"bold",
|
|
193
|
+
"italic",
|
|
194
|
+
"underline",
|
|
195
|
+
"strike",
|
|
196
|
+
"code",
|
|
197
|
+
"bulletList",
|
|
198
|
+
"orderedList",
|
|
199
|
+
"blockquote",
|
|
200
|
+
"codeBlock",
|
|
201
|
+
"horizontalRule",
|
|
202
|
+
"link",
|
|
203
|
+
"fontSize",
|
|
204
|
+
"color"
|
|
205
|
+
];
|
|
206
|
+
//#endregion
|
|
207
|
+
export { DEFAULT_TOOLBAR_PRESETS, TOOLBAR_PRESET_RENDERERS };
|
|
208
|
+
|
|
209
|
+
//# sourceMappingURL=toolbar-presets.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar-presets.mjs","names":["LinkIcon"],"sources":["../../../../src/components/atoms/rich-text-editor/toolbar-presets.tsx"],"sourcesContent":["'use client';\n\nimport { type ReactNode, useState } from 'react';\n\nimport type { Editor } from '@tiptap/core';\nimport {\n Bold,\n Code,\n Code2,\n Italic,\n Link as LinkIcon,\n List,\n ListOrdered,\n Minus,\n Quote,\n Redo2,\n Strikethrough,\n Underline,\n Undo2,\n} from 'lucide-react';\n\nimport Button from '../button';\nimport ColorPicker from '../color-picker';\nimport Input from '../input';\nimport Popover from '../popover';\nimport Select from '../select';\nimport ToolbarButton from './toolbar-button';\nimport type { ToolbarPreset } from './types';\n\nconst FONT_SIZE_OPTIONS = [\n { label: '12', value: '12px' },\n { label: '14', value: '14px' },\n { label: '16', value: '16px' },\n { label: '18', value: '18px' },\n { label: '24', value: '24px' },\n { label: '32', value: '32px' },\n];\n\nconst HEADING_LEVELS = [1, 2, 3, 4] as const;\n\nconst HEADING_OPTIONS = [\n { label: 'Paragraph', value: 'paragraph' },\n ...HEADING_LEVELS.map(level => ({\n label: `Heading ${level}`,\n value: String(level),\n })),\n];\n\n// Owns its own open/href state, so it has to be a real component (not a\n// plain render function like the other presets) for the hooks below to be\n// valid.\nconst LinkControl = ({ editor }: { editor: Editor }) => {\n const [open, setOpen] = useState(false);\n const [href, setHref] = useState('');\n\n const apply = () => {\n const next = href.trim();\n\n if (next) {\n editor\n .chain()\n .focus()\n .extendMarkRange('link')\n .setLink({ href: next })\n .run();\n } else {\n editor.chain().focus().unsetLink().run();\n }\n setOpen(false);\n };\n\n return (\n <Popover\n placement=\"bottom\"\n open={open}\n onOpenChange={next => {\n if (next) {\n setHref(editor.getAttributes('link').href ?? '');\n }\n setOpen(next);\n }}\n content={\n <div className=\"flex items-center gap-2\">\n <Input\n value={href}\n placeholder=\"https://\"\n className=\"w-48\"\n onChange={e => setHref(e.target.value)}\n onKeyDown={e => {\n if (e.key === 'Enter') {\n e.preventDefault();\n apply();\n }\n }}\n />\n <Button size=\"small\" onClick={apply}>\n Apply\n </Button>\n </div>\n }\n >\n <ToolbarButton\n icon={<LinkIcon />}\n aria-label=\"Link\"\n active={editor.isActive('link')}\n />\n </Popover>\n );\n};\n\nexport const TOOLBAR_PRESET_RENDERERS: Record<\n ToolbarPreset,\n (editor: Editor) => ReactNode\n> = {\n undo: editor => (\n <ToolbarButton\n key=\"undo\"\n icon={<Undo2 />}\n aria-label=\"Undo\"\n disabled={!editor.can().undo()}\n onClick={() => editor.chain().focus().undo().run()}\n />\n ),\n redo: editor => (\n <ToolbarButton\n key=\"redo\"\n icon={<Redo2 />}\n aria-label=\"Redo\"\n disabled={!editor.can().redo()}\n onClick={() => editor.chain().focus().redo().run()}\n />\n ),\n heading: editor => {\n const activeLevel = HEADING_LEVELS.find(level =>\n editor.isActive('heading', { level }),\n );\n\n return (\n <Select\n key=\"heading\"\n className=\"h-6 w-28\"\n placeholder=\"Style\"\n options={HEADING_OPTIONS}\n value={activeLevel ? String(activeLevel) : 'paragraph'}\n // No `.focus()` here — the Select is still open when this fires,\n // and refocusing the editor mid-selection dismisses it early (same\n // class of bug as the color picker below).\n onChange={value =>\n value === 'paragraph'\n ? editor.chain().setParagraph().run()\n : editor\n .chain()\n .setHeading({ level: Number(value) as 1 | 2 | 3 | 4 })\n .run()\n }\n />\n );\n },\n bold: editor => (\n <ToolbarButton\n key=\"bold\"\n icon={<Bold />}\n aria-label=\"Bold\"\n active={editor.isActive('bold')}\n onClick={() => editor.chain().focus().toggleBold().run()}\n />\n ),\n italic: editor => (\n <ToolbarButton\n key=\"italic\"\n icon={<Italic />}\n aria-label=\"Italic\"\n active={editor.isActive('italic')}\n onClick={() => editor.chain().focus().toggleItalic().run()}\n />\n ),\n underline: editor => (\n <ToolbarButton\n key=\"underline\"\n icon={<Underline />}\n aria-label=\"Underline\"\n active={editor.isActive('underline')}\n onClick={() => editor.chain().focus().toggleUnderline().run()}\n />\n ),\n strike: editor => (\n <ToolbarButton\n key=\"strike\"\n icon={<Strikethrough />}\n aria-label=\"Strikethrough\"\n active={editor.isActive('strike')}\n onClick={() => editor.chain().focus().toggleStrike().run()}\n />\n ),\n code: editor => (\n <ToolbarButton\n key=\"code\"\n icon={<Code />}\n aria-label=\"Inline code\"\n active={editor.isActive('code')}\n onClick={() => editor.chain().focus().toggleCode().run()}\n />\n ),\n bulletList: editor => (\n <ToolbarButton\n key=\"bulletList\"\n icon={<List />}\n aria-label=\"Bullet list\"\n active={editor.isActive('bulletList')}\n onClick={() => editor.chain().focus().toggleBulletList().run()}\n />\n ),\n orderedList: editor => (\n <ToolbarButton\n key=\"orderedList\"\n icon={<ListOrdered />}\n aria-label=\"Ordered list\"\n active={editor.isActive('orderedList')}\n onClick={() => editor.chain().focus().toggleOrderedList().run()}\n />\n ),\n blockquote: editor => (\n <ToolbarButton\n key=\"blockquote\"\n icon={<Quote />}\n aria-label=\"Blockquote\"\n active={editor.isActive('blockquote')}\n onClick={() => editor.chain().focus().toggleBlockquote().run()}\n />\n ),\n codeBlock: editor => (\n <ToolbarButton\n key=\"codeBlock\"\n icon={<Code2 />}\n aria-label=\"Code block\"\n active={editor.isActive('codeBlock')}\n onClick={() => editor.chain().focus().toggleCodeBlock().run()}\n />\n ),\n horizontalRule: editor => (\n <ToolbarButton\n key=\"horizontalRule\"\n icon={<Minus />}\n aria-label=\"Horizontal rule\"\n onClick={() => editor.chain().focus().setHorizontalRule().run()}\n />\n ),\n link: editor => <LinkControl key=\"link\" editor={editor} />,\n fontSize: editor => (\n <Select\n key=\"fontSize\"\n className=\"h-6 w-20\"\n placeholder=\"Size\"\n options={FONT_SIZE_OPTIONS}\n value={editor.getAttributes('textStyle').fontSize ?? undefined}\n // No `.focus()` — see the `heading` preset above.\n onChange={value => editor.chain().setFontSize(value).run()}\n />\n ),\n color: editor => (\n <ColorPicker\n key=\"color\"\n value={editor.getAttributes('textStyle').color || '#000000'}\n // No `.focus()`: react-colorful fires `onChange` continuously while\n // dragging, and refocusing the editor on every tick moves DOM focus\n // out from under the still-open popover, which Radix reads as an\n // outside interaction and dismisses immediately (ui-kit reported\n // symptom: \"picker closes as soon as you try to change the color\").\n // The mark still applies to the stored selection without it.\n onChange={value => editor.chain().setColor(value).run()}\n />\n ),\n};\n\nexport const DEFAULT_TOOLBAR_PRESETS: ToolbarPreset[] = [\n 'undo',\n 'redo',\n 'heading',\n 'bold',\n 'italic',\n 'underline',\n 'strike',\n 'code',\n 'bulletList',\n 'orderedList',\n 'blockquote',\n 'codeBlock',\n 'horizontalRule',\n 'link',\n 'fontSize',\n 'color',\n];\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { cn } from "../../../utils.mjs";
|
|
2
|
+
import ToolbarButton from "./toolbar-button.mjs";
|
|
3
|
+
import { TOOLBAR_PRESET_RENDERERS } from "./toolbar-presets.mjs";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/atoms/rich-text-editor/toolbar.tsx
|
|
6
|
+
const Toolbar = ({ editor, presets, items, className }) => {
|
|
7
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
8
|
+
"data-slot": "rich-text-editor-toolbar",
|
|
9
|
+
className: cn("flex flex-wrap items-center gap-1 border-b border-gray-200 px-2 py-1", className),
|
|
10
|
+
children: [presets.map((preset) => TOOLBAR_PRESET_RENDERERS[preset](editor)), items?.map((item) => /* @__PURE__ */ jsx(ToolbarButton, {
|
|
11
|
+
icon: item.icon,
|
|
12
|
+
"aria-label": item.label,
|
|
13
|
+
active: item.isActive?.(editor),
|
|
14
|
+
onClick: () => item.onClick(editor)
|
|
15
|
+
}, item.key))]
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { Toolbar as default };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=toolbar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toolbar.mjs","names":[],"sources":["../../../../src/components/atoms/rich-text-editor/toolbar.tsx"],"sourcesContent":["import type { Editor } from '@tiptap/core';\n\nimport { cn } from '@repo/ui/utils';\n\nimport ToolbarButton from './toolbar-button';\nimport { TOOLBAR_PRESET_RENDERERS } from './toolbar-presets';\nimport type { ToolbarItem, ToolbarPreset } from './types';\n\nexport interface ToolbarProps {\n editor: Editor;\n presets: ToolbarPreset[];\n items?: ToolbarItem[];\n className?: string;\n}\n\nconst Toolbar = ({ editor, presets, items, className }: ToolbarProps) => {\n return (\n <div\n data-slot=\"rich-text-editor-toolbar\"\n className={cn(\n 'flex flex-wrap items-center gap-1 border-b border-gray-200 px-2 py-1',\n className,\n //\n )}\n >\n {presets.map(preset => TOOLBAR_PRESET_RENDERERS[preset](editor))}\n {items?.map(item => (\n <ToolbarButton\n key={item.key}\n icon={item.icon}\n aria-label={item.label}\n active={item.isActive?.(editor)}\n onClick={() => item.onClick(editor)}\n />\n ))}\n </div>\n );\n};\n\nexport default Toolbar;\n"],"mappings":";;;;;AAeA,MAAM,WAAW,EAAE,QAAQ,SAAS,OAAO,gBAA8B;CACvE,OACE,qBAAC,OAAD;EACE,aAAU;EACV,WAAW,GACT,wEACA,SAEF;EANF,UAAA,CAQG,QAAQ,KAAI,WAAU,yBAAyB,OAAO,CAAC,MAAM,CAAC,GAC9D,OAAO,KAAI,SACV,oBAAC,eAAD;GAEE,MAAM,KAAK;GACX,cAAY,KAAK;GACjB,QAAQ,KAAK,WAAW,MAAM;GAC9B,eAAe,KAAK,QAAQ,MAAM;EACnC,GALM,KAAK,GAKX,CACF,CACE;;AAET"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { Editor } from "@tiptap/core";
|
|
3
|
+
//#region src/components/atoms/rich-text-editor/types.d.ts
|
|
4
|
+
type ToolbarPreset = 'undo' | 'redo' | 'heading' | 'bold' | 'italic' | 'underline' | 'strike' | 'code' | 'bulletList' | 'orderedList' | 'blockquote' | 'codeBlock' | 'horizontalRule' | 'link' | 'fontSize' | 'color';
|
|
5
|
+
interface ToolbarItem {
|
|
6
|
+
key: string;
|
|
7
|
+
icon: ReactNode;
|
|
8
|
+
label?: string;
|
|
9
|
+
isActive?: (editor: Editor) => boolean;
|
|
10
|
+
onClick: (editor: Editor) => void;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { ToolbarItem, ToolbarPreset };
|
|
14
|
+
//# sourceMappingURL=types.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../src/components/atoms/rich-text-editor/types.ts"],"mappings":";;;KAIY;UAkBK;EACf;EACA,MAAM;EACN;EACA,YAAY,QAAQ;EACpB,UAAU,QAAQ"}
|
package/dist/index.d.mts
CHANGED
|
@@ -20,6 +20,7 @@ import Input from "./components/atoms/input/index.mjs";
|
|
|
20
20
|
import Popover from "./components/atoms/popover.mjs";
|
|
21
21
|
import Progress from "./components/atoms/progress.mjs";
|
|
22
22
|
import Radio from "./components/atoms/radio/index.mjs";
|
|
23
|
+
import RichTextEditor from "./components/atoms/rich-text-editor/rich-text-editor.mjs";
|
|
23
24
|
import Select from "./components/atoms/select.mjs";
|
|
24
25
|
import Skeleton from "./components/atoms/skeleton/index.mjs";
|
|
25
26
|
import Slider from "./components/atoms/slider/slider.mjs";
|
|
@@ -52,4 +53,4 @@ import { ComponentSize, DefaultProps, Locale, ThemeConfig, ThemeToken } from "./
|
|
|
52
53
|
import { DEFAULT_LOCALE, useConfig } from "./providers/config/context.mjs";
|
|
53
54
|
import Config, { Props as Props$3 } from "./providers/config/config.mjs";
|
|
54
55
|
import { ConfigSnapshotProvider, getRootConfigValue } from "./providers/config/registry.mjs";
|
|
55
|
-
export { Button, Card, type Props as CardProps, Checkbox, Col, type Props$1 as ColProps, Collapse, type Props$2 as CollapseProps, ColorPicker, type ComponentSize, Config, type Props$3 as ConfigProps, ConfigSnapshotProvider, Container, type Props$4 as ContainerProps, type Props$5 as ContentProps, DEFAULT_LOCALE, DatePicker, type DefaultProps, Drawer, type Props$6 as DrawerProps, Dropdown, type Props$7 as DropdownProps, Empty, type Props$8 as EmptyProps, FloatButton, type Props$9 as FooterProps, type Props$10 as HeaderProps, Input, Layout, type Props$11 as LayoutProps, List, type Props$12 as ListProps, type Locale, Marquees, type Props$13 as MarqueesProps, Menu, type Props$14 as MenuProps, Modal, type Props$15 as ModalProps, PageHeader, type Props$16 as PageHeaderProps, Popover, Progress, Radio, Result, type Props$17 as ResultProps, Reveals, type Props$18 as RevealsProps, Row, type Props$19 as RowProps, Select, type Props$20 as SiderProps, Skeleton, Slider, Space, type Props$21 as SpaceProps, Spin, Splitter, type Props$22 as SplitterProps, Swiper, type Props$23 as SwiperProps, Switch, Tag, type ThemeConfig, type ThemeToken, Toast, type StaticProps as ToastOptions, Typography, Upload, type UploadFile, type Props$24 as UploadProps, getRootConfigValue, useConfig };
|
|
56
|
+
export { Button, Card, type Props as CardProps, Checkbox, Col, type Props$1 as ColProps, Collapse, type Props$2 as CollapseProps, ColorPicker, type ComponentSize, Config, type Props$3 as ConfigProps, ConfigSnapshotProvider, Container, type Props$4 as ContainerProps, type Props$5 as ContentProps, DEFAULT_LOCALE, DatePicker, type DefaultProps, Drawer, type Props$6 as DrawerProps, Dropdown, type Props$7 as DropdownProps, Empty, type Props$8 as EmptyProps, FloatButton, type Props$9 as FooterProps, type Props$10 as HeaderProps, Input, Layout, type Props$11 as LayoutProps, List, type Props$12 as ListProps, type Locale, Marquees, type Props$13 as MarqueesProps, Menu, type Props$14 as MenuProps, Modal, type Props$15 as ModalProps, PageHeader, type Props$16 as PageHeaderProps, Popover, Progress, Radio, Result, type Props$17 as ResultProps, Reveals, type Props$18 as RevealsProps, RichTextEditor, Row, type Props$19 as RowProps, Select, type Props$20 as SiderProps, Skeleton, Slider, Space, type Props$21 as SpaceProps, Spin, Splitter, type Props$22 as SplitterProps, Swiper, type Props$23 as SwiperProps, Switch, Tag, type ThemeConfig, type ThemeToken, Toast, type StaticProps as ToastOptions, Typography, Upload, type UploadFile, type Props$24 as UploadProps, getRootConfigValue, useConfig };
|
package/dist/index.mjs
CHANGED
|
@@ -13,6 +13,7 @@ import Input from "./components/atoms/input/index.mjs";
|
|
|
13
13
|
import Progress from "./components/atoms/progress.mjs";
|
|
14
14
|
import Radio from "./components/atoms/radio/index.mjs";
|
|
15
15
|
import Select from "./components/atoms/select.mjs";
|
|
16
|
+
import RichTextEditor from "./components/atoms/rich-text-editor/rich-text-editor.mjs";
|
|
16
17
|
import Skeleton from "./components/atoms/skeleton/index.mjs";
|
|
17
18
|
import slider_default from "./components/atoms/slider/index.mjs";
|
|
18
19
|
import Spin from "./components/atoms/spin.mjs";
|
|
@@ -39,4 +40,4 @@ import Result from "./components/templates/result.mjs";
|
|
|
39
40
|
import PageHeader from "./components/templates/page-header.mjs";
|
|
40
41
|
import Row from "./components/templates/grid/row.mjs";
|
|
41
42
|
import Col from "./components/templates/grid/col.mjs";
|
|
42
|
-
export { Button, Card, Checkbox, Col, Collapse, ColorPicker, Config, ConfigSnapshotProvider, Container, DEFAULT_LOCALE, DatePicker, Drawer, Dropdown, Empty, FloatButton, Input, Layout, List, Marquees, Menu, Modal, PageHeader, Popover, Progress, Radio, Result, Reveals, Row, Select, Skeleton, slider_default as Slider, Space, Spin, Splitter, Swiper, Switch, Tag, Toast, Typography, Upload, getRootConfigValue, useConfig };
|
|
43
|
+
export { Button, Card, Checkbox, Col, Collapse, ColorPicker, Config, ConfigSnapshotProvider, Container, DEFAULT_LOCALE, DatePicker, Drawer, Dropdown, Empty, FloatButton, Input, Layout, List, Marquees, Menu, Modal, PageHeader, Popover, Progress, Radio, Result, Reveals, RichTextEditor, Row, Select, Skeleton, slider_default as Slider, Space, Spin, Splitter, Swiper, Switch, Tag, Toast, Typography, Upload, getRootConfigValue, useConfig };
|
package/dist/lib/colors.d.mts
CHANGED
|
@@ -3,10 +3,43 @@
|
|
|
3
3
|
* The shared preset colour palette. `Button` and `Tag` both express these via
|
|
4
4
|
* the `--btn-*` / `--tag-*` custom-property systems in `globals.css` (keyed off
|
|
5
5
|
* `data-color`), so the same word means the same hue on either component
|
|
6
|
-
* (#320).
|
|
6
|
+
* (#320).
|
|
7
|
+
*
|
|
8
|
+
* The names track **Tailwind's own palette names** (#342). They always did
|
|
9
|
+
* track Tailwind's *values* — every preset was a Tailwind hue, just spelled
|
|
10
|
+
* with antd's vocabulary — so four of them said one thing and rendered
|
|
11
|
+
* another: `magenta` rendered Tailwind's `fuchsia`, `geekblue` rendered
|
|
12
|
+
* `indigo`, `gold` rendered `amber`. Aligning the spelling makes the name and
|
|
13
|
+
* the value agree, and lets `globals.css` resolve each entry straight from the
|
|
14
|
+
* matching `--color-*` theme variable instead of a hand-copied hex.
|
|
15
|
+
*
|
|
16
|
+
* Keep this list in sync with the `[data-color='…']` rules there.
|
|
7
17
|
*/
|
|
8
|
-
declare const PRESET_COLORS: readonly ["blue", "purple", "cyan", "green", "
|
|
18
|
+
declare const PRESET_COLORS: readonly ["blue", "purple", "cyan", "green", "fuchsia", "pink", "red", "orange", "yellow", "indigo", "lime", "amber"];
|
|
9
19
|
type PresetColor = (typeof PRESET_COLORS)[number];
|
|
20
|
+
/**
|
|
21
|
+
* Pre-8.0 spellings, still accepted and still rendering exactly what they
|
|
22
|
+
* rendered before — they are aliases, not a behaviour change:
|
|
23
|
+
*
|
|
24
|
+
* | deprecated | renders | canonical spelling |
|
|
25
|
+
* | ---------- | ----------------- | ----------------------------- |
|
|
26
|
+
* | `magenta` | `--color-fuchsia-500` | `fuchsia` |
|
|
27
|
+
* | `geekblue` | `--color-indigo-500` | `indigo` |
|
|
28
|
+
* | `gold` | `--color-amber-500` | `amber` |
|
|
29
|
+
* | `volcano` | `--color-orange-600` | *(none — see below)* |
|
|
30
|
+
*
|
|
31
|
+
* `volcano` is the odd one out and has no canonical successor. Every other
|
|
32
|
+
* preset is a distinct hue at Tailwind's `500` step, but `volcano` is
|
|
33
|
+
* `orange-600` — the same hue as `orange`, one step darker. That makes it a
|
|
34
|
+
* lightness variant masquerading as a hue, so it doesn't belong in a list
|
|
35
|
+
* whose axis is "one entry per hue". It keeps rendering `orange-600` until the
|
|
36
|
+
* next major; callers who want that exact shade can set `--btn-bg`/`--tag-bg`
|
|
37
|
+
* directly.
|
|
38
|
+
*
|
|
39
|
+
* All four are removed in the next major.
|
|
40
|
+
*/
|
|
41
|
+
declare const DEPRECATED_PRESET_COLORS: readonly ["magenta", "geekblue", "gold", "volcano"];
|
|
42
|
+
type DeprecatedPresetColor = (typeof DEPRECATED_PRESET_COLORS)[number];
|
|
10
43
|
//#endregion
|
|
11
|
-
export { PRESET_COLORS, PresetColor };
|
|
44
|
+
export { DEPRECATED_PRESET_COLORS, DeprecatedPresetColor, PRESET_COLORS, PresetColor };
|
|
12
45
|
//# sourceMappingURL=colors.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colors.d.mts","names":[],"sources":["../../src/lib/colors.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"colors.d.mts","names":[],"sources":["../../src/lib/colors.ts"],"mappings":";;;;;;;;;;;;;;;;;cAgBa;KAeD,sBAAsB;;;;;;;;;;;;;;;;;;;;;;cAuBrB;KAOD,gCAAgC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../src/providers/config/config.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../src/providers/config/config.tsx"],"mappings":";;;UAoEU;EACR,QAAQ;EACR,SAAS;EACT,qBAAqB;EACrB,gBAAgB;EAChB;EACA,eAAe;EACf;EACA,QAAQ,MAAM;EACd,UAAU,MAAM;;cAGZ,WAAU,OAAA,QAAA,cAAA,eAAA,WAAA,cAAA,WAAA,OAAA,YAUb,0BAAK,IAAA"}
|
|
@@ -1,21 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { cn } from "../../utils.mjs";
|
|
3
3
|
import { Context, DEFAULT_LOCALE, useConfig } from "./context.mjs";
|
|
4
|
+
import { resolveDark, useSystemPrefersDark } from "./dark-mode.mjs";
|
|
4
5
|
import { registerRootConfig } from "./registry.mjs";
|
|
5
|
-
import { useCallback, useEffect, useMemo, useState
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
7
|
import { DirectionProvider } from "@radix-ui/react-direction";
|
|
7
8
|
import { jsx } from "react/jsx-runtime";
|
|
8
9
|
//#region src/providers/config/config.tsx
|
|
9
|
-
const DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
|
|
10
|
-
const subscribeToSystemColorScheme = (callback) => {
|
|
11
|
-
if (typeof window === "undefined") return () => {};
|
|
12
|
-
const mql = window.matchMedia(DARK_MEDIA_QUERY);
|
|
13
|
-
mql.addEventListener("change", callback);
|
|
14
|
-
return () => mql.removeEventListener("change", callback);
|
|
15
|
-
};
|
|
16
|
-
const getSystemPrefersDark = () => typeof window !== "undefined" && window.matchMedia(DARK_MEDIA_QUERY).matches;
|
|
17
|
-
const getServerSnapshot = () => false;
|
|
18
|
-
const useSystemPrefersDark = () => useSyncExternalStore(subscribeToSystemColorScheme, getSystemPrefersDark, getServerSnapshot);
|
|
19
10
|
const tokenToCssVar = {
|
|
20
11
|
colorPrimary: "--primary",
|
|
21
12
|
colorPrimaryForeground: "--primary-foreground",
|
|
@@ -36,26 +27,8 @@ const tokenToCssVar = {
|
|
|
36
27
|
colorInput: "--input",
|
|
37
28
|
colorRing: "--ring",
|
|
38
29
|
borderRadius: "--radius",
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
btnBackgroundActive: "--btn-bg-active",
|
|
42
|
-
btnBorder: "--btn-border",
|
|
43
|
-
btnForeground: "--btn-fg",
|
|
44
|
-
sidebar: "--sidebar",
|
|
45
|
-
sidebarForeground: "--sidebar-foreground",
|
|
46
|
-
sidebarPrimary: "--sidebar-primary",
|
|
47
|
-
sidebarPrimaryForeground: "--sidebar-primary-foreground",
|
|
48
|
-
sidebarAccent: "--sidebar-accent",
|
|
49
|
-
sidebarAccentForeground: "--sidebar-accent-foreground",
|
|
50
|
-
sidebarBorder: "--sidebar-border",
|
|
51
|
-
sidebarRing: "--sidebar-ring",
|
|
52
|
-
chart1: "--chart-1",
|
|
53
|
-
chart2: "--chart-2",
|
|
54
|
-
chart3: "--chart-3",
|
|
55
|
-
chart4: "--chart-4",
|
|
56
|
-
chart5: "--chart-5",
|
|
57
|
-
fontSans: "--font-sans",
|
|
58
|
-
fontMono: "--font-mono"
|
|
30
|
+
fontSans: "--ui-font-sans",
|
|
31
|
+
fontMono: "--ui-font-mono"
|
|
59
32
|
};
|
|
60
33
|
const buildCssVars = (token) => {
|
|
61
34
|
if (!token) return {};
|
|
@@ -92,7 +65,7 @@ const Config = ({ theme = {}, locale = {}, getContainer, componentSize, directio
|
|
|
92
65
|
...locale
|
|
93
66
|
}), [parentLocaleKey, localeKey]);
|
|
94
67
|
const systemPrefersDark = useSystemPrefersDark();
|
|
95
|
-
const isDarkActive = mergedTheme.dark
|
|
68
|
+
const isDarkActive = resolveDark(mergedTheme.dark, systemPrefersDark);
|
|
96
69
|
const cssVars = useMemo(() => buildCssVars(isDarkActive ? {
|
|
97
70
|
...mergedTheme.token,
|
|
98
71
|
...mergedTheme.darkToken
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../../../src/providers/config/config.tsx"],"sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n} from 'react';\n\nimport { DirectionProvider } from '@radix-ui/react-direction';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, DEFAULT_LOCALE, useConfig } from './context';\nimport { registerRootConfig } from './registry';\nimport type {\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n} from './types';\n\nconst DARK_MEDIA_QUERY = '(prefers-color-scheme: dark)';\n\nconst subscribeToSystemColorScheme = (callback: () => void) => {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const mql = window.matchMedia(DARK_MEDIA_QUERY);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n};\n\nconst getSystemPrefersDark = () =>\n typeof window !== 'undefined' && window.matchMedia(DARK_MEDIA_QUERY).matches;\n\n// No `window` during SSR, so there's no way to know the visitor's actual\n// preference before hydration — assume light (the conservative default)\n// and let useSyncExternalStore correct it client-side once matchMedia is\n// available, same flash-of-incorrect-guess tradeoff already accepted for\n// Sider's breakpoint prop elsewhere in this library.\nconst getServerSnapshot = () => false;\n\nconst useSystemPrefersDark = () =>\n useSyncExternalStore(\n subscribeToSystemColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n\n btnBackground: '--btn-bg',\n btnBackgroundHover: '--btn-bg-hover',\n btnBackgroundActive: '--btn-bg-active',\n btnBorder: '--btn-border',\n btnForeground: '--btn-fg',\n\n sidebar: '--sidebar',\n sidebarForeground: '--sidebar-foreground',\n sidebarPrimary: '--sidebar-primary',\n sidebarPrimaryForeground: '--sidebar-primary-foreground',\n sidebarAccent: '--sidebar-accent',\n sidebarAccentForeground: '--sidebar-accent-foreground',\n sidebarBorder: '--sidebar-border',\n sidebarRing: '--sidebar-ring',\n\n chart1: '--chart-1',\n chart2: '--chart-2',\n chart3: '--chart-3',\n chart4: '--chart-4',\n chart5: '--chart-5',\n\n fontSans: '--font-sans',\n fontMono: '--font-mono',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n locale?: Locale;\n getContainer?: () => HTMLElement;\n componentSize?: ComponentSize;\n direction?: 'ltr' | 'rtl';\n defaultProps?: DefaultProps;\n className?: string;\n style?: React.CSSProperties;\n children: React.ReactNode;\n}\n\nconst Config = ({\n theme = {},\n locale = {},\n getContainer,\n componentSize,\n direction,\n defaultProps,\n className,\n style,\n children,\n}: Props) => {\n const parent = useConfig();\n const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);\n\n // Only a Config with no Config ancestor of its own registers for the\n // imperative stack (Modal.*/Toast.*) to inherit — see registry.tsx.\n const isRoot = !parent.isConfigured;\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n const localeKey = JSON.stringify(locale);\n const parentLocaleKey = JSON.stringify(parent.locale);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n darkToken: {\n ...parent.theme.darkToken,\n ...theme.darkToken,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const mergedLocale = useMemo<Locale>(\n () => ({\n ...parent.locale,\n ...locale,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentLocaleKey, localeKey],\n );\n\n const systemPrefersDark = useSystemPrefersDark();\n const isDarkActive =\n mergedTheme.dark === 'system'\n ? systemPrefersDark\n : mergedTheme.dark === 'dark';\n\n // `darkToken` overrides individual keys of `token` (not a full swap)\n // only while dark mode is actually active.\n const cssVars = useMemo(\n () =>\n buildCssVars(\n isDarkActive\n ? { ...mergedTheme.token, ...mergedTheme.darkToken }\n : mergedTheme.token,\n ),\n [mergedTheme.token, mergedTheme.darkToken, isDarkActive],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper =\n hasCssVars || hasDarkMode || !!className || !!style || !!direction;\n\n // Resolution order: an explicit `getContainer` prop on this Config wins;\n // otherwise, if this Config renders its own themed wrapper below, that's\n // the default (portaled content appended into it still inherits its CSS\n // custom properties and matches the `.dark` selector — `display:\n // contents` only removes the element from the layout box tree, not from\n // the DOM/cascade); otherwise defer to the parent Config's resolution,\n // so an inner Config that only sets e.g. `locale` doesn't shadow an\n // outer one's theme container.\n const resolvedGetContainer = useCallback((): HTMLElement | undefined => {\n if (getContainer) {\n return getContainer();\n }\n if (needsWrapper && wrapperEl) {\n return wrapperEl;\n }\n return parent.getContainer();\n }, [getContainer, needsWrapper, wrapperEl, parent]);\n\n const resolvedComponentSize = componentSize ?? parent.componentSize;\n const resolvedDirection = direction ?? parent.direction;\n\n const defaultPropsKey = JSON.stringify(defaultProps);\n const parentDefaultPropsKey = JSON.stringify(parent.defaultProps);\n\n const mergedDefaultProps = useMemo<DefaultProps | undefined>(\n () =>\n parent.defaultProps || defaultProps\n ? { ...parent.defaultProps, ...defaultProps }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentDefaultPropsKey, defaultPropsKey],\n );\n\n const contextValue = useMemo(\n () => ({\n theme: mergedTheme,\n locale: mergedLocale,\n getContainer: resolvedGetContainer,\n componentSize: resolvedComponentSize,\n direction: resolvedDirection,\n defaultProps: mergedDefaultProps,\n isConfigured: true,\n }),\n [\n mergedTheme,\n mergedLocale,\n resolvedGetContainer,\n resolvedComponentSize,\n resolvedDirection,\n mergedDefaultProps,\n ],\n );\n\n useEffect(() => {\n if (!isRoot) {\n return;\n }\n return registerRootConfig(contextValue);\n }, [isRoot, contextValue]);\n\n const content = (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n ref={setWrapperEl}\n dir={direction}\n className={cn(isDarkActive && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n ...style,\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className/style\n // shouldn't be used here for anything that needs a real box\n // (padding, background, borders); Config's className/style are\n // meant for theming hooks (the `dark` class, arbitrary\n // selector hooks, custom properties), not layout — spread\n // after the caller's `style` so it can't be overridden into\n // something that reintroduces the layout-breaking wrapper this\n // was added to fix in the first place.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n\n // DirectionProvider is Radix's own mechanism (@radix-ui/react-direction)\n // for propagating `dir` to every primitive that calls its useDirection()\n // internally — Select, Menu (Dropdown/Menu), Accordion (Collapse),\n // RadioGroup, ScrollArea all pick up RTL automatically this way, no\n // per-component wiring needed. Only wraps when this Config's own\n // `direction` prop is set (not the merely-inherited resolved value) —\n // context already cascades on its own, so re-wrapping at every nested\n // Config that doesn't override direction would just be redundant.\n // Components with hardcoded LTR-only layout logic that Radix's Direction\n // context doesn't reach (Popover placement, Splitter, Sider, Drawer)\n // aren't covered by this and would need dedicated follow-up work.\n return direction ? (\n <DirectionProvider dir={direction}>{content}</DirectionProvider>\n ) : (\n content\n );\n};\n\nexport default Config;\nexport { useConfig, DEFAULT_LOCALE };\nexport type {\n Props,\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../../src/providers/config/config.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport { DirectionProvider } from '@radix-ui/react-direction';\n\nimport { cn } from '@repo/ui/utils';\n\nimport { Context, DEFAULT_LOCALE, useConfig } from './context';\nimport { resolveDark, useSystemPrefersDark } from './dark-mode';\nimport { registerRootConfig } from './registry';\nimport type {\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n} from './types';\n\nconst tokenToCssVar: Record<keyof ThemeToken, string> = {\n colorPrimary: '--primary',\n colorPrimaryForeground: '--primary-foreground',\n colorBackground: '--background',\n colorForeground: '--foreground',\n colorCard: '--card',\n colorCardForeground: '--card-foreground',\n colorPopover: '--popover',\n colorPopoverForeground: '--popover-foreground',\n colorSecondary: '--secondary',\n colorSecondaryForeground: '--secondary-foreground',\n colorMuted: '--muted',\n colorMutedForeground: '--muted-foreground',\n colorAccent: '--accent',\n colorAccentForeground: '--accent-foreground',\n colorDestructive: '--destructive',\n colorBorder: '--border',\n colorInput: '--input',\n colorRing: '--ring',\n borderRadius: '--radius',\n\n // Not `--font-sans`/`--font-mono`: those are `@theme inline` entries, so\n // Tailwind substitutes their value into each utility rather than referencing\n // them, and a wrapper-level override never applied. `globals.css` reads these\n // hooks ahead of the app's own font variable instead (#343).\n fontSans: '--ui-font-sans',\n fontMono: '--ui-font-mono',\n};\n\nconst buildCssVars = (token?: ThemeToken): React.CSSProperties => {\n if (!token) {\n return {};\n }\n\n const vars: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(token)) {\n if (value === undefined) {\n continue;\n }\n const cssVar = tokenToCssVar[key as keyof ThemeToken];\n if (cssVar) {\n vars[cssVar] = value;\n }\n }\n\n return vars as React.CSSProperties;\n};\n\ninterface Props {\n theme?: ThemeConfig;\n locale?: Locale;\n getContainer?: () => HTMLElement;\n componentSize?: ComponentSize;\n direction?: 'ltr' | 'rtl';\n defaultProps?: DefaultProps;\n className?: string;\n style?: React.CSSProperties;\n children: React.ReactNode;\n}\n\nconst Config = ({\n theme = {},\n locale = {},\n getContainer,\n componentSize,\n direction,\n defaultProps,\n className,\n style,\n children,\n}: Props) => {\n const parent = useConfig();\n const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);\n\n // Only a Config with no Config ancestor of its own registers for the\n // imperative stack (Modal.*/Toast.*) to inherit — see registry.tsx.\n const isRoot = !parent.isConfigured;\n\n // `theme` is typically a fresh inline object literal at the call site\n // (and `parent.theme` inherits the same problem from its own Config\n // ancestor), so depending on them directly meant mergedTheme/contextValue\n // got a new identity on every render regardless of whether the actual\n // values changed — every useConfig() consumer in the tree re-rendered on\n // every render of whatever's above this Config, which for the common\n // app-root usage is everything. Depend on a serialized key instead so\n // the memo only invalidates when the content actually changes; these\n // are small, string/boolean-only objects, so JSON.stringify is cheap.\n const themeKey = JSON.stringify(theme);\n const parentThemeKey = JSON.stringify(parent.theme);\n const localeKey = JSON.stringify(locale);\n const parentLocaleKey = JSON.stringify(parent.locale);\n\n const mergedTheme = useMemo<ThemeConfig>(\n () => ({\n ...parent.theme,\n ...theme,\n token: {\n ...parent.theme.token,\n ...theme.token,\n },\n darkToken: {\n ...parent.theme.darkToken,\n ...theme.darkToken,\n },\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentThemeKey, themeKey],\n );\n\n const mergedLocale = useMemo<Locale>(\n () => ({\n ...parent.locale,\n ...locale,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentLocaleKey, localeKey],\n );\n\n const systemPrefersDark = useSystemPrefersDark();\n const isDarkActive = resolveDark(mergedTheme.dark, systemPrefersDark);\n\n // `darkToken` overrides individual keys of `token` (not a full swap)\n // only while dark mode is actually active.\n const cssVars = useMemo(\n () =>\n buildCssVars(\n isDarkActive\n ? { ...mergedTheme.token, ...mergedTheme.darkToken }\n : mergedTheme.token,\n ),\n [mergedTheme.token, mergedTheme.darkToken, isDarkActive],\n );\n\n const hasCssVars = Object.keys(cssVars).length > 0;\n const hasDarkMode = mergedTheme.dark !== undefined;\n const needsWrapper =\n hasCssVars || hasDarkMode || !!className || !!style || !!direction;\n\n // Resolution order: an explicit `getContainer` prop on this Config wins;\n // otherwise, if this Config renders its own themed wrapper below, that's\n // the default (portaled content appended into it still inherits its CSS\n // custom properties and matches the `.dark` selector — `display:\n // contents` only removes the element from the layout box tree, not from\n // the DOM/cascade); otherwise defer to the parent Config's resolution,\n // so an inner Config that only sets e.g. `locale` doesn't shadow an\n // outer one's theme container.\n const resolvedGetContainer = useCallback((): HTMLElement | undefined => {\n if (getContainer) {\n return getContainer();\n }\n if (needsWrapper && wrapperEl) {\n return wrapperEl;\n }\n return parent.getContainer();\n }, [getContainer, needsWrapper, wrapperEl, parent]);\n\n const resolvedComponentSize = componentSize ?? parent.componentSize;\n const resolvedDirection = direction ?? parent.direction;\n\n const defaultPropsKey = JSON.stringify(defaultProps);\n const parentDefaultPropsKey = JSON.stringify(parent.defaultProps);\n\n const mergedDefaultProps = useMemo<DefaultProps | undefined>(\n () =>\n parent.defaultProps || defaultProps\n ? { ...parent.defaultProps, ...defaultProps }\n : undefined,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [parentDefaultPropsKey, defaultPropsKey],\n );\n\n const contextValue = useMemo(\n () => ({\n theme: mergedTheme,\n locale: mergedLocale,\n getContainer: resolvedGetContainer,\n componentSize: resolvedComponentSize,\n direction: resolvedDirection,\n defaultProps: mergedDefaultProps,\n isConfigured: true,\n }),\n [\n mergedTheme,\n mergedLocale,\n resolvedGetContainer,\n resolvedComponentSize,\n resolvedDirection,\n mergedDefaultProps,\n ],\n );\n\n useEffect(() => {\n if (!isRoot) {\n return;\n }\n return registerRootConfig(contextValue);\n }, [isRoot, contextValue]);\n\n const content = (\n <Context.Provider value={contextValue}>\n {needsWrapper ? (\n <div\n ref={setWrapperEl}\n dir={direction}\n className={cn(isDarkActive && 'dark', className)}\n style={{\n ...(hasCssVars ? cssVars : undefined),\n ...style,\n // Keeps this element out of the layout box tree — so it\n // doesn't intercept flex/grid child relationships or break\n // height chains like min-h-screen — while custom properties\n // and the `dark` class selector still cascade to children\n // normally (inheritance and box generation are separate\n // mechanisms in CSS). Note this means className/style\n // shouldn't be used here for anything that needs a real box\n // (padding, background, borders); Config's className/style are\n // meant for theming hooks (the `dark` class, arbitrary\n // selector hooks, custom properties), not layout — spread\n // after the caller's `style` so it can't be overridden into\n // something that reintroduces the layout-breaking wrapper this\n // was added to fix in the first place.\n display: 'contents',\n }}\n >\n {children}\n </div>\n ) : (\n children\n )}\n </Context.Provider>\n );\n\n // DirectionProvider is Radix's own mechanism (@radix-ui/react-direction)\n // for propagating `dir` to every primitive that calls its useDirection()\n // internally — Select, Menu (Dropdown/Menu), Accordion (Collapse),\n // RadioGroup, ScrollArea all pick up RTL automatically this way, no\n // per-component wiring needed. Only wraps when this Config's own\n // `direction` prop is set (not the merely-inherited resolved value) —\n // context already cascades on its own, so re-wrapping at every nested\n // Config that doesn't override direction would just be redundant.\n // Components with hardcoded LTR-only layout logic that Radix's Direction\n // context doesn't reach (Popover placement, Splitter, Sider, Drawer)\n // aren't covered by this and would need dedicated follow-up work.\n return direction ? (\n <DirectionProvider dir={direction}>{content}</DirectionProvider>\n ) : (\n content\n );\n};\n\nexport default Config;\nexport { useConfig, DEFAULT_LOCALE };\nexport type {\n Props,\n ComponentSize,\n DefaultProps,\n Locale,\n ThemeConfig,\n ThemeToken,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useConfig } from "./context.mjs";
|
|
3
|
+
import { useSyncExternalStore } from "react";
|
|
4
|
+
//#region src/providers/config/dark-mode.ts
|
|
5
|
+
const DARK_MEDIA_QUERY = "(prefers-color-scheme: dark)";
|
|
6
|
+
const subscribeToSystemColorScheme = (callback) => {
|
|
7
|
+
if (typeof window === "undefined") return () => {};
|
|
8
|
+
const mql = window.matchMedia(DARK_MEDIA_QUERY);
|
|
9
|
+
mql.addEventListener("change", callback);
|
|
10
|
+
return () => mql.removeEventListener("change", callback);
|
|
11
|
+
};
|
|
12
|
+
const getSystemPrefersDark = () => typeof window !== "undefined" && window.matchMedia(DARK_MEDIA_QUERY).matches;
|
|
13
|
+
const getServerSnapshot = () => false;
|
|
14
|
+
const useSystemPrefersDark = () => useSyncExternalStore(subscribeToSystemColorScheme, getSystemPrefersDark, getServerSnapshot);
|
|
15
|
+
/**
|
|
16
|
+
* Turns the three-valued `theme.dark` setting into the boolean that decides
|
|
17
|
+
* whether dark treatments actually apply. Kept as a pure function so `Config`
|
|
18
|
+
* can resolve the theme it is about to publish (its merged value isn't in
|
|
19
|
+
* context yet) while consumers resolve the one already in context, without the
|
|
20
|
+
* two drifting apart.
|
|
21
|
+
*/
|
|
22
|
+
const resolveDark = (dark, systemPrefersDark) => dark === "system" ? systemPrefersDark : dark === "dark";
|
|
23
|
+
/**
|
|
24
|
+
* Whether dark mode is active for the nearest `Config`, with `'system'`
|
|
25
|
+
* resolved against `prefers-color-scheme`.
|
|
26
|
+
*
|
|
27
|
+
* Internal to the package: components that render *CSS* can just use Tailwind's
|
|
28
|
+
* `dark:` variants against the `.dark` class `Config` puts on its wrapper. This
|
|
29
|
+
* exists for the ones that have to hand a light/dark choice to a third party in
|
|
30
|
+
* JS instead — `CodeEditor` picking a CodeMirror theme extension.
|
|
31
|
+
*/
|
|
32
|
+
const useIsDarkActive = () => {
|
|
33
|
+
const { theme } = useConfig();
|
|
34
|
+
return resolveDark(theme.dark, useSystemPrefersDark());
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { resolveDark, useIsDarkActive, useSystemPrefersDark };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=dark-mode.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dark-mode.mjs","names":[],"sources":["../../../src/providers/config/dark-mode.ts"],"sourcesContent":["'use client';\n\nimport { useSyncExternalStore } from 'react';\n\nimport { useConfig } from './context';\nimport type { ThemeConfig } from './types';\n\nconst DARK_MEDIA_QUERY = '(prefers-color-scheme: dark)';\n\nconst subscribeToSystemColorScheme = (callback: () => void) => {\n if (typeof window === 'undefined') {\n return () => {};\n }\n\n const mql = window.matchMedia(DARK_MEDIA_QUERY);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n};\n\nconst getSystemPrefersDark = () =>\n typeof window !== 'undefined' && window.matchMedia(DARK_MEDIA_QUERY).matches;\n\n// No `window` during SSR, so there's no way to know the visitor's actual\n// preference before hydration — assume light (the conservative default)\n// and let useSyncExternalStore correct it client-side once matchMedia is\n// available, same flash-of-incorrect-guess tradeoff already accepted for\n// Sider's breakpoint prop elsewhere in this library.\nconst getServerSnapshot = () => false;\n\nexport const useSystemPrefersDark = () =>\n useSyncExternalStore(\n subscribeToSystemColorScheme,\n getSystemPrefersDark,\n getServerSnapshot,\n );\n\n/**\n * Turns the three-valued `theme.dark` setting into the boolean that decides\n * whether dark treatments actually apply. Kept as a pure function so `Config`\n * can resolve the theme it is about to publish (its merged value isn't in\n * context yet) while consumers resolve the one already in context, without the\n * two drifting apart.\n */\nexport const resolveDark = (\n dark: ThemeConfig['dark'],\n systemPrefersDark: boolean,\n) => (dark === 'system' ? systemPrefersDark : dark === 'dark');\n\n/**\n * Whether dark mode is active for the nearest `Config`, with `'system'`\n * resolved against `prefers-color-scheme`.\n *\n * Internal to the package: components that render *CSS* can just use Tailwind's\n * `dark:` variants against the `.dark` class `Config` puts on its wrapper. This\n * exists for the ones that have to hand a light/dark choice to a third party in\n * JS instead — `CodeEditor` picking a CodeMirror theme extension.\n */\nexport const useIsDarkActive = () => {\n const { theme } = useConfig();\n\n return resolveDark(theme.dark, useSystemPrefersDark());\n};\n"],"mappings":";;;;AAOA,MAAM,mBAAmB;AAEzB,MAAM,gCAAgC,aAAyB;CAC7D,IAAI,OAAO,WAAW,aACpB,aAAa,CAAC;CAGhB,MAAM,MAAM,OAAO,WAAW,gBAAgB;CAC9C,IAAI,iBAAiB,UAAU,QAAQ;CACvC,aAAa,IAAI,oBAAoB,UAAU,QAAQ;AACzD;AAEA,MAAM,6BACJ,OAAO,WAAW,eAAe,OAAO,WAAW,gBAAgB,CAAC,CAAC;AAOvE,MAAM,0BAA0B;AAEhC,MAAa,6BACX,qBACE,8BACA,sBACA,iBACF;;;;;;;;AASF,MAAa,eACX,MACA,sBACI,SAAS,WAAW,oBAAoB,SAAS;;;;;;;;;;AAWvD,MAAa,wBAAwB;CACnC,MAAM,EAAE,UAAU,UAAU;CAE5B,OAAO,YAAY,MAAM,MAAM,qBAAqB,CAAC;AACvD"}
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
//#region src/providers/config/types.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Theme overrides applied as CSS custom properties on `Config`'s wrapper
|
|
4
|
+
* element, inherited by everything inside it.
|
|
5
|
+
*
|
|
6
|
+
* Every token here is verified to actually reach a component. That is worth
|
|
7
|
+
* stating because it did not use to be true: 19 of the previous 40 tokens were
|
|
8
|
+
* inert (#343). Three groups were dropped in 8.0 —
|
|
9
|
+
*
|
|
10
|
+
* - `btnBackground`/`btnBackgroundHover`/`btnBackgroundActive`/`btnBorder`/
|
|
11
|
+
* `btnForeground` — `globals.css` declares `--btn-*` on the button element
|
|
12
|
+
* itself (keyed off `data-color`), and an element's own declaration always
|
|
13
|
+
* beats an inherited one, so a wrapper value could never win. Re-theming a
|
|
14
|
+
* button is done through `colorPrimary`/`colorDestructive` for the semantic
|
|
15
|
+
* colours, or by overriding `--preset-color` in CSS for the preset hues.
|
|
16
|
+
* - `sidebar*` and `chart*` — no component in the library reads them, and the
|
|
17
|
+
* matching utilities (`bg-sidebar`, `bg-chart-1`) are never generated into
|
|
18
|
+
* the published stylesheet, so nothing consumed them. The underlying
|
|
19
|
+
* `--sidebar-*`/`--chart-*` custom properties are still declared in
|
|
20
|
+
* `globals.css` and remain overridable in plain CSS; only the inert token
|
|
21
|
+
* API is gone. They should come back alongside an actual Sidebar/Chart
|
|
22
|
+
* component.
|
|
23
|
+
*
|
|
24
|
+
* `fontSans`/`fontMono` were inert for a different reason and were fixed
|
|
25
|
+
* rather than removed — see below.
|
|
26
|
+
*/
|
|
2
27
|
interface ThemeToken {
|
|
3
28
|
colorPrimary?: string;
|
|
4
29
|
colorPrimaryForeground?: string;
|
|
@@ -19,24 +44,6 @@ interface ThemeToken {
|
|
|
19
44
|
colorInput?: string;
|
|
20
45
|
colorRing?: string;
|
|
21
46
|
borderRadius?: string;
|
|
22
|
-
btnBackground?: string;
|
|
23
|
-
btnBackgroundHover?: string;
|
|
24
|
-
btnBackgroundActive?: string;
|
|
25
|
-
btnBorder?: string;
|
|
26
|
-
btnForeground?: string;
|
|
27
|
-
sidebar?: string;
|
|
28
|
-
sidebarForeground?: string;
|
|
29
|
-
sidebarPrimary?: string;
|
|
30
|
-
sidebarPrimaryForeground?: string;
|
|
31
|
-
sidebarAccent?: string;
|
|
32
|
-
sidebarAccentForeground?: string;
|
|
33
|
-
sidebarBorder?: string;
|
|
34
|
-
sidebarRing?: string;
|
|
35
|
-
chart1?: string;
|
|
36
|
-
chart2?: string;
|
|
37
|
-
chart3?: string;
|
|
38
|
-
chart4?: string;
|
|
39
|
-
chart5?: string;
|
|
40
47
|
fontSans?: string;
|
|
41
48
|
fontMono?: string;
|
|
42
49
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/providers/config/types.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/providers/config/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;UAyBiB;EAEf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EASA;EAMA;EACA;;UAGe;EACf,QAAQ;EAKR,YAAY;EASZ;;UAQe;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;KAMU;KAYA,eAAe,eAAe;UAEzB;EACf,OAAO;EACP,QAAQ;EACR;EAUA,oBAAoB;EACpB,gBAAgB;EAChB,eAAe;EAKf"}
|