made human-friendly editing buttons

This commit is contained in:
Fedor Katurov 2023-04-26 17:09:23 +06:00
parent 6d854f57a5
commit 02333e6049
15 changed files with 175 additions and 28 deletions

View 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

View file

@ -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

View file

@ -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

View 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 };

View 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);
}
}
}

View 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 };

View file

@ -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;
}

View file

@ -1,6 +1,7 @@
import { FC } from "react";
import styles from "./styles.module.scss";
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
import { Button } from "~/components/buttons/Button";
interface EmptyViewerProps {
startEditing?: () => void;
@ -13,11 +14,14 @@ const EmptyViewer: FC<EmptyViewerProps> = ({ startEditing }) => {
<div className={styles.empty} style={style}>
<div className={styles.title}>Nothing's here</div>
<div>
<small>
<a href="javascript:void();" onClick={startEditing}>
start editing
</a>
</small>
<Button
onClick={startEditing}
role="button"
variant="outline"
size="small"
>
Edit it
</Button>
</div>
</div>
);

View file

@ -10,10 +10,10 @@
transition: opacity 0.25s;
&:hover {
opacity: 1;
opacity: 0.5;
}
}
.title {
opacity: 0.5;
margin-bottom: 1em;
}

View file

@ -34,6 +34,7 @@ const ReactMarkdownEditor: FC<ReactMarkdownEditorProps> = ({
style={style}
placeholder="Start typing here..."
value={value}
rows={1}
/>
);
};

View file

@ -2,18 +2,36 @@ import { FC } from "react";
import ReactMarkdown from "react-markdown";
import { useContainerPaddings } from "~/modules/theme/hooks/useContainerPaddings";
import styles from "./styles.module.scss";
import { Button } from "~/components/buttons/Button";
interface ReactMarkdownViewerProps {
value: string;
startEditing: () => void;
}
const ReactMarkdownViewer: FC<ReactMarkdownViewerProps> = ({ value }) => {
const ReactMarkdownViewer: FC<ReactMarkdownViewerProps> = ({
value,
startEditing,
}) => {
const style = useContainerPaddings();
return (
<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>
</div>
</div>
);
};

View file

@ -1,4 +1,27 @@
.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 {
margin-top: 0 !important;
margin-left: 0 !important;

View file

@ -5,6 +5,7 @@ import { usePersistedValue } from "./hooks/usePersistedValue";
import styles from "./styles.module.scss";
import { useSettings } from "~/modules/settings/context/SettingsContext";
import { EmptyViewer } from "../../components/EmptyViewer";
import { EditorWrapper } from "../../components/EditorWrapper";
interface MarkdownEditorContainerProps {
id: string;
@ -34,14 +35,19 @@ export const MarkdownEditorContainer: FC<MarkdownEditorContainerProps> = ({
const viewer = empty ? (
<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} />
) : (
<ReactMarkdownEditor value={value} onChange={setValue} />
)}
</EditorWrapper>
);
return (
<div className={styles.editor}>
<Suspense>{locked ? viewer : editor}</Suspense>

View file

@ -5,8 +5,7 @@ import { IconButton } from "~/components/buttons/IconButton";
import DeleteIcon from "~/assets/images/delete.svg";
import SplitVertical from "~/assets/images/split-vertical.svg";
import SplitHorizontal from "~/assets/images/split-horizontal.svg";
import Locked from "~/assets/images/locked.svg";
import Unlocked from "~/assets/images/unlocked.svg";
import Gear from "~/assets/images/gear.svg";
type GridLayoutItemWrapperProps = PropsWithChildren & {
splitVertical: () => void;
@ -24,7 +23,6 @@ const GridLayoutItemWrapper: FC<GridLayoutItemWrapperProps> = ({
showSettings,
remove,
locked,
lock,
}) => (
<div className={styles.wrapper}>
<div className={styles.menu}>
@ -49,7 +47,7 @@ const GridLayoutItemWrapper: FC<GridLayoutItemWrapperProps> = ({
role="button"
className={styles.button}
>
S
<Gear />
</IconButton>
{!locked && (
@ -57,10 +55,6 @@ const GridLayoutItemWrapper: FC<GridLayoutItemWrapperProps> = ({
<DeleteIcon />
</IconButton>
)}
<IconButton onClick={lock} role="button" className={styles.button}>
{locked ? <Locked /> : <Unlocked />}
</IconButton>
</div>
{children}

View file

@ -17,6 +17,7 @@
top: 2px;
right: 4px;
transition: all 0.25s;
z-index: 1;
}
.button {