@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
package/dist/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-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
  * ***********************************
@@ -11,110 +11,6 @@
11
11
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.LDesignKeyboardCore = {}));
12
12
  })(this, function(exports) {
13
13
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
14
- //#region src/layouts/qwerty.ts
15
- function createCharKey$4(char) {
16
- return {
17
- id: `char-${char.toLowerCase()}`,
18
- type: "char",
19
- label: {
20
- lower: char.toLowerCase(),
21
- upper: char.toUpperCase()
22
- },
23
- value: {
24
- lower: char.toLowerCase(),
25
- upper: char.toUpperCase()
26
- }
27
- };
28
- }
29
- const qwertyLayout = {
30
- type: "qwerty",
31
- name: "字母键盘",
32
- supportShift: true,
33
- defaultUpperCase: false,
34
- switchable: ["number", "symbol"],
35
- rows: [
36
- { keys: [
37
- createCharKey$4("Q"),
38
- createCharKey$4("W"),
39
- createCharKey$4("E"),
40
- createCharKey$4("R"),
41
- createCharKey$4("T"),
42
- createCharKey$4("Y"),
43
- createCharKey$4("U"),
44
- createCharKey$4("I"),
45
- createCharKey$4("O"),
46
- createCharKey$4("P")
47
- ] },
48
- { keys: [
49
- createCharKey$4("A"),
50
- createCharKey$4("S"),
51
- createCharKey$4("D"),
52
- createCharKey$4("F"),
53
- createCharKey$4("G"),
54
- createCharKey$4("H"),
55
- createCharKey$4("J"),
56
- createCharKey$4("K"),
57
- createCharKey$4("L")
58
- ] },
59
- { keys: [
60
- {
61
- id: "shift",
62
- type: "shift",
63
- label: "⇧",
64
- width: 1.5,
65
- className: "key-shift"
66
- },
67
- createCharKey$4("Z"),
68
- createCharKey$4("X"),
69
- createCharKey$4("C"),
70
- createCharKey$4("V"),
71
- createCharKey$4("B"),
72
- createCharKey$4("N"),
73
- createCharKey$4("M"),
74
- {
75
- id: "backspace",
76
- type: "backspace",
77
- label: "⌫",
78
- width: 1.5,
79
- className: "key-backspace"
80
- }
81
- ] },
82
- { keys: [
83
- {
84
- id: "switch-number",
85
- type: "tab",
86
- label: "123",
87
- width: 1.5,
88
- className: "key-switch",
89
- data: { targetType: "number" }
90
- },
91
- {
92
- id: "switch-symbol",
93
- type: "tab",
94
- label: "#+=",
95
- width: 1.25,
96
- className: "key-switch",
97
- data: { targetType: "symbol" }
98
- },
99
- {
100
- id: "space",
101
- type: "space",
102
- label: "空格",
103
- value: " ",
104
- width: 4,
105
- className: "key-space"
106
- },
107
- {
108
- id: "done",
109
- type: "done",
110
- label: "完成",
111
- width: 2,
112
- className: "key-done"
113
- }
114
- ] }
115
- ]
116
- };
117
- //#endregion
118
14
  //#region src/icons.ts
119
15
  const ICON_DELETE = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 5a2 2 0 0 0-1.344.519l-6.328 5.74a1 1 0 0 0 0 1.481l6.328 5.741A2 2 0 0 0 10 19h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z"/><path d="m12 9 6 6"/><path d="m18 9-6 6"/></svg>`;
120
16
  const ICON_CLEAR = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`;
@@ -137,21 +33,20 @@
137
33
  voice: ICON_VOICE
138
34
  };
139
35
  //#endregion
140
- //#region src/layouts/number.ts
36
+ //#region src/layouts/amount.ts
141
37
  function createNumberKey$6(num) {
142
38
  return {
143
- id: `num-${num}`,
39
+ id: `amount-${num}`,
144
40
  type: "char",
145
41
  label: num,
146
42
  value: num,
147
43
  className: "key-num"
148
44
  };
149
45
  }
150
- const numberLayout = {
151
- type: "number",
152
- name: "数字键盘",
46
+ const amountLayout = {
47
+ type: "amount",
48
+ name: "金额键盘",
153
49
  supportShift: false,
154
- switchable: ["qwerty", "symbol"],
155
50
  rows: [
156
51
  {
157
52
  className: "numpad-row",
@@ -190,10 +85,11 @@
190
85
  createNumberKey$6("8"),
191
86
  createNumberKey$6("9"),
192
87
  {
193
- id: "close",
194
- type: "close",
195
- label: "关闭",
196
- className: "key-numpad-fn"
88
+ id: "double-zero",
89
+ type: "char",
90
+ label: "00",
91
+ value: "00",
92
+ className: "key-num"
197
93
  }
198
94
  ]
199
95
  },
@@ -217,13 +113,16 @@
217
113
  }
218
114
  ]
219
115
  }
220
- ]
116
+ ],
117
+ data: {
118
+ inputType: "amount",
119
+ decimalPlaces: 2
120
+ }
221
121
  };
222
- const integerLayout = {
223
- type: "integer",
224
- name: "整数键盘",
122
+ const amountSimpleLayout = {
123
+ type: "amount-simple",
124
+ name: "金额键盘",
225
125
  supportShift: false,
226
- switchable: ["qwerty"],
227
126
  rows: [
228
127
  {
229
128
  className: "numpad-row",
@@ -269,6 +168,92 @@
269
168
  }
270
169
  ]
271
170
  },
171
+ {
172
+ className: "numpad-row",
173
+ keys: [
174
+ {
175
+ id: "decimal",
176
+ type: "char",
177
+ label: ".",
178
+ value: ".",
179
+ className: "key-num"
180
+ },
181
+ createNumberKey$6("0"),
182
+ {
183
+ id: "done",
184
+ type: "done",
185
+ label: "确定",
186
+ width: 2,
187
+ className: "key-numpad-done"
188
+ }
189
+ ]
190
+ }
191
+ ],
192
+ data: {
193
+ inputType: "amount",
194
+ decimalPlaces: 2
195
+ }
196
+ };
197
+ //#endregion
198
+ //#region src/layouts/bankcard.ts
199
+ function createNumberKey$5(num) {
200
+ return {
201
+ id: `bankcard-${num}`,
202
+ type: "char",
203
+ label: num,
204
+ value: num,
205
+ className: "key-num"
206
+ };
207
+ }
208
+ const bankcardLayout = {
209
+ type: "bankcard",
210
+ name: "银行卡键盘",
211
+ supportShift: false,
212
+ rows: [
213
+ {
214
+ className: "numpad-row",
215
+ keys: [
216
+ createNumberKey$5("1"),
217
+ createNumberKey$5("2"),
218
+ createNumberKey$5("3"),
219
+ {
220
+ id: "backspace",
221
+ type: "backspace",
222
+ icon: ICON_DELETE,
223
+ label: "",
224
+ className: "key-numpad-fn"
225
+ }
226
+ ]
227
+ },
228
+ {
229
+ className: "numpad-row",
230
+ keys: [
231
+ createNumberKey$5("4"),
232
+ createNumberKey$5("5"),
233
+ createNumberKey$5("6"),
234
+ {
235
+ id: "clear",
236
+ type: "clear",
237
+ icon: ICON_CLEAR,
238
+ label: "",
239
+ className: "key-numpad-fn"
240
+ }
241
+ ]
242
+ },
243
+ {
244
+ className: "numpad-row",
245
+ keys: [
246
+ createNumberKey$5("7"),
247
+ createNumberKey$5("8"),
248
+ createNumberKey$5("9"),
249
+ {
250
+ id: "close",
251
+ type: "close",
252
+ label: "关闭",
253
+ className: "key-numpad-fn"
254
+ }
255
+ ]
256
+ },
272
257
  {
273
258
  className: "numpad-row",
274
259
  keys: [
@@ -279,7 +264,7 @@
279
264
  className: "key-numpad-fn",
280
265
  data: { targetType: "qwerty" }
281
266
  },
282
- createNumberKey$6("0"),
267
+ createNumberKey$5("0"),
283
268
  {
284
269
  id: "done",
285
270
  type: "done",
@@ -289,78 +274,80 @@
289
274
  }
290
275
  ]
291
276
  }
292
- ]
277
+ ],
278
+ data: {
279
+ maxLength: 19,
280
+ inputType: "bankcard"
281
+ }
293
282
  };
294
283
  //#endregion
295
- //#region src/layouts/symbol.ts
296
- function createSymbolKey(symbol, id) {
284
+ //#region src/layouts/email.ts
285
+ function createCharKey$4(char) {
297
286
  return {
298
- id: id || `symbol-${symbol.charCodeAt(0)}`,
287
+ id: `email-char-${char.toLowerCase()}`,
299
288
  type: "char",
300
- label: symbol,
301
- value: symbol
289
+ label: {
290
+ lower: char.toLowerCase(),
291
+ upper: char.toUpperCase()
292
+ },
293
+ value: {
294
+ lower: char.toLowerCase(),
295
+ upper: char.toUpperCase()
296
+ }
302
297
  };
303
298
  }
304
- const symbolLayout = {
305
- type: "symbol",
306
- name: "符号键盘",
307
- supportShift: false,
308
- switchable: ["qwerty", "number"],
299
+ function snippet$1(label, id) {
300
+ return {
301
+ id: id || `email-snippet-${label.replace(/[^a-z0-9]/gi, "")}`,
302
+ type: "char",
303
+ label,
304
+ value: label,
305
+ className: "key-snippet"
306
+ };
307
+ }
308
+ const EMAIL_SUFFIXES = [
309
+ "@qq.com",
310
+ "@163.com",
311
+ "@126.com",
312
+ "@gmail.com",
313
+ "@outlook.com",
314
+ "@hotmail.com",
315
+ "@sina.com",
316
+ "@sohu.com",
317
+ "@icloud.com",
318
+ "@foxmail.com",
319
+ "@yeah.net",
320
+ "@139.com"
321
+ ];
322
+ const emailLayout = {
323
+ type: "email",
324
+ name: "邮箱键盘",
325
+ supportShift: true,
326
+ defaultUpperCase: false,
327
+ switchable: ["symbol", "number"],
309
328
  rows: [
310
- { keys: [
311
- createSymbolKey("1"),
312
- createSymbolKey("2"),
313
- createSymbolKey("3"),
314
- createSymbolKey("4"),
315
- createSymbolKey("5"),
316
- createSymbolKey("6"),
317
- createSymbolKey("7"),
318
- createSymbolKey("8"),
319
- createSymbolKey("9"),
320
- createSymbolKey("0")
321
- ] },
322
- { keys: [
323
- createSymbolKey("-"),
324
- createSymbolKey("/"),
325
- createSymbolKey(":"),
326
- createSymbolKey(";"),
327
- createSymbolKey("("),
328
- createSymbolKey(")"),
329
- createSymbolKey("¥", "yen"),
330
- createSymbolKey("@"),
331
- createSymbolKey("\"", "quote")
332
- ] },
329
+ { keys: "qwertyuiop".split("").map(createCharKey$4) },
330
+ { keys: "asdfghjkl".split("").map(createCharKey$4) },
333
331
  { keys: [
334
332
  {
335
- id: "switch-symbol-2",
336
- type: "tab",
337
- label: "#+= ",
333
+ id: "shift",
334
+ type: "shift",
335
+ icon: ICON_SHIFT,
336
+ label: "",
338
337
  width: 1.5,
339
- className: "key-switch",
340
- data: { targetType: "symbol-2" }
338
+ className: "key-shift"
341
339
  },
342
- createSymbolKey("."),
343
- createSymbolKey(","),
344
- createSymbolKey("?"),
345
- createSymbolKey("!"),
346
- createSymbolKey("'", "apostrophe"),
340
+ ..."zxcvbnm".split("").map(createCharKey$4),
347
341
  {
348
342
  id: "backspace",
349
343
  type: "backspace",
350
- label: "⌫",
344
+ icon: ICON_DELETE,
345
+ label: "",
351
346
  width: 1.5,
352
347
  className: "key-backspace"
353
348
  }
354
349
  ] },
355
350
  { keys: [
356
- {
357
- id: "switch-qwerty",
358
- type: "tab",
359
- label: "ABC",
360
- width: 1.5,
361
- className: "key-switch",
362
- data: { targetType: "qwerty" }
363
- },
364
351
  {
365
352
  id: "switch-number",
366
353
  type: "tab",
@@ -370,110 +357,129 @@
370
357
  data: { targetType: "number" }
371
358
  },
372
359
  {
373
- id: "space",
374
- type: "space",
375
- label: "空格",
376
- value: " ",
377
- width: 4,
378
- className: "key-space"
379
- },
380
- {
381
- id: "done",
382
- type: "done",
383
- label: "完成",
384
- width: 2,
385
- className: "key-done"
386
- }
387
- ] }
388
- ]
389
- };
390
- const symbolLayout2 = {
391
- type: "symbol",
392
- name: "符号键盘2",
393
- supportShift: false,
394
- switchable: ["qwerty", "number"],
395
- rows: [
396
- { keys: [
397
- createSymbolKey("["),
398
- createSymbolKey("]"),
399
- createSymbolKey("{"),
400
- createSymbolKey("}"),
401
- createSymbolKey("#"),
402
- createSymbolKey("%"),
403
- createSymbolKey("^"),
404
- createSymbolKey("*"),
405
- createSymbolKey("+"),
406
- createSymbolKey("=")
407
- ] },
408
- { keys: [
409
- createSymbolKey("_"),
410
- createSymbolKey("\\"),
411
- createSymbolKey("|"),
412
- createSymbolKey("~"),
413
- createSymbolKey("<"),
414
- createSymbolKey(">"),
415
- createSymbolKey("€", "euro"),
416
- createSymbolKey("$"),
417
- createSymbolKey("&")
418
- ] },
419
- { keys: [
420
- {
421
- id: "switch-symbol-1",
422
- type: "tab",
423
- label: "123",
424
- width: 1.5,
425
- className: "key-switch",
426
- data: { targetType: "symbol" }
360
+ id: "email-at",
361
+ type: "char",
362
+ label: "@",
363
+ value: "@",
364
+ className: "key-at"
427
365
  },
428
- createSymbolKey("`", "backtick"),
429
- createSymbolKey("·", "middle-dot"),
430
- createSymbolKey("…", "ellipsis"),
431
- createSymbolKey("°", "degree"),
432
- createSymbolKey("•", "bullet"),
433
- {
434
- id: "backspace",
435
- type: "backspace",
436
- label: "⌫",
437
- width: 1.5,
438
- className: "key-backspace"
439
- }
440
- ] },
441
- { keys: [
442
366
  {
443
- id: "switch-qwerty",
444
- type: "tab",
445
- label: "ABC",
367
+ id: "email-suffix",
368
+ type: "char",
369
+ label: ".com",
370
+ value: "",
446
371
  width: 1.5,
447
- className: "key-switch",
448
- data: { targetType: "qwerty" }
449
- },
450
- {
451
- id: "switch-number",
452
- type: "tab",
453
- label: "123",
454
- width: 1.25,
455
- className: "key-switch",
456
- data: { targetType: "number" }
372
+ className: "key-suffix",
373
+ options: EMAIL_SUFFIXES
457
374
  },
375
+ snippet$1(".", "email-dot"),
376
+ snippet$1("-", "email-dash"),
377
+ snippet$1("_", "email-underscore"),
458
378
  {
459
379
  id: "space",
460
380
  type: "space",
461
- label: "空格",
381
+ label: "",
462
382
  value: " ",
463
- width: 4,
383
+ width: 2,
464
384
  className: "key-space"
465
385
  },
466
386
  {
467
- id: "done",
387
+ id: "email-done",
468
388
  type: "done",
469
- label: "完成",
470
- width: 2,
389
+ label: "确定",
390
+ width: 1.5,
471
391
  className: "key-done"
472
392
  }
473
393
  ] }
474
394
  ]
475
395
  };
476
396
  //#endregion
