mirror of
https://github.com/muerwre/markdown-home-tab.git
synced 2025-04-25 00:46:41 +07:00
made human-friendly editing buttons
This commit is contained in:
parent
6d854f57a5
commit
02333e6049
15 changed files with 175 additions and 28 deletions
3
src/assets/images/gear.svg
Normal file
3
src/assets/images/gear.svg
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48">
|
||||||
|
<path d="m388 976-20-126q-19-7-40-19t-37-25l-118 54-93-164 108-79q-2-9-2.5-20.5T185 576q0-9 .5-20.5T188 535L80 456l93-164 118 54q16-13 37-25t40-18l20-127h184l20 126q19 7 40.5 18.5T669 346l118-54 93 164-108 77q2 10 2.5 21.5t.5 21.5q0 10-.5 21t-2.5 21l108 78-93 164-118-54q-16 13-36.5 25.5T592 850l-20 126H388Zm92-270q54 0 92-38t38-92q0-54-38-92t-92-38q-54 0-92 38t-38 92q0 54 38 92t92 38Zm0-60q-29 0-49.5-20.5T410 576q0-29 20.5-49.5T480 506q29 0 49.5 20.5T550 576q0 29-20.5 49.5T480 646Zm0-70Zm-44 340h88l14-112q33-8 62.5-25t53.5-41l106 46 40-72-94-69q4-17 6.5-33.5T715 576q0-17-2-33.5t-7-33.5l94-69-40-72-106 46q-23-26-52-43.5T538 348l-14-112h-88l-14 112q-34 7-63.5 24T306 414l-106-46-40 72 94 69q-4 17-6.5 33.5T245 576q0 17 2.5 33.5T254 643l-94 69 40 72 106-46q24 24 53.5 41t62.5 25l14 112Z" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 891 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48">
|
|
||||||
<path d="M220 976q-24.75 0-42.375-17.625T160 916V482q0-24.75 17.625-42.375T220 422h70v-96q0-78.85 55.606-134.425Q401.212 136 480.106 136T614.5 191.575Q670 247.15 670 326v96h70q24.75 0 42.375 17.625T800 482v434q0 24.75-17.625 42.375T740 976H220Zm0-60h520V482H220v434Zm260.168-140Q512 776 534.5 753.969T557 701q0-30-22.668-54.5t-54.5-24.5Q448 622 425.5 646.5t-22.5 55q0 30.5 22.668 52.5t54.5 22ZM350 422h260v-96q0-54.167-37.882-92.083-37.883-37.917-92-37.917Q426 196 388 233.917 350 271.833 350 326v96ZM220 916V482v434Z" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 617 B |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48">
|
|
||||||
<path d="M220 422h390v-96q0-54.167-37.882-92.083-37.883-37.917-92-37.917Q426 196 388 233.917 350 271.833 350 326h-60q0-79 55.606-134.5t134.5-55.5Q559 136 614.5 191.575T670 326v96h70q24.75 0 42.375 17.625T800 482v434q0 24.75-17.625 42.375T740 976H220q-24.75 0-42.375-17.625T160 916V482q0-24.75 17.625-42.375T220 422Zm0 494h520V482H220v434Zm260.168-140Q512 776 534.5 753.969T557 701q0-30-22.668-54.5t-54.5-24.5Q448 622 425.5 646.5t-22.5 55q0 30.5 22.668 52.5t54.5 22ZM220 916V482v434Z" />
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 582 B |
31
src/components/buttons/Button/index.tsx
Normal file
31
src/components/buttons/Button/index.tsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import classNames from "classnames";
|
||||||
|
import React, { FC } from "react";
|
||||||
|
import styles from "./styles.module.scss";
|
||||||
|
|
||||||
|
interface ButtonProps
|
||||||
|
extends React.DetailedHTMLProps<
|
||||||
|
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
HTMLButtonElement
|
||||||
|
> {
|
||||||
|
variant?: "solid" | "outline";
|
||||||
|
size?: "normal" | "small";
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button: FC<ButtonProps> = ({
|
||||||
|
variant = "solid",
|
||||||
|
size = "normal",
|
||||||
|
className,
|
||||||
|
...rest
|
||||||
|
}) => (
|
||||||
|
<button
|
||||||
|
className={classNames(
|
||||||
|
styles.button,
|
||||||
|
styles[`variant-${variant}`],
|
||||||
|
styles[`size-${size}`],
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...rest}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export { Button };
|
25
src/components/buttons/Button/styles.module.scss
Normal file
25
src/components/buttons/Button/styles.module.scss
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
.button {
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--color-text);
|
||||||
|
color: var(--color-background);
|
||||||
|
background-color: var(--color-text);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.25s;
|
||||||
|
|
||||||
|
&.size-small {
|
||||||
|
padding: 2px 16px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.variant-outline {
|
||||||
|
color: var(--color-text);
|
||||||
|
background-color: var(--color-background);
|
||||||
|
border: 1px solid var(--color-text);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid var(--color-text);
|
||||||
|
color: var(--color-background);
|
||||||
|
background-color: var(--color-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/modules/editor/components/EditorWrapper/index.tsx
Normal file
25
src/modules/editor/components/EditorWrapper/index.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { FC, PropsWithChildren } from "react";
|
||||||
|
import styles from "./styles.module.scss";
|
||||||
|
import { Button } from "~/components/buttons/Button";
|
||||||
|
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
|
||||||
|
|
||||||
|
interface EditorWrapperProps extends PropsWithChildren {
|
||||||
|
onSave: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditorWrapper: FC<EditorWrapperProps> = ({ children, onSave }) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<div className={styles.content}>{children}</div>
|
||||||
|
|
||||||
|
<div className={styles.panel}>
|
||||||
|
<div className={styles.filler} />
|
||||||
|
<Button onClick={onSave} role="button" size="small">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { EditorWrapper };
|
|
@ -0,0 +1,22 @@
|
||||||
|
.wrapper {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
flex: 0;
|
||||||
|
padding: 10px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filler {
|
||||||
|
flex: 1;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import styles from "./styles.module.scss";
|
import styles from "./styles.module.scss";
|
||||||
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
|
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
|
||||||
|
import { Button } from "~/components/buttons/Button";
|
||||||
|
|
||||||
interface EmptyViewerProps {
|
interface EmptyViewerProps {
|
||||||
startEditing?: () => void;
|
startEditing?: () => void;
|
||||||
|
@ -13,11 +14,14 @@ const EmptyViewer: FC<EmptyViewerProps> = ({ startEditing }) => {
|
||||||
<div className={styles.empty} style={style}>
|
<div className={styles.empty} style={style}>
|
||||||
<div className={styles.title}>Nothing's here</div>
|
<div className={styles.title}>Nothing's here</div>
|
||||||
<div>
|
<div>
|
||||||
<small>
|
<Button
|
||||||
<a href="javascript:void();" onClick={startEditing}>
|
onClick={startEditing}
|
||||||
start editing
|
role="button"
|
||||||
</a>
|
variant="outline"
|
||||||
</small>
|
size="small"
|
||||||
|
>
|
||||||
|
Edit it
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,10 +10,10 @@
|
||||||
transition: opacity 0.25s;
|
transition: opacity 0.25s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
opacity: 1;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
opacity: 0.5;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@ const ReactMarkdownEditor: FC<ReactMarkdownEditorProps> = ({
|
||||||
style={style}
|
style={style}
|
||||||
placeholder="Start typing here..."
|
placeholder="Start typing here..."
|
||||||
value={value}
|
value={value}
|
||||||
|
rows={1}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,18 +2,36 @@ import { FC } from "react";
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
|
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
|
||||||
import styles from "./styles.module.scss";
|
import styles from "./styles.module.scss";
|
||||||
|
import { Button } from "~/components/buttons/Button";
|
||||||
|
|
||||||
interface ReactMarkdownViewerProps {
|
interface ReactMarkdownViewerProps {
|
||||||
value: string;
|
value: string;
|
||||||
|
startEditing: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReactMarkdownViewer: FC<ReactMarkdownViewerProps> = ({ value }) => {
|
const ReactMarkdownViewer: FC<ReactMarkdownViewerProps> = ({
|
||||||
|
value,
|
||||||
|
startEditing,
|
||||||
|
}) => {
|
||||||
const style = useContainerPaddings();
|
const style = useContainerPaddings();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={style} className={styles.editor}>
|
<div style={style} className={styles.editor}>
|
||||||
|
<div className={styles.edit}>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outline"
|
||||||
|
role="button"
|
||||||
|
onClick={startEditing}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.content}>
|
||||||
<ReactMarkdown>{value}</ReactMarkdown>
|
<ReactMarkdown>{value}</ReactMarkdown>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,27 @@
|
||||||
.editor {
|
.editor {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.25s;
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.editor:hover & {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
& > :first-child {
|
& > :first-child {
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { usePersistedValue } from "./hooks/usePersistedValue";
|
||||||
import styles from "./styles.module.scss";
|
import styles from "./styles.module.scss";
|
||||||
import { useSettings } from "~/modules/settings/context/SettingsContext";
|
import { useSettings } from "~/modules/settings/context/SettingsContext";
|
||||||
import { EmptyViewer } from "../../components/EmptyViewer";
|
import { EmptyViewer } from "../../components/EmptyViewer";
|
||||||
|
import { EditorWrapper } from "../../components/EditorWrapper";
|
||||||
|
|
||||||
interface MarkdownEditorContainerProps {
|
interface MarkdownEditorContainerProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -34,14 +35,19 @@ export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
|
||||||
const viewer = empty ? (
|
const viewer = empty ? (
|
||||||
<EmptyViewer startEditing={startEditing} />
|
<EmptyViewer startEditing={startEditing} />
|
||||||
) : (
|
) : (
|
||||||
<ReactMarkdownViewer value={value} />
|
<ReactMarkdownViewer value={value} startEditing={startEditing} />
|
||||||
);
|
);
|
||||||
|
|
||||||
const editor = richEditorEnabled ? (
|
const editor = (
|
||||||
|
<EditorWrapper onSave={startEditing}>
|
||||||
|
{richEditorEnabled ? (
|
||||||
<RichEditor value={value} onChange={setValue} locked={locked} />
|
<RichEditor value={value} onChange={setValue} locked={locked} />
|
||||||
) : (
|
) : (
|
||||||
<ReactMarkdownEditor value={value} onChange={setValue} />
|
<ReactMarkdownEditor value={value} onChange={setValue} />
|
||||||
|
)}
|
||||||
|
</EditorWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.editor}>
|
<div className={styles.editor}>
|
||||||
<Suspense>{locked ? viewer : editor}</Suspense>
|
<Suspense>{locked ? viewer : editor}</Suspense>
|
||||||
|
|
|
@ -5,8 +5,7 @@ import { IconButton } from "~/components/buttons/IconButton";
|
||||||
import DeleteIcon from "~/assets/images/delete.svg";
|
import DeleteIcon from "~/assets/images/delete.svg";
|
||||||
import SplitVertical from "~/assets/images/split-vertical.svg";
|
import SplitVertical from "~/assets/images/split-vertical.svg";
|
||||||
import SplitHorizontal from "~/assets/images/split-horizontal.svg";
|
import SplitHorizontal from "~/assets/images/split-horizontal.svg";
|
||||||
import Locked from "~/assets/images/locked.svg";
|
import Gear from "~/assets/images/gear.svg";
|
||||||
import Unlocked from "~/assets/images/unlocked.svg";
|
|
||||||
|
|
||||||
type GridLayoutItemWrapperProps = PropsWithChildren & {
|
type GridLayoutItemWrapperProps = PropsWithChildren & {
|
||||||
splitVertical: () => void;
|
splitVertical: () => void;
|
||||||
|
@ -24,7 +23,6 @@ const GridLayoutItemWrapper: FC<GridLayoutItemWrapperProps> = ({
|
||||||
showSettings,
|
showSettings,
|
||||||
remove,
|
remove,
|
||||||
locked,
|
locked,
|
||||||
lock,
|
|
||||||
}) => (
|
}) => (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<div className={styles.menu}>
|
<div className={styles.menu}>
|
||||||
|
@ -49,7 +47,7 @@ const GridLayoutItemWrapper: FC<GridLayoutItemWrapperProps> = ({
|
||||||
role="button"
|
role="button"
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
>
|
>
|
||||||
S
|
<Gear />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
{!locked && (
|
{!locked && (
|
||||||
|
@ -57,10 +55,6 @@ const GridLayoutItemWrapper: FC<GridLayoutItemWrapperProps> = ({
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<IconButton onClick={lock} role="button" className={styles.button}>
|
|
||||||
{locked ? <Locked /> : <Unlocked />}
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
top: 2px;
|
top: 2px;
|
||||||
right: 4px;
|
right: 4px;
|
||||||
transition: all 0.25s;
|
transition: all 0.25s;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue