diff --git a/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelDetails.tsx b/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelDetails.tsx new file mode 100644 index 0000000..7626bd3 --- /dev/null +++ b/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelDetails.tsx @@ -0,0 +1,116 @@ +import { FC, MouseEvent, useRef, useState } from 'react'; + +import { MdMenu } from '@material/web/all'; + +import { menuItemStyles } from '@pages/Notes/lib/const'; +import cn from '@shared/lib/helpers/cn'; +import ConfirmDialog from '@shared/ui/ConfirmDialog'; +import ContextMenu from '@shared/ui/ContextMenu'; +import FilledTonalIconButton from '@shared/ui/FilledTonalIconButton'; +import Icon from '@shared/ui/Icon'; +import InputDialog from '@shared/ui/InputDialog'; +import MenuItem from '@shared/ui/MenuItem'; +import { useTranslation } from 'react-i18next'; + +type DetailsProps = { + id: number; +}; + +const Details: FC = ({ id }) => { + const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); + const [isDialogRenameOpen, setIsDialogRenameOpen] = useState(false); + const [isContextMenuOpen, setIsContextMenuOpen] = useState(false); + const { t } = useTranslation(); + const menuRef = useRef(null); + + function handleMenuToggle(e: MouseEvent) { + e.stopPropagation(); + e.preventDefault(); + if (menuRef.current) { + menuRef.current.open = !menuRef.current.open; + + setIsContextMenuOpen(menuRef.current.open); + } + } + + const handleToggleConfirmDialog = (e: MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + console.log('confirm'); + + setIsConfirmDialogOpen((prev) => !prev); + }; + + const handleDeleteLabel = async (e: MouseEvent) => { + e.stopPropagation(); + }; + + const handleToggleDialogRename = (e: MouseEvent) => { + e.stopPropagation(); + e.preventDefault(); + + console.log('rename'); + setIsDialogRenameOpen((prev) => !prev); + }; + + const handleRenameLabel = async (e: MouseEvent) => { + e.stopPropagation(); + }; + + return ( + <> + + + handleMenuToggle(e)}> + more_vert + + }> + + Rename + + edit + + + + Delete + + delete + + + + + ); +}; + +export default Details; diff --git a/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelItem.tsx b/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelItem.tsx new file mode 100644 index 0000000..686b6c6 --- /dev/null +++ b/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelItem.tsx @@ -0,0 +1,81 @@ +import { FC, PropsWithChildren } from 'react'; + +import { filledIconStyles } from '@shared/lib/const'; +import cn from '@shared/lib/helpers/cn'; +import Icon from '@shared/ui/Icon'; +import { motion } from 'framer-motion'; +import { NavLink } from 'react-router-dom'; + +type TabItemProps = PropsWithChildren<{ + id: number; + to: string; +}>; + +const animations = { + initial: { scale: 0, opacity: 0 }, + animate: { scale: 1, opacity: 1 }, + transition: { type: 'spring', stiffness: 550, damping: 40 }, +}; + +const LabelItem: FC = ({ children, to }) => { + return ( + + + {({ isActive }) => ( + <> + + + + + label + + + {children} + + + )} + + {/*
*/} + {/* */} + {/* */} + {/* */} + {/* label */} + {/* */} + {/* {children} */} + {/* */} + {/*
*/} +
+ ); +}; + +export default LabelItem; diff --git a/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelsList.tsx b/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelsList.tsx index ffb03ec..0197c7d 100644 --- a/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelsList.tsx +++ b/client/src/layout/NotesLayout/ui/NavigationDrawer/ui/LabelsList.tsx @@ -1,5 +1,6 @@ +import Details from '@layout/NotesLayout/ui/NavigationDrawer/ui/LabelDetails'; +import LabelItem from '@layout/NotesLayout/ui/NavigationDrawer/ui/LabelItem'; import { routes } from '@shared/lib/const'; -import NavItem from '@shared/ui/NavItem'; import { mockLabels } from '../../../../../../dev-data'; @@ -7,9 +8,12 @@ const LabelsList = () => { return (
    {mockLabels.map((label, i) => ( - - {label.title} - + + + {label.title} + +
    + ))}
); diff --git a/client/src/locales/en.json b/client/src/locales/en.json index 30701d5..3e5a49a 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -143,6 +143,18 @@ "userOptions": "User options", "options": "Options", "compose": "Compose a new note" + }, + "renameDialog": { + "title": "Rename this label", + "cancel": "Cancel", + "rename": "Rename" + }, + "deleteDialog": { + "title": "Delete label?", + "content": + "You'll no longer see this label. This will also delete label from all the related notes.", + "cancel": "Cancel", + "delete": "Delete" } } } diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json index 2978094..3ed9586 100644 --- a/client/src/locales/ru.json +++ b/client/src/locales/ru.json @@ -142,6 +142,18 @@ "userOptions": "Пользовательские опции", "options": "Опции", "compose": "Создать новую заметку" + }, + "renameDialog": { + "title": "Переименовать этот лейбл", + "cancel": "Отмена", + "rename": "Переименовать" + }, + "deleteDialog": { + "title": "Удалить лейбл?", + "content": + "Вы больше не увидите этот лейбл. Это также удалит лейбл из всех заметок.", + "cancel": "Отмена", + "delete": "Удалить" } } } diff --git a/client/src/shared/ui/ContextMenu.tsx b/client/src/shared/ui/ContextMenu.tsx index 89a213b..87f4953 100644 --- a/client/src/shared/ui/ContextMenu.tsx +++ b/client/src/shared/ui/ContextMenu.tsx @@ -17,6 +17,7 @@ type ContextMenuProps = PropsWithChildren<{ anchorCorner?: Corner; yOffset?: number; tooltipContent?: string; + className?: string; }>; const ContextMenu: FC = ({ @@ -27,6 +28,7 @@ const ContextMenu: FC = ({ anchorCorner = Corner.END_END, yOffset = 12, tooltipContent, + className, ...props }) => { const menuRef = useRef(null); @@ -35,6 +37,7 @@ const ContextMenu: FC = ({ const handleMenuOpen = (e: MouseEvent) => { e.stopPropagation(); + e.preventDefault(); if (menuRef.current?.open) { menuRef.current?.close(); @@ -56,7 +59,7 @@ const ContextMenu: FC = ({ ); return ( -
+
diff --git a/client/src/shared/ui/FilledTonalIconButton.tsx b/client/src/shared/ui/FilledTonalIconButton.tsx new file mode 100644 index 0000000..19361be --- /dev/null +++ b/client/src/shared/ui/FilledTonalIconButton.tsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import { createComponent } from '@lit/react'; +import { MdFilledTonalIconButton } from '@material/web/all'; + +const FilledTonalIconButton = createComponent({ + react: React, + tagName: 'md-filled-tonal-icon-button', + elementClass: MdFilledTonalIconButton, +}); + +export default FilledTonalIconButton; diff --git a/client/src/shared/ui/InputDialog.tsx b/client/src/shared/ui/InputDialog.tsx new file mode 100644 index 0000000..1e4856f --- /dev/null +++ b/client/src/shared/ui/InputDialog.tsx @@ -0,0 +1,81 @@ +import { + Dispatch, + FC, + MouseEvent, + SetStateAction, + useRef, + useState, +} from 'react'; + +import { MdDialog } from '@material/web/all'; +import { MdOutlinedTextField } from '@material/web/textfield/outlined-text-field'; + +import FilledTonalButton from '@/shared/ui/FilledTonalButton'; +import TextButton from '@/shared/ui/TextButton'; +import Dialog from '@shared/ui/Dialog'; +import OutlinedTextField from '@shared/ui/OutlinedTextField'; + +type ConfirmDialogProps = { + setIsOpen: Dispatch>; + open: boolean; + title: string; + cancelText?: string; + confirmText?: string; + initialValue?: string; + onCancel: (e: MouseEvent) => void | Promise; + onConfirm: (e: MouseEvent) => void | Promise; +}; + +const InputDialog: FC = ({ + open, + setIsOpen, + title, + cancelText = 'Cancel', + confirmText = 'Save', + onCancel, + onConfirm, + initialValue, +}) => { + const [val, setVal] = useState(initialValue); + const dialogRef = useRef(null); + + const handleConfirm = async (e: MouseEvent) => { + e.stopPropagation(); + await dialogRef.current?.close(); + onConfirm(e); + }; + + const handleCancel = async (e: MouseEvent) => { + e.stopPropagation(); + await dialogRef.current?.close(); + onCancel(e); + }; + + return ( + setIsOpen(false)}> +

{title}

+
+ { + setVal((e.target as MdOutlinedTextField).value); + }} + className="w-full text-start" + textDirection="ltr" + value={val} + /> + +
+ {cancelText} + + {confirmText} + +
+
+ ); +}; + +export default InputDialog; diff --git a/client/src/shared/ui/NavItem.tsx b/client/src/shared/ui/NavItem.tsx index e213f62..c69eb28 100644 --- a/client/src/shared/ui/NavItem.tsx +++ b/client/src/shared/ui/NavItem.tsx @@ -34,16 +34,17 @@ const NavItem: FC = ({ {({ isActive }) => (