mirror of
https://github.com/khairul169/code-share.git
synced 2026-10-24 11:53:58 +07:00
feat: update layout
This commit is contained in:
@@ -23,12 +23,9 @@ const ViewProjectPage = () => {
|
||||
return (
|
||||
<ProjectContext.Provider value={{ project, isCompact, isEmbed }}>
|
||||
<ResizablePanelGroup
|
||||
autoSaveId="main-panel"
|
||||
autoSaveId={!isEmbed ? "main-panel" : null}
|
||||
direction={{ sm: "vertical", md: "horizontal" }}
|
||||
className={cn(
|
||||
"w-full !h-dvh bg-slate-600",
|
||||
!isCompact && !isEmbed ? "md:p-4" : ""
|
||||
)}
|
||||
className={cn("w-full !h-screen")}
|
||||
>
|
||||
<ResizablePanel
|
||||
defaultSize={hidePreview ? 100 : 60}
|
||||
@@ -38,14 +35,7 @@ const ViewProjectPage = () => {
|
||||
>
|
||||
<Editor />
|
||||
</ResizablePanel>
|
||||
<ResizableHandle
|
||||
withHandle
|
||||
className={
|
||||
!isCompact && !isEmbed
|
||||
? "bg-slate-800 md:bg-transparent hover:bg-slate-500 transition-colors md:mx-1 w-2 md:data-[panel-group-direction=vertical]:h-2 md:rounded-lg"
|
||||
: "bg-slate-800"
|
||||
}
|
||||
/>
|
||||
<ResizableHandle withHandle={!isEmbed && !isCompact} />
|
||||
<WebPreview
|
||||
defaultSize={40}
|
||||
defaultCollapsed={hidePreview}
|
||||
|
||||
@@ -12,7 +12,7 @@ export const data = async (ctx: PageContext) => {
|
||||
throw render(404, "Project not found!");
|
||||
}
|
||||
|
||||
const files = await trpc.file.getAll({ projectId: project.id });
|
||||
const files = await trpc.file.getAll({ projectId: project.id! });
|
||||
const initialFiles = files.filter((i) =>
|
||||
filesParam != null ? filesParam.includes(i.path) : i.isPinned
|
||||
);
|
||||
|
||||
@@ -4,12 +4,11 @@ import {
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from "~/components/ui/resizable";
|
||||
import Tabs, { Tab } from "~/components/ui/tabs";
|
||||
import Tabs, { Tab, TabView } from "~/components/ui/tabs";
|
||||
import FileViewer from "./file-viewer";
|
||||
import trpc from "~/lib/trpc";
|
||||
import EditorContext from "../context/editor";
|
||||
import type { FileSchema } from "~/server/db/schema/file";
|
||||
import Panel from "~/components/ui/panel";
|
||||
import { useProjectContext } from "../context/project";
|
||||
import Sidebar from "./sidebar";
|
||||
import ConsoleLogger from "./console-logger";
|
||||
@@ -22,11 +21,14 @@ import SettingsDialog from "./settings-dialog";
|
||||
import FileIcon from "~/components/ui/file-icon";
|
||||
import { api } from "~/lib/api";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { FaExternalLinkAlt } from "react-icons/fa";
|
||||
import { BASE_URL } from "~/lib/consts";
|
||||
|
||||
const Editor = () => {
|
||||
const { project, initialFiles } = useData<Data>();
|
||||
const trpcUtils = trpc.useUtils();
|
||||
const projectCtx = useProjectContext();
|
||||
const { isEmbed } = useProjectContext();
|
||||
const [breakpoint] = useBreakpoint();
|
||||
|
||||
const [curTabIdx, setCurTabIdx] = useState(0);
|
||||
@@ -178,8 +180,7 @@ const Editor = () => {
|
||||
return tabs;
|
||||
}, [curOpenFiles, openedFiles, breakpoint]);
|
||||
|
||||
const PanelComponent =
|
||||
!projectCtx.isCompact || !projectCtx.isEmbed ? Panel : "div";
|
||||
const currentTab = Math.min(Math.max(curTabIdx, 0), tabs.length - 1);
|
||||
|
||||
return (
|
||||
<EditorContext.Provider
|
||||
@@ -189,9 +190,9 @@ const Editor = () => {
|
||||
onDeleteFile,
|
||||
}}
|
||||
>
|
||||
<PanelComponent className="h-full relative flex flex-col">
|
||||
<div className="h-full relative flex flex-col">
|
||||
<ResizablePanelGroup
|
||||
autoSaveId="veditor-panel"
|
||||
autoSaveId={!isEmbed ? "veditor-panel" : null}
|
||||
direction="horizontal"
|
||||
className="flex-1 order-2 md:order-1"
|
||||
>
|
||||
@@ -206,15 +207,36 @@ const Editor = () => {
|
||||
<ResizableHandle className="w-0" />
|
||||
|
||||
<ResizablePanel defaultSize={{ md: 100, lg: 75 }}>
|
||||
<ResizablePanelGroup autoSaveId="code-editor" direction="vertical">
|
||||
<ResizablePanelGroup
|
||||
autoSaveId={!isEmbed ? "code-editor" : null}
|
||||
direction="vertical"
|
||||
>
|
||||
<ResizablePanel defaultSize={{ sm: 100, md: 80 }} minSize={20}>
|
||||
<Tabs
|
||||
tabs={tabs}
|
||||
current={Math.min(Math.max(curTabIdx, 0), tabs.length - 1)}
|
||||
onChange={setCurTabIdx}
|
||||
onClose={onCloseFile}
|
||||
className="h-full"
|
||||
/>
|
||||
<div className="w-full h-full flex flex-col items-stretch bg-slate-800">
|
||||
<div className="flex items-center">
|
||||
<Tabs
|
||||
tabs={tabs}
|
||||
current={currentTab}
|
||||
onChange={setCurTabIdx}
|
||||
onClose={onCloseFile}
|
||||
className="flex-1 p-1 md:h-12 md:p-1.5 md:gap-1.5"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="dark:hover:bg-slate-700"
|
||||
onClick={() =>
|
||||
window.open(`${BASE_URL}/${project.slug}`, "_blank")
|
||||
}
|
||||
>
|
||||
<FaExternalLinkAlt />
|
||||
</Button>
|
||||
</div>
|
||||
<TabView
|
||||
tabs={tabs}
|
||||
current={currentTab}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
|
||||
{breakpoint >= 2 ? (
|
||||
@@ -236,7 +258,7 @@ const Editor = () => {
|
||||
</ResizablePanelGroup>
|
||||
|
||||
<StatusBar className="order-1 md:order-2" />
|
||||
</PanelComponent>
|
||||
</div>
|
||||
|
||||
<SettingsDialog />
|
||||
</EditorContext.Provider>
|
||||
|
||||
@@ -39,7 +39,7 @@ const FileListing = () => {
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="h-10 flex items-center pl-4 pr-1">
|
||||
<div className="h-10 md:h-12 flex items-center pl-4 pr-1">
|
||||
<p className="text-xs uppercase truncate flex-1">{project.title}</p>
|
||||
<ActionButton
|
||||
icon={FiFilePlus}
|
||||
|
||||
@@ -15,7 +15,7 @@ type Props = {
|
||||
const FileViewer = ({ id }: Props) => {
|
||||
const { project } = useProjectContext();
|
||||
const { initialFiles } = useData<Data>();
|
||||
const initialData = initialFiles.find((i) => i.id === id);
|
||||
const initialData = initialFiles.find((i) => i.id === id) as any;
|
||||
|
||||
const { data, isLoading, refetch } = trpc.file.getById.useQuery(id, {
|
||||
initialData,
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "~/components/ui/dialog";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Fragment, useMemo, useState } from "react";
|
||||
import { useProjectContext } from "../context/project";
|
||||
import { useForm, useFormReturn } from "~/hooks/useForm";
|
||||
import Input from "~/components/ui/input";
|
||||
@@ -21,8 +21,10 @@ import {
|
||||
import trpc from "~/lib/trpc";
|
||||
import { toast } from "~/lib/utils";
|
||||
import Checkbox from "~/components/ui/checkbox";
|
||||
import Tabs, { Tab } from "~/components/ui/tabs";
|
||||
import Tabs, { Tab, TabView } from "~/components/ui/tabs";
|
||||
import { navigate } from "vike/client/router";
|
||||
import { useFieldArray } from "react-hook-form";
|
||||
import { FaTrashAlt } from "react-icons/fa";
|
||||
|
||||
const defaultValues: ProjectSettingsSchema = {
|
||||
title: "",
|
||||
@@ -102,8 +104,9 @@ const SettingsDialog = () => {
|
||||
tabs={tabs}
|
||||
current={tab}
|
||||
onChange={setTab}
|
||||
containerClassName="mt-4"
|
||||
className="rounded-md overflow-hidden"
|
||||
/>
|
||||
<TabView current={tab} tabs={tabs} className="mt-4" />
|
||||
|
||||
<div className="flex flex-col sm:flex-row items-stretch sm:items-center sm:justify-end gap-4 mt-8">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
@@ -157,17 +160,55 @@ const CSSTab = ({ form }: TabProps) => {
|
||||
};
|
||||
|
||||
const JSTab = ({ form }: TabProps) => {
|
||||
const packages = useFieldArray({
|
||||
control: form.control,
|
||||
name: "settings.js.packages",
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div>
|
||||
<Select
|
||||
form={form}
|
||||
name="settings.js.transpiler"
|
||||
label="Transpiler"
|
||||
items={jsTranspilerList}
|
||||
/>
|
||||
<p className="text-sm">
|
||||
<p className="text-sm mt-1">
|
||||
* Set transpiler to <strong>SWC</strong> to use JSX
|
||||
</p>
|
||||
|
||||
<p className="text-sm mt-8">External Packages</p>
|
||||
<div>
|
||||
{packages.fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center gap-2 mt-2">
|
||||
<Input
|
||||
key={field.id}
|
||||
form={form}
|
||||
name={`settings.js.packages.${index}.name`}
|
||||
placeholder={`Package alias`}
|
||||
className="flex-1"
|
||||
/>
|
||||
|
||||
<Input
|
||||
key={field.id}
|
||||
form={form}
|
||||
name={`settings.js.packages.${index}.url`}
|
||||
placeholder={`URL`}
|
||||
className="flex-1"
|
||||
/>
|
||||
|
||||
<Button size="icon" onClick={() => packages.remove(index)}>
|
||||
<FaTrashAlt />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => packages.append({ name: "", url: "" })}
|
||||
className="mt-3"
|
||||
>
|
||||
Add Package
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,11 +17,11 @@ import { useProjectContext } from "../context/project";
|
||||
|
||||
const StatusBar = ({ className }: React.ComponentProps<"div">) => {
|
||||
const { user, urlPathname } = usePageContext();
|
||||
const { isCompact, project } = useProjectContext();
|
||||
const { isEmbed, project } = useProjectContext();
|
||||
const sidebarExpanded = useStore(sidebarStore, (i) => i.expanded);
|
||||
const previewExpanded = useStore(previewStore, (i) => i.open);
|
||||
|
||||
if (isCompact) {
|
||||
if (isEmbed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import Panel from "~/components/ui/panel";
|
||||
import { ComponentProps, useCallback, useEffect, useRef } from "react";
|
||||
import { useProjectContext } from "../context/project";
|
||||
import { Button } from "~/components/ui/button";
|
||||
@@ -56,8 +55,6 @@ const WebPreview = ({ url, ...props }: WebPreviewProps) => {
|
||||
refresh();
|
||||
}, [refresh, togglePanel]);
|
||||
|
||||
const PanelComponent = !project.isCompact || !project.isEmbed ? Panel : "div";
|
||||
|
||||
return (
|
||||
<ResizablePanel
|
||||
ref={panelRef}
|
||||
@@ -65,8 +62,8 @@ const WebPreview = ({ url, ...props }: WebPreviewProps) => {
|
||||
onCollapse={() => previewStore.setState({ open: false })}
|
||||
{...props}
|
||||
>
|
||||
<PanelComponent className="h-full flex flex-col bg-slate-800">
|
||||
<div className="h-10 hidden md:flex items-center pl-4">
|
||||
<div className="h-full flex flex-col bg-slate-800">
|
||||
<div className="h-10 md:h-12 hidden md:flex items-center pl-4">
|
||||
<p className="flex-1 truncate text-xs uppercase">Preview</p>
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -75,13 +72,16 @@ const WebPreview = ({ url, ...props }: WebPreviewProps) => {
|
||||
>
|
||||
<FaRedo />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="dark:hover:bg-slate-700"
|
||||
onClick={() => window.open(url || "#", "_blank")}
|
||||
>
|
||||
<FaExternalLinkAlt />
|
||||
</Button>
|
||||
|
||||
{!project.isEmbed ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="dark:hover:bg-slate-700"
|
||||
onClick={() => window.open(url || "#", "_blank")}
|
||||
>
|
||||
<FaExternalLinkAlt />
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{url != null ? (
|
||||
@@ -92,7 +92,7 @@ const WebPreview = ({ url, ...props }: WebPreviewProps) => {
|
||||
sandbox="allow-scripts allow-forms"
|
||||
/>
|
||||
) : null}
|
||||
</PanelComponent>
|
||||
</div>
|
||||
</ResizablePanel>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user