feat: mobile layout for editor page

This commit is contained in:
zsavko 2023-04-09 20:00:18 +08:00 committed by zsakvo
parent a463c1a64a
commit 7a907baa94
7 changed files with 312 additions and 67 deletions

View File

@ -108,6 +108,7 @@
"sharp": "0.32.0",
"sortablejs": "1.15.0",
"tailwind-merge": "^1.12.0",
"tailwind-scrollbar-hide": "^1.1.7",
"tailwindcss-animate": "^1.0.5",
"unidata.js": "0.7.18",
"unified": "10.1.2",

View File

@ -253,6 +253,9 @@ dependencies:
tailwind-merge:
specifier: ^1.12.0
version: 1.12.0
tailwind-scrollbar-hide:
specifier: ^1.1.7
version: 1.1.7
tailwindcss-animate:
specifier: ^1.0.5
version: 1.0.5(tailwindcss@3.3.1)
@ -12129,6 +12132,10 @@ packages:
resolution: {integrity: sha512-Y17eDp7FtN1+JJ4OY0Bqv9OA41O+MS8c1Iyr3T6JFLnOgLg3EvcyMKZAnQ8AGyvB5Nxm3t9Xb5Mhe139m8QT/g==}
dev: false
/tailwind-scrollbar-hide@1.1.7:
resolution: {integrity: sha512-X324n9OtpTmOMqEgDUEA/RgLrNfBF/jwJdctaPZDzB3mppxJk7TLIDmOreEDm1Bq4R9LSPu4Epf8VSdovNU+iA==}
dev: false
/tailwindcss-animate@1.0.5(tailwindcss@3.3.1):
resolution: {integrity: sha512-UU3qrOJ4lFQABY+MVADmBm+0KW3xZyhMdRvejwtXqYOL7YjHYxmuREFAZdmVG5LPe5E9CAst846SLC4j5I3dcw==}
peerDependencies:

View File

