feat: add toolbar to notepad

This commit is contained in:
MAZE 2024-02-24 19:23:17 +03:30
parent ae3ea8c74f
commit 7463334053
7 changed files with 143 additions and 4 deletions

View file

@ -0,0 +1,39 @@
.button {
display: flex;
align-items: center;
justify-content: center;
width: 30px;
height: 30px;
font-size: var(--font-sm);
color: var(--color-foreground);
cursor: pointer;
background-color: var(--color-neutral-100);
border: 1px solid var(--color-neutral-200);
border-radius: 4px;
outline: none;
transition: 0.2s;
transition-property: border-color, color, background-color;
&.critical {
color: #f43f5e;
border-color: #f43f5e;
&:hover {
background-color: rgb(244 63 94 / 10%);
}
}
&.recommended {
font-size: var(--font-xsm);
color: #22c55e;
border-color: #22c55e;
&:hover {
background-color: rgb(34 197 94 / 10%);
}
}
&:hover {
background-color: var(--color-neutral-200);
}
}

View file

@ -0,0 +1,36 @@
import { Tooltip } from '@/components/tooltip';
import { cn } from '@/helpers/styles';
import styles from './button.module.css';
interface ButtonProps {
critical?: boolean;
icon: React.ReactElement;
onClick: () => void;
recommended?: boolean;
tooltip: string;
}
export function Button({
critical,
icon,
onClick,
recommended,
tooltip,
}: ButtonProps) {
return (
<Tooltip content={tooltip} hideDelay={0} placement="bottom" showDelay={0}>
<button
className={cn(
styles.button,
critical && styles.critical,
recommended && styles.recommended,
)}
onClick={onClick}
>
{icon}
</button>
</Tooltip>
);
}

View file

@ -0,0 +1 @@
export { Button } from './button';

View file

@ -9,6 +9,12 @@
font-weight: 500; font-weight: 500;
color: var(--color-foreground-subtle); color: var(--color-foreground-subtle);
} }
& .buttons {
display: flex;
column-gap: 4px;
align-items: center;
}
} }
.textarea { .textarea {
@ -27,7 +33,7 @@
.counter { .counter {
margin-top: 8px; margin-top: 8px;
font-size: var(--font-sm); font-size: var(--font-xsm);
color: var(--color-foreground-subtle); color: var(--color-foreground-subtle);
text-align: center; text-align: center;
} }

View file

@ -1,7 +1,16 @@
import { BiTrash } from 'react-icons/bi/index';
import { LuCopy, LuDownload } from 'react-icons/lu/index';
import { FaCheck } from 'react-icons/fa6/index';
import { FaUndo } from 'react-icons/fa/index';
import { Modal } from '@/components/modal'; import { Modal } from '@/components/modal';
import { Button } from './button';
import { useNoteStore } from '@/store';
import { useCopy } from '@/hooks/use-copy';
import { download } from '@/helpers/download';
import styles from './notepad.module.css'; import styles from './notepad.module.css';
import { useNoteStore } from '@/store';
interface NotepadProps { interface NotepadProps {
onClose: () => void; onClose: () => void;
@ -10,19 +19,44 @@ interface NotepadProps {
export function Notepad({ onClose, show }: NotepadProps) { export function Notepad({ onClose, show }: NotepadProps) {
const note = useNoteStore(state => state.note); const note = useNoteStore(state => state.note);
const history = useNoteStore(state => state.history);
const write = useNoteStore(state => state.write); const write = useNoteStore(state => state.write);
const words = useNoteStore(state => state.words()); const words = useNoteStore(state => state.words());
const characters = useNoteStore(state => state.characters()); const characters = useNoteStore(state => state.characters());
const clear = useNoteStore(state => state.clear);
const restore = useNoteStore(state => state.restore);
const { copy, copying } = useCopy();
return ( return (
<Modal show={show} wide onClose={onClose}> <Modal show={show} wide onClose={onClose}>
<header className={styles.header}> <header className={styles.header}>
<h2 className={styles.label}>Your Note</h2> <h2 className={styles.label}>Your Note</h2>
<div className={styles.buttons}>
<Button
icon={copying ? <FaCheck /> : <LuCopy />}
tooltip="Copy Note"
onClick={() => copy(note)}
/>
<Button
icon={<LuDownload />}
tooltip="Download Note"
onClick={() => download('Moodit Note.txt', note)}
/>
<Button
critical={!history}
icon={history ? <FaUndo /> : <BiTrash />}
recommended={history}
tooltip="Clear Note"
onClick={() => (history ? restore() : clear())}
/>
</div>
</header> </header>
<textarea <textarea
className={styles.textarea} className={styles.textarea}
dir="auto" dir="auto"
placeholder="What is on your mind?"
value={note} value={note}
onChange={e => write(e.target.value)} onChange={e => write(e.target.value)}
/> />

9
src/helpers/download.ts Normal file
View file

@ -0,0 +1,9 @@
export function download(filename: string, content: string) {
const element = document.createElement('a');
element.setAttribute(
'href',
'data:text/plain;charset=utf-8,' + encodeURIComponent(content),
);
element.setAttribute('download', filename);
element.click();
}

View file

@ -3,6 +3,8 @@ import type { StateCreator } from 'zustand';
import type { NoteState } from './note.state'; import type { NoteState } from './note.state';
export interface NoteActions { export interface NoteActions {
clear: () => void;
restore: () => void;
write: (note: string) => void; write: (note: string) => void;
} }
@ -11,10 +13,22 @@ export const createActions: StateCreator<
[], [],
[], [],
NoteActions NoteActions
> = set => { > = (set, get) => {
return { return {
clear() {
if (!get().note) return;
set({ history: get().note, note: '' });
},
restore() {
if (!get().history) return;
set({ history: null, note: get().history! });
},
write(note) { write(note) {
set({ note }); set({ history: null, note });
}, },
}; };
}; };