@composy/keyboard-core 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +156 -37
- package/dist/index.js +1540 -1395
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +2981 -1
- package/dist/index.min.js.map +1 -0
- package/es/.dts-output-manifest.json +1 -0
- package/es/.dts.tsbuildinfo +1 -0
- package/es/icons.d.ts +0 -1
- package/es/icons.js +1 -1
- package/es/icons.js.map +1 -1
- package/es/index.d.ts +3 -4
- package/es/index.js +15 -15
- package/es/layouts/amount.d.ts +0 -1
- package/es/layouts/amount.js +1 -1
- package/es/layouts/amount.js.map +1 -1
- package/es/layouts/bankcard.d.ts +0 -1
- package/es/layouts/bankcard.js +1 -1
- package/es/layouts/bankcard.js.map +1 -1
- package/es/layouts/email.d.ts +0 -1
- package/es/layouts/email.js +1 -1
- package/es/layouts/email.js.map +1 -1
- package/es/layouts/idcard.d.ts +0 -1
- package/es/layouts/idcard.js +1 -1
- package/es/layouts/idcard.js.map +1 -1
- package/es/layouts/index.d.ts +11 -12
- package/es/layouts/index.js +23 -23
- package/es/layouts/index.js.map +1 -1
- package/es/layouts/license-plate.d.ts +0 -1
- package/es/layouts/license-plate.js +1 -1
- package/es/layouts/license-plate.js.map +1 -1
- package/es/layouts/number.d.ts +0 -1
- package/es/layouts/number.js +1 -1
- package/es/layouts/number.js.map +1 -1
- package/es/layouts/phone.d.ts +0 -1
- package/es/layouts/phone.js +1 -1
- package/es/layouts/phone.js.map +1 -1
- package/es/layouts/punct.d.ts +0 -1
- package/es/layouts/punct.js +1 -1
- package/es/layouts/punct.js.map +1 -1
- package/es/layouts/qwerty.d.ts +0 -1
- package/es/layouts/qwerty.js +1 -1
- package/es/layouts/qwerty.js.map +1 -1
- package/es/layouts/search.d.ts +0 -1
- package/es/layouts/search.js +1 -1
- package/es/layouts/search.js.map +1 -1
- package/es/layouts/symbol.d.ts +0 -1
- package/es/layouts/symbol.js +1 -1
- package/es/layouts/symbol.js.map +1 -1
- package/es/layouts/url.d.ts +0 -1
- package/es/layouts/url.js +1 -1
- package/es/layouts/url.js.map +1 -1
- package/es/layouts/verifycode.d.ts +0 -1
- package/es/layouts/verifycode.js +1 -1
- package/es/layouts/verifycode.js.map +1 -1
- package/es/managers/index.d.ts +1 -2
- package/es/managers/index.js +1 -1
- package/es/managers/keyboard-manager.d.ts +2 -1
- package/es/managers/keyboard-manager.js +79 -22
- package/es/managers/keyboard-manager.js.map +1 -1
- package/es/managers/keyboard-renderer.d.ts +3 -2
- package/es/managers/keyboard-renderer.js +81 -39
- package/es/managers/keyboard-renderer.js.map +1 -1
- package/es/managers/position-manager.d.ts +4 -3
- package/es/managers/position-manager.js +42 -21
- package/es/managers/position-manager.js.map +1 -1
- package/es/themes/index.d.ts +1 -2
- package/es/themes/index.js +2 -1
- package/es/themes/index.js.map +1 -1
- package/es/themes/presets.d.ts +0 -1
- package/es/themes/presets.js +1 -1
- package/es/themes/presets.js.map +1 -1
- package/es/themes/types.d.ts +1 -1
- package/es/themes/types.js +1 -1
- package/es/types/index.d.ts +0 -1
- package/es/types/index.js +1 -1
- package/es/types/keyboard.d.ts +0 -1
- package/es/types/keyboard.js +1 -1
- package/es/types/position.d.ts +0 -1
- package/es/types/position.js +1 -1
- package/es/utils/device.d.ts +0 -1
- package/es/utils/device.js +11 -7
- package/es/utils/device.js.map +1 -1
- package/es/utils/dom.d.ts +4 -1
- package/es/utils/dom.js +25 -7
- package/es/utils/dom.js.map +1 -1
- package/es/utils/event-emitter.d.ts +0 -1
- package/es/utils/event-emitter.js +2 -1
- package/es/utils/event-emitter.js.map +1 -1
- package/es/utils/formatters.d.ts +0 -1
- package/es/utils/formatters.js +4 -4
- package/es/utils/formatters.js.map +1 -1
- package/es/utils/index.d.ts +2 -3
- package/es/utils/index.js +5 -5
- package/es/utils/random.d.ts +4 -1
- package/es/utils/random.js +2 -2
- package/es/utils/random.js.map +1 -1
- package/esm/icons.js +1 -1
- package/esm/icons.js.map +1 -1
- package/esm/index.js +15 -15
- package/esm/layouts/amount.js +1 -1
- package/esm/layouts/amount.js.map +1 -1
- package/esm/layouts/bankcard.js +1 -1
- package/esm/layouts/bankcard.js.map +1 -1
- package/esm/layouts/email.js +1 -1
- package/esm/layouts/email.js.map +1 -1
- package/esm/layouts/idcard.js +1 -1
- package/esm/layouts/idcard.js.map +1 -1
- package/esm/layouts/index.js +23 -23
- package/esm/layouts/index.js.map +1 -1
- package/esm/layouts/license-plate.js +1 -1
- package/esm/layouts/license-plate.js.map +1 -1
- package/esm/layouts/number.js +1 -1
- package/esm/layouts/number.js.map +1 -1
- package/esm/layouts/phone.js +1 -1
- package/esm/layouts/phone.js.map +1 -1
- package/esm/layouts/punct.js +1 -1
- package/esm/layouts/punct.js.map +1 -1
- package/esm/layouts/qwerty.js +1 -1
- package/esm/layouts/qwerty.js.map +1 -1
- package/esm/layouts/search.js +1 -1
- package/esm/layouts/search.js.map +1 -1
- package/esm/layouts/symbol.js +1 -1
- package/esm/layouts/symbol.js.map +1 -1
- package/esm/layouts/url.js +1 -1
- package/esm/layouts/url.js.map +1 -1
- package/esm/layouts/verifycode.js +1 -1
- package/esm/layouts/verifycode.js.map +1 -1
- package/esm/managers/index.js +1 -1
- package/esm/managers/keyboard-manager.js +79 -22
- package/esm/managers/keyboard-manager.js.map +1 -1
- package/esm/managers/keyboard-renderer.js +81 -39
- package/esm/managers/keyboard-renderer.js.map +1 -1
- package/esm/managers/position-manager.js +42 -21
- package/esm/managers/position-manager.js.map +1 -1
- package/esm/themes/index.js +2 -1
- package/esm/themes/index.js.map +1 -1
- package/esm/themes/presets.js +1 -1
- package/esm/themes/presets.js.map +1 -1
- package/esm/utils/device.js +11 -7
- package/esm/utils/device.js.map +1 -1
- package/esm/utils/dom.js +25 -7
- package/esm/utils/dom.js.map +1 -1
- package/esm/utils/event-emitter.js +2 -1
- package/esm/utils/event-emitter.js.map +1 -1
- package/esm/utils/formatters.js +4 -4
- package/esm/utils/formatters.js.map +1 -1
- package/esm/utils/random.js +2 -2
- package/esm/utils/random.js.map +1 -1
- package/lib/icons.cjs +1 -1
- package/lib/icons.cjs.map +1 -1
- package/{esm/icons.d.ts → lib/icons.d.cts} +0 -1
- package/lib/index.cjs +14 -13
- package/{esm/index.d.ts → lib/index.d.cts} +3 -4
- package/lib/layouts/amount.cjs +1 -1
- package/lib/layouts/amount.cjs.map +1 -1
- package/{esm/layouts/amount.d.ts → lib/layouts/amount.d.cts} +0 -1
- package/lib/layouts/bankcard.cjs +1 -1
- package/lib/layouts/bankcard.cjs.map +1 -1
- package/{esm/layouts/bankcard.d.ts → lib/layouts/bankcard.d.cts} +0 -1
- package/lib/layouts/email.cjs +1 -1
- package/lib/layouts/email.cjs.map +1 -1
- package/lib/layouts/{email.d.ts → email.d.cts} +0 -1
- package/lib/layouts/idcard.cjs +1 -1
- package/lib/layouts/idcard.cjs.map +1 -1
- package/{esm/layouts/idcard.d.ts → lib/layouts/idcard.d.cts} +0 -1
- package/lib/layouts/index.cjs +23 -23
- package/lib/layouts/index.cjs.map +1 -1
- package/{esm/layouts/index.d.ts → lib/layouts/index.d.cts} +11 -12
- package/lib/layouts/license-plate.cjs +1 -1
- package/lib/layouts/license-plate.cjs.map +1 -1
- package/{esm/layouts/license-plate.d.ts → lib/layouts/license-plate.d.cts} +0 -1
- package/lib/layouts/number.cjs +1 -1
- package/lib/layouts/number.cjs.map +1 -1
- package/{esm/layouts/number.d.ts → lib/layouts/number.d.cts} +0 -1
- package/lib/layouts/phone.cjs +1 -1
- package/lib/layouts/phone.cjs.map +1 -1
- package/lib/layouts/{phone.d.ts → phone.d.cts} +0 -1
- package/lib/layouts/punct.cjs +1 -1
- package/lib/layouts/punct.cjs.map +1 -1
- package/lib/layouts/{punct.d.ts → punct.d.cts} +0 -1
- package/lib/layouts/qwerty.cjs +1 -1
- package/lib/layouts/qwerty.cjs.map +1 -1
- package/{esm/layouts/qwerty.d.ts → lib/layouts/qwerty.d.cts} +0 -1
- package/lib/layouts/search.cjs +1 -1
- package/lib/layouts/search.cjs.map +1 -1
- package/{esm/layouts/search.d.ts → lib/layouts/search.d.cts} +0 -1
- package/lib/layouts/symbol.cjs +1 -1
- package/lib/layouts/symbol.cjs.map +1 -1
- package/{esm/layouts/symbol.d.ts → lib/layouts/symbol.d.cts} +0 -1
- package/lib/layouts/url.cjs +1 -1
- package/lib/layouts/url.cjs.map +1 -1
- package/{esm/layouts/url.d.ts → lib/layouts/url.d.cts} +0 -1
- package/lib/layouts/verifycode.cjs +1 -1
- package/lib/layouts/verifycode.cjs.map +1 -1
- package/lib/layouts/{verifycode.d.ts → verifycode.d.cts} +0 -1
- package/lib/managers/index.cjs +1 -1
- package/lib/managers/{index.d.ts → index.d.cts} +1 -2
- package/lib/managers/keyboard-manager.cjs +79 -22
- package/lib/managers/keyboard-manager.cjs.map +1 -1
- package/lib/managers/{keyboard-manager.d.ts → keyboard-manager.d.cts} +2 -1
- package/lib/managers/keyboard-renderer.cjs +80 -38
- package/lib/managers/keyboard-renderer.cjs.map +1 -1
- package/lib/managers/{keyboard-renderer.d.ts → keyboard-renderer.d.cts} +3 -2
- package/lib/managers/position-manager.cjs +42 -21
- package/lib/managers/position-manager.cjs.map +1 -1
- package/lib/managers/{position-manager.d.ts → position-manager.d.cts} +4 -3
- package/lib/themes/index.cjs +2 -1
- package/lib/themes/index.cjs.map +1 -1
- package/lib/themes/{index.d.ts → index.d.cts} +1 -2
- package/lib/themes/presets.cjs +1 -1
- package/lib/themes/presets.cjs.map +1 -1
- package/lib/themes/{presets.d.ts → presets.d.cts} +0 -1
- package/lib/themes/types.cjs +1 -1
- package/{esm/themes/types.d.ts → lib/themes/types.d.cts} +1 -1
- package/lib/types/index.cjs +1 -1
- package/{esm/types/index.d.ts → lib/types/index.d.cts} +0 -1
- package/lib/types/keyboard.cjs +1 -1
- package/{esm/types/keyboard.d.ts → lib/types/keyboard.d.cts} +0 -1
- package/lib/types/position.cjs +1 -1
- package/lib/types/{position.d.ts → position.d.cts} +0 -1
- package/lib/utils/device.cjs +11 -7
- package/lib/utils/device.cjs.map +1 -1
- package/{esm/utils/device.d.ts → lib/utils/device.d.cts} +0 -1
- package/lib/utils/dom.cjs +25 -6
- package/lib/utils/dom.cjs.map +1 -1
- package/{esm/utils/dom.d.ts → lib/utils/dom.d.cts} +4 -1
- package/lib/utils/event-emitter.cjs +2 -1
- package/lib/utils/event-emitter.cjs.map +1 -1
- package/{esm/utils/event-emitter.d.ts → lib/utils/event-emitter.d.cts} +0 -1
- package/lib/utils/formatters.cjs +4 -4
- package/lib/utils/formatters.cjs.map +1 -1
- package/{esm/utils/formatters.d.ts → lib/utils/formatters.d.cts} +0 -1
- package/lib/utils/index.cjs +4 -3
- package/{esm/utils/index.d.ts → lib/utils/index.d.cts} +2 -3
- package/lib/utils/random.cjs +2 -2
- package/lib/utils/random.cjs.map +1 -1
- package/{esm/utils/random.d.ts → lib/utils/random.d.cts} +4 -1
- package/package.json +28 -14
- package/es/icons.d.ts.map +0 -1
- package/es/index.d.ts.map +0 -1
- package/es/layouts/amount.d.ts.map +0 -1
- package/es/layouts/bankcard.d.ts.map +0 -1
- package/es/layouts/email.d.ts.map +0 -1
- package/es/layouts/idcard.d.ts.map +0 -1
- package/es/layouts/index.d.ts.map +0 -1
- package/es/layouts/license-plate.d.ts.map +0 -1
- package/es/layouts/number.d.ts.map +0 -1
- package/es/layouts/phone.d.ts.map +0 -1
- package/es/layouts/punct.d.ts.map +0 -1
- package/es/layouts/qwerty.d.ts.map +0 -1
- package/es/layouts/search.d.ts.map +0 -1
- package/es/layouts/symbol.d.ts.map +0 -1
- package/es/layouts/url.d.ts.map +0 -1
- package/es/layouts/verifycode.d.ts.map +0 -1
- package/es/managers/index.d.ts.map +0 -1
- package/es/managers/keyboard-manager.d.ts.map +0 -1
- package/es/managers/keyboard-renderer.d.ts.map +0 -1
- package/es/managers/position-manager.d.ts.map +0 -1
- package/es/themes/index.d.ts.map +0 -1
- package/es/themes/presets.d.ts.map +0 -1
- package/es/themes/types.d.ts.map +0 -1
- package/es/types/index.d.ts.map +0 -1
- package/es/types/keyboard.d.ts.map +0 -1
- package/es/types/position.d.ts.map +0 -1
- package/es/utils/device.d.ts.map +0 -1
- package/es/utils/dom.d.ts.map +0 -1
- package/es/utils/event-emitter.d.ts.map +0 -1
- package/es/utils/formatters.d.ts.map +0 -1
- package/es/utils/index.d.ts.map +0 -1
- package/es/utils/random.d.ts.map +0 -1
- package/esm/icons.d.ts.map +0 -1
- package/esm/index.d.ts.map +0 -1
- package/esm/layouts/amount.d.ts.map +0 -1
- package/esm/layouts/bankcard.d.ts.map +0 -1
- package/esm/layouts/email.d.ts +0 -11
- package/esm/layouts/email.d.ts.map +0 -1
- package/esm/layouts/idcard.d.ts.map +0 -1
- package/esm/layouts/index.d.ts.map +0 -1
- package/esm/layouts/license-plate.d.ts.map +0 -1
- package/esm/layouts/number.d.ts.map +0 -1
- package/esm/layouts/phone.d.ts +0 -15
- package/esm/layouts/phone.d.ts.map +0 -1
- package/esm/layouts/punct.d.ts +0 -7
- package/esm/layouts/punct.d.ts.map +0 -1
- package/esm/layouts/qwerty.d.ts.map +0 -1
- package/esm/layouts/search.d.ts.map +0 -1
- package/esm/layouts/symbol.d.ts.map +0 -1
- package/esm/layouts/url.d.ts.map +0 -1
- package/esm/layouts/verifycode.d.ts +0 -15
- package/esm/layouts/verifycode.d.ts.map +0 -1
- package/esm/managers/index.d.ts +0 -8
- package/esm/managers/index.d.ts.map +0 -1
- package/esm/managers/keyboard-manager.d.ts +0 -130
- package/esm/managers/keyboard-manager.d.ts.map +0 -1
- package/esm/managers/keyboard-renderer.d.ts +0 -115
- package/esm/managers/keyboard-renderer.d.ts.map +0 -1
- package/esm/managers/position-manager.d.ts +0 -72
- package/esm/managers/position-manager.d.ts.map +0 -1
- package/esm/themes/index.d.ts +0 -70
- package/esm/themes/index.d.ts.map +0 -1
- package/esm/themes/presets.d.ts +0 -39
- package/esm/themes/presets.d.ts.map +0 -1
- package/esm/themes/types.d.ts.map +0 -1
- package/esm/themes/types.js +0 -10
- package/esm/types/index.d.ts.map +0 -1
- package/esm/types/index.js +0 -10
- package/esm/types/keyboard.d.ts.map +0 -1
- package/esm/types/keyboard.js +0 -10
- package/esm/types/position.d.ts +0 -139
- package/esm/types/position.d.ts.map +0 -1
- package/esm/types/position.js +0 -10
- package/esm/utils/device.d.ts.map +0 -1
- package/esm/utils/dom.d.ts.map +0 -1
- package/esm/utils/event-emitter.d.ts.map +0 -1
- package/esm/utils/formatters.d.ts.map +0 -1
- package/esm/utils/index.d.ts.map +0 -1
- package/esm/utils/index.js +0 -16
- package/esm/utils/random.d.ts.map +0 -1
- package/lib/icons.d.ts +0 -58
- package/lib/index.d.ts +0 -12
- package/lib/layouts/amount.d.ts +0 -14
- package/lib/layouts/bankcard.d.ts +0 -15
- package/lib/layouts/idcard.d.ts +0 -16
- package/lib/layouts/index.d.ts +0 -54
- package/lib/layouts/license-plate.d.ts +0 -35
- package/lib/layouts/number.d.ts +0 -14
- package/lib/layouts/qwerty.d.ts +0 -10
- package/lib/layouts/search.d.ts +0 -7
- package/lib/layouts/symbol.d.ts +0 -14
- package/lib/layouts/url.d.ts +0 -7
- package/lib/themes/types.d.ts +0 -118
- package/lib/types/index.d.ts +0 -7
- package/lib/types/keyboard.d.ts +0 -253
- package/lib/utils/device.d.ts +0 -45
- package/lib/utils/dom.d.ts +0 -69
- package/lib/utils/event-emitter.d.ts +0 -52
- package/lib/utils/formatters.d.ts +0 -51
- package/lib/utils/index.d.ts +0 -10
- package/lib/utils/random.d.ts +0 -71
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-manager.cjs","names":[],"sources":["../../src/managers/keyboard-manager.ts"],"sourcesContent":["/**\n * 键盘管理器\n * @module managers/keyboard-manager\n */\n\nimport type {\n KeyboardConfig,\n KeyboardEventMap,\n KeyboardOptions,\n KeyboardState,\n KeyboardType,\n KeyDefinition,\n} from '../types'\nimport { getLayout } from '../layouts'\nimport { EventEmitter } from '../utils/event-emitter'\nimport { isMobile, vibrate } from '../utils/device'\nimport {\n deleteCharBeforeCursor,\n getInputCursorPosition,\n insertTextAtCursor,\n setInputCursorPosition,\n} from '../utils/dom'\n\n/**\n * 默认键盘选项\n */\nconst DEFAULT_OPTIONS: Required<KeyboardOptions> = {\n type: 'qwerty',\n value: '',\n maxLength: undefined as unknown as number,\n defaultUpperCase: false,\n closeOnClickOutside: true,\n enableSound: false,\n enableVibrate: true,\n customLayouts: {},\n}\n\n/**\n * 键盘管理器类\n * 处理键盘状态管理、输入处理等核心逻辑\n */\nexport class KeyboardManager extends EventEmitter<KeyboardEventMap> {\n /** 配置选项 */\n private options: Required<KeyboardOptions>\n\n /** 当前状态 */\n private state: KeyboardState\n\n /** 当前布局配置 */\n private currentLayout: KeyboardConfig | null = null\n\n constructor(options: KeyboardOptions = {}) {\n super()\n this.options = { ...DEFAULT_OPTIONS, ...options }\n\n // 注册自定义布局\n if (options.customLayouts) {\n Object.entries(options.customLayouts).forEach(([name, layout]) => {\n // 这里不用 registerLayout 以避免覆盖内置布局的警告\n this.options.customLayouts[name] = layout\n })\n }\n\n // 初始化状态\n this.state = {\n visible: false,\n type: this.options.type,\n isUpperCase: this.options.defaultUpperCase,\n value: this.options.value,\n cursorPosition: this.options.value.length,\n maxLength: this.options.maxLength,\n inputElement: null,\n }\n\n // 加载初始布局\n this.loadLayout(this.options.type)\n }\n\n /**\n * 获取当前状态\n */\n getState(): Readonly<KeyboardState> {\n return { ...this.state }\n }\n\n /**\n * 获取当前布局配置\n */\n getLayout(): KeyboardConfig | null {\n return this.currentLayout\n }\n\n /**\n * 获取配置选项\n */\n getOptions(): Readonly<Required<KeyboardOptions>> {\n return { ...this.options }\n }\n\n /**\n * 加载键盘布局\n */\n private loadLayout(type: KeyboardType | string): boolean {\n // 先从自定义布局查找\n let layout = this.options.customLayouts[type]\n\n // 再从内置布局查找\n if (!layout) {\n layout = getLayout(type) as KeyboardConfig\n }\n\n if (!layout) {\n console.error(`[KeyboardManager] Layout \"${type}\" not found`)\n return false\n }\n\n this.currentLayout = layout\n\n // 更新大小写状态\n if (layout.supportShift) {\n this.state.isUpperCase = layout.defaultUpperCase ?? this.options.defaultUpperCase\n }\n\n return true\n }\n\n /**\n * 显示键盘\n */\n show(inputElement?: HTMLInputElement | HTMLTextAreaElement): void {\n if (this.state.visible) return\n\n this.state.visible = true\n this.state.inputElement = inputElement ?? null\n\n // 同步输入框的值\n if (inputElement) {\n this.state.value = inputElement.value\n this.state.cursorPosition = getInputCursorPosition(inputElement)\n }\n\n this.emitStateChange()\n this.emit('show', { type: this.state.type })\n }\n\n /**\n * 隐藏键盘\n */\n hide(): void {\n if (!this.state.visible) return\n\n const prevType = this.state.type\n this.state.visible = false\n this.state.inputElement = null\n\n this.emitStateChange()\n this.emit('hide', { type: prevType })\n }\n\n /**\n * 切换键盘显示/隐藏\n */\n toggle(inputElement?: HTMLInputElement | HTMLTextAreaElement): void {\n if (this.state.visible) {\n this.hide()\n }\n else {\n this.show(inputElement)\n }\n }\n\n /**\n * 切换键盘类型\n */\n switchType(type: KeyboardType | string): void {\n if (this.state.type === type) return\n\n const fromType = this.state.type\n if (!this.loadLayout(type)) return\n\n this.state.type = type as KeyboardType\n\n this.emitStateChange()\n this.emit('switch', { from: fromType, to: type as KeyboardType })\n }\n\n /**\n * 切换大小写\n */\n toggleShift(): void {\n if (!this.currentLayout?.supportShift) return\n\n this.state.isUpperCase = !this.state.isUpperCase\n this.emitStateChange()\n this.emit('shift', { isUpperCase: this.state.isUpperCase })\n }\n\n /**\n * 设置大小写状态\n */\n setShift(isUpperCase: boolean): void {\n if (!this.currentLayout?.supportShift) return\n if (this.state.isUpperCase === isUpperCase) return\n\n this.state.isUpperCase = isUpperCase\n this.emitStateChange()\n this.emit('shift', { isUpperCase })\n }\n\n /**\n * 处理按键\n */\n handleKey(key: KeyDefinition): void {\n // 触发震动反馈\n if (this.options.enableVibrate && isMobile()) {\n vibrate(10)\n }\n\n this.emit('keypress', { key, value: this.state.value })\n\n switch (key.type) {\n case 'char':\n case 'space':\n this.handleCharKey(key)\n break\n case 'backspace':\n this.handleBackspace()\n break\n case 'shift':\n this.toggleShift()\n break\n case 'clear':\n this.handleClear()\n break\n case 'enter':\n case 'done':\n this.handleConfirm()\n break\n case 'close':\n this.hide()\n break\n case 'tab':\n this.handleTab(key)\n break\n case 'custom':\n // 自定义按键,只触发事件\n break\n }\n }\n\n /**\n * 处理字符输入\n */\n private handleCharKey(key: KeyDefinition): void {\n const keyValue = this.getKeyValue(key)\n if (!keyValue) return\n\n const input = this.state.inputElement\n\n if (input) {\n // 如果有绑定输入框,直接操作输入框\n const result = insertTextAtCursor(input, keyValue, this.state.maxLength)\n input.value = result.value\n setInputCursorPosition(input, result.cursorPosition)\n this.state.value = result.value\n this.state.cursorPosition = result.cursorPosition\n\n // 触发原生 input 事件\n input.dispatchEvent(new Event('input', { bubbles: true }))\n }\n else {\n // 没有输入框,直接修改内部值\n if (this.state.maxLength && this.state.value.length >= this.state.maxLength) {\n return\n }\n const newValue = this.state.value.slice(0, this.state.cursorPosition)\n + keyValue\n + this.state.value.slice(this.state.cursorPosition)\n this.state.value = newValue\n this.state.cursorPosition += keyValue.length\n }\n\n this.emitStateChange()\n this.emit('input', { value: this.state.value, key })\n\n // 车牌号智能切换\n this.autoSwitchLicensePlate()\n }\n\n /**\n * 处理退格\n */\n private handleBackspace(): void {\n const input = this.state.inputElement\n\n if (input) {\n const result = deleteCharBeforeCursor(input)\n input.value = result.value\n setInputCursorPosition(input, result.cursorPosition)\n this.state.value = result.value\n this.state.cursorPosition = result.cursorPosition\n\n // 触发原生 input 事件\n input.dispatchEvent(new Event('input', { bubbles: true }))\n\n this.emitStateChange()\n this.emit('backspace', { deletedChar: result.deletedChar, value: this.state.value })\n }\n else {\n if (this.state.cursorPosition === 0) return\n\n const deletedChar = this.state.value.charAt(this.state.cursorPosition - 1)\n this.state.value = this.state.value.slice(0, this.state.cursorPosition - 1)\n + this.state.value.slice(this.state.cursorPosition)\n this.state.cursorPosition--\n\n this.emitStateChange()\n this.emit('backspace', { deletedChar, value: this.state.value })\n }\n\n // 车牌号智能切换\n this.autoSwitchLicensePlate()\n }\n\n /**\n * 车牌号智能切换逻辑\n * 根据当前值长度自动切换键盘布局\n * 只有 2 种布局:省份键盘(第1位)和 字母+数字混合键盘(第2-8位)\n */\n private autoSwitchLicensePlate(): void {\n // 只在车牌键盘类型时生效\n if (!this.state.type.startsWith('license-plate')) return\n\n const len = this.state.value.length\n let targetType: string | null = null\n\n if (len === 0) {\n // 第1位:省份简称\n targetType = 'license-plate-province'\n }\n else {\n // 第2-8位:字母+数字混合键盘\n // (第2位虽然只能输入字母,但显示混合键盘,用户自己知道不能输入数字)\n targetType = 'license-plate-alphanumeric'\n }\n\n // 如果需要切换\n if (targetType && this.state.type !== targetType) {\n this.switchType(targetType)\n }\n }\n\n /**\n * 处理清空\n */\n private handleClear(): void {\n const previousValue = this.state.value\n\n if (this.state.inputElement) {\n this.state.inputElement.value = ''\n this.state.inputElement.dispatchEvent(new Event('input', { bubbles: true }))\n }\n\n this.state.value = ''\n this.state.cursorPosition = 0\n\n this.emitStateChange()\n this.emit('clear', { previousValue })\n \n // 车牌号智能切换\n this.autoSwitchLicensePlate()\n }\n\n /**\n * 处理确认\n */\n private handleConfirm(): void {\n this.emit('confirm', { value: this.state.value })\n this.hide()\n }\n\n /**\n * 处理切换键盘\n */\n private handleTab(key: KeyDefinition): void {\n const targetType = key.data?.targetType as KeyboardType | string | undefined\n if (targetType) {\n this.switchType(targetType)\n }\n }\n\n /**\n * 获取按键的实际输出值\n */\n private getKeyValue(key: KeyDefinition): string {\n const value = key.value ?? key.label\n\n if (typeof value === 'string') {\n return value\n }\n\n return this.state.isUpperCase ? value.upper : value.lower\n }\n\n /**\n * 获取按键的显示文本\n */\n getKeyLabel(key: KeyDefinition): string {\n const label = key.label\n\n if (typeof label === 'string') {\n return label\n }\n\n return this.state.isUpperCase ? label.upper : label.lower\n }\n\n /**\n * 设置值\n */\n setValue(value: string): void {\n this.state.value = value\n this.state.cursorPosition = value.length\n\n if (this.state.inputElement) {\n this.state.inputElement.value = value\n setInputCursorPosition(this.state.inputElement, value.length)\n }\n\n this.emitStateChange()\n \n // 车牌号智能切换\n this.autoSwitchLicensePlate()\n }\n\n /**\n * 追加值(在光标位置插入文本)\n */\n appendValue(text: string): void {\n if (!text) return\n\n const input = this.state.inputElement\n\n if (input) {\n const result = insertTextAtCursor(input, text, this.state.maxLength)\n input.value = result.value\n setInputCursorPosition(input, result.cursorPosition)\n this.state.value = result.value\n this.state.cursorPosition = result.cursorPosition\n\n // 触发原生 input 事件\n input.dispatchEvent(new Event('input', { bubbles: true }))\n }\n else {\n if (this.state.maxLength && this.state.value.length + text.length > this.state.maxLength) {\n text = text.slice(0, this.state.maxLength - this.state.value.length)\n }\n const newValue = this.state.value.slice(0, this.state.cursorPosition)\n + text\n + this.state.value.slice(this.state.cursorPosition)\n this.state.value = newValue\n this.state.cursorPosition += text.length\n }\n\n this.emitStateChange()\n }\n\n /**\n * 设置最大长度\n */\n setMaxLength(maxLength?: number): void {\n this.state.maxLength = maxLength\n this.emitStateChange()\n }\n\n /**\n * 绑定输入框\n */\n bindInput(input: HTMLInputElement | HTMLTextAreaElement): void {\n this.state.inputElement = input\n this.state.value = input.value\n this.state.cursorPosition = getInputCursorPosition(input)\n this.emitStateChange()\n }\n\n /**\n * 解绑输入框\n */\n unbindInput(): void {\n this.state.inputElement = null\n this.emitStateChange()\n }\n\n /**\n * 发送状态变化事件\n */\n private emitStateChange(): void {\n this.emit('stateChange', { ...this.state })\n }\n\n /**\n * 销毁\n */\n destroy(): void {\n this.hide()\n this.clear()\n this.state.inputElement = null\n }\n}\n\n/**\n * 创建键盘管理器实例\n */\nexport function createKeyboardManager(options?: KeyboardOptions): KeyboardManager {\n return new KeyboardManager(options)\n}\n"],"mappings":";;;;;;;;;;;;;;;AA0BA,MAAM,kBAA6C;CACjD,MAAM;CACN,OAAO;CACP,WAAW,KAAA;CACX,kBAAkB;CAClB,qBAAqB;CACrB,aAAa;CACb,eAAe;CACf,eAAe,CAAC;AAClB;AAMO,IAAM,kBAAN,cAA8B,sBAAA,aAA+B;CAUlE,YAAY,UAA2B,CAAC,GAAG;EACzC,MAAM;;EAHR,KAAQ,gBAAuC;EAI7C,KAAK,UAAU;GAAE,GAAG;GAAiB,GAAG;EAAQ;EAGhD,IAAI,QAAQ,eACV,OAAO,QAAQ,QAAQ,aAAa,CAAA,CAAE,SAAS,CAAC,MAAM,YAAY;GAEhE,KAAK,QAAQ,cAAc,QAAQ;EACrC,CAAC;EAIH,KAAK,QAAQ;GACX,SAAS;GACT,MAAM,KAAK,QAAQ;GACnB,aAAa,KAAK,QAAQ;GAC1B,OAAO,KAAK,QAAQ;GACpB,gBAAgB,KAAK,QAAQ,MAAM;GACnC,WAAW,KAAK,QAAQ;GACxB,cAAc;EAChB;EAGA,KAAK,WAAW,KAAK,QAAQ,IAAI;CACnC;;;;CAKA,WAAoC;EAClC,OAAO,EAAE,GAAG,KAAK,MAAM;CACzB;;;;CAKA,YAAmC;EACjC,OAAO,KAAK;CACd;;;;CAKA,aAAkD;EAChD,OAAO,EAAE,GAAG,KAAK,QAAQ;CAC3B;;;;CAKQ,WAAW,MAAsC;EAEvD,IAAI,SAAS,KAAK,QAAQ,cAAc;EAGxC,IAAI,CAAC,QACH,SAAS,cAAA,UAAU,IAAI;EAGzB,IAAI,CAAC,QAAQ;GACX,QAAQ,MAAM,6BAA6B,KAAI,YAAa;GAC5D,OAAO;EACT;EAEA,KAAK,gBAAgB;EAGrB,IAAI,OAAO,cACT,KAAK,MAAM,cAAc,OAAO,oBAAoB,KAAK,QAAQ;EAGnE,OAAO;CACT;;;;CAKA,KAAK,cAA6D;EAChE,IAAI,KAAK,MAAM,SAAS;EAExB,KAAK,MAAM,UAAU;EACrB,KAAK,MAAM,eAAe,gBAAgB;EAG1C,IAAI,cAAc;GAChB,KAAK,MAAM,QAAQ,aAAa;GAChC,KAAK,MAAM,iBAAiB,YAAA,uBAAuB,YAAY;EACjE;EAEA,KAAK,gBAAgB;EACrB,KAAK,KAAK,QAAQ,EAAE,MAAM,KAAK,MAAM,KAAK,CAAC;CAC7C;;;;CAKA,OAAa;EACX,IAAI,CAAC,KAAK,MAAM,SAAS;EAEzB,MAAM,WAAW,KAAK,MAAM;EAC5B,KAAK,MAAM,UAAU;EACrB,KAAK,MAAM,eAAe;EAE1B,KAAK,gBAAgB;EACrB,KAAK,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;CACtC;;;;CAKA,OAAO,cAA6D;EAClE,IAAI,KAAK,MAAM,SACb,KAAK,KAAK;OAGV,KAAK,KAAK,YAAY;CAE1B;;;;CAKA,WAAW,MAAmC;EAC5C,IAAI,KAAK,MAAM,SAAS,MAAM;EAE9B,MAAM,WAAW,KAAK,MAAM;EAC5B,IAAI,CAAC,KAAK,WAAW,IAAI,GAAG;EAE5B,KAAK,MAAM,OAAO;EAElB,KAAK,gBAAgB;EACrB,KAAK,KAAK,UAAU;GAAE,MAAM;GAAU,IAAI;EAAqB,CAAC;CAClE;;;;CAKA,cAAoB;EAClB,IAAI,CAAC,KAAK,eAAe,cAAc;EAEvC,KAAK,MAAM,cAAc,CAAC,KAAK,MAAM;EACrC,KAAK,gBAAgB;EACrB,KAAK,KAAK,SAAS,EAAE,aAAa,KAAK,MAAM,YAAY,CAAC;CAC5D;;;;CAKA,SAAS,aAA4B;EACnC,IAAI,CAAC,KAAK,eAAe,cAAc;EACvC,IAAI,KAAK,MAAM,gBAAgB,aAAa;EAE5C,KAAK,MAAM,cAAc;EACzB,KAAK,gBAAgB;EACrB,KAAK,KAAK,SAAS,EAAE,YAAY,CAAC;CACpC;;;;CAKA,UAAU,KAA0B;EAElC,IAAI,KAAK,QAAQ,iBAAiB,eAAA,SAAS,GACzC,eAAA,QAAQ,EAAE;EAGZ,KAAK,KAAK,YAAY;GAAE;GAAK,OAAO,KAAK,MAAM;EAAM,CAAC;EAEtD,QAAQ,IAAI,MAAZ;GACE,KAAK;GACL,KAAK;IACH,KAAK,cAAc,GAAG;IACtB;GACF,KAAK;IACH,KAAK,gBAAgB;IACrB;GACF,KAAK;IACH,KAAK,YAAY;IACjB;GACF,KAAK;IACH,KAAK,YAAY;IACjB;GACF,KAAK;GACL,KAAK;IACH,KAAK,cAAc;IACnB;GACF,KAAK;IACH,KAAK,KAAK;IACV;GACF,KAAK;IACH,KAAK,UAAU,GAAG;IAClB;GACF,KAAK,UAEH;EACJ;CACF;;;;CAKQ,cAAc,KAA0B;EAC9C,MAAM,WAAW,KAAK,YAAY,GAAG;EACrC,IAAI,CAAC,UAAU;EAEf,MAAM,QAAQ,KAAK,MAAM;EAEzB,IAAI,OAAO;GAET,MAAM,SAAS,YAAA,mBAAmB,OAAO,UAAU,KAAK,MAAM,SAAS;GACvE,MAAM,QAAQ,OAAO;GACrB,YAAA,uBAAuB,OAAO,OAAO,cAAc;GACnD,KAAK,MAAM,QAAQ,OAAO;GAC1B,KAAK,MAAM,iBAAiB,OAAO;GAGnC,MAAM,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;EAC3D,OACK;GAEH,IAAI,KAAK,MAAM,aAAa,KAAK,MAAM,MAAM,UAAU,KAAK,MAAM,WAChE;GAEF,MAAM,WAAW,KAAK,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,cAAc,IAChE,WACA,KAAK,MAAM,MAAM,MAAM,KAAK,MAAM,cAAc;GACpD,KAAK,MAAM,QAAQ;GACnB,KAAK,MAAM,kBAAkB,SAAS;EACxC;EAEA,KAAK,gBAAgB;EACrB,KAAK,KAAK,SAAS;GAAE,OAAO,KAAK,MAAM;GAAO;EAAI,CAAC;EAGnD,KAAK,uBAAuB;CAC9B;;;;CAKQ,kBAAwB;EAC9B,MAAM,QAAQ,KAAK,MAAM;EAEzB,IAAI,OAAO;GACT,MAAM,SAAS,YAAA,uBAAuB,KAAK;GAC3C,MAAM,QAAQ,OAAO;GACrB,YAAA,uBAAuB,OAAO,OAAO,cAAc;GACnD,KAAK,MAAM,QAAQ,OAAO;GAC1B,KAAK,MAAM,iBAAiB,OAAO;GAGnC,MAAM,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;GAEzD,KAAK,gBAAgB;GACrB,KAAK,KAAK,aAAa;IAAE,aAAa,OAAO;IAAa,OAAO,KAAK,MAAM;GAAM,CAAC;EACrF,OACK;GACH,IAAI,KAAK,MAAM,mBAAmB,GAAG;GAErC,MAAM,cAAc,KAAK,MAAM,MAAM,OAAO,KAAK,MAAM,iBAAiB,CAAC;GACzE,KAAK,MAAM,QAAQ,KAAK,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,iBAAiB,CAAC,IACtE,KAAK,MAAM,MAAM,MAAM,KAAK,MAAM,cAAc;GACpD,KAAK,MAAM;GAEX,KAAK,gBAAgB;GACrB,KAAK,KAAK,aAAa;IAAE;IAAa,OAAO,KAAK,MAAM;GAAM,CAAC;EACjE;EAGA,KAAK,uBAAuB;CAC9B;;;;;;CAOQ,yBAA+B;EAErC,IAAI,CAAC,KAAK,MAAM,KAAK,WAAW,eAAe,GAAG;EAElD,MAAM,MAAM,KAAK,MAAM,MAAM;EAC7B,IAAI,aAA4B;EAEhC,IAAI,QAAQ,GAEV,aAAa;OAKb,aAAa;EAIf,IAAI,cAAc,KAAK,MAAM,SAAS,YACpC,KAAK,WAAW,UAAU;CAE9B;;;;CAKQ,cAAoB;EAC1B,MAAM,gBAAgB,KAAK,MAAM;EAEjC,IAAI,KAAK,MAAM,cAAc;GAC3B,KAAK,MAAM,aAAa,QAAQ;GAChC,KAAK,MAAM,aAAa,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;EAC7E;EAEA,KAAK,MAAM,QAAQ;EACnB,KAAK,MAAM,iBAAiB;EAE5B,KAAK,gBAAgB;EACrB,KAAK,KAAK,SAAS,EAAE,cAAc,CAAC;EAGpC,KAAK,uBAAuB;CAC9B;;;;CAKQ,gBAAsB;EAC5B,KAAK,KAAK,WAAW,EAAE,OAAO,KAAK,MAAM,MAAM,CAAC;EAChD,KAAK,KAAK;CACZ;;;;CAKQ,UAAU,KAA0B;EAC1C,MAAM,aAAa,IAAI,MAAM;EAC7B,IAAI,YACF,KAAK,WAAW,UAAU;CAE9B;;;;CAKQ,YAAY,KAA4B;EAC9C,MAAM,QAAQ,IAAI,SAAS,IAAI;EAE/B,IAAI,OAAO,UAAU,UACnB,OAAO;EAGT,OAAO,KAAK,MAAM,cAAc,MAAM,QAAQ,MAAM;CACtD;;;;CAKA,YAAY,KAA4B;EACtC,MAAM,QAAQ,IAAI;EAElB,IAAI,OAAO,UAAU,UACnB,OAAO;EAGT,OAAO,KAAK,MAAM,cAAc,MAAM,QAAQ,MAAM;CACtD;;;;CAKA,SAAS,OAAqB;EAC5B,KAAK,MAAM,QAAQ;EACnB,KAAK,MAAM,iBAAiB,MAAM;EAElC,IAAI,KAAK,MAAM,cAAc;GAC3B,KAAK,MAAM,aAAa,QAAQ;GAChC,YAAA,uBAAuB,KAAK,MAAM,cAAc,MAAM,MAAM;EAC9D;EAEA,KAAK,gBAAgB;EAGrB,KAAK,uBAAuB;CAC9B;;;;CAKA,YAAY,MAAoB;EAC9B,IAAI,CAAC,MAAM;EAEX,MAAM,QAAQ,KAAK,MAAM;EAEzB,IAAI,OAAO;GACT,MAAM,SAAS,YAAA,mBAAmB,OAAO,MAAM,KAAK,MAAM,SAAS;GACnE,MAAM,QAAQ,OAAO;GACrB,YAAA,uBAAuB,OAAO,OAAO,cAAc;GACnD,KAAK,MAAM,QAAQ,OAAO;GAC1B,KAAK,MAAM,iBAAiB,OAAO;GAGnC,MAAM,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;EAC3D,OACK;GACH,IAAI,KAAK,MAAM,aAAa,KAAK,MAAM,MAAM,SAAS,KAAK,SAAS,KAAK,MAAM,WAC7E,OAAO,KAAK,MAAM,GAAG,KAAK,MAAM,YAAY,KAAK,MAAM,MAAM,MAAM;GAErE,MAAM,WAAW,KAAK,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,cAAc,IAChE,OACA,KAAK,MAAM,MAAM,MAAM,KAAK,MAAM,cAAc;GACpD,KAAK,MAAM,QAAQ;GACnB,KAAK,MAAM,kBAAkB,KAAK;EACpC;EAEA,KAAK,gBAAgB;CACvB;;;;CAKA,aAAa,WAA0B;EACrC,KAAK,MAAM,YAAY;EACvB,KAAK,gBAAgB;CACvB;;;;CAKA,UAAU,OAAqD;EAC7D,KAAK,MAAM,eAAe;EAC1B,KAAK,MAAM,QAAQ,MAAM;EACzB,KAAK,MAAM,iBAAiB,YAAA,uBAAuB,KAAK;EACxD,KAAK,gBAAgB;CACvB;;;;CAKA,cAAoB;EAClB,KAAK,MAAM,eAAe;EAC1B,KAAK,gBAAgB;CACvB;;;;CAKQ,kBAAwB;EAC9B,KAAK,KAAK,eAAe,EAAE,GAAG,KAAK,MAAM,CAAC;CAC5C;;;;CAKA,UAAgB;EACd,KAAK,KAAK;EACV,KAAK,MAAM;EACX,KAAK,MAAM,eAAe;CAC5B;AACF;AAKO,SAAS,sBAAsB,SAA4C;CAChF,OAAO,IAAI,gBAAgB,OAAO;AACpC"}
|
|
1
|
+
{"version":3,"file":"keyboard-manager.cjs","names":[],"sources":["../../src/managers/keyboard-manager.ts"],"sourcesContent":["/**\r\n * 键盘管理器\r\n * @module managers/keyboard-manager\r\n */\r\n\r\nimport type {\r\n KeyboardConfig,\r\n KeyboardEventMap,\r\n KeyboardOptions,\r\n KeyboardState,\r\n KeyboardType,\r\n KeyDefinition,\r\n} from '../types'\r\nimport { getLayout } from '../layouts'\r\nimport { isMobile, vibrate } from '../utils/device'\r\nimport {\r\n deleteCharBeforeCursor,\r\n getInputCursorPosition,\r\n insertTextAtCursor,\r\n setInputCursorPosition,\r\n} from '../utils/dom'\r\nimport { EventEmitter } from '../utils/event-emitter'\r\n\r\n/**\r\n * 默认键盘选项\r\n */\r\nconst DEFAULT_OPTIONS: Required<KeyboardOptions> = {\r\n type: 'qwerty',\r\n value: '',\r\n maxLength: undefined as unknown as number,\r\n defaultUpperCase: false,\r\n closeOnClickOutside: true,\r\n enableSound: false,\r\n enableVibrate: true,\r\n customLayouts: {},\r\n}\r\n\r\nfunction normalizeMaxLength(value: number | undefined): number | undefined {\r\n if (value === undefined || !Number.isFinite(value)) return undefined\r\n return Math.max(0, Math.floor(value))\r\n}\r\n\r\nfunction clampValue(value: string, maxLength: number | undefined): string {\r\n return maxLength === undefined ? value : value.slice(0, maxLength)\r\n}\r\n\r\n/**\r\n * 键盘管理器类\r\n * 处理键盘状态管理、输入处理等核心逻辑\r\n */\r\nexport class KeyboardManager extends EventEmitter<KeyboardEventMap> {\r\n /** 配置选项 */\r\n private options: Required<KeyboardOptions>\r\n\r\n /** 当前状态 */\r\n private state: KeyboardState\r\n\r\n /** 当前布局配置 */\r\n private currentLayout: KeyboardConfig | null = null\r\n\r\n private destroyed = false\r\n\r\n constructor(options: KeyboardOptions = {}) {\r\n super()\r\n const maxLength = normalizeMaxLength(options.maxLength)\r\n this.options = {\r\n ...DEFAULT_OPTIONS,\r\n ...options,\r\n maxLength,\r\n customLayouts: {\r\n ...DEFAULT_OPTIONS.customLayouts,\r\n ...(options.customLayouts ?? {}),\r\n },\r\n }\r\n\r\n // 注册自定义布局\r\n if (options.customLayouts) {\r\n Object.entries(options.customLayouts).forEach(([name, layout]) => {\r\n // 这里不用 registerLayout 以避免覆盖内置布局的警告\r\n this.options.customLayouts[name] = layout\r\n })\r\n }\r\n\r\n // 初始化状态\r\n this.state = {\r\n visible: false,\r\n type: this.options.type,\r\n isUpperCase: this.options.defaultUpperCase,\r\n value: clampValue(this.options.value, this.options.maxLength),\r\n cursorPosition: clampValue(this.options.value, this.options.maxLength)\r\n .length,\r\n maxLength: this.options.maxLength,\r\n inputElement: null,\r\n }\r\n\r\n // 加载初始布局\r\n this.loadLayout(this.options.type)\r\n }\r\n\r\n /**\r\n * 获取当前状态\r\n */\r\n getState(): Readonly<KeyboardState> {\r\n return { ...this.state }\r\n }\r\n\r\n /**\r\n * 获取当前布局配置\r\n */\r\n getLayout(): KeyboardConfig | null {\r\n return this.currentLayout\r\n }\r\n\r\n /**\r\n * 获取配置选项\r\n */\r\n getOptions(): Readonly<Required<KeyboardOptions>> {\r\n return { ...this.options, customLayouts: { ...this.options.customLayouts } }\r\n }\r\n\r\n /**\r\n * 加载键盘布局\r\n */\r\n private loadLayout(type: KeyboardType | string): boolean {\r\n // 先从自定义布局查找\r\n let layout = this.options.customLayouts[type]\r\n\r\n // 再从内置布局查找\r\n if (!layout) {\r\n layout = getLayout(type) as KeyboardConfig\r\n }\r\n\r\n if (!layout) {\r\n console.error(`[KeyboardManager] Layout \"${type}\" not found`)\r\n return false\r\n }\r\n\r\n this.currentLayout = layout\r\n\r\n // 更新大小写状态\r\n if (layout.supportShift) {\r\n this.state.isUpperCase =\r\n layout.defaultUpperCase ?? this.options.defaultUpperCase\r\n }\r\n\r\n return true\r\n }\r\n\r\n /**\r\n * 显示键盘\r\n */\r\n show(inputElement?: HTMLInputElement | HTMLTextAreaElement): void {\r\n if (this.destroyed) return\r\n if (this.state.visible) {\r\n if (inputElement && this.state.inputElement !== inputElement) {\r\n this.bindInput(inputElement)\r\n }\r\n return\r\n }\r\n\r\n this.state.visible = true\r\n this.state.inputElement = inputElement ?? null\r\n\r\n // 同步输入框的值\r\n if (inputElement) {\r\n this.state.value = inputElement.value\r\n this.state.cursorPosition = getInputCursorPosition(inputElement)\r\n }\r\n\r\n this.emitStateChange()\r\n this.emit('show', { type: this.state.type })\r\n }\r\n\r\n /**\r\n * 隐藏键盘\r\n */\r\n hide(): void {\r\n if (this.destroyed) return\r\n if (!this.state.visible) return\r\n\r\n const prevType = this.state.type\r\n this.state.visible = false\r\n this.state.inputElement = null\r\n\r\n this.emitStateChange()\r\n this.emit('hide', { type: prevType })\r\n }\r\n\r\n /**\r\n * 切换键盘显示/隐藏\r\n */\r\n toggle(inputElement?: HTMLInputElement | HTMLTextAreaElement): void {\r\n if (this.destroyed) return\r\n if (this.state.visible) {\r\n this.hide()\r\n } else {\r\n this.show(inputElement)\r\n }\r\n }\r\n\r\n /**\r\n * 切换键盘类型\r\n */\r\n switchType(type: KeyboardType | string): void {\r\n if (this.destroyed) return\r\n if (this.state.type === type) return\r\n\r\n const fromType = this.state.type\r\n if (!this.loadLayout(type)) return\r\n\r\n this.state.type = type as KeyboardType\r\n\r\n this.emitStateChange()\r\n this.emit('switch', { from: fromType, to: type as KeyboardType })\r\n }\r\n\r\n /**\r\n * 切换大小写\r\n */\r\n toggleShift(): void {\r\n if (this.destroyed) return\r\n if (!this.currentLayout?.supportShift) return\r\n\r\n this.state.isUpperCase = !this.state.isUpperCase\r\n this.emitStateChange()\r\n this.emit('shift', { isUpperCase: this.state.isUpperCase })\r\n }\r\n\r\n /**\r\n * 设置大小写状态\r\n */\r\n setShift(isUpperCase: boolean): void {\r\n if (this.destroyed) return\r\n if (!this.currentLayout?.supportShift) return\r\n if (this.state.isUpperCase === isUpperCase) return\r\n\r\n this.state.isUpperCase = isUpperCase\r\n this.emitStateChange()\r\n this.emit('shift', { isUpperCase })\r\n }\r\n\r\n /**\r\n * 处理按键\r\n */\r\n handleKey(key: KeyDefinition): void {\r\n if (this.destroyed) return\r\n // 触发震动反馈\r\n if (this.options.enableVibrate && isMobile()) {\r\n vibrate(10)\r\n }\r\n\r\n this.emit('keypress', { key, value: this.state.value })\r\n\r\n switch (key.type) {\r\n case 'char':\r\n case 'space':\r\n this.handleCharKey(key)\r\n break\r\n case 'backspace':\r\n this.handleBackspace()\r\n break\r\n case 'shift':\r\n this.toggleShift()\r\n break\r\n case 'clear':\r\n this.handleClear()\r\n break\r\n case 'enter':\r\n case 'done':\r\n this.handleConfirm()\r\n break\r\n case 'close':\r\n this.hide()\r\n break\r\n case 'tab':\r\n this.handleTab(key)\r\n break\r\n case 'custom':\r\n // 自定义按键,只触发事件\r\n break\r\n }\r\n }\r\n\r\n /**\r\n * 处理字符输入\r\n */\r\n private handleCharKey(key: KeyDefinition): void {\r\n const keyValue = this.getKeyValue(key)\r\n if (!keyValue) return\r\n\r\n const input = this.state.inputElement\r\n\r\n if (input) {\r\n // 如果有绑定输入框,直接操作输入框\r\n const result = insertTextAtCursor(input, keyValue, this.state.maxLength)\r\n input.value = result.value\r\n setInputCursorPosition(input, result.cursorPosition)\r\n this.state.value = result.value\r\n this.state.cursorPosition = result.cursorPosition\r\n\r\n // 触发原生 input 事件\r\n this.dispatchInputEvent(input)\r\n } else {\r\n // 没有输入框,直接修改内部值\r\n if (\r\n this.state.maxLength !== undefined &&\r\n this.state.value.length >= this.state.maxLength\r\n ) {\r\n return\r\n }\r\n const availableLength =\r\n this.state.maxLength === undefined\r\n ? keyValue.length\r\n : this.state.maxLength - this.state.value.length\r\n const valueToInsert = keyValue.slice(0, Math.max(0, availableLength))\r\n if (!valueToInsert) return\r\n const newValue =\r\n this.state.value.slice(0, this.state.cursorPosition) +\r\n valueToInsert +\r\n this.state.value.slice(this.state.cursorPosition)\r\n this.state.value = newValue\r\n this.state.cursorPosition += valueToInsert.length\r\n }\r\n\r\n this.emitStateChange()\r\n this.emit('input', { value: this.state.value, key })\r\n\r\n // 车牌号智能切换\r\n this.autoSwitchLicensePlate()\r\n }\r\n\r\n /**\r\n * 处理退格\r\n */\r\n private handleBackspace(): void {\r\n const input = this.state.inputElement\r\n\r\n if (input) {\r\n const result = deleteCharBeforeCursor(input)\r\n input.value = result.value\r\n setInputCursorPosition(input, result.cursorPosition)\r\n this.state.value = result.value\r\n this.state.cursorPosition = result.cursorPosition\r\n\r\n // 触发原生 input 事件\r\n this.dispatchInputEvent(input)\r\n\r\n this.emitStateChange()\r\n this.emit('backspace', {\r\n deletedChar: result.deletedChar,\r\n value: this.state.value,\r\n })\r\n } else {\r\n if (this.state.cursorPosition === 0) return\r\n\r\n const deletedChar = this.state.value.charAt(this.state.cursorPosition - 1)\r\n this.state.value =\r\n this.state.value.slice(0, this.state.cursorPosition - 1) +\r\n this.state.value.slice(this.state.cursorPosition)\r\n this.state.cursorPosition--\r\n\r\n this.emitStateChange()\r\n this.emit('backspace', { deletedChar, value: this.state.value })\r\n }\r\n\r\n // 车牌号智能切换\r\n this.autoSwitchLicensePlate()\r\n }\r\n\r\n /**\r\n * 车牌号智能切换逻辑\r\n * 根据当前值长度自动切换键盘布局\r\n * 只有 2 种布局:省份键盘(第1位)和 字母+数字混合键盘(第2-8位)\r\n */\r\n private autoSwitchLicensePlate(): void {\r\n // 只在车牌键盘类型时生效\r\n if (!this.state.type.startsWith('license-plate')) return\r\n\r\n const len = this.state.value.length\r\n let targetType: string | null = null\r\n\r\n if (len === 0) {\r\n // 第1位:省份简称\r\n targetType = 'license-plate-province'\r\n } else {\r\n // 第2-8位:字母+数字混合键盘\r\n // (第2位虽然只能输入字母,但显示混合键盘,用户自己知道不能输入数字)\r\n targetType = 'license-plate-alphanumeric'\r\n }\r\n\r\n // 如果需要切换\r\n if (targetType && this.state.type !== targetType) {\r\n this.switchType(targetType)\r\n }\r\n }\r\n\r\n /**\r\n * 处理清空\r\n */\r\n private handleClear(): void {\r\n const previousValue = this.state.value\r\n\r\n if (this.state.inputElement) {\r\n this.state.inputElement.value = ''\r\n this.dispatchInputEvent(this.state.inputElement)\r\n }\r\n\r\n this.state.value = ''\r\n this.state.cursorPosition = 0\r\n\r\n this.emitStateChange()\r\n this.emit('clear', { previousValue })\r\n\r\n // 车牌号智能切换\r\n this.autoSwitchLicensePlate()\r\n }\r\n\r\n /**\r\n * 处理确认\r\n */\r\n private handleConfirm(): void {\r\n this.emit('confirm', { value: this.state.value })\r\n this.hide()\r\n }\r\n\r\n /**\r\n * 处理切换键盘\r\n */\r\n private handleTab(key: KeyDefinition): void {\r\n const targetType = key.data?.targetType as KeyboardType | string | undefined\r\n if (targetType) {\r\n this.switchType(targetType)\r\n }\r\n }\r\n\r\n /**\r\n * 获取按键的实际输出值\r\n */\r\n private getKeyValue(key: KeyDefinition): string {\r\n const value = key.value ?? key.label\r\n\r\n if (typeof value === 'string') {\r\n return value\r\n }\r\n\r\n return this.state.isUpperCase ? value.upper : value.lower\r\n }\r\n\r\n /**\r\n * 获取按键的显示文本\r\n */\r\n getKeyLabel(key: KeyDefinition): string {\r\n const label = key.label\r\n\r\n if (typeof label === 'string') {\r\n return label\r\n }\r\n\r\n return this.state.isUpperCase ? label.upper : label.lower\r\n }\r\n\r\n /**\r\n * 设置值\r\n */\r\n setValue(value: string): void {\r\n if (this.destroyed) return\r\n const nextValue = clampValue(value, this.state.maxLength)\r\n this.state.value = nextValue\r\n this.state.cursorPosition = nextValue.length\r\n\r\n if (this.state.inputElement) {\r\n this.state.inputElement.value = nextValue\r\n setInputCursorPosition(this.state.inputElement, nextValue.length)\r\n this.dispatchInputEvent(this.state.inputElement)\r\n }\r\n\r\n this.emitStateChange()\r\n\r\n // 车牌号智能切换\r\n this.autoSwitchLicensePlate()\r\n }\r\n\r\n /**\r\n * 追加值(在光标位置插入文本)\r\n */\r\n appendValue(text: string): void {\r\n if (this.destroyed) return\r\n if (!text) return\r\n\r\n const input = this.state.inputElement\r\n\r\n if (input) {\r\n const result = insertTextAtCursor(input, text, this.state.maxLength)\r\n input.value = result.value\r\n setInputCursorPosition(input, result.cursorPosition)\r\n this.state.value = result.value\r\n this.state.cursorPosition = result.cursorPosition\r\n\r\n // 触发原生 input 事件\r\n this.dispatchInputEvent(input)\r\n } else {\r\n if (this.state.maxLength !== undefined) {\r\n text = text.slice(\r\n 0,\r\n Math.max(0, this.state.maxLength - this.state.value.length)\r\n )\r\n }\r\n if (!text) return\r\n const newValue =\r\n this.state.value.slice(0, this.state.cursorPosition) +\r\n text +\r\n this.state.value.slice(this.state.cursorPosition)\r\n this.state.value = newValue\r\n this.state.cursorPosition += text.length\r\n }\r\n\r\n this.emitStateChange()\r\n }\r\n\r\n /**\r\n * 设置最大长度\r\n */\r\n setMaxLength(maxLength?: number): void {\r\n if (this.destroyed) return\r\n const normalizedMaxLength = normalizeMaxLength(maxLength)\r\n this.state.maxLength = normalizedMaxLength\r\n const nextValue = clampValue(this.state.value, normalizedMaxLength)\r\n const changed = nextValue !== this.state.value\r\n this.state.value = nextValue\r\n this.state.cursorPosition = Math.min(\r\n this.state.cursorPosition,\r\n nextValue.length\r\n )\r\n if (\r\n this.state.inputElement &&\r\n this.state.inputElement.value !== nextValue\r\n ) {\r\n this.state.inputElement.value = nextValue\r\n setInputCursorPosition(this.state.inputElement, this.state.cursorPosition)\r\n this.dispatchInputEvent(this.state.inputElement)\r\n }\r\n this.emitStateChange()\r\n if (changed) this.autoSwitchLicensePlate()\r\n }\r\n\r\n /**\r\n * 绑定输入框\r\n */\r\n bindInput(input: HTMLInputElement | HTMLTextAreaElement): void {\r\n if (this.destroyed) return\r\n this.state.inputElement = input\r\n const nextValue = clampValue(input.value, this.state.maxLength)\r\n if (input.value !== nextValue) input.value = nextValue\r\n this.state.value = nextValue\r\n this.state.cursorPosition = Math.min(\r\n getInputCursorPosition(input),\r\n nextValue.length\r\n )\r\n this.emitStateChange()\r\n }\r\n\r\n /**\r\n * 解绑输入框\r\n */\r\n unbindInput(): void {\r\n if (this.destroyed) return\r\n this.state.inputElement = null\r\n this.emitStateChange()\r\n }\r\n\r\n /**\r\n * 发送状态变化事件\r\n */\r\n private emitStateChange(): void {\r\n this.emit('stateChange', { ...this.state })\r\n }\r\n\r\n private dispatchInputEvent(\r\n input: HTMLInputElement | HTMLTextAreaElement\r\n ): void {\r\n const EventConstructor = input.ownerDocument.defaultView?.Event ?? Event\r\n input.dispatchEvent(new EventConstructor('input', { bubbles: true }))\r\n }\r\n\r\n /**\r\n * 销毁\r\n */\r\n destroy(): void {\r\n if (this.destroyed) return\r\n this.hide()\r\n this.clear()\r\n this.state.inputElement = null\r\n this.destroyed = true\r\n }\r\n}\r\n\r\n/**\r\n * 创建键盘管理器实例\r\n */\r\nexport function createKeyboardManager(\r\n options?: KeyboardOptions\r\n): KeyboardManager {\r\n return new KeyboardManager(options)\r\n}\r\n"],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@ export declare class KeyboardManager extends EventEmitter<KeyboardEventMap> {
|
|
|
15
15
|
private state;
|
|
16
16
|
/** 当前布局配置 */
|
|
17
17
|
private currentLayout;
|
|
18
|
+
private destroyed;
|
|
18
19
|
constructor(options?: KeyboardOptions);
|
|
19
20
|
/**
|
|
20
21
|
* 获取当前状态
|
|
@@ -118,6 +119,7 @@ export declare class KeyboardManager extends EventEmitter<KeyboardEventMap> {
|
|
|
118
119
|
* 发送状态变化事件
|
|
119
120
|
*/
|
|
120
121
|
private emitStateChange;
|
|
122
|
+
private dispatchInputEvent;
|
|
121
123
|
/**
|
|
122
124
|
* 销毁
|
|
123
125
|
*/
|
|
@@ -127,4 +129,3 @@ export declare class KeyboardManager extends EventEmitter<KeyboardEventMap> {
|
|
|
127
129
|
* 创建键盘管理器实例
|
|
128
130
|
*/
|
|
129
131
|
export declare function createKeyboardManager(options?: KeyboardOptions): KeyboardManager;
|
|
130
|
-
//# sourceMappingURL=keyboard-manager.d.ts.map
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ***********************************
|
|
3
3
|
* @composy/keyboard-core v0.0.1 *
|
|
4
4
|
* Built with rollup *
|
|
5
|
-
* Build time: 2024-
|
|
5
|
+
* Build time: 2024-10-12 17:29:15 *
|
|
6
6
|
* Build mode: production *
|
|
7
7
|
* Minified: No *
|
|
8
8
|
* ***********************************
|
|
@@ -11,10 +11,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
11
11
|
const require_device = require("../utils/device.cjs");
|
|
12
12
|
const require_dom = require("../utils/dom.cjs");
|
|
13
13
|
const require_keyboard_manager = require("./keyboard-manager.cjs");
|
|
14
|
-
const require_position_manager = require("./position-manager.cjs");
|
|
15
14
|
const require_presets = require("../themes/presets.cjs");
|
|
16
15
|
const require_index = require("../themes/index.cjs");
|
|
16
|
+
const require_position_manager = require("./position-manager.cjs");
|
|
17
17
|
//#region src/managers/keyboard-renderer.ts
|
|
18
|
+
const rendererStyles = /* @__PURE__ */ new WeakMap();
|
|
18
19
|
var KeyboardRenderer = class {
|
|
19
20
|
constructor(options = {}) {
|
|
20
21
|
/** DOM 元素 */
|
|
@@ -25,10 +26,14 @@ var KeyboardRenderer = class {
|
|
|
25
26
|
this.targetEl = null;
|
|
26
27
|
/** 清理函数 */
|
|
27
28
|
this.cleanupFns = [];
|
|
29
|
+
this.inputCleanupFns = /* @__PURE__ */ new Set();
|
|
30
|
+
this.styleDocument = null;
|
|
28
31
|
/** 是否已销毁 */
|
|
29
32
|
this.destroyed = false;
|
|
30
33
|
this.options = options;
|
|
31
|
-
|
|
34
|
+
const container = options.container ?? (typeof document !== "undefined" ? document.body : null);
|
|
35
|
+
if (!container) throw new Error("[KeyboardRenderer] A DOM container is required in non-browser environments");
|
|
36
|
+
this.container = container;
|
|
32
37
|
this.manager = new require_keyboard_manager.KeyboardManager(options);
|
|
33
38
|
this.positionManager = new require_position_manager.PositionManager(options.position);
|
|
34
39
|
if (typeof options.theme === "string") this.theme = require_index.getTheme(options.theme);
|
|
@@ -50,17 +55,12 @@ var KeyboardRenderer = class {
|
|
|
50
55
|
this.manager.on("confirm", ({ value }) => {
|
|
51
56
|
this.options.onConfirm?.(value);
|
|
52
57
|
});
|
|
53
|
-
this.manager.on("switch", () => {
|
|
54
|
-
if (this.manager.getState().visible) this.render();
|
|
55
|
-
});
|
|
56
|
-
this.manager.on("shift", () => {
|
|
57
|
-
if (this.manager.getState().visible) this.render();
|
|
58
|
-
});
|
|
59
58
|
}
|
|
60
59
|
/**
|
|
61
60
|
* 打开键盘
|
|
62
61
|
*/
|
|
63
62
|
open(target) {
|
|
63
|
+
if (this.destroyed) return;
|
|
64
64
|
this.targetEl = target ?? null;
|
|
65
65
|
this.manager.show(target);
|
|
66
66
|
}
|
|
@@ -68,19 +68,23 @@ var KeyboardRenderer = class {
|
|
|
68
68
|
* 关闭键盘
|
|
69
69
|
*/
|
|
70
70
|
close() {
|
|
71
|
+
if (this.destroyed) return;
|
|
72
|
+
const wasVisible = this.manager.getState().visible;
|
|
71
73
|
this.manager.hide();
|
|
72
|
-
this.options.onClose?.();
|
|
74
|
+
if (wasVisible) this.options.onClose?.();
|
|
73
75
|
}
|
|
74
76
|
/**
|
|
75
77
|
* 切换键盘类型
|
|
76
78
|
*/
|
|
77
79
|
switchType(type) {
|
|
80
|
+
if (this.destroyed) return;
|
|
78
81
|
this.manager.switchType(type);
|
|
79
82
|
}
|
|
80
83
|
/**
|
|
81
84
|
* 设置主题
|
|
82
85
|
*/
|
|
83
86
|
setTheme(theme) {
|
|
87
|
+
if (this.destroyed) return;
|
|
84
88
|
if (typeof theme === "string") this.theme = require_index.getTheme(theme);
|
|
85
89
|
else this.theme = theme;
|
|
86
90
|
if (this.manager.getState().visible) this.render();
|
|
@@ -95,12 +99,14 @@ var KeyboardRenderer = class {
|
|
|
95
99
|
* 设置值
|
|
96
100
|
*/
|
|
97
101
|
setValue(value) {
|
|
102
|
+
if (this.destroyed) return;
|
|
98
103
|
this.manager.setValue(value);
|
|
99
104
|
}
|
|
100
105
|
/**
|
|
101
106
|
* 渲染键盘 DOM
|
|
102
107
|
*/
|
|
103
108
|
render() {
|
|
109
|
+
if (this.destroyed) return;
|
|
104
110
|
const layout = this.manager.getLayout();
|
|
105
111
|
if (!layout) return;
|
|
106
112
|
this.unmount();
|
|
@@ -110,14 +116,18 @@ var KeyboardRenderer = class {
|
|
|
110
116
|
if (isMobileDevice) {
|
|
111
117
|
this.maskEl = document.createElement("div");
|
|
112
118
|
this.maskEl.className = "lk-mask";
|
|
113
|
-
this.maskEl.addEventListener("click", () =>
|
|
119
|
+
this.maskEl.addEventListener("click", () => {
|
|
120
|
+
if (this.positionManager.isMaskClosable()) this.close();
|
|
121
|
+
});
|
|
114
122
|
this.rootEl.appendChild(this.maskEl);
|
|
115
123
|
}
|
|
116
124
|
this.keyboardEl = document.createElement("div");
|
|
117
125
|
this.keyboardEl.className = `lk-popup lk-popup--${isMobileDevice ? "mobile" : "desktop"}`;
|
|
118
126
|
const cssVars = require_index.themeToCSS(this.theme);
|
|
127
|
+
const keyboardElement = this.keyboardEl;
|
|
128
|
+
if (!keyboardElement) return;
|
|
119
129
|
Object.entries(cssVars).forEach(([key, value]) => {
|
|
120
|
-
if (value)
|
|
130
|
+
if (value) keyboardElement.style.setProperty(key, value);
|
|
121
131
|
});
|
|
122
132
|
const contentEl = this.renderKeyboard(layout);
|
|
123
133
|
this.keyboardEl.appendChild(contentEl);
|
|
@@ -146,7 +156,7 @@ var KeyboardRenderer = class {
|
|
|
146
156
|
});
|
|
147
157
|
if (this.options.closeOnClickOutside !== false && this.keyboardEl) {
|
|
148
158
|
const cleanup = require_dom.onClickOutside(this.keyboardEl, (event) => {
|
|
149
|
-
if (this.targetEl && this.targetEl.contains(event.target)) return;
|
|
159
|
+
if (this.targetEl && event.target instanceof Node && this.targetEl.contains(event.target)) return;
|
|
150
160
|
this.close();
|
|
151
161
|
});
|
|
152
162
|
this.cleanupFns.push(cleanup);
|
|
@@ -178,8 +188,11 @@ var KeyboardRenderer = class {
|
|
|
178
188
|
btnEl.type = "button";
|
|
179
189
|
btnEl.className = this.getKeyClass(key, state.isUpperCase);
|
|
180
190
|
if (key.disabled) btnEl.disabled = true;
|
|
181
|
-
if (key.icon)
|
|
182
|
-
|
|
191
|
+
if (key.icon) {
|
|
192
|
+
const safeIcon = require_dom.sanitizeSvgIcon(key.icon);
|
|
193
|
+
if (safeIcon) btnEl.innerHTML = safeIcon;
|
|
194
|
+
else btnEl.textContent = key.icon;
|
|
195
|
+
} else btnEl.textContent = typeof key.label === "string" ? key.label : state.isUpperCase ? key.label.upper : key.label.lower;
|
|
183
196
|
const handler = (e) => {
|
|
184
197
|
e.preventDefault();
|
|
185
198
|
e.stopPropagation();
|
|
@@ -219,49 +232,67 @@ var KeyboardRenderer = class {
|
|
|
219
232
|
* 注入样式(单例)
|
|
220
233
|
*/
|
|
221
234
|
injectStyles() {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
const styleDocument = this.container.ownerDocument;
|
|
236
|
+
if (this.styleDocument === styleDocument) return;
|
|
237
|
+
const existing = rendererStyles.get(styleDocument);
|
|
238
|
+
if (existing) {
|
|
239
|
+
existing.users += 1;
|
|
240
|
+
this.styleDocument = styleDocument;
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const style = styleDocument.createElement("style");
|
|
225
244
|
style.textContent = `
|
|
226
|
-
.lk-renderer-root { font-family: -
|
|
227
|
-
.lk-mask { position: fixed; inset: 0;
|
|
245
|
+
.lk-renderer-root { font-family: var(--ld-font-family); }
|
|
246
|
+
.lk-mask { position: fixed; inset: 0; z-index: 9999; background: var(--keyboard-mask-bg); opacity: 0; transition: opacity var(--ld-duration-fast); }
|
|
228
247
|
.lk-mask--visible { opacity: 1; }
|
|
229
|
-
.lk-popup { background: var(--keyboard-bg
|
|
248
|
+
.lk-popup { overflow: hidden; background: var(--keyboard-bg); box-shadow: var(--ld-shadow-md, none); transform: translateY(var(--ld-px-20)); opacity: 0; transition: transform var(--ld-duration-fast), opacity var(--ld-duration-fast); }
|
|
230
249
|
.lk-popup--visible { transform: translateY(0); opacity: 1; }
|
|
231
|
-
.lk-popup--desktop { border-radius:
|
|
232
|
-
.lk-popup--mobile { border-top-left-radius:
|
|
233
|
-
.lk-keyboard { display: flex; flex-direction: column; gap: var(--keyboard-key-gap
|
|
234
|
-
.lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap
|
|
250
|
+
.lk-popup--desktop { min-width: var(--ld-px-280); border-radius: var(--ld-radius-lg); box-shadow: var(--ld-shadow-lg, none); }
|
|
251
|
+
.lk-popup--mobile { border-top-left-radius: var(--ld-radius-lg); border-top-right-radius: var(--ld-radius-lg); padding-bottom: env(safe-area-inset-bottom, var(--ld-px-0)); }
|
|
252
|
+
.lk-keyboard { display: flex; flex-direction: column; gap: var(--keyboard-key-gap); padding: var(--keyboard-padding); user-select: none; -webkit-user-select: none; font-family: var(--ld-font-family); }
|
|
253
|
+
.lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap); padding: 0 var(--ld-px-2); }
|
|
235
254
|
.lk-row.numpad-row { justify-content: stretch; }
|
|
236
255
|
.lk-row.numpad-row .lk-key-wrapper { flex: 1; }
|
|
237
256
|
.lk-key-wrapper { position: relative; display: inline-flex; }
|
|
238
|
-
.lk-key { display: inline-flex; align-items: center; justify-content: center; height: var(--keyboard-key-height
|
|
239
|
-
.lk-key:active { background: var(--keyboard-key-active-bg
|
|
257
|
+
.lk-key { display: inline-flex; align-items: center; justify-content: center; width: 100%; min-width: var(--ld-px-32); height: var(--keyboard-key-height); padding: 0 var(--ld-spacing-xs); border: 0; border-radius: var(--keyboard-key-radius); background: var(--keyboard-key-bg); color: var(--keyboard-key-color); font-size: var(--keyboard-font-size); font-weight: 500; cursor: pointer; touch-action: manipulation; box-shadow: var(--keyboard-key-shadow); transition: background-color var(--ld-duration-fast), transform var(--ld-duration-fast); }
|
|
258
|
+
.lk-key:active { background: var(--keyboard-key-active-bg); transform: scale(0.95); }
|
|
240
259
|
.lk-key--disabled { opacity: 0.4; cursor: not-allowed; }
|
|
241
|
-
.lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg
|
|
242
|
-
.lk-key--backspace, .lk-key--shift, .lk-key--tab, .lk-key--clear, .lk-key--close { background: var(--keyboard-fn-key-bg
|
|
243
|
-
.lk-key--backspace:active, .lk-key--shift:active, .lk-key--tab:active, .lk-key--clear:active, .lk-key--close:active { background: var(--keyboard-fn-key-active-bg
|
|
244
|
-
.lk-key--active { background: var(--keyboard-key-active-bg
|
|
245
|
-
.lk-key--done, .lk-key--enter { background: var(--keyboard-primary-bg
|
|
246
|
-
.lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg
|
|
247
|
-
.lk-key--space { background: var(--keyboard-key-bg
|
|
260
|
+
.lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg); }
|
|
261
|
+
.lk-key--backspace, .lk-key--shift, .lk-key--tab, .lk-key--clear, .lk-key--close { background: var(--keyboard-fn-key-bg); color: var(--keyboard-fn-key-color); font-size: var(--keyboard-fn-font-size); }
|
|
262
|
+
.lk-key--backspace:active, .lk-key--shift:active, .lk-key--tab:active, .lk-key--clear:active, .lk-key--close:active { background: var(--keyboard-fn-key-active-bg); }
|
|
263
|
+
.lk-key--active { background: var(--keyboard-key-active-bg) !important; color: var(--ld-text-inverse) !important; }
|
|
264
|
+
.lk-key--done, .lk-key--enter { background: var(--keyboard-primary-bg); color: var(--keyboard-primary-color); font-size: var(--keyboard-fn-font-size); }
|
|
265
|
+
.lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg); }
|
|
266
|
+
.lk-key--space { background: var(--keyboard-key-bg); }
|
|
248
267
|
.lk-key svg { pointer-events: none; }
|
|
249
268
|
`;
|
|
250
|
-
|
|
269
|
+
style.id = "lk-renderer-styles";
|
|
270
|
+
styleDocument.head?.appendChild(style);
|
|
271
|
+
rendererStyles.set(styleDocument, {
|
|
272
|
+
element: style,
|
|
273
|
+
users: 1
|
|
274
|
+
});
|
|
275
|
+
this.styleDocument = styleDocument;
|
|
251
276
|
}
|
|
252
277
|
/**
|
|
253
278
|
* 绑定到输入框(聚焦时自动打开键盘)
|
|
254
279
|
*/
|
|
255
280
|
bindInput(input) {
|
|
281
|
+
if (this.destroyed) return () => void 0;
|
|
256
282
|
const handleFocus = () => this.open(input);
|
|
257
283
|
input.addEventListener("focus", handleFocus);
|
|
258
284
|
input.setAttribute("readonly", "readonly");
|
|
259
285
|
input.style.caretColor = "transparent";
|
|
260
|
-
|
|
286
|
+
let cleaned = false;
|
|
287
|
+
const cleanup = () => {
|
|
288
|
+
if (cleaned) return;
|
|
289
|
+
cleaned = true;
|
|
261
290
|
input.removeEventListener("focus", handleFocus);
|
|
262
291
|
input.removeAttribute("readonly");
|
|
263
292
|
input.style.caretColor = "";
|
|
264
293
|
};
|
|
294
|
+
this.inputCleanupFns.add(cleanup);
|
|
295
|
+
return cleanup;
|
|
265
296
|
}
|
|
266
297
|
/**
|
|
267
298
|
* 销毁渲染器
|
|
@@ -270,9 +301,20 @@ var KeyboardRenderer = class {
|
|
|
270
301
|
if (this.destroyed) return;
|
|
271
302
|
this.destroyed = true;
|
|
272
303
|
this.unmount();
|
|
304
|
+
this.inputCleanupFns.forEach((cleanup) => cleanup());
|
|
305
|
+
this.inputCleanupFns.clear();
|
|
273
306
|
this.manager.destroy();
|
|
274
|
-
|
|
275
|
-
|
|
307
|
+
if (this.styleDocument) {
|
|
308
|
+
const entry = rendererStyles.get(this.styleDocument);
|
|
309
|
+
if (entry) {
|
|
310
|
+
entry.users -= 1;
|
|
311
|
+
if (entry.users <= 0) {
|
|
312
|
+
entry.element.remove();
|
|
313
|
+
rendererStyles.delete(this.styleDocument);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
this.styleDocument = null;
|
|
317
|
+
}
|
|
276
318
|
}
|
|
277
319
|
};
|
|
278
320
|
function createKeyboardRenderer(options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-renderer.cjs","names":[],"sources":["../../src/managers/keyboard-renderer.ts"],"sourcesContent":["/**\n * 键盘 DOM 渲染器\n * 框架无关的键盘渲染器,用于原生 JS 项目\n * @module managers/keyboard-renderer\n */\n\nimport type {\n KeyboardConfig,\n KeyboardOptions,\n KeyboardType,\n KeyDefinition,\n PopupPositionConfig,\n} from '../types'\nimport type { ThemeConfig } from '../themes/types'\nimport { KeyboardManager } from './keyboard-manager'\nimport { PositionManager } from './position-manager'\nimport { getTheme, themeToCSS, defaultTheme } from '../themes'\nimport { isMobile } from '../utils/device'\nimport { onClickOutside } from '../utils/dom'\n\n/**\n * 渲染器配置\n */\nexport interface KeyboardRendererOptions extends KeyboardOptions {\n /** 挂载容器(默认 document.body) */\n container?: HTMLElement\n /** 主题名称或主题配置 */\n theme?: string | ThemeConfig\n /** 弹出层配置 */\n position?: PopupPositionConfig\n /** 点击外部关闭 */\n closeOnClickOutside?: boolean\n /** 确认回调 */\n onConfirm?: (value: string) => void\n /** 输入变化回调 */\n onInput?: (value: string) => void\n /** 关闭回调 */\n onClose?: () => void\n}\n\n/**\n * 键盘 DOM 渲染器\n * 提供完整的键盘 DOM 创建、事件绑定、位置管理\n */\nexport class KeyboardRenderer {\n /** 键盘管理器 */\n readonly manager: KeyboardManager\n\n /** 位置管理器 */\n private positionManager: PositionManager\n\n /** 配置 */\n private options: KeyboardRendererOptions\n\n /** DOM 元素 */\n private rootEl: HTMLElement | null = null\n private maskEl: HTMLElement | null = null\n private keyboardEl: HTMLElement | null = null\n\n /** 挂载容器 */\n private container: HTMLElement\n\n /** 主题配置 */\n private theme: ThemeConfig\n\n /** 当前目标元素 */\n private targetEl: HTMLInputElement | HTMLTextAreaElement | null = null\n\n /** 清理函数 */\n private cleanupFns: Array<() => void> = []\n\n /** 是否已销毁 */\n private destroyed = false\n\n constructor(options: KeyboardRendererOptions = {}) {\n this.options = options\n this.container = options.container || document.body\n this.manager = new KeyboardManager(options)\n this.positionManager = new PositionManager(options.position)\n\n // 解析主题\n if (typeof options.theme === 'string') {\n this.theme = getTheme(options.theme)\n } else if (options.theme) {\n this.theme = options.theme\n } else {\n this.theme = defaultTheme\n }\n\n // 绑定事件\n this.setupEvents()\n }\n\n /**\n * 设置事件监听\n */\n private setupEvents(): void {\n this.manager.on('stateChange', (state) => {\n if (state.visible) {\n this.render()\n } else {\n this.unmount()\n }\n })\n\n this.manager.on('input', ({ value }) => {\n this.options.onInput?.(value)\n })\n\n this.manager.on('confirm', ({ value }) => {\n this.options.onConfirm?.(value)\n })\n\n this.manager.on('switch', () => {\n if (this.manager.getState().visible) {\n this.render()\n }\n })\n\n this.manager.on('shift', () => {\n if (this.manager.getState().visible) {\n this.render()\n }\n })\n }\n\n /**\n * 打开键盘\n */\n open(target?: HTMLInputElement | HTMLTextAreaElement): void {\n this.targetEl = target ?? null\n this.manager.show(target)\n }\n\n /**\n * 关闭键盘\n */\n close(): void {\n this.manager.hide()\n this.options.onClose?.()\n }\n\n /**\n * 切换键盘类型\n */\n switchType(type: KeyboardType | string): void {\n this.manager.switchType(type)\n }\n\n /**\n * 设置主题\n */\n setTheme(theme: string | ThemeConfig): void {\n if (typeof theme === 'string') {\n this.theme = getTheme(theme)\n } else {\n this.theme = theme\n }\n if (this.manager.getState().visible) {\n this.render()\n }\n }\n\n /**\n * 获取当前值\n */\n getValue(): string {\n return this.manager.getState().value\n }\n\n /**\n * 设置值\n */\n setValue(value: string): void {\n this.manager.setValue(value)\n }\n\n /**\n * 渲染键盘 DOM\n */\n private render(): void {\n const layout = this.manager.getLayout()\n if (!layout) return\n\n // 清理旧的 DOM\n this.unmount()\n\n // 创建根元素\n this.rootEl = document.createElement('div')\n this.rootEl.className = 'lk-renderer-root'\n\n const isMobileDevice = isMobile()\n\n // 移动端创建遮罩\n if (isMobileDevice) {\n this.maskEl = document.createElement('div')\n this.maskEl.className = 'lk-mask'\n this.maskEl.addEventListener('click', () => this.close())\n this.rootEl.appendChild(this.maskEl)\n }\n\n // 创建键盘容器\n this.keyboardEl = document.createElement('div')\n this.keyboardEl.className = `lk-popup lk-popup--${isMobileDevice ? 'mobile' : 'desktop'}`\n\n // 应用主题 CSS 变量\n const cssVars = themeToCSS(this.theme)\n Object.entries(cssVars).forEach(([key, value]) => {\n if (value) {\n this.keyboardEl!.style.setProperty(key, value)\n }\n })\n\n // 渲染键盘内容\n const contentEl = this.renderKeyboard(layout)\n this.keyboardEl.appendChild(contentEl)\n\n this.rootEl.appendChild(this.keyboardEl)\n this.container.appendChild(this.rootEl)\n\n // 计算位置\n if (!isMobileDevice && this.targetEl) {\n const rect = this.keyboardEl.getBoundingClientRect()\n const result = this.positionManager.calculate(this.targetEl, {\n width: rect.width,\n height: rect.height,\n })\n this.keyboardEl.style.position = 'fixed'\n this.keyboardEl.style.left = `${result.position.x}px`\n this.keyboardEl.style.top = `${result.position.y}px`\n this.keyboardEl.style.zIndex = '10000'\n } else if (isMobileDevice) {\n this.keyboardEl.style.position = 'fixed'\n this.keyboardEl.style.left = '0'\n this.keyboardEl.style.right = '0'\n this.keyboardEl.style.bottom = '0'\n this.keyboardEl.style.zIndex = '10000'\n }\n\n // 添加动画类\n requestAnimationFrame(() => {\n this.keyboardEl?.classList.add('lk-popup--visible')\n this.maskEl?.classList.add('lk-mask--visible')\n })\n\n // 点击外部关闭\n if (this.options.closeOnClickOutside !== false && this.keyboardEl) {\n const cleanup = onClickOutside(this.keyboardEl, (event) => {\n if (this.targetEl && this.targetEl.contains(event.target as Node)) return\n this.close()\n })\n this.cleanupFns.push(cleanup)\n }\n\n // 注入样式\n this.injectStyles()\n }\n\n /**\n * 渲染键盘内容\n */\n private renderKeyboard(layout: KeyboardConfig): HTMLElement {\n const el = document.createElement('div')\n el.className = 'lk-keyboard'\n\n const state = this.manager.getState()\n\n layout.rows.forEach((row) => {\n const rowEl = document.createElement('div')\n rowEl.className = `lk-row ${row.className || ''}`\n\n const isNumpad = row.className === 'numpad-row'\n\n row.keys.forEach((key) => {\n const wrapperEl = document.createElement('div')\n wrapperEl.className = 'lk-key-wrapper'\n\n if (isNumpad) {\n wrapperEl.style.flex = String(key.width ?? 1)\n } else if (key.width) {\n const baseW = 36\n const gap = this.theme.sizes?.keyGap ?? 6\n const w = baseW * key.width + (key.width - 1) * gap\n wrapperEl.style.width = `${w}px`\n }\n\n const btnEl = document.createElement('button')\n btnEl.type = 'button'\n btnEl.className = this.getKeyClass(key, state.isUpperCase)\n\n if (key.disabled) {\n btnEl.disabled = true\n }\n\n // 内容\n if (key.icon) {\n btnEl.innerHTML = key.icon\n } else {\n const label = typeof key.label === 'string'\n ? key.label\n : state.isUpperCase ? key.label.upper : key.label.lower\n btnEl.textContent = label\n }\n\n // 事件\n const handler = (e: Event) => {\n e.preventDefault()\n e.stopPropagation()\n if (!key.disabled) {\n this.manager.handleKey(key)\n }\n }\n\n btnEl.addEventListener('mousedown', handler)\n btnEl.addEventListener('touchstart', handler, { passive: false })\n\n wrapperEl.appendChild(btnEl)\n rowEl.appendChild(wrapperEl)\n })\n\n el.appendChild(rowEl)\n })\n\n return el\n }\n\n /**\n * 获取按键 CSS 类名\n */\n private getKeyClass(key: KeyDefinition, isUpperCase: boolean): string {\n const classes = ['lk-key', `lk-key--${key.type}`]\n if (key.className) classes.push(key.className)\n if (key.disabled) classes.push('lk-key--disabled')\n if (key.type === 'shift' && isUpperCase) classes.push('lk-key--active')\n return classes.join(' ')\n }\n\n /**\n * 卸载 DOM\n */\n private unmount(): void {\n this.cleanupFns.forEach(fn => fn())\n this.cleanupFns = []\n\n if (this.rootEl && this.rootEl.parentNode) {\n this.rootEl.parentNode.removeChild(this.rootEl)\n }\n this.rootEl = null\n this.maskEl = null\n this.keyboardEl = null\n }\n\n /**\n * 注入样式(单例)\n */\n private injectStyles(): void {\n if (document.getElementById('lk-renderer-styles')) return\n\n const style = document.createElement('style')\n style.id = 'lk-renderer-styles'\n style.textContent = `\n .lk-renderer-root { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }\n .lk-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 9999; opacity: 0; transition: opacity 0.3s ease; }\n .lk-mask--visible { opacity: 1; }\n .lk-popup { background: var(--keyboard-bg, #d1d5db); box-shadow: 0 -2px 16px rgba(0,0,0,0.15); overflow: hidden; transform: translateY(20px); opacity: 0; transition: transform 0.3s cubic-bezier(0.4,0,0.2,1), opacity 0.2s ease; }\n .lk-popup--visible { transform: translateY(0); opacity: 1; }\n .lk-popup--desktop { border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.18), 0 2px 8px rgba(0,0,0,0.1); min-width: 320px; }\n .lk-popup--mobile { border-top-left-radius: 16px; border-top-right-radius: 16px; padding-bottom: env(safe-area-inset-bottom, 0); }\n .lk-keyboard { display: flex; flex-direction: column; gap: var(--keyboard-key-gap, 6px); padding: var(--keyboard-padding, 6px); user-select: none; -webkit-user-select: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif; }\n .lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap, 6px); padding: 0 2px; }\n .lk-row.numpad-row { justify-content: stretch; }\n .lk-row.numpad-row .lk-key-wrapper { flex: 1; }\n .lk-key-wrapper { position: relative; display: inline-flex; }\n .lk-key { display: inline-flex; align-items: center; justify-content: center; height: var(--keyboard-key-height, 42px); min-width: 32px; width: 100%; padding: 0 6px; border: none; border-radius: var(--keyboard-key-radius, 6px); background: var(--keyboard-key-bg, #fff); color: var(--keyboard-key-color, #333); font-size: var(--keyboard-font-size, 16px); font-weight: 500; cursor: pointer; touch-action: manipulation; box-shadow: var(--keyboard-key-shadow, 0 1px 3px rgba(0,0,0,0.1)); transition: background-color 0.1s, transform 0.1s; }\n .lk-key:active { background: var(--keyboard-key-active-bg, #e0e0e0); transform: scale(0.95); }\n .lk-key--disabled { opacity: 0.4; cursor: not-allowed; }\n .lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg, #fff); }\n .lk-key--backspace, .lk-key--shift, .lk-key--tab, .lk-key--clear, .lk-key--close { background: var(--keyboard-fn-key-bg, #d4d6dc); color: var(--keyboard-fn-key-color, #333); font-size: var(--keyboard-fn-font-size, 14px); }\n .lk-key--backspace:active, .lk-key--shift:active, .lk-key--tab:active, .lk-key--clear:active, .lk-key--close:active { background: var(--keyboard-fn-key-active-bg, #b8bac0); }\n .lk-key--active { background: var(--keyboard-key-active-bg, #007aff) !important; color: #fff !important; }\n .lk-key--done, .lk-key--enter { background: var(--keyboard-primary-bg, #007aff); color: var(--keyboard-primary-color, #fff); font-size: var(--keyboard-fn-font-size, 14px); }\n .lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg, #0062cc); }\n .lk-key--space { background: var(--keyboard-key-bg, #fff); }\n .lk-key svg { pointer-events: none; }\n `\n document.head.appendChild(style)\n }\n\n /**\n * 绑定到输入框(聚焦时自动打开键盘)\n */\n bindInput(input: HTMLInputElement | HTMLTextAreaElement): () => void {\n const handleFocus = () => this.open(input)\n input.addEventListener('focus', handleFocus)\n\n // 阻止移动端原生键盘\n input.setAttribute('readonly', 'readonly')\n input.style.caretColor = 'transparent'\n\n return () => {\n input.removeEventListener('focus', handleFocus)\n input.removeAttribute('readonly')\n input.style.caretColor = ''\n }\n }\n\n /**\n * 销毁渲染器\n */\n destroy(): void {\n if (this.destroyed) return\n this.destroyed = true\n this.unmount()\n this.manager.destroy()\n\n // 移除样式\n const styleEl = document.getElementById('lk-renderer-styles')\n if (styleEl) styleEl.remove()\n }\n}\n\n/**\n * 创建键盘渲染器\n */\nexport function createKeyboardRenderer(options?: KeyboardRendererOptions): KeyboardRenderer {\n return new KeyboardRenderer(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"keyboard-renderer.cjs","names":[],"sources":["../../src/managers/keyboard-renderer.ts"],"sourcesContent":["/**\r\n * 键盘 DOM 渲染器\r\n * 框架无关的键盘渲染器,用于原生 JS 项目\r\n * @module managers/keyboard-renderer\r\n */\r\n\r\nimport type { ThemeConfig } from '../themes/types'\r\nimport type {\r\n KeyboardConfig,\r\n KeyboardOptions,\r\n KeyboardType,\r\n KeyDefinition,\r\n PopupPositionConfig,\r\n} from '../types'\r\nimport { defaultTheme, getTheme, themeToCSS } from '../themes'\r\nimport { isMobile } from '../utils/device'\r\nimport { onClickOutside, sanitizeSvgIcon } from '../utils/dom'\r\nimport { KeyboardManager } from './keyboard-manager'\r\nimport { PositionManager } from './position-manager'\r\n\r\n/**\r\n * 渲染器配置\r\n */\r\nexport interface KeyboardRendererOptions extends KeyboardOptions {\r\n /** 挂载容器(默认 document.body) */\r\n container?: HTMLElement\r\n /** 主题名称或主题配置 */\r\n theme?: string | ThemeConfig\r\n /** 弹出层配置 */\r\n position?: PopupPositionConfig\r\n /** 点击外部关闭 */\r\n closeOnClickOutside?: boolean\r\n /** 确认回调 */\r\n onConfirm?: (value: string) => void\r\n /** 输入变化回调 */\r\n onInput?: (value: string) => void\r\n /** 关闭回调 */\r\n onClose?: () => void\r\n}\r\n\r\ninterface RendererStyleEntry {\r\n element: HTMLStyleElement\r\n users: number\r\n}\r\n\r\nconst rendererStyles = new WeakMap<Document, RendererStyleEntry>()\r\n\r\n/**\r\n * 键盘 DOM 渲染器\r\n * 提供完整的键盘 DOM 创建、事件绑定、位置管理\r\n */\r\nexport class KeyboardRenderer {\r\n /** 键盘管理器 */\r\n readonly manager: KeyboardManager\r\n\r\n /** 位置管理器 */\r\n private positionManager: PositionManager\r\n\r\n /** 配置 */\r\n private options: KeyboardRendererOptions\r\n\r\n /** DOM 元素 */\r\n private rootEl: HTMLElement | null = null\r\n private maskEl: HTMLElement | null = null\r\n private keyboardEl: HTMLElement | null = null\r\n\r\n /** 挂载容器 */\r\n private container: HTMLElement\r\n\r\n /** 主题配置 */\r\n private theme: ThemeConfig\r\n\r\n /** 当前目标元素 */\r\n private targetEl: HTMLInputElement | HTMLTextAreaElement | null = null\r\n\r\n /** 清理函数 */\r\n private cleanupFns: Array<() => void> = []\r\n private inputCleanupFns = new Set<() => void>()\r\n private styleDocument: Document | null = null\r\n\r\n /** 是否已销毁 */\r\n private destroyed = false\r\n\r\n constructor(options: KeyboardRendererOptions = {}) {\r\n this.options = options\r\n const container =\r\n options.container ??\r\n (typeof document !== 'undefined' ? document.body : null)\r\n if (!container) {\r\n throw new Error(\r\n '[KeyboardRenderer] A DOM container is required in non-browser environments'\r\n )\r\n }\r\n this.container = container\r\n this.manager = new KeyboardManager(options)\r\n this.positionManager = new PositionManager(options.position)\r\n\r\n // 解析主题\r\n if (typeof options.theme === 'string') {\r\n this.theme = getTheme(options.theme)\r\n } else if (options.theme) {\r\n this.theme = options.theme\r\n } else {\r\n this.theme = defaultTheme\r\n }\r\n\r\n // 绑定事件\r\n this.setupEvents()\r\n }\r\n\r\n /**\r\n * 设置事件监听\r\n */\r\n private setupEvents(): void {\r\n this.manager.on('stateChange', state => {\r\n if (state.visible) {\r\n this.render()\r\n } else {\r\n this.unmount()\r\n }\r\n })\r\n\r\n this.manager.on('input', ({ value }) => {\r\n this.options.onInput?.(value)\r\n })\r\n\r\n this.manager.on('confirm', ({ value }) => {\r\n this.options.onConfirm?.(value)\r\n })\r\n }\r\n\r\n /**\r\n * 打开键盘\r\n */\r\n open(target?: HTMLInputElement | HTMLTextAreaElement): void {\r\n if (this.destroyed) return\r\n this.targetEl = target ?? null\r\n this.manager.show(target)\r\n }\r\n\r\n /**\r\n * 关闭键盘\r\n */\r\n close(): void {\r\n if (this.destroyed) return\r\n const wasVisible = this.manager.getState().visible\r\n this.manager.hide()\r\n if (wasVisible) this.options.onClose?.()\r\n }\r\n\r\n /**\r\n * 切换键盘类型\r\n */\r\n switchType(type: KeyboardType | string): void {\r\n if (this.destroyed) return\r\n this.manager.switchType(type)\r\n }\r\n\r\n /**\r\n * 设置主题\r\n */\r\n setTheme(theme: string | ThemeConfig): void {\r\n if (this.destroyed) return\r\n if (typeof theme === 'string') {\r\n this.theme = getTheme(theme)\r\n } else {\r\n this.theme = theme\r\n }\r\n if (this.manager.getState().visible) {\r\n this.render()\r\n }\r\n }\r\n\r\n /**\r\n * 获取当前值\r\n */\r\n getValue(): string {\r\n return this.manager.getState().value\r\n }\r\n\r\n /**\r\n * 设置值\r\n */\r\n setValue(value: string): void {\r\n if (this.destroyed) return\r\n this.manager.setValue(value)\r\n }\r\n\r\n /**\r\n * 渲染键盘 DOM\r\n */\r\n private render(): void {\r\n if (this.destroyed) return\r\n const layout = this.manager.getLayout()\r\n if (!layout) return\r\n\r\n // 清理旧的 DOM\r\n this.unmount()\r\n\r\n // 创建根元素\r\n this.rootEl = document.createElement('div')\r\n this.rootEl.className = 'lk-renderer-root'\r\n\r\n const isMobileDevice = isMobile()\r\n\r\n // 移动端创建遮罩\r\n if (isMobileDevice) {\r\n this.maskEl = document.createElement('div')\r\n this.maskEl.className = 'lk-mask'\r\n this.maskEl.addEventListener('click', () => {\r\n if (this.positionManager.isMaskClosable()) this.close()\r\n })\r\n this.rootEl.appendChild(this.maskEl)\r\n }\r\n\r\n // 创建键盘容器\r\n this.keyboardEl = document.createElement('div')\r\n this.keyboardEl.className = `lk-popup lk-popup--${isMobileDevice ? 'mobile' : 'desktop'}`\r\n\r\n // 应用主题 CSS 变量\r\n const cssVars = themeToCSS(this.theme)\r\n const keyboardElement = this.keyboardEl\r\n if (!keyboardElement) return\r\n Object.entries(cssVars).forEach(([key, value]) => {\r\n if (value) {\r\n keyboardElement.style.setProperty(key, value)\r\n }\r\n })\r\n\r\n // 渲染键盘内容\r\n const contentEl = this.renderKeyboard(layout)\r\n this.keyboardEl.appendChild(contentEl)\r\n\r\n this.rootEl.appendChild(this.keyboardEl)\r\n this.container.appendChild(this.rootEl)\r\n\r\n // 计算位置\r\n if (!isMobileDevice && this.targetEl) {\r\n const rect = this.keyboardEl.getBoundingClientRect()\r\n const result = this.positionManager.calculate(this.targetEl, {\r\n width: rect.width,\r\n height: rect.height,\r\n })\r\n this.keyboardEl.style.position = 'fixed'\r\n this.keyboardEl.style.left = `${result.position.x}px`\r\n this.keyboardEl.style.top = `${result.position.y}px`\r\n this.keyboardEl.style.zIndex = '10000'\r\n } else if (isMobileDevice) {\r\n this.keyboardEl.style.position = 'fixed'\r\n this.keyboardEl.style.left = '0'\r\n this.keyboardEl.style.right = '0'\r\n this.keyboardEl.style.bottom = '0'\r\n this.keyboardEl.style.zIndex = '10000'\r\n }\r\n\r\n // 添加动画类\r\n requestAnimationFrame(() => {\r\n this.keyboardEl?.classList.add('lk-popup--visible')\r\n this.maskEl?.classList.add('lk-mask--visible')\r\n })\r\n\r\n // 点击外部关闭\r\n if (this.options.closeOnClickOutside !== false && this.keyboardEl) {\r\n const cleanup = onClickOutside(this.keyboardEl, event => {\r\n if (\r\n this.targetEl &&\r\n event.target instanceof Node &&\r\n this.targetEl.contains(event.target)\r\n )\r\n return\r\n this.close()\r\n })\r\n this.cleanupFns.push(cleanup)\r\n }\r\n\r\n // 注入样式\r\n this.injectStyles()\r\n }\r\n\r\n /**\r\n * 渲染键盘内容\r\n */\r\n private renderKeyboard(layout: KeyboardConfig): HTMLElement {\r\n const el = document.createElement('div')\r\n el.className = 'lk-keyboard'\r\n\r\n const state = this.manager.getState()\r\n\r\n layout.rows.forEach(row => {\r\n const rowEl = document.createElement('div')\r\n rowEl.className = `lk-row ${row.className || ''}`\r\n\r\n const isNumpad = row.className === 'numpad-row'\r\n\r\n row.keys.forEach(key => {\r\n const wrapperEl = document.createElement('div')\r\n wrapperEl.className = 'lk-key-wrapper'\r\n\r\n if (isNumpad) {\r\n wrapperEl.style.flex = String(key.width ?? 1)\r\n } else if (key.width) {\r\n const baseW = 36\r\n const gap = this.theme.sizes?.keyGap ?? 6\r\n const w = baseW * key.width + (key.width - 1) * gap\r\n wrapperEl.style.width = `${w}px`\r\n }\r\n\r\n const btnEl = document.createElement('button')\r\n btnEl.type = 'button'\r\n btnEl.className = this.getKeyClass(key, state.isUpperCase)\r\n\r\n if (key.disabled) {\r\n btnEl.disabled = true\r\n }\r\n\r\n // 内容\r\n if (key.icon) {\r\n const safeIcon = sanitizeSvgIcon(key.icon)\r\n if (safeIcon) btnEl.innerHTML = safeIcon\r\n else btnEl.textContent = key.icon\r\n } else {\r\n const label =\r\n typeof key.label === 'string'\r\n ? key.label\r\n : state.isUpperCase\r\n ? key.label.upper\r\n : key.label.lower\r\n btnEl.textContent = label\r\n }\r\n\r\n // 事件\r\n const handler = (e: Event) => {\r\n e.preventDefault()\r\n e.stopPropagation()\r\n if (!key.disabled) {\r\n this.manager.handleKey(key)\r\n }\r\n }\r\n\r\n btnEl.addEventListener('mousedown', handler)\r\n btnEl.addEventListener('touchstart', handler, { passive: false })\r\n\r\n wrapperEl.appendChild(btnEl)\r\n rowEl.appendChild(wrapperEl)\r\n })\r\n\r\n el.appendChild(rowEl)\r\n })\r\n\r\n return el\r\n }\r\n\r\n /**\r\n * 获取按键 CSS 类名\r\n */\r\n private getKeyClass(key: KeyDefinition, isUpperCase: boolean): string {\r\n const classes = ['lk-key', `lk-key--${key.type}`]\r\n if (key.className) classes.push(key.className)\r\n if (key.disabled) classes.push('lk-key--disabled')\r\n if (key.type === 'shift' && isUpperCase) classes.push('lk-key--active')\r\n return classes.join(' ')\r\n }\r\n\r\n /**\r\n * 卸载 DOM\r\n */\r\n private unmount(): void {\r\n this.cleanupFns.forEach(fn => fn())\r\n this.cleanupFns = []\r\n\r\n if (this.rootEl && this.rootEl.parentNode) {\r\n this.rootEl.parentNode.removeChild(this.rootEl)\r\n }\r\n this.rootEl = null\r\n this.maskEl = null\r\n this.keyboardEl = null\r\n }\r\n\r\n /**\r\n * 注入样式(单例)\r\n */\r\n private injectStyles(): void {\r\n const styleDocument = this.container.ownerDocument\r\n if (this.styleDocument === styleDocument) return\r\n const existing = rendererStyles.get(styleDocument)\r\n if (existing) {\r\n existing.users += 1\r\n this.styleDocument = styleDocument\r\n return\r\n }\r\n\r\n const style = styleDocument.createElement('style')\r\n style.textContent = `\r\n .lk-renderer-root { font-family: var(--ld-font-family); }\r\n .lk-mask { position: fixed; inset: 0; z-index: 9999; background: var(--keyboard-mask-bg); opacity: 0; transition: opacity var(--ld-duration-fast); }\r\n .lk-mask--visible { opacity: 1; }\r\n .lk-popup { overflow: hidden; background: var(--keyboard-bg); box-shadow: var(--ld-shadow-md, none); transform: translateY(var(--ld-px-20)); opacity: 0; transition: transform var(--ld-duration-fast), opacity var(--ld-duration-fast); }\r\n .lk-popup--visible { transform: translateY(0); opacity: 1; }\r\n .lk-popup--desktop { min-width: var(--ld-px-280); border-radius: var(--ld-radius-lg); box-shadow: var(--ld-shadow-lg, none); }\r\n .lk-popup--mobile { border-top-left-radius: var(--ld-radius-lg); border-top-right-radius: var(--ld-radius-lg); padding-bottom: env(safe-area-inset-bottom, var(--ld-px-0)); }\r\n .lk-keyboard { display: flex; flex-direction: column; gap: var(--keyboard-key-gap); padding: var(--keyboard-padding); user-select: none; -webkit-user-select: none; font-family: var(--ld-font-family); }\r\n .lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap); padding: 0 var(--ld-px-2); }\r\n .lk-row.numpad-row { justify-content: stretch; }\r\n .lk-row.numpad-row .lk-key-wrapper { flex: 1; }\r\n .lk-key-wrapper { position: relative; display: inline-flex; }\r\n .lk-key { display: inline-flex; align-items: center; justify-content: center; width: 100%; min-width: var(--ld-px-32); height: var(--keyboard-key-height); padding: 0 var(--ld-spacing-xs); border: 0; border-radius: var(--keyboard-key-radius); background: var(--keyboard-key-bg); color: var(--keyboard-key-color); font-size: var(--keyboard-font-size); font-weight: 500; cursor: pointer; touch-action: manipulation; box-shadow: var(--keyboard-key-shadow); transition: background-color var(--ld-duration-fast), transform var(--ld-duration-fast); }\r\n .lk-key:active { background: var(--keyboard-key-active-bg); transform: scale(0.95); }\r\n .lk-key--disabled { opacity: 0.4; cursor: not-allowed; }\r\n .lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg); }\r\n .lk-key--backspace, .lk-key--shift, .lk-key--tab, .lk-key--clear, .lk-key--close { background: var(--keyboard-fn-key-bg); color: var(--keyboard-fn-key-color); font-size: var(--keyboard-fn-font-size); }\r\n .lk-key--backspace:active, .lk-key--shift:active, .lk-key--tab:active, .lk-key--clear:active, .lk-key--close:active { background: var(--keyboard-fn-key-active-bg); }\r\n .lk-key--active { background: var(--keyboard-key-active-bg) !important; color: var(--ld-text-inverse) !important; }\r\n .lk-key--done, .lk-key--enter { background: var(--keyboard-primary-bg); color: var(--keyboard-primary-color); font-size: var(--keyboard-fn-font-size); }\r\n .lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg); }\r\n .lk-key--space { background: var(--keyboard-key-bg); }\r\n .lk-key svg { pointer-events: none; }\r\n `\r\n style.id = 'lk-renderer-styles'\r\n styleDocument.head?.appendChild(style)\r\n rendererStyles.set(styleDocument, { element: style, users: 1 })\r\n this.styleDocument = styleDocument\r\n }\r\n\r\n /**\r\n * 绑定到输入框(聚焦时自动打开键盘)\r\n */\r\n bindInput(input: HTMLInputElement | HTMLTextAreaElement): () => void {\r\n if (this.destroyed) return () => undefined\r\n const handleFocus = () => this.open(input)\r\n input.addEventListener('focus', handleFocus)\r\n\r\n // 阻止移动端原生键盘\r\n input.setAttribute('readonly', 'readonly')\r\n input.style.caretColor = 'transparent'\r\n\r\n let cleaned = false\r\n const cleanup = () => {\r\n if (cleaned) return\r\n cleaned = true\r\n input.removeEventListener('focus', handleFocus)\r\n input.removeAttribute('readonly')\r\n input.style.caretColor = ''\r\n }\r\n this.inputCleanupFns.add(cleanup)\r\n return cleanup\r\n }\r\n\r\n /**\r\n * 销毁渲染器\r\n */\r\n destroy(): void {\r\n if (this.destroyed) return\r\n this.destroyed = true\r\n this.unmount()\r\n this.inputCleanupFns.forEach(cleanup => cleanup())\r\n this.inputCleanupFns.clear()\r\n this.manager.destroy()\r\n\r\n // 移除样式\r\n if (this.styleDocument) {\r\n const entry = rendererStyles.get(this.styleDocument)\r\n if (entry) {\r\n entry.users -= 1\r\n if (entry.users <= 0) {\r\n entry.element.remove()\r\n rendererStyles.delete(this.styleDocument)\r\n }\r\n }\r\n this.styleDocument = null\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 创建键盘渲染器\r\n */\r\nexport function createKeyboardRenderer(\r\n options?: KeyboardRendererOptions\r\n): KeyboardRenderer {\r\n return new KeyboardRenderer(options)\r\n}\r\n"],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* 框架无关的键盘渲染器,用于原生 JS 项目
|
|
4
4
|
* @module managers/keyboard-renderer
|
|
5
5
|
*/
|
|
6
|
-
import type { KeyboardOptions, KeyboardType, PopupPositionConfig } from '../types';
|
|
7
6
|
import type { ThemeConfig } from '../themes/types';
|
|
7
|
+
import type { KeyboardOptions, KeyboardType, PopupPositionConfig } from '../types';
|
|
8
8
|
import { KeyboardManager } from './keyboard-manager';
|
|
9
9
|
/**
|
|
10
10
|
* 渲染器配置
|
|
@@ -48,6 +48,8 @@ export declare class KeyboardRenderer {
|
|
|
48
48
|
private targetEl;
|
|
49
49
|
/** 清理函数 */
|
|
50
50
|
private cleanupFns;
|
|
51
|
+
private inputCleanupFns;
|
|
52
|
+
private styleDocument;
|
|
51
53
|
/** 是否已销毁 */
|
|
52
54
|
private destroyed;
|
|
53
55
|
constructor(options?: KeyboardRendererOptions);
|
|
@@ -112,4 +114,3 @@ export declare class KeyboardRenderer {
|
|
|
112
114
|
* 创建键盘渲染器
|
|
113
115
|
*/
|
|
114
116
|
export declare function createKeyboardRenderer(options?: KeyboardRendererOptions): KeyboardRenderer;
|
|
115
|
-
//# sourceMappingURL=keyboard-renderer.d.ts.map
|