397
+ //#region src/layouts/idcard.ts
398
+ function createNumberKey$4(num) {
399
+ return {
400
+ id: `idcard-${num}`,
401
+ type: "char",
402
+ label: num,
403
+ value: num,
404
+ className: "key-num"
405
+ };
406
+ }
407
+ const idcardLayout = {
408
+ type: "idcard",
409
+ name: "身份证键盘",
410
+ supportShift: false,
411
+ rows: [
412
+ {
413
+ className: "numpad-row",
414
+ keys: [
415
+ createNumberKey$4("1"),
416
+ createNumberKey$4("2"),
417
+ createNumberKey$4("3"),
418
+ {
419
+ id: "backspace",
420
+ type: "backspace",
421
+ icon: ICON_DELETE,
422
+ label: "",
423
+ className: "key-numpad-fn"
424
+ }
425
+ ]
426
+ },
427
+ {
428
+ className: "numpad-row",
429
+ keys: [
430
+ createNumberKey$4("4"),
431
+ createNumberKey$4("5"),
432
+ createNumberKey$4("6"),
433
+ {
434
+ id: "clear",
435
+ type: "clear",
436
+ icon: ICON_CLEAR,
437
+ label: "",
438
+ className: "key-numpad-fn"
439
+ }
440
+ ]
441
+ },
442
+ {
443
+ className: "numpad-row",
444
+ keys: [
445
+ createNumberKey$4("7"),
446
+ createNumberKey$4("8"),
447
+ createNumberKey$4("9"),
448
+ {
449
+ id: "idcard-x",
450
+ type: "char",
451
+ label: "X",
452
+ value: "X",
453
+ className: "key-num"
454
+ }
455
+ ]
456
+ },
457
+ {
458
+ className: "numpad-row",
459
+ keys: [
460
+ {
461
+ id: "close",
462
+ type: "close",
463
+ label: "关闭",
464
+ className: "key-numpad-fn"
465
+ },
466
+ createNumberKey$4("0"),
467
+ {
468
+ id: "done",
469
+ type: "done",
470
+ label: "确定",
471
+ width: 2,
472
+ className: "key-numpad-done"
473
+ }
474
+ ]
475
+ }
476
+ ],
477
+ data: {
478
+ maxLength: 18,
479
+ inputType: "idcard"
480
+ }
481
+ };
482
+ //#endregion
477
483
  //#region src/layouts/license-plate.ts
478
484
  const PROVINCES = [
479
485
  "京",
@@ -555,7 +561,7 @@
555
561
  className: "key-plate-letter"
556
562
  };
557
563
  }
