feat: add EditorToolbar (#73)
* feat: add EditorToolbar * feat: add github icon * feat: add wrapExecute, prependExecute * feat: add preview mode * feat: move the position of Toolbar * feat: remove max width * feat: add preview button active style * refactor: preview visible
This commit is contained in:
parent
cfc41fe2fd
commit
a78eae899a
|
|
@ -0,0 +1,21 @@
|
|||
import { EditorSelection } from "@codemirror/state"
|
||||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const Bold: ICommand = {
|
||||
name: "bold",
|
||||
icon: (
|
||||
<svg
|
||||
height="16"
|
||||
width="16"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M4 2a1 1 0 00-1 1v10a1 1 0 001 1h5.5a3.5 3.5 0 001.852-6.47A3.5 3.5 0 008.5 2H4zm4.5 5a1.5 1.5 0 100-3H5v3h3.5zM5 9v3h4.5a1.5 1.5 0 000-3H5z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "**", append: "**" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { EditorSelection } from "@codemirror/state"
|
||||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const Code: ICommand = {
|
||||
name: "code",
|
||||
icon: (
|
||||
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M4.72 3.22a.75.75 0 011.06 1.06L2.06 8l3.72 3.72a.75.75 0 11-1.06 1.06L.47 8.53a.75.75 0 010-1.06l4.25-4.25zm6.56 0a.75.75 0 10-1.06 1.06L13.94 8l-3.72 3.72a.75.75 0 101.06 1.06l4.25-4.25a.75.75 0 000-1.06l-4.25-4.25z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "`", append: "`" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ICommand, prependExecute } from "."
|
||||
|
||||
export const Heading: ICommand = {
|
||||
name: "heading",
|
||||
icon: (
|
||||
<svg height="16" width="16" viewBox="0 0 16 16">
|
||||
<path d="M3.75 2a.75.75 0 01.75.75V7h7V2.75a.75.75 0 011.5 0v10.5a.75.75 0 01-1.5 0V8.5h-7v4.75a.75.75 0 01-1.5 0V2.75A.75.75 0 013.75 2z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
prependExecute({ view, prepend: "### " })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import { EditorSelection } from "@codemirror/state"
|
||||
import { ICommand, prependExecute, wrapExecute } from "."
|
||||
|
||||
export const Image: ICommand = {
|
||||
name: "image",
|
||||
icon: (
|
||||
<svg viewBox="0 0 1024 1024" width="16" height="16">
|
||||
<path d="M204.8 204.8a51.2 51.2 0 0 0-51.2 51.2v563.2a51.2 51.2 0 0 0 51.2 51.2h614.4a51.2 51.2 0 0 0 51.2-51.2V256a51.2 51.2 0 0 0-51.2-51.2H204.8z m0-51.2h614.4a102.4 102.4 0 0 1 102.4 102.4v563.2a102.4 102.4 0 0 1-102.4 102.4H204.8a102.4 102.4 0 0 1-102.4-102.4V256a102.4 102.4 0 0 1 102.4-102.4z"></path>
|
||||
<path d="M142.6432 738.816l-36.352-36.0448 205.1584-206.8992 144.3328 146.2784-36.4544 35.9424-107.9808-109.4144zM358.4 435.2a76.8 76.8 0 1 1 0-153.6 76.8 76.8 0 0 1 0 153.6z m0-51.2a25.6 25.6 0 1 0 0-51.2 25.6 25.6 0 0 0 0 51.2z"></path>
|
||||
<path d="M673.3312 466.7392l-433.664 453.9392-37.0688-35.3792 469.504-491.4176 236.544 231.5776-35.7888 36.608z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "![", append: "]()" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const Italic: ICommand = {
|
||||
name: "italic",
|
||||
icon: (
|
||||
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M6 2.75A.75.75 0 016.75 2h6.5a.75.75 0 010 1.5h-2.505l-3.858 9H9.25a.75.75 0 010 1.5h-6.5a.75.75 0 010-1.5h2.505l3.858-9H6.75A.75.75 0 016 2.75z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "_", append: "_" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { EditorSelection } from "@codemirror/state"
|
||||
import { ICommand, prependExecute, wrapExecute } from "."
|
||||
|
||||
export const Link: ICommand = {
|
||||
name: "link",
|
||||
icon: (
|
||||
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "[", append: "]()" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import { EditorSelection } from "@codemirror/state"
|
||||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const ListOrdered: ICommand = {
|
||||
name: "list-ordered",
|
||||
icon: (
|
||||
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M2.003 2.5a.5.5 0 00-.723-.447l-1.003.5a.5.5 0 00.446.895l.28-.14V6H.5a.5.5 0 000 1h2.006a.5.5 0 100-1h-.503V2.5zM5 3.25a.75.75 0 01.75-.75h8.5a.75.75 0 010 1.5h-8.5A.75.75 0 015 3.25zm0 5a.75.75 0 01.75-.75h8.5a.75.75 0 010 1.5h-8.5A.75.75 0 015 8.25zm0 5a.75.75 0 01.75-.75h8.5a.75.75 0 010 1.5h-8.5a.75.75 0 01-.75-.75zM.924 10.32l.003-.004a.851.851 0 01.144-.153A.66.66 0 011.5 10c.195 0 .306.068.374.146a.57.57 0 01.128.376c0 .453-.269.682-.8 1.078l-.035.025C.692 11.98 0 12.495 0 13.5a.5.5 0 00.5.5h2.003a.5.5 0 000-1H1.146c.132-.197.351-.372.654-.597l.047-.035c.47-.35 1.156-.858 1.156-1.845 0-.365-.118-.744-.377-1.038-.268-.303-.658-.484-1.126-.484-.48 0-.84.202-1.068.392a1.858 1.858 0 00-.348.384l-.007.011-.002.004-.001.002-.001.001a.5.5 0 00.851.525zM.5 10.055l-.427-.26.427.26z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "1. ", append: "\n" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const ListUnordered: ICommand = {
|
||||
name: "list-unordered",
|
||||
icon: (
|
||||
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M2 4a1 1 0 100-2 1 1 0 000 2zm3.75-1.5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zM3 8a1 1 0 11-2 0 1 1 0 012 0zm-1 6a1 1 0 100-2 1 1 0 000 2z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "- ", append: "\n" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ICommand } from "."
|
||||
|
||||
export const Preview: ICommand = {
|
||||
name: "preview",
|
||||
icon: (
|
||||
<svg width="16" height="16" viewBox="0 0 32 32">
|
||||
<path d="M0 16c3.037-5.864 9.058-9.802 16-9.802s12.963 3.938 15.953 9.703l0.047 0.1c-3.037 5.864-9.058 9.802-16 9.802s-12.963-3.938-15.953-9.703l-0.047-0.1zM16 22.531c3.607 0 6.531-2.924 6.531-6.531s-2.924-6.531-6.531-6.531v0c-3.607 0-6.531 2.924-6.531 6.531s2.924 6.531 6.531 6.531v0zM16 19.265c-1.804 0-3.265-1.461-3.265-3.265s1.461-3.265 3.265-3.265v0c1.804 0 3.265 1.461 3.265 3.265s-1.461 3.265-3.265 3.265v0z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (_, options) => {
|
||||
options?.setPreviewVisible?.((visible) => !visible)
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const Quote: ICommand = {
|
||||
name: "quote",
|
||||
icon: (
|
||||
<svg height="16" viewBox="0 0 16 16" width="16">
|
||||
<path d="M1.75 2.5a.75.75 0 000 1.5h10.5a.75.75 0 000-1.5H1.75zm4 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zm0 5a.75.75 0 000 1.5h8.5a.75.75 0 000-1.5h-8.5zM2.5 7.75a.75.75 0 00-1.5 0v6a.75.75 0 001.5 0v-6z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "> ", append: "\n" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const Strikethrough: ICommand = {
|
||||
name: "strikethrough",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
d="M665.088 512H921.6v51.2h-199.6288c17.0496 24.832 25.6512 53.9136 25.6512 87.3472 0 56.2176-23.9616 101.376-70.9632 134.5536-45.1584 31.3344-105.0624 47.0016-179.712 47.0016-75.5712 0-135.4752-17.5104-178.7904-51.6096-49.7664-39.6288-77.4144-99.5328-82.944-178.7904h55.296c6.4512 61.7472 27.648 107.8272 64.512 138.24 33.1776 26.7264 80.1792 40.5504 141.0048 40.5504 58.0608 0 105.0624-11.9808 141.0048-34.0992 35.0208-23.04 53.4528-54.3744 53.4528-93.0816 0-36.7616-15.2576-66.7648-44.8-90.112H102.4v-51.2h426.3936c-15.1552-4.608-31.8976-9.472-50.2784-14.4384-78.336-22.1184-129.024-39.6288-151.1424-52.5312-50.688-28.5696-75.5712-70.0416-75.5712-123.4944 0-57.1392 23.04-100.4544 70.9632-130.8672 42.3936-28.5696 97.6896-42.3936 164.0448-42.3936 71.8848 0 129.024 17.5104 171.4176 52.5312 43.3152 35.0208 68.1984 86.6304 73.728 154.8288h-56.2176c-7.3728-51.6096-26.7264-90.3168-58.0608-117.0432-31.3344-26.7264-75.5712-39.6288-132.7104-39.6288-53.4528 0-94.9248 9.216-125.3376 27.648-35.0208 20.2752-51.6096 51.6096-51.6096 93.0816 0 36.864 20.2752 65.4336 61.7472 87.552 19.3536 9.216 61.7472 23.9616 126.2592 42.3936 83.4048 23.808 139.4688 44.032 169.0624 62.3616z"
|
||||
></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "~~", append: "~~" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { ICommand, wrapExecute } from "."
|
||||
|
||||
export const Underline: ICommand = {
|
||||
name: "underline",
|
||||
icon: (
|
||||
<svg viewBox="0 0 1024 1024" width="16" height="16">
|
||||
<path d="M204.8 819.2h614.4v51.2H204.8zM268.3392 146.5344h53.0944v381.2352c0 66.1504 13.9264 116.6336 43.52 150.5792 31.3344 35.6864 79.2064 53.9648 145.3568 53.9648s114.0224-18.2784 145.3568-53.9648c28.7232-33.9456 43.52-84.4288 43.52-150.5792V146.5344h53.0944v381.2352c0 80.9472-20.0192 142.7456-58.3168 185.3952-40.9088 44.3904-101.8368 67.0208-183.6544 67.0208s-142.7456-22.6304-182.784-66.1504c-40.0384-43.52-59.1872-105.3184-59.1872-186.2656V146.5344z"></path>
|
||||
</svg>
|
||||
),
|
||||
execute: (view) => {
|
||||
wrapExecute({ view, prepend: "<u>", append: "</u>" })
|
||||
},
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
import { EditorSelection } from "@codemirror/state"
|
||||
import { EditorView } from "@codemirror/view"
|
||||
import { Dispatch, SetStateAction } from "react"
|
||||
import { Bold } from "./Bold"
|
||||
import { Code } from "./Code"
|
||||
import { Heading } from "./Heading"
|
||||
import { Image } from "./Image"
|
||||
import { Italic } from "./Italic"
|
||||
import { Link } from "./Link"
|
||||
import { ListOrdered } from "./ListOrdered"
|
||||
import { ListUnordered } from "./ListUnordered"
|
||||
import { Preview } from "./Preview"
|
||||
import { Quote } from "./Quote"
|
||||
import { Strikethrough } from "./Strikethrough"
|
||||
|
||||
export type ICommand = {
|
||||
icon: React.ReactElement
|
||||
name: string
|
||||
execute: (
|
||||
view: EditorView,
|
||||
options?: {
|
||||
setPreviewVisible?: Dispatch<SetStateAction<boolean>>
|
||||
container?: HTMLElement | null
|
||||
},
|
||||
) => void
|
||||
}
|
||||
|
||||
export type IPrependExecute = {
|
||||
view: EditorView
|
||||
prepend: string
|
||||
}
|
||||
|
||||
export type IWrapExecute = {
|
||||
view: EditorView
|
||||
prepend: string
|
||||
append: string
|
||||
}
|
||||
|
||||
export const prependExecute = ({ view, prepend }: IPrependExecute) => {
|
||||
const range = view.state.selection.ranges[0]
|
||||
const selection = view.state.sliceDoc(range.from - prepend.length, range.to)
|
||||
if (selection.startsWith(prepend)) {
|
||||
view.dispatch(
|
||||
view.state.changeByRange((range) => ({
|
||||
changes: [
|
||||
{
|
||||
from: range.from - prepend.length,
|
||||
to: range.to,
|
||||
insert: view.state.sliceDoc(range.from, range.to),
|
||||
},
|
||||
],
|
||||
range: EditorSelection.range(
|
||||
range.from - prepend.length,
|
||||
range.to - prepend.length,
|
||||
),
|
||||
})),
|
||||
)
|
||||
view.focus()
|
||||
return
|
||||
}
|
||||
view.dispatch(
|
||||
view.state.changeByRange((range) => {
|
||||
return {
|
||||
changes: [{ from: range.from, insert: prepend }],
|
||||
range: EditorSelection.range(
|
||||
range.from + prepend.length,
|
||||
range.to + prepend.length,
|
||||
),
|
||||
}
|
||||
}),
|
||||
)
|
||||
view.focus()
|
||||
}
|
||||
|
||||
export const wrapExecute = ({ view, prepend, append }: IWrapExecute) => {
|
||||
const range = view.state.selection.ranges[0]
|
||||
const selection = view.state.sliceDoc(
|
||||
range.from - prepend.length,
|
||||
range.to + append.length,
|
||||
)
|
||||
if (selection.startsWith(prepend) && selection.endsWith(append)) {
|
||||
view.dispatch(
|
||||
view.state.changeByRange((range) => ({
|
||||
changes: [
|
||||
{
|
||||
from: range.from - prepend.length,
|
||||
to: range.to + append.length,
|
||||
insert: view.state.sliceDoc(range.from, range.to),
|
||||
},
|
||||
],
|
||||
range: EditorSelection.range(
|
||||
range.from - prepend.length,
|
||||
range.to - prepend.length,
|
||||
),
|
||||
})),
|
||||
)
|
||||
view.focus()
|
||||
return
|
||||
}
|
||||
view.dispatch(
|
||||
view.state.changeByRange((range) => ({
|
||||
changes: [
|
||||
{ from: range.from, insert: prepend },
|
||||
{ from: range.to, insert: append },
|
||||
],
|
||||
range: EditorSelection.range(
|
||||
range.from + prepend.length,
|
||||
range.to + prepend.length,
|
||||
),
|
||||
})),
|
||||
)
|
||||
view.focus()
|
||||
}
|
||||
|
||||
export const toolbars: ICommand[] = [
|
||||
Heading,
|
||||
Bold,
|
||||
Italic,
|
||||
Strikethrough,
|
||||
Quote,
|
||||
Code,
|
||||
ListUnordered,
|
||||
ListOrdered,
|
||||
Link,
|
||||
Image,
|
||||
]
|
||||
|
||||
export const modeToolbars: ICommand[] = [Preview]
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import clsx from "clsx"
|
||||
import { FC, useCallback, useEffect, useState } from "react"
|
||||
import { renderPageContent } from "~/markdown"
|
||||
|
||||
export interface EditorPreviewProps {
|
||||
className?: string
|
||||
content: string
|
||||
previewVisible: boolean
|
||||
}
|
||||
|
||||
export const EditorPreview: FC<EditorPreviewProps> = ({
|
||||
className,
|
||||
content,
|
||||
previewVisible,
|
||||
}) => {
|
||||
const [html, setHtml] = useState("")
|
||||
const renderMarkdown = useCallback(async (doc: string) => {
|
||||
const pageContent = await renderPageContent(doc)
|
||||
setHtml(pageContent.contentHTML)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
renderMarkdown(content)
|
||||
}, [content, renderMarkdown])
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"prose border-l border-gray-100 overflow-auto",
|
||||
className,
|
||||
{ hidden: !previewVisible },
|
||||
)}
|
||||
dangerouslySetInnerHTML={{ __html: html ?? "" }}
|
||||
></div>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import { EditorView } from "@codemirror/view"
|
||||
import clsx from "clsx"
|
||||
import { Dispatch, FC, SetStateAction } from "react"
|
||||
import { ICommand } from "../command"
|
||||
|
||||
export interface EditorToolbarProps {
|
||||
view: EditorView | null
|
||||
toolbars: ICommand[]
|
||||
modeToolbars: ICommand[]
|
||||
previewVisible: boolean
|
||||
setPreviewVisible: Dispatch<SetStateAction<boolean>>
|
||||
}
|
||||
|
||||
enum ToolbarMode {
|
||||
Normal,
|
||||
Preview,
|
||||
}
|
||||
|
||||
export const EditorToolbar: FC<EditorToolbarProps> = ({
|
||||
view,
|
||||
toolbars,
|
||||
modeToolbars,
|
||||
previewVisible,
|
||||
setPreviewVisible,
|
||||
}) => {
|
||||
const renderToolbar =
|
||||
(mode: ToolbarMode) =>
|
||||
// eslint-disable-next-line react/display-name
|
||||
({ name, icon, execute }: ICommand) =>
|
||||
(
|
||||
<button
|
||||
key={name}
|
||||
className={clsx(
|
||||
"px-1 py-1 mr-1 fill-gray-700 transition-colors hover:fill-blue-500",
|
||||
{
|
||||
"fill-blue-500": mode === ToolbarMode.Preview && previewVisible,
|
||||
},
|
||||
)}
|
||||
onClick={() => {
|
||||
view && execute(view, { setPreviewVisible, container: view.dom })
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</button>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex px-1 py-1 bg-gray-100 border rounded border-gray-200">
|
||||
<div className="flex-1">
|
||||
{toolbars?.map(renderToolbar(ToolbarMode.Normal))}
|
||||
</div>
|
||||
<div>{modeToolbars?.map(renderToolbar(ToolbarMode.Preview))}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,21 +1,24 @@
|
|||
import { type EditorView } from "@codemirror/view"
|
||||
import clsx from "clsx"
|
||||
import dayjs from "dayjs"
|
||||
import { ChangeEvent, useCallback, useEffect, useState } from "react"
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
import toast from "react-hot-toast"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { useEditor } from "~/components/ui/Editor"
|
||||
import { type EditorView } from "@codemirror/view"
|
||||
import { trpc } from "~/lib/trpc"
|
||||
import { useRouter } from "next/router"
|
||||
import { ChangeEvent, useCallback, useEffect, useState } from "react"
|
||||
import toast from "react-hot-toast"
|
||||
import { modeToolbars, toolbars } from "~/components/command"
|
||||
import { DashboardLayout } from "~/components/dashboard/DashboardLayout"
|
||||
import { DashboardMain } from "~/components/dashboard/DashboardMain"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import { useEditor } from "~/components/ui/Editor"
|
||||
import { EditorPreview } from "~/components/ui/EditorPreview"
|
||||
import { EditorToolbar } from "~/components/ui/EditorToolbar"
|
||||
import { Input } from "~/components/ui/Input"
|
||||
import { UniLink } from "~/components/ui/UniLink"
|
||||
import { useUploadFile } from "~/hooks/useUploadFile"
|
||||
import { PublishButton } from "~/components/dashboard/PublishButton"
|
||||
import { inLocalTimezone } from "~/lib/date"
|
||||
import { getSiteLink } from "~/lib/helpers"
|
||||
import { getPageVisibility } from "~/lib/page-helpers"
|
||||
import { trpc } from "~/lib/trpc"
|
||||
import { PageVisibilityEnum } from "~/lib/types"
|
||||
|
||||
const getInputDatetimeValue = (date: Date | string) => {
|
||||
const str = dayjs(date).format()
|
||||
|
|
@ -62,6 +65,8 @@ export default function SubdomainEditor() {
|
|||
})
|
||||
const [content, setContent] = useState("")
|
||||
|
||||
const [previewVisible, setPreviewVisible] = useState(false)
|
||||
|
||||
type Values = typeof values
|
||||
|
||||
const updateValue = useCallback(
|
||||
|
|
@ -171,7 +176,13 @@ export default function SubdomainEditor() {
|
|||
<DashboardLayout title="Editor">
|
||||
<DashboardMain fullWidth>
|
||||
<header className="flex justify-between absolute top-0 left-0 right-0 z-10 px-5 h-14 border-b items-center text-sm">
|
||||
<div></div>
|
||||
<EditorToolbar
|
||||
view={view}
|
||||
toolbars={toolbars}
|
||||
modeToolbars={modeToolbars}
|
||||
previewVisible={previewVisible}
|
||||
setPreviewVisible={setPreviewVisible}
|
||||
></EditorToolbar>
|
||||
<div className="flex items-center space-x-3">
|
||||
<span
|
||||
className={clsx(
|
||||
|
|
@ -194,7 +205,7 @@ export default function SubdomainEditor() {
|
|||
</header>
|
||||
<div className="h-screen pt-14 flex w-full">
|
||||
<div className="h-full overflow-auto w-full">
|
||||
<div className="max-w-screen-md mx-auto py-5 px-5">
|
||||
<div className="mx-auto py-5 px-5">
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -211,8 +222,15 @@ export default function SubdomainEditor() {
|
|||
/>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="">
|
||||
<div ref={editorRef}></div>
|
||||
<div className="border border-gray-200 rounded">
|
||||
<div className="flex">
|
||||
<div className="flex-1" ref={editorRef}></div>
|
||||
<EditorPreview
|
||||
className="flex-1"
|
||||
previewVisible={previewVisible}
|
||||
content={content}
|
||||
></EditorPreview>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue