@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.
Files changed (339) hide show
  1. package/README.md +156 -37
  2. package/dist/index.js +1540 -1395
  3. package/dist/index.js.map +1 -1
  4. package/dist/index.min.js +2981 -1
  5. package/dist/index.min.js.map +1 -0
  6. package/es/.dts-output-manifest.json +1 -0
  7. package/es/.dts.tsbuildinfo +1 -0
  8. package/es/icons.d.ts +0 -1
  9. package/es/icons.js +1 -1
  10. package/es/icons.js.map +1 -1
  11. package/es/index.d.ts +3 -4
  12. package/es/index.js +15 -15
  13. package/es/layouts/amount.d.ts +0 -1
  14. package/es/layouts/amount.js +1 -1
  15. package/es/layouts/amount.js.map +1 -1
  16. package/es/layouts/bankcard.d.ts +0 -1
  17. package/es/layouts/bankcard.js +1 -1
  18. package/es/layouts/bankcard.js.map +1 -1
  19. package/es/layouts/email.d.ts +0 -1
  20. package/es/layouts/email.js +1 -1
  21. package/es/layouts/email.js.map +1 -1
  22. package/es/layouts/idcard.d.ts +0 -1
  23. package/es/layouts/idcard.js +1 -1
  24. package/es/layouts/idcard.js.map +1 -1
  25. package/es/layouts/index.d.ts +11 -12
  26. package/es/layouts/index.js +23 -23
  27. package/es/layouts/index.js.map +1 -1
  28. package/es/layouts/license-plate.d.ts +0 -1
  29. package/es/layouts/license-plate.js +1 -1
  30. package/es/layouts/license-plate.js.map +1 -1
  31. package/es/layouts/number.d.ts +0 -1
  32. package/es/layouts/number.js +1 -1
  33. package/es/layouts/number.js.map +1 -1
  34. package/es/layouts/phone.d.ts +0 -1
  35. package/es/layouts/phone.js +1 -1
  36. package/es/layouts/phone.js.map +1 -1
  37. package/es/layouts/punct.d.ts +0 -1
  38. package/es/layouts/punct.js +1 -1
  39. package/es/layouts/punct.js.map +1 -1
  40. package/es/layouts/qwerty.d.ts +0 -1
  41. package/es/layouts/qwerty.js +1 -1
  42. package/es/layouts/qwerty.js.map +1 -1
  43. package/es/layouts/search.d.ts +0 -1
  44. package/es/layouts/search.js +1 -1
  45. package/es/layouts/search.js.map +1 -1
  46. package/es/layouts/symbol.d.ts +0 -1
  47. package/es/layouts/symbol.js +1 -1
  48. package/es/layouts/symbol.js.map +1 -1
  49. package/es/layouts/url.d.ts +0 -1
  50. package/es/layouts/url.js +1 -1
  51. package/es/layouts/url.js.map +1 -1
  52. package/es/layouts/verifycode.d.ts +0 -1
  53. package/es/layouts/verifycode.js +1 -1
  54. package/es/layouts/verifycode.js.map +1 -1
  55. package/es/managers/index.d.ts +1 -2
  56. package/es/managers/index.js +1 -1
  57. package/es/managers/keyboard-manager.d.ts +2 -1
  58. package/es/managers/keyboard-manager.js +79 -22
  59. package/es/managers/keyboard-manager.js.map +1 -1
  60. package/es/managers/keyboard-renderer.d.ts +3 -2
  61. package/es/managers/keyboard-renderer.js +81 -39
  62. package/es/managers/keyboard-renderer.js.map +1 -1
  63. package/es/managers/position-manager.d.ts +4 -3
  64. package/es/managers/position-manager.js +42 -21
  65. package/es/managers/position-manager.js.map +1 -1
  66. package/es/themes/index.d.ts +1 -2
  67. package/es/themes/index.js +2 -1
  68. package/es/themes/index.js.map +1 -1
  69. package/es/themes/presets.d.ts +0 -1
  70. package/es/themes/presets.js +1 -1
  71. package/es/themes/presets.js.map +1 -1
  72. package/es/themes/types.d.ts +1 -1
  73. package/es/themes/types.js +1 -1
  74. package/es/types/index.d.ts +0 -1
  75. package/es/types/index.js +1 -1
  76. package/es/types/keyboard.d.ts +0 -1
  77. package/es/types/keyboard.js +1 -1
  78. package/es/types/position.d.ts +0 -1
  79. package/es/types/position.js +1 -1
  80. package/es/utils/device.d.ts +0 -1
  81. package/es/utils/device.js +11 -7
  82. package/es/utils/device.js.map +1 -1
  83. package/es/utils/dom.d.ts +4 -1
  84. package/es/utils/dom.js +25 -7
  85. package/es/utils/dom.js.map +1 -1
  86. package/es/utils/event-emitter.d.ts +0 -1
  87. package/es/utils/event-emitter.js +2 -1
  88. package/es/utils/event-emitter.js.map +1 -1
  89. package/es/utils/formatters.d.ts +0 -1
  90. package/es/utils/formatters.js +4 -4
  91. package/es/utils/formatters.js.map +1 -1
  92. package/es/utils/index.d.ts +2 -3
  93. package/es/utils/index.js +5 -5
  94. package/es/utils/random.d.ts +4 -1
  95. package/es/utils/random.js +2 -2
  96. package/es/utils/random.js.map +1 -1
  97. package/esm/icons.js +1 -1
  98. package/esm/icons.js.map +1 -1
  99. package/esm/index.js +15 -15
  100. package/esm/layouts/amount.js +1 -1
  101. package/esm/layouts/amount.js.map +1 -1
  102. package/esm/layouts/bankcard.js +1 -1
  103. package/esm/layouts/bankcard.js.map +1 -1
  104. package/esm/layouts/email.js +1 -1
  105. package/esm/layouts/email.js.map +1 -1
  106. package/esm/layouts/idcard.js +1 -1
  107. package/esm/layouts/idcard.js.map +1 -1
  108. package/esm/layouts/index.js +23 -23
  109. package/esm/layouts/index.js.map +1 -1
  110. package/esm/layouts/license-plate.js +1 -1
  111. package/esm/layouts/license-plate.js.map +1 -1
  112. package/esm/layouts/number.js +1 -1
  113. package/esm/layouts/number.js.map +1 -1
  114. package/esm/layouts/phone.js +1 -1
  115. package/esm/layouts/phone.js.map +1 -1
  116. package/esm/layouts/punct.js +1 -1
  117. package/esm/layouts/punct.js.map +1 -1
  118. package/esm/layouts/qwerty.js +1 -1
  119. package/esm/layouts/qwerty.js.map +1 -1
  120. package/esm/layouts/search.js +1 -1
  121. package/esm/layouts/search.js.map +1 -1
  122. package/esm/layouts/symbol.js +1 -1
  123. package/esm/layouts/symbol.js.map +1 -1
  124. package/esm/layouts/url.js +1 -1
  125. package/esm/layouts/url.js.map +1 -1
  126. package/esm/layouts/verifycode.js +1 -1
  127. package/esm/layouts/verifycode.js.map +1 -1
  128. package/esm/managers/index.js +1 -1
  129. package/esm/managers/keyboard-manager.js +79 -22
  130. package/esm/managers/keyboard-manager.js.map +1 -1
  131. package/esm/managers/keyboard-renderer.js +81 -39
  132. package/esm/managers/keyboard-renderer.js.map +1 -1
  133. package/esm/managers/position-manager.js +42 -21
  134. package/esm/managers/position-manager.js.map +1 -1
  135. package/esm/themes/index.js +2 -1
  136. package/esm/themes/index.js.map +1 -1
  137. package/esm/themes/presets.js +1 -1
  138. package/esm/themes/presets.js.map +1 -1
  139. package/esm/utils/device.js +11 -7
  140. package/esm/utils/device.js.map +1 -1
  141. package/esm/utils/dom.js +25 -7
  142. package/esm/utils/dom.js.map +1 -1
  143. package/esm/utils/event-emitter.js +2 -1
  144. package/esm/utils/event-emitter.js.map +1 -1
  145. package/esm/utils/formatters.js +4 -4
  146. package/esm/utils/formatters.js.map +1 -1
  147. package/esm/utils/random.js +2 -2
  148. package/esm/utils/random.js.map +1 -1
  149. package/lib/icons.cjs +1 -1
  150. package/lib/icons.cjs.map +1 -1
  151. package/{esm/icons.d.ts → lib/icons.d.cts} +0 -1
  152. package/lib/index.cjs +14 -13
  153. package/{esm/index.d.ts → lib/index.d.cts} +3 -4
  154. package/lib/layouts/amount.cjs +1 -1
  155. package/lib/layouts/amount.cjs.map +1 -1
  156. package/{esm/layouts/amount.d.ts → lib/layouts/amount.d.cts} +0 -1
  157. package/lib/layouts/bankcard.cjs +1 -1
  158. package/lib/layouts/bankcard.cjs.map +1 -1
  159. package/{esm/layouts/bankcard.d.ts → lib/layouts/bankcard.d.cts} +0 -1
  160. package/lib/layouts/email.cjs +1 -1
  161. package/lib/layouts/email.cjs.map +1 -1
  162. package/lib/layouts/{email.d.ts → email.d.cts} +0 -1
  163. package/lib/layouts/idcard.cjs +1 -1
  164. package/lib/layouts/idcard.cjs.map +1 -1
  165. package/{esm/layouts/idcard.d.ts → lib/layouts/idcard.d.cts} +0 -1
  166. package/lib/layouts/index.cjs +23 -23
  167. package/lib/layouts/index.cjs.map +1 -1
  168. package/{esm/layouts/index.d.ts → lib/layouts/index.d.cts} +11 -12
  169. package/lib/layouts/license-plate.cjs +1 -1
  170. package/lib/layouts/license-plate.cjs.map +1 -1
  171. package/{esm/layouts/license-plate.d.ts → lib/layouts/license-plate.d.cts} +0 -1
  172. package/lib/layouts/number.cjs +1 -1
  173. package/lib/layouts/number.cjs.map +1 -1
  174. package/{esm/layouts/number.d.ts → lib/layouts/number.d.cts} +0 -1
  175. package/lib/layouts/phone.cjs +1 -1
  176. package/lib/layouts/phone.cjs.map +1 -1
  177. package/lib/layouts/{phone.d.ts → phone.d.cts} +0 -1
  178. package/lib/layouts/punct.cjs +1 -1
  179. package/lib/layouts/punct.cjs.map +1 -1
  180. package/lib/layouts/{punct.d.ts → punct.d.cts} +0 -1
  181. package/lib/layouts/qwerty.cjs +1 -1
  182. package/lib/layouts/qwerty.cjs.map +1 -1
  183. package/{esm/layouts/qwerty.d.ts → lib/layouts/qwerty.d.cts} +0 -1
  184. package/lib/layouts/search.cjs +1 -1
  185. package/lib/layouts/search.cjs.map +1 -1
  186. package/{esm/layouts/search.d.ts → lib/layouts/search.d.cts} +0 -1
  187. package/lib/layouts/symbol.cjs +1 -1
  188. package/lib/layouts/symbol.cjs.map +1 -1
  189. package/{esm/layouts/symbol.d.ts → lib/layouts/symbol.d.cts} +0 -1
  190. package/lib/layouts/url.cjs +1 -1
  191. package/lib/layouts/url.cjs.map +1 -1
  192. package/{esm/layouts/url.d.ts → lib/layouts/url.d.cts} +0 -1
  193. package/lib/layouts/verifycode.cjs +1 -1
  194. package/lib/layouts/verifycode.cjs.map +1 -1
  195. package/lib/layouts/{verifycode.d.ts → verifycode.d.cts} +0 -1
  196. package/lib/managers/index.cjs +1 -1
  197. package/lib/managers/{index.d.ts → index.d.cts} +1 -2
  198. package/lib/managers/keyboard-manager.cjs +79 -22
  199. package/lib/managers/keyboard-manager.cjs.map +1 -1
  200. package/lib/managers/{keyboard-manager.d.ts → keyboard-manager.d.cts} +2 -1
  201. package/lib/managers/keyboard-renderer.cjs +80 -38
  202. package/lib/managers/keyboard-renderer.cjs.map +1 -1
  203. package/lib/managers/{keyboard-renderer.d.ts → keyboard-renderer.d.cts} +3 -2
  204. package/lib/managers/position-manager.cjs +42 -21
  205. package/lib/managers/position-manager.cjs.map +1 -1
  206. package/lib/managers/{position-manager.d.ts → position-manager.d.cts} +4 -3
  207. package/lib/themes/index.cjs +2 -1
  208. package/lib/themes/index.cjs.map +1 -1
  209. package/lib/themes/{index.d.ts → index.d.cts} +1 -2
  210. package/lib/themes/presets.cjs +1 -1
  211. package/lib/themes/presets.cjs.map +1 -1
  212. package/lib/themes/{presets.d.ts → presets.d.cts} +0 -1
  213. package/lib/themes/types.cjs +1 -1
  214. package/{esm/themes/types.d.ts → lib/themes/types.d.cts} +1 -1
  215. package/lib/types/index.cjs +1 -1
  216. package/{esm/types/index.d.ts → lib/types/index.d.cts} +0 -1
  217. package/lib/types/keyboard.cjs +1 -1
  218. package/{esm/types/keyboard.d.ts → lib/types/keyboard.d.cts} +0 -1
  219. package/lib/types/position.cjs +1 -1
  220. package/lib/types/{position.d.ts → position.d.cts} +0 -1
  221. package/lib/utils/device.cjs +11 -7
  222. package/lib/utils/device.cjs.map +1 -1
  223. package/{esm/utils/device.d.ts → lib/utils/device.d.cts} +0 -1
  224. package/lib/utils/dom.cjs +25 -6
  225. package/lib/utils/dom.cjs.map +1 -1
  226. package/{esm/utils/dom.d.ts → lib/utils/dom.d.cts} +4 -1
  227. package/lib/utils/event-emitter.cjs +2 -1
  228. package/lib/utils/event-emitter.cjs.map +1 -1
  229. package/{esm/utils/event-emitter.d.ts → lib/utils/event-emitter.d.cts} +0 -1
  230. package/lib/utils/formatters.cjs +4 -4
  231. package/lib/utils/formatters.cjs.map +1 -1
  232. package/{esm/utils/formatters.d.ts → lib/utils/formatters.d.cts} +0 -1
  233. package/lib/utils/index.cjs +4 -3
  234. package/{esm/utils/index.d.ts → lib/utils/index.d.cts} +2 -3
  235. package/lib/utils/random.cjs +2 -2
  236. package/lib/utils/random.cjs.map +1 -1
  237. package/{esm/utils/random.d.ts → lib/utils/random.d.cts} +4 -1
  238. package/package.json +28 -14
  239. package/es/icons.d.ts.map +0 -1
  240. package/es/index.d.ts.map +0 -1
  241. package/es/layouts/amount.d.ts.map +0 -1
  242. package/es/layouts/bankcard.d.ts.map +0 -1
  243. package/es/layouts/email.d.ts.map +0 -1
  244. package/es/layouts/idcard.d.ts.map +0 -1
  245. package/es/layouts/index.d.ts.map +0 -1
  246. package/es/layouts/license-plate.d.ts.map +0 -1
  247. package/es/layouts/number.d.ts.map +0 -1
  248. package/es/layouts/phone.d.ts.map +0 -1
  249. package/es/layouts/punct.d.ts.map +0 -1
  250. package/es/layouts/qwerty.d.ts.map +0 -1
  251. package/es/layouts/search.d.ts.map +0 -1
  252. package/es/layouts/symbol.d.ts.map +0 -1
  253. package/es/layouts/url.d.ts.map +0 -1
  254. package/es/layouts/verifycode.d.ts.map +0 -1
  255. package/es/managers/index.d.ts.map +0 -1
  256. package/es/managers/keyboard-manager.d.ts.map +0 -1
  257. package/es/managers/keyboard-renderer.d.ts.map +0 -1
  258. package/es/managers/position-manager.d.ts.map +0 -1
  259. package/es/themes/index.d.ts.map +0 -1
  260. package/es/themes/presets.d.ts.map +0 -1
  261. package/es/themes/types.d.ts.map +0 -1
  262. package/es/types/index.d.ts.map +0 -1
  263. package/es/types/keyboard.d.ts.map +0 -1
  264. package/es/types/position.d.ts.map +0 -1
  265. package/es/utils/device.d.ts.map +0 -1
  266. package/es/utils/dom.d.ts.map +0 -1
  267. package/es/utils/event-emitter.d.ts.map +0 -1
  268. package/es/utils/formatters.d.ts.map +0 -1
  269. package/es/utils/index.d.ts.map +0 -1
  270. package/es/utils/random.d.ts.map +0 -1
  271. package/esm/icons.d.ts.map +0 -1
  272. package/esm/index.d.ts.map +0 -1
  273. package/esm/layouts/amount.d.ts.map +0 -1
  274. package/esm/layouts/bankcard.d.ts.map +0 -1
  275. package/esm/layouts/email.d.ts +0 -11
  276. package/esm/layouts/email.d.ts.map +0 -1
  277. package/esm/layouts/idcard.d.ts.map +0 -1
  278. package/esm/layouts/index.d.ts.map +0 -1
  279. package/esm/layouts/license-plate.d.ts.map +0 -1
  280. package/esm/layouts/number.d.ts.map +0 -1
  281. package/esm/layouts/phone.d.ts +0 -15
  282. package/esm/layouts/phone.d.ts.map +0 -1
  283. package/esm/layouts/punct.d.ts +0 -7
  284. package/esm/layouts/punct.d.ts.map +0 -1
  285. package/esm/layouts/qwerty.d.ts.map +0 -1
  286. package/esm/layouts/search.d.ts.map +0 -1
  287. package/esm/layouts/symbol.d.ts.map +0 -1
  288. package/esm/layouts/url.d.ts.map +0 -1
  289. package/esm/layouts/verifycode.d.ts +0 -15
  290. package/esm/layouts/verifycode.d.ts.map +0 -1
  291. package/esm/managers/index.d.ts +0 -8
  292. package/esm/managers/index.d.ts.map +0 -1
  293. package/esm/managers/keyboard-manager.d.ts +0 -130
  294. package/esm/managers/keyboard-manager.d.ts.map +0 -1
  295. package/esm/managers/keyboard-renderer.d.ts +0 -115
  296. package/esm/managers/keyboard-renderer.d.ts.map +0 -1
  297. package/esm/managers/position-manager.d.ts +0 -72
  298. package/esm/managers/position-manager.d.ts.map +0 -1
  299. package/esm/themes/index.d.ts +0 -70
  300. package/esm/themes/index.d.ts.map +0 -1
  301. package/esm/themes/presets.d.ts +0 -39
  302. package/esm/themes/presets.d.ts.map +0 -1
  303. package/esm/themes/types.d.ts.map +0 -1
  304. package/esm/themes/types.js +0 -10
  305. package/esm/types/index.d.ts.map +0 -1
  306. package/esm/types/index.js +0 -10
  307. package/esm/types/keyboard.d.ts.map +0 -1
  308. package/esm/types/keyboard.js +0 -10
  309. package/esm/types/position.d.ts +0 -139
  310. package/esm/types/position.d.ts.map +0 -1
  311. package/esm/types/position.js +0 -10
  312. package/esm/utils/device.d.ts.map +0 -1
  313. package/esm/utils/dom.d.ts.map +0 -1
  314. package/esm/utils/event-emitter.d.ts.map +0 -1
  315. package/esm/utils/formatters.d.ts.map +0 -1
  316. package/esm/utils/index.d.ts.map +0 -1
  317. package/esm/utils/index.js +0 -16
  318. package/esm/utils/random.d.ts.map +0 -1
  319. package/lib/icons.d.ts +0 -58
  320. package/lib/index.d.ts +0 -12
  321. package/lib/layouts/amount.d.ts +0 -14
  322. package/lib/layouts/bankcard.d.ts +0 -15
  323. package/lib/layouts/idcard.d.ts +0 -16
  324. package/lib/layouts/index.d.ts +0 -54
  325. package/lib/layouts/license-plate.d.ts +0 -35
  326. package/lib/layouts/number.d.ts +0 -14
  327. package/lib/layouts/qwerty.d.ts +0 -10
  328. package/lib/layouts/search.d.ts +0 -7
  329. package/lib/layouts/symbol.d.ts +0 -14
  330. package/lib/layouts/url.d.ts +0 -7
  331. package/lib/themes/types.d.ts +0 -118
  332. package/lib/types/index.d.ts +0 -7
  333. package/lib/types/keyboard.d.ts +0 -253
  334. package/lib/utils/device.d.ts +0 -45
  335. package/lib/utils/dom.d.ts +0 -69
  336. package/lib/utils/event-emitter.d.ts +0 -52
  337. package/lib/utils/formatters.d.ts +0 -51
  338. package/lib/utils/index.d.ts +0 -10
  339. package/lib/utils/random.d.ts +0 -71
