mirror of
https://github.com/muerwre/markdown-home-tab.git
synced 2025-04-25 08:56:41 +07:00
initial commit
This commit is contained in:
commit
9a4eb6ef58
23 changed files with 2490 additions and 0 deletions
|
@ -0,0 +1,55 @@
|
|||
import { DockviewApi, DockviewReadyEvent } from "dockview";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { createDefaultLayout } from "../utils/createDefaultLayout";
|
||||
|
||||
export const useGridLayoutPersistance = () => {
|
||||
const api = useRef<DockviewApi>();
|
||||
|
||||
const onReady = (event: DockviewReadyEvent) => {
|
||||
api.current = event.api;
|
||||
|
||||
const layoutString = localStorage.getItem("dockview_persistance_layout");
|
||||
|
||||
if (!layoutString) {
|
||||
createDefaultLayout(event.api);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const layout = JSON.parse(layoutString);
|
||||
event.api.fromJSON(layout);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
createDefaultLayout(event.api);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!api.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const disposable = api.current.onDidLayoutChange(() => {
|
||||
if (!api.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!api.current.groups.length) {
|
||||
createDefaultLayout(api.current);
|
||||
}
|
||||
|
||||
const layout = api.current.toJSON();
|
||||
|
||||
localStorage.setItem(
|
||||
"dockview_persistance_layout",
|
||||
JSON.stringify(layout)
|
||||
);
|
||||
});
|
||||
|
||||
return () => {
|
||||
disposable.dispose();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return { api, onReady };
|
||||
};
|
32
src/modules/layout/components/GridLayout/index.tsx
Normal file
32
src/modules/layout/components/GridLayout/index.tsx
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { DockviewReact, IDockviewPanelProps } from "dockview";
|
||||
import { useGridLayoutPersistance } from "./hooks/useGridLayoutPersistance";
|
||||
import { FC, createElement, useMemo } from "react";
|
||||
import { GridLayoutComponentProps } from "../../types";
|
||||
|
||||
export interface GridLayoutProps {
|
||||
component: FC<GridLayoutComponentProps>;
|
||||
}
|
||||
|
||||
export const GridLayout: FC<GridLayoutProps> = ({ component }) => {
|
||||
const { onReady } = useGridLayoutPersistance();
|
||||
|
||||
const components = useMemo(
|
||||
() => ({
|
||||
default: (props: IDockviewPanelProps<{ title: string }>) => {
|
||||
return createElement(component, {
|
||||
id: props.api.id,
|
||||
title: props.params.title,
|
||||
});
|
||||
},
|
||||
}),
|
||||
[component]
|
||||
);
|
||||
|
||||
return (
|
||||
<DockviewReact
|
||||
components={components}
|
||||
onReady={onReady}
|
||||
className="dockview-theme-abyss"
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,16 @@
|
|||
import { DockviewApi } from "dockview";
|
||||
import { v4 } from "uuid";
|
||||
|
||||
export const createDefaultLayout = (api: DockviewApi) => {
|
||||
api.addPanel({
|
||||
id: v4(),
|
||||
component: "default",
|
||||
title: "New editor",
|
||||
params: {
|
||||
title: "Panel 1",
|
||||
},
|
||||
});
|
||||
|
||||
// panel.group.locked = true;
|
||||
// panel.group.header.hidden = true;
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
import { FC } from "react";
|
||||
import {
|
||||
Editor,
|
||||
rootCtx,
|
||||
defaultValueCtx,
|
||||
editorViewOptionsCtx,
|
||||
} from "@milkdown/core";
|
||||
import { Milkdown, useEditor } from "@milkdown/react";
|
||||
import { commonmark } from "@milkdown/preset-commonmark";
|
||||
import { listener, listenerCtx } from "@milkdown/plugin-listener";
|
||||
import { clipboard } from "@milkdown/plugin-clipboard";
|
||||
|
||||
import styles from "./styles.module.scss";
|
||||
|
||||
interface MarkdownEditorProps {
|
||||
value?: string;
|
||||
onChange?: (val: string) => void;
|
||||
}
|
||||
|
||||
export const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
||||
value = "",
|
||||
onChange,
|
||||
}) => {
|
||||
useEditor((root) =>
|
||||
Editor.make()
|
||||
.config((ctx) => {
|
||||
ctx.set(rootCtx, root);
|
||||
ctx.set(defaultValueCtx, value);
|
||||
ctx.get(listenerCtx).markdownUpdated((_, markdown) => {
|
||||
onChange?.(markdown);
|
||||
});
|
||||
|
||||
ctx.update(editorViewOptionsCtx, (prev) => ({
|
||||
...prev,
|
||||
attributes: {
|
||||
class: styles.editor,
|
||||
spellcheck: "false",
|
||||
},
|
||||
}));
|
||||
})
|
||||
.use(commonmark)
|
||||
.use(listener)
|
||||
.use(clipboard)
|
||||
);
|
||||
|
||||
return <Milkdown />;
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
.editor {
|
||||
outline: none;
|
||||
height: 100%;
|
||||
|
||||
& > :first-child {
|
||||
margin-top: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
div[data-milkdown-root="true"] {
|
||||
height: 100%;
|
||||
|
||||
:global(.milkdown) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { useEffect, useState } from "react";
|
||||
|
||||
const prefix = "VALUE__";
|
||||
|
||||
const safelyGetStringValue = (key: string) => {
|
||||
try {
|
||||
return localStorage.getItem(key) ?? "";
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
return "";
|
||||
}
|
||||
};
|
||||
const safelySetStringValue = (key: string, value: string) => {
|
||||
try {
|
||||
return localStorage.setItem(key, value);
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
};
|
||||
|
||||
export const usePersistedValue = (
|
||||
id: string
|
||||
): [string, (val: string) => void] => {
|
||||
const key = `${prefix}${id}`;
|
||||
const [value, setValue] = useState(safelyGetStringValue(key));
|
||||
|
||||
useEffect(() => {
|
||||
setValue(safelyGetStringValue(key));
|
||||
}, [id, key]);
|
||||
|
||||
useEffect(() => {
|
||||
safelySetStringValue(key, value);
|
||||
}, [key, value]);
|
||||
|
||||
return [value, setValue];
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
import { MilkdownProvider } from "@milkdown/react";
|
||||
import { FC } from "react";
|
||||
import { MarkdownEditor } from "../../components/MarkdownEditor";
|
||||
import styles from "./styles.module.scss";
|
||||
import { usePersistedValue } from "./hooks/usePersistedValue";
|
||||
|
||||
interface MarkdownEditorContainerProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
|
||||
id,
|
||||
}) => {
|
||||
const [value, setValue] = usePersistedValue(id);
|
||||
|
||||
return (
|
||||
<div className={styles.editor}>
|
||||
<MilkdownProvider>
|
||||
<MarkdownEditor value={value} onChange={setValue} />
|
||||
</MilkdownProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
.editor {
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
overflow: scroll;
|
||||
box-sizing: border-box;
|
||||
}
|
4
src/modules/layout/types/index.ts
Normal file
4
src/modules/layout/types/index.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export interface GridLayoutComponentProps {
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue