mirror of
https://github.com/khairul169/code-share.git
synced 2025-04-28 16:49:36 +07:00
fix: responsive fix
This commit is contained in:
parent
efe51a9b5e
commit
b232410d83
@ -1,73 +1,51 @@
|
|||||||
import { GripVertical } from "lucide-react";
|
import { GripVertical } from "lucide-react";
|
||||||
import { createContext, forwardRef, useContext } from "react";
|
import { forwardRef } from "react";
|
||||||
import * as ResizablePrimitive from "react-resizable-panels";
|
import * as ResizablePrimitive from "react-resizable-panels";
|
||||||
import cookieJs from "cookiejs";
|
|
||||||
|
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
import { usePageContext } from "~/renderer/context";
|
import {
|
||||||
import { useDebounce } from "~/hooks/useDebounce";
|
BreakpointValues,
|
||||||
|
useBreakpointValue,
|
||||||
|
} from "~/hooks/useBreakpointValue";
|
||||||
|
|
||||||
const ResizableContext = createContext<{ initialSize: number[] }>(null!);
|
type Direction = "horizontal" | "vertical";
|
||||||
|
|
||||||
|
type ResizablePanelGroupProps = Omit<
|
||||||
|
React.ComponentProps<typeof ResizablePrimitive.PanelGroup>,
|
||||||
|
"direction"
|
||||||
|
> & {
|
||||||
|
direction: Direction | BreakpointValues<Direction>;
|
||||||
|
};
|
||||||
|
|
||||||
const ResizablePanelGroup = ({
|
const ResizablePanelGroup = ({
|
||||||
className,
|
className,
|
||||||
autoSaveId,
|
|
||||||
direction,
|
direction,
|
||||||
...props
|
...props
|
||||||
}: React.ComponentProps<typeof ResizablePrimitive.PanelGroup>) => {
|
}: ResizablePanelGroupProps) => {
|
||||||
const { cookies } = usePageContext();
|
const directionValue = useBreakpointValue(direction);
|
||||||
const [debouncePersistLayout] = useDebounce((sizes: number[]) => {
|
|
||||||
if (autoSaveId && typeof window !== "undefined") {
|
|
||||||
cookieJs.set(panelKey, JSON.stringify(sizes));
|
|
||||||
}
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
const panelKey = ["panel", direction, autoSaveId].join(":");
|
|
||||||
let initialSize: number[] = [];
|
|
||||||
|
|
||||||
if (autoSaveId && cookies && cookies[panelKey]) {
|
|
||||||
initialSize = JSON.parse(cookies[panelKey]) || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const onLayout = (sizes: number[]) => {
|
|
||||||
if (props.onLayout) {
|
|
||||||
props.onLayout(sizes);
|
|
||||||
}
|
|
||||||
debouncePersistLayout(sizes);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizableContext.Provider value={{ initialSize }}>
|
|
||||||
<ResizablePrimitive.PanelGroup
|
<ResizablePrimitive.PanelGroup
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
"flex h-full w-full data-[panel-group-direction=vertical]:flex-col",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
direction={directionValue}
|
||||||
{...props}
|
{...props}
|
||||||
direction={direction}
|
|
||||||
onLayout={onLayout}
|
|
||||||
/>
|
/>
|
||||||
</ResizableContext.Provider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
type ResizablePanelProps = React.ComponentProps<
|
type ResizablePanelProps = Omit<
|
||||||
typeof ResizablePrimitive.Panel
|
React.ComponentProps<typeof ResizablePrimitive.Panel>,
|
||||||
|
"defaultSize"
|
||||||
> & {
|
> & {
|
||||||
panelId: number;
|
defaultSize: number | BreakpointValues<number>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ResizablePanel = forwardRef((props: ResizablePanelProps, ref: any) => {
|
const ResizablePanel = forwardRef((props: ResizablePanelProps, ref: any) => {
|
||||||
const { panelId, defaultSize, ...restProps } = props;
|
const { defaultSize, ...restProps } = props;
|
||||||
const ctx = useContext(ResizableContext);
|
const initialSize = useBreakpointValue(defaultSize);
|
||||||
let initialSize = defaultSize;
|
|
||||||
|
|
||||||
if (panelId != null) {
|
|
||||||
const size = ctx?.initialSize[panelId];
|
|
||||||
if (size != null) {
|
|
||||||
initialSize = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ResizablePrimitive.Panel
|
<ResizablePrimitive.Panel
|
||||||
|
57
hooks/useBreakpoint.ts
Normal file
57
hooks/useBreakpoint.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
export const breakpoints = {
|
||||||
|
sm: 640,
|
||||||
|
md: 768,
|
||||||
|
lg: 1024,
|
||||||
|
xl: 1280,
|
||||||
|
"2xl": 1536,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Breakpoint = keyof typeof breakpoints;
|
||||||
|
|
||||||
|
export const breakpointKeys = Object.keys(breakpoints);
|
||||||
|
export const breakpointValues = Object.values(breakpoints);
|
||||||
|
|
||||||
|
export const useBreakpoint = (onChange?: (breakpoint: number) => void) => {
|
||||||
|
const prevRef = useRef<number>(0);
|
||||||
|
const [curBreakpoint, setBreakpoint] = useState<number>(
|
||||||
|
getScreenBreakpoint()
|
||||||
|
);
|
||||||
|
|
||||||
|
const onResize = useCallback(() => {
|
||||||
|
const breakpointIdx = getScreenBreakpoint();
|
||||||
|
|
||||||
|
if (breakpointIdx >= 0 && prevRef.current !== breakpointIdx) {
|
||||||
|
if (onChange) {
|
||||||
|
onChange(breakpointIdx);
|
||||||
|
} else {
|
||||||
|
setBreakpoint(breakpointIdx);
|
||||||
|
}
|
||||||
|
prevRef.current = breakpointIdx;
|
||||||
|
}
|
||||||
|
}, [onChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("resize", onResize);
|
||||||
|
onResize();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", onResize);
|
||||||
|
};
|
||||||
|
}, [onResize]);
|
||||||
|
|
||||||
|
const breakpoint = breakpointKeys[curBreakpoint];
|
||||||
|
|
||||||
|
return [curBreakpoint, breakpoint] as [number, Breakpoint];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getScreenBreakpoint() {
|
||||||
|
const width = typeof window !== "undefined" ? window.innerWidth : 0;
|
||||||
|
let breakpointIdx = breakpointValues.findIndex((i) => width <= i);
|
||||||
|
if (breakpointIdx < 0) {
|
||||||
|
breakpointIdx = breakpointKeys.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return breakpointIdx;
|
||||||
|
}
|
55
hooks/useBreakpointValue.ts
Normal file
55
hooks/useBreakpointValue.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Breakpoint,
|
||||||
|
breakpointKeys,
|
||||||
|
getScreenBreakpoint,
|
||||||
|
useBreakpoint,
|
||||||
|
} from "./useBreakpoint";
|
||||||
|
|
||||||
|
export type BreakpointValues<T> = Partial<Record<Breakpoint, T | null>>;
|
||||||
|
|
||||||
|
export const useBreakpointValue = <T>(values: T | BreakpointValues<T>) => {
|
||||||
|
const [value, setValue] = useState(
|
||||||
|
typeof values === "object"
|
||||||
|
? getValueByBreakpoint(
|
||||||
|
values as BreakpointValues<T>,
|
||||||
|
getScreenBreakpoint()
|
||||||
|
)
|
||||||
|
: values
|
||||||
|
);
|
||||||
|
|
||||||
|
useBreakpoint((breakpoint) => {
|
||||||
|
if (typeof values !== "object") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newValue = getValueByBreakpoint(
|
||||||
|
values as BreakpointValues<T>,
|
||||||
|
breakpoint
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newValue !== value) {
|
||||||
|
setValue(newValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return value as T;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getValueByBreakpoint<T>(
|
||||||
|
values: BreakpointValues<T>,
|
||||||
|
breakpoint: number
|
||||||
|
) {
|
||||||
|
const valueEntries = Object.entries(values as never);
|
||||||
|
|
||||||
|
let resIdx = valueEntries.findIndex(([key]) => {
|
||||||
|
const bpIdx = breakpointKeys.indexOf(key);
|
||||||
|
return breakpoint <= bpIdx;
|
||||||
|
});
|
||||||
|
if (resIdx < 0) {
|
||||||
|
resIdx = valueEntries.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = valueEntries[resIdx]?.[1] as T;
|
||||||
|
return value;
|
||||||
|
}
|
@ -59,7 +59,6 @@
|
|||||||
"clsx": "^2.1.0",
|
"clsx": "^2.1.0",
|
||||||
"console-feed": "^3.5.0",
|
"console-feed": "^3.5.0",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"cookiejs": "^2.1.3",
|
|
||||||
"copy-to-clipboard": "^3.3.3",
|
"copy-to-clipboard": "^3.3.3",
|
||||||
"cssnano": "^6.0.3",
|
"cssnano": "^6.0.3",
|
||||||
"drizzle-orm": "^0.29.3",
|
"drizzle-orm": "^0.29.3",
|
||||||
|
@ -4,6 +4,7 @@ import Link from "~/renderer/link";
|
|||||||
|
|
||||||
const HomePage = () => {
|
const HomePage = () => {
|
||||||
const { posts } = useData<Data>();
|
const { posts } = useData<Data>();
|
||||||
|
|
||||||
if (!posts?.length) {
|
if (!posts?.length) {
|
||||||
return <p>No posts.</p>;
|
return <p>No posts.</p>;
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,15 @@ import {
|
|||||||
ResizablePanelGroup,
|
ResizablePanelGroup,
|
||||||
} from "~/components/ui/resizable";
|
} from "~/components/ui/resizable";
|
||||||
import WebPreview from "./components/web-preview";
|
import WebPreview from "./components/web-preview";
|
||||||
import { usePortrait } from "~/hooks/usePortrait";
|
|
||||||
import Editor from "./components/editor";
|
import Editor from "./components/editor";
|
||||||
import ProjectContext from "./context/project";
|
import ProjectContext from "./context/project";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
import { useParams, useSearchParams } from "~/renderer/hooks";
|
import { useParams, useSearchParams } from "~/renderer/hooks";
|
||||||
import { BASE_URL } from "~/lib/consts";
|
import { BASE_URL } from "~/lib/consts";
|
||||||
|
import { withClientOnly } from "~/renderer/client-only";
|
||||||
|
import Spinner from "~/components/ui/spinner";
|
||||||
|
|
||||||
const ViewProjectPage = () => {
|
const ViewProjectPage = () => {
|
||||||
const isPortrait = usePortrait();
|
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const isCompact =
|
const isCompact =
|
||||||
@ -24,15 +24,14 @@ const ViewProjectPage = () => {
|
|||||||
<ProjectContext.Provider value={{ slug, isCompact }}>
|
<ProjectContext.Provider value={{ slug, isCompact }}>
|
||||||
<ResizablePanelGroup
|
<ResizablePanelGroup
|
||||||
autoSaveId="main-panel"
|
autoSaveId="main-panel"
|
||||||
direction={isPortrait ? "vertical" : "horizontal"}
|
direction={{ sm: "vertical", md: "horizontal" }}
|
||||||
className={cn("w-full !h-dvh bg-slate-600", !isCompact ? "md:p-4" : "")}
|
className={cn("w-full !h-dvh bg-slate-600", !isCompact ? "md:p-4" : "")}
|
||||||
>
|
>
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
panelId={0}
|
defaultSize={60}
|
||||||
defaultSize={isPortrait ? 50 : 60}
|
|
||||||
collapsible
|
collapsible
|
||||||
collapsedSize={0}
|
collapsedSize={0}
|
||||||
minSize={isPortrait ? 10 : 30}
|
minSize={30}
|
||||||
>
|
>
|
||||||
<Editor />
|
<Editor />
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
@ -45,8 +44,7 @@ const ViewProjectPage = () => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
panelId={1}
|
defaultSize={40}
|
||||||
defaultSize={isPortrait ? 50 : 40}
|
|
||||||
collapsible
|
collapsible
|
||||||
collapsedSize={0}
|
collapsedSize={0}
|
||||||
minSize={10}
|
minSize={10}
|
||||||
@ -58,4 +56,12 @@ const ViewProjectPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ViewProjectPage;
|
const LoadingPage = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex w-full h-dvh items-center justify-center">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withClientOnly(ViewProjectPage, LoadingPage);
|
||||||
|
@ -20,12 +20,14 @@ import { FaCompress, FaCompressArrowsAlt } from "react-icons/fa";
|
|||||||
import ConsoleLogger from "./console-logger";
|
import ConsoleLogger from "./console-logger";
|
||||||
import { useData } from "~/renderer/hooks";
|
import { useData } from "~/renderer/hooks";
|
||||||
import { Data } from "../+data";
|
import { Data } from "../+data";
|
||||||
|
import { useBreakpoint } from "~/hooks/useBreakpoint";
|
||||||
|
|
||||||
const Editor = () => {
|
const Editor = () => {
|
||||||
const { pinnedFiles } = useData<Data>();
|
const { pinnedFiles } = useData<Data>();
|
||||||
const trpcUtils = trpc.useUtils();
|
const trpcUtils = trpc.useUtils();
|
||||||
const project = useProjectContext();
|
const project = useProjectContext();
|
||||||
const sidebarPanel = useRef<ImperativePanelHandle>(null);
|
const sidebarPanel = useRef<ImperativePanelHandle>(null);
|
||||||
|
const [breakpoint] = useBreakpoint();
|
||||||
|
|
||||||
const [sidebarExpanded, setSidebarExpanded] = useState(false);
|
const [sidebarExpanded, setSidebarExpanded] = useState(false);
|
||||||
const [curTabIdx, setCurTabIdx] = useState(0);
|
const [curTabIdx, setCurTabIdx] = useState(0);
|
||||||
@ -168,8 +170,7 @@ const Editor = () => {
|
|||||||
<ResizablePanelGroup autoSaveId="veditor-panel" direction="horizontal">
|
<ResizablePanelGroup autoSaveId="veditor-panel" direction="horizontal">
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
ref={sidebarPanel}
|
ref={sidebarPanel}
|
||||||
panelId={0}
|
defaultSize={{ sm: 0, md: 25 }}
|
||||||
defaultSize={25}
|
|
||||||
minSize={10}
|
minSize={10}
|
||||||
collapsible
|
collapsible
|
||||||
collapsedSize={0}
|
collapsedSize={0}
|
||||||
@ -182,9 +183,9 @@ const Editor = () => {
|
|||||||
|
|
||||||
<ResizableHandle className="bg-slate-900" />
|
<ResizableHandle className="bg-slate-900" />
|
||||||
|
|
||||||
<ResizablePanel panelId={1} defaultSize={75}>
|
<ResizablePanel defaultSize={{ sm: 100, md: 75 }}>
|
||||||
<ResizablePanelGroup autoSaveId="code-editor" direction="vertical">
|
<ResizablePanelGroup autoSaveId="code-editor" direction="vertical">
|
||||||
<ResizablePanel panelId={0} defaultSize={80} minSize={20}>
|
<ResizablePanel defaultSize={{ sm: 100, md: 80 }} minSize={20}>
|
||||||
<Tabs
|
<Tabs
|
||||||
tabs={openFileList}
|
tabs={openFileList}
|
||||||
current={curTabIdx}
|
current={curTabIdx}
|
||||||
@ -193,16 +194,20 @@ const Editor = () => {
|
|||||||
/>
|
/>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
|
|
||||||
|
{breakpoint >= 2 ? (
|
||||||
|
<>
|
||||||
<ResizableHandle />
|
<ResizableHandle />
|
||||||
|
|
||||||
<ResizablePanel
|
<ResizablePanel
|
||||||
panelId={1}
|
defaultSize={{ sm: 0, md: 20 }}
|
||||||
defaultSize={20}
|
|
||||||
collapsible
|
collapsible
|
||||||
collapsedSize={0}
|
collapsedSize={0}
|
||||||
minSize={10}
|
minSize={10}
|
||||||
>
|
>
|
||||||
<ConsoleLogger />
|
<ConsoleLogger />
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { getFileExt } from "~/lib/utils";
|
import { getFileExt } from "~/lib/utils";
|
||||||
import React from "react";
|
|
||||||
import CodeEditor from "../../../../components/ui/code-editor";
|
import CodeEditor from "../../../../components/ui/code-editor";
|
||||||
import trpc from "~/lib/trpc";
|
import trpc from "~/lib/trpc";
|
||||||
import { useData } from "~/renderer/hooks";
|
import { useData } from "~/renderer/hooks";
|
||||||
import { Data } from "../+data";
|
import { Data } from "../+data";
|
||||||
import ClientOnly from "~/renderer/client-only";
|
|
||||||
import Spinner from "~/components/ui/spinner";
|
import Spinner from "~/components/ui/spinner";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@ -42,14 +40,12 @@ const FileViewer = ({ id, onFileContentChange }: Props) => {
|
|||||||
const ext = getFileExt(filename);
|
const ext = getFileExt(filename);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ClientOnly fallback={<SSRCodeEditor value={data?.content} />}>
|
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
lang={ext}
|
lang={ext}
|
||||||
value={data?.content || ""}
|
value={data?.content || ""}
|
||||||
formatOnSave
|
formatOnSave
|
||||||
onChange={(val) => updateFileContent.mutate({ id, content: val })}
|
onChange={(val) => updateFileContent.mutate({ id, content: val })}
|
||||||
/>
|
/>
|
||||||
</ClientOnly>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,14 +60,4 @@ const LoadingLayout = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SSRCodeEditor = ({ value }: { value?: string | null }) => {
|
|
||||||
return (
|
|
||||||
<textarea
|
|
||||||
className="w-full h-full py-3 pl-11 pr-2 overflow-x-auto text-nowrap font-mono text-sm md:text-[16px] md:leading-[22px] bg-[#1a1b26] text-[#787c99]"
|
|
||||||
value={value || ""}
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FileViewer;
|
export default FileViewer;
|
||||||
|
7
pnpm-lock.yaml
generated
7
pnpm-lock.yaml
generated
@ -74,9 +74,6 @@ dependencies:
|
|||||||
cookie-parser:
|
cookie-parser:
|
||||||
specifier: ^1.4.6
|
specifier: ^1.4.6
|
||||||
version: 1.4.6
|
version: 1.4.6
|
||||||
cookiejs:
|
|
||||||
specifier: ^2.1.3
|
|
||||||
version: 2.1.3
|
|
||||||
copy-to-clipboard:
|
copy-to-clipboard:
|
||||||
specifier: ^3.3.3
|
specifier: ^3.3.3
|
||||||
version: 3.3.3
|
version: 3.3.3
|
||||||
@ -2925,10 +2922,6 @@ packages:
|
|||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/cookiejs@2.1.3:
|
|
||||||
resolution: {integrity: sha512-pA/nRQVka2eTXm1/Dq8pNt1PN+e1PJNItah0vL15qwpet81/tUfrAp8e0iiVM8WEAzDcTGK5/1hDyR6BdBZMVg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/copy-anything@3.0.5:
|
/copy-anything@3.0.5:
|
||||||
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
|
resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
|
||||||
engines: {node: '>=12.13'}
|
engines: {node: '>=12.13'}
|
||||||
|
@ -3,7 +3,7 @@ import type { ReactNode } from "react";
|
|||||||
|
|
||||||
type ClientOnlyProps = {
|
type ClientOnlyProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
fallback?: ReactNode | null;
|
fallback?: () => JSX.Element | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ClientOnly = ({ children, fallback }: ClientOnlyProps) => {
|
const ClientOnly = ({ children, fallback }: ClientOnlyProps) => {
|
||||||
@ -14,15 +14,19 @@ const ClientOnly = ({ children, fallback }: ClientOnlyProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (typeof window === "undefined") {
|
if (typeof window === "undefined") {
|
||||||
return fallback;
|
return fallback ? fallback() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isMounted ? children : fallback;
|
if (isMounted) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallback ? fallback() : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const withClientOnly = <T extends unknown>(
|
export const withClientOnly = <T extends unknown>(
|
||||||
Component: React.ComponentType<T>,
|
Component: React.ComponentType<T>,
|
||||||
fallback?: ReactNode | null
|
fallback?: () => JSX.Element | null
|
||||||
): React.ComponentType<T> => {
|
): React.ComponentType<T> => {
|
||||||
return (props: any) => (
|
return (props: any) => (
|
||||||
<ClientOnly fallback={fallback}>
|
<ClientOnly fallback={fallback}>
|
||||||
|
@ -1,20 +1,14 @@
|
|||||||
import postcssPlugin from "postcss";
|
import postcssPlugin from "postcss";
|
||||||
import tailwindcss from "tailwindcss";
|
import tailwindcss from "tailwindcss";
|
||||||
import cssnano from "cssnano";
|
import cssnano from "cssnano";
|
||||||
import { fileExists, getProjectDir } from "~/server/lib/utils";
|
|
||||||
import { FileSchema } from "~/server/db/schema/file";
|
import { FileSchema } from "~/server/db/schema/file";
|
||||||
import { unpackProject } from "~/server/lib/unpack-project";
|
import { unpackProject } from "~/server/lib/unpack-project";
|
||||||
|
|
||||||
export const postcss = async (fileData: FileSchema) => {
|
export const postcss = async (fileData: FileSchema) => {
|
||||||
const content = fileData.content || "";
|
const content = fileData.content || "";
|
||||||
|
|
||||||
const projectDir = getProjectDir();
|
|
||||||
if (!fileExists(projectDir)) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await unpackProject({ ext: "ts,tsx,js,jsx,html" });
|
const projectDir = await unpackProject({ ext: "ts,tsx,js,jsx,html" });
|
||||||
|
|
||||||
const result = await postcssPlugin([
|
const result = await postcssPlugin([
|
||||||
tailwindcss({
|
tailwindcss({
|
||||||
@ -29,6 +23,7 @@ export const postcss = async (fileData: FileSchema) => {
|
|||||||
|
|
||||||
return result.css;
|
return result.css;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error("postcss error", err);
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,6 +23,7 @@ export const unpackProject = async (
|
|||||||
|
|
||||||
const projectDir = getProjectDir();
|
const projectDir = getProjectDir();
|
||||||
if (!fileExists(projectDir)) {
|
if (!fileExists(projectDir)) {
|
||||||
|
console.log("not exist", projectDir);
|
||||||
await fs.mkdir(projectDir, { recursive: true });
|
await fs.mkdir(projectDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user