@@ -2,18 +2,19 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:29 *
5
+ * Build time: 2024-10-12 17:29:15 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
9
9
  */
10
10
  import { isMobile } from "../utils/device.js";
11
- import { onClickOutside } from "../utils/dom.js";
11
+ import { onClickOutside, sanitizeSvgIcon } from "../utils/dom.js";
12
12
  import { KeyboardManager } from "./keyboard-manager.js";
13
- import { PositionManager } from "./position-manager.js";
14
13
  import { defaultTheme } from "../themes/presets.js";
15
14
  import { getTheme, themeToCSS } from "../themes/index.js";
15
+ import { PositionManager } from "./position-manager.js";
16
16
  //#region src/managers/keyboard-renderer.ts
17
+ const rendererStyles = /* @__PURE__ */ new WeakMap();
17
18
  var KeyboardRenderer = class {
18
19
  constructor(options = {}) {
19
20
  /** DOM 元素 */
@@ -24,10 +25,14 @@ var KeyboardRenderer = class {
24
25
  this.targetEl = null;
25
26
  /** 清理函数 */
26
27
  this.cleanupFns = [];
28
+ this.inputCleanupFns = /* @__PURE__ */ new Set();
29
+ this.styleDocument = null;
27
30
  /** 是否已销毁 */
28
31
  this.destroyed = false;
29
32
  this.options = options;
30
- this.container = options.container || document.body;
33
+ const container = options.container ?? (typeof document !== "undefined" ? document.body : null);
34
+ if (!container) throw new Error("[KeyboardRenderer] A DOM container is required in non-browser environments");
35
+ this.container = container;
31
36
  this.manager = new KeyboardManager(options);
32
37
  this.positionManager = new PositionManager(options.position);
33
38
  if (typeof options.theme === "string") this.theme = getTheme(options.theme);
@@ -49,17 +54,12 @@ var KeyboardRenderer = class {
49
54
  this.manager.on("confirm", ({ value }) => {
50
55
  this.options.onConfirm?.(value);
51
56
  });
52
- this.manager.on("switch", () => {
53
- if (this.manager.getState().visible) this.render();
54
- });
55
- this.manager.on("shift", () => {
56
- if (this.manager.getState().visible) this.render();
57
- });
58
57
  }
59
58
  /**
60
59
  * 打开键盘
61
60
  */
62
61
  open(target) {
62
+ if (this.destroyed) return;
63
63
  this.targetEl = target ?? null;
64
64
  this.manager.show(target);
65
65
  }
@@ -67,19 +67,23 @@ var KeyboardRenderer = class {
67
67
  * 关闭键盘
68
68
  */
69
69
  close() {
70
+ if (this.destroyed) return;
71
+ const wasVisible = this.manager.getState().visible;
70
72
  this.manager.hide();
71
- this.options.onClose?.();
73
+ if (wasVisible) this.options.onClose?.();
72
74
  }
73
75
  /**
74
76
  * 切换键盘类型
75
77
  */
76
78
  switchType(type) {
79
+ if (this.destroyed) return;
77
80
  this.manager.switchType(type);
78
81
  }
79
82
  /**
80
83
  * 设置主题
81
84
  */
82
85
  setTheme(theme) {
86
+ if (this.destroyed) return;
83
87
  if (typeof theme === "string") this.theme = getTheme(theme);
84
88
  else this.theme = theme;
85
89
  if (this.manager.getState().visible) this.render();
@@ -94,12 +98,14 @@ var KeyboardRenderer = class {
94
98
  * 设置值
95
99
  */
96
100
  setValue(value) {
101
+ if (this.destroyed) return;
97
102
  this.manager.setValue(value);
98
103
  }
99
104
  /**
100
105
  * 渲染键盘 DOM
101
106
  */
102
107
  render() {
108
+ if (this.destroyed) return;
103
109
  const layout = this.manager.getLayout();
104
110
  if (!layout) return;
105
111
  this.unmount();
@@ -109,14 +115,18 @@ var KeyboardRenderer = class {
109
115
  if (isMobileDevice) {
110
116
  this.maskEl = document.createElement("div");
111
117
  this.maskEl.className = "lk-mask";
112
- this.maskEl.addEventListener("click", () => this.close());
118
+ this.maskEl.addEventListener("click", () => {
119
+ if (this.positionManager.isMaskClosable()) this.close();
120
+ });
113
121
  this.rootEl.appendChild(this.maskEl);
114
122
  }
115
123
  this.keyboardEl = document.createElement("div");
116
124
  this.keyboardEl.className = `lk-popup lk-popup--${isMobileDevice ? "mobile" : "desktop"}`;
117
125
  const cssVars = themeToCSS(this.theme);
126
+ const keyboardElement = this.keyboardEl;
127
+ if (!keyboardElement) return;
118
128
  Object.entries(cssVars).forEach(([key, value]) => {
119
- if (value) this.keyboardEl.style.setProperty(key, value);
129
+ if (value) keyboardElement.style.setProperty(key, value);
120
130
  });
121
131
  const contentEl = this.renderKeyboard(layout);
122
132
  this.keyboardEl.appendChild(contentEl);
@@ -145,7 +155,7 @@ var KeyboardRenderer = class {
145
155
  });
146
156
  if (this.options.closeOnClickOutside !== false && this.keyboardEl) {
147
157
  const cleanup = onClickOutside(this.keyboardEl, (event) => {
148
- if (this.targetEl && this.targetEl.contains(event.target)) return;
158
+ if (this.targetEl && event.target instanceof Node && this.targetEl.contains(event.target)) return;
149
159
  this.close();
150
160
  });
151
161
  this.cleanupFns.push(cleanup);
@@ -177,8 +187,11 @@ var KeyboardRenderer = class {
177
187
  btnEl.type = "button";
178
188
  btnEl.className = this.getKeyClass(key, state.isUpperCase);
179
189
  if (key.disabled) btnEl.disabled = true;
180
- if (key.icon) btnEl.innerHTML = key.icon;
181
- else btnEl.textContent = typeof key.label === "string" ? key.label : state.isUpperCase ? key.label.upper : key.label.lower;
190
+ if (key.icon) {
191
+ const safeIcon = sanitizeSvgIcon(key.icon);
192
+ if (safeIcon) btnEl.innerHTML = safeIcon;
193
+ else btnEl.textContent = key.icon;
194
+ } else btnEl.textContent = typeof key.label === "string" ? key.label : state.isUpperCase ? key.label.upper : key.label.lower;
182
195
  const handler = (e) => {
183
196
  e.preventDefault();
184
197
  e.stopPropagation();
@@ -218,49 +231,67 @@ var KeyboardRenderer = class {
218
231
  * 注入样式(单例)
219
232
  */
220
233
  injectStyles() {
221
- if (document.getElementById("lk-renderer-styles")) return;
222
- const style = document.createElement("style");
223
- style.id = "lk-renderer-styles";
234
+ const styleDocument = this.container.ownerDocument;
235
+ if (this.styleDocument === styleDocument) return;
236
+ const existing = rendererStyles.get(styleDocument);
237
+ if (existing) {
238
+ existing.users += 1;
239
+ this.styleDocument = styleDocument;
240
+ return;
241
+ }
242
+ const style = styleDocument.createElement("style");
224
243
  style.textContent = `
225
- .lk-renderer-root { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
226
- .lk-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 9999; opacity: 0; transition: opacity 0.3s ease; }
244
+ .lk-renderer-root { font-family: var(--ld-font-family); }
245
+ .lk-mask { position: fixed; inset: 0; z-index: 9999; background: var(--keyboard-mask-bg); opacity: 0; transition: opacity var(--ld-duration-fast); }
227
246
  .lk-mask--visible { opacity: 1; }
228
- .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; }
247
+ .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); }
229
248
  .lk-popup--visible { transform: translateY(0); opacity: 1; }
230
- .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; }
231
- .lk-popup--mobile { border-top-left-radius: 16px; border-top-right-radius: 16px; padding-bottom: env(safe-area-inset-bottom, 0); }
232
- .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; }
233
- .lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap, 6px); padding: 0 2px; }
249
+ .lk-popup--desktop { min-width: var(--ld-px-280); border-radius: var(--ld-radius-lg); box-shadow: var(--ld-shadow-lg, none); }
250
+ .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)); }
251
+ .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); }
252
+ .lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap); padding: 0 var(--ld-px-2); }
234
253
  .lk-row.numpad-row { justify-content: stretch; }
235
254
  .lk-row.numpad-row .lk-key-wrapper { flex: 1; }
236
255
  .lk-key-wrapper { position: relative; display: inline-flex; }
237
- .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; }
238
- .lk-key:active { background: var(--keyboard-key-active-bg, #e0e0e0); transform: scale(0.95); }
256
+ .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); }
257
+ .lk-key:active { background: var(--keyboard-key-active-bg); transform: scale(0.95); }
239
258
  .lk-key--disabled { opacity: 0.4; cursor: not-allowed; }
240
- .lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg, #fff); }
241
- .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); }
242
- .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); }
243
- .lk-key--active { background: var(--keyboard-key-active-bg, #007aff) !important; color: #fff !important; }
244
- .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); }
245
- .lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg, #0062cc); }
246
- .lk-key--space { background: var(--keyboard-key-bg, #fff); }
259
+ .lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg); }
260
+ .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); }
261
+ .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); }
262
+ .lk-key--active { background: var(--keyboard-key-active-bg) !important; color: var(--ld-text-inverse) !important; }
263
+ .lk-key--done, .lk-key--enter { background: var(--keyboard-primary-bg); color: var(--keyboard-primary-color); font-size: var(--keyboard-fn-font-size); }
264
+ .lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg); }
265
+ .lk-key--space { background: var(--keyboard-key-bg); }
247
266
  .lk-key svg { pointer-events: none; }
248
267
  `;
249
- document.head.appendChild(style);
268
+ style.id = "lk-renderer-styles";
269
+ styleDocument.head?.appendChild(style);
270
+ rendererStyles.set(styleDocument, {
271
+ element: style,
272
+ users: 1
273
+ });
274
+ this.styleDocument = styleDocument;
250
275
  }
251
276
  /**
252
277
  * 绑定到输入框(聚焦时自动打开键盘)
253
278
  */
254
279
  bindInput(input) {
280
+ if (this.destroyed) return () => void 0;
255
281
  const handleFocus = () => this.open(input);
256
282
  input.addEventListener("focus", handleFocus);
257
283
  input.setAttribute("readonly", "readonly");
258
284
  input.style.caretColor = "transparent";
259
- return () => {
285
+ let cleaned = false;
286
+ const cleanup = () => {
287
+ if (cleaned) return;
288
+ cleaned = true;
260
289
  input.removeEventListener("focus", handleFocus);
261
290
  input.removeAttribute("readonly");
262
291
  input.style.caretColor = "";
263
292
  };
293
+ this.inputCleanupFns.add(cleanup);
294
+ return cleanup;
264
295
  }
265
296
  /**
266
297
  * 销毁渲染器
@@ -269,9 +300,20 @@ var KeyboardRenderer = class {
269
300
  if (this.destroyed) return;
270
301
  this.destroyed = true;
271
302
  this.unmount();
303
+ this.inputCleanupFns.forEach((cleanup) => cleanup());
304
+ this.inputCleanupFns.clear();
272
305
  this.manager.destroy();
273
- const styleEl = document.getElementById("lk-renderer-styles");
274
- if (styleEl) styleEl.remove();
306
+ if (this.styleDocument) {
307
+ const entry = rendererStyles.get(this.styleDocument);
308
+ if (entry) {
309
+ entry.users -= 1;
310
+ if (entry.users <= 0) {
311
+ entry.element.remove();
312
+ rendererStyles.delete(this.styleDocument);
313
+ }
314
+ }
315
+ this.styleDocument = null;
316
+ }
275
317
  }
276
318
  };
277
319
  function createKeyboardRenderer(options) {
@@ -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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"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:29 *
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
- Object.assign(this.config, config);
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 { ...this.config };
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 - popupSize.height - safeAreaBottom
90
+ y: Math.max(0, viewport.height - visibleHeight - safeAreaBottom)
70
91
  },
71
92
  placement: "bottom-sheet",
72
93
  flipped: false,
73
- maxHeight: viewport.height * .6,
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
- if (position.x < viewportPadding) position.x = viewportPadding;
128
- if (position.x + popupSize.width > viewport.width - viewportPadding) position.x = viewport.width - popupSize.width - viewportPadding;
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(200, maxHeight),
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(-10px)",
170
+ transform: "translateY(calc(-1 * var(--ld-px-10)))",
150
171
  opacity: "0"
151
172
  };
152
173
  case "top": return {
153
- transform: "translateY(10px)",
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(placement) {
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(placement) {
172
- return this.getInitialStyles(placement);
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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"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:29 *
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
@@ -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 './types'\nexport * from './presets'\n\n/**\n * 自定义主题存储\n */\nconst customThemes: Map<string, ThemeConfig> = new Map()\n\n/**\n * 当前激活的主题\n */\nlet currentTheme: ThemeConfig = defaultTheme\n\n/**\n * 获取主题配置\n * @param name - 主题名称\n * @returns 主题配置,如果不存在则返回默认主题\n */\nexport function getTheme(name: ThemeName | string): ThemeConfig {\n return customThemes.get(name) || presetThemes[name] || defaultTheme\n}\n\n/**\n * 注册自定义主题\n * @param theme - 主题配置\n */\nexport function registerTheme(theme: ThemeConfig): void {\n customThemes.set(theme.name, theme)\n}\n\n/**\n * 获取所有可用主题\n * @returns 主题名称列表\n */\nexport function getAvailableThemes(): string[] {\n return [...Object.keys(presetThemes), ...customThemes.keys()]\n}\n\n/**\n * 获取所有主题配置\n * @returns 主题配置列表\n */\nexport function getAllThemes(): ThemeConfig[] {\n return [\n ...Object.values(presetThemes),\n ...customThemes.values(),\n ]\n}\n\n/**\n * 设置当前主题\n * @param name - 主题名称\n */\nexport function setCurrentTheme(name: ThemeName | string): ThemeConfig {\n currentTheme = getTheme(name)\n return currentTheme\n}\n\n/**\n * 获取当前主题\n */\nexport function getCurrentTheme(): ThemeConfig {\n return currentTheme\n}\n\n/**\n * 将主题配置转换为 CSS 变量\n * @param theme - 主题配置\n * @returns CSS 变量对象\n */\nexport function themeToCSS(theme: ThemeConfig): ThemeCSSVariables {\n return {\n '--keyboard-bg': theme.colors.background,\n '--keyboard-key-bg': theme.colors.keyBackground,\n '--keyboard-key-color': theme.colors.keyColor,\n '--keyboard-key-active-bg': theme.colors.keyActiveBackground,\n '--keyboard-fn-key-bg': theme.colors.fnKeyBackground,\n '--keyboard-fn-key-color': theme.colors.fnKeyColor,\n '--keyboard-fn-key-active-bg': theme.colors.fnKeyActiveBackground,\n '--keyboard-primary-bg': theme.colors.primaryBackground,\n '--keyboard-primary-active-bg': theme.colors.primaryActiveBackground,\n '--keyboard-primary-color': theme.colors.primaryColor,\n '--keyboard-key-height': `${theme.sizes.keyHeight}px`,\n '--keyboard-key-radius': `${theme.sizes.keyRadius}px`,\n '--keyboard-key-gap': `${theme.sizes.keyGap}px`,\n '--keyboard-font-size': `${theme.sizes.fontSize}px`,\n '--keyboard-fn-font-size': `${theme.sizes.fnFontSize}px`,\n '--keyboard-padding': `${theme.sizes.padding}px`,\n '--keyboard-key-shadow': theme.effects.keyShadow,\n '--keyboard-transition-duration': `${theme.effects.transitionDuration}ms`,\n '--keyboard-border-color': theme.colors.borderColor,\n '--keyboard-divider-color': theme.colors.dividerColor,\n }\n}\n\n/**\n * 将 CSS 变量应用到元素\n * @param element - 目标元素\n * @param theme - 主题配置\n */\nexport function applyTheme(element: HTMLElement, theme: ThemeConfig): void {\n const cssVars = themeToCSS(theme)\n Object.entries(cssVars).forEach(([key, value]) => {\n if (value) {\n element.style.setProperty(key, value)\n }\n })\n\n // 添加主题类名\n element.classList.remove('keyboard-theme-light', 'keyboard-theme-dark')\n element.classList.add(theme.isDark ? 'keyboard-theme-dark' : 'keyboard-theme-light')\n element.dataset.theme = theme.name\n}\n\n/**\n * 生成主题的内联样式字符串\n * @param theme - 主题配置\n * @returns 内联样式字符串\n */\nexport function generateThemeStyle(theme: ThemeConfig): string {\n const cssVars = themeToCSS(theme)\n return Object.entries(cssVars)\n .filter(([, value]) => value)\n .map(([key, value]) => `${key}: ${value}`)\n .join('; ')\n}\n\n/**\n * 合并主题配置\n * @param base - 基础主题\n * @param overrides - 覆盖配置\n * @returns 合并后的主题\n */\nexport function mergeTheme(\n base: ThemeConfig,\n overrides: Partial<ThemeConfig>,\n): ThemeConfig {\n return {\n ...base,\n ...overrides,\n colors: { ...base.colors, ...overrides.colors },\n sizes: { ...base.sizes, ...overrides.sizes },\n effects: { ...base.effects, ...overrides.effects },\n }\n}\n\n/**\n * 创建自定义主题\n * @param name - 主题名称\n * @param baseTheme - 基础主题名称\n * @param customization - 自定义配置\n */\nexport function createTheme(\n name: string,\n baseTheme: ThemeName | string,\n customization: Partial<Omit<ThemeConfig, 'name'>>,\n): ThemeConfig {\n const base = getTheme(baseTheme)\n const theme = mergeTheme(base, {\n ...customization,\n name,\n })\n registerTheme(theme)\n return theme\n}\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,CACL,GAAG,OAAO,OAAO,YAAY,GAC7B,GAAG,aAAa,OAAO,CACzB;AACF;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,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,IAAI,MAAM,SAAS,wBAAwB,sBAAsB;CACnF,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"}
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"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:29 *
5
+ * Build time: 2024-10-12 17:29:15 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************