diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/menu-bar.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/menu-bar.tsx index e72800db62..472dcf8baf 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/menu-bar.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/menu-bar.tsx @@ -1,6 +1,12 @@ -import { DropdownDivider } from '@/features/ui/components/bootstrap-5/dropdown-menu' +import { + DropdownDivider, + DropdownHeader, +} from '@/features/ui/components/bootstrap-5/dropdown-menu' import { MenuBar } from '@/shared/components/menu-bar/menu-bar' -import { MenuBarDropdown } from '@/shared/components/menu-bar/menu-bar-dropdown' +import { + MenuBarDropdown, + NestedMenuBarDropdown, +} from '@/shared/components/menu-bar/menu-bar-dropdown' import { MenuBarOption } from '@/shared/components/menu-bar/menu-bar-option' import { useTranslation } from 'react-i18next' import ChangeLayoutOptions from './change-layout-options' @@ -17,8 +23,16 @@ export const ToolbarMenuBar = () => { id="file" className="ide-redesign-toolbar-dropdown-toggle-subdued ide-redesign-toolbar-button-subdued" > - - + + + + + + + + + + { - + + + + + { className="ide-redesign-toolbar-dropdown-toggle-subdued ide-redesign-toolbar-button-subdued" > + Editor settings + + + PDF preview + + + + + - - - - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + Paragraph styles + + + + + + = ({ @@ -18,8 +23,9 @@ export const MenuBarDropdown: FC = ({ children, id, className, + align = 'start', }) => { - const { menuId, selected, setSelected } = useMenuBar() + const { menuId, selected, setSelected } = useNestableDropdown() const onToggle = useCallback( show => { @@ -38,7 +44,12 @@ export const MenuBarDropdown: FC = ({ }, [id, setSelected]) return ( - + = ({ > {title} - {children} + + {children} + + + ) +} + +const NestableDropdownMenu: FC = ({ + children, + id, + ...props +}) => { + return ( + + + {children} + + + ) +} + +const NestedDropdownToggle: FC = forwardRef( + function NestedDropdownToggle( + { children, className, onMouseEnter, id }, + ref + ) { + return ( + // eslint-disable-next-line jsx-a11y/anchor-is-valid + + {children} + + + ) + } +) + +export const NestedMenuBarDropdown: FC<{ id: string; title: string }> = ({ + children, + id, + title, +}) => { + const { menuId, selected, setSelected } = useNestableDropdown() + const select = useCallback(() => { + setSelected(id) + }, [id, setSelected]) + const onToggle = useCallback( + show => { + setSelected(show ? id : null) + }, + [setSelected, id] + ) + const active = selected === id + return ( + + + {title} + + + {children} + ) } diff --git a/services/web/frontend/js/shared/components/menu-bar/menu-bar-option.tsx b/services/web/frontend/js/shared/components/menu-bar/menu-bar-option.tsx index b1683aa377..916dd79f80 100644 --- a/services/web/frontend/js/shared/components/menu-bar/menu-bar-option.tsx +++ b/services/web/frontend/js/shared/components/menu-bar/menu-bar-option.tsx @@ -1,5 +1,6 @@ import DropdownListItem from '@/features/ui/components/bootstrap-5/dropdown-list-item' import { DropdownItem } from '@/features/ui/components/bootstrap-5/dropdown-menu' +import { useNestableDropdown } from '@/shared/hooks/use-nestable-dropdown' type MenuBarOptionProps = { title: string @@ -7,9 +8,12 @@ type MenuBarOptionProps = { } export const MenuBarOption = ({ title, onClick }: MenuBarOptionProps) => { + const { setSelected } = useNestableDropdown() return ( - {title} + setSelected(null)} onClick={onClick}> + {title} + ) } diff --git a/services/web/frontend/js/shared/components/menu-bar/menu-bar.tsx b/services/web/frontend/js/shared/components/menu-bar/menu-bar.tsx index 45298c32e0..4236bfa5e8 100644 --- a/services/web/frontend/js/shared/components/menu-bar/menu-bar.tsx +++ b/services/web/frontend/js/shared/components/menu-bar/menu-bar.tsx @@ -1,17 +1,16 @@ -import { MenuBarContext } from '@/shared/context/menu-bar-context' -import { FC, HTMLProps, useState } from 'react' +import { NestableDropdownContextProvider } from '@/shared/context/nestable-dropdown-context' +import { FC, HTMLProps } from 'react' export const MenuBar: FC & { id: string }> = ({ children, id, ...props }) => { - const [selected, setSelected] = useState(null) return (
- + {children} - +
) } diff --git a/services/web/frontend/js/shared/context/menu-bar-context.tsx b/services/web/frontend/js/shared/context/menu-bar-context.tsx deleted file mode 100644 index e303e19417..0000000000 --- a/services/web/frontend/js/shared/context/menu-bar-context.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { createContext, Dispatch, SetStateAction } from 'react' - -export type MenuBarContextType = { - selected: string | null - setSelected: Dispatch> - menuId: string -} - -export const MenuBarContext = createContext( - undefined -) diff --git a/services/web/frontend/js/shared/context/nestable-dropdown-context.tsx b/services/web/frontend/js/shared/context/nestable-dropdown-context.tsx new file mode 100644 index 0000000000..3f1b7f9c0f --- /dev/null +++ b/services/web/frontend/js/shared/context/nestable-dropdown-context.tsx @@ -0,0 +1,25 @@ +import { createContext, Dispatch, FC, SetStateAction, useState } from 'react' + +export type NestableDropdownContextType = { + selected: string | null + setSelected: Dispatch> + menuId: string +} + +export const NestableDropdownContext = createContext< + NestableDropdownContextType | undefined +>(undefined) + +export const NestableDropdownContextProvider: FC<{ id: string }> = ({ + id, + children, +}) => { + const [selected, setSelected] = useState(null) + return ( + + {children} + + ) +} diff --git a/services/web/frontend/js/shared/hooks/use-menu-bar.tsx b/services/web/frontend/js/shared/hooks/use-menu-bar.tsx deleted file mode 100644 index 1493668c09..0000000000 --- a/services/web/frontend/js/shared/hooks/use-menu-bar.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { MenuBarContext } from '@/shared/context/menu-bar-context' -import { useContext } from 'react' - -export const useMenuBar = () => { - const context = useContext(MenuBarContext) - if (context === undefined) { - throw new Error('useMenuBarContext must be used within a MenuBarContext') - } - return context -} diff --git a/services/web/frontend/js/shared/hooks/use-nestable-dropdown.tsx b/services/web/frontend/js/shared/hooks/use-nestable-dropdown.tsx new file mode 100644 index 0000000000..170b6f6d4d --- /dev/null +++ b/services/web/frontend/js/shared/hooks/use-nestable-dropdown.tsx @@ -0,0 +1,12 @@ +import { NestableDropdownContext } from '@/shared/context/nestable-dropdown-context' +import { useContext } from 'react' + +export const useNestableDropdown = () => { + const context = useContext(NestableDropdownContext) + if (context === undefined) { + throw new Error( + 'useNestableDropdown must be used within a NestableDropdownContextProvider' + ) + } + return context +} diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss index a05963f00e..719e04adf2 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss @@ -72,7 +72,8 @@ $dropdown-item-min-height: 36px; } &:hover:not(.active), - &:focus:not(.active) { + &:focus:not(.active), + &.nested-dropdown-toggle-shown { background-color: var(--bg-light-secondary); cursor: pointer; text-decoration: none; @@ -211,3 +212,12 @@ $dropdown-item-min-height: 36px; text-align: center; } } + +.nested-dropdown-toggle { + &::after { + content: none !important; + } + + display: flex; + justify-content: space-between; +}