@mittwald/flow-react-components 0.2.0-alpha.450 → 0.2.0-alpha.452
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/CHANGELOG.md +10 -0
- package/dist/assets/doc-properties.json +72020 -69195
- package/dist/css/all.css +1 -1
- package/dist/js/_virtual/_.locale.json@bb0db7b5021f874310cbe5b6cc3d9cac.mjs +26 -0
- package/dist/js/_virtual/_.locale.json@bb0db7b5021f874310cbe5b6cc3d9cac.mjs.map +1 -0
- package/dist/js/components/src/components/List/hooks/useAriaAnnounceSearchState.mjs +2 -0
- package/dist/js/components/src/components/List/hooks/useAriaAnnounceSearchState.mjs.map +1 -1
- package/dist/js/components/src/components/Markdown/Markdown.mjs +5 -3
- package/dist/js/components/src/components/Markdown/Markdown.mjs.map +1 -1
- package/dist/js/components/src/components/MarkdownEditor/MarkdownEditor.mjs +74 -0
- package/dist/js/components/src/components/MarkdownEditor/MarkdownEditor.mjs.map +1 -0
- package/dist/js/components/src/components/MarkdownEditor/MarkdownEditor.module.scss.mjs +19 -0
- package/dist/js/components/src/components/MarkdownEditor/MarkdownEditor.module.scss.mjs.map +1 -0
- package/dist/js/components/src/components/MarkdownEditor/components/ModeButton.mjs +29 -0
- package/dist/js/components/src/components/MarkdownEditor/components/ModeButton.mjs.map +1 -0
- package/dist/js/components/src/components/MarkdownEditor/components/Toolbar.mjs +39 -0
- package/dist/js/components/src/components/MarkdownEditor/components/Toolbar.mjs.map +1 -0
- package/dist/js/components/src/components/MarkdownEditor/components/ToolbarButton.mjs +37 -0
- package/dist/js/components/src/components/MarkdownEditor/components/ToolbarButton.mjs.map +1 -0
- package/dist/js/components/src/components/MarkdownEditor/lib/handleKeyDown.mjs +72 -0
- package/dist/js/components/src/components/MarkdownEditor/lib/handleKeyDown.mjs.map +1 -0
- package/dist/js/components/src/components/MarkdownEditor/lib/insertAtCursor.mjs +152 -0
- package/dist/js/components/src/components/MarkdownEditor/lib/insertAtCursor.mjs.map +1 -0
- package/dist/js/components/src/components/NumberField/NumberField.mjs +1 -1
- package/dist/js/components/src/components/NumberField/NumberField.mjs.map +1 -1
- package/dist/js/components/src/components/TextFieldBase/TextFieldBase.mjs +1 -1
- package/dist/js/components/src/components/TextFieldBase/TextFieldBase.mjs.map +1 -1
- package/dist/js/components/src/components/propTypes/index.mjs +1 -0
- package/dist/js/components/src/components/propTypes/index.mjs.map +1 -1
- package/dist/js/components/src/integrations/react-hook-form/components/Field/Field.mjs +1 -0
- package/dist/js/components/src/integrations/react-hook-form/components/Field/Field.mjs.map +1 -1
- package/dist/js/default.mjs +1 -0
- package/dist/js/default.mjs.map +1 -1
- package/dist/js/flr-universal.mjs +2 -0
- package/dist/js/flr-universal.mjs.map +1 -1
- package/dist/types/components/Markdown/Markdown.d.ts +2 -1
- package/dist/types/components/Markdown/Markdown.d.ts.map +1 -1
- package/dist/types/components/MarkdownEditor/MarkdownEditor.d.ts +8 -0
- package/dist/types/components/MarkdownEditor/MarkdownEditor.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/components/ModeButton.d.ts +10 -0
- package/dist/types/components/MarkdownEditor/components/ModeButton.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/components/Toolbar.d.ts +14 -0
- package/dist/types/components/MarkdownEditor/components/Toolbar.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/components/ToolbarButton.d.ts +15 -0
- package/dist/types/components/MarkdownEditor/components/ToolbarButton.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/index.d.ts +3 -0
- package/dist/types/components/MarkdownEditor/index.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/lib/handleKeyDown.d.ts +3 -0
- package/dist/types/components/MarkdownEditor/lib/handleKeyDown.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/lib/handleKeyDown.test.d.ts +2 -0
- package/dist/types/components/MarkdownEditor/lib/handleKeyDown.test.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/lib/insertAtCursor.d.ts +4 -0
- package/dist/types/components/MarkdownEditor/lib/insertAtCursor.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/lib/insertAtCursor.test.d.ts +2 -0
- package/dist/types/components/MarkdownEditor/lib/insertAtCursor.test.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/stories/Default.stories.d.ts +13 -0
- package/dist/types/components/MarkdownEditor/stories/Default.stories.d.ts.map +1 -0
- package/dist/types/components/MarkdownEditor/view.d.ts +8 -0
- package/dist/types/components/MarkdownEditor/view.d.ts.map +1 -0
- package/dist/types/components/propTypes/index.d.ts +3 -1
- package/dist/types/components/propTypes/index.d.ts.map +1 -1
- package/dist/types/components/public.d.ts +1 -0
- package/dist/types/components/public.d.ts.map +1 -1
- package/dist/types/integrations/react-hook-form/components/Field/Field.d.ts.map +1 -1
- package/dist/types/lib/propsContext/propsContext.d.ts +2 -0
- package/dist/types/lib/propsContext/propsContext.d.ts.map +1 -1
- package/dist/types/views/MarkdownEditorView.d.ts +5 -0
- package/dist/types/views/MarkdownEditorView.d.ts.map +1 -0
- package/package.json +5 -4
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
const locales = {"de-DE": { "mode.editor": `Editor`,
|
|
4
|
+
"mode.preview": `Vorschau`,
|
|
5
|
+
"toolbar.bold": `Fett`,
|
|
6
|
+
"toolbar.code": `Code`,
|
|
7
|
+
"toolbar.italic": `Kursiv`,
|
|
8
|
+
"toolbar.link": `Link`,
|
|
9
|
+
"toolbar.orderedList": `Sortierte Liste`,
|
|
10
|
+
"toolbar.quote": `Zitat`,
|
|
11
|
+
"toolbar.strikeThrough": `Durchgestrichen`,
|
|
12
|
+
"toolbar.unorderedList": `Liste`,
|
|
13
|
+
},"en-US": { "mode.editor": `Editor`,
|
|
14
|
+
"mode.preview": `Preview`,
|
|
15
|
+
"toolbar.bold": `Bold`,
|
|
16
|
+
"toolbar.code": `Code`,
|
|
17
|
+
"toolbar.italic": `Italic`,
|
|
18
|
+
"toolbar.link": `Link`,
|
|
19
|
+
"toolbar.orderedList": `Ordered List`,
|
|
20
|
+
"toolbar.quote": `Quote`,
|
|
21
|
+
"toolbar.strikeThrough": `Strike through`,
|
|
22
|
+
"toolbar.unorderedList": `List`,
|
|
23
|
+
}};
|
|
24
|
+
|
|
25
|
+
export { locales as default };
|
|
26
|
+
//# sourceMappingURL=_.locale.json@bb0db7b5021f874310cbe5b6cc3d9cac.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_.locale.json@bb0db7b5021f874310cbe5b6cc3d9cac.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -69,6 +69,8 @@ import '../components/ListSummary/ListSummary.mjs';
|
|
|
69
69
|
import { useList } from './useList.mjs';
|
|
70
70
|
import '../List.mjs';
|
|
71
71
|
import 'react-markdown';
|
|
72
|
+
import 'remark-gfm';
|
|
73
|
+
import '../../MarkdownEditor/MarkdownEditor.mjs';
|
|
72
74
|
import '../../Message/Message.mjs';
|
|
73
75
|
import '../../MessageThread/MessageThread.mjs';
|
|
74
76
|
import '../../Modal/Modal.mjs';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAriaAnnounceSearchState.mjs","sources":["../../../../../../../src/components/List/hooks/useAriaAnnounceSearchState.ts"],"sourcesContent":["import { useLocalizedStringFormatter } from \"react-aria\";\nimport locales from \"../locales/*.locale.json\";\nimport { announce } from \"@react-aria/live-announcer\";\nimport { useList } from \"@/index/default\";\nimport { useDebounceCallback } from \"usehooks-ts\";\nimport { useEffect } from \"react\";\n\nconst announceDebounceMs = 600;\n\nexport const useAriaAnnounceSearchState = (): void => {\n const formatter = useLocalizedStringFormatter(locales);\n const list = useList();\n const debouncedAnnounce = useDebounceCallback(announce, announceDebounceMs);\n\n const searchTerm = list.search?.value;\n const resultCount = list.batches.getTotalItemsCount();\n const isLoading = list.loader.loaderState.useIsLoading();\n\n useEffect(() => {\n if (isLoading || !searchTerm) {\n debouncedAnnounce.cancel();\n return;\n }\n\n const text = formatter.format(\n resultCount > 0\n ? \"list.search.announce.result\"\n : \"list.search.announce.noResult\",\n {\n resultCount,\n searchTerm,\n },\n );\n\n debouncedAnnounce(text, \"polite\");\n }, [searchTerm, resultCount, isLoading]);\n};\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useAriaAnnounceSearchState.mjs","sources":["../../../../../../../src/components/List/hooks/useAriaAnnounceSearchState.ts"],"sourcesContent":["import { useLocalizedStringFormatter } from \"react-aria\";\nimport locales from \"../locales/*.locale.json\";\nimport { announce } from \"@react-aria/live-announcer\";\nimport { useList } from \"@/index/default\";\nimport { useDebounceCallback } from \"usehooks-ts\";\nimport { useEffect } from \"react\";\n\nconst announceDebounceMs = 600;\n\nexport const useAriaAnnounceSearchState = (): void => {\n const formatter = useLocalizedStringFormatter(locales);\n const list = useList();\n const debouncedAnnounce = useDebounceCallback(announce, announceDebounceMs);\n\n const searchTerm = list.search?.value;\n const resultCount = list.batches.getTotalItemsCount();\n const isLoading = list.loader.loaderState.useIsLoading();\n\n useEffect(() => {\n if (isLoading || !searchTerm) {\n debouncedAnnounce.cancel();\n return;\n }\n\n const text = formatter.format(\n resultCount > 0\n ? \"list.search.announce.result\"\n : \"list.search.announce.noResult\",\n {\n resultCount,\n searchTerm,\n },\n );\n\n debouncedAnnounce(text, \"polite\");\n }, [searchTerm, resultCount, isLoading]);\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,MAAM,kBAAqB,GAAA,GAAA;AAEpB,MAAM,6BAA6B,MAAY;AACpD,EAAM,MAAA,SAAA,GAAY,4BAA4B,OAAO,CAAA;AACrD,EAAA,MAAM,OAAO,OAAQ,EAAA;AACrB,EAAM,MAAA,iBAAA,GAAoB,mBAAoB,CAAA,QAAA,EAAU,kBAAkB,CAAA;AAE1E,EAAM,MAAA,UAAA,GAAa,KAAK,MAAQ,EAAA,KAAA;AAChC,EAAM,MAAA,WAAA,GAAc,IAAK,CAAA,OAAA,CAAQ,kBAAmB,EAAA;AACpD,EAAA,MAAM,SAAY,GAAA,IAAA,CAAK,MAAO,CAAA,WAAA,CAAY,YAAa,EAAA;AAEvD,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,SAAA,IAAa,CAAC,UAAY,EAAA;AAC5B,MAAA,iBAAA,CAAkB,MAAO,EAAA;AACzB,MAAA;AAAA;AAGF,IAAA,MAAM,OAAO,SAAU,CAAA,MAAA;AAAA,MACrB,WAAA,GAAc,IACV,6BACA,GAAA,+BAAA;AAAA,MACJ;AAAA,QACE,WAAA;AAAA,QACA;AAAA;AACF,KACF;AAEA,IAAA,iBAAA,CAAkB,MAAM,QAAQ,CAAA;AAAA,GAC/B,EAAA,CAAC,UAAY,EAAA,WAAA,EAAa,SAAS,CAAC,CAAA;AACzC;;;;"}
|
|
@@ -11,9 +11,11 @@ import { Children, isValidElement } from 'react';
|
|
|
11
11
|
import ReactMarkdown from 'react-markdown';
|
|
12
12
|
import styles from './Markdown.module.scss.mjs';
|
|
13
13
|
import { extractTextFromFirstChild } from '../../lib/react/remote.mjs';
|
|
14
|
+
import clsx from 'clsx';
|
|
15
|
+
import remarkGfm from 'remark-gfm';
|
|
14
16
|
|
|
15
17
|
const Markdown = (props) => {
|
|
16
|
-
const { children, color = "default", ...rest } = props;
|
|
18
|
+
const { children, color = "default", className, ...rest } = props;
|
|
17
19
|
const headingAndLinkColor = color === "default" ? "primary" : color;
|
|
18
20
|
const textColor = color === "default" ? void 0 : color;
|
|
19
21
|
const components = {
|
|
@@ -33,7 +35,7 @@ const Markdown = (props) => {
|
|
|
33
35
|
CodeBlock,
|
|
34
36
|
{
|
|
35
37
|
copyable: false,
|
|
36
|
-
color,
|
|
38
|
+
color: "dark",
|
|
37
39
|
language: isValidElement(preElementContent) && preElementContent.props.className ? preElementContent.props.className.replace("language-", "") : void 0,
|
|
38
40
|
code: String(
|
|
39
41
|
isValidElement(preElementContent) ? preElementContent.props.children : preElementContent
|
|
@@ -46,7 +48,7 @@ const Markdown = (props) => {
|
|
|
46
48
|
blockquote: (props2) => /* @__PURE__ */ jsx(Text, { color: textColor, children: /* @__PURE__ */ jsx("blockquote", { children: props2.children }) })
|
|
47
49
|
};
|
|
48
50
|
const textContent = extractTextFromFirstChild(children);
|
|
49
|
-
return /* @__PURE__ */ jsx("div", { className: styles.markdown, ...rest, children: /* @__PURE__ */ jsx(ReactMarkdown, { components, children: textContent }) });
|
|
51
|
+
return /* @__PURE__ */ jsx("div", { className: clsx(styles.markdown, className), ...rest, children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children: textContent }) });
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
export { Markdown, Markdown as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.mjs","sources":["../../../../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["import { CodeBlock } from \"@/components/CodeBlock\";\nimport { Heading } from \"@/components/Heading\";\nimport { InlineCode } from \"@/components/InlineCode\";\nimport { Link } from \"@/components/Link\";\nimport { Separator } from \"@/components/Separator\";\nimport { Text } from \"@/components/Text\";\nimport type { FC, ReactNode } from \"react\";\nimport React, { Children, isValidElement } from \"react\";\nimport type { Components, Options } from \"react-markdown\";\nimport ReactMarkdown from \"react-markdown\";\nimport styles from \"./Markdown.module.scss\";\nimport { extractTextFromFirstChild } from \"@/lib/react/remote\";\n\nexport interface MarkdownProps
|
|
1
|
+
{"version":3,"file":"Markdown.mjs","sources":["../../../../../../src/components/Markdown/Markdown.tsx"],"sourcesContent":["import { CodeBlock } from \"@/components/CodeBlock\";\nimport { Heading } from \"@/components/Heading\";\nimport { InlineCode } from \"@/components/InlineCode\";\nimport { Link } from \"@/components/Link\";\nimport { Separator } from \"@/components/Separator\";\nimport { Text } from \"@/components/Text\";\nimport type { CSSProperties, FC, ReactNode } from \"react\";\nimport React, { Children, isValidElement } from \"react\";\nimport type { Components, Options } from \"react-markdown\";\nimport ReactMarkdown from \"react-markdown\";\nimport styles from \"./Markdown.module.scss\";\nimport { extractTextFromFirstChild } from \"@/lib/react/remote\";\nimport type { PropsWithClassName } from \"@/lib/types/props\";\nimport clsx from \"clsx\";\nimport remarkGfm from \"remark-gfm\";\n\nexport interface MarkdownProps\n extends PropsWithClassName,\n Omit<Options, \"components\"> {\n /** The color schema of the markdown component. */\n color?: \"dark\" | \"light\" | \"default\";\n /** @internal */\n style?: CSSProperties;\n}\n\n/** @flr-generate all */\nexport const Markdown: FC<MarkdownProps> = (props) => {\n const { children, color = \"default\", className, ...rest } = props;\n\n const headingAndLinkColor = color === \"default\" ? \"primary\" : color;\n const textColor = color === \"default\" ? undefined : color;\n\n const components: Components = {\n a: (props) => (\n <Link target=\"_blank\" color={headingAndLinkColor} href={props.href}>\n {props.children}\n </Link>\n ),\n p: (props) => (\n <Text elementType=\"p\" color={textColor}>\n {props.children}\n </Text>\n ),\n code: (props) => <InlineCode color={color}>{props.children}</InlineCode>,\n h1: (props) => (\n <Heading level={1} color={headingAndLinkColor}>\n {props.children}\n </Heading>\n ),\n h2: (props) => (\n <Heading level={2} color={headingAndLinkColor}>\n {props.children}\n </Heading>\n ),\n h3: (props) => (\n <Heading level={3} color={headingAndLinkColor}>\n {props.children}\n </Heading>\n ),\n h4: (props) => (\n <Heading level={4} color={headingAndLinkColor}>\n {props.children}\n </Heading>\n ),\n h5: (props) => (\n <Heading level={5} color={headingAndLinkColor}>\n {props.children}\n </Heading>\n ),\n h6: (props) => (\n <Heading level={6} color={headingAndLinkColor}>\n {props.children}\n </Heading>\n ),\n hr: () => <Separator />,\n pre: (props) => {\n const preElementContent = Children.toArray(props.children)[0];\n\n return (\n <CodeBlock\n copyable={false}\n color=\"dark\"\n language={\n isValidElement<{ className?: string }>(preElementContent) &&\n preElementContent.props.className\n ? preElementContent.props.className.replace(\"language-\", \"\")\n : undefined\n }\n code={String(\n isValidElement<{ children: string }>(preElementContent)\n ? preElementContent.props.children\n : preElementContent,\n )}\n />\n );\n },\n ul: (props) => (\n <Text color={textColor}>\n <ul>{props.children as ReactNode}</ul>\n </Text>\n ),\n ol: (props) => (\n <Text color={textColor}>\n <ol>{props.children as ReactNode}</ol>\n </Text>\n ),\n blockquote: (props) => (\n <Text color={textColor}>\n <blockquote>{props.children}</blockquote>\n </Text>\n ),\n };\n\n const textContent = extractTextFromFirstChild(children);\n\n return (\n <div className={clsx(styles.markdown, className)} {...rest}>\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={components}>\n {textContent}\n </ReactMarkdown>\n </div>\n );\n};\n\nexport default Markdown;\n"],"names":["props"],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import { useState, useRef } from 'react';
|
|
5
|
+
import styles from './MarkdownEditor.module.scss.mjs';
|
|
6
|
+
import { Markdown } from '../Markdown/Markdown.mjs';
|
|
7
|
+
import { TextArea } from '../TextArea/TextArea.mjs';
|
|
8
|
+
import { Toolbar } from './components/Toolbar.mjs';
|
|
9
|
+
import clsx from 'clsx';
|
|
10
|
+
import { flowComponent } from '../../lib/componentFactory/flowComponent.mjs';
|
|
11
|
+
import { handleKeyDown } from './lib/handleKeyDown.mjs';
|
|
12
|
+
|
|
13
|
+
const MarkdownEditor = flowComponent("MarkdownEditor", (props) => {
|
|
14
|
+
const {
|
|
15
|
+
isDisabled,
|
|
16
|
+
children,
|
|
17
|
+
className,
|
|
18
|
+
value,
|
|
19
|
+
onChange,
|
|
20
|
+
rows,
|
|
21
|
+
autoResizeMaxRows,
|
|
22
|
+
...rest
|
|
23
|
+
} = props;
|
|
24
|
+
const [markdown, setMarkdown] = useState(value ?? "");
|
|
25
|
+
const [mode, setMode] = useState("editor");
|
|
26
|
+
const textAreaRef = useRef(null);
|
|
27
|
+
const rootClassName = clsx(
|
|
28
|
+
styles.markdownEditor,
|
|
29
|
+
className,
|
|
30
|
+
styles[`mode-${mode}`]
|
|
31
|
+
);
|
|
32
|
+
return /* @__PURE__ */ jsxs(
|
|
33
|
+
TextArea,
|
|
34
|
+
{
|
|
35
|
+
...rest,
|
|
36
|
+
isDisabled: isDisabled || mode === "preview",
|
|
37
|
+
className: rootClassName,
|
|
38
|
+
ref: textAreaRef,
|
|
39
|
+
value: markdown,
|
|
40
|
+
rows,
|
|
41
|
+
autoResizeMaxRows,
|
|
42
|
+
onChange: (v) => setMarkdown(v),
|
|
43
|
+
onKeyDown: (e) => handleKeyDown(e, textAreaRef, setMarkdown, onChange),
|
|
44
|
+
children: [
|
|
45
|
+
/* @__PURE__ */ jsx(
|
|
46
|
+
Toolbar,
|
|
47
|
+
{
|
|
48
|
+
markdown,
|
|
49
|
+
setMarkdown,
|
|
50
|
+
textAreaRef,
|
|
51
|
+
setMode,
|
|
52
|
+
mode,
|
|
53
|
+
isDisabled,
|
|
54
|
+
onChange
|
|
55
|
+
}
|
|
56
|
+
),
|
|
57
|
+
/* @__PURE__ */ jsx(
|
|
58
|
+
Markdown,
|
|
59
|
+
{
|
|
60
|
+
className: styles.markdown,
|
|
61
|
+
style: {
|
|
62
|
+
maxHeight: `calc(var(--line-height--m) * ${autoResizeMaxRows ?? rows} + (var(--form-control--padding-y) * 2))`
|
|
63
|
+
},
|
|
64
|
+
children: markdown
|
|
65
|
+
}
|
|
66
|
+
),
|
|
67
|
+
children
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export { MarkdownEditor, MarkdownEditor as default };
|
|
74
|
+
//# sourceMappingURL=MarkdownEditor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownEditor.mjs","sources":["../../../../../../src/components/MarkdownEditor/MarkdownEditor.tsx"],"sourcesContent":["import React, { useRef, useState } from \"react\";\nimport styles from \"./MarkdownEditor.module.scss\";\nimport { Markdown } from \"@/components/Markdown\";\nimport { TextArea, type TextAreaProps } from \"@/components/TextArea\";\nimport { Toolbar } from \"@/components/MarkdownEditor/components/Toolbar\";\nimport clsx from \"clsx\";\nimport { flowComponent } from \"@/lib/componentFactory/flowComponent\";\nimport { handleKeyDown } from \"@/components/MarkdownEditor/lib/handleKeyDown\";\n\nexport type MarkdownEditorMode = \"editor\" | \"preview\";\n\nexport type MarkdownEditorProps = TextAreaProps;\n\n/** @flr-generate all */\nexport const MarkdownEditor = flowComponent(\"MarkdownEditor\", (props) => {\n const {\n isDisabled,\n children,\n className,\n value,\n onChange,\n rows,\n autoResizeMaxRows,\n ...rest\n } = props;\n\n const [markdown, setMarkdown] = useState(value ?? \"\");\n const [mode, setMode] = useState<MarkdownEditorMode>(\"editor\");\n const textAreaRef = useRef<HTMLTextAreaElement>(null);\n\n const rootClassName = clsx(\n styles.markdownEditor,\n className,\n styles[`mode-${mode}`],\n );\n\n return (\n <TextArea\n {...rest}\n isDisabled={isDisabled || mode === \"preview\"}\n className={rootClassName}\n ref={textAreaRef}\n value={markdown}\n rows={rows}\n autoResizeMaxRows={autoResizeMaxRows}\n onChange={(v) => setMarkdown(v)}\n onKeyDown={(e) => handleKeyDown(e, textAreaRef, setMarkdown, onChange)}\n >\n <Toolbar\n markdown={markdown}\n setMarkdown={setMarkdown}\n textAreaRef={textAreaRef}\n setMode={setMode}\n mode={mode}\n isDisabled={isDisabled}\n onChange={onChange}\n />\n\n <Markdown\n className={styles.markdown}\n style={{\n maxHeight: `calc(var(--line-height--m) * ${autoResizeMaxRows ?? rows} + (var(--form-control--padding-y) * 2))`,\n }}\n >\n {markdown}\n </Markdown>\n\n {children}\n </TextArea>\n );\n});\n\nexport default MarkdownEditor;\n"],"names":[],"mappings":";;;;;;;;;;AAcO,MAAM,cAAiB,GAAA,aAAA,CAAc,gBAAkB,EAAA,CAAC,KAAU,KAAA;AACvE,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,iBAAA;AAAA,IACA,GAAG;AAAA,GACD,GAAA,KAAA;AAEJ,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,CAAI,GAAA,QAAA,CAAS,SAAS,EAAE,CAAA;AACpD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAA6B,QAAQ,CAAA;AAC7D,EAAM,MAAA,WAAA,GAAc,OAA4B,IAAI,CAAA;AAEpD,EAAA,MAAM,aAAgB,GAAA,IAAA;AAAA,IACpB,MAAO,CAAA,cAAA;AAAA,IACP,SAAA;AAAA,IACA,MAAA,CAAO,CAAQ,KAAA,EAAA,IAAI,CAAE,CAAA;AAAA,GACvB;AAEA,EACE,uBAAA,IAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACE,GAAG,IAAA;AAAA,MACJ,UAAA,EAAY,cAAc,IAAS,KAAA,SAAA;AAAA,MACnC,SAAW,EAAA,aAAA;AAAA,MACX,GAAK,EAAA,WAAA;AAAA,MACL,KAAO,EAAA,QAAA;AAAA,MACP,IAAA;AAAA,MACA,iBAAA;AAAA,MACA,QAAU,EAAA,CAAC,CAAM,KAAA,WAAA,CAAY,CAAC,CAAA;AAAA,MAC9B,WAAW,CAAC,CAAA,KAAM,cAAc,CAAG,EAAA,WAAA,EAAa,aAAa,QAAQ,CAAA;AAAA,MAErE,QAAA,EAAA;AAAA,wBAAA,GAAA;AAAA,UAAC,OAAA;AAAA,UAAA;AAAA,YACC,QAAA;AAAA,YACA,WAAA;AAAA,YACA,WAAA;AAAA,YACA,OAAA;AAAA,YACA,IAAA;AAAA,YACA,UAAA;AAAA,YACA;AAAA;AAAA,SACF;AAAA,wBAEA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,WAAW,MAAO,CAAA,QAAA;AAAA,YAClB,KAAO,EAAA;AAAA,cACL,SAAA,EAAW,CAAgC,6BAAA,EAAA,iBAAA,IAAqB,IAAI,CAAA,wCAAA;AAAA,aACtE;AAAA,YAEC,QAAA,EAAA;AAAA;AAAA,SACH;AAAA,QAEC;AAAA;AAAA;AAAA,GACH;AAEJ,CAAC;;;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
const markdownEditor = "flow--markdown-editor";
|
|
4
|
+
const toolbar = "flow--markdown-editor--toolbar";
|
|
5
|
+
const toolbarButtons = "flow--markdown-editor--toolbar-buttons";
|
|
6
|
+
const modeButton = "flow--markdown-editor--mode-button";
|
|
7
|
+
const markdown = "flow--markdown-editor--markdown";
|
|
8
|
+
const styles = {
|
|
9
|
+
markdownEditor: markdownEditor,
|
|
10
|
+
"mode-editor": "flow--markdown-editor--mode-editor",
|
|
11
|
+
"mode-preview": "flow--markdown-editor--mode-preview",
|
|
12
|
+
toolbar: toolbar,
|
|
13
|
+
toolbarButtons: toolbarButtons,
|
|
14
|
+
modeButton: modeButton,
|
|
15
|
+
markdown: markdown
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export { styles as default, markdown, markdownEditor, modeButton, toolbar, toolbarButtons };
|
|
19
|
+
//# sourceMappingURL=MarkdownEditor.module.scss.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MarkdownEditor.module.scss.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
import 'react';
|
|
5
|
+
import { Button } from '../../Button/Button.mjs';
|
|
6
|
+
import { useLocalizedStringFormatter } from 'react-aria';
|
|
7
|
+
import locales from '../../../../../_virtual/_.locale.json@bb0db7b5021f874310cbe5b6cc3d9cac.mjs';
|
|
8
|
+
import styles from '../MarkdownEditor.module.scss.mjs';
|
|
9
|
+
|
|
10
|
+
const ModeButton = (props) => {
|
|
11
|
+
const { setMode, mode, isDisabled } = props;
|
|
12
|
+
const stringFormatter = useLocalizedStringFormatter(locales);
|
|
13
|
+
const otherMode = mode === "editor" ? "preview" : "editor";
|
|
14
|
+
return /* @__PURE__ */ jsx(
|
|
15
|
+
Button,
|
|
16
|
+
{
|
|
17
|
+
isDisabled,
|
|
18
|
+
className: styles.modeButton,
|
|
19
|
+
size: "s",
|
|
20
|
+
variant: "plain",
|
|
21
|
+
color: "dark",
|
|
22
|
+
onPress: () => setMode(otherMode),
|
|
23
|
+
children: stringFormatter.format(`mode.${otherMode}`)
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { ModeButton };
|
|
29
|
+
//# sourceMappingURL=ModeButton.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModeButton.mjs","sources":["../../../../../../../src/components/MarkdownEditor/components/ModeButton.tsx"],"sourcesContent":["import React, { type FC } from \"react\";\nimport { Button } from \"@/components/Button\";\nimport { useLocalizedStringFormatter } from \"react-aria\";\nimport locales from \"../locales/*.locale.json\";\nimport type { MarkdownEditorMode } from \"@/components/MarkdownEditor/MarkdownEditor\";\nimport styles from \"../MarkdownEditor.module.scss\";\n\ninterface Props {\n mode: MarkdownEditorMode;\n setMode: (mode: MarkdownEditorMode) => void;\n isDisabled?: boolean;\n}\n\nexport const ModeButton: FC<Props> = (props) => {\n const { setMode, mode, isDisabled } = props;\n\n const stringFormatter = useLocalizedStringFormatter(locales);\n\n const otherMode = mode === \"editor\" ? \"preview\" : \"editor\";\n\n return (\n <Button\n isDisabled={isDisabled}\n className={styles.modeButton}\n size=\"s\"\n variant=\"plain\"\n color=\"dark\"\n onPress={() => setMode(otherMode)}\n >\n {stringFormatter.format(`mode.${otherMode}`)}\n </Button>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAaa,MAAA,UAAA,GAAwB,CAAC,KAAU,KAAA;AAC9C,EAAA,MAAM,EAAE,OAAA,EAAS,IAAM,EAAA,UAAA,EAAe,GAAA,KAAA;AAEtC,EAAM,MAAA,eAAA,GAAkB,4BAA4B,OAAO,CAAA;AAE3D,EAAM,MAAA,SAAA,GAAY,IAAS,KAAA,QAAA,GAAW,SAAY,GAAA,QAAA;AAElD,EACE,uBAAA,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,UAAA;AAAA,MACA,WAAW,MAAO,CAAA,UAAA;AAAA,MAClB,IAAK,EAAA,GAAA;AAAA,MACL,OAAQ,EAAA,OAAA;AAAA,MACR,KAAM,EAAA,MAAA;AAAA,MACN,OAAA,EAAS,MAAM,OAAA,CAAQ,SAAS,CAAA;AAAA,MAE/B,QAAgB,EAAA,eAAA,CAAA,MAAA,CAAO,CAAQ,KAAA,EAAA,SAAS,CAAE,CAAA;AAAA;AAAA,GAC7C;AAEJ;;;;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
import 'react';
|
|
5
|
+
import styles from '../MarkdownEditor.module.scss.mjs';
|
|
6
|
+
import { Icon } from '../../Icon/Icon.mjs';
|
|
7
|
+
import { IconBold, IconItalic, IconStrikethrough, IconQuoteFilled, IconList, IconListNumbers } from '@tabler/icons-react';
|
|
8
|
+
import '../../../lib/viewComponentContext/viewComponentContext.mjs';
|
|
9
|
+
import { IconCode } from '../../Icon/components/icons/IconCode.mjs';
|
|
10
|
+
import { IconLink } from '../../Icon/components/icons/IconLink.mjs';
|
|
11
|
+
import { ToolbarButton } from './ToolbarButton.mjs';
|
|
12
|
+
import { ModeButton } from './ModeButton.mjs';
|
|
13
|
+
|
|
14
|
+
const Toolbar = (props) => {
|
|
15
|
+
const { setMode, ...rest } = props;
|
|
16
|
+
return /* @__PURE__ */ jsxs("header", { className: styles.toolbar, role: "toolbar", children: [
|
|
17
|
+
/* @__PURE__ */ jsxs("div", { className: styles.toolbarButtons, children: [
|
|
18
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "bold", children: /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(IconBold, {}) }) }),
|
|
19
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "italic", children: /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(IconItalic, {}) }) }),
|
|
20
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "strikeThrough", children: /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(IconStrikethrough, {}) }) }),
|
|
21
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "quote", children: /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(IconQuoteFilled, {}) }) }),
|
|
22
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "code", children: /* @__PURE__ */ jsx(IconCode, {}) }),
|
|
23
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "link", children: /* @__PURE__ */ jsx(IconLink, {}) }),
|
|
24
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "unorderedList", children: /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(IconList, {}) }) }),
|
|
25
|
+
/* @__PURE__ */ jsx(ToolbarButton, { ...rest, type: "orderedList", children: /* @__PURE__ */ jsx(Icon, { children: /* @__PURE__ */ jsx(IconListNumbers, {}) }) })
|
|
26
|
+
] }),
|
|
27
|
+
/* @__PURE__ */ jsx(
|
|
28
|
+
ModeButton,
|
|
29
|
+
{
|
|
30
|
+
setMode,
|
|
31
|
+
mode: props.mode,
|
|
32
|
+
isDisabled: props.isDisabled
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
] });
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { Toolbar };
|
|
39
|
+
//# sourceMappingURL=Toolbar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toolbar.mjs","sources":["../../../../../../../src/components/MarkdownEditor/components/Toolbar.tsx"],"sourcesContent":["import React, { type FC, type RefObject } from \"react\";\nimport styles from \"@/components/MarkdownEditor/MarkdownEditor.module.scss\";\nimport { Icon } from \"@/components/Icon\";\nimport {\n IconBold,\n IconItalic,\n IconList,\n IconListNumbers,\n IconQuoteFilled,\n IconStrikethrough,\n} from \"@tabler/icons-react\";\nimport { IconCode, IconLink } from \"@/components/Icon/components/icons\";\nimport { ToolbarButton } from \"@/components/MarkdownEditor/components/ToolbarButton\";\nimport type { MarkdownEditorMode } from \"@/components/MarkdownEditor/MarkdownEditor\";\nimport { ModeButton } from \"@/components/MarkdownEditor/components/ModeButton\";\n\ninterface Props {\n markdown: string;\n setMarkdown: (markdown: string) => void;\n textAreaRef: RefObject<HTMLTextAreaElement | null>;\n setMode: (mode: MarkdownEditorMode) => void;\n mode: MarkdownEditorMode;\n isDisabled?: boolean;\n onChange?: (markdown: string) => void;\n}\n\nexport const Toolbar: FC<Props> = (props) => {\n const { setMode, ...rest } = props;\n\n return (\n <header className={styles.toolbar} role=\"toolbar\">\n <div className={styles.toolbarButtons}>\n <ToolbarButton {...rest} type=\"bold\">\n <Icon>\n <IconBold />\n </Icon>\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"italic\">\n <Icon>\n <IconItalic />\n </Icon>\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"strikeThrough\">\n <Icon>\n <IconStrikethrough />\n </Icon>\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"quote\">\n <Icon>\n <IconQuoteFilled />\n </Icon>\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"code\">\n <IconCode />\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"link\">\n <IconLink />\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"unorderedList\">\n <Icon>\n <IconList />\n </Icon>\n </ToolbarButton>\n\n <ToolbarButton {...rest} type=\"orderedList\">\n <Icon>\n <IconListNumbers />\n </Icon>\n </ToolbarButton>\n </div>\n\n <ModeButton\n setMode={setMode}\n mode={props.mode}\n isDisabled={props.isDisabled}\n />\n </header>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;AA0Ba,MAAA,OAAA,GAAqB,CAAC,KAAU,KAAA;AAC3C,EAAA,MAAM,EAAE,OAAA,EAAS,GAAG,IAAA,EAAS,GAAA,KAAA;AAE7B,EAAA,4BACG,QAAO,EAAA,EAAA,SAAA,EAAW,MAAO,CAAA,OAAA,EAAS,MAAK,SACtC,EAAA,QAAA,EAAA;AAAA,oBAAC,IAAA,CAAA,KAAA,EAAA,EAAI,SAAW,EAAA,MAAA,CAAO,cACrB,EAAA,QAAA,EAAA;AAAA,sBAAC,GAAA,CAAA,aAAA,EAAA,EAAe,GAAG,IAAA,EAAM,IAAK,EAAA,MAAA,EAC5B,8BAAC,IACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,QAAS,EAAA,EAAA,CAAA,EACZ,CACF,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,aAAe,EAAA,EAAA,GAAG,IAAM,EAAA,IAAA,EAAK,QAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,IACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,UAAW,EAAA,EAAA,CAAA,EACd,CACF,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,aAAe,EAAA,EAAA,GAAG,IAAM,EAAA,IAAA,EAAK,eAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,IACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,iBAAkB,EAAA,EAAA,CAAA,EACrB,CACF,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,aAAe,EAAA,EAAA,GAAG,IAAM,EAAA,IAAA,EAAK,OAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,IACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,eAAgB,EAAA,EAAA,CAAA,EACnB,CACF,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,iBAAe,GAAG,IAAA,EAAM,MAAK,MAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,YAAS,CACZ,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,iBAAe,GAAG,IAAA,EAAM,MAAK,MAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,YAAS,CACZ,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,aAAe,EAAA,EAAA,GAAG,IAAM,EAAA,IAAA,EAAK,eAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,IACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,QAAS,EAAA,EAAA,CAAA,EACZ,CACF,EAAA,CAAA;AAAA,sBAEA,GAAA,CAAC,aAAe,EAAA,EAAA,GAAG,IAAM,EAAA,IAAA,EAAK,aAC5B,EAAA,QAAA,kBAAA,GAAA,CAAC,IACC,EAAA,EAAA,QAAA,kBAAA,GAAA,CAAC,eAAgB,EAAA,EAAA,CAAA,EACnB,CACF,EAAA;AAAA,KACF,EAAA,CAAA;AAAA,oBAEA,GAAA;AAAA,MAAC,UAAA;AAAA,MAAA;AAAA,QACC,OAAA;AAAA,QACA,MAAM,KAAM,CAAA,IAAA;AAAA,QACZ,YAAY,KAAM,CAAA;AAAA;AAAA;AACpB,GACF,EAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
import 'react';
|
|
5
|
+
import { insertAtCursor } from '../lib/insertAtCursor.mjs';
|
|
6
|
+
import { Button } from '../../Button/Button.mjs';
|
|
7
|
+
import { useLocalizedStringFormatter } from 'react-aria';
|
|
8
|
+
import locales from '../../../../../_virtual/_.locale.json@bb0db7b5021f874310cbe5b6cc3d9cac.mjs';
|
|
9
|
+
|
|
10
|
+
const ToolbarButton = (props) => {
|
|
11
|
+
const {
|
|
12
|
+
markdown,
|
|
13
|
+
setMarkdown,
|
|
14
|
+
textAreaRef,
|
|
15
|
+
children,
|
|
16
|
+
isDisabled,
|
|
17
|
+
type,
|
|
18
|
+
onChange,
|
|
19
|
+
mode
|
|
20
|
+
} = props;
|
|
21
|
+
const stringFormatter = useLocalizedStringFormatter(locales);
|
|
22
|
+
return /* @__PURE__ */ jsx(
|
|
23
|
+
Button,
|
|
24
|
+
{
|
|
25
|
+
isDisabled: isDisabled || mode === "preview",
|
|
26
|
+
"aria-label": stringFormatter.format(`toolbar.${type}`),
|
|
27
|
+
size: "s",
|
|
28
|
+
variant: "plain",
|
|
29
|
+
color: "dark",
|
|
30
|
+
onPress: () => insertAtCursor(markdown, setMarkdown, textAreaRef, type, onChange),
|
|
31
|
+
children
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { ToolbarButton };
|
|
37
|
+
//# sourceMappingURL=ToolbarButton.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ToolbarButton.mjs","sources":["../../../../../../../src/components/MarkdownEditor/components/ToolbarButton.tsx"],"sourcesContent":["import React, { type FC, type PropsWithChildren, type RefObject } from \"react\";\nimport {\n insertAtCursor,\n type InsertType,\n} from \"@/components/MarkdownEditor/lib/insertAtCursor\";\nimport { Button } from \"@/components/Button\";\nimport { useLocalizedStringFormatter } from \"react-aria\";\nimport locales from \"../locales/*.locale.json\";\nimport type { MarkdownEditorMode } from \"@/components/MarkdownEditor/MarkdownEditor\";\n\ninterface Props extends PropsWithChildren {\n markdown: string;\n setMarkdown: (markdown: string) => void;\n textAreaRef: RefObject<HTMLTextAreaElement | null>;\n isDisabled?: boolean;\n type: InsertType;\n onChange?: (markdown: string) => void;\n mode: MarkdownEditorMode;\n}\n\nexport const ToolbarButton: FC<Props> = (props) => {\n const {\n markdown,\n setMarkdown,\n textAreaRef,\n children,\n isDisabled,\n type,\n onChange,\n mode,\n } = props;\n\n const stringFormatter = useLocalizedStringFormatter(locales);\n\n return (\n <Button\n isDisabled={isDisabled || mode === \"preview\"}\n aria-label={stringFormatter.format(`toolbar.${type}`)}\n size=\"s\"\n variant=\"plain\"\n color=\"dark\"\n onPress={() =>\n insertAtCursor(markdown, setMarkdown, textAreaRef, type, onChange)\n }\n >\n {children}\n </Button>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAoBa,MAAA,aAAA,GAA2B,CAAC,KAAU,KAAA;AACjD,EAAM,MAAA;AAAA,IACJ,QAAA;AAAA,IACA,WAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,UAAA;AAAA,IACA,IAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACE,GAAA,KAAA;AAEJ,EAAM,MAAA,eAAA,GAAkB,4BAA4B,OAAO,CAAA;AAE3D,EACE,uBAAA,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,UAAA,EAAY,cAAc,IAAS,KAAA,SAAA;AAAA,MACnC,YAAY,EAAA,eAAA,CAAgB,MAAO,CAAA,CAAA,QAAA,EAAW,IAAI,CAAE,CAAA,CAAA;AAAA,MACpD,IAAK,EAAA,GAAA;AAAA,MACL,OAAQ,EAAA,OAAA;AAAA,MACR,KAAM,EAAA,MAAA;AAAA,MACN,SAAS,MACP,cAAA,CAAe,UAAU,WAAa,EAAA,WAAA,EAAa,MAAM,QAAQ,CAAA;AAAA,MAGlE;AAAA;AAAA,GACH;AAEJ;;;;"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
const scrollToCursor = (textarea) => {
|
|
4
|
+
const { selectionStart } = textarea;
|
|
5
|
+
const lineHeight = parseInt(
|
|
6
|
+
getComputedStyle(textarea).lineHeight || "20",
|
|
7
|
+
10
|
|
8
|
+
);
|
|
9
|
+
const lines = textarea.value.slice(0, selectionStart).split("\n").length;
|
|
10
|
+
textarea.scrollTop = (lines - 1) * lineHeight;
|
|
11
|
+
};
|
|
12
|
+
const handleKeyDown = (e, textAreaRef, setMarkdown, onChange) => {
|
|
13
|
+
if (e.key !== "Enter") return;
|
|
14
|
+
const textarea = textAreaRef.current;
|
|
15
|
+
if (!textarea) return;
|
|
16
|
+
const start = textarea.selectionStart;
|
|
17
|
+
const end = textarea.selectionEnd;
|
|
18
|
+
const value = textarea.value;
|
|
19
|
+
const before = value.slice(0, start);
|
|
20
|
+
const after = value.slice(end);
|
|
21
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
22
|
+
const currentLine = before.slice(lineStart);
|
|
23
|
+
const orderedMatch = currentLine.match(/^(\s*)(\d+)\.\s+/);
|
|
24
|
+
const unorderedMatch = currentLine.match(/^(\s*)([-*+])\s+/);
|
|
25
|
+
if ((orderedMatch || unorderedMatch) && currentLine.trim().match(/^([-*+]|\d+\.)$/)) {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
const newText = value.slice(0, lineStart) + "\n" + after;
|
|
28
|
+
setMarkdown(newText);
|
|
29
|
+
if (onChange) {
|
|
30
|
+
onChange(newText);
|
|
31
|
+
}
|
|
32
|
+
requestAnimationFrame(() => {
|
|
33
|
+
textarea.selectionStart = textarea.selectionEnd = lineStart + 1;
|
|
34
|
+
scrollToCursor(textarea);
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (orderedMatch) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
const indent = orderedMatch[1];
|
|
41
|
+
const nextNum = parseInt(orderedMatch[2] ?? "", 10) + 1;
|
|
42
|
+
const insert = `
|
|
43
|
+
${indent}${nextNum}. `;
|
|
44
|
+
const newText = before + insert + after;
|
|
45
|
+
setMarkdown(newText);
|
|
46
|
+
if (onChange) {
|
|
47
|
+
onChange(newText);
|
|
48
|
+
}
|
|
49
|
+
requestAnimationFrame(() => {
|
|
50
|
+
textarea.selectionStart = textarea.selectionEnd = start + insert.length;
|
|
51
|
+
scrollToCursor(textarea);
|
|
52
|
+
});
|
|
53
|
+
} else if (unorderedMatch) {
|
|
54
|
+
e.preventDefault();
|
|
55
|
+
const indent = unorderedMatch[1];
|
|
56
|
+
const bullet = unorderedMatch[2];
|
|
57
|
+
const insert = `
|
|
58
|
+
${indent}${bullet} `;
|
|
59
|
+
const newText = before + insert + after;
|
|
60
|
+
setMarkdown(newText);
|
|
61
|
+
if (onChange) {
|
|
62
|
+
onChange(newText);
|
|
63
|
+
}
|
|
64
|
+
requestAnimationFrame(() => {
|
|
65
|
+
textarea.selectionStart = textarea.selectionEnd = start + insert.length;
|
|
66
|
+
scrollToCursor(textarea);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export { handleKeyDown };
|
|
72
|
+
//# sourceMappingURL=handleKeyDown.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handleKeyDown.mjs","sources":["../../../../../../../src/components/MarkdownEditor/lib/handleKeyDown.ts"],"sourcesContent":["import type { KeyboardEvent, RefObject } from \"react\";\n\nconst scrollToCursor = (textarea: HTMLTextAreaElement) => {\n const { selectionStart } = textarea;\n const lineHeight = parseInt(\n getComputedStyle(textarea).lineHeight || \"20\",\n 10,\n );\n const lines = textarea.value.slice(0, selectionStart).split(\"\\n\").length;\n textarea.scrollTop = (lines - 1) * lineHeight;\n};\n\nexport const handleKeyDown = (\n e: KeyboardEvent,\n textAreaRef: RefObject<HTMLTextAreaElement | null>,\n setMarkdown: (markdown: string) => void,\n onChange?: (markdown: string) => void,\n) => {\n if (e.key !== \"Enter\") return;\n\n const textarea = textAreaRef.current;\n\n if (!textarea) return;\n\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const value = textarea.value;\n\n const before = value.slice(0, start);\n const after = value.slice(end);\n const lineStart = before.lastIndexOf(\"\\n\") + 1;\n const currentLine = before.slice(lineStart);\n\n const orderedMatch = currentLine.match(/^(\\s*)(\\d+)\\.\\s+/);\n const unorderedMatch = currentLine.match(/^(\\s*)([-*+])\\s+/);\n\n if (\n (orderedMatch || unorderedMatch) &&\n currentLine.trim().match(/^([-*+]|\\d+\\.)$/)\n ) {\n e.preventDefault();\n const newText = value.slice(0, lineStart) + \"\\n\" + after;\n\n setMarkdown(newText);\n\n if (onChange) {\n onChange(newText);\n }\n\n requestAnimationFrame(() => {\n textarea.selectionStart = textarea.selectionEnd = lineStart + 1;\n scrollToCursor(textarea);\n });\n return;\n }\n\n if (orderedMatch) {\n e.preventDefault();\n const indent = orderedMatch[1];\n const nextNum = parseInt(orderedMatch[2] ?? \"\", 10) + 1;\n const insert = `\\n${indent}${nextNum}. `;\n\n const newText = before + insert + after;\n\n setMarkdown(newText);\n\n if (onChange) {\n onChange(newText);\n }\n\n requestAnimationFrame(() => {\n textarea.selectionStart = textarea.selectionEnd = start + insert.length;\n scrollToCursor(textarea);\n });\n } else if (unorderedMatch) {\n e.preventDefault();\n const indent = unorderedMatch[1];\n const bullet = unorderedMatch[2];\n const insert = `\\n${indent}${bullet} `;\n\n const newText = before + insert + after;\n\n setMarkdown(newText);\n\n if (onChange) {\n onChange(newText);\n }\n\n requestAnimationFrame(() => {\n textarea.selectionStart = textarea.selectionEnd = start + insert.length;\n scrollToCursor(textarea);\n });\n }\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
const markdownSyntax = {
|
|
4
|
+
bold: { before: "**", after: "**", toggleable: true },
|
|
5
|
+
italic: { before: "_", after: "_", toggleable: true },
|
|
6
|
+
strikeThrough: { before: "~~", after: "~~", toggleable: true },
|
|
7
|
+
quote: { before: "> " },
|
|
8
|
+
code: { before: "`", after: "`", toggleable: true },
|
|
9
|
+
link: { before: "[", after: "](https://)" },
|
|
10
|
+
unorderedList: { before: "- " },
|
|
11
|
+
orderedList: { before: "1. " }
|
|
12
|
+
};
|
|
13
|
+
const getLineStart = (text, pos) => {
|
|
14
|
+
const lastNewline = text.lastIndexOf("\n", pos - 1);
|
|
15
|
+
return lastNewline === -1 ? 0 : lastNewline + 1;
|
|
16
|
+
};
|
|
17
|
+
const getLineEnd = (text, pos) => {
|
|
18
|
+
const nextNewline = text.indexOf("\n", pos);
|
|
19
|
+
return nextNewline === -1 ? text.length : nextNewline;
|
|
20
|
+
};
|
|
21
|
+
const insertAtCursor = (markdown, setMarkdown, textAreaRef, type, onChange) => {
|
|
22
|
+
const textarea = textAreaRef.current;
|
|
23
|
+
if (!textarea) return;
|
|
24
|
+
const { before, after = "", toggleable = false } = markdownSyntax[type];
|
|
25
|
+
const start = textarea.selectionStart;
|
|
26
|
+
const end = textarea.selectionEnd;
|
|
27
|
+
const selectedText = markdown.substring(start, end);
|
|
28
|
+
const lines = selectedText.split("\n");
|
|
29
|
+
let newText = markdown;
|
|
30
|
+
let selectionStart = start;
|
|
31
|
+
let selectionEnd = end;
|
|
32
|
+
if (type === "code" && selectedText.includes("\n")) {
|
|
33
|
+
newText = markdown.substring(0, start) + "```\n" + selectedText + "\n```\n" + markdown.substring(end);
|
|
34
|
+
selectionStart = start + 4;
|
|
35
|
+
selectionEnd = selectionStart + selectedText.length;
|
|
36
|
+
} else if (type === "orderedList") {
|
|
37
|
+
if (selectedText) {
|
|
38
|
+
const numbered = lines.map((line, i) => `${i + 1}. ${line}`).join("\n");
|
|
39
|
+
newText = markdown.substring(0, start) + numbered + markdown.substring(end);
|
|
40
|
+
selectionStart = start;
|
|
41
|
+
selectionEnd = start + numbered.length;
|
|
42
|
+
} else {
|
|
43
|
+
const lineStart = getLineStart(markdown, start);
|
|
44
|
+
const lineEnd = getLineEnd(markdown, start);
|
|
45
|
+
const numberedLine = `1. ${markdown.substring(lineStart, lineEnd)}`;
|
|
46
|
+
newText = markdown.substring(0, lineStart) + before + markdown.substring(lineStart);
|
|
47
|
+
selectionStart = lineStart + numberedLine.length;
|
|
48
|
+
selectionEnd = selectionStart;
|
|
49
|
+
}
|
|
50
|
+
} else if (type === "unorderedList") {
|
|
51
|
+
if (selectedText) {
|
|
52
|
+
const bulleted = lines.map((line) => `${before}${line}`).join("\n");
|
|
53
|
+
newText = markdown.substring(0, start) + bulleted + markdown.substring(end);
|
|
54
|
+
selectionStart = start;
|
|
55
|
+
selectionEnd = start + bulleted.length;
|
|
56
|
+
} else {
|
|
57
|
+
const lineStart = getLineStart(markdown, start);
|
|
58
|
+
const lineEnd = getLineEnd(markdown, start);
|
|
59
|
+
const bulletedLine = `- ${markdown.substring(lineStart, lineEnd)}`;
|
|
60
|
+
newText = markdown.substring(0, lineStart) + before + markdown.substring(lineStart);
|
|
61
|
+
selectionStart = lineStart + bulletedLine.length;
|
|
62
|
+
selectionEnd = selectionStart;
|
|
63
|
+
}
|
|
64
|
+
} else if (type === "quote") {
|
|
65
|
+
if (selectedText) {
|
|
66
|
+
const quoted = lines.map((line) => `${before}${line}`).join("\n");
|
|
67
|
+
newText = markdown.substring(0, start) + quoted + markdown.substring(end);
|
|
68
|
+
selectionStart = start;
|
|
69
|
+
selectionEnd = start + quoted.length;
|
|
70
|
+
} else {
|
|
71
|
+
const quoteLine = `
|
|
72
|
+
${before} `;
|
|
73
|
+
newText = markdown.substring(0, start) + quoteLine + markdown.substring(end);
|
|
74
|
+
selectionStart = start + quoteLine.length;
|
|
75
|
+
selectionEnd = selectionStart;
|
|
76
|
+
}
|
|
77
|
+
} else if (toggleable) {
|
|
78
|
+
const prefix = markdown.substring(start - before.length, start);
|
|
79
|
+
const suffix = markdown.substring(end, end + after.length);
|
|
80
|
+
const isSurrounded = prefix === before && suffix === after;
|
|
81
|
+
const isWrappedInside = selectedText.startsWith(before) && selectedText.endsWith(after);
|
|
82
|
+
if (isSurrounded) {
|
|
83
|
+
newText = markdown.substring(0, start - before.length) + selectedText + markdown.substring(end + after.length);
|
|
84
|
+
selectionStart = start - before.length;
|
|
85
|
+
selectionEnd = selectionStart + selectedText.length;
|
|
86
|
+
} else if (isWrappedInside) {
|
|
87
|
+
const unwrapped = selectedText.slice(
|
|
88
|
+
before.length,
|
|
89
|
+
selectedText.length - after.length
|
|
90
|
+
);
|
|
91
|
+
newText = markdown.substring(0, start) + unwrapped + markdown.substring(end);
|
|
92
|
+
selectionStart = start;
|
|
93
|
+
selectionEnd = start + unwrapped.length;
|
|
94
|
+
} else {
|
|
95
|
+
newText = markdown.substring(0, start) + before + selectedText + after + markdown.substring(end);
|
|
96
|
+
if (selectedText.length === 0) {
|
|
97
|
+
selectionStart = start + before.length;
|
|
98
|
+
selectionEnd = selectionStart;
|
|
99
|
+
} else {
|
|
100
|
+
selectionStart = start + before.length;
|
|
101
|
+
selectionEnd = selectionStart + selectedText.length;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else if (type === "link") {
|
|
105
|
+
let linkText = "";
|
|
106
|
+
let linkUrl = "";
|
|
107
|
+
let inserted = "";
|
|
108
|
+
let cursorOffsetStart = 0;
|
|
109
|
+
const isValidUrl = (str) => {
|
|
110
|
+
try {
|
|
111
|
+
new URL(str);
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
if (selectedText) {
|
|
118
|
+
if (isValidUrl(selectedText)) {
|
|
119
|
+
linkUrl = selectedText;
|
|
120
|
+
inserted = `[](${linkUrl})`;
|
|
121
|
+
cursorOffsetStart = start + 1;
|
|
122
|
+
} else {
|
|
123
|
+
linkText = selectedText;
|
|
124
|
+
inserted = `[${linkText}]()`;
|
|
125
|
+
cursorOffsetStart = start + inserted.indexOf("](") + 2;
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
inserted = `[](https://)`;
|
|
129
|
+
cursorOffsetStart = start + 1;
|
|
130
|
+
}
|
|
131
|
+
newText = markdown.substring(0, start) + inserted + markdown.substring(end);
|
|
132
|
+
selectionStart = selectionEnd = cursorOffsetStart;
|
|
133
|
+
} else {
|
|
134
|
+
newText = markdown.substring(0, start) + before + selectedText + after + markdown.substring(end);
|
|
135
|
+
if (selectedText.length === 0) {
|
|
136
|
+
selectionStart = start + before.length;
|
|
137
|
+
selectionEnd = selectionStart;
|
|
138
|
+
} else {
|
|
139
|
+
selectionStart = start + before.length;
|
|
140
|
+
selectionEnd = selectionStart + selectedText.length;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
setMarkdown(newText);
|
|
144
|
+
if (onChange) onChange(newText);
|
|
145
|
+
requestAnimationFrame(() => {
|
|
146
|
+
textarea.setSelectionRange(selectionStart, selectionEnd);
|
|
147
|
+
textarea.focus();
|
|
148
|
+
});
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
export { insertAtCursor };
|
|
152
|
+
//# sourceMappingURL=insertAtCursor.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insertAtCursor.mjs","sources":["../../../../../../../src/components/MarkdownEditor/lib/insertAtCursor.ts"],"sourcesContent":["import type { RefObject } from \"react\";\n\nexport type InsertType =\n | \"bold\"\n | \"italic\"\n | \"strikeThrough\"\n | \"quote\"\n | \"code\"\n | \"link\"\n | \"unorderedList\"\n | \"orderedList\";\n\nconst markdownSyntax: Record<\n InsertType,\n { before: string; after?: string; toggleable?: boolean }\n> = {\n bold: { before: \"**\", after: \"**\", toggleable: true },\n italic: { before: \"_\", after: \"_\", toggleable: true },\n strikeThrough: { before: \"~~\", after: \"~~\", toggleable: true },\n quote: { before: \"> \" },\n code: { before: \"`\", after: \"`\", toggleable: true },\n link: { before: \"[\", after: \"](https://)\" },\n unorderedList: { before: \"- \" },\n orderedList: { before: \"1. \" },\n};\n\nconst getLineStart = (text: string, pos: number) => {\n const lastNewline = text.lastIndexOf(\"\\n\", pos - 1);\n return lastNewline === -1 ? 0 : lastNewline + 1;\n};\n\nconst getLineEnd = (text: string, pos: number) => {\n const nextNewline = text.indexOf(\"\\n\", pos);\n return nextNewline === -1 ? text.length : nextNewline;\n};\n\nexport const insertAtCursor = (\n markdown: string,\n setMarkdown: (markdown: string) => void,\n textAreaRef: RefObject<HTMLTextAreaElement | null>,\n type: InsertType,\n onChange?: (markdown: string) => void,\n) => {\n const textarea = textAreaRef.current;\n if (!textarea) return;\n\n const { before, after = \"\", toggleable = false } = markdownSyntax[type];\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const selectedText = markdown.substring(start, end);\n const lines = selectedText.split(\"\\n\");\n\n let newText = markdown;\n let selectionStart = start;\n let selectionEnd = end;\n\n if (type === \"code\" && selectedText.includes(\"\\n\")) {\n newText =\n markdown.substring(0, start) +\n \"```\\n\" +\n selectedText +\n \"\\n```\\n\" +\n markdown.substring(end);\n selectionStart = start + 4;\n selectionEnd = selectionStart + selectedText.length;\n } else if (type === \"orderedList\") {\n if (selectedText) {\n const numbered = lines.map((line, i) => `${i + 1}. ${line}`).join(\"\\n\");\n newText =\n markdown.substring(0, start) + numbered + markdown.substring(end);\n selectionStart = start;\n selectionEnd = start + numbered.length;\n } else {\n const lineStart = getLineStart(markdown, start);\n const lineEnd = getLineEnd(markdown, start);\n const numberedLine = `1. ${markdown.substring(lineStart, lineEnd)}`;\n\n newText =\n markdown.substring(0, lineStart) +\n before +\n markdown.substring(lineStart);\n selectionStart = lineStart + numberedLine.length;\n selectionEnd = selectionStart;\n }\n } else if (type === \"unorderedList\") {\n if (selectedText) {\n const bulleted = lines.map((line) => `${before}${line}`).join(\"\\n\");\n newText =\n markdown.substring(0, start) + bulleted + markdown.substring(end);\n selectionStart = start;\n selectionEnd = start + bulleted.length;\n } else {\n const lineStart = getLineStart(markdown, start);\n const lineEnd = getLineEnd(markdown, start);\n const bulletedLine = `- ${markdown.substring(lineStart, lineEnd)}`;\n\n newText =\n markdown.substring(0, lineStart) +\n before +\n markdown.substring(lineStart);\n selectionStart = lineStart + bulletedLine.length;\n selectionEnd = selectionStart;\n }\n } else if (type === \"quote\") {\n if (selectedText) {\n const quoted = lines.map((line) => `${before}${line}`).join(\"\\n\");\n newText = markdown.substring(0, start) + quoted + markdown.substring(end);\n selectionStart = start;\n selectionEnd = start + quoted.length;\n } else {\n const quoteLine = `\\n${before} `;\n newText =\n markdown.substring(0, start) + quoteLine + markdown.substring(end);\n selectionStart = start + quoteLine.length;\n selectionEnd = selectionStart;\n }\n } else if (toggleable) {\n const prefix = markdown.substring(start - before.length, start);\n const suffix = markdown.substring(end, end + after.length);\n const isSurrounded = prefix === before && suffix === after;\n const isWrappedInside =\n selectedText.startsWith(before) && selectedText.endsWith(after);\n\n if (isSurrounded) {\n // Remove external wrapping (not selected)\n newText =\n markdown.substring(0, start - before.length) +\n selectedText +\n markdown.substring(end + after.length);\n selectionStart = start - before.length;\n selectionEnd = selectionStart + selectedText.length;\n } else if (isWrappedInside) {\n // Remove internal wrapping (selected)\n const unwrapped = selectedText.slice(\n before.length,\n selectedText.length - after.length,\n );\n newText =\n markdown.substring(0, start) + unwrapped + markdown.substring(end);\n selectionStart = start;\n selectionEnd = start + unwrapped.length;\n } else {\n // Add wrapping\n newText =\n markdown.substring(0, start) +\n before +\n selectedText +\n after +\n markdown.substring(end);\n\n if (selectedText.length === 0) {\n selectionStart = start + before.length;\n selectionEnd = selectionStart;\n } else {\n selectionStart = start + before.length;\n selectionEnd = selectionStart + selectedText.length;\n }\n }\n } else if (type === \"link\") {\n let linkText = \"\";\n let linkUrl = \"\";\n let inserted = \"\";\n let cursorOffsetStart = 0;\n\n const isValidUrl = (str: string): boolean => {\n try {\n new URL(str);\n return true;\n } catch {\n return false;\n }\n };\n\n if (selectedText) {\n if (isValidUrl(selectedText)) {\n linkUrl = selectedText;\n inserted = `[](${linkUrl})`;\n cursorOffsetStart = start + 1;\n } else {\n linkText = selectedText;\n inserted = `[${linkText}]()`;\n cursorOffsetStart = start + inserted.indexOf(\"](\") + 2;\n }\n } else {\n inserted = `[](https://)`;\n cursorOffsetStart = start + 1;\n }\n\n newText = markdown.substring(0, start) + inserted + markdown.substring(end);\n selectionStart = selectionEnd = cursorOffsetStart;\n } else {\n // Fallback for non-toggleable, inline syntax\n newText =\n markdown.substring(0, start) +\n before +\n selectedText +\n after +\n markdown.substring(end);\n\n if (selectedText.length === 0) {\n // No text selected – place cursor between syntax\n selectionStart = start + before.length;\n selectionEnd = selectionStart;\n } else {\n // Keep selection\n selectionStart = start + before.length;\n selectionEnd = selectionStart + selectedText.length;\n }\n }\n\n setMarkdown(newText);\n if (onChange) onChange(newText);\n\n requestAnimationFrame(() => {\n textarea.setSelectionRange(selectionStart, selectionEnd);\n textarea.focus();\n });\n};\n"],"names":[],"mappings":"AAYA,MAAM,cAGF,GAAA;AAAA,EACF,MAAM,EAAE,MAAA,EAAQ,MAAM,KAAO,EAAA,IAAA,EAAM,YAAY,IAAK,EAAA;AAAA,EACpD,QAAQ,EAAE,MAAA,EAAQ,KAAK,KAAO,EAAA,GAAA,EAAK,YAAY,IAAK,EAAA;AAAA,EACpD,eAAe,EAAE,MAAA,EAAQ,MAAM,KAAO,EAAA,IAAA,EAAM,YAAY,IAAK,EAAA;AAAA,EAC7D,KAAA,EAAO,EAAE,MAAA,EAAQ,IAAK,EAAA;AAAA,EACtB,MAAM,EAAE,MAAA,EAAQ,KAAK,KAAO,EAAA,GAAA,EAAK,YAAY,IAAK,EAAA;AAAA,EAClD,IAAM,EAAA,EAAE,MAAQ,EAAA,GAAA,EAAK,OAAO,aAAc,EAAA;AAAA,EAC1C,aAAA,EAAe,EAAE,MAAA,EAAQ,IAAK,EAAA;AAAA,EAC9B,WAAA,EAAa,EAAE,MAAA,EAAQ,KAAM;AAC/B,CAAA;AAEA,MAAM,YAAA,GAAe,CAAC,IAAA,EAAc,GAAgB,KAAA;AAClD,EAAA,MAAM,WAAc,GAAA,IAAA,CAAK,WAAY,CAAA,IAAA,EAAM,MAAM,CAAC,CAAA;AAClD,EAAO,OAAA,WAAA,KAAgB,EAAK,GAAA,CAAA,GAAI,WAAc,GAAA,CAAA;AAChD,CAAA;AAEA,MAAM,UAAA,GAAa,CAAC,IAAA,EAAc,GAAgB,KAAA;AAChD,EAAA,MAAM,WAAc,GAAA,IAAA,CAAK,OAAQ,CAAA,IAAA,EAAM,GAAG,CAAA;AAC1C,EAAO,OAAA,WAAA,KAAgB,EAAK,GAAA,IAAA,CAAK,MAAS,GAAA,WAAA;AAC5C,CAAA;AAEO,MAAM,iBAAiB,CAC5B,QAAA,EACA,WACA,EAAA,WAAA,EACA,MACA,QACG,KAAA;AACH,EAAA,MAAM,WAAW,WAAY,CAAA,OAAA;AAC7B,EAAA,IAAI,CAAC,QAAU,EAAA;AAEf,EAAM,MAAA,EAAE,QAAQ,KAAQ,GAAA,EAAA,EAAI,aAAa,KAAM,EAAA,GAAI,eAAe,IAAI,CAAA;AACtE,EAAA,MAAM,QAAQ,QAAS,CAAA,cAAA;AACvB,EAAA,MAAM,MAAM,QAAS,CAAA,YAAA;AACrB,EAAA,MAAM,YAAe,GAAA,QAAA,CAAS,SAAU,CAAA,KAAA,EAAO,GAAG,CAAA;AAClD,EAAM,MAAA,KAAA,GAAQ,YAAa,CAAA,KAAA,CAAM,IAAI,CAAA;AAErC,EAAA,IAAI,OAAU,GAAA,QAAA;AACd,EAAA,IAAI,cAAiB,GAAA,KAAA;AACrB,EAAA,IAAI,YAAe,GAAA,GAAA;AAEnB,EAAA,IAAI,IAAS,KAAA,MAAA,IAAU,YAAa,CAAA,QAAA,CAAS,IAAI,CAAG,EAAA;AAClD,IACE,OAAA,GAAA,QAAA,CAAS,SAAU,CAAA,CAAA,EAAG,KAAK,CAAA,GAC3B,UACA,YACA,GAAA,SAAA,GACA,QAAS,CAAA,SAAA,CAAU,GAAG,CAAA;AACxB,IAAA,cAAA,GAAiB,KAAQ,GAAA,CAAA;AACzB,IAAA,YAAA,GAAe,iBAAiB,YAAa,CAAA,MAAA;AAAA,GAC/C,MAAA,IAAW,SAAS,aAAe,EAAA;AACjC,IAAA,IAAI,YAAc,EAAA;AAChB,MAAA,MAAM,QAAW,GAAA,KAAA,CAAM,GAAI,CAAA,CAAC,MAAM,CAAM,KAAA,CAAA,EAAG,CAAI,GAAA,CAAC,CAAK,EAAA,EAAA,IAAI,CAAE,CAAA,CAAA,CAAE,KAAK,IAAI,CAAA;AACtE,MACE,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,KAAK,IAAI,QAAW,GAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AAClE,MAAiB,cAAA,GAAA,KAAA;AACjB,MAAA,YAAA,GAAe,QAAQ,QAAS,CAAA,MAAA;AAAA,KAC3B,MAAA;AACL,MAAM,MAAA,SAAA,GAAY,YAAa,CAAA,QAAA,EAAU,KAAK,CAAA;AAC9C,MAAM,MAAA,OAAA,GAAU,UAAW,CAAA,QAAA,EAAU,KAAK,CAAA;AAC1C,MAAA,MAAM,eAAe,CAAM,GAAA,EAAA,QAAA,CAAS,SAAU,CAAA,SAAA,EAAW,OAAO,CAAC,CAAA,CAAA;AAEjE,MACE,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,SAAS,IAC/B,MACA,GAAA,QAAA,CAAS,UAAU,SAAS,CAAA;AAC9B,MAAA,cAAA,GAAiB,YAAY,YAAa,CAAA,MAAA;AAC1C,MAAe,YAAA,GAAA,cAAA;AAAA;AACjB,GACF,MAAA,IAAW,SAAS,eAAiB,EAAA;AACnC,IAAA,IAAI,YAAc,EAAA;AAChB,MAAA,MAAM,QAAW,GAAA,KAAA,CAAM,GAAI,CAAA,CAAC,IAAS,KAAA,CAAA,EAAG,MAAM,CAAA,EAAG,IAAI,CAAA,CAAE,CAAE,CAAA,IAAA,CAAK,IAAI,CAAA;AAClE,MACE,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,KAAK,IAAI,QAAW,GAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AAClE,MAAiB,cAAA,GAAA,KAAA;AACjB,MAAA,YAAA,GAAe,QAAQ,QAAS,CAAA,MAAA;AAAA,KAC3B,MAAA;AACL,MAAM,MAAA,SAAA,GAAY,YAAa,CAAA,QAAA,EAAU,KAAK,CAAA;AAC9C,MAAM,MAAA,OAAA,GAAU,UAAW,CAAA,QAAA,EAAU,KAAK,CAAA;AAC1C,MAAA,MAAM,eAAe,CAAK,EAAA,EAAA,QAAA,CAAS,SAAU,CAAA,SAAA,EAAW,OAAO,CAAC,CAAA,CAAA;AAEhE,MACE,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,SAAS,IAC/B,MACA,GAAA,QAAA,CAAS,UAAU,SAAS,CAAA;AAC9B,MAAA,cAAA,GAAiB,YAAY,YAAa,CAAA,MAAA;AAC1C,MAAe,YAAA,GAAA,cAAA;AAAA;AACjB,GACF,MAAA,IAAW,SAAS,OAAS,EAAA;AAC3B,IAAA,IAAI,YAAc,EAAA;AAChB,MAAA,MAAM,MAAS,GAAA,KAAA,CAAM,GAAI,CAAA,CAAC,IAAS,KAAA,CAAA,EAAG,MAAM,CAAA,EAAG,IAAI,CAAA,CAAE,CAAE,CAAA,IAAA,CAAK,IAAI,CAAA;AAChE,MAAU,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,KAAK,IAAI,MAAS,GAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AACxE,MAAiB,cAAA,GAAA,KAAA;AACjB,MAAA,YAAA,GAAe,QAAQ,MAAO,CAAA,MAAA;AAAA,KACzB,MAAA;AACL,MAAA,MAAM,SAAY,GAAA;AAAA,EAAK,MAAM,CAAA,CAAA,CAAA;AAC7B,MACE,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,KAAK,IAAI,SAAY,GAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AACnE,MAAA,cAAA,GAAiB,QAAQ,SAAU,CAAA,MAAA;AACnC,MAAe,YAAA,GAAA,cAAA;AAAA;AACjB,aACS,UAAY,EAAA;AACrB,IAAA,MAAM,SAAS,QAAS,CAAA,SAAA,CAAU,KAAQ,GAAA,MAAA,CAAO,QAAQ,KAAK,CAAA;AAC9D,IAAA,MAAM,SAAS,QAAS,CAAA,SAAA,CAAU,GAAK,EAAA,GAAA,GAAM,MAAM,MAAM,CAAA;AACzD,IAAM,MAAA,YAAA,GAAe,MAAW,KAAA,MAAA,IAAU,MAAW,KAAA,KAAA;AACrD,IAAA,MAAM,kBACJ,YAAa,CAAA,UAAA,CAAW,MAAM,CAAK,IAAA,YAAA,CAAa,SAAS,KAAK,CAAA;AAEhE,IAAA,IAAI,YAAc,EAAA;AAEhB,MAAA,OAAA,GACE,QAAS,CAAA,SAAA,CAAU,CAAG,EAAA,KAAA,GAAQ,MAAO,CAAA,MAAM,CAC3C,GAAA,YAAA,GACA,QAAS,CAAA,SAAA,CAAU,GAAM,GAAA,KAAA,CAAM,MAAM,CAAA;AACvC,MAAA,cAAA,GAAiB,QAAQ,MAAO,CAAA,MAAA;AAChC,MAAA,YAAA,GAAe,iBAAiB,YAAa,CAAA,MAAA;AAAA,eACpC,eAAiB,EAAA;AAE1B,MAAA,MAAM,YAAY,YAAa,CAAA,KAAA;AAAA,QAC7B,MAAO,CAAA,MAAA;AAAA,QACP,YAAA,CAAa,SAAS,KAAM,CAAA;AAAA,OAC9B;AACA,MACE,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,KAAK,IAAI,SAAY,GAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AACnE,MAAiB,cAAA,GAAA,KAAA;AACjB,MAAA,YAAA,GAAe,QAAQ,SAAU,CAAA,MAAA;AAAA,KAC5B,MAAA;AAEL,MACE,OAAA,GAAA,QAAA,CAAS,SAAU,CAAA,CAAA,EAAG,KAAK,CAAA,GAC3B,SACA,YACA,GAAA,KAAA,GACA,QAAS,CAAA,SAAA,CAAU,GAAG,CAAA;AAExB,MAAI,IAAA,YAAA,CAAa,WAAW,CAAG,EAAA;AAC7B,QAAA,cAAA,GAAiB,QAAQ,MAAO,CAAA,MAAA;AAChC,QAAe,YAAA,GAAA,cAAA;AAAA,OACV,MAAA;AACL,QAAA,cAAA,GAAiB,QAAQ,MAAO,CAAA,MAAA;AAChC,QAAA,YAAA,GAAe,iBAAiB,YAAa,CAAA,MAAA;AAAA;AAC/C;AACF,GACF,MAAA,IAAW,SAAS,MAAQ,EAAA;AAC1B,IAAA,IAAI,QAAW,GAAA,EAAA;AACf,IAAA,IAAI,OAAU,GAAA,EAAA;AACd,IAAA,IAAI,QAAW,GAAA,EAAA;AACf,IAAA,IAAI,iBAAoB,GAAA,CAAA;AAExB,IAAM,MAAA,UAAA,GAAa,CAAC,GAAyB,KAAA;AAC3C,MAAI,IAAA;AACF,QAAA,IAAI,IAAI,GAAG,CAAA;AACX,QAAO,OAAA,IAAA;AAAA,OACD,CAAA,MAAA;AACN,QAAO,OAAA,KAAA;AAAA;AACT,KACF;AAEA,IAAA,IAAI,YAAc,EAAA;AAChB,MAAI,IAAA,UAAA,CAAW,YAAY,CAAG,EAAA;AAC5B,QAAU,OAAA,GAAA,YAAA;AACV,QAAA,QAAA,GAAW,MAAM,OAAO,CAAA,CAAA,CAAA;AACxB,QAAA,iBAAA,GAAoB,KAAQ,GAAA,CAAA;AAAA,OACvB,MAAA;AACL,QAAW,QAAA,GAAA,YAAA;AACX,QAAA,QAAA,GAAW,IAAI,QAAQ,CAAA,GAAA,CAAA;AACvB,QAAA,iBAAA,GAAoB,KAAQ,GAAA,QAAA,CAAS,OAAQ,CAAA,IAAI,CAAI,GAAA,CAAA;AAAA;AACvD,KACK,MAAA;AACL,MAAW,QAAA,GAAA,CAAA,YAAA,CAAA;AACX,MAAA,iBAAA,GAAoB,KAAQ,GAAA,CAAA;AAAA;AAG9B,IAAU,OAAA,GAAA,QAAA,CAAS,UAAU,CAAG,EAAA,KAAK,IAAI,QAAW,GAAA,QAAA,CAAS,UAAU,GAAG,CAAA;AAC1E,IAAA,cAAA,GAAiB,YAAe,GAAA,iBAAA;AAAA,GAC3B,MAAA;AAEL,IACE,OAAA,GAAA,QAAA,CAAS,SAAU,CAAA,CAAA,EAAG,KAAK,CAAA,GAC3B,SACA,YACA,GAAA,KAAA,GACA,QAAS,CAAA,SAAA,CAAU,GAAG,CAAA;AAExB,IAAI,IAAA,YAAA,CAAa,WAAW,CAAG,EAAA;AAE7B,MAAA,cAAA,GAAiB,QAAQ,MAAO,CAAA,MAAA;AAChC,MAAe,YAAA,GAAA,cAAA;AAAA,KACV,MAAA;AAEL,MAAA,cAAA,GAAiB,QAAQ,MAAO,CAAA,MAAA;AAChC,MAAA,YAAA,GAAe,iBAAiB,YAAa,CAAA,MAAA;AAAA;AAC/C;AAGF,EAAA,WAAA,CAAY,OAAO,CAAA;AACnB,EAAI,IAAA,QAAA,WAAmB,OAAO,CAAA;AAE9B,EAAA,qBAAA,CAAsB,MAAM;AAC1B,IAAS,QAAA,CAAA,iBAAA,CAAkB,gBAAgB,YAAY,CAAA;AACvD,IAAA,QAAA,CAAS,KAAM,EAAA;AAAA,GAChB,CAAA;AACH;;;;"}
|