{"version":3,"file":"HotkeyInput.mjs","names":["hotkeyMessages","useControlledState","Flexbox"],"sources":["../../src/HotkeyInput/HotkeyInput.tsx"],"sourcesContent":["'use client';\n\nimport { type InputRef } from 'antd';\nimport { cx, useThemeMode } from 'antd-style';\nimport { isEqual } from 'es-toolkit/compat';\nimport { Undo2Icon, XIcon } from 'lucide-react';\nimport {\n  type FocusEvent,\n  memo,\n  type MouseEvent,\n  useCallback,\n  useEffect,\n  useMemo,\n  useRef,\n  useState,\n} from 'react';\nimport { useHotkeys, useRecordHotkeys } from 'react-hotkeys-hook';\nimport useControlledState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport { Flexbox } from '@/Flex';\nimport Hotkey from '@/Hotkey';\nimport { checkIsAppleDevice, NORMATIVE_MODIFIER, splitKeysByPlus } from '@/Hotkey/utils';\nimport hotkeyMessages from '@/i18n/resources/en/hotkey';\nimport { useTranslation } from '@/i18n/useTranslation';\n\nimport { styles, variants } from './style';\nimport { type HotkeyInputProps } from './type';\n\nconst HotkeyInput = memo<HotkeyInputProps>(\n  ({\n    value = '',\n    defaultValue = '',\n    resetValue = '',\n    onChange,\n    onClear,\n    onConflict,\n    placeholder,\n    disabled,\n    shadow,\n    allowClear,\n    allowReset = true,\n    style,\n    className,\n    hotkeyConflicts = [],\n    variant,\n    texts,\n    isApple,\n    onBlur,\n    onReset,\n    onFocus,\n  }) => {\n    const [isFocused, setIsFocused] = useState(false);\n    const [hasConflict, setHasConflict] = useState(false);\n    const [hasInvalidCombination, setHasInvalidCombination] = useState(false);\n    const inputRef = useRef<InputRef>(null);\n    const { isDarkMode } = useThemeMode();\n    const { t } = useTranslation(hotkeyMessages);\n    const isAppleDevice = useMemo(() => checkIsAppleDevice(isApple), [isApple]);\n    const [hotkeyValue, setHotkeyValue] = useControlledState(defaultValue, {\n      defaultValue,\n      onChange,\n      value,\n    });\n\n    // 使用 useRecordHotkeys 处理快捷键录入\n    const [recordedKeys, { start, stop, isRecording, resetKeys }] = useRecordHotkeys();\n\n    useHotkeys(\n      '*',\n      () => {\n        inputRef.current?.blur();\n      },\n      {\n        enableOnContentEditable: true,\n        enableOnFormTags: true,\n        enabled: isRecording && !disabled,\n        keydown: false,\n        keyup: true,\n        preventDefault: true,\n      },\n    );\n\n    // 处理按键，保证格式正确：修饰键在前，最多一个非修饰键在后\n    const formatKeys = useCallback((keysSet: Set<string>) => {\n      const modifiers: string[] = [];\n      const normalKeys: string[] = [];\n\n      for (const key of keysSet) {\n        // 处理不同表示的修饰键\n        const normalizedKey: any = key.toLowerCase();\n        if (NORMATIVE_MODIFIER.includes(normalizedKey)) {\n          // 统一修饰键表示\n          if (\n            (!isAppleDevice && normalizedKey === 'ctrl') ||\n            (isAppleDevice && normalizedKey === 'meta')\n          ) {\n            if (!modifiers.includes('mod')) modifiers.push('mod');\n          } else if (!modifiers.includes(normalizedKey)) {\n            modifiers.push(normalizedKey);\n          }\n        } else {\n          normalKeys.push(key);\n        }\n      }\n\n      // 至少需要一个修饰键\n      if (modifiers.length === 0 && normalKeys.length > 0) {\n        return { isValid: false, keys: [] };\n      }\n\n      // 只允许一个非修饰键，如果有多个，只保留最后一个\n      const finalKey = normalKeys.length > 0 ? [normalKeys.at(-1)] : [];\n      const shortcuts = [modifiers, finalKey];\n\n      return {\n        // 组合必须包含至少一个按键\n        isValid: shortcuts.every((k) => k.length > 0),\n        keys: shortcuts.flat(),\n      };\n    }, []);\n\n    // 获取格式化后的按键字符串\n    const { isValid, keys } = formatKeys(recordedKeys);\n    const keysString = keys.join('+');\n\n    // 检查快捷键冲突\n    const checkHotkeyConflict = useCallback(\n      (newHotkey: string): boolean => {\n        return hotkeyConflicts\n          .filter((conflictKey) => conflictKey !== resetValue)\n          .some((conflictKey) => {\n            const newKeys = splitKeysByPlus(newHotkey);\n            const conflictKeys = splitKeysByPlus(conflictKey);\n            return isEqual(newKeys, conflictKeys);\n          });\n      },\n      [hotkeyConflicts],\n    );\n\n    // 当按键组合完成时处理结果\n    useEffect(() => {\n      if (recordedKeys.size > 0 && !isRecording) {\n        if (!isValid) {\n          setHasInvalidCombination(true);\n          setHasConflict(false);\n          return;\n        }\n\n        setHasInvalidCombination(false);\n        const newKeysString = keysString;\n\n        // 检查冲突\n        const conflict = checkHotkeyConflict(newKeysString);\n        if (conflict) {\n          setHasConflict(true);\n          onConflict?.(newKeysString);\n        } else {\n          setHasConflict(false);\n          setHotkeyValue?.(newKeysString);\n        }\n      }\n    }, [\n      recordedKeys,\n      isRecording,\n      isValid,\n      keysString,\n      checkHotkeyConflict,\n      setHotkeyValue,\n      onConflict,\n    ]);\n\n    // 处理输入框焦点\n    const handleFocus = (e: FocusEvent<HTMLInputElement>) => {\n      if (disabled) return;\n      setIsFocused(true);\n      setHasConflict(false);\n      setHasInvalidCombination(false);\n      start(); // 开始记录\n      onFocus?.(e);\n    };\n\n    const handleBlur = (e: FocusEvent<HTMLInputElement>) => {\n      setIsFocused(false);\n      stop(); // 停止记录\n      onBlur?.(e);\n    };\n\n    const handleClear = (e: MouseEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      setHotkeyValue?.('');\n      resetKeys();\n      setHasConflict(false);\n      setHasInvalidCombination(false);\n      setIsFocused(false);\n      stop();\n      onClear?.(hotkeyValue);\n    };\n\n    // 重置功能\n    const handleReset = (e: MouseEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      setHotkeyValue?.(resetValue);\n      resetKeys();\n      setHasConflict(false);\n      setHasInvalidCombination(false);\n      setIsFocused(false);\n      stop(); // 停止记录\n      onReset?.(hotkeyValue, resetValue);\n    };\n\n    const handleClick = (e: MouseEvent) => {\n      e.preventDefault();\n      e.stopPropagation();\n      if (disabled || isFocused) return;\n      inputRef.current?.focus();\n    };\n\n    const placeholderText = placeholder ?? t('hotkey.placeholder');\n    const resetTitle = texts?.reset ?? t('hotkey.reset');\n    const clearTitle = texts?.clear ?? t('hotkey.clear');\n    const conflictText = texts?.conflicts ?? t('hotkey.conflict');\n    const invalidText = texts?.invalidCombination ?? t('hotkey.invalidCombination');\n\n    return (\n      <Flexbox\n        className={className}\n        gap={8}\n        style={{\n          position: 'relative',\n          ...style,\n        }}\n      >\n        <Flexbox\n          horizontal\n          align={'center'}\n          justify={'space-between'}\n          className={cx(\n            variants({\n              disabled,\n              error: hasConflict || hasInvalidCombination,\n              focused: isFocused,\n              shadow,\n              variant: variant || (isDarkMode ? 'filled' : 'outlined'),\n            }),\n          )}\n          onClick={handleClick}\n        >\n          <div style={{ pointerEvents: 'none' }}>\n            {isRecording ? (\n              <span className={styles.placeholder}>\n                {keys.length > 0 ? <Hotkey keys={keysString} /> : placeholderText}\n              </span>\n            ) : hotkeyValue ? (\n              <Hotkey keys={hotkeyValue} />\n            ) : (\n              <span className={styles.placeholder}>{placeholderText}</span>\n            )}\n          </div>\n\n          {/* 隐藏的输入框，用于接收焦点 */}\n          <input\n            readOnly\n            className={styles.hiddenInput}\n            disabled={disabled}\n            ref={inputRef as any}\n            style={{ pointerEvents: 'none' }}\n            onBlur={handleBlur}\n            onFocus={handleFocus}\n          />\n\n          {!isFocused && hotkeyValue && !disabled && (allowReset || allowClear) && (\n            <Flexbox horizontal gap={4}>\n              {allowReset && hotkeyValue !== resetValue && (\n                <ActionIcon\n                  icon={Undo2Icon}\n                  size={'small'}\n                  title={resetTitle}\n                  variant={'filled'}\n                  onClick={handleReset}\n                />\n              )}\n              {allowClear && (\n                <ActionIcon\n                  icon={XIcon}\n                  size={'small'}\n                  title={clearTitle}\n                  variant={'filled'}\n                  onClick={handleClear}\n                />\n              )}\n            </Flexbox>\n          )}\n        </Flexbox>\n        {hasConflict && <div className={styles.errorText}>{conflictText}</div>}\n        {hasInvalidCombination && <div className={styles.errorText}>{invalidText}</div>}\n      </Flexbox>\n    );\n  },\n);\n\nHotkeyInput.displayName = 'HotkeyInput';\n\nexport default HotkeyInput;\n"],"mappings":"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"}