@ -14,11 +14,11 @@ export const DashboardTopbar: React.FC<{
return (
<div
className={`${`w-full top-0 h-16`} bg-slate-50 z-10 transition-all flex flex-row fixed px-5 md:px-10 items-center`}
className={`${`w-screen top-0 h-16`} bg-slate-50 z-20 transition-all flex flex-row fixed px-5 md:px-10 items-center`}
>
<Popover className="relative">
{({ open, close }) => (
<>
<div className="h-6">
<Transition
as={Fragment}
enter="transition ease-out duration-200"
@ -48,13 +48,13 @@ export const DashboardTopbar: React.FC<{
leaveFrom="opacity-100 transform translate-x-0"
leaveTo="opacity-0 transform -translate-x-full"
>
<Popover.Panel className="fixed bg-slate-50 h-screen left-0 top-0 z-10 w-sidebar transform sm:px-0 lg:max-w-3xl">
<Popover.Panel className="fixed bg-slate-50 h-screen left-0 top-0 z-20 w-sidebar transform sm:px-0 lg:max-w-3xl">
<div className="overflow-hidden rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 h-full">
{drawerWidget(close)}
</div>
</Popover.Panel>
</Transition>
</>
</div>
)}
</Popover>

View File

@ -0,0 +1,126 @@
import { Menu, Transition } from "@headlessui/react"
import { Dispatch, Fragment, SetStateAction, useState } from "react"
import { useTranslation } from "react-i18next"
import { PageVisibilityEnum } from "~/lib/types"
import { cn } from "~/lib/utils"
export const OptionsButton: React.FC<{
visibility: PageVisibilityEnum | undefined
previewPage: () => void
renderPage: Dispatch<SetStateAction<boolean>>
savePage: (published: boolean) => any
isRendering: boolean
published: boolean
}> = ({
visibility,
previewPage,
savePage,
isRendering,
renderPage,
published,
}) => {
const { t } = useTranslation("dashboard")
return (
<>
<div
className="bg-accent rounded-full cursor-pointer text-white w-6 h-6 flex justify-center items-center"
onClick={() => renderPage(!isRendering)}
>
{isRendering ? (
<i className="i-mingcute:eye-close-line text-xl inline-block w-6 h-6" />
) : (
<i className="i-mingcute:eye2-line text-xl inline-block w-6 h-6" />
)}
</div>
<Menu as="div" className="relative inline-block text-left h-6">
{({ open, close }) => {
return (
<>
<Menu.Button>
<div className="bg-accent rounded-full cursor-pointer text-white w-6 h-6">
{open ? (
<i className="i-mingcute:up-line text-2xl inline-block w-6 h-6" />
) : (
<i className="i-mingcute:down-line text-2xl inline-block w-6 h-6" />
)}
</div>
</Menu.Button>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 mt-2 w-56 origin-top-right divide-y divide-gray-100 rounded-md bg-white shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
<div className="px-1 py-1 ">
<Menu.Item disabled>
{({ active }) => (
<button
className={`${cn(
`text-sm capitalize`,
visibility === PageVisibilityEnum.Draft
? `text-zinc-300`
: visibility === PageVisibilityEnum.Modified
? "text-orange-600"
: "text-green-600",
)} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
>
{t(visibility as string)}
</button>
)}
</Menu.Item>
</div>
<div className="px-1 py-1 ">
<Menu.Item>
{({ active }) => (
<button
className={`${
active ? "bg-accent text-white" : "text-gray-900"
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
onClick={previewPage}
>
{t("Preview")}
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
className={`${
active ? "bg-accent text-white" : "text-gray-900"
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
onClick={() => savePage(true)}
>
{t(published ? "Update" : "Publish")}
</button>
)}
</Menu.Item>
<Menu.Item>
{({ active }) => (
<button
className={`${
active
? "bg-red-600 text-white"
: "text-red-600 bg-white"
} group flex w-full items-center rounded-md px-2 py-2 text-sm`}
onClick={() => savePage(false)}
>
{t("Delete")}
</button>
)}
</Menu.Item>
</div>
</Menu.Items>
</Transition>
</>
)
}}
</Menu>
</>
)
}

View File

@ -5,6 +5,7 @@ import { scroll } from "@uiw/codemirror-extensions-events"
import type { EditorState } from "@codemirror/state"
import { useState, useEffect, useMemo } from "react"
import { useTranslation } from "next-i18next"
import { useMobileLayout } from "~/hooks/useMobileLayout"
export const Editor: React.FC<{
value: string
@ -25,6 +26,7 @@ export const Editor: React.FC<{
}) => {
const { t } = useTranslation("dashboard")
const [extensions, setExtensions] = useState<any>([])
const isMobileLayout = useMobileLayout()
useEffect(() => {
setExtensions([
markdown(),
@ -34,6 +36,7 @@ export const Editor: React.FC<{
fontSize: "1rem",
overflow: "auto",
height: "100%",
padding: isMobileLayout ? "0 1.25rem" : "unset",
},
".cm-content": {
paddingBottom: "600px",
@ -85,7 +88,7 @@ export const Editor: React.FC<{
return useMemo(
() => (
<CodeMirror
className="px-5 h-full border-r w-1/2"
className={`h-full ${isMobileLayout ? "" : "border-r w-1/2 px-5"}`}
value={value}
extensions={extensions}
onCreateEditor={onCreateEditor}

View File

@ -42,6 +42,8 @@ import { getServerSideProps as getLayoutServerSideProps } from "~/components/das
import { GetServerSideProps } from "next"
import { serverSidePropsHandler } from "~/lib/server-side-props"
import { getDefaultSlug } from "~/lib/helpers"
import { useMobileLayout } from "~/hooks/useMobileLayout"
import { OptionsButton } from "~/components/dashboard/OptionsButton"
export const getServerSideProps: GetServerSideProps = serverSidePropsHandler(
async (ctx) => {
@ -163,6 +165,9 @@ export default function SubdomainEditor() {
const createOrUpdatePage = useCreateOrUpdatePage()
const isMobileLayout = useMobileLayout()
const [isRendering, setIsRendering] = useState(false)
const savePage = async (published: boolean) => {
const check = await checkPageSlug({
slug: values.slug || defaultSlug,
@ -406,8 +411,18 @@ export default function SubdomainEditor() {
</div>
) : (
<>
<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 className="flex items-center">
<header
className={`flex justify-between absolute top-0 left-0 right-0 z-10 px-5 h-14 border-b items-center text-sm ${
isMobileLayout ? "w-screen" : undefined
}`}
>
<div
className={`flex items-center ${
isMobileLayout
? "flex-1 overflow-x-auto scrollbar-hide"
: undefined
}`}
>
<EditorToolbar view={view} toolbars={toolbars}></EditorToolbar>
<UniLink
className="ml-7 align-middle hidden xl:block"
@ -418,44 +433,105 @@ export default function SubdomainEditor() {
{t("Tip: xLog Flavored Markdown")}
</UniLink>
</div>
<div className="flex items-center space-x-3">
<span
className={cn(
`text-sm capitalize`,
visibility === PageVisibilityEnum.Draft
? `text-zinc-300`
: visibility === PageVisibilityEnum.Modified
? "text-orange-600"
: "text-green-600",
)}
>
{t(visibility as string)}
</span>
<Button
isAutoWidth
onClick={() => {
window.open(
`/_site/${subdomain}/preview/${draftKey.replace(
`draft-${subdomain}-`,
"",
)}`,
)
}}
>
{t("Preview")}
</Button>
<PublishButton
save={savePage}
published={visibility !== PageVisibilityEnum.Draft}
isSaving={createOrUpdatePage.isLoading}
isDisabled={
visibility !== PageVisibilityEnum.Modified &&
visibility !== PageVisibilityEnum.Draft
}
/>
</div>
{isMobileLayout ? (
<div className="flex items-center space-x-3 w-auto inline-block pl-5">
{/* <span
className={cn(
`text-sm capitalize`,
visibility === PageVisibilityEnum.Draft
? `text-zinc-300`
: visibility === PageVisibilityEnum.Modified
? "text-orange-600"
: "text-green-600",
)}
>
{t(visibility as string)}
</span> */}
{/* <Button
isAutoWidth
onClick={() => {
window.open(
`/_site/${subdomain}/preview/${draftKey.replace(
`draft-${subdomain}-`,
"",
)}`,
)
}}
>
{t("Preview")}
</Button> */}
{/* <PublishButton
save={savePage}
published={visibility !== PageVisibilityEnum.Draft}
isSaving={createOrUpdatePage.isLoading}
isDisabled={
visibility !== PageVisibilityEnum.Modified &&
visibility !== PageVisibilityEnum.Draft
}
/> */}
<OptionsButton
visibility={visibility}
savePage={savePage}
published={visibility !== PageVisibilityEnum.Draft}
isRendering={isRendering}
renderPage={setIsRendering}
previewPage={() => {
window.open(
`/_site/${subdomain}/preview/${draftKey.replace(
`draft-${subdomain}-`,
"",
)}`,
)
}}
/>
</div>
) : (
<div className="flex items-center space-x-3">
<span
className={cn(
`text-sm capitalize`,
visibility === PageVisibilityEnum.Draft
? `text-zinc-300`
: visibility === PageVisibilityEnum.Modified
? "text-orange-600"
: "text-green-600",
)}
>
{t(visibility as string)}
</span>
<Button
isAutoWidth
onClick={() => {
window.open(
`/_site/${subdomain}/preview/${draftKey.replace(
`draft-${subdomain}-`,
"",
)}`,
)
}}
>
{t("Preview")}
</Button>
<PublishButton
save={savePage}
published={visibility !== PageVisibilityEnum.Draft}
isSaving={createOrUpdatePage.isLoading}
isDisabled={
visibility !== PageVisibilityEnum.Modified &&
visibility !== PageVisibilityEnum.Draft
}
/>
</div>
)}
</header>
<div className="h-screen pt-14 flex w-full min-w-[840px]">
<div
className={`h-screen pt-14 flex w-full ${
isMobileLayout ? "w-screen" : "min-w-[840px]"
}`}
style={
isMobileLayout ? { height: "calc(100vh - 64px)" } : undefined
}
>
<div className="h-full overflow-auto w-full">
<div className="h-full mx-auto pt-5 flex flex-col">
<div className="px-5 h-12">
@ -474,30 +550,62 @@ export default function SubdomainEditor() {
/>
</div>
<div className="mt-5 flex-1 flex overflow-hidden">
<Editor
value={values.content}
onChange={onChange}
handleDropFile={handleDropFile}
onScroll={onEditorScroll}
// onUpdate={onUpdate}
onCreateEditor={onCreateEditor}
onMouseEnter={() => {
setCurrentScrollArea("editor")
}}
/>
<PageContent
className="px-5 w-1/2 overflow-scroll pb-[200px]"
parsedContent={parsedContent}
inputRef={previewRef}
onScroll={onPreviewScroll}
onMouseEnter={() => {
setCurrentScrollArea("preview")
}}
></PageContent>
{isMobileLayout ? (
!isRendering ? (
<Editor
value={values.content}
onChange={onChange}
handleDropFile={handleDropFile}
onScroll={onEditorScroll}
// onUpdate={onUpdate}
onCreateEditor={onCreateEditor}
onMouseEnter={() => {
setCurrentScrollArea("editor")
}}
/>
) : (
<PageContent
className={`px-5 overflow-scroll pb-[200px] ${
isMobileLayout ? "" : "w-1/2 "
}`}
parsedContent={parsedContent}
inputRef={previewRef}
onScroll={onPreviewScroll}
onMouseEnter={() => {
setCurrentScrollArea("preview")
}}
></PageContent>
)
) : (
<>
<Editor
value={values.content}
onChange={onChange}
handleDropFile={handleDropFile}
onScroll={onEditorScroll}
// onUpdate={onUpdate}
onCreateEditor={onCreateEditor}
onMouseEnter={() => {
setCurrentScrollArea("editor")
}}
/>
<PageContent
className={`px-5 overflow-scroll pb-[200px] ${
isMobileLayout ? "" : "w-1/2 "
}`}
parsedContent={parsedContent}
inputRef={previewRef}
onScroll={onPreviewScroll}
onMouseEnter={() => {
setCurrentScrollArea("preview")
}}
></PageContent>
</>
)}
</div>
</div>
</div>
<div className="h-full overflow-auto flex-shrink-0 w-[280px] border-l bg-zinc-50 p-5 space-y-5">
{/* <div className="h-full overflow-auto flex-shrink-0 w-[280px] border-l bg-zinc-50 p-5 space-y-5">
<div>
<Input
type="datetime-local"
@ -596,7 +704,7 @@ export default function SubdomainEditor() {
help={t("Leave it blank to use auto-generated excerpt")}
/>
</div>
</div>
</div> */}
</div>
</>
)}

View File

@ -60,5 +60,5 @@ module.exports = {
},
},
variants: {},
plugins: [require("tailwindcss-animate")],
plugins: [require("tailwindcss-animate"), require("tailwind-scrollbar-hide")],
}