From a1853ec61872d98c02f75bf6b90826f9a3049fcd Mon Sep 17 00:00:00 2001 From: wadjih-bencheikh18 Date: Sat, 25 Nov 2023 11:58:43 +0100 Subject: [PATCH 1/2] refactor!: remove ContextMenu and Dropdown components --- src/components/dropdown-menu/DropdownMenu.tsx | 160 -------- src/components/dropdown-menu/MenuItems.tsx | 146 -------- src/components/dropdown-menu/index.ts | 2 - .../dropdown-menu/useContextMenuPlacement.ts | 76 ---- src/components/index.ts | 1 - stories/components/dropdown.stories.tsx | 347 ++++++------------ 6 files changed, 117 insertions(+), 615 deletions(-) delete mode 100644 src/components/dropdown-menu/DropdownMenu.tsx delete mode 100644 src/components/dropdown-menu/MenuItems.tsx delete mode 100644 src/components/dropdown-menu/index.ts delete mode 100644 src/components/dropdown-menu/useContextMenuPlacement.ts diff --git a/src/components/dropdown-menu/DropdownMenu.tsx b/src/components/dropdown-menu/DropdownMenu.tsx deleted file mode 100644 index 87913b9c..00000000 --- a/src/components/dropdown-menu/DropdownMenu.tsx +++ /dev/null @@ -1,160 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error -// @ts-ignore This import fails when compiling to CJS. -import { Menu } from '@headlessui/react'; -import type { Placement } from '@popperjs/core'; -import { ReactNode, useRef, ElementType, ComponentProps } from 'react'; - -import { useModifiedPopper } from '../hooks/useModifiedPopper'; -import { useOnClickOutside } from '../hooks/useOnClickOutside'; -import { useOnOff } from '../hooks/useOnOff'; -import { Portal } from '../root-layout/Portal'; - -import { MenuItems, MenuOption, MenuOptions } from './MenuItems'; -import { useContextMenuPlacement } from './useContextMenuPlacement'; - -interface DropdownMenuBaseProps { - /** - * Placement for react-popper - */ - placement?: Placement; - - options: MenuOptions; - onSelect: (selected: MenuOption) => void; -} - -type ElementProps = E extends ElementType - ? ComponentProps - : never; - -interface DropdownMenuClickProps extends DropdownMenuBaseProps { - /** - * Node to be inside the Button - */ - children: ReactNode; - trigger: 'click'; -} - -interface DropdownMenuContextProps extends DropdownMenuBaseProps { - trigger: 'contextMenu'; - children: ReactNode; - as?: E; -} - -export type DropdownMenuProps = - | DropdownMenuContextProps - | DropdownMenuClickProps; - -export function DropdownMenu( - props: DropdownMenuProps & - Omit, keyof DropdownMenuProps>, -) { - const { trigger, ...otherProps } = props; - - if (trigger === 'contextMenu') { - return {...props} />; - } - return ( - {props.children} - ); -} - -function DropdownContextMenu( - props: Omit, 'trigger'> & - Omit, keyof DropdownMenuProps>, -) { - const { - children, - onSelect, - as: Wrapper = 'div', - placement = 'right-start', - options, - ...otherProps - } = props; - - const { - isPopperElementOpen, - closePopperElement, - handleContextMenu, - setPopperElement, - styles, - attributes, - } = useContextMenuPlacement(placement); - - const ref = useRef(null); - useOnClickOutside(ref, closePopperElement); - - const { style = {}, ...otherWrapperProps } = otherProps as ElementProps; - - return ( - - <> - {isPopperElementOpen && ( - -
-
- - { - closePopperElement(); - onSelect(selected); - }} - options={options} - /> - -
-
-
- )} - - {children} - -
- ); -} - -function DropdownClickMenu( - props: Omit, 'trigger'> & { children: ReactNode }, -) { - const { placement = 'bottom-start', onSelect, ...otherProps } = props; - - const [isOpened, , closeItems, toggle] = useOnOff(false); - - const ref = useRef(null); - useOnClickOutside(ref, closeItems); - - const { setReferenceElement, setPopperElement, popperProps } = - useModifiedPopper({ placement, offset: 6 }); - - return ( - - - {props.children} - - {isOpened && ( - -
-
- { - closeItems(); - onSelect(selected); - }} - {...otherProps} - /> -
-
-
- )} -
- ); -} diff --git a/src/components/dropdown-menu/MenuItems.tsx b/src/components/dropdown-menu/MenuItems.tsx deleted file mode 100644 index 258711c1..00000000 --- a/src/components/dropdown-menu/MenuItems.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import styled from '@emotion/styled'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error -// @ts-ignore This import fails when compiling to CJS. -import { Menu } from '@headlessui/react'; -import type { ReactNode } from 'react'; - -export interface MenuOption { - type: 'option'; - label: ReactNode; - icon?: ReactNode; - disabled?: boolean; - data?: T; -} - -export interface MenuDivider { - type: 'divider'; -} - -export type MenuOptions = Array | MenuDivider>; - -const ItemDiv = styled.div<{ - disabled: boolean; - active: boolean; -}>` - display: contents; - cursor: ${(props) => (props.disabled ? 'default' : 'pointer')}; - font-size: 0.875rem; - color: ${(props) => (!props.disabled ? 'black' : 'rgb(163, 163, 163)')}; - & > div { - padding-top: 2px; - padding-bottom: 2px; - ${(props) => props.active && 'background-color: rgb(243, 244, 246);'} - } - - ${(props) => - !props.disabled && - ` - &:hover > div { - background-color: rgb(243, 244, 246); - } - `} -`; - -const Divider = styled.hr` - width: 100%; - color: rgb(229, 229, 229); - margin-top: 5px; - margin-bottom: 5px; - grid-column: 1 / -1; -`; - -const ItemsDiv = styled.div<{ - hasOneIconOrMore: boolean; -}>` - display: grid; - grid-template-columns: [icon-start] ${(props) => - props.hasOneIconOrMore ? '40px' : '0px'} [label-start] auto; - width: fit-content; - align-items: center; - border-radius: 6px; - background-color: white; - box-shadow: - rgba(0, 0, 0, 0.3) 0px 19px 38px, - rgba(0, 0, 0, 0.22) 0px 5px 12px; - padding-top: 5px; - padding-bottom: 5px; - --cell-padding: 16px; -`; - -const LabelDiv = styled.div` - grid-column-start: label-start; - padding-right: var(--cell-padding); - padding-left: var(--cell-padding); -`; - -const IconDiv = styled.div` - grid-column-start: icon-start; - width: 100%; - height: 100%; - padding-left: var(--cell-padding); - display: flex; - justify-content: center; - align-items: center; -`; - -export interface MenuItemsProps { - options: MenuOptions; - onSelect: (selected: MenuOption) => void; - itemsStatic?: boolean; -} - -export function MenuItems(props: MenuItemsProps) { - const { options, onSelect, itemsStatic } = props; - const hasOneOrMoreIcon = options.some( - (option) => option.type === 'option' && option.icon, - ); - - return ( - - {options.map((option, index) => ( - - ))} - - ); -} - -interface ItemProps { - option: MenuOptions[number]; - onSelect: MenuItemsProps['onSelect']; -} - -function Item(props: ItemProps) { - const { option, onSelect } = props; - const isDivider = option.type === 'divider'; - - if (isDivider) { - return ; - } - - return ( - - {({ active }) => ( - { - event.stopPropagation(); - onSelect(option); - }} - active={active} - disabled={option.disabled || false} - > - {option.icon} - {option.label} - - )} - - ); -} diff --git a/src/components/dropdown-menu/index.ts b/src/components/dropdown-menu/index.ts deleted file mode 100644 index 123eed9a..00000000 --- a/src/components/dropdown-menu/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './DropdownMenu'; -export type { MenuOptions, MenuOption, MenuDivider } from './MenuItems'; diff --git a/src/components/dropdown-menu/useContextMenuPlacement.ts b/src/components/dropdown-menu/useContextMenuPlacement.ts deleted file mode 100644 index 687d1b5d..00000000 --- a/src/components/dropdown-menu/useContextMenuPlacement.ts +++ /dev/null @@ -1,76 +0,0 @@ -import type { Placement } from '@popperjs/core'; -import React, { useCallback, useMemo, useState } from 'react'; -import { usePopper } from 'react-popper'; - -interface PositionState { - clientX: number; - clientY: number; - pageX: number; - pageY: number; -} - -export function useContextMenuPlacement(placement: Placement) { - const [positionState, setPositionState] = useState( - null, - ); - - const handleContextMenu = useCallback( - (event: React.MouseEvent) => { - const { clientX, clientY, pageX, pageY } = event; - event.preventDefault(); - - setPositionState({ - clientX, - clientY, - pageX, - pageY, - }); - }, - [], - ); - - const boundingClientRect = useMemo(() => { - return { - top: positionState?.clientY || 0, - left: positionState?.clientX || 0, - x: positionState?.pageX || 0, - y: positionState?.pageY || 0, - - bottom: 0, - right: 0, - height: 0, - width: 0, - toJSON: () => '', - }; - }, [positionState]); - - const virtualElement = useMemo(() => { - return { - getBoundingClientRect: () => boundingClientRect, - }; - }, [boundingClientRect]); - - const [popperElement, setPopperElement] = useState( - null, - ); - - const { styles, attributes, state } = usePopper( - virtualElement, - popperElement, - { - placement, - }, - ); - - return { - setPopperElement, - styles, - attributes, - popperState: state, - isPopperElementOpen: positionState !== null, - handleContextMenu, - closePopperElement: () => { - setPositionState(null); - }, - }; -} diff --git a/src/components/index.ts b/src/components/index.ts index b6c11aa1..959850aa 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -2,7 +2,6 @@ export * from './accordion/index'; export * from './button/index'; export * from './color-picker/index'; export * from './drop-zone/index'; -export * from './dropdown-menu/index'; export * from './forms/index'; export * from './fullscreen/index'; export * from './header/index'; diff --git a/stories/components/dropdown.stories.tsx b/stories/components/dropdown.stories.tsx index 4c2a40ff..669ea7d2 100644 --- a/stories/components/dropdown.stories.tsx +++ b/stories/components/dropdown.stories.tsx @@ -1,72 +1,43 @@ +import { + ContextMenu, + Menu, + MenuDivider, + MenuItem, + Popover, +} from '@blueprintjs/core'; import styled from '@emotion/styled'; -import { Menu } from '@headlessui/react'; import { useMemo } from 'react'; -import { - FaMeteor, - FaAddressBook, - FaAccessibleIcon, - Fa500Px, - FaAccusoft, - FaAcquisitionsIncorporated, - FaAd, - FaAddressCard, - FaAdjust, -} from 'react-icons/fa'; -import { - MenuItems, - MenuOptions, -} from '../../src/components/dropdown-menu/MenuItems'; -import { - DropdownMenu, - Table, - ValueRenderers, -} from '../../src/components/index'; +import { Button, Table, ValueRenderers } from '../../src/components/index'; import data from '../data/table.json'; export default { title: 'Components / DropdownMenu', }; -const defaultOptions: MenuOptions = [ - { label: 'Default workspace', type: 'option' }, -]; - -function noop() { - // do nothing -} - -const ButtonStyled = styled.div` - box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); - color: white; - font-weight: 600; - font-size: 0.875rem; - line-height: 1.25rem; - padding-top: 0.5rem; - padding-bottom: 0.5rem; - padding-left: 1rem; - padding-right: 1rem; - background-color: rgb(107 114 128); - border-color: transparent; - border-radius: 0.375rem; - border-width: 1px; -`; +const defaultOptions = ; export function Dropdown() { - const options = useMemo>(() => { - return [ - { label: 'Default workspace', type: 'option' }, - { label: 'Exercise', type: 'option', icon: }, - { type: 'divider' }, - { label: 'Test', type: 'option', disabled: true }, - { label: 'Test 2', type: 'option' }, - ]; - }, []); + const content = ( + + + + + + + + + + ); return ( - - Default workspace - + +