@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-renderer.js","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.js","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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"}
|
|
@@ -22,6 +22,8 @@ export declare class PositionManager {
|
|
|
22
22
|
* 计算弹出位置
|
|
23
23
|
* @param targetElement - 目标元素(如 input)
|
|
24
24
|
* @param popupSize - 弹出层尺寸
|
|
25
|
+
* @param popupSize.width - Popup width
|
|
26
|
+
* @param popupSize.height - Popup height
|
|
25
27
|
* @returns 位置计算结果
|
|
26
28
|
*/
|
|
27
29
|
calculate(targetElement: HTMLElement | null, popupSize: {
|
|
@@ -47,11 +49,11 @@ export declare class PositionManager {
|
|
|
47
49
|
/**
|
|
48
50
|
* 获取弹出层最终样式(动画结束)
|
|
49
51
|
*/
|
|
50
|
-
getFinalStyles(
|
|
52
|
+
getFinalStyles(_placement: PopupPlacement): Partial<CSSStyleDeclaration>;
|
|
51
53
|
/**
|
|
52
54
|
* 获取弹出层退出样式
|
|
53
55
|
*/
|
|
54
|
-
getExitStyles(
|
|
56
|
+
getExitStyles(_placement: PopupPlacement): Partial<CSSStyleDeclaration>;
|
|
55
57
|
/**
|
|
56
58
|
* 获取动画时长
|
|
57
59
|
*/
|
|
@@ -69,4 +71,3 @@ export declare class PositionManager {
|
|
|
69
71
|
* 创建位置管理器实例
|
|
70
72
|
*/
|
|
71
73
|
export declare function createPositionManager(config?: PopupPositionConfig): PositionManager;
|
|
72
|
-
//# sourceMappingURL=position-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
|
* ***********************************
|
|
@@ -23,29 +23,48 @@ const DEFAULT_CONFIG = {
|
|
|
23
23
|
maskClosable: true,
|
|
24
24
|
animationDuration: 300
|
|
25
25
|
};
|
|
26
|
+
function mergeConfig(config = {}) {
|
|
27
|
+
return {
|
|
28
|
+
...DEFAULT_CONFIG,
|
|
29
|
+
...config,
|
|
30
|
+
offset: {
|
|
31
|
+
...DEFAULT_CONFIG.offset,
|
|
32
|
+
...config.offset
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
26
36
|
var PositionManager = class {
|
|
27
37
|
constructor(config = {}) {
|
|
28
|
-
this.config =
|
|
29
|
-
...DEFAULT_CONFIG,
|
|
30
|
-
...config
|
|
31
|
-
};
|
|
38
|
+
this.config = mergeConfig(config);
|
|
32
39
|
}
|
|
33
40
|
/**
|
|
34
41
|
* 更新配置
|
|
35
42
|
*/
|
|
36
43
|
updateConfig(config) {
|
|
37
|
-
|
|
44
|
+
this.config = mergeConfig({
|
|
45
|
+
...this.config,
|
|
46
|
+
...config,
|
|
47
|
+
offset: {
|
|
48
|
+
...this.config.offset,
|
|
49
|
+
...config.offset
|
|
50
|
+
}
|
|
51
|
+
});
|
|
38
52
|
}
|
|
39
53
|
/**
|
|
40
54
|
* 获取配置
|
|
41
55
|
*/
|
|
42
56
|
getConfig() {
|
|
43
|
-
return {
|
|
57
|
+
return {
|
|
58
|
+
...this.config,
|
|
59
|
+
offset: { ...this.config.offset }
|
|
60
|
+
};
|
|
44
61
|
}
|
|
45
62
|
/**
|
|
46
63
|
* 计算弹出位置
|
|
47
64
|
* @param targetElement - 目标元素(如 input)
|
|
48
65
|
* @param popupSize - 弹出层尺寸
|
|
66
|
+
* @param popupSize.width - Popup width
|
|
67
|
+
* @param popupSize.height - Popup height
|
|
49
68
|
* @returns 位置计算结果
|
|
50
69
|
*/
|
|
51
70
|
calculate(targetElement, popupSize) {
|
|
@@ -63,15 +82,17 @@ var PositionManager = class {
|
|
|
63
82
|
*/
|
|
64
83
|
calculateBottomSheet(viewport, popupSize) {
|
|
65
84
|
const safeAreaBottom = viewport.safeArea?.bottom ?? 0;
|
|
85
|
+
const maxHeight = Math.max(0, viewport.height * .6 - safeAreaBottom);
|
|
86
|
+
const visibleHeight = Math.min(Math.max(0, popupSize.height), maxHeight);
|
|
66
87
|
return {
|
|
67
88
|
position: {
|
|
68
89
|
x: 0,
|
|
69
|
-
y: viewport.height -
|
|
90
|
+
y: Math.max(0, viewport.height - visibleHeight - safeAreaBottom)
|
|
70
91
|
},
|
|
71
92
|
placement: "bottom-sheet",
|
|
72
93
|
flipped: false,
|
|
73
|
-
maxHeight
|
|
74
|
-
maxWidth: viewport.width,
|
|
94
|
+
maxHeight,
|
|
95
|
+
maxWidth: Math.max(0, viewport.width),
|
|
75
96
|
transform: "translateY(0)"
|
|
76
97
|
};
|
|
77
98
|
}
|
|
@@ -81,8 +102,8 @@ var PositionManager = class {
|
|
|
81
102
|
calculateCentered(viewport, popupSize) {
|
|
82
103
|
return {
|
|
83
104
|
position: {
|
|
84
|
-
x: (viewport.width - popupSize.width) / 2,
|
|
85
|
-
y: (viewport.height - popupSize.height) / 2
|
|
105
|
+
x: Math.max(0, (viewport.width - popupSize.width) / 2),
|
|
106
|
+
y: Math.max(0, (viewport.height - popupSize.height) / 2)
|
|
86
107
|
},
|
|
87
108
|
placement: "bottom",
|
|
88
109
|
flipped: false
|
|
@@ -124,16 +145,16 @@ var PositionManager = class {
|
|
|
124
145
|
}
|
|
125
146
|
}
|
|
126
147
|
if (autoAdjust) {
|
|
127
|
-
|
|
128
|
-
|
|
148
|
+
const maxX = Math.max(viewportPadding, viewport.width - popupSize.width - viewportPadding);
|
|
149
|
+
position.x = Math.min(Math.max(position.x, viewportPadding), maxX);
|
|
129
150
|
}
|
|
130
151
|
const maxHeight = placement === "bottom" ? viewport.height - position.y - viewportPadding : targetRect.y - viewportPadding;
|
|
131
152
|
return {
|
|
132
153
|
position,
|
|
133
154
|
placement,
|
|
134
155
|
flipped,
|
|
135
|
-
maxHeight: Math.max(
|
|
136
|
-
maxWidth: viewport.width - viewportPadding * 2
|
|
156
|
+
maxHeight: Math.max(0, maxHeight),
|
|
157
|
+
maxWidth: Math.max(0, viewport.width - viewportPadding * 2)
|
|
137
158
|
};
|
|
138
159
|
}
|
|
139
160
|
/**
|
|
@@ -146,11 +167,11 @@ var PositionManager = class {
|
|
|
146
167
|
opacity: "1"
|
|
147
168
|
};
|
|
148
169
|
case "bottom": return {
|
|
149
|
-
transform: "translateY(-
|
|
170
|
+
transform: "translateY(calc(-1 * var(--ld-px-10)))",
|
|
150
171
|
opacity: "0"
|
|
151
172
|
};
|
|
152
173
|
case "top": return {
|
|
153
|
-
transform: "translateY(
|
|
174
|
+
transform: "translateY(var(--ld-px-10))",
|
|
154
175
|
opacity: "0"
|
|
155
176
|
};
|
|
156
177
|
default: return { opacity: "0" };
|
|
@@ -159,7 +180,7 @@ var PositionManager = class {
|
|
|
159
180
|
/**
|
|
160
181
|
* 获取弹出层最终样式(动画结束)
|
|
161
182
|
*/
|
|
162
|
-
getFinalStyles(
|
|
183
|
+
getFinalStyles(_placement) {
|
|
163
184
|
return {
|
|
164
185
|
transform: "translateY(0)",
|
|
165
186
|
opacity: "1"
|
|
@@ -168,8 +189,8 @@ var PositionManager = class {
|
|
|
168
189
|
/**
|
|
169
190
|
* 获取弹出层退出样式
|
|
170
191
|
*/
|
|
171
|
-
getExitStyles(
|
|
172
|
-
return this.getInitialStyles(
|
|
192
|
+
getExitStyles(_placement) {
|
|
193
|
+
return this.getInitialStyles(_placement);
|
|
173
194
|
}
|
|
174
195
|
/**
|
|
175
196
|
* 获取动画时长
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position-manager.js","names":[],"sources":["../../src/managers/position-manager.ts"],"sourcesContent":["/**\n * 弹出位置管理器\n * @module managers/position-manager\n */\n\nimport type {\n BoundingRect,\n PopupPlacement,\n PopupPositionConfig,\n PopupPositionResult,\n Position,\n} from '../types'\nimport { getBoundingRect } from '../utils/dom'\nimport { getViewportInfo, isMobile } from '../utils/device'\n\n/**\n * 默认位置配置\n */\nconst DEFAULT_CONFIG: Required<PopupPositionConfig> = {\n offset: { x: 0, y: 8 },\n autoAdjust: true,\n viewportPadding: 8,\n desktopPlacement: 'bottom',\n mobilePlacement: 'bottom-sheet',\n showMask: true,\n maskClosable: true,\n animationDuration: 300,\n}\n\n/**\n * 位置管理器类\n * 计算键盘弹出层的位置,支持 PC 和移动端自适应\n */\nexport class PositionManager {\n private config: Required<PopupPositionConfig>\n\n constructor(config: PopupPositionConfig = {}) {\n this.config = { ...DEFAULT_CONFIG, ...config }\n }\n\n /**\n * 更新配置\n */\n updateConfig(config: Partial<PopupPositionConfig>): void {\n Object.assign(this.config, config)\n }\n\n /**\n * 获取配置\n */\n getConfig(): Readonly<Required<PopupPositionConfig>> {\n return { ...this.config }\n }\n\n /**\n * 计算弹出位置\n * @param targetElement - 目标元素(如 input)\n * @param popupSize - 弹出层尺寸\n * @returns 位置计算结果\n */\n calculate(\n targetElement: HTMLElement | null,\n popupSize: { width: number; height: number },\n ): PopupPositionResult {\n const viewport = getViewportInfo()\n const isMobileDevice = isMobile()\n\n // 移动端使用 bottom-sheet\n if (isMobileDevice && this.config.mobilePlacement === 'bottom-sheet') {\n return this.calculateBottomSheet(viewport, popupSize)\n }\n\n // PC 端或移动端非 bottom-sheet\n if (!targetElement) {\n // 无目标元素,居中显示\n return this.calculateCentered(viewport, popupSize)\n }\n\n const targetRect = getBoundingRect(targetElement)\n const placement = isMobileDevice\n ? this.config.mobilePlacement\n : this.config.desktopPlacement\n\n // bottom-sheet 已经在前面处理,这里只会是 'bottom' 或 'top'\n const relativePlace = placement === 'bottom-sheet' ? 'bottom' : placement\n return this.calculateRelativeToTarget(targetRect, popupSize, viewport, relativePlace)\n }\n\n /**\n * 计算 bottom-sheet 位置(移动端从底部滑出)\n */\n private calculateBottomSheet(\n viewport: { width: number; height: number; scrollY: number; safeArea?: { bottom: number } },\n popupSize: { width: number; height: number },\n ): PopupPositionResult {\n const safeAreaBottom = viewport.safeArea?.bottom ?? 0\n\n return {\n position: {\n x: 0,\n y: viewport.height - popupSize.height - safeAreaBottom,\n },\n placement: 'bottom-sheet',\n flipped: false,\n maxHeight: viewport.height * 0.6, // 最大占屏幕高度的 60%\n maxWidth: viewport.width,\n transform: 'translateY(0)',\n }\n }\n\n /**\n * 计算居中位置\n */\n private calculateCentered(\n viewport: { width: number; height: number },\n popupSize: { width: number; height: number },\n ): PopupPositionResult {\n return {\n position: {\n x: (viewport.width - popupSize.width) / 2,\n y: (viewport.height - popupSize.height) / 2,\n },\n placement: 'bottom',\n flipped: false,\n }\n }\n\n /**\n * 计算相对于目标元素的位置\n */\n private calculateRelativeToTarget(\n targetRect: BoundingRect,\n popupSize: { width: number; height: number },\n viewport: { width: number; height: number; scrollX: number; scrollY: number },\n preferredPlacement: Exclude<PopupPlacement, 'bottom-sheet'>,\n ): PopupPositionResult {\n const { offset, viewportPadding, autoAdjust } = this.config\n\n let position: Position\n let placement = preferredPlacement\n let flipped = false\n\n // 计算 bottom 位置(左对齐)\n const bottomPosition: Position = {\n x: targetRect.x + (offset.x ?? 0),\n y: targetRect.y + targetRect.height + (offset.y ?? 8),\n }\n\n // 计算 top 位置(左对齐)\n const topPosition: Position = {\n x: targetRect.x + (offset.x ?? 0),\n y: targetRect.y - popupSize.height - (offset.y ?? 8),\n }\n\n if (preferredPlacement === 'bottom') {\n position = bottomPosition\n\n // 检查是否需要翻转到顶部\n if (autoAdjust && position.y + popupSize.height > viewport.height - viewportPadding) {\n // 检查顶部是否有足够空间\n if (topPosition.y >= viewportPadding) {\n position = topPosition\n placement = 'top'\n flipped = true\n }\n }\n }\n else {\n position = topPosition\n\n // 检查是否需要翻转到底部\n if (autoAdjust && position.y < viewportPadding) {\n // 检查底部是否有足够空间\n if (bottomPosition.y + popupSize.height <= viewport.height - viewportPadding) {\n position = bottomPosition\n placement = 'bottom'\n flipped = true\n }\n }\n }\n\n // 水平方向调整,确保不超出视口\n if (autoAdjust) {\n // 左边界检查\n if (position.x < viewportPadding) {\n position.x = viewportPadding\n }\n\n // 右边界检查\n if (position.x + popupSize.width > viewport.width - viewportPadding) {\n position.x = viewport.width - popupSize.width - viewportPadding\n }\n }\n\n // 计算最大可用高度\n const maxHeight = placement === 'bottom'\n ? viewport.height - position.y - viewportPadding\n : targetRect.y - viewportPadding\n\n return {\n position,\n placement,\n flipped,\n maxHeight: Math.max(200, maxHeight), // 最小高度 200px\n maxWidth: viewport.width - viewportPadding * 2,\n }\n }\n\n /**\n * 获取弹出层初始样式(用于动画)\n */\n getInitialStyles(placement: PopupPlacement): Partial<CSSStyleDeclaration> {\n switch (placement) {\n case 'bottom-sheet':\n return {\n transform: 'translateY(100%)',\n opacity: '1',\n }\n case 'bottom':\n return {\n transform: 'translateY(-10px)',\n opacity: '0',\n }\n case 'top':\n return {\n transform: 'translateY(10px)',\n opacity: '0',\n }\n default:\n return {\n opacity: '0',\n }\n }\n }\n\n /**\n * 获取弹出层最终样式(动画结束)\n */\n getFinalStyles(placement: PopupPlacement): Partial<CSSStyleDeclaration> {\n return {\n transform: 'translateY(0)',\n opacity: '1',\n }\n }\n\n /**\n * 获取弹出层退出样式\n */\n getExitStyles(placement: PopupPlacement): Partial<CSSStyleDeclaration> {\n return this.getInitialStyles(placement)\n }\n\n /**\n * 获取动画时长\n */\n getAnimationDuration(): number {\n return this.config.animationDuration\n }\n\n /**\n * 检查是否显示遮罩\n */\n shouldShowMask(): boolean {\n return isMobile() && this.config.showMask\n }\n\n /**\n * 检查遮罩是否可点击关闭\n */\n isMaskClosable(): boolean {\n return this.config.maskClosable\n }\n}\n\n/**\n * 创建位置管理器实例\n */\nexport function createPositionManager(config?: PopupPositionConfig): PositionManager {\n return new PositionManager(config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"position-manager.js","names":[],"sources":["../../src/managers/position-manager.ts"],"sourcesContent":["/**\r\n * 弹出位置管理器\r\n * @module managers/position-manager\r\n */\r\n\r\nimport type {\r\n BoundingRect,\r\n PopupPlacement,\r\n PopupPositionConfig,\r\n PopupPositionResult,\r\n Position,\r\n} from '../types'\r\nimport { getViewportInfo, isMobile } from '../utils/device'\r\nimport { getBoundingRect } from '../utils/dom'\r\n\r\n/**\r\n * 默认位置配置\r\n */\r\nconst DEFAULT_CONFIG: Required<PopupPositionConfig> = {\r\n offset: { x: 0, y: 8 },\r\n autoAdjust: true,\r\n viewportPadding: 8,\r\n desktopPlacement: 'bottom',\r\n mobilePlacement: 'bottom-sheet',\r\n showMask: true,\r\n maskClosable: true,\r\n animationDuration: 300,\r\n}\r\n\r\nfunction mergeConfig(\r\n config: PopupPositionConfig = {}\r\n): Required<PopupPositionConfig> {\r\n return {\r\n ...DEFAULT_CONFIG,\r\n ...config,\r\n offset: {\r\n ...DEFAULT_CONFIG.offset,\r\n ...config.offset,\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * 位置管理器类\r\n * 计算键盘弹出层的位置,支持 PC 和移动端自适应\r\n */\r\nexport class PositionManager {\r\n private config: Required<PopupPositionConfig>\r\n\r\n constructor(config: PopupPositionConfig = {}) {\r\n this.config = mergeConfig(config)\r\n }\r\n\r\n /**\r\n * 更新配置\r\n */\r\n updateConfig(config: Partial<PopupPositionConfig>): void {\r\n this.config = mergeConfig({\r\n ...this.config,\r\n ...config,\r\n offset: { ...this.config.offset, ...config.offset },\r\n })\r\n }\r\n\r\n /**\r\n * 获取配置\r\n */\r\n getConfig(): Readonly<Required<PopupPositionConfig>> {\r\n return { ...this.config, offset: { ...this.config.offset } }\r\n }\r\n\r\n /**\r\n * 计算弹出位置\r\n * @param targetElement - 目标元素(如 input)\r\n * @param popupSize - 弹出层尺寸\r\n * @param popupSize.width - Popup width\r\n * @param popupSize.height - Popup height\r\n * @returns 位置计算结果\r\n */\r\n calculate(\r\n targetElement: HTMLElement | null,\r\n popupSize: { width: number; height: number }\r\n ): PopupPositionResult {\r\n const viewport = getViewportInfo()\r\n const isMobileDevice = isMobile()\r\n\r\n // 移动端使用 bottom-sheet\r\n if (isMobileDevice && this.config.mobilePlacement === 'bottom-sheet') {\r\n return this.calculateBottomSheet(viewport, popupSize)\r\n }\r\n\r\n // PC 端或移动端非 bottom-sheet\r\n if (!targetElement) {\r\n // 无目标元素,居中显示\r\n return this.calculateCentered(viewport, popupSize)\r\n }\r\n\r\n const targetRect = getBoundingRect(targetElement)\r\n const placement = isMobileDevice\r\n ? this.config.mobilePlacement\r\n : this.config.desktopPlacement\r\n\r\n // bottom-sheet 已经在前面处理,这里只会是 'bottom' 或 'top'\r\n const relativePlace = placement === 'bottom-sheet' ? 'bottom' : placement\r\n return this.calculateRelativeToTarget(\r\n targetRect,\r\n popupSize,\r\n viewport,\r\n relativePlace\r\n )\r\n }\r\n\r\n /**\r\n * 计算 bottom-sheet 位置(移动端从底部滑出)\r\n */\r\n private calculateBottomSheet(\r\n viewport: {\r\n width: number\r\n height: number\r\n scrollY: number\r\n safeArea?: { bottom: number }\r\n },\r\n popupSize: { width: number; height: number }\r\n ): PopupPositionResult {\r\n const safeAreaBottom = viewport.safeArea?.bottom ?? 0\r\n const maxHeight = Math.max(0, viewport.height * 0.6 - safeAreaBottom)\r\n const visibleHeight = Math.min(Math.max(0, popupSize.height), maxHeight)\r\n\r\n return {\r\n position: {\r\n x: 0,\r\n y: Math.max(0, viewport.height - visibleHeight - safeAreaBottom),\r\n },\r\n placement: 'bottom-sheet',\r\n flipped: false,\r\n maxHeight,\r\n maxWidth: Math.max(0, viewport.width),\r\n transform: 'translateY(0)',\r\n }\r\n }\r\n\r\n /**\r\n * 计算居中位置\r\n */\r\n private calculateCentered(\r\n viewport: { width: number; height: number },\r\n popupSize: { width: number; height: number }\r\n ): PopupPositionResult {\r\n return {\r\n position: {\r\n x: Math.max(0, (viewport.width - popupSize.width) / 2),\r\n y: Math.max(0, (viewport.height - popupSize.height) / 2),\r\n },\r\n placement: 'bottom',\r\n flipped: false,\r\n }\r\n }\r\n\r\n /**\r\n * 计算相对于目标元素的位置\r\n */\r\n private calculateRelativeToTarget(\r\n targetRect: BoundingRect,\r\n popupSize: { width: number; height: number },\r\n viewport: {\r\n width: number\r\n height: number\r\n scrollX: number\r\n scrollY: number\r\n },\r\n preferredPlacement: Exclude<PopupPlacement, 'bottom-sheet'>\r\n ): PopupPositionResult {\r\n const { offset, viewportPadding, autoAdjust } = this.config\r\n\r\n let position: Position\r\n let placement = preferredPlacement\r\n let flipped = false\r\n\r\n // 计算 bottom 位置(左对齐)\r\n const bottomPosition: Position = {\r\n x: targetRect.x + (offset.x ?? 0),\r\n y: targetRect.y + targetRect.height + (offset.y ?? 8),\r\n }\r\n\r\n // 计算 top 位置(左对齐)\r\n const topPosition: Position = {\r\n x: targetRect.x + (offset.x ?? 0),\r\n y: targetRect.y - popupSize.height - (offset.y ?? 8),\r\n }\r\n\r\n if (preferredPlacement === 'bottom') {\r\n position = bottomPosition\r\n\r\n // 检查是否需要翻转到顶部\r\n if (\r\n autoAdjust &&\r\n position.y + popupSize.height > viewport.height - viewportPadding\r\n ) {\r\n // 检查顶部是否有足够空间\r\n if (topPosition.y >= viewportPadding) {\r\n position = topPosition\r\n placement = 'top'\r\n flipped = true\r\n }\r\n }\r\n } else {\r\n position = topPosition\r\n\r\n // 检查是否需要翻转到底部\r\n if (autoAdjust && position.y < viewportPadding) {\r\n // 检查底部是否有足够空间\r\n if (\r\n bottomPosition.y + popupSize.height <=\r\n viewport.height - viewportPadding\r\n ) {\r\n position = bottomPosition\r\n placement = 'bottom'\r\n flipped = true\r\n }\r\n }\r\n }\r\n\r\n // 水平方向调整,确保不超出视口\r\n if (autoAdjust) {\r\n const maxX = Math.max(\r\n viewportPadding,\r\n viewport.width - popupSize.width - viewportPadding\r\n )\r\n position.x = Math.min(Math.max(position.x, viewportPadding), maxX)\r\n }\r\n\r\n // 计算最大可用高度\r\n const maxHeight =\r\n placement === 'bottom'\r\n ? viewport.height - position.y - viewportPadding\r\n : targetRect.y - viewportPadding\r\n\r\n return {\r\n position,\r\n placement,\r\n flipped,\r\n maxHeight: Math.max(0, maxHeight),\r\n maxWidth: Math.max(0, viewport.width - viewportPadding * 2),\r\n }\r\n }\r\n\r\n /**\r\n * 获取弹出层初始样式(用于动画)\r\n */\r\n getInitialStyles(placement: PopupPlacement): Partial<CSSStyleDeclaration> {\r\n switch (placement) {\r\n case 'bottom-sheet':\r\n return {\r\n transform: 'translateY(100%)',\r\n opacity: '1',\r\n }\r\n case 'bottom':\r\n return {\r\n transform: 'translateY(calc(-1 * var(--ld-px-10)))',\r\n opacity: '0',\r\n }\r\n case 'top':\r\n return {\r\n transform: 'translateY(var(--ld-px-10))',\r\n opacity: '0',\r\n }\r\n default:\r\n return {\r\n opacity: '0',\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 获取弹出层最终样式(动画结束)\r\n */\r\n getFinalStyles(_placement: PopupPlacement): Partial<CSSStyleDeclaration> {\r\n return {\r\n transform: 'translateY(0)',\r\n opacity: '1',\r\n }\r\n }\r\n\r\n /**\r\n * 获取弹出层退出样式\r\n */\r\n getExitStyles(_placement: PopupPlacement): Partial<CSSStyleDeclaration> {\r\n return this.getInitialStyles(_placement)\r\n }\r\n\r\n /**\r\n * 获取动画时长\r\n */\r\n getAnimationDuration(): number {\r\n return this.config.animationDuration\r\n }\r\n\r\n /**\r\n * 检查是否显示遮罩\r\n */\r\n shouldShowMask(): boolean {\r\n return isMobile() && this.config.showMask\r\n }\r\n\r\n /**\r\n * 检查遮罩是否可点击关闭\r\n */\r\n isMaskClosable(): boolean {\r\n return this.config.maskClosable\r\n }\r\n}\r\n\r\n/**\r\n * 创建位置管理器实例\r\n */\r\nexport function createPositionManager(\r\n config?: PopupPositionConfig\r\n): PositionManager {\r\n return new PositionManager(config)\r\n}\r\n"],"mappings":"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"}
|
package/es/themes/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* @module themes
|
|
4
4
|
*/
|
|
5
5
|
import type { ThemeConfig, ThemeCSSVariables, ThemeName } from './types';
|
|
6
|
-
export * from './types';
|
|
7
6
|
export * from './presets';
|
|
7
|
+
export * from './types';
|
|
8
8
|
/**
|
|
9
9
|
* 获取主题配置
|
|
10
10
|
* @param name - 主题名称
|
|
@@ -67,4 +67,3 @@ export declare function mergeTheme(base: ThemeConfig, overrides: Partial<ThemeCo
|
|
|
67
67
|
* @param customization - 自定义配置
|
|
68
68
|
*/
|
|
69
69
|
export declare function createTheme(name: string, baseTheme: ThemeName | string, customization: Partial<Omit<ThemeConfig, 'name'>>): ThemeConfig;
|
|
70
|
-
//# sourceMappingURL=index.d.ts.map
|
package/es/themes/index.js
CHANGED
|
@@ -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
|
* ***********************************
|
|
@@ -49,6 +49,7 @@ function themeToCSS(theme) {
|
|
|
49
49
|
"--keyboard-fn-font-size": `${theme.sizes.fnFontSize}px`,
|
|
50
50
|
"--keyboard-padding": `${theme.sizes.padding}px`,
|
|
51
51
|
"--keyboard-key-shadow": theme.effects.keyShadow,
|
|
52
|
+
"--keyboard-mask-bg": "color-mix(in srgb, var(--ld-black) 40%, var(--ld-transparent))",
|
|
52
53
|
"--keyboard-transition-duration": `${theme.effects.transitionDuration}ms`,
|
|
53
54
|
"--keyboard-border-color": theme.colors.borderColor,
|
|
54
55
|
"--keyboard-divider-color": theme.colors.dividerColor
|
package/es/themes/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/themes/index.ts"],"sourcesContent":["/**\n * 主题管理模块\n * @module themes\n */\n\nimport type { ThemeConfig, ThemeCSSVariables, ThemeName } from './types'\nimport { defaultTheme, presetThemes } from './presets'\n\nexport * from './
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/themes/index.ts"],"sourcesContent":["/**\r\n * 主题管理模块\r\n * @module themes\r\n */\r\n\r\nimport type { ThemeConfig, ThemeCSSVariables, ThemeName } from './types'\r\nimport { defaultTheme, presetThemes } from './presets'\r\n\r\nexport * from './presets'\r\nexport * from './types'\r\n\r\n/**\r\n * 自定义主题存储\r\n */\r\nconst customThemes: Map<string, ThemeConfig> = new Map()\r\n\r\n/**\r\n * 当前激活的主题\r\n */\r\nlet currentTheme: ThemeConfig = defaultTheme\r\n\r\n/**\r\n * 获取主题配置\r\n * @param name - 主题名称\r\n * @returns 主题配置,如果不存在则返回默认主题\r\n */\r\nexport function getTheme(name: ThemeName | string): ThemeConfig {\r\n return customThemes.get(name) || presetThemes[name] || defaultTheme\r\n}\r\n\r\n/**\r\n * 注册自定义主题\r\n * @param theme - 主题配置\r\n */\r\nexport function registerTheme(theme: ThemeConfig): void {\r\n customThemes.set(theme.name, theme)\r\n}\r\n\r\n/**\r\n * 获取所有可用主题\r\n * @returns 主题名称列表\r\n */\r\nexport function getAvailableThemes(): string[] {\r\n return [...Object.keys(presetThemes), ...customThemes.keys()]\r\n}\r\n\r\n/**\r\n * 获取所有主题配置\r\n * @returns 主题配置列表\r\n */\r\nexport function getAllThemes(): ThemeConfig[] {\r\n return [...Object.values(presetThemes), ...customThemes.values()]\r\n}\r\n\r\n/**\r\n * 设置当前主题\r\n * @param name - 主题名称\r\n */\r\nexport function setCurrentTheme(name: ThemeName | string): ThemeConfig {\r\n currentTheme = getTheme(name)\r\n return currentTheme\r\n}\r\n\r\n/**\r\n * 获取当前主题\r\n */\r\nexport function getCurrentTheme(): ThemeConfig {\r\n return currentTheme\r\n}\r\n\r\n/**\r\n * 将主题配置转换为 CSS 变量\r\n * @param theme - 主题配置\r\n * @returns CSS 变量对象\r\n */\r\nexport function themeToCSS(theme: ThemeConfig): ThemeCSSVariables {\r\n return {\r\n '--keyboard-bg': theme.colors.background,\r\n '--keyboard-key-bg': theme.colors.keyBackground,\r\n '--keyboard-key-color': theme.colors.keyColor,\r\n '--keyboard-key-active-bg': theme.colors.keyActiveBackground,\r\n '--keyboard-fn-key-bg': theme.colors.fnKeyBackground,\r\n '--keyboard-fn-key-color': theme.colors.fnKeyColor,\r\n '--keyboard-fn-key-active-bg': theme.colors.fnKeyActiveBackground,\r\n '--keyboard-primary-bg': theme.colors.primaryBackground,\r\n '--keyboard-primary-active-bg': theme.colors.primaryActiveBackground,\r\n '--keyboard-primary-color': theme.colors.primaryColor,\r\n '--keyboard-key-height': `${theme.sizes.keyHeight}px`,\r\n '--keyboard-key-radius': `${theme.sizes.keyRadius}px`,\r\n '--keyboard-key-gap': `${theme.sizes.keyGap}px`,\r\n '--keyboard-font-size': `${theme.sizes.fontSize}px`,\r\n '--keyboard-fn-font-size': `${theme.sizes.fnFontSize}px`,\r\n '--keyboard-padding': `${theme.sizes.padding}px`,\r\n '--keyboard-key-shadow': theme.effects.keyShadow,\r\n '--keyboard-mask-bg':\r\n 'color-mix(in srgb, var(--ld-black) 40%, var(--ld-transparent))',\r\n '--keyboard-transition-duration': `${theme.effects.transitionDuration}ms`,\r\n '--keyboard-border-color': theme.colors.borderColor,\r\n '--keyboard-divider-color': theme.colors.dividerColor,\r\n }\r\n}\r\n\r\n/**\r\n * 将 CSS 变量应用到元素\r\n * @param element - 目标元素\r\n * @param theme - 主题配置\r\n */\r\nexport function applyTheme(element: HTMLElement, theme: ThemeConfig): void {\r\n const cssVars = themeToCSS(theme)\r\n Object.entries(cssVars).forEach(([key, value]) => {\r\n if (value) {\r\n element.style.setProperty(key, value)\r\n }\r\n })\r\n\r\n // 添加主题类名\r\n element.classList.remove('keyboard-theme-light', 'keyboard-theme-dark')\r\n element.classList.add(\r\n theme.isDark ? 'keyboard-theme-dark' : 'keyboard-theme-light'\r\n )\r\n element.dataset.theme = theme.name\r\n}\r\n\r\n/**\r\n * 生成主题的内联样式字符串\r\n * @param theme - 主题配置\r\n * @returns 内联样式字符串\r\n */\r\nexport function generateThemeStyle(theme: ThemeConfig): string {\r\n const cssVars = themeToCSS(theme)\r\n return Object.entries(cssVars)\r\n .filter(([, value]) => value)\r\n .map(([key, value]) => `${key}: ${value}`)\r\n .join('; ')\r\n}\r\n\r\n/**\r\n * 合并主题配置\r\n * @param base - 基础主题\r\n * @param overrides - 覆盖配置\r\n * @returns 合并后的主题\r\n */\r\nexport function mergeTheme(\r\n base: ThemeConfig,\r\n overrides: Partial<ThemeConfig>\r\n): ThemeConfig {\r\n return {\r\n ...base,\r\n ...overrides,\r\n colors: { ...base.colors, ...overrides.colors },\r\n sizes: { ...base.sizes, ...overrides.sizes },\r\n effects: { ...base.effects, ...overrides.effects },\r\n }\r\n}\r\n\r\n/**\r\n * 创建自定义主题\r\n * @param name - 主题名称\r\n * @param baseTheme - 基础主题名称\r\n * @param customization - 自定义配置\r\n */\r\nexport function createTheme(\r\n name: string,\r\n baseTheme: ThemeName | string,\r\n customization: Partial<Omit<ThemeConfig, 'name'>>\r\n): ThemeConfig {\r\n const base = getTheme(baseTheme)\r\n const theme = mergeTheme(base, {\r\n ...customization,\r\n name,\r\n })\r\n registerTheme(theme)\r\n return theme\r\n}\r\n"],"mappings":";;;;;;;;;;;AAcA,MAAM,+BAAyC,IAAI,IAAI;AAKvD,IAAI,eAA4B;AAOzB,SAAS,SAAS,MAAuC;CAC9D,OAAO,aAAa,IAAI,IAAI,KAAK,aAAa,SAAS;AACzD;AAMO,SAAS,cAAc,OAA0B;CACtD,aAAa,IAAI,MAAM,MAAM,KAAK;AACpC;AAMO,SAAS,qBAA+B;CAC7C,OAAO,CAAC,GAAG,OAAO,KAAK,YAAY,GAAG,GAAG,aAAa,KAAK,CAAC;AAC9D;AAMO,SAAS,eAA8B;CAC5C,OAAO,CAAC,GAAG,OAAO,OAAO,YAAY,GAAG,GAAG,aAAa,OAAO,CAAC;AAClE;AAMO,SAAS,gBAAgB,MAAuC;CACrE,eAAe,SAAS,IAAI;CAC5B,OAAO;AACT;AAKO,SAAS,kBAA+B;CAC7C,OAAO;AACT;AAOO,SAAS,WAAW,OAAuC;CAChE,OAAO;EACL,iBAAiB,MAAM,OAAO;EAC9B,qBAAqB,MAAM,OAAO;EAClC,wBAAwB,MAAM,OAAO;EACrC,4BAA4B,MAAM,OAAO;EACzC,wBAAwB,MAAM,OAAO;EACrC,2BAA2B,MAAM,OAAO;EACxC,+BAA+B,MAAM,OAAO;EAC5C,yBAAyB,MAAM,OAAO;EACtC,gCAAgC,MAAM,OAAO;EAC7C,4BAA4B,MAAM,OAAO;EACzC,yBAAyB,GAAG,MAAM,MAAM,UAAS;EACjD,yBAAyB,GAAG,MAAM,MAAM,UAAS;EACjD,sBAAsB,GAAG,MAAM,MAAM,OAAM;EAC3C,wBAAwB,GAAG,MAAM,MAAM,SAAQ;EAC/C,2BAA2B,GAAG,MAAM,MAAM,WAAU;EACpD,sBAAsB,GAAG,MAAM,MAAM,QAAO;EAC5C,yBAAyB,MAAM,QAAQ;EACvC,sBACE;EACF,kCAAkC,GAAG,MAAM,QAAQ,mBAAkB;EACrE,2BAA2B,MAAM,OAAO;EACxC,4BAA4B,MAAM,OAAO;CAC3C;AACF;AAOO,SAAS,WAAW,SAAsB,OAA0B;CACzE,MAAM,UAAU,WAAW,KAAK;CAChC,OAAO,QAAQ,OAAO,CAAA,CAAE,SAAS,CAAC,KAAK,WAAW;EAChD,IAAI,OACF,QAAQ,MAAM,YAAY,KAAK,KAAK;CAExC,CAAC;CAGD,QAAQ,UAAU,OAAO,wBAAwB,qBAAqB;CACtE,QAAQ,UAAU,IAChB,MAAM,SAAS,wBAAwB,sBACzC;CACA,QAAQ,QAAQ,QAAQ,MAAM;AAChC;AAOO,SAAS,mBAAmB,OAA4B;CAC7D,MAAM,UAAU,WAAW,KAAK;CAChC,OAAO,OAAO,QAAQ,OAAO,CAAA,CAC1B,QAAQ,GAAG,WAAW,KAAK,CAAA,CAC3B,KAAK,CAAC,KAAK,WAAW,GAAG,IAAG,IAAK,OAAO,CAAA,CACxC,KAAK,IAAI;AACd;AAQO,SAAS,WACd,MACA,WACa;CACb,OAAO;EACL,GAAG;EACH,GAAG;EACH,QAAQ;GAAE,GAAG,KAAK;GAAQ,GAAG,UAAU;EAAO;EAC9C,OAAO;GAAE,GAAG,KAAK;GAAO,GAAG,UAAU;EAAM;EAC3C,SAAS;GAAE,GAAG,KAAK;GAAS,GAAG,UAAU;EAAQ;CACnD;AACF;AAQO,SAAS,YACd,MACA,WACA,eACa;CAEb,MAAM,QAAQ,WADD,SAAS,SACG,GAAM;EAC7B,GAAG;EACH;CACF,CAAC;CACD,cAAc,KAAK;CACnB,OAAO;AACT"}
|
package/es/themes/presets.d.ts
CHANGED
package/es/themes/presets.js
CHANGED
package/es/themes/presets.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.js","names":[],"sources":["../../src/themes/presets.ts"],"sourcesContent":["/**\n * 预设主题配置\n * @module themes/presets\n */\n\nimport type { ThemeConfig } from './types'\n\n/**\n * MIUI 小米风格主题(默认)\n * 特点:纯净、扁平、简洁、无多余装饰\n */\nexport const miuiTheme: ThemeConfig = {\n name: 'miui',\n displayName: 'MIUI',\n description: '小米风格 - 纯净简洁',\n isDark: false,\n colors: {\n background: '#f5f5f5',\n keyBackground: '#ffffff',\n keyColor: '#1a1a1a',\n keyActiveBackground: '#e8e8e8',\n fnKeyBackground: '#e0e0e0',\n fnKeyColor: '#666666',\n fnKeyActiveBackground: '#d0d0d0',\n primaryBackground: '#ff6700', // 小米橙\n primaryActiveBackground: '#e55d00',\n primaryColor: '#ffffff',\n borderColor: 'transparent',\n dividerColor: '#eeeeee',\n },\n sizes: {\n keyHeight: 46,\n keyRadius: 8,\n keyGap: 6,\n fontSize: 18,\n fnFontSize: 14,\n padding: 8,\n },\n effects: {\n keyShadow: 'none',\n keyActiveShadow: 'none',\n keyActiveScale: 0.96,\n transitionDuration: 100,\n enableBlur: false,\n blurAmount: 0,\n enableRipple: false,\n },\n}\n\n/**\n * iOS 风格主题\n * 特点:毛玻璃、圆润阴影、精致\n */\nexport const iosTheme: ThemeConfig = {\n name: 'ios',\n displayName: 'iOS',\n description: 'Apple 风格 - 精致优雅',\n isDark: false,\n colors: {\n background: '#d1d3d9',\n keyBackground: '#ffffff',\n keyColor: '#000000',\n keyActiveBackground: '#b8bac2',\n fnKeyBackground: '#a8aab4',\n fnKeyColor: '#000000',\n fnKeyActiveBackground: '#9699a3',\n primaryBackground: '#007aff',\n primaryActiveBackground: '#0062cc',\n primaryColor: '#ffffff',\n borderColor: 'transparent',\n dividerColor: '#c8cad0',\n },\n sizes: {\n keyHeight: 43,\n keyRadius: 5,\n keyGap: 6,\n fontSize: 22,\n fnFontSize: 15,\n padding: 4,\n },\n effects: {\n keyShadow: '0 1px 0 rgba(0,0,0,0.35)',\n keyActiveShadow: '0 0 0 rgba(0,0,0,0)',\n keyActiveScale: 1,\n transitionDuration: 80,\n enableBlur: true,\n blurAmount: 20,\n enableRipple: false,\n },\n}\n\n/**\n * Material Design 主题\n * 特点:深色、涟漪动效、层次感\n */\nexport const materialTheme: ThemeConfig = {\n name: 'material',\n displayName: 'Material',\n description: 'Google 风格 - 深邃现代',\n isDark: true,\n colors: {\n background: '#263238',\n keyBackground: '#37474f',\n keyColor: '#ffffff',\n keyActiveBackground: '#455a64',\n fnKeyBackground: '#455a64',\n fnKeyColor: '#b0bec5',\n fnKeyActiveBackground: '#546e7a',\n primaryBackground: '#4caf50', // Material Green\n primaryActiveBackground: '#43a047',\n primaryColor: '#ffffff',\n borderColor: 'transparent',\n dividerColor: '#37474f',\n },\n sizes: {\n keyHeight: 48,\n keyRadius: 4,\n keyGap: 4,\n fontSize: 20,\n fnFontSize: 14,\n padding: 6,\n },\n effects: {\n keyShadow: '0 1px 3px rgba(0,0,0,0.2)',\n keyActiveShadow: '0 2px 6px rgba(0,0,0,0.3)',\n keyActiveScale: 1,\n transitionDuration: 150,\n enableBlur: false,\n blurAmount: 0,\n enableRipple: true,\n },\n}\n\n/**\n * 极简主题\n * 特点:无边框、超简洁、透明感\n */\nexport const minimalTheme: ThemeConfig = {\n name: 'minimal',\n displayName: 'Minimal',\n description: '极简风格 - 轻盈透明',\n isDark: false,\n colors: {\n background: 'rgba(250, 250, 250, 0.95)',\n keyBackground: 'transparent',\n keyColor: '#333333',\n keyActiveBackground: 'rgba(0, 0, 0, 0.08)',\n fnKeyBackground: 'transparent',\n fnKeyColor: '#888888',\n fnKeyActiveBackground: 'rgba(0, 0, 0, 0.08)',\n primaryBackground: '#333333',\n primaryActiveBackground: '#000000',\n primaryColor: '#ffffff',\n borderColor: '#eeeeee',\n dividerColor: '#eeeeee',\n },\n sizes: {\n keyHeight: 44,\n keyRadius: 12,\n keyGap: 8,\n fontSize: 17,\n fnFontSize: 13,\n padding: 12,\n },\n effects: {\n keyShadow: 'none',\n keyActiveShadow: 'none',\n keyActiveScale: 0.92,\n transitionDuration: 120,\n enableBlur: true,\n blurAmount: 30,\n enableRipple: false,\n },\n}\n\n/**\n * 深色主题\n * 特点:护眼、现代、高对比\n */\nexport const darkTheme: ThemeConfig = {\n name: 'dark',\n displayName: 'Dark',\n description: '深色模式 - 护眼舒适',\n isDark: true,\n colors: {\n background: '#1c1c1e',\n keyBackground: '#3a3a3c',\n keyColor: '#ffffff',\n keyActiveBackground: '#4a4a4c',\n fnKeyBackground: '#2c2c2e',\n fnKeyColor: '#ebebf5',\n fnKeyActiveBackground: '#3c3c3e',\n primaryBackground: '#0a84ff',\n primaryActiveBackground: '#0070e0',\n primaryColor: '#ffffff',\n borderColor: '#3a3a3c',\n dividerColor: '#3a3a3c',\n },\n sizes: {\n keyHeight: 45,\n keyRadius: 8,\n keyGap: 6,\n fontSize: 18,\n fnFontSize: 14,\n padding: 8,\n },\n effects: {\n keyShadow: '0 1px 2px rgba(0,0,0,0.4)',\n keyActiveShadow: '0 0 8px rgba(10,132,255,0.3)',\n keyActiveScale: 0.97,\n transitionDuration: 100,\n enableBlur: true,\n blurAmount: 20,\n enableRipple: false,\n },\n}\n\n/**\n * 所有预设主题\n */\nexport const presetThemes: Record<string, ThemeConfig> = {\n miui: miuiTheme,\n ios: iosTheme,\n material: materialTheme,\n minimal: minimalTheme,\n dark: darkTheme,\n}\n\n/**\n * 默认主题\n */\nexport const defaultTheme = miuiTheme\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presets.js","names":[],"sources":["../../src/themes/presets.ts"],"sourcesContent":["/**\r\n * 预设主题配置\r\n * @module themes/presets\r\n */\r\n\r\nimport type { ThemeConfig } from './types'\r\n\r\n/**\r\n * MIUI 小米风格主题(默认)\r\n * 特点:纯净、扁平、简洁、无多余装饰\r\n */\r\nexport const miuiTheme: ThemeConfig = {\r\n name: 'miui',\r\n displayName: 'MIUI',\r\n description: '小米风格 - 纯净简洁',\r\n isDark: false,\r\n colors: {\r\n background: '#f5f5f5',\r\n keyBackground: '#ffffff',\r\n keyColor: '#1a1a1a',\r\n keyActiveBackground: '#e8e8e8',\r\n fnKeyBackground: '#e0e0e0',\r\n fnKeyColor: '#666666',\r\n fnKeyActiveBackground: '#d0d0d0',\r\n primaryBackground: '#ff6700', // 小米橙\r\n primaryActiveBackground: '#e55d00',\r\n primaryColor: '#ffffff',\r\n borderColor: 'transparent',\r\n dividerColor: '#eeeeee',\r\n },\r\n sizes: {\r\n keyHeight: 46,\r\n keyRadius: 8,\r\n keyGap: 6,\r\n fontSize: 18,\r\n fnFontSize: 14,\r\n padding: 8,\r\n },\r\n effects: {\r\n keyShadow: 'none',\r\n keyActiveShadow: 'none',\r\n keyActiveScale: 0.96,\r\n transitionDuration: 100,\r\n enableBlur: false,\r\n blurAmount: 0,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * iOS 风格主题\r\n * 特点:毛玻璃、圆润阴影、精致\r\n */\r\nexport const iosTheme: ThemeConfig = {\r\n name: 'ios',\r\n displayName: 'iOS',\r\n description: 'Apple 风格 - 精致优雅',\r\n isDark: false,\r\n colors: {\r\n background: '#d1d3d9',\r\n keyBackground: '#ffffff',\r\n keyColor: '#000000',\r\n keyActiveBackground: '#b8bac2',\r\n fnKeyBackground: '#a8aab4',\r\n fnKeyColor: '#000000',\r\n fnKeyActiveBackground: '#9699a3',\r\n primaryBackground: '#007aff',\r\n primaryActiveBackground: '#0062cc',\r\n primaryColor: '#ffffff',\r\n borderColor: 'transparent',\r\n dividerColor: '#c8cad0',\r\n },\r\n sizes: {\r\n keyHeight: 43,\r\n keyRadius: 5,\r\n keyGap: 6,\r\n fontSize: 22,\r\n fnFontSize: 15,\r\n padding: 4,\r\n },\r\n effects: {\r\n keyShadow: '0 1px 0 rgba(0,0,0,0.35)',\r\n keyActiveShadow: '0 0 0 rgba(0,0,0,0)',\r\n keyActiveScale: 1,\r\n transitionDuration: 80,\r\n enableBlur: true,\r\n blurAmount: 20,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * Material Design 主题\r\n * 特点:深色、涟漪动效、层次感\r\n */\r\nexport const materialTheme: ThemeConfig = {\r\n name: 'material',\r\n displayName: 'Material',\r\n description: 'Google 风格 - 深邃现代',\r\n isDark: true,\r\n colors: {\r\n background: '#263238',\r\n keyBackground: '#37474f',\r\n keyColor: '#ffffff',\r\n keyActiveBackground: '#455a64',\r\n fnKeyBackground: '#455a64',\r\n fnKeyColor: '#b0bec5',\r\n fnKeyActiveBackground: '#546e7a',\r\n primaryBackground: '#4caf50', // Material Green\r\n primaryActiveBackground: '#43a047',\r\n primaryColor: '#ffffff',\r\n borderColor: 'transparent',\r\n dividerColor: '#37474f',\r\n },\r\n sizes: {\r\n keyHeight: 48,\r\n keyRadius: 4,\r\n keyGap: 4,\r\n fontSize: 20,\r\n fnFontSize: 14,\r\n padding: 6,\r\n },\r\n effects: {\r\n keyShadow: '0 1px 3px rgba(0,0,0,0.2)',\r\n keyActiveShadow: '0 2px 6px rgba(0,0,0,0.3)',\r\n keyActiveScale: 1,\r\n transitionDuration: 150,\r\n enableBlur: false,\r\n blurAmount: 0,\r\n enableRipple: true,\r\n },\r\n}\r\n\r\n/**\r\n * 极简主题\r\n * 特点:无边框、超简洁、透明感\r\n */\r\nexport const minimalTheme: ThemeConfig = {\r\n name: 'minimal',\r\n displayName: 'Minimal',\r\n description: '极简风格 - 轻盈透明',\r\n isDark: false,\r\n colors: {\r\n background: 'rgba(250, 250, 250, 0.95)',\r\n keyBackground: 'transparent',\r\n keyColor: '#333333',\r\n keyActiveBackground: 'rgba(0, 0, 0, 0.08)',\r\n fnKeyBackground: 'transparent',\r\n fnKeyColor: '#888888',\r\n fnKeyActiveBackground: 'rgba(0, 0, 0, 0.08)',\r\n primaryBackground: '#333333',\r\n primaryActiveBackground: '#000000',\r\n primaryColor: '#ffffff',\r\n borderColor: '#eeeeee',\r\n dividerColor: '#eeeeee',\r\n },\r\n sizes: {\r\n keyHeight: 44,\r\n keyRadius: 12,\r\n keyGap: 8,\r\n fontSize: 17,\r\n fnFontSize: 13,\r\n padding: 12,\r\n },\r\n effects: {\r\n keyShadow: 'none',\r\n keyActiveShadow: 'none',\r\n keyActiveScale: 0.92,\r\n transitionDuration: 120,\r\n enableBlur: true,\r\n blurAmount: 30,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * 深色主题\r\n * 特点:护眼、现代、高对比\r\n */\r\nexport const darkTheme: ThemeConfig = {\r\n name: 'dark',\r\n displayName: 'Dark',\r\n description: '深色模式 - 护眼舒适',\r\n isDark: true,\r\n colors: {\r\n background: '#1c1c1e',\r\n keyBackground: '#3a3a3c',\r\n keyColor: '#ffffff',\r\n keyActiveBackground: '#4a4a4c',\r\n fnKeyBackground: '#2c2c2e',\r\n fnKeyColor: '#ebebf5',\r\n fnKeyActiveBackground: '#3c3c3e',\r\n primaryBackground: '#0a84ff',\r\n primaryActiveBackground: '#0070e0',\r\n primaryColor: '#ffffff',\r\n borderColor: '#3a3a3c',\r\n dividerColor: '#3a3a3c',\r\n },\r\n sizes: {\r\n keyHeight: 45,\r\n keyRadius: 8,\r\n keyGap: 6,\r\n fontSize: 18,\r\n fnFontSize: 14,\r\n padding: 8,\r\n },\r\n effects: {\r\n keyShadow: '0 1px 2px rgba(0,0,0,0.4)',\r\n keyActiveShadow: '0 0 8px rgba(10,132,255,0.3)',\r\n keyActiveScale: 0.97,\r\n transitionDuration: 100,\r\n enableBlur: true,\r\n blurAmount: 20,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * 所有预设主题\r\n */\r\nexport const presetThemes: Record<string, ThemeConfig> = {\r\n miui: miuiTheme,\r\n ios: iosTheme,\r\n material: materialTheme,\r\n minimal: minimalTheme,\r\n dark: darkTheme,\r\n}\r\n\r\n/**\r\n * 默认主题\r\n */\r\nexport const defaultTheme = miuiTheme\r\n"],"mappings":"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"}
|
package/es/themes/types.d.ts
CHANGED
|
@@ -111,8 +111,8 @@ export interface ThemeCSSVariables {
|
|
|
111
111
|
'--keyboard-fn-font-size': string;
|
|
112
112
|
'--keyboard-padding': string;
|
|
113
113
|
'--keyboard-key-shadow': string;
|
|
114
|
+
'--keyboard-mask-bg'?: string;
|
|
114
115
|
'--keyboard-transition-duration': string;
|
|
115
116
|
'--keyboard-border-color'?: string;
|
|
116
117
|
'--keyboard-divider-color'?: string;
|
|
117
118
|
}
|
|
118
|
-
//# sourceMappingURL=types.d.ts.map
|
package/es/themes/types.js
CHANGED
package/es/types/index.d.ts
CHANGED
package/es/types/index.js
CHANGED
package/es/types/keyboard.d.ts
CHANGED
package/es/types/keyboard.js
CHANGED
package/es/types/position.d.ts
CHANGED
package/es/types/position.js
CHANGED
package/es/utils/device.d.ts
CHANGED
package/es/utils/device.js
CHANGED
|
@@ -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
|
* ***********************************
|
|
@@ -12,10 +12,7 @@ const MOBILE_UA_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Oper
|
|
|
12
12
|
let cachedIsTouchDevice = null;
|
|
13
13
|
function isTouchDevice() {
|
|
14
14
|
if (cachedIsTouchDevice !== null) return cachedIsTouchDevice;
|
|
15
|
-
if (typeof window === "undefined")
|
|
16
|
-
cachedIsTouchDevice = false;
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
15
|
+
if (typeof window === "undefined") return false;
|
|
19
16
|
cachedIsTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
|
20
17
|
return cachedIsTouchDevice;
|
|
21
18
|
}
|
|
@@ -75,7 +72,14 @@ function getSafeAreaInsets() {
|
|
|
75
72
|
pointer-events: none;
|
|
76
73
|
visibility: hidden;
|
|
77
74
|
`;
|
|
78
|
-
document.body
|
|
75
|
+
const body = document.body;
|
|
76
|
+
if (!body) return {
|
|
77
|
+
top: 0,
|
|
78
|
+
right: 0,
|
|
79
|
+
bottom: 0,
|
|
80
|
+
left: 0
|
|
81
|
+
};
|
|
82
|
+
body.appendChild(div);
|
|
79
83
|
const computed = getComputedStyle(div);
|
|
80
84
|
const safeArea = {
|
|
81
85
|
top: Number.parseFloat(computed.top) || 0,
|
|
@@ -83,7 +87,7 @@ function getSafeAreaInsets() {
|
|
|
83
87
|
bottom: Number.parseFloat(computed.bottom) || 0,
|
|
84
88
|
left: Number.parseFloat(computed.left) || 0
|
|
85
89
|
};
|
|
86
|
-
|
|
90
|
+
body.removeChild(div);
|
|
87
91
|
return safeArea;
|
|
88
92
|
}
|
|
89
93
|
function supportsVibrate() {
|