{"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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"}