Improve editor and no longer crash if markdawn Line has bad parameters
This commit is contained in:
parent
b6882cd1e5
commit
d68e238b00
|
@ -1,8 +1,10 @@
|
||||||
|
// eslint-disable-next-line import/named
|
||||||
|
import { Placement } from "tippy.js";
|
||||||
import { Button } from "./Button";
|
import { Button } from "./Button";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { cJoin } from "helpers/className";
|
import { cJoin } from "helpers/className";
|
||||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
import { isDefined } from "helpers/asserts";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -12,7 +14,8 @@ import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||||
interface Props {
|
interface Props {
|
||||||
className?: string;
|
className?: string;
|
||||||
buttonsProps: (Parameters<typeof Button>[0] & {
|
buttonsProps: (Parameters<typeof Button>[0] & {
|
||||||
tooltip?: string | null | undefined;
|
tooltip?: React.ReactNode | null | undefined;
|
||||||
|
tooltipPlacement?: Placement;
|
||||||
})[];
|
})[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +26,9 @@ export const ButtonGroup = ({ buttonsProps, className }: Props): JSX.Element =>
|
||||||
{buttonsProps.map((buttonProps, index) => (
|
{buttonsProps.map((buttonProps, index) => (
|
||||||
<ConditionalWrapper
|
<ConditionalWrapper
|
||||||
key={index}
|
key={index}
|
||||||
isWrapping={isDefinedAndNotEmpty(buttonProps.tooltip)}
|
isWrapping={isDefined(buttonProps.tooltip)}
|
||||||
wrapper={ToolTipWrapper}
|
wrapper={ToolTipWrapper}
|
||||||
wrapperProps={{ text: buttonProps.tooltip ?? "" }}>
|
wrapperProps={{ text: buttonProps.tooltip ?? "", placement: buttonProps.tooltipPlacement }}>
|
||||||
<Button
|
<Button
|
||||||
{...buttonProps}
|
{...buttonProps}
|
||||||
className={
|
className={
|
||||||
|
@ -47,11 +50,12 @@ export const ButtonGroup = ({ buttonsProps, className }: Props): JSX.Element =>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
interface ToolTipWrapperProps {
|
interface ToolTipWrapperProps {
|
||||||
text: string;
|
text: React.ReactNode;
|
||||||
|
placement?: Placement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ToolTipWrapper = ({ text, children }: ToolTipWrapperProps & Wrapper) => (
|
const ToolTipWrapper = ({ text, children, placement }: ToolTipWrapperProps & Wrapper) => (
|
||||||
<ToolTip content={text}>
|
<ToolTip content={text} placement={placement}>
|
||||||
<>{children}</>
|
<>{children}</>
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import Markdown from "markdown-to-jsx";
|
import Markdown from "markdown-to-jsx";
|
||||||
import React, { Fragment, MouseEventHandler, useMemo } from "react";
|
import React, { Fragment, MouseEventHandler, useMemo } from "react";
|
||||||
import ReactDOMServer from "react-dom/server";
|
import ReactDOMServer from "react-dom/server";
|
||||||
|
import { z } from "zod";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Img } from "components/Img";
|
import { Img } from "components/Img";
|
||||||
import { InsetBox } from "components/Containers/InsetBox";
|
import { InsetBox } from "components/Containers/InsetBox";
|
||||||
|
@ -35,7 +36,7 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
||||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
|
|
||||||
/* eslint-disable no-irregular-whitespace */
|
/* eslint-disable no-irregular-whitespace */
|
||||||
const text = `${preprocessMarkDawn(rawText, playerName)}
|
const text = `${preprocessMarkDawn(rawText, playerName)}\n
|
||||||
`;
|
`;
|
||||||
/* eslint-enable no-irregular-whitespace */
|
/* eslint-enable no-irregular-whitespace */
|
||||||
|
|
||||||
|
@ -117,15 +118,30 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
||||||
},
|
},
|
||||||
|
|
||||||
Line: {
|
Line: {
|
||||||
component: (compProps) => (
|
component: (compProps) => {
|
||||||
<>
|
const schema = z.object({ name: z.string(), children: z.any() });
|
||||||
<strong
|
if (!schema.safeParse(compProps).success) {
|
||||||
className={cJoin("!my-0 text-dark/60", cIf(!isContentPanelAtLeastLg, "!-mb-4"))}>
|
return (
|
||||||
<Markdawn text={compProps.name} />
|
<MarkdawnError
|
||||||
</strong>
|
message={`Error while parsing a <Line/> tag. Here is the correct usage:
|
||||||
<p className="whitespace-pre-line">{compProps.children}</p>
|
<Line name="John">Hello!</Line>`}
|
||||||
</>
|
/>
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
const safeProps: z.infer<typeof schema> = compProps;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<strong
|
||||||
|
className={cJoin(
|
||||||
|
"!my-0 text-dark/60",
|
||||||
|
cIf(!isContentPanelAtLeastLg, "!-mb-4")
|
||||||
|
)}>
|
||||||
|
<Markdawn text={safeProps.name} />
|
||||||
|
</strong>
|
||||||
|
<p className="whitespace-pre-line">{safeProps.children}</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
InsetBox: {
|
InsetBox: {
|
||||||
|
@ -215,6 +231,21 @@ export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Eleme
|
||||||
|
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
|
interface MarkdawnErrorProps {
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarkdawnError = ({ message }: MarkdawnErrorProps): JSX.Element => (
|
||||||
|
<div
|
||||||
|
className="flex place-items-center gap-4 whitespace-pre-line rounded-md
|
||||||
|
bg-[red]/10 px-4 text-[red]">
|
||||||
|
<Ico icon="error" isFilled={false} />
|
||||||
|
<p>{message}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
interface TableOfContentsProps {
|
interface TableOfContentsProps {
|
||||||
toc: TocInterface;
|
toc: TocInterface;
|
||||||
onContentClicked?: MouseEventHandler<HTMLAnchorElement>;
|
onContentClicked?: MouseEventHandler<HTMLAnchorElement>;
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getFormat } from "helpers/i18n";
|
import { getFormat } from "helpers/i18n";
|
||||||
import { isDefined } from "helpers/asserts";
|
import { isDefined } from "helpers/asserts";
|
||||||
import { atomPairing, useAtomPair } from "helpers/atoms";
|
import { atomPairing, useAtomPair } from "helpers/atoms";
|
||||||
|
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -39,23 +40,23 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
value: string,
|
value: string,
|
||||||
selectionStart: number,
|
selectionStart: number,
|
||||||
selectedEnd: number
|
selectedEnd: number
|
||||||
) => { prependLength: number; transformedValue: string }
|
) => { prependLength: number; selectionLength?: number; transformedValue: string }
|
||||||
) => {
|
) => {
|
||||||
if (textAreaRef.current) {
|
if (textAreaRef.current) {
|
||||||
const { value, selectionStart, selectionEnd } = textAreaRef.current;
|
const { value, selectionStart, selectionEnd } = textAreaRef.current;
|
||||||
|
|
||||||
const { prependLength, transformedValue } = transformation(
|
const {
|
||||||
value,
|
prependLength,
|
||||||
selectionStart,
|
transformedValue,
|
||||||
selectionEnd
|
selectionLength = selectionEnd - selectionStart,
|
||||||
);
|
} = transformation(value, selectionStart, selectionEnd);
|
||||||
|
|
||||||
textAreaRef.current.value = transformedValue;
|
textAreaRef.current.value = transformedValue;
|
||||||
setMarkdown(textAreaRef.current.value);
|
setMarkdown(textAreaRef.current.value);
|
||||||
|
|
||||||
textAreaRef.current.focus();
|
textAreaRef.current.focus();
|
||||||
textAreaRef.current.selectionStart = selectionStart + prependLength;
|
textAreaRef.current.selectionStart = selectionStart + prependLength;
|
||||||
textAreaRef.current.selectionEnd = selectionEnd + prependLength;
|
textAreaRef.current.selectionEnd = selectionStart + selectionLength + prependLength;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[setMarkdown]
|
[setMarkdown]
|
||||||
|
@ -92,13 +93,13 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const unwrap = useCallback(
|
const unwrap = useCallback(
|
||||||
(wrapper: string) => {
|
(openingWrapper: string, closingWrapper: string) => {
|
||||||
transformationWrapper((value, selectionStart, selectionEnd) => {
|
transformationWrapper((value, selectionStart, selectionEnd) => {
|
||||||
let newValue = "";
|
let newValue = "";
|
||||||
newValue += value.slice(0, selectionStart - wrapper.length);
|
newValue += value.slice(0, selectionStart - openingWrapper.length);
|
||||||
newValue += value.slice(selectionStart, selectionEnd);
|
newValue += value.slice(selectionStart, selectionEnd);
|
||||||
newValue += value.slice(wrapper.length + selectionEnd);
|
newValue += value.slice(closingWrapper.length + selectionEnd);
|
||||||
return { prependLength: -wrapper.length, transformedValue: newValue };
|
return { prependLength: -openingWrapper.length, transformedValue: newValue };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[transformationWrapper]
|
[transformationWrapper]
|
||||||
|
@ -109,11 +110,16 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
if (textAreaRef.current) {
|
if (textAreaRef.current) {
|
||||||
const { value, selectionStart, selectionEnd } = textAreaRef.current;
|
const { value, selectionStart, selectionEnd } = textAreaRef.current;
|
||||||
|
|
||||||
|
const openingWrapper =
|
||||||
|
properties && Object.keys(properties).length === 0 ? `<${wrapper}>` : wrapper;
|
||||||
|
const closingWrapper =
|
||||||
|
properties && Object.keys(properties).length === 0 ? `</${wrapper}>` : wrapper;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
value.slice(selectionStart - wrapper.length, selectionStart) === wrapper &&
|
value.slice(selectionStart - openingWrapper.length, selectionStart) === openingWrapper &&
|
||||||
value.slice(selectionEnd, selectionEnd + wrapper.length) === wrapper
|
value.slice(selectionEnd, selectionEnd + closingWrapper.length) === closingWrapper
|
||||||
) {
|
) {
|
||||||
unwrap(wrapper);
|
unwrap(openingWrapper, closingWrapper);
|
||||||
} else {
|
} else {
|
||||||
wrap(wrapper, properties, addInnerNewLines);
|
wrap(wrapper, properties, addInnerNewLines);
|
||||||
}
|
}
|
||||||
|
@ -124,20 +130,77 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
|
|
||||||
const preline = useCallback(
|
const preline = useCallback(
|
||||||
(prepend: string) => {
|
(prepend: string) => {
|
||||||
transformationWrapper((value, selectionStart) => {
|
transformationWrapper((value, selectionStart, selectionEnd) => {
|
||||||
const lastNewLine = value.slice(0, selectionStart).lastIndexOf("\n") + 1;
|
const lastNewLine = value.slice(0, selectionStart).lastIndexOf("\n") + 1;
|
||||||
|
const nextNewLine = value.slice(selectionEnd).indexOf("\n") + selectionEnd;
|
||||||
|
|
||||||
|
const lines = value.slice(lastNewLine, nextNewLine).split("\n");
|
||||||
|
|
||||||
|
const processedLines = lines.map((line) => `${prepend}${line}`);
|
||||||
|
|
||||||
let newValue = "";
|
let newValue = "";
|
||||||
newValue += value.slice(0, lastNewLine);
|
newValue += value.slice(0, lastNewLine);
|
||||||
newValue += prepend;
|
newValue += processedLines.join("\n");
|
||||||
newValue += value.slice(lastNewLine);
|
newValue += value.slice(nextNewLine);
|
||||||
|
|
||||||
return { prependLength: prepend.length, transformedValue: newValue };
|
return {
|
||||||
|
prependLength: prepend.length,
|
||||||
|
selectionLength: selectionEnd - selectionStart + (lines.length - 1) * prepend.length,
|
||||||
|
transformedValue: newValue,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[transformationWrapper]
|
[transformationWrapper]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const unpreline = useCallback(
|
||||||
|
(prepend: string) => {
|
||||||
|
transformationWrapper((value, selectionStart, selectionEnd) => {
|
||||||
|
const lastNewLine = value.slice(0, selectionStart).lastIndexOf("\n") + 1;
|
||||||
|
const nextNewLine = value.slice(selectionEnd).indexOf("\n") + selectionEnd;
|
||||||
|
|
||||||
|
const lines = value.slice(lastNewLine, nextNewLine).split("\n");
|
||||||
|
|
||||||
|
const processedLines = lines.map((line) =>
|
||||||
|
line.startsWith(prepend) ? line.slice(prepend.length) : line
|
||||||
|
);
|
||||||
|
|
||||||
|
let newValue = "";
|
||||||
|
newValue += value.slice(0, lastNewLine);
|
||||||
|
newValue += processedLines.join("\n");
|
||||||
|
newValue += value.slice(nextNewLine);
|
||||||
|
|
||||||
|
return {
|
||||||
|
prependLength: -prepend.length,
|
||||||
|
selectionLength: selectionEnd - selectionStart + (lines.length - 1) * -prepend.length,
|
||||||
|
transformedValue: newValue,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[transformationWrapper]
|
||||||
|
);
|
||||||
|
|
||||||
|
const togglePreline = useCallback(
|
||||||
|
(prepend: string) => {
|
||||||
|
if (!textAreaRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { value, selectionStart, selectionEnd } = textAreaRef.current;
|
||||||
|
|
||||||
|
const lastNewLine = value.slice(0, selectionStart).lastIndexOf("\n") + 1;
|
||||||
|
const nextNewLine = value.slice(selectionEnd).indexOf("\n") + selectionEnd;
|
||||||
|
|
||||||
|
const lines = value.slice(lastNewLine, nextNewLine).split("\n");
|
||||||
|
|
||||||
|
if (lines.every((line) => line.startsWith(prepend))) {
|
||||||
|
unpreline(prepend);
|
||||||
|
} else {
|
||||||
|
preline(prepend);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[preline, unpreline]
|
||||||
|
);
|
||||||
|
|
||||||
const insert = useCallback(
|
const insert = useCallback(
|
||||||
(prepend: string) => {
|
(prepend: string) => {
|
||||||
transformationWrapper((value, selectionStart) => {
|
transformationWrapper((value, selectionStart) => {
|
||||||
|
@ -208,23 +271,48 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
content={
|
content={
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<h3 className="text-lg">Headers</h3>
|
<h3 className="text-lg">Headers</h3>
|
||||||
<Button onClick={() => preline("# ")} text={"H1"} />
|
<Button onClick={() => togglePreline("# ")} text={"H1"} />
|
||||||
<Button onClick={() => preline("## ")} text={"H2"} />
|
<Button onClick={() => togglePreline("## ")} text={"H2"} />
|
||||||
<Button onClick={() => preline("### ")} text={"H3"} />
|
<Button onClick={() => togglePreline("### ")} text={"H3"} />
|
||||||
<Button onClick={() => preline("#### ")} text={"H4"} />
|
<Button onClick={() => togglePreline("#### ")} text={"H4"} />
|
||||||
<Button onClick={() => preline("##### ")} text={"H5"} />
|
<Button onClick={() => togglePreline("##### ")} text={"H5"} />
|
||||||
<Button onClick={() => preline("###### ")} text={"H6"} />
|
<Button onClick={() => togglePreline("###### ")} text={"H6"} />
|
||||||
</div>
|
</div>
|
||||||
}>
|
}>
|
||||||
<Button icon="title" />
|
<Button icon="title" />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip placement="bottom" content={<h3 className="text-lg">Toggle Bold</h3>}>
|
<ButtonGroup
|
||||||
<Button onClick={() => toggleWrap("**")} icon="format_bold" />
|
buttonsProps={[
|
||||||
</ToolTip>
|
{
|
||||||
|
onClick: () => toggleWrap("**"),
|
||||||
|
tooltip: <h3 className="text-lg">Toggle Bold</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
icon: "format_bold",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: () => toggleWrap("_"),
|
||||||
|
tooltip: <h3 className="text-lg">Toggle Italic</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
icon: "format_italic",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: () => toggleWrap("u", {}),
|
||||||
|
tooltip: <h3 className="text-lg">Toggle Underline</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
icon: "format_underlined",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: () => toggleWrap("~~"),
|
||||||
|
tooltip: <h3 className="text-lg">Toggle Strikethrough</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
icon: "strikethrough_s",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<ToolTip placement="bottom" content={<h3 className="text-lg">Toggle Italic</h3>}>
|
<ToolTip placement="bottom" content={<h3 className="text-lg">Highlight</h3>}>
|
||||||
<Button onClick={() => toggleWrap("_")} icon="format_italic" />
|
<Button onClick={() => toggleWrap("==")} icon="format_ink_highlighter" />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
@ -241,6 +329,40 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
<Button onClick={() => toggleWrap("`")} icon="code" />
|
<Button onClick={() => toggleWrap("`")} icon="code" />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
|
<ButtonGroup
|
||||||
|
buttonsProps={[
|
||||||
|
{
|
||||||
|
onClick: () => togglePreline("- "),
|
||||||
|
icon: "format_list_bulleted",
|
||||||
|
tooltip: <h3 className="text-lg">Bulleted List</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: () => togglePreline("1. "),
|
||||||
|
icon: "format_list_numbered",
|
||||||
|
tooltip: <h3 className="text-lg">Numbered List</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ButtonGroup
|
||||||
|
buttonsProps={[
|
||||||
|
{
|
||||||
|
onClick: () => preline("\t"),
|
||||||
|
icon: "format_indent_increase",
|
||||||
|
tooltip: <h3 className="text-lg">Increase Indent</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: () => unpreline("\t"),
|
||||||
|
icon: "format_indent_decrease",
|
||||||
|
tooltip: <h3 className="text-lg">Decrease Indent</h3>,
|
||||||
|
tooltipPlacement: "bottom",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
placement="bottom"
|
placement="bottom"
|
||||||
content={
|
content={
|
||||||
|
@ -299,12 +421,28 @@ const Editor = (props: Props): JSX.Element => {
|
||||||
<Button icon="record_voice_over" />
|
<Button icon="record_voice_over" />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip placement="bottom" content={<h3 className="text-lg">Inset box</h3>}>
|
<ToolTip
|
||||||
<Button onClick={() => wrap("InsetBox", {}, true)} icon="check_box_outline_blank" />
|
placement="bottom"
|
||||||
|
content={
|
||||||
|
<>
|
||||||
|
<h3 className="text-lg">Layouts</h3>
|
||||||
|
<div className="grid gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => wrap("InsetBox", {}, true)}
|
||||||
|
icon="check_box_outline_blank"
|
||||||
|
text="InsetBox"
|
||||||
|
/>
|
||||||
|
<Button onClick={() => togglePreline("> ")} icon="format_quote" text="Blockquote" />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}>
|
||||||
|
<Button icon="dashboard" />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip placement="bottom" content={<h3 className="text-lg">Scene break</h3>}>
|
<ToolTip placement="bottom" content={<h3 className="text-lg">Scene break</h3>}>
|
||||||
<Button onClick={() => insert("\n* * *\n")} icon="more_horiz" />
|
<Button onClick={() => insert("\n* * *\n")} icon="more_horiz" />
|
||||||
</ToolTip>
|
</ToolTip>
|
||||||
|
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={
|
content={
|
||||||
<div className="flex flex-col place-items-center gap-2">
|
<div className="flex flex-col place-items-center gap-2">
|
||||||
|
|
Loading…
Reference in New Issue