558
- function createNumberKey$5(num) {
564
+ function createNumberKey$3(num) {
559
565
  return {
560
566
  id: `num-${num}`,
561
567
  type: "char",
@@ -631,7 +637,7 @@
631
637
  rows: [
632
638
  {
633
639
  className: "plate-keyboard-row",
634
- keys: "1234567890".split("").map(createNumberKey$5)
640
+ keys: "1234567890".split("").map(createNumberKey$3)
635
641
  },
636
642
  {
637
643
  className: "plate-keyboard-row",
@@ -751,27 +757,28 @@
751
757
  rows: licensePlateProvinceLayout.rows
752
758
  };
753
759
  //#endregion
754
- //#region src/layouts/idcard.ts
755
- function createNumberKey$4(num) {
760
+ //#region src/layouts/number.ts
761
+ function createNumberKey$2(num) {
756
762
  return {
757
- id: `idcard-${num}`,
763
+ id: `num-${num}`,
758
764
  type: "char",
759
765
  label: num,
760
766
  value: num,
761
767
  className: "key-num"
762
768
  };
763
769
  }
764
- const idcardLayout = {
765
- type: "idcard",
766
- name: "身份证键盘",
770
+ const numberLayout = {
771
+ type: "number",
772
+ name: "数字键盘",
767
773
  supportShift: false,
774
+ switchable: ["qwerty", "symbol"],
768
775
  rows: [
769
776
  {
770
777
  className: "numpad-row",
771
778
  keys: [
772
- createNumberKey$4("1"),
773
- createNumberKey$4("2"),
774
- createNumberKey$4("3"),
779
+ createNumberKey$2("1"),
780
+ createNumberKey$2("2"),
781
+ createNumberKey$2("3"),
775
782
  {
776
783
  id: "backspace",
777
784
  type: "backspace",
@@ -784,9 +791,9 @@
784
791
  {
785
792
  className: "numpad-row",
786
793
  keys: [
787
- createNumberKey$4("4"),
788
- createNumberKey$4("5"),
789
- createNumberKey$4("6"),
794
+ createNumberKey$2("4"),
795
+ createNumberKey$2("5"),
796
+ createNumberKey$2("6"),
790
797
  {
791
798
  id: "clear",
792
799
  type: "clear",
@@ -799,28 +806,100 @@
799
806
  {
800
807
  className: "numpad-row",
801
808
  keys: [
802
- createNumberKey$4("7"),
803
- createNumberKey$4("8"),
804
- createNumberKey$4("9"),
809
+ createNumberKey$2("7"),
810
+ createNumberKey$2("8"),
811
+ createNumberKey$2("9"),
805
812
  {
806
- id: "idcard-x",
813
+ id: "close",
814
+ type: "close",
815
+ label: "关闭",
816
+ className: "key-numpad-fn"
817
+ }
818
+ ]
819
+ },
820
+ {
821
+ className: "numpad-row",
822
+ keys: [
823
+ {
824
+ id: "decimal",
807
825
  type: "char",
808
- label: "X",
809
- value: "X",
826
+ label: ".",
827
+ value: ".",
810
828
  className: "key-num"
829
+ },
830
+ createNumberKey$2("0"),
831
+ {
832
+ id: "done",
833
+ type: "done",
834
+ label: "确定",
835
+ width: 2,
836
+ className: "key-numpad-done"
837
+ }
838
+ ]
839
+ }
840
+ ]
841
+ };
842
+ const integerLayout = {
843
+ type: "integer",
844
+ name: "整数键盘",
845
+ supportShift: false,
846
+ switchable: ["qwerty"],
847
+ rows: [
848
+ {
849
+ className: "numpad-row",
850
+ keys: [
851
+ createNumberKey$2("1"),
852
+ createNumberKey$2("2"),
853
+ createNumberKey$2("3"),
854
+ {
855
+ id: "backspace",
856
+ type: "backspace",
857
+ icon: ICON_DELETE,
858
+ label: "",
859
+ className: "key-numpad-fn"
860
+ }
861
+ ]
862
+ },
863
+ {
864
+ className: "numpad-row",
865
+ keys: [
866
+ createNumberKey$2("4"),
867
+ createNumberKey$2("5"),
868
+ createNumberKey$2("6"),
869
+ {
870
+ id: "clear",
871
+ type: "clear",
872
+ icon: ICON_CLEAR,
873
+ label: "",
874
+ className: "key-numpad-fn"
811
875
  }
812
876
  ]
813
877
  },
814
878
  {
815
879
  className: "numpad-row",
816
880
  keys: [
881
+ createNumberKey$2("7"),
882
+ createNumberKey$2("8"),
883
+ createNumberKey$2("9"),
817
884
  {
818
885
  id: "close",
819
886
  type: "close",
820
887
  label: "关闭",
821
888
  className: "key-numpad-fn"
889
+ }
890
+ ]
891
+ },
892
+ {
893
+ className: "numpad-row",
894
+ keys: [
895
+ {
896
+ id: "switch-qwerty",
897
+ type: "tab",
898
+ label: "ABC",
899
+ className: "key-numpad-fn",
900
+ data: { targetType: "qwerty" }
822
901
  },
823
- createNumberKey$4("0"),
902
+ createNumberKey$2("0"),
824
903
  {
825
904
  id: "done",
826
905
  type: "done",
@@ -830,15 +909,11 @@
830
909
  }
831
910
  ]
832
911
  }
833
- ],
834
- data: {
835
- maxLength: 18,
836
- inputType: "idcard"
837
- }
912
+ ]
838
913
  };
839
914
  //#endregion
840
915
  //#region src/layouts/phone.ts
841
- function createNumberKey$3(num) {
916
+ function createNumberKey$1(num) {
842
917
  return {
843
918
  id: `phone-${num}`,
844
919
  type: "char",
@@ -855,9 +930,9 @@
855
930
  {
856
931
  className: "numpad-row",
857
932
  keys: [
858
- createNumberKey$3("1"),
859
- createNumberKey$3("2"),
860
- createNumberKey$3("3"),
933
+ createNumberKey$1("1"),
934
+ createNumberKey$1("2"),
935
+ createNumberKey$1("3"),
861
936
  {
862
937
  id: "backspace",
863
938
  type: "backspace",
@@ -870,9 +945,9 @@
870
945
  {
871
946
  className: "numpad-row",
872
947
  keys: [
873
- createNumberKey$3("4"),
874
- createNumberKey$3("5"),
875
- createNumberKey$3("6"),
948
+ createNumberKey$1("4"),
949
+ createNumberKey$1("5"),
950
+ createNumberKey$1("6"),
876
951
  {
877
952
  id: "clear",
878
953
  type: "clear",
@@ -885,9 +960,9 @@
885
960
  {
886
961
  className: "numpad-row",
887
962
  keys: [
888
- createNumberKey$3("7"),
889
- createNumberKey$3("8"),
890
- createNumberKey$3("9"),
963
+ createNumberKey$1("7"),
964
+ createNumberKey$1("8"),
965
+ createNumberKey$1("9"),
891
966
  {
892
967
  id: "close",
893
968
  type: "close",
@@ -906,7 +981,7 @@
906
981
  value: "+",
907
982
  className: "key-num"
908
983
  },
909
- createNumberKey$3("0"),
984
+ createNumberKey$1("0"),
910
985
  {
911
986
  id: "done",
912
987
  type: "done",
@@ -930,9 +1005,9 @@
930
1005
  {
931
1006
  className: "numpad-row",
932
1007
  keys: [
933
- createNumberKey$3("1"),
934
- createNumberKey$3("2"),
935
- createNumberKey$3("3"),
1008
+ createNumberKey$1("1"),
1009
+ createNumberKey$1("2"),
1010
+ createNumberKey$1("3"),
936
1011
  {
937
1012
  id: "backspace",
938
1013
  type: "backspace",
@@ -945,9 +1020,9 @@
945
1020
  {
946
1021
  className: "numpad-row",
947
1022
  keys: [
948
- createNumberKey$3("4"),
949
- createNumberKey$3("5"),
950
- createNumberKey$3("6"),
1023
+ createNumberKey$1("4"),
1024
+ createNumberKey$1("5"),
1025
+ createNumberKey$1("6"),
951
1026
  {
952
1027
  id: "clear",
953
1028
  type: "clear",
@@ -960,9 +1035,9 @@
960
1035
  {
961
1036
  className: "numpad-row",
962
1037
  keys: [
963
- createNumberKey$3("7"),
964
- createNumberKey$3("8"),
965
- createNumberKey$3("9"),
1038
+ createNumberKey$1("7"),
1039
+ createNumberKey$1("8"),
1040
+ createNumberKey$1("9"),
966
1041
  {
967
1042
  id: "phone-hash",
968
1043
  type: "char",
@@ -982,7 +1057,7 @@
982
1057
  value: "*",
983
1058
  className: "key-num"
984
1059
  },
985
- createNumberKey$3("0"),
1060
+ createNumberKey$1("0"),
986
1061
  {
987
1062
  id: "done",
988
1063
  type: "done",
@@ -999,27 +1074,58 @@
999
1074
  }
1000
1075
  };
1001
1076
  //#endregion
1002
- //#region src/layouts/amount.ts
1003
- function createNumberKey$2(num) {
1077
+ //#region src/layouts/punct.ts
1078
+ function p(label, value) {
1004
1079
  return {
1005
- id: `amount-${num}`,
1080
+ id: `punct-${label}`,
1006
1081
  type: "char",
1007
- label: num,
1008
- value: num,
1082
+ label,
1083
+ value: value ?? label,
1009
1084
  className: "key-num"
1010
1085
  };
1011
1086
  }
1012
- const amountLayout = {
1013
- type: "amount",
1014
- name: "金额键盘",
1087
+ const punctLayout = {
1088
+ type: "punct",
1089
+ name: "中文标点",
1015
1090
  supportShift: false,
1091
+ switchable: ["qwerty", "punct-en"],
1016
1092
  rows: [
1017
1093
  {
1018
- className: "numpad-row",
1094
+ className: "virtual-keyboard__row",
1019
1095
  keys: [
1020
- createNumberKey$2("1"),
1021
- createNumberKey$2("2"),
1022
- createNumberKey$2("3"),
1096
+ ",",
1097
+ "。",
1098
+ "?",
1099
+ "!",
1100
+ ":",
1101
+ ";",
1102
+ "\"",
1103
+ "\""
1104
+ ].map((s) => p(s))
1105
+ },
1106
+ {
1107
+ className: "virtual-keyboard__row",
1108
+ keys: [
1109
+ "(",
1110
+ ")",
1111
+ "【",
1112
+ "】",
1113
+ "《",
1114
+ "》",
1115
+ "……",
1116
+ "——"
1117
+ ].map((s) => p(s))
1118
+ },
1119
+ {
1120
+ className: "virtual-keyboard__row",
1121
+ keys: [
1122
+ p("·"),
1123
+ p("、"),
1124
+ p("「"),
1125
+ p("」"),
1126
+ p("『"),
1127
+ p("』"),
1128
+ p("~"),
1023
1129
  {
1024
1130
  id: "backspace",
1025
1131
  type: "backspace",
@@ -1030,357 +1136,171 @@
1030
1136
  ]
1031
1137
  },
1032
1138
  {
1033
- className: "numpad-row",
1139
+ className: "virtual-keyboard__row",
1034
1140
  keys: [
1035
- createNumberKey$2("4"),
1036
- createNumberKey$2("5"),
1037
- createNumberKey$2("6"),
1038
1141
  {
1039
- id: "clear",
1040
- type: "clear",
1041
- icon: ICON_CLEAR,
1042
- label: "",
1142
+ id: "switch-punct-en",
1143
+ type: "tab",
1144
+ label: "英",
1145
+ data: { targetType: "punct-en" },
1043
1146
  className: "key-numpad-fn"
1044
- }
1045
- ]
1046
- },
1047
- {
1048
- className: "numpad-row",
1049
- keys: [
1050
- createNumberKey$2("7"),
1051
- createNumberKey$2("8"),
1052
- createNumberKey$2("9"),
1147
+ },
1053
1148
  {
1054
- id: "double-zero",
1055
- type: "char",
1056
- label: "00",
1057
- value: "00",
1058
- className: "key-num"
1059
- }
1060
- ]
1061
- },
1062
- {
1063
- className: "numpad-row",
1064
- keys: [
1149
+ id: "switch-qwerty",
1150
+ type: "tab",
1151
+ label: "ABC",
1152
+ data: { targetType: "qwerty" },
1153
+ className: "key-numpad-fn"
1154
+ },
1065
1155
  {
1066
- id: "decimal",
1067
- type: "char",
1068
- label: ".",
1069
- value: ".",
1156
+ id: "space",
1157
+ type: "space",
1158
+ label: "空格",
1159
+ value: " ",
1160
+ width: 4,
1070
1161
  className: "key-num"
1071
1162
  },
1072
- createNumberKey$2("0"),
1073
1163
  {
1074
1164
  id: "done",
1075
1165
  type: "done",
1076
- label: "确定",
1166
+ icon: ICON_CHECK,
1167
+ label: "",
1077
1168
  width: 2,
1078
1169
  className: "key-numpad-done"
1079
1170
  }
1080
1171
  ]
1081
1172
  }
1082
- ],
1083
- data: {
1084
- inputType: "amount",
1085
- decimalPlaces: 2
1086
- }
1173
+ ]
1087
1174
  };
1088
- const amountSimpleLayout = {
1089
- type: "amount-simple",
1090
- name: "金额键盘",
1091
- supportShift: false,
1092
- rows: [
1093
- {
1094
- className: "numpad-row",
1095
- keys: [
1096
- createNumberKey$2("1"),
1097
- createNumberKey$2("2"),
1098
- createNumberKey$2("3"),
1099
- {
1100
- id: "backspace",
1101
- type: "backspace",
1102
- icon: ICON_DELETE,
1103
- label: "",
1104
- className: "key-numpad-fn"
1105
- }
1106
- ]
1175
+ //#endregion
1176
+ //#region src/layouts/qwerty.ts
1177
+ function createCharKey$3(char) {
1178
+ return {
1179
+ id: `char-${char.toLowerCase()}`,
1180
+ type: "char",
1181
+ label: {
1182
+ lower: char.toLowerCase(),
1183
+ upper: char.toUpperCase()
1107
1184
  },
1108
- {
1109
- className: "numpad-row",
1110
- keys: [
1111
- createNumberKey$2("4"),
1112
- createNumberKey$2("5"),
1113
- createNumberKey$2("6"),
1114
- {
1115
- id: "clear",
1116
- type: "clear",
1117
- icon: ICON_CLEAR,
1118
- label: "",
1119
- className: "key-numpad-fn"
1120
- }
1121
- ]
1122
- },
1123
- {
1124
- className: "numpad-row",
1125
- keys: [
1126
- createNumberKey$2("7"),
1127
- createNumberKey$2("8"),
1128
- createNumberKey$2("9"),
1129
- {
1130
- id: "close",
1131
- type: "close",
1132
- label: "关闭",
1133
- className: "key-numpad-fn"
1134
- }
1135
- ]
1136
- },
1137
- {
1138
- className: "numpad-row",
1139
- keys: [
1140
- {
1141
- id: "decimal",
1142
- type: "char",
1143
- label: ".",
1144
- value: ".",
1145
- className: "key-num"
1146
- },
1147
- createNumberKey$2("0"),
1148
- {
1149
- id: "done",
1150
- type: "done",
1151
- label: "确定",
1152
- width: 2,
1153
- className: "key-numpad-done"
1154
- }
1155
- ]
1185
+ value: {
1186
+ lower: char.toLowerCase(),
1187
+ upper: char.toUpperCase()
1156
1188
  }
1157
- ],
1158
- data: {
1159
- inputType: "amount",
1160
- decimalPlaces: 2
1161
- }
1162
- };
1163
- //#endregion
1164
- //#region src/layouts/bankcard.ts
1165
- function createNumberKey$1(num) {
1166
- return {
1167
- id: `bankcard-${num}`,
1168
- type: "char",
1169
- label: num,
1170
- value: num,
1171
- className: "key-num"
1172
1189
  };
1173
1190
  }
1174
- const bankcardLayout = {
1175
- type: "bankcard",
1176
- name: "银行卡键盘",
1177
- supportShift: false,
1191
+ const qwertyLayout = {
1192
+ type: "qwerty",
1193
+ name: "字母键盘",
1194
+ supportShift: true,
1195
+ defaultUpperCase: false,
1196
+ switchable: ["number", "symbol"],
1178
1197
  rows: [
1179
- {
1180
- className: "numpad-row",
1181
- keys: [
1182
- createNumberKey$1("1"),
1183
- createNumberKey$1("2"),
1184
- createNumberKey$1("3"),
1185
- {
1186
- id: "backspace",
1187
- type: "backspace",
1188
- icon: ICON_DELETE,
1189
- label: "",
1190
- className: "key-numpad-fn"
1191
- }
1192
- ]
1193
- },
1194
- {
1195
- className: "numpad-row",
1196
- keys: [
1197
- createNumberKey$1("4"),
1198
- createNumberKey$1("5"),
1199
- createNumberKey$1("6"),
1200
- {
1201
- id: "clear",
1202
- type: "clear",
1203
- icon: ICON_CLEAR,
1204
- label: "",
1205
- className: "key-numpad-fn"
1206
- }
1207
- ]
1208
- },
1209
- {
1210
- className: "numpad-row",
1211
- keys: [
1212
- createNumberKey$1("7"),
1213
- createNumberKey$1("8"),
1214
- createNumberKey$1("9"),
1215
- {
1216
- id: "close",
1217
- type: "close",
1218
- label: "关闭",
1219
- className: "key-numpad-fn"
1220
- }
1221
- ]
1222
- },
1223
- {
1224
- className: "numpad-row",
1225
- keys: [
1226
- {
1227
- id: "switch-qwerty",
1228
- type: "tab",
1229
- label: "ABC",
1230
- className: "key-numpad-fn",
1231
- data: { targetType: "qwerty" }
1232
- },
1233
- createNumberKey$1("0"),
1234
- {
1235
- id: "done",
1236
- type: "done",
1237
- label: "确定",
1238
- width: 2,
1239
- className: "key-numpad-done"
1240
- }
1241
- ]
1242
- }
1243
- ],
1244
- data: {
1245
- maxLength: 19,
1246
- inputType: "bankcard"
1247
- }
1198
+ { keys: [
1199
+ createCharKey$3("Q"),
1200
+ createCharKey$3("W"),
1201
+ createCharKey$3("E"),
1202
+ createCharKey$3("R"),
1203
+ createCharKey$3("T"),
1204
+ createCharKey$3("Y"),
1205
+ createCharKey$3("U"),
1206
+ createCharKey$3("I"),
1207
+ createCharKey$3("O"),
1208
+ createCharKey$3("P")
1209
+ ] },
1210
+ { keys: [
1211
+ createCharKey$3("A"),
1212
+ createCharKey$3("S"),
1213
+ createCharKey$3("D"),
1214
+ createCharKey$3("F"),
1215
+ createCharKey$3("G"),
1216
+ createCharKey$3("H"),
1217
+ createCharKey$3("J"),
1218
+ createCharKey$3("K"),
1219
+ createCharKey$3("L")
1220
+ ] },
1221
+ { keys: [
1222
+ {
1223
+ id: "shift",
1224
+ type: "shift",
1225
+ label: "⇧",
1226
+ width: 1.5,
1227
+ className: "key-shift"
1228
+ },
1229
+ createCharKey$3("Z"),
1230
+ createCharKey$3("X"),
1231
+ createCharKey$3("C"),
1232
+ createCharKey$3("V"),
1233
+ createCharKey$3("B"),
1234
+ createCharKey$3("N"),
1235
+ createCharKey$3("M"),
1236
+ {
1237
+ id: "backspace",
1238
+ type: "backspace",
1239
+ label: "⌫",
1240
+ width: 1.5,
1241
+ className: "key-backspace"
1242
+ }
1243
+ ] },
1244
+ { keys: [
1245
+ {
1246
+ id: "switch-number",
1247
+ type: "tab",
1248
+ label: "123",
1249
+ width: 1.5,
1250
+ className: "key-switch",
1251
+ data: { targetType: "number" }
1252
+ },
1253
+ {
1254
+ id: "switch-symbol",
1255
+ type: "tab",
1256
+ label: "#+=",
1257
+ width: 1.25,
1258
+ className: "key-switch",
1259
+ data: { targetType: "symbol" }
1260
+ },
1261
+ {
1262
+ id: "space",
1263
+ type: "space",
1264
+ label: "空格",
1265
+ value: " ",
1266
+ width: 4,
1267
+ className: "key-space"
1268
+ },
1269
+ {
1270
+ id: "done",
1271
+ type: "done",
1272
+ label: "完成",
1273
+ width: 2,
1274
+ className: "key-done"
1275
+ }
1276
+ ] }
1277
+ ]
1248
1278
  };
1249
1279
  //#endregion
1250
- //#region src/layouts/verifycode.ts
1251
- function createNumberKey(num) {
1280
+ //#region src/layouts/search.ts
1281
+ function createCharKey$2(char) {
1252
1282
  return {
1253
- id: `verify-${num}`,
1283
+ id: `search-char-${char.toLowerCase()}`,
1254
1284
  type: "char",
1255
- label: num,
1256
- value: num,
1257
- className: "key-num"
1285
+ label: {
1286
+ lower: char.toLowerCase(),
1287
+ upper: char.toUpperCase()
1288
+ },
1289
+ value: {
1290
+ lower: char.toLowerCase(),
1291
+ upper: char.toUpperCase()
1292
+ }
1258
1293
  };
1259
1294
  }
1260
- const verifycodeLayout = {
1261
- type: "verifycode",
1262
- name: "验证码键盘",
1263
- supportShift: false,
1295
+ const searchLayout = {
1296
+ type: "search",
1297
+ name: "搜索键盘",
1298
+ supportShift: true,
1299
+ defaultUpperCase: false,
1300
+ switchable: ["symbol", "number"],
1264
1301
  rows: [
1265
- {
1266
- className: "numpad-row",
1267
- keys: [
1268
- createNumberKey("1"),
1269
- createNumberKey("2"),
1270
- createNumberKey("3"),
1271
- {
1272
- id: "backspace",
1273
- type: "backspace",
1274
- icon: ICON_DELETE,
1275
- label: "",
1276
- className: "key-numpad-fn"
1277
- }
1278
- ]
1279
- },
1280
- {
1281
- className: "numpad-row",
1282
- keys: [
1283
- createNumberKey("4"),
1284
- createNumberKey("5"),
1285
- createNumberKey("6"),
1286
- {
1287
- id: "clear",
1288
- type: "clear",
1289
- icon: ICON_CLEAR,
1290
- label: "",
1291
- className: "key-numpad-fn"
1292
- }
1293
- ]
1294
- },
1295
- {
1296
- className: "numpad-row",
1297
- keys: [
1298
- createNumberKey("7"),
1299
- createNumberKey("8"),
1300
- createNumberKey("9"),
1301
- {
1302
- id: "close",
1303
- type: "close",
1304
- label: "关闭",
1305
- className: "key-numpad-fn"
1306
- }
1307
- ]
1308
- },
1309
- {
1310
- className: "numpad-row",
1311
- keys: [
1312
- {
1313
- id: "empty",
1314
- type: "char",
1315
- label: "",
1316
- value: "",
1317
- disabled: true,
1318
- className: "key-numpad-empty"
1319
- },
1320
- createNumberKey("0"),
1321
- {
1322
- id: "done",
1323
- type: "done",
1324
- label: "确定",
1325
- width: 2,
1326
- className: "key-numpad-done"
1327
- }
1328
- ]
1329
- }
1330
- ],
1331
- data: {
1332
- maxLength: 6,
1333
- inputType: "verifycode"
1334
- }
1335
- };
1336
- //#endregion
1337
- //#region src/layouts/email.ts
1338
- function createCharKey$3(char) {
1339
- return {
1340
- id: `email-char-${char.toLowerCase()}`,
1341
- type: "char",
1342
- label: {
1343
- lower: char.toLowerCase(),
1344
- upper: char.toUpperCase()
1345
- },
1346
- value: {
1347
- lower: char.toLowerCase(),
1348
- upper: char.toUpperCase()
1349
- }
1350
- };
1351
- }
1352
- function snippet$1(label, id) {
1353
- return {
1354
- id: id || `email-snippet-${label.replace(/[^a-z0-9]/gi, "")}`,
1355
- type: "char",
1356
- label,
1357
- value: label,
1358
- className: "key-snippet"
1359
- };
1360
- }
1361
- const EMAIL_SUFFIXES = [
1362
- "@qq.com",
1363
- "@163.com",
1364
- "@126.com",
1365
- "@gmail.com",
1366
- "@outlook.com",
1367
- "@hotmail.com",
1368
- "@sina.com",
1369
- "@sohu.com",
1370
- "@icloud.com",
1371
- "@foxmail.com",
1372
- "@yeah.net",
1373
- "@139.com"
1374
- ];
1375
- const emailLayout = {
1376
- type: "email",
1377
- name: "邮箱键盘",
1378
- supportShift: true,
1379
- defaultUpperCase: false,
1380
- switchable: ["symbol", "number"],
1381
- rows: [
1382
- { keys: "qwertyuiop".split("").map(createCharKey$3) },
1383
- { keys: "asdfghjkl".split("").map(createCharKey$3) },
1302
+ { keys: "qwertyuiop".split("").map(createCharKey$2) },
1303
+ { keys: "asdfghjkl".split("").map(createCharKey$2) },
1384
1304
  { keys: [
1385
1305
  {
1386
1306
  id: "shift",
@@ -1390,7 +1310,7 @@
1390
1310
  width: 1.5,
1391
1311
  className: "key-shift"
1392
1312
  },
1393
- ..."zxcvbnm".split("").map(createCharKey$3),
1313
+ ..."zxcvbnm".split("").map(createCharKey$2),
1394
1314
  {
1395
1315
  id: "backspace",
1396
1316
  type: "backspace",
@@ -1410,148 +1330,252 @@
1410
1330
  data: { targetType: "number" }
1411
1331
  },
1412
1332
  {
1413
- id: "email-at",
1414
- type: "char",
1415
- label: "@",
1416
- value: "@",
1417
- className: "key-at"
1418
- },
1419
- {
1420
- id: "email-suffix",
1421
- type: "char",
1422
- label: ".com",
1423
- value: "",
1424
- width: 1.5,
1425
- className: "key-suffix",
1426
- options: EMAIL_SUFFIXES
1333
+ id: "switch-symbol",
1334
+ type: "tab",
1335
+ label: "#+=",
1336
+ width: 1.25,
1337
+ className: "key-switch",
1338
+ data: { targetType: "symbol" }
1427
1339
  },
1428
- snippet$1(".", "email-dot"),
1429
- snippet$1("-", "email-dash"),
1430
- snippet$1("_", "email-underscore"),
1431
1340
  {
1432
1341
  id: "space",
1433
1342
  type: "space",
1434
1343
  label: "",
1435
1344
  value: " ",
1436
- width: 2,
1345
+ width: 4,
1437
1346
  className: "key-space"
1438
1347
  },
1439
1348
  {
1440
- id: "email-done",
1349
+ id: "voice",
1350
+ type: "custom",
1351
+ icon: ICON_VOICE,
1352
+ label: "",
1353
+ width: 1,
1354
+ className: "key-voice",
1355
+ data: { action: "voice" }
1356
+ },
1357
+ {
1358
+ id: "search-done",
1441
1359
  type: "done",
1442
- label: "确定",
1443
- width: 1.5,
1360
+ label: "搜索",
1361
+ width: 2,
1444
1362
  className: "key-done"
1445
1363
  }
1446
1364
  ] }
1447
1365
  ]
1448
1366
  };
1449
1367
  //#endregion
1450
- //#region src/layouts/url.ts
1451
- function createCharKey$2(char) {
1452
- return {
1453
- id: `url-char-${char.toLowerCase()}`,
1454
- type: "char",
1455
- label: {
1456
- lower: char.toLowerCase(),
1457
- upper: char.toUpperCase()
1458
- },
1459
- value: {
1460
- lower: char.toLowerCase(),
1461
- upper: char.toUpperCase()
1462
- }
1463
- };
1464
- }
1465
- function snippet(label, width) {
1368
+ //#region src/layouts/symbol.ts
1369
+ function createSymbolKey(symbol, id) {
1466
1370
  return {
1467
- id: `url-snippet-${label.replace(/[^a-z0-9]/gi, "") || label.split("").map((c) => c.charCodeAt(0)).join("")}`,
1371
+ id: id || `symbol-${symbol.charCodeAt(0)}`,
1468
1372
  type: "char",
1469
- label,
1470
- value: label,
1471
- width,
1472
- className: "key-snippet"
1373
+ label: symbol,
1374
+ value: symbol
1473
1375
  };
1474
1376
  }
1475
- const urlLayout = {
1476
- type: "url",
1477
- name: "网址键盘",
1478
- supportShift: true,
1479
- defaultUpperCase: false,
1480
- switchable: ["symbol", "number"],
1377
+ const symbolLayout = {
1378
+ type: "symbol",
1379
+ name: "符号键盘",
1380
+ supportShift: false,
1381
+ switchable: ["qwerty", "number"],
1481
1382
  rows: [
1482
- { keys: "qwertyuiop".split("").map(createCharKey$2) },
1483
- { keys: "asdfghjkl".split("").map(createCharKey$2) },
1383
+ { keys: [
1384
+ createSymbolKey("1"),
1385
+ createSymbolKey("2"),
1386
+ createSymbolKey("3"),
1387
+ createSymbolKey("4"),
1388
+ createSymbolKey("5"),
1389
+ createSymbolKey("6"),
1390
+ createSymbolKey("7"),
1391
+ createSymbolKey("8"),
1392
+ createSymbolKey("9"),
1393
+ createSymbolKey("0")
1394
+ ] },
1395
+ { keys: [
1396
+ createSymbolKey("-"),
1397
+ createSymbolKey("/"),
1398
+ createSymbolKey(":"),
1399
+ createSymbolKey(";"),
1400
+ createSymbolKey("("),
1401
+ createSymbolKey(")"),
1402
+ createSymbolKey("¥", "yen"),
1403
+ createSymbolKey("@"),
1404
+ createSymbolKey("\"", "quote")
1405
+ ] },
1484
1406
  { keys: [
1485
1407
  {
1486
- id: "shift",
1487
- type: "shift",
1488
- icon: ICON_SHIFT,
1489
- label: "",
1408
+ id: "switch-symbol-2",
1409
+ type: "tab",
1410
+ label: "#+= ",
1490
1411
  width: 1.5,
1491
- className: "key-shift"
1412
+ className: "key-switch",
1413
+ data: { targetType: "symbol-2" }
1492
1414
  },
1493
- ..."zxcvbnm".split("").map(createCharKey$2),
1415
+ createSymbolKey("."),
1416
+ createSymbolKey(","),
1417
+ createSymbolKey("?"),
1418
+ createSymbolKey("!"),
1419
+ createSymbolKey("'", "apostrophe"),
1494
1420
  {
1495
1421
  id: "backspace",
1496
1422
  type: "backspace",
1497
- icon: ICON_DELETE,
1498
- label: "",
1423
+ label: "⌫",
1499
1424
  width: 1.5,
1500
1425
  className: "key-backspace"
1501
1426
  }
1502
1427
  ] },
1503
1428
  { keys: [
1429
+ {
1430
+ id: "switch-qwerty",
1431
+ type: "tab",
1432
+ label: "ABC",
1433
+ width: 1.5,
1434
+ className: "key-switch",
1435
+ data: { targetType: "qwerty" }
1436
+ },
1504
1437
  {
1505
1438
  id: "switch-number",
1506
1439
  type: "tab",
1507
1440
  label: "123",
1508
- width: 1.2,
1441
+ width: 1.25,
1509
1442
  className: "key-switch",
1510
1443
  data: { targetType: "number" }
1511
1444
  },
1512
- snippet("/", .8),
1513
- snippet(".", .8),
1514
- snippet("-", .8),
1515
- snippet(".com", 1.5),
1516
- snippet(".cn", 1.2),
1517
1445
  {
1518
1446
  id: "space",
1519
1447
  type: "space",
1520
- label: "",
1448
+ label: "空格",
1521
1449
  value: " ",
1522
- width: 1.5,
1450
+ width: 4,
1523
1451
  className: "key-space"
1524
1452
  },
1525
1453
  {
1526
- id: "url-go",
1454
+ id: "done",
1527
1455
  type: "done",
1528
- label: "前往",
1529
- width: 1.5,
1456
+ label: "完成",
1457
+ width: 2,
1530
1458
  className: "key-done"
1531
1459
  }
1532
1460
  ] }
1533
1461
  ]
1534
1462
  };
1535
- //#endregion
1536
- //#region src/layouts/search.ts
1537
- function createCharKey$1(char) {
1538
- return {
1539
- id: `search-char-${char.toLowerCase()}`,
1540
- type: "char",
1541
- label: {
1542
- lower: char.toLowerCase(),
1543
- upper: char.toUpperCase()
1544
- },
1545
- value: {
1546
- lower: char.toLowerCase(),
1547
- upper: char.toUpperCase()
1548
- }
1549
- };
1550
- }
1551
- const searchLayout = {
1552
- type: "search",
1553
- name: "搜索键盘",
1554
- supportShift: true,
1463
+ const symbolLayout2 = {
1464
+ type: "symbol",
1465
+ name: "符号键盘2",
1466
+ supportShift: false,
1467
+ switchable: ["qwerty", "number"],
1468
+ rows: [
1469
+ { keys: [
1470
+ createSymbolKey("["),
1471
+ createSymbolKey("]"),
1472
+ createSymbolKey("{"),
1473
+ createSymbolKey("}"),
1474
+ createSymbolKey("#"),
1475
+ createSymbolKey("%"),
1476
+ createSymbolKey("^"),
1477
+ createSymbolKey("*"),
1478
+ createSymbolKey("+"),
1479
+ createSymbolKey("=")
1480
+ ] },
1481
+ { keys: [
1482
+ createSymbolKey("_"),
1483
+ createSymbolKey("\\"),
1484
+ createSymbolKey("|"),
1485
+ createSymbolKey("~"),
1486
+ createSymbolKey("<"),
1487
+ createSymbolKey(">"),
1488
+ createSymbolKey("€", "euro"),
1489
+ createSymbolKey("$"),
1490
+ createSymbolKey("&")
1491
+ ] },
1492
+ { keys: [
1493
+ {
1494
+ id: "switch-symbol-1",
1495
+ type: "tab",
1496
+ label: "123",
1497
+ width: 1.5,
1498
+ className: "key-switch",
1499
+ data: { targetType: "symbol" }
1500
+ },
1501
+ createSymbolKey("`", "backtick"),
1502
+ createSymbolKey("·", "middle-dot"),
1503
+ createSymbolKey("…", "ellipsis"),
1504
+ createSymbolKey("°", "degree"),
1505
+ createSymbolKey("•", "bullet"),
1506
+ {
1507
+ id: "backspace",
1508
+ type: "backspace",
1509
+ label: "⌫",
1510
+ width: 1.5,
1511
+ className: "key-backspace"
1512
+ }
1513
+ ] },
1514
+ { keys: [
1515
+ {
1516
+ id: "switch-qwerty",
1517
+ type: "tab",
1518
+ label: "ABC",
1519
+ width: 1.5,
1520
+ className: "key-switch",
1521
+ data: { targetType: "qwerty" }
1522
+ },
1523
+ {
1524
+ id: "switch-number",
1525
+ type: "tab",
1526
+ label: "123",
1527
+ width: 1.25,
1528
+ className: "key-switch",
1529
+ data: { targetType: "number" }
1530
+ },
1531
+ {
1532
+ id: "space",
1533
+ type: "space",
1534
+ label: "空格",
1535
+ value: " ",
1536
+ width: 4,
1537
+ className: "key-space"
1538
+ },
1539
+ {
1540
+ id: "done",
1541
+ type: "done",
1542
+ label: "完成",
1543
+ width: 2,
1544
+ className: "key-done"
1545
+ }
1546
+ ] }
1547
+ ]
1548
+ };
1549
+ //#endregion
1550
+ //#region src/layouts/url.ts
1551
+ function createCharKey$1(char) {
1552
+ return {
1553
+ id: `url-char-${char.toLowerCase()}`,
1554
+ type: "char",
1555
+ label: {
1556
+ lower: char.toLowerCase(),
1557
+ upper: char.toUpperCase()
1558
+ },
1559
+ value: {
1560
+ lower: char.toLowerCase(),
1561
+ upper: char.toUpperCase()
1562
+ }
1563
+ };
1564
+ }
1565
+ function snippet(label, width) {
1566
+ return {
1567
+ id: `url-snippet-${label.replace(/[^a-z0-9]/gi, "") || label.split("").map((c) => c.charCodeAt(0)).join("")}`,
1568
+ type: "char",
1569
+ label,
1570
+ value: label,
1571
+ width,
1572
+ className: "key-snippet"
1573
+ };
1574
+ }
1575
+ const urlLayout = {
1576
+ type: "url",
1577
+ name: "网址键盘",
1578
+ supportShift: true,
1555
1579
  defaultUpperCase: false,
1556
1580
  switchable: ["symbol", "number"],
1557
1581
  rows: [
@@ -1581,98 +1605,55 @@
1581
1605
  id: "switch-number",
1582
1606
  type: "tab",
1583
1607
  label: "123",
1584
- width: 1.25,
1608
+ width: 1.2,
1585
1609
  className: "key-switch",
1586
1610
  data: { targetType: "number" }
1587
1611
  },
1588
- {
1589
- id: "switch-symbol",
1590
- type: "tab",
1591
- label: "#+=",
1592
- width: 1.25,
1593
- className: "key-switch",
1594
- data: { targetType: "symbol" }
1595
- },
1612
+ snippet("/", .8),
1613
+ snippet(".", .8),
1614
+ snippet("-", .8),
1615
+ snippet(".com", 1.5),
1616
+ snippet(".cn", 1.2),
1596
1617
  {
1597
1618
  id: "space",
1598
1619
  type: "space",
1599
1620
  label: "",
1600
1621
  value: " ",
1601
- width: 4,
1622
+ width: 1.5,
1602
1623
  className: "key-space"
1603
1624
  },
1604
1625
  {
1605
- id: "voice",
1606
- type: "custom",
1607
- icon: ICON_VOICE,
1608
- label: "",
1609
- width: 1,
1610
- className: "key-voice",
1611
- data: { action: "voice" }
1612
- },
1613
- {
1614
- id: "search-done",
1626
+ id: "url-go",
1615
1627
  type: "done",
1616
- label: "搜索",
1617
- width: 2,
1628
+ label: "前往",
1629
+ width: 1.5,
1618
1630
  className: "key-done"
1619
1631
  }
1620
1632
  ] }
1621
1633
  ]
1622
1634
  };
1623
1635
  //#endregion
1624
- //#region src/layouts/punct.ts
1625
- function p(label, value) {
1636
+ //#region src/layouts/verifycode.ts
1637
+ function createNumberKey(num) {
1626
1638
  return {
1627
- id: `punct-${label}`,
1639
+ id: `verify-${num}`,
1628
1640
  type: "char",
1629
- label,
1630
- value: value ?? label,
1641
+ label: num,
1642
+ value: num,
1631
1643
  className: "key-num"
1632
1644
  };
1633
1645
  }
1634
- const punctLayout = {
1635
- type: "punct",
1636
- name: "中文标点",
1646
+ const verifycodeLayout = {
1647
+ type: "verifycode",
1648
+ name: "验证码键盘",
1637
1649
  supportShift: false,
1638
- switchable: ["qwerty", "punct-en"],
1639
1650
  rows: [
1640
1651
  {
1641
- className: "virtual-keyboard__row",
1642
- keys: [
1643
- ",",
1644
- "。",
1645
- "?",
1646
- "!",
1647
- ":",
1648
- ";",
1649
- "\"",
1650
- "\""
1651
- ].map((s) => p(s))
1652
- },
1653
- {
1654
- className: "virtual-keyboard__row",
1655
- keys: [
1656
- "(",
1657
- ")",
1658
- "【",
1659
- "】",
1660
- "《",
1661
- "》",
1662
- "……",
1663
- "——"
1664
- ].map((s) => p(s))
1665
- },
1666
- {
1667
- className: "virtual-keyboard__row",
1652
+ className: "numpad-row",
1668
1653
  keys: [
1669
- p("·"),
1670
- p("、"),
1671
- p("「"),
1672
- p("」"),
1673
- p("『"),
1674
- p("』"),
1675
- p("~"),
1654
+ createNumberKey("1"),
1655
+ createNumberKey("2"),
1656
+ createNumberKey("3"),
1676
1657
  {
1677
1658
  id: "backspace",
1678
1659
  type: "backspace",
@@ -1683,65 +1664,84 @@
1683
1664
  ]
1684
1665
  },
1685
1666
  {
1686
- className: "virtual-keyboard__row",
1667
+ className: "numpad-row",
1687
1668
  keys: [
1669
+ createNumberKey("4"),
1670
+ createNumberKey("5"),
1671
+ createNumberKey("6"),
1688
1672
  {
1689
- id: "switch-punct-en",
1690
- type: "tab",
1691
- label: "英",
1692
- data: { targetType: "punct-en" },
1673
+ id: "clear",
1674
+ type: "clear",
1675
+ icon: ICON_CLEAR,
1676
+ label: "",
1693
1677
  className: "key-numpad-fn"
1694
- },
1678
+ }
1679
+ ]
1680
+ },
1681
+ {
1682
+ className: "numpad-row",
1683
+ keys: [
1684
+ createNumberKey("7"),
1685
+ createNumberKey("8"),
1686
+ createNumberKey("9"),
1695
1687
  {
1696
- id: "switch-qwerty",
1697
- type: "tab",
1698
- label: "ABC",
1699
- data: { targetType: "qwerty" },
1688
+ id: "close",
1689
+ type: "close",
1690
+ label: "关闭",
1700
1691
  className: "key-numpad-fn"
1701
- },
1692
+ }
1693
+ ]
1694
+ },
1695
+ {
1696
+ className: "numpad-row",
1697
+ keys: [
1702
1698
  {
1703
- id: "space",
1704
- type: "space",
1705
- label: "空格",
1706
- value: " ",
1707
- width: 4,
1708
- className: "key-num"
1699
+ id: "empty",
1700
+ type: "char",
1701
+ label: "",
1702
+ value: "",
1703
+ disabled: true,
1704
+ className: "key-numpad-empty"
1709
1705
  },
1706
+ createNumberKey("0"),
1710
1707
  {
1711
1708
  id: "done",
1712
1709
  type: "done",
1713
- icon: ICON_CHECK,
1714
- label: "",
1710
+ label: "确定",
1715
1711
  width: 2,
1716
1712
  className: "key-numpad-done"
1717
1713
  }
1718
1714
  ]
1719
1715
  }
1720
- ]
1716
+ ],
1717
+ data: {
1718
+ maxLength: 6,
1719
+ inputType: "verifycode"
1720
+ }
1721
1721
  };
1722
1722
  //#endregion
1723
1723
  //#region src/layouts/index.ts
1724
1724
  const builtinLayouts = {
1725
- "qwerty": qwertyLayout,
1726
- "number": numberLayout,
1727
- "integer": integerLayout,
1728
- "symbol": symbolLayout,
1725
+ qwerty: qwertyLayout,
1726
+ number: numberLayout,
1727
+ integer: integerLayout,
1728
+ symbol: symbolLayout,
1729
1729
  "symbol-2": symbolLayout2,
1730
1730
  "license-plate": licensePlateLayout,
1731
1731
  "license-plate-province": licensePlateProvinceLayout,
1732
1732
  "license-plate-letter": licensePlateLetterLayout,
1733
1733
  "license-plate-alphanumeric": licensePlateAlphanumericLayout,
1734
- "idcard": idcardLayout,
1735
- "phone": phoneLayout,
1734
+ idcard: idcardLayout,
1735
+ phone: phoneLayout,
1736
1736
  "phone-simple": phoneSimpleLayout,
1737
- "amount": amountLayout,
1737
+ amount: amountLayout,
1738
1738
  "amount-simple": amountSimpleLayout,
1739
- "bankcard": bankcardLayout,
1740
- "verifycode": verifycodeLayout,
1741
- "email": emailLayout,
1742
- "url": urlLayout,
1743
- "search": searchLayout,
1744
- "punct": punctLayout
1739
+ bankcard: bankcardLayout,
1740
+ verifycode: verifycodeLayout,
1741
+ email: emailLayout,
1742
+ url: urlLayout,
1743
+ search: searchLayout,
1744
+ punct: punctLayout
1745
1745
  };
1746
1746
  const customLayouts = {};
1747
1747
  function registerLayout(name, layout) {
@@ -1773,102 +1773,12 @@
1773
1773
  });
1774
1774
  }
1775
1775
  //#endregion
1776
- //#region src/utils/event-emitter.ts
1777
- var EventEmitter = class {
1778
- constructor() {
1779
- this.handlers = /* @__PURE__ */ new Map();
1780
- }
1781
- /**
1782
- * 订阅事件
1783
- */
1784
- on(event, handler, options = {}) {
1785
- if (!this.handlers.has(event)) this.handlers.set(event, /* @__PURE__ */ new Set());
1786
- const handlers = this.handlers.get(event);
1787
- const wrapper = {
1788
- handler,
1789
- options
1790
- };
1791
- handlers.add(wrapper);
1792
- return () => {
1793
- handlers.delete(wrapper);
1794
- if (handlers.size === 0) this.handlers.delete(event);
1795
- };
1796
- }
1797
- /**
1798
- * 订阅事件(仅触发一次)
1799
- */
1800
- once(event, handler, options = {}) {
1801
- return this.on(event, handler, {
1802
- ...options,
1803
- once: true
1804
- });
1805
- }
1806
- /**
1807
- * 取消订阅事件
1808
- */
1809
- off(event, handler) {
1810
- if (!handler) {
1811
- this.handlers.delete(event);
1812
- return;
1813
- }
1814
- const handlers = this.handlers.get(event);
1815
- if (handlers) {
1816
- handlers.forEach((wrapper) => {
1817
- if (wrapper.handler === handler) handlers.delete(wrapper);
1818
- });
1819
- if (handlers.size === 0) this.handlers.delete(event);
1820
- }
1821
- }
1822
- /**
1823
- * 发布事件
1824
- */
1825
- emit(event, data) {
1826
- const handlers = this.handlers.get(event);
1827
- if (!handlers || handlers.size === 0) return;
1828
- const sortedHandlers = Array.from(handlers).sort((a, b) => {
1829
- const priorityA = a.options.priority ?? 0;
1830
- return (b.options.priority ?? 0) - priorityA;
1831
- });
1832
- const toRemove = [];
1833
- sortedHandlers.forEach((wrapper) => {
1834
- try {
1835
- wrapper.handler(data);
1836
- if (wrapper.options.once) toRemove.push(wrapper);
1837
- } catch (error) {
1838
- console.error(`[KeyboardEventEmitter] Error in event handler for "${String(event)}":`, error);
1839
- }
1840
- });
1841
- toRemove.forEach((wrapper) => handlers.delete(wrapper));
1842
- if (handlers.size === 0) this.handlers.delete(event);
1843
- }
1844
- /**
1845
- * 清除所有事件订阅
1846
- */
1847
- clear() {
1848
- this.handlers.clear();
1849
- }
1850
- /**
1851
- * 获取事件订阅数量
1852
- */
1853
- listenerCount(event) {
1854
- if (event !== void 0) return this.handlers.get(event)?.size ?? 0;
1855
- let count = 0;
1856
- this.handlers.forEach((handlers) => {
1857
- count += handlers.size;
1858
- });
1859
- return count;
1860
- }
1861
- };
1862
- //#endregion
1863
1776
  //#region src/utils/device.ts
1864
1777
  const MOBILE_UA_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i;
1865
1778
  let cachedIsTouchDevice = null;
1866
1779
  function isTouchDevice() {
1867
1780
  if (cachedIsTouchDevice !== null) return cachedIsTouchDevice;
1868
- if (typeof window === "undefined") {
1869
- cachedIsTouchDevice = false;
1870
- return false;
1871
- }
1781
+ if (typeof window === "undefined") return false;
1872
1782
  cachedIsTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
1873
1783
  return cachedIsTouchDevice;
1874
1784
  }
@@ -1928,7 +1838,14 @@
1928
1838
  pointer-events: none;
1929
1839
  visibility: hidden;
1930
1840
  `;
1931
- document.body.appendChild(div);
1841
+ const body = document.body;
1842
+ if (!body) return {
1843
+ top: 0,
1844
+ right: 0,
1845
+ bottom: 0,
1846
+ left: 0
1847
+ };
1848
+ body.appendChild(div);
1932
1849
  const computed = getComputedStyle(div);
1933
1850
  const safeArea = {
1934
1851
  top: Number.parseFloat(computed.top) || 0,
@@ -1936,7 +1853,7 @@
1936
1853
  bottom: Number.parseFloat(computed.bottom) || 0,
1937
1854
  left: Number.parseFloat(computed.left) || 0
1938
1855
  };
1939
- document.body.removeChild(div);
1856
+ body.removeChild(div);
1940
1857
  return safeArea;
1941
1858
  }
1942
1859
  function supportsVibrate() {
@@ -2024,16 +1941,19 @@
2024
1941
  const start = input.selectionStart ?? input.value.length;
2025
1942
  const end = input.selectionEnd ?? input.value.length;
2026
1943
  const currentValue = input.value;
2027
- let newValue = currentValue.substring(0, start) + text + currentValue.substring(end);
2028
- if (maxLength !== void 0 && newValue.length > maxLength) {
2029
- const allowedLength = maxLength - (currentValue.length - (end - start));
1944
+ const normalizedMaxLength = maxLength === void 0 || !Number.isFinite(maxLength) ? void 0 : Math.max(0, Math.floor(maxLength));
1945
+ let insertedText = text;
1946
+ let newValue = currentValue.substring(0, start) + insertedText + currentValue.substring(end);
1947
+ if (normalizedMaxLength !== void 0 && newValue.length > normalizedMaxLength) {
1948
+ const allowedLength = normalizedMaxLength - (currentValue.length - (end - start));
2030
1949
  if (allowedLength <= 0) return {
2031
1950
  value: currentValue,
2032
1951
  cursorPosition: start
2033
1952
  };
2034
- newValue = currentValue.substring(0, start) + text.substring(0, allowedLength) + currentValue.substring(end);
1953
+ insertedText = text.substring(0, allowedLength);
1954
+ newValue = currentValue.substring(0, start) + insertedText + currentValue.substring(end);
2035
1955
  }
2036
- const newCursorPosition = start + text.length;
1956
+ const newCursorPosition = start + insertedText.length;
2037
1957
  return {
2038
1958
  value: newValue,
2039
1959
  cursorPosition: Math.min(newCursorPosition, newValue.length)
@@ -2074,6 +1994,21 @@
2074
1994
  });
2075
1995
  return element;
2076
1996
  }
1997
+ function sanitizeSvgIcon(icon) {
1998
+ if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") return "";
1999
+ const document2 = new DOMParser().parseFromString(icon, "image/svg+xml");
2000
+ const svg = document2.documentElement;
2001
+ if (svg.tagName.toLowerCase() !== "svg" || document2.querySelector("parsererror")) return "";
2002
+ svg.querySelectorAll("script, style, foreignObject").forEach((node) => node.remove());
2003
+ svg.querySelectorAll("*").forEach((element) => {
2004
+ Array.from(element.attributes).forEach((attribute) => {
2005
+ const name = attribute.name.toLowerCase();
2006
+ const value = attribute.value.trim();
2007
+ if (name.startsWith("on") || (name === "href" || name === "xlink:href") && !value.startsWith("#")) element.removeAttribute(attribute.name);
2008
+ });
2009
+ });
2010
+ return new XMLSerializer().serializeToString(svg);
2011
+ }
2077
2012
  function toggleClass(element, className, force) {
2078
2013
  element.classList.toggle(className, force);
2079
2014
  }
@@ -2081,6 +2016,94 @@
2081
2016
  Object.assign(element.style, styles);
2082
2017
  }
2083
2018
  //#endregion
2019
+ //#region src/utils/event-emitter.ts
2020
+ var EventEmitter = class {
2021
+ constructor() {
2022
+ this.handlers = /* @__PURE__ */ new Map();
2023
+ }
2024
+ /**
2025
+ * 订阅事件
2026
+ */
2027
+ on(event, handler, options = {}) {
2028
+ if (!this.handlers.has(event)) this.handlers.set(event, /* @__PURE__ */ new Set());
2029
+ const handlers = this.handlers.get(event);
2030
+ if (!handlers) return () => void 0;
2031
+ const wrapper = {
2032
+ handler,
2033
+ options
2034
+ };
2035
+ handlers.add(wrapper);
2036
+ return () => {
2037
+ handlers.delete(wrapper);
2038
+ if (handlers.size === 0) this.handlers.delete(event);
2039
+ };
2040
+ }
2041
+ /**
2042
+ * 订阅事件(仅触发一次)
2043
+ */
2044
+ once(event, handler, options = {}) {
2045
+ return this.on(event, handler, {
2046
+ ...options,
2047
+ once: true
2048
+ });
2049
+ }
2050
+ /**
2051
+ * 取消订阅事件
2052
+ */
2053
+ off(event, handler) {
2054
+ if (!handler) {
2055
+ this.handlers.delete(event);
2056
+ return;
2057
+ }
2058
+ const handlers = this.handlers.get(event);
2059
+ if (handlers) {
2060
+ handlers.forEach((wrapper) => {
2061
+ if (wrapper.handler === handler) handlers.delete(wrapper);
2062
+ });
2063
+ if (handlers.size === 0) this.handlers.delete(event);
2064
+ }
2065
+ }
2066
+ /**
2067
+ * 发布事件
2068
+ */
2069
+ emit(event, data) {
2070
+ const handlers = this.handlers.get(event);
2071
+ if (!handlers || handlers.size === 0) return;
2072
+ const sortedHandlers = Array.from(handlers).sort((a, b) => {
2073
+ const priorityA = a.options.priority ?? 0;
2074
+ return (b.options.priority ?? 0) - priorityA;
2075
+ });
2076
+ const toRemove = [];
2077
+ sortedHandlers.forEach((wrapper) => {
2078
+ try {
2079
+ wrapper.handler(data);
2080
+ if (wrapper.options.once) toRemove.push(wrapper);
2081
+ } catch (error) {
2082
+ console.error(`[KeyboardEventEmitter] Error in event handler for "${String(event)}":`, error);
2083
+ }
2084
+ });
2085
+ toRemove.forEach((wrapper) => handlers.delete(wrapper));
2086
+ if (handlers.size === 0) this.handlers.delete(event);
2087
+ }
2088
+ /**
2089
+ * 清除所有事件订阅
2090
+ */
2091
+ clear() {
2092
+ this.handlers.clear();
2093
+ }
2094
+ /**
2095
+ * 获取事件订阅数量
2096
+ */
2097
+ listenerCount(event) {
2098
+ if (event !== void 0) return this.handlers.get(event)?.size ?? 0;
2099
+ let count = 0;
2100
+ this.handlers.forEach((handlers) => {
2101
+ count += handlers.size;
2102
+ });
2103
+ return count;
2104
+ }
2105
+ };
2106
+ //#endregion
2084
2107
  //#region src/managers/keyboard-manager.ts
2085
2108
  const DEFAULT_OPTIONS = {
2086
2109
  type: "qwerty",
@@ -2092,14 +2115,28 @@
2092
2115
  enableVibrate: true,
2093
2116
  customLayouts: {}
2094
2117
  };
2118
+ function normalizeMaxLength(value) {
2119
+ if (value === void 0 || !Number.isFinite(value)) return void 0;
2120
+ return Math.max(0, Math.floor(value));
2121
+ }
2122
+ function clampValue(value, maxLength) {
2123
+ return maxLength === void 0 ? value : value.slice(0, maxLength);
2124
+ }
2095
2125
  var KeyboardManager = class extends EventEmitter {
2096
2126
  constructor(options = {}) {
2097
2127
  super();
2098
2128
  /** 当前布局配置 */
2099
2129
  this.currentLayout = null;
2130
+ this.destroyed = false;
2131
+ const maxLength = normalizeMaxLength(options.maxLength);
2100
2132
  this.options = {
2101
2133
  ...DEFAULT_OPTIONS,
2102
- ...options
2134
+ ...options,
2135
+ maxLength,
2136
+ customLayouts: {
2137
+ ...DEFAULT_OPTIONS.customLayouts,
2138
+ ...options.customLayouts ?? {}
2139
+ }
2103
2140
  };
2104
2141
  if (options.customLayouts) Object.entries(options.customLayouts).forEach(([name, layout]) => {
2105
2142
  this.options.customLayouts[name] = layout;
@@ -2108,8 +2145,8 @@
2108
2145
  visible: false,
2109
2146
  type: this.options.type,
2110
2147
  isUpperCase: this.options.defaultUpperCase,
2111
- value: this.options.value,
2112
- cursorPosition: this.options.value.length,
2148
+ value: clampValue(this.options.value, this.options.maxLength),
2149
+ cursorPosition: clampValue(this.options.value, this.options.maxLength).length,
2113
2150
  maxLength: this.options.maxLength,
2114
2151
  inputElement: null
2115
2152
  };
@@ -2131,7 +2168,10 @@
2131
2168
  * 获取配置选项
2132
2169
  */
2133
2170
  getOptions() {
2134
- return { ...this.options };
2171
+ return {
2172
+ ...this.options,
2173
+ customLayouts: { ...this.options.customLayouts }
2174
+ };
2135
2175
  }
2136
2176
  /**
2137
2177
  * 加载键盘布局
@@ -2151,7 +2191,11 @@
2151
2191
  * 显示键盘
2152
2192
  */
2153
2193
  show(inputElement) {
2154
- if (this.state.visible) return;
2194
+ if (this.destroyed) return;
2195
+ if (this.state.visible) {
2196
+ if (inputElement && this.state.inputElement !== inputElement) this.bindInput(inputElement);
2197
+ return;
2198
+ }
2155
2199
  this.state.visible = true;
2156
2200
  this.state.inputElement = inputElement ?? null;
2157
2201
  if (inputElement) {
@@ -2165,6 +2209,7 @@
2165
2209
  * 隐藏键盘
2166
2210
  */
2167
2211
  hide() {
2212
+ if (this.destroyed) return;
2168
2213
  if (!this.state.visible) return;
2169
2214
  const prevType = this.state.type;
2170
2215
  this.state.visible = false;
@@ -2176,6 +2221,7 @@
2176
2221
  * 切换键盘显示/隐藏
2177
2222
  */
2178
2223
  toggle(inputElement) {
2224
+ if (this.destroyed) return;
2179
2225
  if (this.state.visible) this.hide();
2180
2226
  else this.show(inputElement);
2181
2227
  }
@@ -2183,6 +2229,7 @@
2183
2229
  * 切换键盘类型
2184
2230
  */
2185
2231
  switchType(type) {
2232
+ if (this.destroyed) return;
2186
2233
  if (this.state.type === type) return;
2187
2234
  const fromType = this.state.type;
2188
2235
  if (!this.loadLayout(type)) return;
@@ -2197,6 +2244,7 @@
2197
2244
  * 切换大小写
2198
2245
  */
2199
2246
  toggleShift() {
2247
+ if (this.destroyed) return;
2200
2248
  if (!this.currentLayout?.supportShift) return;
2201
2249
  this.state.isUpperCase = !this.state.isUpperCase;
2202
2250
  this.emitStateChange();
@@ -2206,6 +2254,7 @@
2206
2254
  * 设置大小写状态
2207
2255
  */
2208
2256
  setShift(isUpperCase) {
2257
+ if (this.destroyed) return;
2209
2258
  if (!this.currentLayout?.supportShift) return;
2210
2259
  if (this.state.isUpperCase === isUpperCase) return;
2211
2260
  this.state.isUpperCase = isUpperCase;
@@ -2216,6 +2265,7 @@
2216
2265
  * 处理按键
2217
2266
  */
2218
2267
  handleKey(key) {
2268
+ if (this.destroyed) return;
2219
2269
  if (this.options.enableVibrate && isMobile()) vibrate(10);
2220
2270
  this.emit("keypress", {
2221
2271
  key,
@@ -2261,12 +2311,15 @@
2261
2311
  setInputCursorPosition(input, result.cursorPosition);
2262
2312
  this.state.value = result.value;
2263
2313
  this.state.cursorPosition = result.cursorPosition;
2264
- input.dispatchEvent(new Event("input", { bubbles: true }));
2314
+ this.dispatchInputEvent(input);
2265
2315
  } else {
2266
- if (this.state.maxLength && this.state.value.length >= this.state.maxLength) return;
2267
- const newValue = this.state.value.slice(0, this.state.cursorPosition) + keyValue + this.state.value.slice(this.state.cursorPosition);
2316
+ if (this.state.maxLength !== void 0 && this.state.value.length >= this.state.maxLength) return;
2317
+ const availableLength = this.state.maxLength === void 0 ? keyValue.length : this.state.maxLength - this.state.value.length;
2318
+ const valueToInsert = keyValue.slice(0, Math.max(0, availableLength));
2319
+ if (!valueToInsert) return;
2320
+ const newValue = this.state.value.slice(0, this.state.cursorPosition) + valueToInsert + this.state.value.slice(this.state.cursorPosition);
2268
2321
  this.state.value = newValue;
2269
- this.state.cursorPosition += keyValue.length;
2322
+ this.state.cursorPosition += valueToInsert.length;
2270
2323
  }
2271
2324
  this.emitStateChange();
2272
2325
  this.emit("input", {
@@ -2286,7 +2339,7 @@
2286
2339
  setInputCursorPosition(input, result.cursorPosition);
2287
2340
  this.state.value = result.value;
2288
2341
  this.state.cursorPosition = result.cursorPosition;
2289
- input.dispatchEvent(new Event("input", { bubbles: true }));
2342
+ this.dispatchInputEvent(input);
2290
2343
  this.emitStateChange();
2291
2344
  this.emit("backspace", {
2292
2345
  deletedChar: result.deletedChar,
@@ -2325,7 +2378,7 @@
2325
2378
  const previousValue = this.state.value;
2326
2379
  if (this.state.inputElement) {
2327
2380
  this.state.inputElement.value = "";
2328
- this.state.inputElement.dispatchEvent(new Event("input", { bubbles: true }));
2381
+ this.dispatchInputEvent(this.state.inputElement);
2329
2382
  }
2330
2383
  this.state.value = "";
2331
2384
  this.state.cursorPosition = 0;
@@ -2367,11 +2420,14 @@
2367
2420
  * 设置值
2368
2421
  */
2369
2422
  setValue(value) {
2370
- this.state.value = value;
2371
- this.state.cursorPosition = value.length;
2423
+ if (this.destroyed) return;
2424
+ const nextValue = clampValue(value, this.state.maxLength);
2425
+ this.state.value = nextValue;
2426
+ this.state.cursorPosition = nextValue.length;
2372
2427
  if (this.state.inputElement) {
2373
- this.state.inputElement.value = value;
2374
- setInputCursorPosition(this.state.inputElement, value.length);
2428
+ this.state.inputElement.value = nextValue;
2429
+ setInputCursorPosition(this.state.inputElement, nextValue.length);
2430
+ this.dispatchInputEvent(this.state.inputElement);
2375
2431
  }
2376
2432
  this.emitStateChange();
2377
2433
  this.autoSwitchLicensePlate();
@@ -2380,6 +2436,7 @@
2380
2436
  * 追加值(在光标位置插入文本)
2381
2437
  */
2382
2438
  appendValue(text) {
2439
+ if (this.destroyed) return;
2383
2440
  if (!text) return;
2384
2441
  const input = this.state.inputElement;
2385
2442
  if (input) {
@@ -2388,55 +2445,360 @@
2388
2445
  setInputCursorPosition(input, result.cursorPosition);
2389
2446
  this.state.value = result.value;
2390
2447
  this.state.cursorPosition = result.cursorPosition;
2391
- input.dispatchEvent(new Event("input", { bubbles: true }));
2448
+ this.dispatchInputEvent(input);
2392
2449
  } else {
2393
- if (this.state.maxLength && this.state.value.length + text.length > this.state.maxLength) text = text.slice(0, this.state.maxLength - this.state.value.length);
2450
+ if (this.state.maxLength !== void 0) text = text.slice(0, Math.max(0, this.state.maxLength - this.state.value.length));
2451
+ if (!text) return;
2394
2452
  const newValue = this.state.value.slice(0, this.state.cursorPosition) + text + this.state.value.slice(this.state.cursorPosition);
2395
2453
  this.state.value = newValue;
2396
2454
  this.state.cursorPosition += text.length;
2397
2455
  }
2398
- this.emitStateChange();
2399
- }
2400
- /**
2401
- * 设置最大长度
2402
- */
2403
- setMaxLength(maxLength) {
2404
- this.state.maxLength = maxLength;
2405
- this.emitStateChange();
2406
- }
2407
- /**
2408
- * 绑定输入框
2409
- */
2410
- bindInput(input) {
2411
- this.state.inputElement = input;
2412
- this.state.value = input.value;
2413
- this.state.cursorPosition = getInputCursorPosition(input);
2414
- this.emitStateChange();
2415
- }
2416
- /**
2417
- * 解绑输入框
2418
- */
2419
- unbindInput() {
2420
- this.state.inputElement = null;
2421
- this.emitStateChange();
2422
- }
2423
- /**
2424
- * 发送状态变化事件
2425
- */
2426
- emitStateChange() {
2427
- this.emit("stateChange", { ...this.state });
2428
- }
2429
- /**
2430
- * 销毁
2431
- */
2432
- destroy() {
2433
- this.hide();
2434
- this.clear();
2435
- this.state.inputElement = null;
2436
- }
2437
- };
2438
- function createKeyboardManager(options) {
2439
- return new KeyboardManager(options);
2456
+ this.emitStateChange();
2457
+ }
2458
+ /**
2459
+ * 设置最大长度
2460
+ */
2461
+ setMaxLength(maxLength) {
2462
+ if (this.destroyed) return;
2463
+ const normalizedMaxLength = normalizeMaxLength(maxLength);
2464
+ this.state.maxLength = normalizedMaxLength;
2465
+ const nextValue = clampValue(this.state.value, normalizedMaxLength);
2466
+ const changed = nextValue !== this.state.value;
2467
+ this.state.value = nextValue;
2468
+ this.state.cursorPosition = Math.min(this.state.cursorPosition, nextValue.length);
2469
+ if (this.state.inputElement && this.state.inputElement.value !== nextValue) {
2470
+ this.state.inputElement.value = nextValue;
2471
+ setInputCursorPosition(this.state.inputElement, this.state.cursorPosition);
2472
+ this.dispatchInputEvent(this.state.inputElement);
2473
+ }
2474
+ this.emitStateChange();
2475
+ if (changed) this.autoSwitchLicensePlate();
2476
+ }
2477
+ /**
2478
+ * 绑定输入框
2479
+ */
2480
+ bindInput(input) {
2481
+ if (this.destroyed) return;
2482
+ this.state.inputElement = input;
2483
+ const nextValue = clampValue(input.value, this.state.maxLength);
2484
+ if (input.value !== nextValue) input.value = nextValue;
2485
+ this.state.value = nextValue;
2486
+ this.state.cursorPosition = Math.min(getInputCursorPosition(input), nextValue.length);
2487
+ this.emitStateChange();
2488
+ }
2489
+ /**
2490
+ * 解绑输入框
2491
+ */
2492
+ unbindInput() {
2493
+ if (this.destroyed) return;
2494
+ this.state.inputElement = null;
2495
+ this.emitStateChange();
2496
+ }
2497
+ /**
2498
+ * 发送状态变化事件
2499
+ */
2500
+ emitStateChange() {
2501
+ this.emit("stateChange", { ...this.state });
2502
+ }
2503
+ dispatchInputEvent(input) {
2504
+ const EventConstructor = input.ownerDocument.defaultView?.Event ?? Event;
2505
+ input.dispatchEvent(new EventConstructor("input", { bubbles: true }));
2506
+ }
2507
+ /**
2508
+ * 销毁
2509
+ */
2510
+ destroy() {
2511
+ if (this.destroyed) return;
2512
+ this.hide();
2513
+ this.clear();
2514
+ this.state.inputElement = null;
2515
+ this.destroyed = true;
2516
+ }
2517
+ };
2518
+ function createKeyboardManager(options) {
2519
+ return new KeyboardManager(options);
2520
+ }
2521
+ //#endregion
2522
+ //#region src/themes/presets.ts
2523
+ const miuiTheme = {
2524
+ name: "miui",
2525
+ displayName: "MIUI",
2526
+ description: "小米风格 - 纯净简洁",
2527
+ isDark: false,
2528
+ colors: {
2529
+ background: "#f5f5f5",
2530
+ keyBackground: "#ffffff",
2531
+ keyColor: "#1a1a1a",
2532
+ keyActiveBackground: "#e8e8e8",
2533
+ fnKeyBackground: "#e0e0e0",
2534
+ fnKeyColor: "#666666",
2535
+ fnKeyActiveBackground: "#d0d0d0",
2536
+ primaryBackground: "#ff6700",
2537
+ primaryActiveBackground: "#e55d00",
2538
+ primaryColor: "#ffffff",
2539
+ borderColor: "transparent",
2540
+ dividerColor: "#eeeeee"
2541
+ },
2542
+ sizes: {
2543
+ keyHeight: 46,
2544
+ keyRadius: 8,
2545
+ keyGap: 6,
2546
+ fontSize: 18,
2547
+ fnFontSize: 14,
2548
+ padding: 8
2549
+ },
2550
+ effects: {
2551
+ keyShadow: "none",
2552
+ keyActiveShadow: "none",
2553
+ keyActiveScale: .96,
2554
+ transitionDuration: 100,
2555
+ enableBlur: false,
2556
+ blurAmount: 0,
2557
+ enableRipple: false
2558
+ }
2559
+ };
2560
+ const iosTheme = {
2561
+ name: "ios",
2562
+ displayName: "iOS",
2563
+ description: "Apple 风格 - 精致优雅",
2564
+ isDark: false,
2565
+ colors: {
2566
+ background: "#d1d3d9",
2567
+ keyBackground: "#ffffff",
2568
+ keyColor: "#000000",
2569
+ keyActiveBackground: "#b8bac2",
2570
+ fnKeyBackground: "#a8aab4",
2571
+ fnKeyColor: "#000000",
2572
+ fnKeyActiveBackground: "#9699a3",
2573
+ primaryBackground: "#007aff",
2574
+ primaryActiveBackground: "#0062cc",
2575
+ primaryColor: "#ffffff",
2576
+ borderColor: "transparent",
2577
+ dividerColor: "#c8cad0"
2578
+ },
2579
+ sizes: {
2580
+ keyHeight: 43,
2581
+ keyRadius: 5,
2582
+ keyGap: 6,
2583
+ fontSize: 22,
2584
+ fnFontSize: 15,
2585
+ padding: 4
2586
+ },
2587
+ effects: {
2588
+ keyShadow: "0 1px 0 rgba(0,0,0,0.35)",
2589
+ keyActiveShadow: "0 0 0 rgba(0,0,0,0)",
2590
+ keyActiveScale: 1,
2591
+ transitionDuration: 80,
2592
+ enableBlur: true,
2593
+ blurAmount: 20,
2594
+ enableRipple: false
2595
+ }
2596
+ };
2597
+ const materialTheme = {
2598
+ name: "material",
2599
+ displayName: "Material",
2600
+ description: "Google 风格 - 深邃现代",
2601
+ isDark: true,
2602
+ colors: {
2603
+ background: "#263238",
2604
+ keyBackground: "#37474f",
2605
+ keyColor: "#ffffff",
2606
+ keyActiveBackground: "#455a64",
2607
+ fnKeyBackground: "#455a64",
2608
+ fnKeyColor: "#b0bec5",
2609
+ fnKeyActiveBackground: "#546e7a",
2610
+ primaryBackground: "#4caf50",
2611
+ primaryActiveBackground: "#43a047",
2612
+ primaryColor: "#ffffff",
2613
+ borderColor: "transparent",
2614
+ dividerColor: "#37474f"
2615
+ },
2616
+ sizes: {
2617
+ keyHeight: 48,
2618
+ keyRadius: 4,
2619
+ keyGap: 4,
2620
+ fontSize: 20,
2621
+ fnFontSize: 14,
2622
+ padding: 6
2623
+ },
2624
+ effects: {
2625
+ keyShadow: "0 1px 3px rgba(0,0,0,0.2)",
2626
+ keyActiveShadow: "0 2px 6px rgba(0,0,0,0.3)",
2627
+ keyActiveScale: 1,
2628
+ transitionDuration: 150,
2629
+ enableBlur: false,
2630
+ blurAmount: 0,
2631
+ enableRipple: true
2632
+ }
2633
+ };
2634
+ const minimalTheme = {
2635
+ name: "minimal",
2636
+ displayName: "Minimal",
2637
+ description: "极简风格 - 轻盈透明",
2638
+ isDark: false,
2639
+ colors: {
2640
+ background: "rgba(250, 250, 250, 0.95)",
2641
+ keyBackground: "transparent",
2642
+ keyColor: "#333333",
2643
+ keyActiveBackground: "rgba(0, 0, 0, 0.08)",
2644
+ fnKeyBackground: "transparent",
2645
+ fnKeyColor: "#888888",
2646
+ fnKeyActiveBackground: "rgba(0, 0, 0, 0.08)",
2647
+ primaryBackground: "#333333",
2648
+ primaryActiveBackground: "#000000",
2649
+ primaryColor: "#ffffff",
2650
+ borderColor: "#eeeeee",
2651
+ dividerColor: "#eeeeee"
2652
+ },
2653
+ sizes: {
2654
+ keyHeight: 44,
2655
+ keyRadius: 12,
2656
+ keyGap: 8,
2657
+ fontSize: 17,
2658
+ fnFontSize: 13,
2659
+ padding: 12
2660
+ },
2661
+ effects: {
2662
+ keyShadow: "none",
2663
+ keyActiveShadow: "none",
2664
+ keyActiveScale: .92,
2665
+ transitionDuration: 120,
2666
+ enableBlur: true,
2667
+ blurAmount: 30,
2668
+ enableRipple: false
2669
+ }
2670
+ };
2671
+ const darkTheme = {
2672
+ name: "dark",
2673
+ displayName: "Dark",
2674
+ description: "深色模式 - 护眼舒适",
2675
+ isDark: true,
2676
+ colors: {
2677
+ background: "#1c1c1e",
2678
+ keyBackground: "#3a3a3c",
2679
+ keyColor: "#ffffff",
2680
+ keyActiveBackground: "#4a4a4c",
2681
+ fnKeyBackground: "#2c2c2e",
2682
+ fnKeyColor: "#ebebf5",
2683
+ fnKeyActiveBackground: "#3c3c3e",
2684
+ primaryBackground: "#0a84ff",
2685
+ primaryActiveBackground: "#0070e0",
2686
+ primaryColor: "#ffffff",
2687
+ borderColor: "#3a3a3c",
2688
+ dividerColor: "#3a3a3c"
2689
+ },
2690
+ sizes: {
2691
+ keyHeight: 45,
2692
+ keyRadius: 8,
2693
+ keyGap: 6,
2694
+ fontSize: 18,
2695
+ fnFontSize: 14,
2696
+ padding: 8
2697
+ },
2698
+ effects: {
2699
+ keyShadow: "0 1px 2px rgba(0,0,0,0.4)",
2700
+ keyActiveShadow: "0 0 8px rgba(10,132,255,0.3)",
2701
+ keyActiveScale: .97,
2702
+ transitionDuration: 100,
2703
+ enableBlur: true,
2704
+ blurAmount: 20,
2705
+ enableRipple: false
2706
+ }
2707
+ };
2708
+ const presetThemes = {
2709
+ miui: miuiTheme,
2710
+ ios: iosTheme,
2711
+ material: materialTheme,
2712
+ minimal: minimalTheme,
2713
+ dark: darkTheme
2714
+ };
2715
+ const defaultTheme = miuiTheme;
2716
+ //#endregion
2717
+ //#region src/themes/index.ts
2718
+ const customThemes = /* @__PURE__ */ new Map();
2719
+ let currentTheme = defaultTheme;
2720
+ function getTheme(name) {
2721
+ return customThemes.get(name) || presetThemes[name] || defaultTheme;
2722
+ }
2723
+ function registerTheme(theme) {
2724
+ customThemes.set(theme.name, theme);
2725
+ }
2726
+ function getAvailableThemes() {
2727
+ return [...Object.keys(presetThemes), ...customThemes.keys()];
2728
+ }
2729
+ function getAllThemes() {
2730
+ return [...Object.values(presetThemes), ...customThemes.values()];
2731
+ }
2732
+ function setCurrentTheme(name) {
2733
+ currentTheme = getTheme(name);
2734
+ return currentTheme;
2735
+ }
2736
+ function getCurrentTheme() {
2737
+ return currentTheme;
2738
+ }
2739
+ function themeToCSS(theme) {
2740
+ return {
2741
+ "--keyboard-bg": theme.colors.background,
2742
+ "--keyboard-key-bg": theme.colors.keyBackground,
2743
+ "--keyboard-key-color": theme.colors.keyColor,
2744
+ "--keyboard-key-active-bg": theme.colors.keyActiveBackground,
2745
+ "--keyboard-fn-key-bg": theme.colors.fnKeyBackground,
2746
+ "--keyboard-fn-key-color": theme.colors.fnKeyColor,
2747
+ "--keyboard-fn-key-active-bg": theme.colors.fnKeyActiveBackground,
2748
+ "--keyboard-primary-bg": theme.colors.primaryBackground,
2749
+ "--keyboard-primary-active-bg": theme.colors.primaryActiveBackground,
2750
+ "--keyboard-primary-color": theme.colors.primaryColor,
2751
+ "--keyboard-key-height": `${theme.sizes.keyHeight}px`,
2752
+ "--keyboard-key-radius": `${theme.sizes.keyRadius}px`,
2753
+ "--keyboard-key-gap": `${theme.sizes.keyGap}px`,
2754
+ "--keyboard-font-size": `${theme.sizes.fontSize}px`,
2755
+ "--keyboard-fn-font-size": `${theme.sizes.fnFontSize}px`,
2756
+ "--keyboard-padding": `${theme.sizes.padding}px`,
2757
+ "--keyboard-key-shadow": theme.effects.keyShadow,
2758
+ "--keyboard-mask-bg": "color-mix(in srgb, var(--ld-black) 40%, var(--ld-transparent))",
2759
+ "--keyboard-transition-duration": `${theme.effects.transitionDuration}ms`,
2760
+ "--keyboard-border-color": theme.colors.borderColor,
2761
+ "--keyboard-divider-color": theme.colors.dividerColor
2762
+ };
2763
+ }
2764
+ function applyTheme(element, theme) {
2765
+ const cssVars = themeToCSS(theme);
2766
+ Object.entries(cssVars).forEach(([key, value]) => {
2767
+ if (value) element.style.setProperty(key, value);
2768
+ });
2769
+ element.classList.remove("keyboard-theme-light", "keyboard-theme-dark");
2770
+ element.classList.add(theme.isDark ? "keyboard-theme-dark" : "keyboard-theme-light");
2771
+ element.dataset.theme = theme.name;
2772
+ }
2773
+ function generateThemeStyle(theme) {
2774
+ const cssVars = themeToCSS(theme);
2775
+ return Object.entries(cssVars).filter(([, value]) => value).map(([key, value]) => `${key}: ${value}`).join("; ");
2776
+ }
2777
+ function mergeTheme(base, overrides) {
2778
+ return {
2779
+ ...base,
2780
+ ...overrides,
2781
+ colors: {
2782
+ ...base.colors,
2783
+ ...overrides.colors
2784
+ },
2785
+ sizes: {
2786
+ ...base.sizes,
2787
+ ...overrides.sizes
2788
+ },
2789
+ effects: {
2790
+ ...base.effects,
2791
+ ...overrides.effects
2792
+ }
2793
+ };
2794
+ }
2795
+ function createTheme(name, baseTheme, customization) {
2796
+ const theme = mergeTheme(getTheme(baseTheme), {
2797
+ ...customization,
2798
+ name
2799
+ });
2800
+ registerTheme(theme);
2801
+ return theme;
2440
2802
  }
2441
2803
  //#endregion
2442
2804
  //#region src/managers/position-manager.ts
@@ -2453,29 +2815,48 @@
2453
2815
  maskClosable: true,
2454
2816
  animationDuration: 300
2455
2817
  };
2818
+ function mergeConfig(config = {}) {
2819
+ return {
2820
+ ...DEFAULT_CONFIG,
2821
+ ...config,
2822
+ offset: {
2823
+ ...DEFAULT_CONFIG.offset,
2824
+ ...config.offset
2825
+ }
2826
+ };
2827
+ }
2456
2828
  var PositionManager = class {
2457
2829
  constructor(config = {}) {
2458
- this.config = {
2459
- ...DEFAULT_CONFIG,
2460
- ...config
2461
- };
2830
+ this.config = mergeConfig(config);
2462
2831
  }
2463
2832
  /**
2464
2833
  * 更新配置
2465
2834
  */
2466
2835
  updateConfig(config) {
2467
- Object.assign(this.config, config);
2836
+ this.config = mergeConfig({
2837
+ ...this.config,
2838
+ ...config,
2839
+ offset: {
2840
+ ...this.config.offset,
2841
+ ...config.offset
2842
+ }
2843
+ });
2468
2844
  }
2469
2845
  /**
2470
2846
  * 获取配置
2471
2847
  */
2472
2848
  getConfig() {
2473
- return { ...this.config };
2849
+ return {
2850
+ ...this.config,
2851
+ offset: { ...this.config.offset }
2852
+ };
2474
2853
  }
2475
2854
  /**
2476
2855
  * 计算弹出位置
2477
2856
  * @param targetElement - 目标元素(如 input)
2478
2857
  * @param popupSize - 弹出层尺寸
2858
+ * @param popupSize.width - Popup width
2859
+ * @param popupSize.height - Popup height
2479
2860
  * @returns 位置计算结果
2480
2861
  */
2481
2862
  calculate(targetElement, popupSize) {
@@ -2493,15 +2874,17 @@
2493
2874
  */
2494
2875
  calculateBottomSheet(viewport, popupSize) {
2495
2876
  const safeAreaBottom = viewport.safeArea?.bottom ?? 0;
2877
+ const maxHeight = Math.max(0, viewport.height * .6 - safeAreaBottom);
2878
+ const visibleHeight = Math.min(Math.max(0, popupSize.height), maxHeight);
2496
2879
  return {
2497
2880
  position: {
2498
2881
  x: 0,
2499
- y: viewport.height - popupSize.height - safeAreaBottom
2882
+ y: Math.max(0, viewport.height - visibleHeight - safeAreaBottom)
2500
2883
  },
2501
2884
  placement: "bottom-sheet",
2502
2885
  flipped: false,
2503
- maxHeight: viewport.height * .6,
2504
- maxWidth: viewport.width,
2886
+ maxHeight,
2887
+ maxWidth: Math.max(0, viewport.width),
2505
2888
  transform: "translateY(0)"
2506
2889
  };
2507
2890
  }
@@ -2511,8 +2894,8 @@
2511
2894
  calculateCentered(viewport, popupSize) {
2512
2895
  return {
2513
2896
  position: {
2514
- x: (viewport.width - popupSize.width) / 2,
2515
- y: (viewport.height - popupSize.height) / 2
2897
+ x: Math.max(0, (viewport.width - popupSize.width) / 2),
2898
+ y: Math.max(0, (viewport.height - popupSize.height) / 2)
2516
2899
  },
2517
2900
  placement: "bottom",
2518
2901
  flipped: false
@@ -2554,16 +2937,16 @@
2554
2937
  }
2555
2938
  }
2556
2939
  if (autoAdjust) {
2557
- if (position.x < viewportPadding) position.x = viewportPadding;
2558
- if (position.x + popupSize.width > viewport.width - viewportPadding) position.x = viewport.width - popupSize.width - viewportPadding;
2940
+ const maxX = Math.max(viewportPadding, viewport.width - popupSize.width - viewportPadding);
2941
+ position.x = Math.min(Math.max(position.x, viewportPadding), maxX);
2559
2942
  }
2560
2943
  const maxHeight = placement === "bottom" ? viewport.height - position.y - viewportPadding : targetRect.y - viewportPadding;
2561
2944
  return {
2562
2945
  position,
2563
2946
  placement,
2564
2947
  flipped,
2565
- maxHeight: Math.max(200, maxHeight),
2566
- maxWidth: viewport.width - viewportPadding * 2
2948
+ maxHeight: Math.max(0, maxHeight),
2949
+ maxWidth: Math.max(0, viewport.width - viewportPadding * 2)
2567
2950
  };
2568
2951
  }
2569
2952
  /**
@@ -2576,11 +2959,11 @@
2576
2959
  opacity: "1"
2577
2960
  };
2578
2961
  case "bottom": return {
2579
- transform: "translateY(-10px)",
2962
+ transform: "translateY(calc(-1 * var(--ld-px-10)))",
2580
2963
  opacity: "0"
2581
2964
  };
2582
2965
  case "top": return {
2583
- transform: "translateY(10px)",
2966
+ transform: "translateY(var(--ld-px-10))",
2584
2967
  opacity: "0"
2585
2968
  };
2586
2969
  default: return { opacity: "0" };
@@ -2589,323 +2972,43 @@
2589
2972
  /**
2590
2973
  * 获取弹出层最终样式(动画结束)
2591
2974
  */
2592
- getFinalStyles(placement) {
2975
+ getFinalStyles(_placement) {
2593
2976
  return {
2594
2977
  transform: "translateY(0)",
2595
- opacity: "1"
2596
- };
2597
- }
2598
- /**
2599
- * 获取弹出层退出样式
2600
- */
2601
- getExitStyles(placement) {
2602
- return this.getInitialStyles(placement);
2603
- }
2604
- /**
2605
- * 获取动画时长
2606
- */
2607
- getAnimationDuration() {
2608
- return this.config.animationDuration;
2609
- }
2610
- /**
2611
- * 检查是否显示遮罩
2612
- */
2613
- shouldShowMask() {
2614
- return isMobile() && this.config.showMask;
2615
- }
2616
- /**
2617
- * 检查遮罩是否可点击关闭
2618
- */
2619
- isMaskClosable() {
2620
- return this.config.maskClosable;
2621
- }
2622
- };
2623
- function createPositionManager(config) {
2624
- return new PositionManager(config);
2625
- }
2626
- //#endregion
2627
- //#region src/themes/presets.ts
2628
- const miuiTheme = {
2629
- name: "miui",
2630
- displayName: "MIUI",
2631
- description: "小米风格 - 纯净简洁",
2632
- isDark: false,
2633
- colors: {
2634
- background: "#f5f5f5",
2635
- keyBackground: "#ffffff",
2636
- keyColor: "#1a1a1a",
2637
- keyActiveBackground: "#e8e8e8",
2638
- fnKeyBackground: "#e0e0e0",
2639
- fnKeyColor: "#666666",
2640
- fnKeyActiveBackground: "#d0d0d0",
2641
- primaryBackground: "#ff6700",
2642
- primaryActiveBackground: "#e55d00",
2643
- primaryColor: "#ffffff",
2644
- borderColor: "transparent",
2645
- dividerColor: "#eeeeee"
2646
- },
2647
- sizes: {
2648
- keyHeight: 46,
2649
- keyRadius: 8,
2650
- keyGap: 6,
2651
- fontSize: 18,
2652
- fnFontSize: 14,
2653
- padding: 8
2654
- },
2655
- effects: {
2656
- keyShadow: "none",
2657
- keyActiveShadow: "none",
2658
- keyActiveScale: .96,
2659
- transitionDuration: 100,
2660
- enableBlur: false,
2661
- blurAmount: 0,
2662
- enableRipple: false
2663
- }
2664
- };
2665
- const iosTheme = {
2666
- name: "ios",
2667
- displayName: "iOS",
2668
- description: "Apple 风格 - 精致优雅",
2669
- isDark: false,
2670
- colors: {
2671
- background: "#d1d3d9",
2672
- keyBackground: "#ffffff",
2673
- keyColor: "#000000",
2674
- keyActiveBackground: "#b8bac2",
2675
- fnKeyBackground: "#a8aab4",
2676
- fnKeyColor: "#000000",
2677
- fnKeyActiveBackground: "#9699a3",
2678
- primaryBackground: "#007aff",
2679
- primaryActiveBackground: "#0062cc",
2680
- primaryColor: "#ffffff",
2681
- borderColor: "transparent",
2682
- dividerColor: "#c8cad0"
2683
- },
2684
- sizes: {
2685
- keyHeight: 43,
2686
- keyRadius: 5,
2687
- keyGap: 6,
2688
- fontSize: 22,
2689
- fnFontSize: 15,
2690
- padding: 4
2691
- },
2692
- effects: {
2693
- keyShadow: "0 1px 0 rgba(0,0,0,0.35)",
2694
- keyActiveShadow: "0 0 0 rgba(0,0,0,0)",
2695
- keyActiveScale: 1,
2696
- transitionDuration: 80,
2697
- enableBlur: true,
2698
- blurAmount: 20,
2699
- enableRipple: false
2700
- }
2701
- };
2702
- const materialTheme = {
2703
- name: "material",
2704
- displayName: "Material",
2705
- description: "Google 风格 - 深邃现代",
2706
- isDark: true,
2707
- colors: {
2708
- background: "#263238",
2709
- keyBackground: "#37474f",
2710
- keyColor: "#ffffff",
2711
- keyActiveBackground: "#455a64",
2712
- fnKeyBackground: "#455a64",
2713
- fnKeyColor: "#b0bec5",
2714
- fnKeyActiveBackground: "#546e7a",
2715
- primaryBackground: "#4caf50",
2716
- primaryActiveBackground: "#43a047",
2717
- primaryColor: "#ffffff",
2718
- borderColor: "transparent",
2719
- dividerColor: "#37474f"
2720
- },
2721
- sizes: {
2722
- keyHeight: 48,
2723
- keyRadius: 4,
2724
- keyGap: 4,
2725
- fontSize: 20,
2726
- fnFontSize: 14,
2727
- padding: 6
2728
- },
2729
- effects: {
2730
- keyShadow: "0 1px 3px rgba(0,0,0,0.2)",
2731
- keyActiveShadow: "0 2px 6px rgba(0,0,0,0.3)",
2732
- keyActiveScale: 1,
2733
- transitionDuration: 150,
2734
- enableBlur: false,
2735
- blurAmount: 0,
2736
- enableRipple: true
2737
- }
2738
- };
2739
- const minimalTheme = {
2740
- name: "minimal",
2741
- displayName: "Minimal",
2742
- description: "极简风格 - 轻盈透明",
2743
- isDark: false,
2744
- colors: {
2745
- background: "rgba(250, 250, 250, 0.95)",
2746
- keyBackground: "transparent",
2747
- keyColor: "#333333",
2748
- keyActiveBackground: "rgba(0, 0, 0, 0.08)",
2749
- fnKeyBackground: "transparent",
2750
- fnKeyColor: "#888888",
2751
- fnKeyActiveBackground: "rgba(0, 0, 0, 0.08)",
2752
- primaryBackground: "#333333",
2753
- primaryActiveBackground: "#000000",
2754
- primaryColor: "#ffffff",
2755
- borderColor: "#eeeeee",
2756
- dividerColor: "#eeeeee"
2757
- },
2758
- sizes: {
2759
- keyHeight: 44,
2760
- keyRadius: 12,
2761
- keyGap: 8,
2762
- fontSize: 17,
2763
- fnFontSize: 13,
2764
- padding: 12
2765
- },
2766
- effects: {
2767
- keyShadow: "none",
2768
- keyActiveShadow: "none",
2769
- keyActiveScale: .92,
2770
- transitionDuration: 120,
2771
- enableBlur: true,
2772
- blurAmount: 30,
2773
- enableRipple: false
2978
+ opacity: "1"
2979
+ };
2774
2980
  }
2775
- };
2776
- const darkTheme = {
2777
- name: "dark",
2778
- displayName: "Dark",
2779
- description: "深色模式 - 护眼舒适",
2780
- isDark: true,
2781
- colors: {
2782
- background: "#1c1c1e",
2783
- keyBackground: "#3a3a3c",
2784
- keyColor: "#ffffff",
2785
- keyActiveBackground: "#4a4a4c",
2786
- fnKeyBackground: "#2c2c2e",
2787
- fnKeyColor: "#ebebf5",
2788
- fnKeyActiveBackground: "#3c3c3e",
2789
- primaryBackground: "#0a84ff",
2790
- primaryActiveBackground: "#0070e0",
2791
- primaryColor: "#ffffff",
2792
- borderColor: "#3a3a3c",
2793
- dividerColor: "#3a3a3c"
2794
- },
2795
- sizes: {
2796
- keyHeight: 45,
2797
- keyRadius: 8,
2798
- keyGap: 6,
2799
- fontSize: 18,
2800
- fnFontSize: 14,
2801
- padding: 8
2802
- },
2803
- effects: {
2804
- keyShadow: "0 1px 2px rgba(0,0,0,0.4)",
2805
- keyActiveShadow: "0 0 8px rgba(10,132,255,0.3)",
2806
- keyActiveScale: .97,
2807
- transitionDuration: 100,
2808
- enableBlur: true,
2809
- blurAmount: 20,
2810
- enableRipple: false
2981
+ /**
2982
+ * 获取弹出层退出样式
2983
+ */
2984
+ getExitStyles(_placement) {
2985
+ return this.getInitialStyles(_placement);
2986
+ }
2987
+ /**
2988
+ * 获取动画时长
2989
+ */
2990
+ getAnimationDuration() {
2991
+ return this.config.animationDuration;
2992
+ }
2993
+ /**
2994
+ * 检查是否显示遮罩
2995
+ */
2996
+ shouldShowMask() {
2997
+ return isMobile() && this.config.showMask;
2998
+ }
2999
+ /**
3000
+ * 检查遮罩是否可点击关闭
3001
+ */
3002
+ isMaskClosable() {
3003
+ return this.config.maskClosable;
2811
3004
  }
2812
3005
  };
2813
- const presetThemes = {
2814
- miui: miuiTheme,
2815
- ios: iosTheme,
2816
- material: materialTheme,
2817
- minimal: minimalTheme,
2818
- dark: darkTheme
2819
- };
2820
- const defaultTheme = miuiTheme;
2821
- //#endregion
2822
- //#region src/themes/index.ts
2823
- const customThemes = /* @__PURE__ */ new Map();
2824
- let currentTheme = defaultTheme;
2825
- function getTheme(name) {
2826
- return customThemes.get(name) || presetThemes[name] || defaultTheme;
2827
- }
2828
- function registerTheme(theme) {
2829
- customThemes.set(theme.name, theme);
2830
- }
2831
- function getAvailableThemes() {
2832
- return [...Object.keys(presetThemes), ...customThemes.keys()];
2833
- }
2834
- function getAllThemes() {
2835
- return [...Object.values(presetThemes), ...customThemes.values()];
2836
- }
2837
- function setCurrentTheme(name) {
2838
- currentTheme = getTheme(name);
2839
- return currentTheme;
2840
- }
2841
- function getCurrentTheme() {
2842
- return currentTheme;
2843
- }
2844
- function themeToCSS(theme) {
2845
- return {
2846
- "--keyboard-bg": theme.colors.background,
2847
- "--keyboard-key-bg": theme.colors.keyBackground,
2848
- "--keyboard-key-color": theme.colors.keyColor,
2849
- "--keyboard-key-active-bg": theme.colors.keyActiveBackground,
2850
- "--keyboard-fn-key-bg": theme.colors.fnKeyBackground,
2851
- "--keyboard-fn-key-color": theme.colors.fnKeyColor,
2852
- "--keyboard-fn-key-active-bg": theme.colors.fnKeyActiveBackground,
2853
- "--keyboard-primary-bg": theme.colors.primaryBackground,
2854
- "--keyboard-primary-active-bg": theme.colors.primaryActiveBackground,
2855
- "--keyboard-primary-color": theme.colors.primaryColor,
2856
- "--keyboard-key-height": `${theme.sizes.keyHeight}px`,
2857
- "--keyboard-key-radius": `${theme.sizes.keyRadius}px`,
2858
- "--keyboard-key-gap": `${theme.sizes.keyGap}px`,
2859
- "--keyboard-font-size": `${theme.sizes.fontSize}px`,
2860
- "--keyboard-fn-font-size": `${theme.sizes.fnFontSize}px`,
2861
- "--keyboard-padding": `${theme.sizes.padding}px`,
2862
- "--keyboard-key-shadow": theme.effects.keyShadow,
2863
- "--keyboard-transition-duration": `${theme.effects.transitionDuration}ms`,
2864
- "--keyboard-border-color": theme.colors.borderColor,
2865
- "--keyboard-divider-color": theme.colors.dividerColor
2866
- };
2867
- }
2868
- function applyTheme(element, theme) {
2869
- const cssVars = themeToCSS(theme);
2870
- Object.entries(cssVars).forEach(([key, value]) => {
2871
- if (value) element.style.setProperty(key, value);
2872
- });
2873
- element.classList.remove("keyboard-theme-light", "keyboard-theme-dark");
2874
- element.classList.add(theme.isDark ? "keyboard-theme-dark" : "keyboard-theme-light");
2875
- element.dataset.theme = theme.name;
2876
- }
2877
- function generateThemeStyle(theme) {
2878
- const cssVars = themeToCSS(theme);
2879
- return Object.entries(cssVars).filter(([, value]) => value).map(([key, value]) => `${key}: ${value}`).join("; ");
2880
- }
2881
- function mergeTheme(base, overrides) {
2882
- return {
2883
- ...base,
2884
- ...overrides,
2885
- colors: {
2886
- ...base.colors,
2887
- ...overrides.colors
2888
- },
2889
- sizes: {
2890
- ...base.sizes,
2891
- ...overrides.sizes
2892
- },
2893
- effects: {
2894
- ...base.effects,
2895
- ...overrides.effects
2896
- }
2897
- };
2898
- }
2899
- function createTheme(name, baseTheme, customization) {
2900
- const theme = mergeTheme(getTheme(baseTheme), {
2901
- ...customization,
2902
- name
2903
- });
2904
- registerTheme(theme);
2905
- return theme;
3006
+ function createPositionManager(config) {
3007
+ return new PositionManager(config);
2906
3008
  }
2907
3009
  //#endregion
2908
3010
  //#region src/managers/keyboard-renderer.ts
3011
+ const rendererStyles = /* @__PURE__ */ new WeakMap();
2909
3012
  var KeyboardRenderer = class {
2910
3013
  constructor(options = {}) {
2911
3014
  /** DOM 元素 */
@@ -2916,10 +3019,14 @@
2916
3019
  this.targetEl = null;
2917
3020
  /** 清理函数 */
2918
3021
  this.cleanupFns = [];
3022
+ this.inputCleanupFns = /* @__PURE__ */ new Set();
3023
+ this.styleDocument = null;
2919
3024
  /** 是否已销毁 */
2920
3025
  this.destroyed = false;
2921
3026
  this.options = options;
2922
- this.container = options.container || document.body;
3027
+ const container = options.container ?? (typeof document !== "undefined" ? document.body : null);
3028
+ if (!container) throw new Error("[KeyboardRenderer] A DOM container is required in non-browser environments");
3029
+ this.container = container;
2923
3030
  this.manager = new KeyboardManager(options);
2924
3031
  this.positionManager = new PositionManager(options.position);
2925
3032
  if (typeof options.theme === "string") this.theme = getTheme(options.theme);
@@ -2941,17 +3048,12 @@
2941
3048
  this.manager.on("confirm", ({ value }) => {
2942
3049
  this.options.onConfirm?.(value);
2943
3050
  });
2944
- this.manager.on("switch", () => {
2945
- if (this.manager.getState().visible) this.render();
2946
- });
2947
- this.manager.on("shift", () => {
2948
- if (this.manager.getState().visible) this.render();
2949
- });
2950
3051
  }
2951
3052
  /**
2952
3053
  * 打开键盘
2953
3054
  */
2954
3055
  open(target) {
3056
+ if (this.destroyed) return;
2955
3057
  this.targetEl = target ?? null;
2956
3058
  this.manager.show(target);
2957
3059
  }
@@ -2959,19 +3061,23 @@
2959
3061
  * 关闭键盘
2960
3062
  */
2961
3063
  close() {
3064
+ if (this.destroyed) return;
3065
+ const wasVisible = this.manager.getState().visible;
2962
3066
  this.manager.hide();
2963
- this.options.onClose?.();
3067
+ if (wasVisible) this.options.onClose?.();
2964
3068
  }
2965
3069
  /**
2966
3070
  * 切换键盘类型
2967
3071
  */
2968
3072
  switchType(type) {
3073
+ if (this.destroyed) return;
2969
3074
  this.manager.switchType(type);
2970
3075
  }
2971
3076
  /**
2972
3077
  * 设置主题
2973
3078
  */
2974
3079
  setTheme(theme) {
3080
+ if (this.destroyed) return;
2975
3081
  if (typeof theme === "string") this.theme = getTheme(theme);
2976
3082
  else this.theme = theme;
2977
3083
  if (this.manager.getState().visible) this.render();
@@ -2986,12 +3092,14 @@
2986
3092
  * 设置值
2987
3093
  */
2988
3094
  setValue(value) {
3095
+ if (this.destroyed) return;
2989
3096
  this.manager.setValue(value);
2990
3097
  }
2991
3098
  /**
2992
3099
  * 渲染键盘 DOM
2993
3100
  */
2994
3101
  render() {
3102
+ if (this.destroyed) return;
2995
3103
  const layout = this.manager.getLayout();
2996
3104
  if (!layout) return;
2997
3105
  this.unmount();
@@ -3001,14 +3109,18 @@
3001
3109
  if (isMobileDevice) {
3002
3110
  this.maskEl = document.createElement("div");
3003
3111
  this.maskEl.className = "lk-mask";
3004
- this.maskEl.addEventListener("click", () => this.close());
3112
+ this.maskEl.addEventListener("click", () => {
3113
+ if (this.positionManager.isMaskClosable()) this.close();
3114
+ });
3005
3115
  this.rootEl.appendChild(this.maskEl);
3006
3116
  }
3007
3117
  this.keyboardEl = document.createElement("div");
3008
3118
  this.keyboardEl.className = `lk-popup lk-popup--${isMobileDevice ? "mobile" : "desktop"}`;
3009
3119
  const cssVars = themeToCSS(this.theme);
3120
+ const keyboardElement = this.keyboardEl;
3121
+ if (!keyboardElement) return;
3010
3122
  Object.entries(cssVars).forEach(([key, value]) => {
3011
- if (value) this.keyboardEl.style.setProperty(key, value);
3123
+ if (value) keyboardElement.style.setProperty(key, value);
3012
3124
  });
3013
3125
  const contentEl = this.renderKeyboard(layout);
3014
3126
  this.keyboardEl.appendChild(contentEl);
@@ -3037,7 +3149,7 @@
3037
3149
  });
3038
3150
  if (this.options.closeOnClickOutside !== false && this.keyboardEl) {
3039
3151
  const cleanup = onClickOutside(this.keyboardEl, (event) => {
3040
- if (this.targetEl && this.targetEl.contains(event.target)) return;
3152
+ if (this.targetEl && event.target instanceof Node && this.targetEl.contains(event.target)) return;
3041
3153
  this.close();
3042
3154
  });
3043
3155
  this.cleanupFns.push(cleanup);
@@ -3069,8 +3181,11 @@
3069
3181
  btnEl.type = "button";
3070
3182
  btnEl.className = this.getKeyClass(key, state.isUpperCase);
3071
3183
  if (key.disabled) btnEl.disabled = true;
3072
- if (key.icon) btnEl.innerHTML = key.icon;
3073
- else btnEl.textContent = typeof key.label === "string" ? key.label : state.isUpperCase ? key.label.upper : key.label.lower;
3184
+ if (key.icon) {
3185
+ const safeIcon = sanitizeSvgIcon(key.icon);
3186
+ if (safeIcon) btnEl.innerHTML = safeIcon;
3187
+ else btnEl.textContent = key.icon;
3188
+ } else btnEl.textContent = typeof key.label === "string" ? key.label : state.isUpperCase ? key.label.upper : key.label.lower;
3074
3189
  const handler = (e) => {
3075
3190
  e.preventDefault();
3076
3191
  e.stopPropagation();
@@ -3110,49 +3225,67 @@
3110
3225
  * 注入样式(单例)
3111
3226
  */
3112
3227
  injectStyles() {
3113
- if (document.getElementById("lk-renderer-styles")) return;
3114
- const style = document.createElement("style");
3115
- style.id = "lk-renderer-styles";
3228
+ const styleDocument = this.container.ownerDocument;
3229
+ if (this.styleDocument === styleDocument) return;
3230
+ const existing = rendererStyles.get(styleDocument);
3231
+ if (existing) {
3232
+ existing.users += 1;
3233
+ this.styleDocument = styleDocument;
3234
+ return;
3235
+ }
3236
+ const style = styleDocument.createElement("style");
3116
3237
  style.textContent = `
3117
- .lk-renderer-root { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
3118
- .lk-mask { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 9999; opacity: 0; transition: opacity 0.3s ease; }
3238
+ .lk-renderer-root { font-family: var(--ld-font-family); }
3239
+ .lk-mask { position: fixed; inset: 0; z-index: 9999; background: var(--keyboard-mask-bg); opacity: 0; transition: opacity var(--ld-duration-fast); }
3119
3240
  .lk-mask--visible { opacity: 1; }
3120
- .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; }
3241
+ .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); }
3121
3242
  .lk-popup--visible { transform: translateY(0); opacity: 1; }
3122
- .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; }
3123
- .lk-popup--mobile { border-top-left-radius: 16px; border-top-right-radius: 16px; padding-bottom: env(safe-area-inset-bottom, 0); }
3124
- .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; }
3125
- .lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap, 6px); padding: 0 2px; }
3243
+ .lk-popup--desktop { min-width: var(--ld-px-280); border-radius: var(--ld-radius-lg); box-shadow: var(--ld-shadow-lg, none); }
3244
+ .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)); }
3245
+ .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); }
3246
+ .lk-row { display: flex; justify-content: center; gap: var(--keyboard-key-gap); padding: 0 var(--ld-px-2); }
3126
3247
  .lk-row.numpad-row { justify-content: stretch; }
3127
3248
  .lk-row.numpad-row .lk-key-wrapper { flex: 1; }
3128
3249
  .lk-key-wrapper { position: relative; display: inline-flex; }
3129
- .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; }
3130
- .lk-key:active { background: var(--keyboard-key-active-bg, #e0e0e0); transform: scale(0.95); }
3250
+ .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); }
3251
+ .lk-key:active { background: var(--keyboard-key-active-bg); transform: scale(0.95); }
3131
3252
  .lk-key--disabled { opacity: 0.4; cursor: not-allowed; }
3132
- .lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg, #fff); }
3133
- .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); }
3134
- .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); }
3135
- .lk-key--active { background: var(--keyboard-key-active-bg, #007aff) !important; color: #fff !important; }
3136
- .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); }
3137
- .lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg, #0062cc); }
3138
- .lk-key--space { background: var(--keyboard-key-bg, #fff); }
3253
+ .lk-key--disabled:active { transform: none; background: var(--keyboard-key-bg); }
3254
+ .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); }
3255
+ .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); }
3256
+ .lk-key--active { background: var(--keyboard-key-active-bg) !important; color: var(--ld-text-inverse) !important; }
3257
+ .lk-key--done, .lk-key--enter { background: var(--keyboard-primary-bg); color: var(--keyboard-primary-color); font-size: var(--keyboard-fn-font-size); }
3258
+ .lk-key--done:active, .lk-key--enter:active { background: var(--keyboard-primary-active-bg); }
3259
+ .lk-key--space { background: var(--keyboard-key-bg); }
3139
3260
  .lk-key svg { pointer-events: none; }
3140
3261
  `;
3141
- document.head.appendChild(style);
3262
+ style.id = "lk-renderer-styles";
3263
+ styleDocument.head?.appendChild(style);
3264
+ rendererStyles.set(styleDocument, {
3265
+ element: style,
3266
+ users: 1
3267
+ });
3268
+ this.styleDocument = styleDocument;
3142
3269
  }
3143
3270
  /**
3144
3271
  * 绑定到输入框(聚焦时自动打开键盘)
3145
3272
  */
3146
3273
  bindInput(input) {
3274
+ if (this.destroyed) return () => void 0;
3147
3275
  const handleFocus = () => this.open(input);
3148
3276
  input.addEventListener("focus", handleFocus);
3149
3277
  input.setAttribute("readonly", "readonly");
3150
3278
  input.style.caretColor = "transparent";
3151
- return () => {
3279
+ let cleaned = false;
3280
+ const cleanup = () => {
3281
+ if (cleaned) return;
3282
+ cleaned = true;
3152
3283
  input.removeEventListener("focus", handleFocus);
3153
3284
  input.removeAttribute("readonly");
3154
3285
  input.style.caretColor = "";
3155
3286
  };
3287
+ this.inputCleanupFns.add(cleanup);
3288
+ return cleanup;
3156
3289
  }
3157
3290
  /**
3158
3291
  * 销毁渲染器
@@ -3161,15 +3294,71 @@
3161
3294
  if (this.destroyed) return;
3162
3295
  this.destroyed = true;
3163
3296
  this.unmount();
3297
+ this.inputCleanupFns.forEach((cleanup) => cleanup());
3298
+ this.inputCleanupFns.clear();
3164
3299
  this.manager.destroy();
3165
- const styleEl = document.getElementById("lk-renderer-styles");
3166
- if (styleEl) styleEl.remove();
3300
+ if (this.styleDocument) {
3301
+ const entry = rendererStyles.get(this.styleDocument);
3302
+ if (entry) {
3303
+ entry.users -= 1;
3304
+ if (entry.users <= 0) {
3305
+ entry.element.remove();
3306
+ rendererStyles.delete(this.styleDocument);
3307
+ }
3308
+ }
3309
+ this.styleDocument = null;
3310
+ }
3167
3311
  }
3168
3312
  };
3169
3313
  function createKeyboardRenderer(options) {
3170
3314
  return new KeyboardRenderer(options);
3171
3315
  }
3172
3316
  //#endregion
3317
+ //#region src/utils/formatters.ts
3318
+ function formatPhone(value) {
3319
+ const digits = value.replace(/\D/g, "").slice(0, 11);
3320
+ if (digits.length <= 3) return digits;
3321
+ if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`;
3322
+ return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7)}`;
3323
+ }
3324
+ function formatBankCard(value) {
3325
+ return value.replace(/\D/g, "").slice(0, 19).replace(/(\d{4})(?=\d)/g, "$1 ");
3326
+ }
3327
+ function formatAmount(value, decimalPlaces = 2) {
3328
+ let result = value.replace(/[^\d.]/g, "");
3329
+ const parts = result.split(".");
3330
+ if (parts.length > 2) result = `${parts[0]}.${parts.slice(1).join("")}`;
3331
+ if (parts.length === 2 && parts[1].length > decimalPlaces) result = `${parts[0]}.${parts[1].slice(0, decimalPlaces)}`;
3332
+ if (result.length > 1 && result.startsWith("0") && result[1] !== ".") result = result.replace(/^0+/, "") || "0";
3333
+ return result;
3334
+ }
3335
+ function formatIdCard(value) {
3336
+ return value.replace(/[^\dX]/gi, "").slice(0, 18).toUpperCase();
3337
+ }
3338
+ function formatLicensePlate(value) {
3339
+ return value.toUpperCase().slice(0, 8);
3340
+ }
3341
+ function formatVerifyCode(value, length = 6) {
3342
+ return value.replace(/\D/g, "").slice(0, length);
3343
+ }
3344
+ const formatters = /* @__PURE__ */ new Map([
3345
+ ["phone", formatPhone],
3346
+ ["bankcard", formatBankCard],
3347
+ ["idcard", formatIdCard],
3348
+ ["license-plate", formatLicensePlate],
3349
+ ["verifycode", formatVerifyCode]
3350
+ ]);
3351
+ function registerFormatter(name, formatter) {
3352
+ formatters.set(name, formatter);
3353
+ }
3354
+ function getFormatter(name) {
3355
+ return formatters.get(name);
3356
+ }
3357
+ function applyFormatter(name, value) {
3358
+ const formatter = formatters.get(name);
3359
+ return formatter ? formatter(value) : value;
3360
+ }
3361
+ //#endregion
3173
3362
  //#region src/utils/random.ts
3174
3363
  function shuffleArray(array) {
3175
3364
  const result = [...array];
@@ -3228,7 +3417,7 @@
3228
3417
  }
3229
3418
  function createRandomNumberLayout(options) {
3230
3419
  const { keysPerRow = 3, includeBackspace = true, includeDone = true, includeClear = false } = options || {};
3231
- const rows = chunkArray(getRandomNumbers().map((n, i) => createCharKey(n, i)), keysPerRow).map((keys, index) => ({
3420
+ const rows = chunkArray(getRandomNumbers().map((n, i) => createCharKey(n, i)), keysPerRow).map((keys) => ({
3232
3421
  className: "numpad-row",
3233
3422
  keys
3234
3423
  }));
@@ -3450,51 +3639,6 @@
3450
3639
  }
3451
3640
  }
3452
3641
  //#endregion
3453
- //#region src/utils/formatters.ts
3454
- function formatPhone(value) {
3455
- const digits = value.replace(/\D/g, "").slice(0, 11);
3456
- if (digits.length <= 3) return digits;
3457
- if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`;
3458
- return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7)}`;
3459
- }
3460
- function formatBankCard(value) {
3461
- return value.replace(/\D/g, "").slice(0, 19).replace(/(\d{4})(?=\d)/g, "$1 ");
3462
- }
3463
- function formatAmount(value, decimalPlaces = 2) {
3464
- let result = value.replace(/[^\d.]/g, "");
3465
- const parts = result.split(".");
3466
- if (parts.length > 2) result = parts[0] + "." + parts.slice(1).join("");
3467
- if (parts.length === 2 && parts[1].length > decimalPlaces) result = parts[0] + "." + parts[1].slice(0, decimalPlaces);
3468
- if (result.length > 1 && result.startsWith("0") && result[1] !== ".") result = result.replace(/^0+/, "") || "0";
3469
- return result;
3470
- }
3471
- function formatIdCard(value) {
3472
- return value.replace(/[^\dXx]/g, "").slice(0, 18).toUpperCase();
3473
- }
3474
- function formatLicensePlate(value) {
3475
- return value.toUpperCase().slice(0, 8);
3476
- }
3477
- function formatVerifyCode(value, length = 6) {
3478
- return value.replace(/\D/g, "").slice(0, length);
3479
- }
3480
- const formatters = /* @__PURE__ */ new Map([
3481
- ["phone", formatPhone],
3482
- ["bankcard", formatBankCard],
3483
- ["idcard", formatIdCard],
3484
- ["license-plate", formatLicensePlate],
3485
- ["verifycode", formatVerifyCode]
3486
- ]);
3487
- function registerFormatter(name, formatter) {
3488
- formatters.set(name, formatter);
3489
- }
3490
- function getFormatter(name) {
3491
- return formatters.get(name);
3492
- }
3493
- function applyFormatter(name, value) {
3494
- const formatter = formatters.get(name);
3495
- return formatter ? formatter(value) : value;
3496
- }
3497
- //#endregion
3498
3642
  exports.EMAIL_SUFFIXES = EMAIL_SUFFIXES;
3499
3643
  exports.EventEmitter = EventEmitter;
3500
3644
  exports.ICONS = ICONS;
@@ -3585,6 +3729,7 @@
3585
3729
  exports.registerLayout = registerLayout;
3586
3730
  exports.registerLayouts = registerLayouts;
3587
3731
  exports.registerTheme = registerTheme;
3732
+ exports.sanitizeSvgIcon = sanitizeSvgIcon;
3588
3733
  exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
3589
3734
  exports.searchLayout = searchLayout;
3590
3735
  exports.setCurrentTheme = setCurrentTheme;