{"version":3,"file":"LanguageSwitcher.cjs","sources":["../../../../src/components/Header/LanguageSwitcher/LanguageSwitcher.tsx"],"sourcesContent":["'use client'\n\nimport {\n  type FC,\n  type HTMLAttributes,\n  type KeyboardEvent,\n  type MouseEvent,\n  type ReactNode,\n  memo,\n  useMemo,\n} from 'react'\nimport { type VariantProps, tv } from 'tailwind-variants'\n\nimport { type DecoratorsType, useDecorators } from '../../../hooks/useDecorators'\nimport { useIntl } from '../../../intl'\nimport { tabbable } from '../../../libs/tabbable'\nimport { Button } from '../../Button'\nimport { Dropdown, DropdownContent, DropdownTrigger } from '../../Dropdown'\nimport { FaCaretDownIcon, FaCheckIcon, FaGlobeIcon, LanguageIcon } from '../../Icon'\n\nimport type { Locale } from '../../../intl'\n\nexport type AbstractProps = {\n  narrow?: boolean\n  localeMap: Partial<Record<Locale, string>>\n  locale?: string\n  defaultLocale?: string\n  /** コンポーネント内の文言を変更するための関数を設定 */\n  decorators?: DecoratorsType<DecoratorKeyTypes>\n  /** 言語切替UIで言語を選択した時に発火するコールバック関数 */\n  onLanguageSelect?: (code: string) => void\n} & VariantProps<typeof classNameGenerator>\n\ntype Props = AbstractProps & Omit<HTMLAttributes<HTMLElement>, keyof AbstractProps>\n\n// トリガーはどの言語でも英語のままLanguageと表示するのが好ましいため、intlにはおかない。decoratorは一旦そのままにしている。\nconst DECORATOR_DEFAULT_TEXTS = {\n  triggerLabel: 'Language',\n} as const\n\ntype DecoratorKeyTypes = 'checkIconAlt' | keyof typeof DECORATOR_DEFAULT_TEXTS\n\nconst ARROW_KEY_REGEX = /^Arrow(Up|Down|Left|Right)$/\nconst ARROW_UPS_REGEX = /^Arrow(Up|Left)$/\n\n// 配列のインデックスを循環的に移動する（最初→最後、最後→最初）\nconst getCircularIndex = (currentIndex: number, direction: 'up' | 'down', arrayLength: number) => {\n  if (direction === 'up') {\n    // 負の数を避けるため arrayLength を加算してから剰余を取る\n    return (currentIndex - 1 + arrayLength) % arrayLength\n  }\n  // 次の要素へ移動（最後の場合は 0 に戻る）\n  return (currentIndex + 1) % arrayLength\n}\n\nconst onDelegateKeyDownContent = (e: KeyboardEvent<HTMLDivElement>) => {\n  if (!ARROW_KEY_REGEX.test(e.key)) {\n    return\n  }\n\n  e.preventDefault()\n\n  const buttons = tabbable(e.currentTarget)\n  const currentIndex = buttons.indexOf(e.target as HTMLElement)\n  const direction = ARROW_UPS_REGEX.test(e.key) ? 'up' : 'down'\n  const nextIndex = getCircularIndex(currentIndex, direction, buttons.length)\n\n  buttons[nextIndex]?.focus()\n}\n\nconst classNameGenerator = tv({\n  slots: {\n    switchButton: [\n      'shr-border-none shr-bg-transparent shr-px-0.25 shr-font-normal shr-text-white shr-transition-transform shr-duration-100',\n      'hover:shr-border-transparent hover:shr-bg-transparent',\n      'focus-visible:shr-border-transparent focus-visible:shr-bg-transparent',\n      'forced-colors:shr-border-shorthand',\n      '[&_.smarthr-ui-Icon]:aria-expanded:shr-rotate-180',\n    ],\n    languageItemsList: ['shr-p-0.5'],\n    languageItem: ['shr-flex shr-items-center'],\n    languageButton: [\n      'shr-justify-start shr-border-transparent shr-px-0.5 shr-font-normal',\n      '[&:not(:has(svg))]:shr-ps-2',\n      'hover:shr-border-transparent',\n    ],\n  },\n  variants: {\n    invert: {\n      true: {\n        switchButton: [\n          'shr-text-black',\n          'hover:shr-bg-white-darken',\n          'focus-visible:shr-bg-white-darken',\n        ],\n      },\n    },\n    enableNew: {\n      true: {\n        switchButton: 'shr-px-0.5',\n      },\n    },\n  },\n})\n\nexport const LanguageSwitcher: FC<Props> = ({\n  narrow,\n  enableNew,\n  invert = enableNew,\n  decorators,\n  localeMap,\n  locale,\n  defaultLocale,\n  onLanguageSelect,\n  ...rest\n}) => {\n  const { localize, availableLocales } = useIntl()\n  const { locales, defaultCurrentLang } = useMemo(\n    () => ({\n      locales: Object.entries(localeMap).filter(([code]) => availableLocales.includes(code)),\n      defaultCurrentLang: Object.keys(localeMap)[0],\n    }),\n    [localeMap, availableLocales],\n  )\n\n  const decoratorDefaultTexts = useMemo(\n    () => ({\n      triggerLabel: DECORATOR_DEFAULT_TEXTS.triggerLabel,\n      checkIconAlt: localize({\n        id: 'smarthr-ui/LanguageSwitcher/checkIconAlt',\n        defaultText: '選択中',\n      }),\n    }),\n    [localize],\n  )\n\n  const decorated = useDecorators<DecoratorKeyTypes>(decoratorDefaultTexts, decorators)\n  const currentLang = locale || defaultLocale || defaultCurrentLang\n  const classNames = useMemo(() => {\n    const { languageButton, languageItemsList, languageItem, switchButton } = classNameGenerator()\n\n    return {\n      languageButton: languageButton(),\n      languageItemsList: languageItemsList(),\n      languageItem: languageItem(),\n      switchButton: switchButton({ invert, enableNew }),\n    }\n  }, [enableNew, invert])\n\n  const onClickLanguageSelect = useMemo(\n    () =>\n      onLanguageSelect\n        ? (e: MouseEvent<HTMLButtonElement>) => {\n            onLanguageSelect(e.currentTarget.value)\n          }\n        : undefined,\n    [onLanguageSelect],\n  )\n\n  return (\n    <Dropdown {...rest}>\n      <MemoizedDropdownTrigger\n        narrow={narrow}\n        invert={invert}\n        className={classNames.switchButton}\n        label={decorated.triggerLabel}\n      />\n      <DropdownContent onKeyDown={onDelegateKeyDownContent}>\n        <ul className={classNames.languageItemsList}>\n          {locales.map(([code, label]) => (\n            <LanguageListItemButton\n              key={code}\n              code={code}\n              className={classNames.languageItem}\n              buttonStyle={classNames.languageButton}\n              current={currentLang === code}\n              onClick={onClickLanguageSelect}\n              iconAlt={decorated.checkIconAlt}\n            >\n              {label}\n            </LanguageListItemButton>\n          ))}\n        </ul>\n      </DropdownContent>\n    </Dropdown>\n  )\n}\n\nconst LanguageListItemButton = memo<{\n  code: string\n  children: string\n  className: string\n  buttonStyle: string\n  current: boolean\n  iconAlt: ReactNode\n  onClick?: (e: MouseEvent<HTMLButtonElement>) => void\n}>(({ code, children, buttonStyle, className, current, iconAlt, onClick }) => (\n  <li key={code} className={className} aria-current={current} lang={code}>\n    <Button\n      value={code}\n      onClick={onClick}\n      wide\n      prefix={current ? <FaCheckIcon color=\"MAIN\" alt={iconAlt} /> : null}\n      className={buttonStyle}\n    >\n      {children}\n    </Button>\n  </li>\n))\n\nconst MemoizedDropdownTrigger = memo<\n  Pick<Props, 'narrow' | 'invert'> & { className: string; label: ReactNode }\n>(({ narrow, invert, className, label }) => (\n  <DropdownTrigger>\n    {narrow ? (\n      <Button suffix={<FaCaretDownIcon />} className={className}>\n        {invert ? <LanguageIcon alt={label} /> : <FaGlobeIcon alt={label} />}\n      </Button>\n    ) : (\n      <Button\n        prefix={invert ? <LanguageIcon /> : <FaGlobeIcon />}\n        suffix={<FaCaretDownIcon />}\n        className={className}\n      >\n        {label}\n      </Button>\n    )}\n  </DropdownTrigger>\n))\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA;AACA;AACE;;AAKF;AACA;AAEA;AACA;AACE;;;;;AAKA;AACF;AAEA;;;;;;;AASE;AACA;AAEA;AACF;AAEA;AACE;AACE;;;;;;AAMC;;;AAGD;;;;AAIC;AACF;AACD;AACE;AACE;AACE;;;;AAIC;AACF;AACF;AACD;AACE;AACE;AACD;AACF;AACF;AACF;AAEM;;;;;AAgBF;AAIH;;;AAIM;AACA;;AAEH;;AAKH;AACA;AACE;;;;;;;AAQF;AAEA;AAGM;AACI;;AAEJ;;AA+BR;AAEA;AAsBA;;"}