{"version":3,"file":"DropdownMenuButton.cjs","sources":["../../../../src/components/Dropdown/DropdownMenuButton/DropdownMenuButton.tsx"],"sourcesContent":["'use client'\n\nimport {\n  Children,\n  type ComponentProps,\n  type ComponentPropsWithRef,\n  type ComponentType,\n  type FC,\n  Fragment,\n  type ReactElement,\n  type ReactNode,\n  isValidElement,\n  memo,\n  useContext,\n  useEffect,\n  useMemo,\n  useRef,\n} from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { Dropdown, DropdownCloser, DropdownContent, DropdownMenuGroup, DropdownTrigger } from '..'\nimport { useObjectAttributes } from '../../../hooks/useObjectAttributes'\nimport { useIntl } from '../../../intl'\nimport { type AnchorButton, Button, type AbstractProps as ButtonProps } from '../../Button'\nimport { FaCaretDownIcon, FaEllipsisIcon } from '../../Icon'\nimport { DropdownContext } from '../Dropdown'\n\nimport useKeyboardNavigation from './useKeyboardNavigation'\n\nimport type { RemoteDialogTrigger } from '../../Dialog'\n\ntype Actions = ActionItem | ActionItem[]\n\n// これでコンポーネントを絞れるわけではないが Button[variant=text] を使ってほしいんだよ! という気持ち\ntype ActionItem =\n  | ReactElement<ComponentProps<typeof Button>>\n  | ReactElement<ComponentProps<typeof AnchorButton>>\n  | ReactElement<ComponentProps<typeof RemoteDialogTrigger>>\n  | ReactNode\n\ntype ObjectTriggerType = {\n  /** 引き金となるボタンラベル */\n  children: ReactNode\n  /** 引き金となるボタンの大きさ */\n  size?: ButtonProps['size']\n  /** 引き金となるボタンをアイコンのみとするかどうか */\n  onlyIcon?:\n    | boolean\n    | {\n        /** 引き金となるアイコンを差し替えたい場合（onlyIcon=true の場合のみ有効） */\n        component?: ComponentType<ComponentProps<typeof FaCaretDownIcon>>\n      }\n}\ntype AbstractProps = {\n  /** 引き金となるボタン */\n  trigger: ReactNode | ObjectTriggerType\n  /** 操作群 */\n  children: Actions\n  /** ドロップダウンメニューが開かれた際のイベント */\n  onOpen?: () => void\n  /** ドロップダウンメニューが閉じられた際のイベント */\n  onClose?: () => void\n}\ntype ElementProps = Omit<ComponentPropsWithRef<'button'>, keyof AbstractProps>\ntype Props = AbstractProps & ElementProps\n\nconst triggerObjectConverter = (trigger: ReactNode) => ({\n  children: trigger,\n})\n\nconst classNameGenerator = tv({\n  slots: {\n    triggerWrapper: 'smarthr-ui-DropdownMenuButton',\n    triggerButton:\n      'smarthr-ui-DropdownMenuButton-trigger [&[aria-expanded=\"true\"]_.smarthr-ui-Icon:last-child]:shr-rotate-180',\n    actionList: [\n      'smarthr-ui-DropdownMenuButton-panel',\n      'shr-list-none shr-py-0.5',\n      [\n        /* unset した Button の右 padding 分 */\n        '[&_.smarthr-ui-Button-disabledWrapper]:shr-pe-1',\n        '[&_.smarthr-ui-Button-disabledWrapper]:shr-gap-x-0.5',\n        '[&_.smarthr-ui-Button-disabledWrapper_>_.smarthr-ui-Button]:shr-w-[unset] [&_.smarthr-ui-Button-disabledWrapper_>_.smarthr-ui-Button]:shr-bg-transparent [&_.smarthr-ui-Button-disabledWrapper_>_.smarthr-ui-Button]:shr-pe-[unset]',\n      ],\n    ],\n    actionListItemButton: [\n      // HINT: 実際にレンダリングされた要素のclassに対して追加されるため、優先度を上げる必要がある\n      '[&&]:shr-w-full [&&]:shr-justify-start [&&]:shr-rounded-none [&&]:shr-border-none [&&]:shr-py-0.5 [&&]:shr-font-normal',\n      '[&&]:focus-visible:shr-focus-indicator',\n    ],\n  },\n})\n\nconst { triggerWrapper, triggerButton, actionList, actionListItemButton } = classNameGenerator()\n\nexport const DropdownMenuButton: FC<Props> = ({\n  trigger,\n  children,\n  onOpen,\n  onClose,\n  className,\n  ...rest\n}) => {\n  const {\n    children: triggerChildren,\n    size: triggerSize,\n    onlyIcon: onlyIconTrigger,\n  } = useObjectAttributes<ReactNode | ObjectTriggerType, ObjectTriggerType>(\n    trigger,\n    triggerObjectConverter,\n  )\n\n  const containerRef = useRef<HTMLUListElement>(null)\n\n  useKeyboardNavigation(containerRef)\n\n  const classNames = useMemo(\n    () => ({\n      triggerWrapper: triggerWrapper({ className }),\n      triggerButton: triggerButton(),\n      actionList: actionList(),\n    }),\n    [className],\n  )\n\n  return (\n    <Dropdown onOpen={onOpen} onClose={onClose}>\n      <MemoizedTriggerButton\n        {...rest}\n        onlyIconTrigger={onlyIconTrigger}\n        triggerSize={triggerSize}\n        classNames={classNames}\n      >\n        {triggerChildren}\n      </MemoizedTriggerButton>\n      <DropdownContent controllable={true}>\n        <menu ref={containerRef} role=\"menu\" className={classNames.actionList}>\n          {renderButtonList(children)}\n        </menu>\n      </DropdownContent>\n    </Dropdown>\n  )\n}\n\nconst MemoizedTriggerButton = memo<\n  ElementProps & {\n    onlyIconTrigger: ObjectTriggerType['onlyIcon']\n    triggerSize: ObjectTriggerType['size']\n    children: ObjectTriggerType['children']\n    classNames: {\n      triggerWrapper: string\n      triggerButton: string\n    }\n  }\n>(({ onlyIconTrigger, triggerSize, children, classNames, ...rest }) => {\n  const { localize } = useIntl()\n\n  const { active } = useContext(DropdownContext)\n\n  const showTooltip = !!onlyIconTrigger\n  const tooltip = useMemo(() => ({ show: showTooltip, message: children }), [children, showTooltip])\n\n  return (\n    <DropdownTrigger className={classNames.triggerWrapper} tooltip={tooltip}>\n      <Button\n        {...rest}\n        suffix={\n          !onlyIconTrigger && (\n            <FaCaretDownIcon\n              alt={\n                active\n                  ? localize({\n                      id: 'smarthr-ui/DropdownMenuButton/triggerActive',\n                      defaultText: '候補を閉じる',\n                    })\n                  : localize({\n                      id: 'smarthr-ui/DropdownMenuButton/triggerInactive',\n                      defaultText: '候補を開く',\n                    })\n              }\n            />\n          )\n        }\n        size={triggerSize}\n        className={classNames.triggerButton}\n      >\n        <TriggerLabelText onlyIconTrigger={onlyIconTrigger}>{children}</TriggerLabelText>\n      </Button>\n    </DropdownTrigger>\n  )\n})\n\nconst TriggerLabelText = memo<{\n  onlyIconTrigger: ObjectTriggerType['onlyIcon']\n  children: ObjectTriggerType['children']\n}>(({ children, onlyIconTrigger }) => {\n  if (!onlyIconTrigger) {\n    return children\n  }\n\n  const Icon = (typeof onlyIconTrigger === 'object' && onlyIconTrigger.component) || FaEllipsisIcon\n\n  return <Icon alt={typeof children === 'string' ? children : innerText(children)} />\n})\n\nexport const renderButtonList = (children: Actions) =>\n  Children.map(children, (item): ReactNode => {\n    if (!item || !isValidElement(item)) {\n      return null\n    }\n\n    switch (item.type) {\n      case Fragment:\n        return renderButtonList(item.props.children)\n      case DropdownMenuGroup:\n        return item\n    }\n\n    return <ButtonListItem>{item}</ButtonListItem>\n  })\n\nconst ButtonListItem: FC<{ children: ReactElement }> = ({ children }) => {\n  const ref = useRef<HTMLLIElement>(null)\n\n  useEffect(() => {\n    if (!ref.current) {\n      return\n    }\n\n    const button = ref.current.querySelector('button,a')\n\n    if (button) {\n      button.setAttribute('role', 'menuitem')\n      button.setAttribute(\n        'class',\n        actionListItemButton({ className: button.getAttribute('class') }),\n      )\n    }\n  }, [children])\n\n  return (\n    <li role=\"presentation\" ref={ref}>\n      <DropdownCloser>{children}</DropdownCloser>\n    </li>\n  )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA;AACE;AACD;AAED;AACE;AACE;AACA;AAEA;;;AAGE;;;;;AAKC;AACF;AACD;;;;AAIC;AACF;AACF;AAED;;;AAmBE;;AAIA;AAEI;;;AAGD;AAIH;AAiBF;AAEA;AAWE;;AAIA;;AAGA;;AAUoB;AACA;;;AAGA;AACA;;AAatB;AAEA;;AAKI;;AAGF;;AAGF;AAEO;;AAGD;;AAGF;AACE;;AAEA;AACE;;AAGJ;AACF;AAEF;AACE;;AAGE;;;;;AAOE;AACA;;AAKJ;AAEA;AAKF;;;"}