{"version":3,"file":"FilterDropdown.cjs","sources":["../../../../src/components/Dropdown/FilterDropdown/FilterDropdown.tsx"],"sourcesContent":["'use client'\n\nimport {\n  type ComponentProps,\n  type FC,\n  type FormEvent,\n  type MouseEventHandler,\n  type ReactNode,\n  isValidElement,\n  useMemo,\n} from 'react'\nimport innerText from 'react-innertext'\nimport { tv } from 'tailwind-variants'\n\nimport { type ResponseStatus, useResponseStatus } from '../../../hooks/useResponseStatus'\nimport { useIntl } from '../../../intl'\nimport { Button, type AbstractProps as ButtonProps } from '../../Button'\nimport { FaCircleCheckIcon, FaFilterIcon, FaRotateLeftIcon } from '../../Icon'\nimport { Cluster, Stack } from '../../Layout'\nimport { ResponseMessage } from '../../ResponseMessage'\nimport { Dropdown } from '../Dropdown'\nimport { DropdownCloser } from '../DropdownCloser'\nimport { DropdownContent } from '../DropdownContent'\nimport { DropdownTrigger } from '../DropdownTrigger'\n\ntype ObjectTriggerType = {\n  text?: ReactNode\n  /** 引き金となるボタンの大きさ */\n  size?: ButtonProps['size']\n  /** 引き金となるボタンをアイコンのみとするかどうか */\n  onlyIcon?: boolean\n}\ntype AbstractProps = {\n  /** 引き金となるボタン */\n  trigger?: ReactNode | ObjectTriggerType\n  applyText?: ReactNode\n  cancelText?: ReactNode\n  resetText?: ReactNode\n  children: ReactNode\n  filtered?:\n    | boolean\n    | {\n        iconAlt?: ReactNode\n      }\n  responseStatus?: ResponseStatus\n  onApply: MouseEventHandler<HTMLButtonElement>\n  onCancel?: MouseEventHandler<HTMLButtonElement>\n  onReset?: MouseEventHandler<HTMLButtonElement>\n  onOpen?: () => void\n  onClose?: () => void\n}\ntype Props = AbstractProps & Omit<ComponentProps<'button'>, keyof AbstractProps>\n\nconst CONTROL_CLUSTER_GAP: ComponentProps<typeof Cluster>['gap'] = { column: 1, row: 0.5 }\nconst ON_SUBMIT = (e: FormEvent) => {\n  e.preventDefault()\n}\n\nconst classNameGenerator = tv({\n  slots: {\n    iconWrapper: ['smarthr-ui-Icon-extended', 'shr-relative shr-leading-none'],\n    filteredIcon: 'shr-absolute shr-bottom-[2px] shr-right-[-4px] shr-h-[0.5em] shr-w-[0.5em]',\n    inner: 'shr-p-1.5',\n    actionArea: 'shr-border-t-shorthand shr-sticky shr-bottom-0 shr-bg-white shr-px-1.5 shr-py-1',\n    resetButtonArea: '-shr-ms-0.5',\n    rightButtonArea: 'shr-ms-auto',\n    message: 'shr-text-right',\n  },\n  variants: {\n    filtered: {\n      true: {\n        iconWrapper: 'shr-text-main',\n      },\n    },\n    triggerSize: {\n      M: {},\n      S: {\n        iconWrapper: '-shr-translate-x-0.25',\n      },\n    },\n  },\n})\n\nexport const FilterDropdown: FC<Props> = ({\n  trigger: orgTrigger,\n  applyText,\n  cancelText,\n  resetText,\n  children,\n  filtered,\n  responseStatus,\n  onApply,\n  onCancel,\n  onReset,\n  onOpen,\n  onClose,\n  ...rest\n}) => {\n  // HINT: ReactNodeとObjectのどちらかを判定\n  // typeofはnullの場合もobject判定されてしまうため念の為falsyで判定\n  // ReactNodeの一部であるReactElementもobjectとして判定されてしまうためisValidElementで判定\n  const trigger: ObjectTriggerType =\n    !orgTrigger || typeof orgTrigger !== 'object' || isValidElement(orgTrigger)\n      ? {\n          text: orgTrigger as ReactNode,\n        }\n      : (orgTrigger as ObjectTriggerType)\n  const { localize } = useIntl()\n\n  const decorated = useMemo(\n    () => ({\n      filteredIconAlt:\n        (typeof filtered === 'object' && filtered.iconAlt) ||\n        localize({\n          id: 'smarthr-ui/FilterDropdown/status',\n          defaultText: '適用中',\n        }),\n      trigger:\n        trigger.text ||\n        localize({\n          id: 'smarthr-ui/FilterDropdown/triggerText',\n          defaultText: '絞り込み',\n        }),\n      applyText:\n        applyText ||\n        localize({\n          id: 'smarthr-ui/FilterDropdown/applyText',\n          defaultText: '適用',\n        }),\n      cancelText:\n        cancelText ||\n        localize({\n          id: 'smarthr-ui/FilterDropdown/cancelText',\n          defaultText: 'キャンセル',\n        }),\n      resetText:\n        resetText ||\n        localize({\n          id: 'smarthr-ui/FilterDropdown/resetText',\n          defaultText: '絞り込み条件を解除',\n        }),\n    }),\n    [filtered, trigger.text, applyText, cancelText, resetText, localize],\n  )\n\n  const filteredIconAlt = useMemo(\n    () => innerText(decorated.filteredIconAlt),\n    [decorated.filteredIconAlt],\n  )\n  const calcedResponseStatus = useResponseStatus(responseStatus)\n\n  const classNamesMapper = useMemo(() => {\n    const {\n      iconWrapper,\n      filteredIcon,\n      inner,\n      actionArea,\n      resetButtonArea,\n      rightButtonArea,\n      message,\n    } = classNameGenerator()\n\n    const commonStyles = {\n      filteredIcon: filteredIcon(),\n      inner: inner(),\n      actionArea: actionArea(),\n      resetButtonArea: resetButtonArea(),\n      rightButtonArea: rightButtonArea(),\n      message: message(),\n    }\n\n    return {\n      filtered: {\n        ...commonStyles,\n        iconWrapper: iconWrapper({ filtered: true, triggerSize: trigger.size }),\n      },\n      unfiltered: {\n        ...commonStyles,\n        iconWrapper: iconWrapper({ filtered: false, triggerSize: trigger.size }),\n      },\n    }\n  }, [trigger.size])\n\n  const classNames = classNamesMapper[filtered ? 'filtered' : 'unfiltered']\n\n  const { buttonSuffix, buttonContent } = useMemo(() => {\n    const FilterIcon = (\n      <span className={classNames.iconWrapper}>\n        <FaFilterIcon alt={trigger.onlyIcon ? decorated.trigger : undefined} />\n\n        {filtered && (\n          // HINT: altに揃えたいが、styleが複雑になってしまうためaria-labelを利用している\n          <FaCircleCheckIcon aria-label={filteredIconAlt} className={classNames.filteredIcon} />\n        )}\n      </span>\n    )\n\n    if (trigger.onlyIcon) {\n      return {\n        buttonSuffix: undefined,\n        buttonContent: FilterIcon,\n      }\n    }\n\n    return {\n      buttonSuffix: FilterIcon,\n      buttonContent: decorated.trigger,\n    }\n  }, [filtered, decorated.trigger, trigger.onlyIcon, filteredIconAlt, classNames])\n\n  return (\n    <Dropdown onOpen={onOpen} onClose={onClose}>\n      <DropdownTrigger tooltip={{ show: trigger.onlyIcon, message: decorated.trigger }}>\n        <Button {...rest} suffix={buttonSuffix} size={trigger.size}>\n          {buttonContent}\n        </Button>\n      </DropdownTrigger>\n      <DropdownContent controllable>\n        <form onSubmit={ON_SUBMIT}>\n          <div className={classNames.inner}>{children}</div>\n          <Stack gap={0.5} className={classNames.actionArea}>\n            <Cluster gap={1} align=\"center\" justify=\"space-between\">\n              {onReset && (\n                <div className={classNames.resetButtonArea}>\n                  <Button\n                    variant=\"text\"\n                    size=\"S\"\n                    prefix={<FaRotateLeftIcon />}\n                    onClick={onReset}\n                    disabled={calcedResponseStatus.isProcessing}\n                  >\n                    {decorated.resetText}\n                  </Button>\n                </div>\n              )}\n\n              <Cluster\n                gap={CONTROL_CLUSTER_GAP}\n                justify=\"flex-end\"\n                className={classNames.rightButtonArea}\n              >\n                <DropdownCloser>\n                  <Button onClick={onCancel} disabled={calcedResponseStatus.isProcessing}>\n                    {decorated.cancelText}\n                  </Button>\n                </DropdownCloser>\n                <DropdownCloser>\n                  <Button\n                    variant=\"primary\"\n                    onClick={onApply}\n                    loading={calcedResponseStatus.isProcessing}\n                  >\n                    {decorated.applyText}\n                  </Button>\n                </DropdownCloser>\n              </Cluster>\n            </Cluster>\n            {calcedResponseStatus.message && (\n              <div className={classNames.message}>\n                <ResponseMessage status={calcedResponseStatus.status} role=\"alert\">\n                  {calcedResponseStatus.message}\n                </ResponseMessage>\n              </div>\n            )}\n          </Stack>\n        </form>\n      </DropdownContent>\n    </Dropdown>\n  )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDA;AACA;;AAEA;AAEA;AACE;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACD;AACD;AACE;AACE;AACE;AACD;AACF;AACD;AACE;AACA;AACE;AACD;AACF;AACF;AACF;AAEM;;;;AAkBL;AAEI;AACI;AACD;;AAEP;AAEA;;AAIM;AACE;AACA;;;AAIF;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEJ;AAEE;AACE;AACA;;AAEL;;AAQH;AAEA;AACE;AAUA;;;;;;;;;AAUE;AACE;AACA;AACD;AACD;AACE;AACA;AACD;;AAEL;AAEA;;AAGE;;AAMM;AAKN;;AAEI;AACA;;;;AAKF;;;AAGJ;;AA6DF;;"}