@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
@@ -1 +1 @@
1
- {"version":3,"file":"presets.js","names":[],"sources":["../../src/themes/presets.ts"],"sourcesContent":["/**\n * 预设主题配置\n * @module themes/presets\n */\n\nimport type { ThemeConfig } from './types'\n\n/**\n * MIUI 小米风格主题(默认)\n * 特点:纯净、扁平、简洁、无多余装饰\n */\nexport const miuiTheme: ThemeConfig = {\n name: 'miui',\n displayName: 'MIUI',\n description: '小米风格 - 纯净简洁',\n isDark: false,\n colors: {\n background: '#f5f5f5',\n keyBackground: '#ffffff',\n keyColor: '#1a1a1a',\n keyActiveBackground: '#e8e8e8',\n fnKeyBackground: '#e0e0e0',\n fnKeyColor: '#666666',\n fnKeyActiveBackground: '#d0d0d0',\n primaryBackground: '#ff6700', // 小米橙\n primaryActiveBackground: '#e55d00',\n primaryColor: '#ffffff',\n borderColor: 'transparent',\n dividerColor: '#eeeeee',\n },\n sizes: {\n keyHeight: 46,\n keyRadius: 8,\n keyGap: 6,\n fontSize: 18,\n fnFontSize: 14,\n padding: 8,\n },\n effects: {\n keyShadow: 'none',\n keyActiveShadow: 'none',\n keyActiveScale: 0.96,\n transitionDuration: 100,\n enableBlur: false,\n blurAmount: 0,\n enableRipple: false,\n },\n}\n\n/**\n * iOS 风格主题\n * 特点:毛玻璃、圆润阴影、精致\n */\nexport const iosTheme: ThemeConfig = {\n name: 'ios',\n displayName: 'iOS',\n description: 'Apple 风格 - 精致优雅',\n isDark: false,\n colors: {\n background: '#d1d3d9',\n keyBackground: '#ffffff',\n keyColor: '#000000',\n keyActiveBackground: '#b8bac2',\n fnKeyBackground: '#a8aab4',\n fnKeyColor: '#000000',\n fnKeyActiveBackground: '#9699a3',\n primaryBackground: '#007aff',\n primaryActiveBackground: '#0062cc',\n primaryColor: '#ffffff',\n borderColor: 'transparent',\n dividerColor: '#c8cad0',\n },\n sizes: {\n keyHeight: 43,\n keyRadius: 5,\n keyGap: 6,\n fontSize: 22,\n fnFontSize: 15,\n padding: 4,\n },\n effects: {\n keyShadow: '0 1px 0 rgba(0,0,0,0.35)',\n keyActiveShadow: '0 0 0 rgba(0,0,0,0)',\n keyActiveScale: 1,\n transitionDuration: 80,\n enableBlur: true,\n blurAmount: 20,\n enableRipple: false,\n },\n}\n\n/**\n * Material Design 主题\n * 特点:深色、涟漪动效、层次感\n */\nexport const materialTheme: ThemeConfig = {\n name: 'material',\n displayName: 'Material',\n description: 'Google 风格 - 深邃现代',\n isDark: true,\n colors: {\n background: '#263238',\n keyBackground: '#37474f',\n keyColor: '#ffffff',\n keyActiveBackground: '#455a64',\n fnKeyBackground: '#455a64',\n fnKeyColor: '#b0bec5',\n fnKeyActiveBackground: '#546e7a',\n primaryBackground: '#4caf50', // Material Green\n primaryActiveBackground: '#43a047',\n primaryColor: '#ffffff',\n borderColor: 'transparent',\n dividerColor: '#37474f',\n },\n sizes: {\n keyHeight: 48,\n keyRadius: 4,\n keyGap: 4,\n fontSize: 20,\n fnFontSize: 14,\n padding: 6,\n },\n effects: {\n keyShadow: '0 1px 3px rgba(0,0,0,0.2)',\n keyActiveShadow: '0 2px 6px rgba(0,0,0,0.3)',\n keyActiveScale: 1,\n transitionDuration: 150,\n enableBlur: false,\n blurAmount: 0,\n enableRipple: true,\n },\n}\n\n/**\n * 极简主题\n * 特点:无边框、超简洁、透明感\n */\nexport const minimalTheme: ThemeConfig = {\n name: 'minimal',\n displayName: 'Minimal',\n description: '极简风格 - 轻盈透明',\n isDark: false,\n colors: {\n background: 'rgba(250, 250, 250, 0.95)',\n keyBackground: 'transparent',\n keyColor: '#333333',\n keyActiveBackground: 'rgba(0, 0, 0, 0.08)',\n fnKeyBackground: 'transparent',\n fnKeyColor: '#888888',\n fnKeyActiveBackground: 'rgba(0, 0, 0, 0.08)',\n primaryBackground: '#333333',\n primaryActiveBackground: '#000000',\n primaryColor: '#ffffff',\n borderColor: '#eeeeee',\n dividerColor: '#eeeeee',\n },\n sizes: {\n keyHeight: 44,\n keyRadius: 12,\n keyGap: 8,\n fontSize: 17,\n fnFontSize: 13,\n padding: 12,\n },\n effects: {\n keyShadow: 'none',\n keyActiveShadow: 'none',\n keyActiveScale: 0.92,\n transitionDuration: 120,\n enableBlur: true,\n blurAmount: 30,\n enableRipple: false,\n },\n}\n\n/**\n * 深色主题\n * 特点:护眼、现代、高对比\n */\nexport const darkTheme: ThemeConfig = {\n name: 'dark',\n displayName: 'Dark',\n description: '深色模式 - 护眼舒适',\n isDark: true,\n colors: {\n background: '#1c1c1e',\n keyBackground: '#3a3a3c',\n keyColor: '#ffffff',\n keyActiveBackground: '#4a4a4c',\n fnKeyBackground: '#2c2c2e',\n fnKeyColor: '#ebebf5',\n fnKeyActiveBackground: '#3c3c3e',\n primaryBackground: '#0a84ff',\n primaryActiveBackground: '#0070e0',\n primaryColor: '#ffffff',\n borderColor: '#3a3a3c',\n dividerColor: '#3a3a3c',\n },\n sizes: {\n keyHeight: 45,\n keyRadius: 8,\n keyGap: 6,\n fontSize: 18,\n fnFontSize: 14,\n padding: 8,\n },\n effects: {\n keyShadow: '0 1px 2px rgba(0,0,0,0.4)',\n keyActiveShadow: '0 0 8px rgba(10,132,255,0.3)',\n keyActiveScale: 0.97,\n transitionDuration: 100,\n enableBlur: true,\n blurAmount: 20,\n enableRipple: false,\n },\n}\n\n/**\n * 所有预设主题\n */\nexport const presetThemes: Record<string, ThemeConfig> = {\n miui: miuiTheme,\n ios: iosTheme,\n material: materialTheme,\n minimal: minimalTheme,\n dark: darkTheme,\n}\n\n/**\n * 默认主题\n */\nexport const defaultTheme = miuiTheme\n"],"mappings":"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"}
1
+ {"version":3,"file":"presets.js","names":[],"sources":["../../src/themes/presets.ts"],"sourcesContent":["/**\r\n * 预设主题配置\r\n * @module themes/presets\r\n */\r\n\r\nimport type { ThemeConfig } from './types'\r\n\r\n/**\r\n * MIUI 小米风格主题(默认)\r\n * 特点:纯净、扁平、简洁、无多余装饰\r\n */\r\nexport const miuiTheme: ThemeConfig = {\r\n name: 'miui',\r\n displayName: 'MIUI',\r\n description: '小米风格 - 纯净简洁',\r\n isDark: false,\r\n colors: {\r\n background: '#f5f5f5',\r\n keyBackground: '#ffffff',\r\n keyColor: '#1a1a1a',\r\n keyActiveBackground: '#e8e8e8',\r\n fnKeyBackground: '#e0e0e0',\r\n fnKeyColor: '#666666',\r\n fnKeyActiveBackground: '#d0d0d0',\r\n primaryBackground: '#ff6700', // 小米橙\r\n primaryActiveBackground: '#e55d00',\r\n primaryColor: '#ffffff',\r\n borderColor: 'transparent',\r\n dividerColor: '#eeeeee',\r\n },\r\n sizes: {\r\n keyHeight: 46,\r\n keyRadius: 8,\r\n keyGap: 6,\r\n fontSize: 18,\r\n fnFontSize: 14,\r\n padding: 8,\r\n },\r\n effects: {\r\n keyShadow: 'none',\r\n keyActiveShadow: 'none',\r\n keyActiveScale: 0.96,\r\n transitionDuration: 100,\r\n enableBlur: false,\r\n blurAmount: 0,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * iOS 风格主题\r\n * 特点:毛玻璃、圆润阴影、精致\r\n */\r\nexport const iosTheme: ThemeConfig = {\r\n name: 'ios',\r\n displayName: 'iOS',\r\n description: 'Apple 风格 - 精致优雅',\r\n isDark: false,\r\n colors: {\r\n background: '#d1d3d9',\r\n keyBackground: '#ffffff',\r\n keyColor: '#000000',\r\n keyActiveBackground: '#b8bac2',\r\n fnKeyBackground: '#a8aab4',\r\n fnKeyColor: '#000000',\r\n fnKeyActiveBackground: '#9699a3',\r\n primaryBackground: '#007aff',\r\n primaryActiveBackground: '#0062cc',\r\n primaryColor: '#ffffff',\r\n borderColor: 'transparent',\r\n dividerColor: '#c8cad0',\r\n },\r\n sizes: {\r\n keyHeight: 43,\r\n keyRadius: 5,\r\n keyGap: 6,\r\n fontSize: 22,\r\n fnFontSize: 15,\r\n padding: 4,\r\n },\r\n effects: {\r\n keyShadow: '0 1px 0 rgba(0,0,0,0.35)',\r\n keyActiveShadow: '0 0 0 rgba(0,0,0,0)',\r\n keyActiveScale: 1,\r\n transitionDuration: 80,\r\n enableBlur: true,\r\n blurAmount: 20,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * Material Design 主题\r\n * 特点:深色、涟漪动效、层次感\r\n */\r\nexport const materialTheme: ThemeConfig = {\r\n name: 'material',\r\n displayName: 'Material',\r\n description: 'Google 风格 - 深邃现代',\r\n isDark: true,\r\n colors: {\r\n background: '#263238',\r\n keyBackground: '#37474f',\r\n keyColor: '#ffffff',\r\n keyActiveBackground: '#455a64',\r\n fnKeyBackground: '#455a64',\r\n fnKeyColor: '#b0bec5',\r\n fnKeyActiveBackground: '#546e7a',\r\n primaryBackground: '#4caf50', // Material Green\r\n primaryActiveBackground: '#43a047',\r\n primaryColor: '#ffffff',\r\n borderColor: 'transparent',\r\n dividerColor: '#37474f',\r\n },\r\n sizes: {\r\n keyHeight: 48,\r\n keyRadius: 4,\r\n keyGap: 4,\r\n fontSize: 20,\r\n fnFontSize: 14,\r\n padding: 6,\r\n },\r\n effects: {\r\n keyShadow: '0 1px 3px rgba(0,0,0,0.2)',\r\n keyActiveShadow: '0 2px 6px rgba(0,0,0,0.3)',\r\n keyActiveScale: 1,\r\n transitionDuration: 150,\r\n enableBlur: false,\r\n blurAmount: 0,\r\n enableRipple: true,\r\n },\r\n}\r\n\r\n/**\r\n * 极简主题\r\n * 特点:无边框、超简洁、透明感\r\n */\r\nexport const minimalTheme: ThemeConfig = {\r\n name: 'minimal',\r\n displayName: 'Minimal',\r\n description: '极简风格 - 轻盈透明',\r\n isDark: false,\r\n colors: {\r\n background: 'rgba(250, 250, 250, 0.95)',\r\n keyBackground: 'transparent',\r\n keyColor: '#333333',\r\n keyActiveBackground: 'rgba(0, 0, 0, 0.08)',\r\n fnKeyBackground: 'transparent',\r\n fnKeyColor: '#888888',\r\n fnKeyActiveBackground: 'rgba(0, 0, 0, 0.08)',\r\n primaryBackground: '#333333',\r\n primaryActiveBackground: '#000000',\r\n primaryColor: '#ffffff',\r\n borderColor: '#eeeeee',\r\n dividerColor: '#eeeeee',\r\n },\r\n sizes: {\r\n keyHeight: 44,\r\n keyRadius: 12,\r\n keyGap: 8,\r\n fontSize: 17,\r\n fnFontSize: 13,\r\n padding: 12,\r\n },\r\n effects: {\r\n keyShadow: 'none',\r\n keyActiveShadow: 'none',\r\n keyActiveScale: 0.92,\r\n transitionDuration: 120,\r\n enableBlur: true,\r\n blurAmount: 30,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * 深色主题\r\n * 特点:护眼、现代、高对比\r\n */\r\nexport const darkTheme: ThemeConfig = {\r\n name: 'dark',\r\n displayName: 'Dark',\r\n description: '深色模式 - 护眼舒适',\r\n isDark: true,\r\n colors: {\r\n background: '#1c1c1e',\r\n keyBackground: '#3a3a3c',\r\n keyColor: '#ffffff',\r\n keyActiveBackground: '#4a4a4c',\r\n fnKeyBackground: '#2c2c2e',\r\n fnKeyColor: '#ebebf5',\r\n fnKeyActiveBackground: '#3c3c3e',\r\n primaryBackground: '#0a84ff',\r\n primaryActiveBackground: '#0070e0',\r\n primaryColor: '#ffffff',\r\n borderColor: '#3a3a3c',\r\n dividerColor: '#3a3a3c',\r\n },\r\n sizes: {\r\n keyHeight: 45,\r\n keyRadius: 8,\r\n keyGap: 6,\r\n fontSize: 18,\r\n fnFontSize: 14,\r\n padding: 8,\r\n },\r\n effects: {\r\n keyShadow: '0 1px 2px rgba(0,0,0,0.4)',\r\n keyActiveShadow: '0 0 8px rgba(10,132,255,0.3)',\r\n keyActiveScale: 0.97,\r\n transitionDuration: 100,\r\n enableBlur: true,\r\n blurAmount: 20,\r\n enableRipple: false,\r\n },\r\n}\r\n\r\n/**\r\n * 所有预设主题\r\n */\r\nexport const presetThemes: Record<string, ThemeConfig> = {\r\n miui: miuiTheme,\r\n ios: iosTheme,\r\n material: materialTheme,\r\n minimal: minimalTheme,\r\n dark: darkTheme,\r\n}\r\n\r\n/**\r\n * 默认主题\r\n */\r\nexport const defaultTheme = miuiTheme\r\n"],"mappings":"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"}
@@ -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
  * ***********************************
@@ -12,10 +12,7 @@ const MOBILE_UA_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Oper
12
12
  let cachedIsTouchDevice = null;
13
13
  function isTouchDevice() {
14
14
  if (cachedIsTouchDevice !== null) return cachedIsTouchDevice;
15
- if (typeof window === "undefined") {
16
- cachedIsTouchDevice = false;
17
- return false;
18
- }
15
+ if (typeof window === "undefined") return false;
19
16
  cachedIsTouchDevice = "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
20
17
  return cachedIsTouchDevice;
21
18
  }
@@ -75,7 +72,14 @@ function getSafeAreaInsets() {
75
72
  pointer-events: none;
76
73
  visibility: hidden;
77
74
  `;
78
- document.body.appendChild(div);
75
+ const body = document.body;
76
+ if (!body) return {
77
+ top: 0,
78
+ right: 0,
79
+ bottom: 0,
80
+ left: 0
81
+ };
82
+ body.appendChild(div);
79
83
  const computed = getComputedStyle(div);
80
84
  const safeArea = {
81
85
  top: Number.parseFloat(computed.top) || 0,
@@ -83,7 +87,7 @@ function getSafeAreaInsets() {
83
87
  bottom: Number.parseFloat(computed.bottom) || 0,
84
88
  left: Number.parseFloat(computed.left) || 0
85
89
  };
86
- document.body.removeChild(div);
90
+ body.removeChild(div);
87
91
  return safeArea;
88
92
  }
89
93
  function supportsVibrate() {
@@ -1 +1 @@
1
- {"version":3,"file":"device.js","names":[],"sources":["../../src/utils/device.ts"],"sourcesContent":["/**\n * 设备检测工具\n * @module utils/device\n */\n\nimport type { DeviceType, ViewportInfo } from '../types'\n\n/**\n * 移动设备 UA 正则\n */\nconst MOBILE_UA_REGEX = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i\n\n/**\n * 触摸设备检测缓存\n */\nlet cachedIsTouchDevice: boolean | null = null\n\n/**\n * 检测是否为触摸设备\n */\nexport function isTouchDevice(): boolean {\n if (cachedIsTouchDevice !== null) {\n return cachedIsTouchDevice\n }\n\n if (typeof window === 'undefined') {\n cachedIsTouchDevice = false\n return false\n }\n\n cachedIsTouchDevice = (\n 'ontouchstart' in window\n || navigator.maxTouchPoints > 0\n // @ts-expect-error - msMaxTouchPoints is IE specific\n || navigator.msMaxTouchPoints > 0\n )\n\n return cachedIsTouchDevice\n}\n\n/**\n * 检测是否为移动设备(基于 UA)\n */\nexport function isMobileUA(): boolean {\n if (typeof navigator === 'undefined') {\n return false\n }\n return MOBILE_UA_REGEX.test(navigator.userAgent)\n}\n\n/**\n * 检测是否为移动设备(综合判断)\n */\nexport function isMobile(): boolean {\n if (typeof window === 'undefined') {\n return false\n }\n\n // 优先使用 UA 判断\n if (isMobileUA()) {\n return true\n }\n\n // 其次使用屏幕宽度判断\n const screenWidth = window.innerWidth || document.documentElement.clientWidth\n if (screenWidth <= 768) {\n return true\n }\n\n // 最后使用触摸检测\n return isTouchDevice()\n}\n\n/**\n * 获取设备类型\n */\nexport function getDeviceType(): DeviceType {\n return isMobile() ? 'mobile' : 'desktop'\n}\n\n/**\n * 获取视口信息\n */\nexport function getViewportInfo(): ViewportInfo {\n if (typeof window === 'undefined') {\n return {\n width: 0,\n height: 0,\n scrollX: 0,\n scrollY: 0,\n isMobile: false,\n }\n }\n\n const width = window.innerWidth || document.documentElement.clientWidth\n const height = window.innerHeight || document.documentElement.clientHeight\n\n // 获取安全区域(iOS 刘海屏等)\n const safeArea = getSafeAreaInsets()\n\n return {\n width,\n height,\n scrollX: window.scrollX || window.pageXOffset,\n scrollY: window.scrollY || window.pageYOffset,\n isMobile: isMobile(),\n safeArea,\n }\n}\n\n/**\n * 获取安全区域边距\n */\nexport function getSafeAreaInsets(): ViewportInfo['safeArea'] {\n if (typeof window === 'undefined' || typeof CSS === 'undefined' || !CSS.supports) {\n return { top: 0, right: 0, bottom: 0, left: 0 }\n }\n\n // 检查是否支持 env() 函数\n if (!CSS.supports('padding-top', 'env(safe-area-inset-top)')) {\n return { top: 0, right: 0, bottom: 0, left: 0 }\n }\n\n // 创建临时元素获取安全区域值\n const div = document.createElement('div')\n div.style.cssText = `\n position: fixed;\n top: env(safe-area-inset-top);\n right: env(safe-area-inset-right);\n bottom: env(safe-area-inset-bottom);\n left: env(safe-area-inset-left);\n pointer-events: none;\n visibility: hidden;\n `\n document.body.appendChild(div)\n\n const computed = getComputedStyle(div)\n const safeArea = {\n top: Number.parseFloat(computed.top) || 0,\n right: Number.parseFloat(computed.right) || 0,\n bottom: Number.parseFloat(computed.bottom) || 0,\n left: Number.parseFloat(computed.left) || 0,\n }\n\n document.body.removeChild(div)\n return safeArea\n}\n\n/**\n * 检测是否支持震动 API\n */\nexport function supportsVibrate(): boolean {\n return typeof navigator !== 'undefined' && 'vibrate' in navigator\n}\n\n/**\n * 触发震动反馈\n * @param pattern - 震动模式(毫秒)\n */\nexport function vibrate(pattern: number | number[] = 10): boolean {\n if (supportsVibrate()) {\n return navigator.vibrate(pattern)\n }\n return false\n}\n\n/**\n * 监听视口变化\n * @param callback - 回调函数\n * @returns 取消监听函数\n */\nexport function onViewportChange(callback: (info: ViewportInfo) => void): () => void {\n if (typeof window === 'undefined') {\n return () => {}\n }\n\n const handler = () => {\n callback(getViewportInfo())\n }\n\n window.addEventListener('resize', handler)\n window.addEventListener('orientationchange', handler)\n\n return () => {\n window.removeEventListener('resize', handler)\n window.removeEventListener('orientationchange', handler)\n }\n}\n"],"mappings":";;;;;;;;;;AAUA,MAAM,kBAAkB;AAKxB,IAAI,sBAAsC;AAKnC,SAAS,gBAAyB;CACvC,IAAI,wBAAwB,MAC1B,OAAO;CAGT,IAAI,OAAO,WAAW,aAAa;EACjC,sBAAsB;EACtB,OAAO;CACT;CAEA,sBACE,kBAAkB,UACf,UAAU,iBAAiB,KAE3B,UAAU,mBAAmB;CAGlC,OAAO;AACT;AAKO,SAAS,aAAsB;CACpC,IAAI,OAAO,cAAc,aACvB,OAAO;CAET,OAAO,gBAAgB,KAAK,UAAU,SAAS;AACjD;AAKO,SAAS,WAAoB;CAClC,IAAI,OAAO,WAAW,aACpB,OAAO;CAIT,IAAI,WAAW,GACb,OAAO;CAKT,KADoB,OAAO,cAAc,SAAS,gBAAgB,gBAC/C,KACjB,OAAO;CAIT,OAAO,cAAc;AACvB;AAKO,SAAS,gBAA4B;CAC1C,OAAO,SAAS,IAAI,WAAW;AACjC;AAKO,SAAS,kBAAgC;CAC9C,IAAI,OAAO,WAAW,aACpB,OAAO;EACL,OAAO;EACP,QAAQ;EACR,SAAS;EACT,SAAS;EACT,UAAU;CACZ;CAGF,MAAM,QAAQ,OAAO,cAAc,SAAS,gBAAgB;CAC5D,MAAM,SAAS,OAAO,eAAe,SAAS,gBAAgB;CAG9D,MAAM,WAAW,kBAAkB;CAEnC,OAAO;EACL;EACA;EACA,SAAS,OAAO,WAAW,OAAO;EAClC,SAAS,OAAO,WAAW,OAAO;EAClC,UAAU,SAAS;EACnB;CACF;AACF;AAKO,SAAS,oBAA8C;CAC5D,IAAI,OAAO,WAAW,eAAe,OAAO,QAAQ,eAAe,CAAC,IAAI,UACtE,OAAO;EAAE,KAAK;EAAG,OAAO;EAAG,QAAQ;EAAG,MAAM;CAAE;CAIhD,IAAI,CAAC,IAAI,SAAS,eAAe,0BAA0B,GACzD,OAAO;EAAE,KAAK;EAAG,OAAO;EAAG,QAAQ;EAAG,MAAM;CAAE;CAIhD,MAAM,MAAM,SAAS,cAAc,KAAK;CACxC,IAAI,MAAM,UAAU;;;;;;;;;CASpB,SAAS,KAAK,YAAY,GAAG;CAE7B,MAAM,WAAW,iBAAiB,GAAG;CACrC,MAAM,WAAW;EACf,KAAK,OAAO,WAAW,SAAS,GAAG,KAAK;EACxC,OAAO,OAAO,WAAW,SAAS,KAAK,KAAK;EAC5C,QAAQ,OAAO,WAAW,SAAS,MAAM,KAAK;EAC9C,MAAM,OAAO,WAAW,SAAS,IAAI,KAAK;CAC5C;CAEA,SAAS,KAAK,YAAY,GAAG;CAC7B,OAAO;AACT;AAKO,SAAS,kBAA2B;CACzC,OAAO,OAAO,cAAc,eAAe,aAAa;AAC1D;AAMO,SAAS,QAAQ,UAA6B,IAAa;CAChE,IAAI,gBAAgB,GAClB,OAAO,UAAU,QAAQ,OAAO;CAElC,OAAO;AACT;AAOO,SAAS,iBAAiB,UAAoD;CACnF,IAAI,OAAO,WAAW,aACpB,aAAa,CAAC;CAGhB,MAAM,gBAAgB;EACpB,SAAS,gBAAgB,CAAC;CAC5B;CAEA,OAAO,iBAAiB,UAAU,OAAO;CACzC,OAAO,iBAAiB,qBAAqB,OAAO;CAEpD,aAAa;EACX,OAAO,oBAAoB,UAAU,OAAO;EAC5C,OAAO,oBAAoB,qBAAqB,OAAO;CACzD;AACF"}
1
+ {"version":3,"file":"device.js","names":[],"sources":["../../src/utils/device.ts"],"sourcesContent":["/**\r\n * 设备检测工具\r\n * @module utils/device\r\n */\r\n\r\nimport type { DeviceType, ViewportInfo } from '../types'\r\n\r\n/**\r\n * 移动设备 UA 正则\r\n */\r\nconst MOBILE_UA_REGEX =\r\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i\r\n\r\n/**\r\n * 触摸设备检测缓存\r\n */\r\nlet cachedIsTouchDevice: boolean | null = null\r\n\r\n/**\r\n * 检测是否为触摸设备\r\n */\r\nexport function isTouchDevice(): boolean {\r\n if (cachedIsTouchDevice !== null) {\r\n return cachedIsTouchDevice\r\n }\r\n\r\n if (typeof window === 'undefined') {\r\n return false\r\n }\r\n\r\n cachedIsTouchDevice =\r\n 'ontouchstart' in window ||\r\n navigator.maxTouchPoints > 0 ||\r\n // @ts-expect-error - msMaxTouchPoints is IE specific\r\n navigator.msMaxTouchPoints > 0\r\n\r\n return cachedIsTouchDevice\r\n}\r\n\r\n/**\r\n * 检测是否为移动设备(基于 UA)\r\n */\r\nexport function isMobileUA(): boolean {\r\n if (typeof navigator === 'undefined') {\r\n return false\r\n }\r\n return MOBILE_UA_REGEX.test(navigator.userAgent)\r\n}\r\n\r\n/**\r\n * 检测是否为移动设备(综合判断)\r\n */\r\nexport function isMobile(): boolean {\r\n if (typeof window === 'undefined') {\r\n return false\r\n }\r\n\r\n // 优先使用 UA 判断\r\n if (isMobileUA()) {\r\n return true\r\n }\r\n\r\n // 其次使用屏幕宽度判断\r\n const screenWidth = window.innerWidth || document.documentElement.clientWidth\r\n if (screenWidth <= 768) {\r\n return true\r\n }\r\n\r\n // 最后使用触摸检测\r\n return isTouchDevice()\r\n}\r\n\r\n/**\r\n * 获取设备类型\r\n */\r\nexport function getDeviceType(): DeviceType {\r\n return isMobile() ? 'mobile' : 'desktop'\r\n}\r\n\r\n/**\r\n * 获取视口信息\r\n */\r\nexport function getViewportInfo(): ViewportInfo {\r\n if (typeof window === 'undefined') {\r\n return {\r\n width: 0,\r\n height: 0,\r\n scrollX: 0,\r\n scrollY: 0,\r\n isMobile: false,\r\n }\r\n }\r\n\r\n const width = window.innerWidth || document.documentElement.clientWidth\r\n const height = window.innerHeight || document.documentElement.clientHeight\r\n\r\n // 获取安全区域(iOS 刘海屏等)\r\n const safeArea = getSafeAreaInsets()\r\n\r\n return {\r\n width,\r\n height,\r\n scrollX: window.scrollX || window.pageXOffset,\r\n scrollY: window.scrollY || window.pageYOffset,\r\n isMobile: isMobile(),\r\n safeArea,\r\n }\r\n}\r\n\r\n/**\r\n * 获取安全区域边距\r\n */\r\nexport function getSafeAreaInsets(): ViewportInfo['safeArea'] {\r\n if (\r\n typeof window === 'undefined' ||\r\n typeof CSS === 'undefined' ||\r\n !CSS.supports\r\n ) {\r\n return { top: 0, right: 0, bottom: 0, left: 0 }\r\n }\r\n\r\n // 检查是否支持 env() 函数\r\n if (!CSS.supports('padding-top', 'env(safe-area-inset-top)')) {\r\n return { top: 0, right: 0, bottom: 0, left: 0 }\r\n }\r\n\r\n // 创建临时元素获取安全区域值\r\n const div = document.createElement('div')\r\n div.style.cssText = `\r\n position: fixed;\r\n top: env(safe-area-inset-top);\r\n right: env(safe-area-inset-right);\r\n bottom: env(safe-area-inset-bottom);\r\n left: env(safe-area-inset-left);\r\n pointer-events: none;\r\n visibility: hidden;\r\n `\r\n const body = document.body\r\n if (!body) return { top: 0, right: 0, bottom: 0, left: 0 }\r\n body.appendChild(div)\r\n\r\n const computed = getComputedStyle(div)\r\n const safeArea = {\r\n top: Number.parseFloat(computed.top) || 0,\r\n right: Number.parseFloat(computed.right) || 0,\r\n bottom: Number.parseFloat(computed.bottom) || 0,\r\n left: Number.parseFloat(computed.left) || 0,\r\n }\r\n\r\n body.removeChild(div)\r\n return safeArea\r\n}\r\n\r\n/**\r\n * 检测是否支持震动 API\r\n */\r\nexport function supportsVibrate(): boolean {\r\n return typeof navigator !== 'undefined' && 'vibrate' in navigator\r\n}\r\n\r\n/**\r\n * 触发震动反馈\r\n * @param pattern - 震动模式(毫秒)\r\n */\r\nexport function vibrate(pattern: number | number[] = 10): boolean {\r\n if (supportsVibrate()) {\r\n return navigator.vibrate(pattern)\r\n }\r\n return false\r\n}\r\n\r\n/**\r\n * 监听视口变化\r\n * @param callback - 回调函数\r\n * @returns 取消监听函数\r\n */\r\nexport function onViewportChange(\r\n callback: (info: ViewportInfo) => void\r\n): () => void {\r\n if (typeof window === 'undefined') {\r\n return () => {}\r\n }\r\n\r\n const handler = () => {\r\n callback(getViewportInfo())\r\n }\r\n\r\n window.addEventListener('resize', handler)\r\n window.addEventListener('orientationchange', handler)\r\n\r\n return () => {\r\n window.removeEventListener('resize', handler)\r\n window.removeEventListener('orientationchange', handler)\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;AAUA,MAAM,kBACJ;AAKF,IAAI,sBAAsC;AAKnC,SAAS,gBAAyB;CACvC,IAAI,wBAAwB,MAC1B,OAAO;CAGT,IAAI,OAAO,WAAW,aACpB,OAAO;CAGT,sBACE,kBAAkB,UAClB,UAAU,iBAAiB,KAE3B,UAAU,mBAAmB;CAE/B,OAAO;AACT;AAKO,SAAS,aAAsB;CACpC,IAAI,OAAO,cAAc,aACvB,OAAO;CAET,OAAO,gBAAgB,KAAK,UAAU,SAAS;AACjD;AAKO,SAAS,WAAoB;CAClC,IAAI,OAAO,WAAW,aACpB,OAAO;CAIT,IAAI,WAAW,GACb,OAAO;CAKT,KADoB,OAAO,cAAc,SAAS,gBAAgB,gBAC/C,KACjB,OAAO;CAIT,OAAO,cAAc;AACvB;AAKO,SAAS,gBAA4B;CAC1C,OAAO,SAAS,IAAI,WAAW;AACjC;AAKO,SAAS,kBAAgC;CAC9C,IAAI,OAAO,WAAW,aACpB,OAAO;EACL,OAAO;EACP,QAAQ;EACR,SAAS;EACT,SAAS;EACT,UAAU;CACZ;CAGF,MAAM,QAAQ,OAAO,cAAc,SAAS,gBAAgB;CAC5D,MAAM,SAAS,OAAO,eAAe,SAAS,gBAAgB;CAG9D,MAAM,WAAW,kBAAkB;CAEnC,OAAO;EACL;EACA;EACA,SAAS,OAAO,WAAW,OAAO;EAClC,SAAS,OAAO,WAAW,OAAO;EAClC,UAAU,SAAS;EACnB;CACF;AACF;AAKO,SAAS,oBAA8C;CAC5D,IACE,OAAO,WAAW,eAClB,OAAO,QAAQ,eACf,CAAC,IAAI,UAEL,OAAO;EAAE,KAAK;EAAG,OAAO;EAAG,QAAQ;EAAG,MAAM;CAAE;CAIhD,IAAI,CAAC,IAAI,SAAS,eAAe,0BAA0B,GACzD,OAAO;EAAE,KAAK;EAAG,OAAO;EAAG,QAAQ;EAAG,MAAM;CAAE;CAIhD,MAAM,MAAM,SAAS,cAAc,KAAK;CACxC,IAAI,MAAM,UAAU;;;;;;;;;CASpB,MAAM,OAAO,SAAS;CACtB,IAAI,CAAC,MAAM,OAAO;EAAE,KAAK;EAAG,OAAO;EAAG,QAAQ;EAAG,MAAM;CAAE;CACzD,KAAK,YAAY,GAAG;CAEpB,MAAM,WAAW,iBAAiB,GAAG;CACrC,MAAM,WAAW;EACf,KAAK,OAAO,WAAW,SAAS,GAAG,KAAK;EACxC,OAAO,OAAO,WAAW,SAAS,KAAK,KAAK;EAC5C,QAAQ,OAAO,WAAW,SAAS,MAAM,KAAK;EAC9C,MAAM,OAAO,WAAW,SAAS,IAAI,KAAK;CAC5C;CAEA,KAAK,YAAY,GAAG;CACpB,OAAO;AACT;AAKO,SAAS,kBAA2B;CACzC,OAAO,OAAO,cAAc,eAAe,aAAa;AAC1D;AAMO,SAAS,QAAQ,UAA6B,IAAa;CAChE,IAAI,gBAAgB,GAClB,OAAO,UAAU,QAAQ,OAAO;CAElC,OAAO;AACT;AAOO,SAAS,iBACd,UACY;CACZ,IAAI,OAAO,WAAW,aACpB,aAAa,CAAC;CAGhB,MAAM,gBAAgB;EACpB,SAAS,gBAAgB,CAAC;CAC5B;CAEA,OAAO,iBAAiB,UAAU,OAAO;CACzC,OAAO,iBAAiB,qBAAqB,OAAO;CAEpD,aAAa;EACX,OAAO,oBAAoB,UAAU,OAAO;EAC5C,OAAO,oBAAoB,qBAAqB,OAAO;CACzD;AACF"}
package/esm/utils/dom.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
  * ***********************************
@@ -72,16 +72,19 @@ function insertTextAtCursor(input, text, maxLength) {
72
72
  const start = input.selectionStart ?? input.value.length;
73
73
  const end = input.selectionEnd ?? input.value.length;
74
74
  const currentValue = input.value;
75
- let newValue = currentValue.substring(0, start) + text + currentValue.substring(end);
76
- if (maxLength !== void 0 && newValue.length > maxLength) {
77
- const allowedLength = maxLength - (currentValue.length - (end - start));
75
+ const normalizedMaxLength = maxLength === void 0 || !Number.isFinite(maxLength) ? void 0 : Math.max(0, Math.floor(maxLength));
76
+ let insertedText = text;
77
+ let newValue = currentValue.substring(0, start) + insertedText + currentValue.substring(end);
78
+ if (normalizedMaxLength !== void 0 && newValue.length > normalizedMaxLength) {
79
+ const allowedLength = normalizedMaxLength - (currentValue.length - (end - start));
78
80
  if (allowedLength <= 0) return {
79
81
  value: currentValue,
80
82
  cursorPosition: start
81
83
  };
82
- newValue = currentValue.substring(0, start) + text.substring(0, allowedLength) + currentValue.substring(end);
84
+ insertedText = text.substring(0, allowedLength);
85
+ newValue = currentValue.substring(0, start) + insertedText + currentValue.substring(end);
83
86
  }
84
- const newCursorPosition = start + text.length;
87
+ const newCursorPosition = start + insertedText.length;
85
88
  return {
86
89
  value: newValue,
87
90
  cursorPosition: Math.min(newCursorPosition, newValue.length)
@@ -122,6 +125,21 @@ function createElement(tag, attrs, children) {
122
125
  });
123
126
  return element;
124
127
  }
128
+ function sanitizeSvgIcon(icon) {
129
+ if (typeof DOMParser === "undefined" || typeof XMLSerializer === "undefined") return "";
130
+ const document2 = new DOMParser().parseFromString(icon, "image/svg+xml");
131
+ const svg = document2.documentElement;
132
+ if (svg.tagName.toLowerCase() !== "svg" || document2.querySelector("parsererror")) return "";
133
+ svg.querySelectorAll("script, style, foreignObject").forEach((node) => node.remove());
134
+ svg.querySelectorAll("*").forEach((element) => {
135
+ Array.from(element.attributes).forEach((attribute) => {
136
+ const name = attribute.name.toLowerCase();
137
+ const value = attribute.value.trim();
138
+ if (name.startsWith("on") || (name === "href" || name === "xlink:href") && !value.startsWith("#")) element.removeAttribute(attribute.name);
139
+ });
140
+ });
141
+ return new XMLSerializer().serializeToString(svg);
142
+ }
125
143
  function toggleClass(element, className, force) {
126
144
  element.classList.toggle(className, force);
127
145
  }
@@ -129,6 +147,6 @@ function setStyles(element, styles) {
129
147
  Object.assign(element.style, styles);
130
148
  }
131
149
  //#endregion
132
- export { createElement, deleteCharBeforeCursor, getBoundingRect, getElementOffset, getInputCursorPosition, insertTextAtCursor, isClickOutside, isElementInViewport, onClickOutside, preventDefaultAndStopPropagation, scrollIntoViewIfNeeded, setInputCursorPosition, setStyles, toggleClass };
150
+ export { createElement, deleteCharBeforeCursor, getBoundingRect, getElementOffset, getInputCursorPosition, insertTextAtCursor, isClickOutside, isElementInViewport, onClickOutside, preventDefaultAndStopPropagation, sanitizeSvgIcon, scrollIntoViewIfNeeded, setInputCursorPosition, setStyles, toggleClass };
133
151
  /*! End of @composy/keyboard-core | Powered by @composy/builder */
134
152
  //# sourceMappingURL=dom.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dom.js","names":["deletedChar","newValue"],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\n * DOM 操作工具\n * @module utils/dom\n */\n\nimport type { BoundingRect, Position } from '../types'\n\n/**\n * 获取元素的边界矩形\n */\nexport function getBoundingRect(element: HTMLElement): BoundingRect {\n const rect = element.getBoundingClientRect()\n return {\n x: rect.left,\n y: rect.top,\n width: rect.width,\n height: rect.height,\n }\n}\n\n/**\n * 获取元素相对于文档的位置\n */\nexport function getElementOffset(element: HTMLElement): Position {\n const rect = element.getBoundingClientRect()\n const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop\n\n return {\n x: rect.left + scrollLeft,\n y: rect.top + scrollTop,\n }\n}\n\n/**\n * 检查元素是否在视口内\n */\nexport function isElementInViewport(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect()\n return (\n rect.top >= 0\n && rect.left >= 0\n && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight)\n && rect.right <= (window.innerWidth || document.documentElement.clientWidth)\n )\n}\n\n/**\n * 检查点击是否在元素外部\n */\nexport function isClickOutside(event: MouseEvent | TouchEvent, element: HTMLElement): boolean {\n const target = event.target as Node\n return !element.contains(target)\n}\n\n/**\n * 阻止元素默认事件(用于防止输入框获取焦点)\n */\nexport function preventDefaultAndStopPropagation(event: Event): void {\n event.preventDefault()\n event.stopPropagation()\n}\n\n/**\n * 添加点击外部监听\n */\nexport function onClickOutside(\n element: HTMLElement,\n callback: (event: MouseEvent | TouchEvent) => void,\n): () => void {\n const handler = (event: MouseEvent | TouchEvent) => {\n if (isClickOutside(event, element)) {\n callback(event)\n }\n }\n\n document.addEventListener('mousedown', handler)\n document.addEventListener('touchstart', handler)\n\n return () => {\n document.removeEventListener('mousedown', handler)\n document.removeEventListener('touchstart', handler)\n }\n}\n\n/**\n * 滚动元素到可视区域\n */\nexport function scrollIntoViewIfNeeded(element: HTMLElement, container?: HTMLElement): void {\n if (!container) {\n element.scrollIntoView({ behavior: 'smooth', block: 'nearest' })\n return\n }\n\n const elementRect = element.getBoundingClientRect()\n const containerRect = container.getBoundingClientRect()\n\n if (elementRect.bottom > containerRect.bottom) {\n container.scrollTop += elementRect.bottom - containerRect.bottom\n }\n else if (elementRect.top < containerRect.top) {\n container.scrollTop -= containerRect.top - elementRect.top\n }\n}\n\n/**\n * 获取输入框的光标位置\n */\nexport function getInputCursorPosition(input: HTMLInputElement | HTMLTextAreaElement): number {\n return input.selectionStart ?? input.value.length\n}\n\n/**\n * 设置输入框的光标位置\n */\nexport function setInputCursorPosition(\n input: HTMLInputElement | HTMLTextAreaElement,\n position: number,\n): void {\n input.setSelectionRange(position, position)\n}\n\n/**\n * 在输入框当前光标位置插入文本\n */\nexport function insertTextAtCursor(\n input: HTMLInputElement | HTMLTextAreaElement,\n text: string,\n maxLength?: number,\n): { value: string; cursorPosition: number } {\n const start = input.selectionStart ?? input.value.length\n const end = input.selectionEnd ?? input.value.length\n const currentValue = input.value\n\n let newValue = currentValue.substring(0, start) + text + currentValue.substring(end)\n\n // 检查最大长度\n if (maxLength !== undefined && newValue.length > maxLength) {\n const allowedLength = maxLength - (currentValue.length - (end - start))\n if (allowedLength <= 0) {\n return { value: currentValue, cursorPosition: start }\n }\n newValue = currentValue.substring(0, start) + text.substring(0, allowedLength) + currentValue.substring(end)\n }\n\n const newCursorPosition = start + text.length\n\n return {\n value: newValue,\n cursorPosition: Math.min(newCursorPosition, newValue.length),\n }\n}\n\n/**\n * 删除输入框光标前的字符\n */\nexport function deleteCharBeforeCursor(\n input: HTMLInputElement | HTMLTextAreaElement,\n): { value: string; deletedChar: string; cursorPosition: number } {\n const start = input.selectionStart ?? input.value.length\n const end = input.selectionEnd ?? input.value.length\n const currentValue = input.value\n\n // 如果有选中内容,删除选中内容\n if (start !== end) {\n const deletedChar = currentValue.substring(start, end)\n const newValue = currentValue.substring(0, start) + currentValue.substring(end)\n return {\n value: newValue,\n deletedChar,\n cursorPosition: start,\n }\n }\n\n // 没有选中内容,删除光标前一个字符\n if (start === 0) {\n return {\n value: currentValue,\n deletedChar: '',\n cursorPosition: 0,\n }\n }\n\n const deletedChar = currentValue.charAt(start - 1)\n const newValue = currentValue.substring(0, start - 1) + currentValue.substring(start)\n\n return {\n value: newValue,\n deletedChar,\n cursorPosition: start - 1,\n }\n}\n\n/**\n * 创建 DOM 元素\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (HTMLElement | string)[],\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tag)\n\n if (attrs) {\n Object.entries(attrs).forEach(([key, value]) => {\n element.setAttribute(key, value)\n })\n }\n\n if (children) {\n children.forEach((child) => {\n if (typeof child === 'string') {\n element.appendChild(document.createTextNode(child))\n }\n else {\n element.appendChild(child)\n }\n })\n }\n\n return element\n}\n\n/**\n * 添加/移除 CSS 类\n */\nexport function toggleClass(element: HTMLElement, className: string, force?: boolean): void {\n element.classList.toggle(className, force)\n}\n\n/**\n * 设置 CSS 样式\n */\nexport function setStyles(element: HTMLElement, styles: Partial<CSSStyleDeclaration>): void {\n Object.assign(element.style, styles)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dom.js","names":["deletedChar","newValue","document"],"sources":["../../src/utils/dom.ts"],"sourcesContent":["/**\r\n * DOM 操作工具\r\n * @module utils/dom\r\n */\r\n\r\nimport type { BoundingRect, Position } from '../types'\r\n\r\n/**\r\n * 获取元素的边界矩形\r\n */\r\nexport function getBoundingRect(element: HTMLElement): BoundingRect {\r\n const rect = element.getBoundingClientRect()\r\n return {\r\n x: rect.left,\r\n y: rect.top,\r\n width: rect.width,\r\n height: rect.height,\r\n }\r\n}\r\n\r\n/**\r\n * 获取元素相对于文档的位置\r\n */\r\nexport function getElementOffset(element: HTMLElement): Position {\r\n const rect = element.getBoundingClientRect()\r\n const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft\r\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop\r\n\r\n return {\r\n x: rect.left + scrollLeft,\r\n y: rect.top + scrollTop,\r\n }\r\n}\r\n\r\n/**\r\n * 检查元素是否在视口内\r\n */\r\nexport function isElementInViewport(element: HTMLElement): boolean {\r\n const rect = element.getBoundingClientRect()\r\n return (\r\n rect.top >= 0 &&\r\n rect.left >= 0 &&\r\n rect.bottom <=\r\n (window.innerHeight || document.documentElement.clientHeight) &&\r\n rect.right <= (window.innerWidth || document.documentElement.clientWidth)\r\n )\r\n}\r\n\r\n/**\r\n * 检查点击是否在元素外部\r\n */\r\nexport function isClickOutside(\r\n event: MouseEvent | TouchEvent,\r\n element: HTMLElement\r\n): boolean {\r\n const target = event.target as Node\r\n return !element.contains(target)\r\n}\r\n\r\n/**\r\n * 阻止元素默认事件(用于防止输入框获取焦点)\r\n */\r\nexport function preventDefaultAndStopPropagation(event: Event): void {\r\n event.preventDefault()\r\n event.stopPropagation()\r\n}\r\n\r\n/**\r\n * 添加点击外部监听\r\n */\r\nexport function onClickOutside(\r\n element: HTMLElement,\r\n callback: (event: MouseEvent | TouchEvent) => void\r\n): () => void {\r\n const handler = (event: MouseEvent | TouchEvent) => {\r\n if (isClickOutside(event, element)) {\r\n callback(event)\r\n }\r\n }\r\n\r\n document.addEventListener('mousedown', handler)\r\n document.addEventListener('touchstart', handler)\r\n\r\n return () => {\r\n document.removeEventListener('mousedown', handler)\r\n document.removeEventListener('touchstart', handler)\r\n }\r\n}\r\n\r\n/**\r\n * 滚动元素到可视区域\r\n */\r\nexport function scrollIntoViewIfNeeded(\r\n element: HTMLElement,\r\n container?: HTMLElement\r\n): void {\r\n if (!container) {\r\n element.scrollIntoView({ behavior: 'smooth', block: 'nearest' })\r\n return\r\n }\r\n\r\n const elementRect = element.getBoundingClientRect()\r\n const containerRect = container.getBoundingClientRect()\r\n\r\n if (elementRect.bottom > containerRect.bottom) {\r\n container.scrollTop += elementRect.bottom - containerRect.bottom\r\n } else if (elementRect.top < containerRect.top) {\r\n container.scrollTop -= containerRect.top - elementRect.top\r\n }\r\n}\r\n\r\n/**\r\n * 获取输入框的光标位置\r\n */\r\nexport function getInputCursorPosition(\r\n input: HTMLInputElement | HTMLTextAreaElement\r\n): number {\r\n return input.selectionStart ?? input.value.length\r\n}\r\n\r\n/**\r\n * 设置输入框的光标位置\r\n */\r\nexport function setInputCursorPosition(\r\n input: HTMLInputElement | HTMLTextAreaElement,\r\n position: number\r\n): void {\r\n input.setSelectionRange(position, position)\r\n}\r\n\r\n/**\r\n * 在输入框当前光标位置插入文本\r\n */\r\nexport function insertTextAtCursor(\r\n input: HTMLInputElement | HTMLTextAreaElement,\r\n text: string,\r\n maxLength?: number\r\n): { value: string; cursorPosition: number } {\r\n const start = input.selectionStart ?? input.value.length\r\n const end = input.selectionEnd ?? input.value.length\r\n const currentValue = input.value\r\n\r\n const normalizedMaxLength =\r\n maxLength === undefined || !Number.isFinite(maxLength)\r\n ? undefined\r\n : Math.max(0, Math.floor(maxLength))\r\n let insertedText = text\r\n let newValue =\r\n currentValue.substring(0, start) +\r\n insertedText +\r\n currentValue.substring(end)\r\n\r\n // 检查最大长度\r\n if (\r\n normalizedMaxLength !== undefined &&\r\n newValue.length > normalizedMaxLength\r\n ) {\r\n const allowedLength =\r\n normalizedMaxLength - (currentValue.length - (end - start))\r\n if (allowedLength <= 0) {\r\n return { value: currentValue, cursorPosition: start }\r\n }\r\n insertedText = text.substring(0, allowedLength)\r\n newValue =\r\n currentValue.substring(0, start) +\r\n insertedText +\r\n currentValue.substring(end)\r\n }\r\n\r\n const newCursorPosition = start + insertedText.length\r\n\r\n return {\r\n value: newValue,\r\n cursorPosition: Math.min(newCursorPosition, newValue.length),\r\n }\r\n}\r\n\r\n/**\r\n * 删除输入框光标前的字符\r\n */\r\nexport function deleteCharBeforeCursor(\r\n input: HTMLInputElement | HTMLTextAreaElement\r\n): { value: string; deletedChar: string; cursorPosition: number } {\r\n const start = input.selectionStart ?? input.value.length\r\n const end = input.selectionEnd ?? input.value.length\r\n const currentValue = input.value\r\n\r\n // 如果有选中内容,删除选中内容\r\n if (start !== end) {\r\n const deletedChar = currentValue.substring(start, end)\r\n const newValue =\r\n currentValue.substring(0, start) + currentValue.substring(end)\r\n return {\r\n value: newValue,\r\n deletedChar,\r\n cursorPosition: start,\r\n }\r\n }\r\n\r\n // 没有选中内容,删除光标前一个字符\r\n if (start === 0) {\r\n return {\r\n value: currentValue,\r\n deletedChar: '',\r\n cursorPosition: 0,\r\n }\r\n }\r\n\r\n const deletedChar = currentValue.charAt(start - 1)\r\n const newValue =\r\n currentValue.substring(0, start - 1) + currentValue.substring(start)\r\n\r\n return {\r\n value: newValue,\r\n deletedChar,\r\n cursorPosition: start - 1,\r\n }\r\n}\r\n\r\n/**\r\n * 创建 DOM 元素\r\n */\r\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\r\n tag: K,\r\n attrs?: Record<string, string>,\r\n children?: (HTMLElement | string)[]\r\n): HTMLElementTagNameMap[K] {\r\n const element = document.createElement(tag)\r\n\r\n if (attrs) {\r\n Object.entries(attrs).forEach(([key, value]) => {\r\n element.setAttribute(key, value)\r\n })\r\n }\r\n\r\n if (children) {\r\n children.forEach(child => {\r\n if (typeof child === 'string') {\r\n element.appendChild(document.createTextNode(child))\r\n } else {\r\n element.appendChild(child)\r\n }\r\n })\r\n }\r\n\r\n return element\r\n}\r\n\r\n/**\r\n * Sanitize a caller-provided SVG icon before inserting it into the DOM.\r\n */\r\nexport function sanitizeSvgIcon(icon: string): string {\r\n if (typeof DOMParser === 'undefined' || typeof XMLSerializer === 'undefined')\r\n return ''\r\n\r\n const document = new DOMParser().parseFromString(icon, 'image/svg+xml')\r\n const svg = document.documentElement\r\n if (\r\n svg.tagName.toLowerCase() !== 'svg' ||\r\n document.querySelector('parsererror')\r\n )\r\n return ''\r\n\r\n svg\r\n .querySelectorAll('script, style, foreignObject')\r\n .forEach(node => node.remove())\r\n svg.querySelectorAll('*').forEach(element => {\r\n Array.from(element.attributes).forEach(attribute => {\r\n const name = attribute.name.toLowerCase()\r\n const value = attribute.value.trim()\r\n if (\r\n name.startsWith('on') ||\r\n ((name === 'href' || name === 'xlink:href') && !value.startsWith('#'))\r\n ) {\r\n element.removeAttribute(attribute.name)\r\n }\r\n })\r\n })\r\n\r\n return new XMLSerializer().serializeToString(svg)\r\n}\r\n\r\n/**\r\n * 添加/移除 CSS 类\r\n */\r\nexport function toggleClass(\r\n element: HTMLElement,\r\n className: string,\r\n force?: boolean\r\n): void {\r\n element.classList.toggle(className, force)\r\n}\r\n\r\n/**\r\n * 设置 CSS 样式\r\n */\r\nexport function setStyles(\r\n element: HTMLElement,\r\n styles: Partial<CSSStyleDeclaration>\r\n): void {\r\n Object.assign(element.style, styles)\r\n}\r\n"],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:29 *
5
+ * Build time: 2024-10-12 17:29:15 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
@@ -18,6 +18,7 @@ var EventEmitter = class {
18
18
  on(event, handler, options = {}) {
19
19
  if (!this.handlers.has(event)) this.handlers.set(event, /* @__PURE__ */ new Set());
20
20
  const handlers = this.handlers.get(event);
21
+ if (!handlers) return () => void 0;
21
22
  const wrapper = {
22
23
  handler,
23
24
  options
@@ -1 +1 @@
1
- {"version":3,"file":"event-emitter.js","names":[],"sources":["../../src/utils/event-emitter.ts"],"sourcesContent":["/**\n * 事件发射器\n * @module utils/event-emitter\n */\n\n/**\n * 事件处理函数类型\n */\nexport type EventHandler<T = unknown> = (data: T) => void\n\n/**\n * 事件处理器配置\n */\nexport interface EventHandlerOptions {\n /**\n * 是否只触发一次\n */\n once?: boolean\n\n /**\n * 优先级(数字越大优先级越高)\n */\n priority?: number\n}\n\n/**\n * 内部事件处理器包装\n */\ninterface EventHandlerWrapper<T> {\n handler: EventHandler<T>\n options: EventHandlerOptions\n}\n\n/**\n * 事件发射器类\n */\nexport class EventEmitter<EventMap extends Record<string, unknown>> {\n private handlers = new Map<keyof EventMap, Set<EventHandlerWrapper<unknown>>>()\n\n /**\n * 订阅事件\n */\n on<K extends keyof EventMap>(\n event: K,\n handler: EventHandler<EventMap[K]>,\n options: EventHandlerOptions = {},\n ): () => void {\n if (!this.handlers.has(event)) {\n this.handlers.set(event, new Set())\n }\n\n const handlers = this.handlers.get(event)!\n const wrapper: EventHandlerWrapper<EventMap[K]> = {\n handler: handler as EventHandler<unknown>,\n options,\n }\n\n handlers.add(wrapper as EventHandlerWrapper<unknown>)\n\n return () => {\n handlers.delete(wrapper as EventHandlerWrapper<unknown>)\n if (handlers.size === 0) {\n this.handlers.delete(event)\n }\n }\n }\n\n /**\n * 订阅事件(仅触发一次)\n */\n once<K extends keyof EventMap>(\n event: K,\n handler: EventHandler<EventMap[K]>,\n options: EventHandlerOptions = {},\n ): () => void {\n return this.on(event, handler, { ...options, once: true })\n }\n\n /**\n * 取消订阅事件\n */\n off<K extends keyof EventMap>(\n event: K,\n handler?: EventHandler<EventMap[K]>,\n ): void {\n if (!handler) {\n this.handlers.delete(event)\n return\n }\n\n const handlers = this.handlers.get(event)\n if (handlers) {\n handlers.forEach((wrapper) => {\n if (wrapper.handler === handler) {\n handlers.delete(wrapper)\n }\n })\n if (handlers.size === 0) {\n this.handlers.delete(event)\n }\n }\n }\n\n /**\n * 发布事件\n */\n emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {\n const handlers = this.handlers.get(event)\n if (!handlers || handlers.size === 0) {\n return\n }\n\n // 按优先级排序\n const sortedHandlers = Array.from(handlers).sort((a, b) => {\n const priorityA = a.options.priority ?? 0\n const priorityB = b.options.priority ?? 0\n return priorityB - priorityA\n })\n\n const toRemove: EventHandlerWrapper<unknown>[] = []\n\n sortedHandlers.forEach((wrapper) => {\n try {\n wrapper.handler(data)\n if (wrapper.options.once) {\n toRemove.push(wrapper)\n }\n }\n catch (error) {\n console.error(`[KeyboardEventEmitter] Error in event handler for \"${String(event)}\":`, error)\n }\n })\n\n // 移除 once 处理器\n toRemove.forEach(wrapper => handlers.delete(wrapper))\n if (handlers.size === 0) {\n this.handlers.delete(event)\n }\n }\n\n /**\n * 清除所有事件订阅\n */\n clear(): void {\n this.handlers.clear()\n }\n\n /**\n * 获取事件订阅数量\n */\n listenerCount(event?: keyof EventMap): number {\n if (event !== undefined) {\n return this.handlers.get(event)?.size ?? 0\n }\n\n let count = 0\n this.handlers.forEach((handlers) => {\n count += handlers.size\n })\n return count\n }\n}\n"],"mappings":";;;;;;;;;;AAoCO,IAAM,eAAN,MAA6D;CAA7D,cAAA;EACL,KAAQ,2BAAW,IAAI,IAAuD;CAAA;;;;CAK9E,GACE,OACA,SACA,UAA+B,CAAC,GACpB;EACZ,IAAI,CAAC,KAAK,SAAS,IAAI,KAAK,GAC1B,KAAK,SAAS,IAAI,uBAAO,IAAI,IAAI,CAAC;EAGpC,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK;EACxC,MAAM,UAA4C;GAChD;GACA;EACF;EAEA,SAAS,IAAI,OAAuC;EAEpD,aAAa;GACX,SAAS,OAAO,OAAuC;GACvD,IAAI,SAAS,SAAS,GACpB,KAAK,SAAS,OAAO,KAAK;EAE9B;CACF;;;;CAKA,KACE,OACA,SACA,UAA+B,CAAC,GACpB;EACZ,OAAO,KAAK,GAAG,OAAO,SAAS;GAAE,GAAG;GAAS,MAAM;EAAK,CAAC;CAC3D;;;;CAKA,IACE,OACA,SACM;EACN,IAAI,CAAC,SAAS;GACZ,KAAK,SAAS,OAAO,KAAK;GAC1B;EACF;EAEA,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK;EACxC,IAAI,UAAU;GACZ,SAAS,SAAS,YAAY;IAC5B,IAAI,QAAQ,YAAY,SACtB,SAAS,OAAO,OAAO;GAE3B,CAAC;GACD,IAAI,SAAS,SAAS,GACpB,KAAK,SAAS,OAAO,KAAK;EAE9B;CACF;;;;CAKA,KAA+B,OAAU,MAAyB;EAChE,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK;EACxC,IAAI,CAAC,YAAY,SAAS,SAAS,GACjC;EAIF,MAAM,iBAAiB,MAAM,KAAK,QAAQ,CAAA,CAAE,MAAM,GAAG,MAAM;GACzD,MAAM,YAAY,EAAE,QAAQ,YAAY;GAExC,QADkB,EAAE,QAAQ,YAAY,KACrB;EACrB,CAAC;EAED,MAAM,WAA2C,CAAC;EAElD,eAAe,SAAS,YAAY;GAClC,IAAI;IACF,QAAQ,QAAQ,IAAI;IACpB,IAAI,QAAQ,QAAQ,MAClB,SAAS,KAAK,OAAO;GAEzB,SACO,OAAO;IACZ,QAAQ,MAAM,sDAAsD,OAAO,KAAK,EAAC,KAAM,KAAK;GAC9F;EACF,CAAC;EAGD,SAAS,SAAQ,YAAW,SAAS,OAAO,OAAO,CAAC;EACpD,IAAI,SAAS,SAAS,GACpB,KAAK,SAAS,OAAO,KAAK;CAE9B;;;;CAKA,QAAc;EACZ,KAAK,SAAS,MAAM;CACtB;;;;CAKA,cAAc,OAAgC;EAC5C,IAAI,UAAU,KAAA,GACZ,OAAO,KAAK,SAAS,IAAI,KAAK,CAAA,EAAG,QAAQ;EAG3C,IAAI,QAAQ;EACZ,KAAK,SAAS,SAAS,aAAa;GAClC,SAAS,SAAS;EACpB,CAAC;EACD,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"event-emitter.js","names":[],"sources":["../../src/utils/event-emitter.ts"],"sourcesContent":["/**\r\n * 事件发射器\r\n * @module utils/event-emitter\r\n */\r\n\r\n/**\r\n * 事件处理函数类型\r\n */\r\nexport type EventHandler<T = unknown> = (data: T) => void\r\n\r\n/**\r\n * 事件处理器配置\r\n */\r\nexport interface EventHandlerOptions {\r\n /**\r\n * 是否只触发一次\r\n */\r\n once?: boolean\r\n\r\n /**\r\n * 优先级(数字越大优先级越高)\r\n */\r\n priority?: number\r\n}\r\n\r\n/**\r\n * 内部事件处理器包装\r\n */\r\ninterface EventHandlerWrapper<T> {\r\n handler: EventHandler<T>\r\n options: EventHandlerOptions\r\n}\r\n\r\n/**\r\n * 事件发射器类\r\n */\r\nexport class EventEmitter<EventMap extends Record<string, unknown>> {\r\n private handlers = new Map<\r\n keyof EventMap,\r\n Set<EventHandlerWrapper<unknown>>\r\n >()\r\n\r\n /**\r\n * 订阅事件\r\n */\r\n on<K extends keyof EventMap>(\r\n event: K,\r\n handler: EventHandler<EventMap[K]>,\r\n options: EventHandlerOptions = {}\r\n ): () => void {\r\n if (!this.handlers.has(event)) {\r\n this.handlers.set(event, new Set())\r\n }\r\n\r\n const handlers = this.handlers.get(event)\r\n if (!handlers) {\r\n return () => undefined\r\n }\r\n const wrapper: EventHandlerWrapper<EventMap[K]> = {\r\n handler: handler as EventHandler<unknown>,\r\n options,\r\n }\r\n\r\n handlers.add(wrapper as EventHandlerWrapper<unknown>)\r\n\r\n return () => {\r\n handlers.delete(wrapper as EventHandlerWrapper<unknown>)\r\n if (handlers.size === 0) {\r\n this.handlers.delete(event)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 订阅事件(仅触发一次)\r\n */\r\n once<K extends keyof EventMap>(\r\n event: K,\r\n handler: EventHandler<EventMap[K]>,\r\n options: EventHandlerOptions = {}\r\n ): () => void {\r\n return this.on(event, handler, { ...options, once: true })\r\n }\r\n\r\n /**\r\n * 取消订阅事件\r\n */\r\n off<K extends keyof EventMap>(\r\n event: K,\r\n handler?: EventHandler<EventMap[K]>\r\n ): void {\r\n if (!handler) {\r\n this.handlers.delete(event)\r\n return\r\n }\r\n\r\n const handlers = this.handlers.get(event)\r\n if (handlers) {\r\n handlers.forEach(wrapper => {\r\n if (wrapper.handler === handler) {\r\n handlers.delete(wrapper)\r\n }\r\n })\r\n if (handlers.size === 0) {\r\n this.handlers.delete(event)\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * 发布事件\r\n */\r\n emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {\r\n const handlers = this.handlers.get(event)\r\n if (!handlers || handlers.size === 0) {\r\n return\r\n }\r\n\r\n // 按优先级排序\r\n const sortedHandlers = Array.from(handlers).sort((a, b) => {\r\n const priorityA = a.options.priority ?? 0\r\n const priorityB = b.options.priority ?? 0\r\n return priorityB - priorityA\r\n })\r\n\r\n const toRemove: EventHandlerWrapper<unknown>[] = []\r\n\r\n sortedHandlers.forEach(wrapper => {\r\n try {\r\n wrapper.handler(data)\r\n if (wrapper.options.once) {\r\n toRemove.push(wrapper)\r\n }\r\n } catch (error) {\r\n console.error(\r\n `[KeyboardEventEmitter] Error in event handler for \"${String(event)}\":`,\r\n error\r\n )\r\n }\r\n })\r\n\r\n // 移除 once 处理器\r\n toRemove.forEach(wrapper => handlers.delete(wrapper))\r\n if (handlers.size === 0) {\r\n this.handlers.delete(event)\r\n }\r\n }\r\n\r\n /**\r\n * 清除所有事件订阅\r\n */\r\n clear(): void {\r\n this.handlers.clear()\r\n }\r\n\r\n /**\r\n * 获取事件订阅数量\r\n */\r\n listenerCount(event?: keyof EventMap): number {\r\n if (event !== undefined) {\r\n return this.handlers.get(event)?.size ?? 0\r\n }\r\n\r\n let count = 0\r\n this.handlers.forEach(handlers => {\r\n count += handlers.size\r\n })\r\n return count\r\n }\r\n}\r\n"],"mappings":";;;;;;;;;;AAoCO,IAAM,eAAN,MAA6D;CAA7D,cAAA;EACL,KAAQ,2BAAW,IAAI,IAGrB;CAAA;;;;CAKF,GACE,OACA,SACA,UAA+B,CAAC,GACpB;EACZ,IAAI,CAAC,KAAK,SAAS,IAAI,KAAK,GAC1B,KAAK,SAAS,IAAI,uBAAO,IAAI,IAAI,CAAC;EAGpC,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK;EACxC,IAAI,CAAC,UACH,aAAa,KAAA;EAEf,MAAM,UAA4C;GAChD;GACA;EACF;EAEA,SAAS,IAAI,OAAuC;EAEpD,aAAa;GACX,SAAS,OAAO,OAAuC;GACvD,IAAI,SAAS,SAAS,GACpB,KAAK,SAAS,OAAO,KAAK;EAE9B;CACF;;;;CAKA,KACE,OACA,SACA,UAA+B,CAAC,GACpB;EACZ,OAAO,KAAK,GAAG,OAAO,SAAS;GAAE,GAAG;GAAS,MAAM;EAAK,CAAC;CAC3D;;;;CAKA,IACE,OACA,SACM;EACN,IAAI,CAAC,SAAS;GACZ,KAAK,SAAS,OAAO,KAAK;GAC1B;EACF;EAEA,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK;EACxC,IAAI,UAAU;GACZ,SAAS,SAAQ,YAAW;IAC1B,IAAI,QAAQ,YAAY,SACtB,SAAS,OAAO,OAAO;GAE3B,CAAC;GACD,IAAI,SAAS,SAAS,GACpB,KAAK,SAAS,OAAO,KAAK;EAE9B;CACF;;;;CAKA,KAA+B,OAAU,MAAyB;EAChE,MAAM,WAAW,KAAK,SAAS,IAAI,KAAK;EACxC,IAAI,CAAC,YAAY,SAAS,SAAS,GACjC;EAIF,MAAM,iBAAiB,MAAM,KAAK,QAAQ,CAAA,CAAE,MAAM,GAAG,MAAM;GACzD,MAAM,YAAY,EAAE,QAAQ,YAAY;GAExC,QADkB,EAAE,QAAQ,YAAY,KACrB;EACrB,CAAC;EAED,MAAM,WAA2C,CAAC;EAElD,eAAe,SAAQ,YAAW;GAChC,IAAI;IACF,QAAQ,QAAQ,IAAI;IACpB,IAAI,QAAQ,QAAQ,MAClB,SAAS,KAAK,OAAO;GAEzB,SAAS,OAAO;IACd,QAAQ,MACN,sDAAsD,OAAO,KAAK,EAAC,KACnE,KACF;GACF;EACF,CAAC;EAGD,SAAS,SAAQ,YAAW,SAAS,OAAO,OAAO,CAAC;EACpD,IAAI,SAAS,SAAS,GACpB,KAAK,SAAS,OAAO,KAAK;CAE9B;;;;CAKA,QAAc;EACZ,KAAK,SAAS,MAAM;CACtB;;;;CAKA,cAAc,OAAgC;EAC5C,IAAI,UAAU,KAAA,GACZ,OAAO,KAAK,SAAS,IAAI,KAAK,CAAA,EAAG,QAAQ;EAG3C,IAAI,QAAQ;EACZ,KAAK,SAAS,SAAQ,aAAY;GAChC,SAAS,SAAS;EACpB,CAAC;EACD,OAAO;CACT;AACF"}
@@ -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
  * ***********************************
@@ -20,13 +20,13 @@ function formatBankCard(value) {
20
20
  function formatAmount(value, decimalPlaces = 2) {
21
21
  let result = value.replace(/[^\d.]/g, "");
22
22
  const parts = result.split(".");
23
- if (parts.length > 2) result = parts[0] + "." + parts.slice(1).join("");
24
- if (parts.length === 2 && parts[1].length > decimalPlaces) result = parts[0] + "." + parts[1].slice(0, decimalPlaces);
23
+ if (parts.length > 2) result = `${parts[0]}.${parts.slice(1).join("")}`;
24
+ if (parts.length === 2 && parts[1].length > decimalPlaces) result = `${parts[0]}.${parts[1].slice(0, decimalPlaces)}`;
25
25
  if (result.length > 1 && result.startsWith("0") && result[1] !== ".") result = result.replace(/^0+/, "") || "0";
26
26
  return result;
27
27
  }
28
28
  function formatIdCard(value) {
29
- return value.replace(/[^\dXx]/g, "").slice(0, 18).toUpperCase();
29
+ return value.replace(/[^\dX]/gi, "").slice(0, 18).toUpperCase();
30
30
  }
31
31
  function formatLicensePlate(value) {
32
32
  return value.toUpperCase().slice(0, 8);
@@ -1 +1 @@
1
- {"version":3,"file":"formatters.js","names":[],"sources":["../../src/utils/formatters.ts"],"sourcesContent":["/**\n * 输入格式化工具\n * @module utils/formatters\n */\n\n/**\n * 格式化手机号 (3-4-4)\n * @example formatPhone('13812345678') => '138 1234 5678'\n */\nexport function formatPhone(value: string): string {\n const digits = value.replace(/\\D/g, '').slice(0, 11)\n if (digits.length <= 3) return digits\n if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`\n return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7)}`\n}\n\n/**\n * 格式化银行卡号 (4-4-4-4-3)\n * @example formatBankCard('6222021234567890123') => '6222 0212 3456 7890 123'\n */\nexport function formatBankCard(value: string): string {\n const digits = value.replace(/\\D/g, '').slice(0, 19)\n return digits.replace(/(\\d{4})(?=\\d)/g, '$1 ')\n}\n\n/**\n * 格式化金额(限制小数位数)\n * @param value - 输入值\n * @param decimalPlaces - 小数位数,默认 2\n */\nexport function formatAmount(value: string, decimalPlaces: number = 2): string {\n // 移除非数字和小数点\n let result = value.replace(/[^\\d.]/g, '')\n\n // 只保留第一个小数点\n const parts = result.split('.')\n if (parts.length > 2) {\n result = parts[0] + '.' + parts.slice(1).join('')\n }\n\n // 限制小数位数\n if (parts.length === 2 && parts[1].length > decimalPlaces) {\n result = parts[0] + '.' + parts[1].slice(0, decimalPlaces)\n }\n\n // 移除前导零(保留 0.xx 格式)\n if (result.length > 1 && result.startsWith('0') && result[1] !== '.') {\n result = result.replace(/^0+/, '') || '0'\n }\n\n return result\n}\n\n/**\n * 格式化身份证号(自动大写 X)\n */\nexport function formatIdCard(value: string): string {\n return value\n .replace(/[^\\dXx]/g, '')\n .slice(0, 18)\n .toUpperCase()\n}\n\n/**\n * 格式化车牌号\n */\nexport function formatLicensePlate(value: string): string {\n return value.toUpperCase().slice(0, 8)\n}\n\n/**\n * 格式化验证码(纯数字)\n * @param value - 输入值\n * @param length - 验证码长度,默认 6\n */\nexport function formatVerifyCode(value: string, length: number = 6): string {\n return value.replace(/\\D/g, '').slice(0, length)\n}\n\n/**\n * 通用格式化器注册表\n */\nexport type FormatterFunction = (value: string) => string\n\nconst formatters: Map<string, FormatterFunction> = new Map([\n ['phone', formatPhone],\n ['bankcard', formatBankCard],\n ['idcard', formatIdCard],\n ['license-plate', formatLicensePlate],\n ['verifycode', formatVerifyCode],\n])\n\n/**\n * 注册自定义格式化器\n */\nexport function registerFormatter(name: string, formatter: FormatterFunction): void {\n formatters.set(name, formatter)\n}\n\n/**\n * 获取格式化器\n */\nexport function getFormatter(name: string): FormatterFunction | undefined {\n return formatters.get(name)\n}\n\n/**\n * 应用格式化\n */\nexport function applyFormatter(name: string, value: string): string {\n const formatter = formatters.get(name)\n return formatter ? formatter(value) : value\n}\n"],"mappings":";;;;;;;;;;AASO,SAAS,YAAY,OAAuB;CACjD,MAAM,SAAS,MAAM,QAAQ,OAAO,EAAE,CAAA,CAAE,MAAM,GAAG,EAAE;CACnD,IAAI,OAAO,UAAU,GAAG,OAAO;CAC/B,IAAI,OAAO,UAAU,GAAG,OAAO,GAAG,OAAO,MAAM,GAAG,CAAC,EAAC,GAAI,OAAO,MAAM,CAAC;CACtE,OAAO,GAAG,OAAO,MAAM,GAAG,CAAC,EAAC,GAAI,OAAO,MAAM,GAAG,CAAC,EAAC,GAAI,OAAO,MAAM,CAAC;AACtE;AAMO,SAAS,eAAe,OAAuB;CAEpD,OADe,MAAM,QAAQ,OAAO,EAAE,CAAA,CAAE,MAAM,GAAG,EAC1C,CAAA,CAAO,QAAQ,kBAAkB,KAAK;AAC/C;AAOO,SAAS,aAAa,OAAe,gBAAwB,GAAW;CAE7E,IAAI,SAAS,MAAM,QAAQ,WAAW,EAAE;CAGxC,MAAM,QAAQ,OAAO,MAAM,GAAG;CAC9B,IAAI,MAAM,SAAS,GACjB,SAAS,MAAM,KAAK,MAAM,MAAM,MAAM,CAAC,CAAA,CAAE,KAAK,EAAE;CAIlD,IAAI,MAAM,WAAW,KAAK,MAAM,EAAC,CAAE,SAAS,eAC1C,SAAS,MAAM,KAAK,MAAM,MAAM,EAAC,CAAE,MAAM,GAAG,aAAa;CAI3D,IAAI,OAAO,SAAS,KAAK,OAAO,WAAW,GAAG,KAAK,OAAO,OAAO,KAC/D,SAAS,OAAO,QAAQ,OAAO,EAAE,KAAK;CAGxC,OAAO;AACT;AAKO,SAAS,aAAa,OAAuB;CAClD,OAAO,MACJ,QAAQ,YAAY,EAAE,CAAA,CACtB,MAAM,GAAG,EAAE,CAAA,CACX,YAAY;AACjB;AAKO,SAAS,mBAAmB,OAAuB;CACxD,OAAO,MAAM,YAAY,CAAA,CAAE,MAAM,GAAG,CAAC;AACvC;AAOO,SAAS,iBAAiB,OAAe,SAAiB,GAAW;CAC1E,OAAO,MAAM,QAAQ,OAAO,EAAE,CAAA,CAAE,MAAM,GAAG,MAAM;AACjD;AAOA,MAAM,6BAA6C,IAAI,IAAI;CACzD,CAAC,SAAS,WAAW;CACrB,CAAC,YAAY,cAAc;CAC3B,CAAC,UAAU,YAAY;CACvB,CAAC,iBAAiB,kBAAkB;CACpC,CAAC,cAAc,gBAAgB;AACjC,CAAC;AAKM,SAAS,kBAAkB,MAAc,WAAoC;CAClF,WAAW,IAAI,MAAM,SAAS;AAChC;AAKO,SAAS,aAAa,MAA6C;CACxE,OAAO,WAAW,IAAI,IAAI;AAC5B;AAKO,SAAS,eAAe,MAAc,OAAuB;CAClE,MAAM,YAAY,WAAW,IAAI,IAAI;CACrC,OAAO,YAAY,UAAU,KAAK,IAAI;AACxC"}
1
+ {"version":3,"file":"formatters.js","names":[],"sources":["../../src/utils/formatters.ts"],"sourcesContent":["/**\r\n * 输入格式化工具\r\n * @module utils/formatters\r\n */\r\n\r\n/**\r\n * 格式化手机号 (3-4-4)\r\n * @example formatPhone('13812345678') => '138 1234 5678'\r\n */\r\nexport function formatPhone(value: string): string {\r\n const digits = value.replace(/\\D/g, '').slice(0, 11)\r\n if (digits.length <= 3) return digits\r\n if (digits.length <= 7) return `${digits.slice(0, 3)} ${digits.slice(3)}`\r\n return `${digits.slice(0, 3)} ${digits.slice(3, 7)} ${digits.slice(7)}`\r\n}\r\n\r\n/**\r\n * 格式化银行卡号 (4-4-4-4-3)\r\n * @example formatBankCard('6222021234567890123') => '6222 0212 3456 7890 123'\r\n */\r\nexport function formatBankCard(value: string): string {\r\n const digits = value.replace(/\\D/g, '').slice(0, 19)\r\n return digits.replace(/(\\d{4})(?=\\d)/g, '$1 ')\r\n}\r\n\r\n/**\r\n * 格式化金额(限制小数位数)\r\n * @param value - 输入值\r\n * @param decimalPlaces - 小数位数,默认 2\r\n */\r\nexport function formatAmount(value: string, decimalPlaces: number = 2): string {\r\n // 移除非数字和小数点\r\n let result = value.replace(/[^\\d.]/g, '')\r\n\r\n // 只保留第一个小数点\r\n const parts = result.split('.')\r\n if (parts.length > 2) {\r\n result = `${parts[0]}.${parts.slice(1).join('')}`\r\n }\r\n\r\n // 限制小数位数\r\n if (parts.length === 2 && parts[1].length > decimalPlaces) {\r\n result = `${parts[0]}.${parts[1].slice(0, decimalPlaces)}`\r\n }\r\n\r\n // 移除前导零(保留 0.xx 格式)\r\n if (result.length > 1 && result.startsWith('0') && result[1] !== '.') {\r\n result = result.replace(/^0+/, '') || '0'\r\n }\r\n\r\n return result\r\n}\r\n\r\n/**\r\n * 格式化身份证号(自动大写 X)\r\n */\r\nexport function formatIdCard(value: string): string {\r\n return value\r\n .replace(/[^\\dX]/gi, '')\r\n .slice(0, 18)\r\n .toUpperCase()\r\n}\r\n\r\n/**\r\n * 格式化车牌号\r\n */\r\nexport function formatLicensePlate(value: string): string {\r\n return value.toUpperCase().slice(0, 8)\r\n}\r\n\r\n/**\r\n * 格式化验证码(纯数字)\r\n * @param value - 输入值\r\n * @param length - 验证码长度,默认 6\r\n */\r\nexport function formatVerifyCode(value: string, length: number = 6): string {\r\n return value.replace(/\\D/g, '').slice(0, length)\r\n}\r\n\r\n/**\r\n * 通用格式化器注册表\r\n */\r\nexport type FormatterFunction = (value: string) => string\r\n\r\nconst formatters: Map<string, FormatterFunction> = new Map([\r\n ['phone', formatPhone],\r\n ['bankcard', formatBankCard],\r\n ['idcard', formatIdCard],\r\n ['license-plate', formatLicensePlate],\r\n ['verifycode', formatVerifyCode],\r\n])\r\n\r\n/**\r\n * 注册自定义格式化器\r\n */\r\nexport function registerFormatter(\r\n name: string,\r\n formatter: FormatterFunction\r\n): void {\r\n formatters.set(name, formatter)\r\n}\r\n\r\n/**\r\n * 获取格式化器\r\n */\r\nexport function getFormatter(name: string): FormatterFunction | undefined {\r\n return formatters.get(name)\r\n}\r\n\r\n/**\r\n * 应用格式化\r\n */\r\nexport function applyFormatter(name: string, value: string): string {\r\n const formatter = formatters.get(name)\r\n return formatter ? formatter(value) : value\r\n}\r\n"],"mappings":";;;;;;;;;;AASO,SAAS,YAAY,OAAuB;CACjD,MAAM,SAAS,MAAM,QAAQ,OAAO,EAAE,CAAA,CAAE,MAAM,GAAG,EAAE;CACnD,IAAI,OAAO,UAAU,GAAG,OAAO;CAC/B,IAAI,OAAO,UAAU,GAAG,OAAO,GAAG,OAAO,MAAM,GAAG,CAAC,EAAC,GAAI,OAAO,MAAM,CAAC;CACtE,OAAO,GAAG,OAAO,MAAM,GAAG,CAAC,EAAC,GAAI,OAAO,MAAM,GAAG,CAAC,EAAC,GAAI,OAAO,MAAM,CAAC;AACtE;AAMO,SAAS,eAAe,OAAuB;CAEpD,OADe,MAAM,QAAQ,OAAO,EAAE,CAAA,CAAE,MAAM,GAAG,EAC1C,CAAA,CAAO,QAAQ,kBAAkB,KAAK;AAC/C;AAOO,SAAS,aAAa,OAAe,gBAAwB,GAAW;CAE7E,IAAI,SAAS,MAAM,QAAQ,WAAW,EAAE;CAGxC,MAAM,QAAQ,OAAO,MAAM,GAAG;CAC9B,IAAI,MAAM,SAAS,GACjB,SAAS,GAAG,MAAM,GAAE,GAAI,MAAM,MAAM,CAAC,CAAA,CAAE,KAAK,EAAE;CAIhD,IAAI,MAAM,WAAW,KAAK,MAAM,EAAC,CAAE,SAAS,eAC1C,SAAS,GAAG,MAAM,GAAE,GAAI,MAAM,EAAC,CAAE,MAAM,GAAG,aAAa;CAIzD,IAAI,OAAO,SAAS,KAAK,OAAO,WAAW,GAAG,KAAK,OAAO,OAAO,KAC/D,SAAS,OAAO,QAAQ,OAAO,EAAE,KAAK;CAGxC,OAAO;AACT;AAKO,SAAS,aAAa,OAAuB;CAClD,OAAO,MACJ,QAAQ,YAAY,EAAE,CAAA,CACtB,MAAM,GAAG,EAAE,CAAA,CACX,YAAY;AACjB;AAKO,SAAS,mBAAmB,OAAuB;CACxD,OAAO,MAAM,YAAY,CAAA,CAAE,MAAM,GAAG,CAAC;AACvC;AAOO,SAAS,iBAAiB,OAAe,SAAiB,GAAW;CAC1E,OAAO,MAAM,QAAQ,OAAO,EAAE,CAAA,CAAE,MAAM,GAAG,MAAM;AACjD;AAOA,MAAM,6BAA6C,IAAI,IAAI;CACzD,CAAC,SAAS,WAAW;CACrB,CAAC,YAAY,cAAc;CAC3B,CAAC,UAAU,YAAY;CACvB,CAAC,iBAAiB,kBAAkB;CACpC,CAAC,cAAc,gBAAgB;AACjC,CAAC;AAKM,SAAS,kBACd,MACA,WACM;CACN,WAAW,IAAI,MAAM,SAAS;AAChC;AAKO,SAAS,aAAa,MAA6C;CACxE,OAAO,WAAW,IAAI,IAAI;AAC5B;AAKO,SAAS,eAAe,MAAc,OAAuB;CAClE,MAAM,YAAY,WAAW,IAAI,IAAI;CACrC,OAAO,YAAY,UAAU,KAAK,IAAI;AACxC"}
@@ -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
  * ***********************************
@@ -65,7 +65,7 @@ function chunkArray(array, chunkSize) {
65
65
  }
66
66
  function createRandomNumberLayout(options) {
67
67
  const { keysPerRow = 3, includeBackspace = true, includeDone = true, includeClear = false } = options || {};
68
- const rows = chunkArray(getRandomNumbers().map((n, i) => createCharKey(n, i)), keysPerRow).map((keys, index) => ({
68
+ const rows = chunkArray(getRandomNumbers().map((n, i) => createCharKey(n, i)), keysPerRow).map((keys) => ({
69
69
  className: "numpad-row",
70
70
  keys
71
71
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"random.js","names":[],"sources":["../../src/utils/random.ts"],"sourcesContent":["/**\n * 随机工具函数\n * @module utils/random\n */\n\nimport type { KeyboardConfig, KeyboardRow, KeyDefinition } from '../types'\n\n/**\n * Fisher-Yates 洗牌算法\n * @param array - 要洗牌的数组\n * @returns 洗牌后的新数组\n */\nexport function shuffleArray<T>(array: T[]): T[] {\n const result = [...array]\n for (let i = result.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1))\n ;[result[i], result[j]] = [result[j], result[i]]\n }\n return result\n}\n\n/**\n * 生成随机数字数组 (0-9)\n */\nexport function getRandomNumbers(): string[] {\n return shuffleArray(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])\n}\n\n/**\n * 生成随机字母数组 (A-Z)\n */\nexport function getRandomLetters(): string[] {\n const letters = Array.from({ length: 26 }, (_, i) =>\n String.fromCharCode(65 + i),\n )\n return shuffleArray(letters)\n}\n\n/**\n * 生成随机数字+字母数组\n */\nexport function getRandomAlphanumeric(): string[] {\n const numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']\n const letters = Array.from({ length: 26 }, (_, i) =>\n String.fromCharCode(65 + i),\n )\n return shuffleArray([...numbers, ...letters])\n}\n\n/**\n * 创建字符按键\n */\nfunction createCharKey(char: string, index: number): KeyDefinition {\n return {\n id: `random-${char}-${index}`,\n type: 'char',\n label: char,\n value: char,\n className: 'key-random',\n }\n}\n\n/**\n * 将字符数组分成行\n */\nfunction chunkArray<T>(array: T[], chunkSize: number): T[][] {\n const chunks: T[][] = []\n for (let i = 0; i < array.length; i += chunkSize) {\n chunks.push(array.slice(i, i + chunkSize))\n }\n return chunks\n}\n\n/**\n * 生成随机数字键盘布局\n * @param options - 配置选项\n */\nexport function createRandomNumberLayout(options?: {\n /** 每行按键数 */\n keysPerRow?: number\n /** 是否包含退格键 */\n includeBackspace?: boolean\n /** 是否包含完成键 */\n includeDone?: boolean\n /** 是否包含清空键 */\n includeClear?: boolean\n}): KeyboardConfig {\n const {\n keysPerRow = 3,\n includeBackspace = true,\n includeDone = true,\n includeClear = false,\n } = options || {}\n\n const randomNumbers = getRandomNumbers()\n const numberKeys = randomNumbers.map((n, i) => createCharKey(n, i))\n const chunks = chunkArray(numberKeys, keysPerRow)\n\n const rows: KeyboardRow[] = chunks.map((keys, index) => ({\n className: 'numpad-row',\n keys,\n }))\n\n // 添加功能键行\n const fnKeys: KeyDefinition[] = []\n\n if (includeClear) {\n fnKeys.push({\n id: 'clear',\n type: 'clear',\n label: '清空',\n className: 'key-clear key-fn',\n })\n }\n\n if (includeBackspace) {\n fnKeys.push({\n id: 'backspace',\n type: 'backspace',\n label: '⌫',\n className: 'key-backspace key-fn',\n })\n }\n\n if (includeDone) {\n fnKeys.push({\n id: 'done',\n type: 'done',\n label: '确定',\n className: 'key-done key-fn',\n })\n }\n\n if (fnKeys.length > 0) {\n rows.push({\n className: 'numpad-row',\n keys: fnKeys,\n })\n }\n\n return {\n type: 'custom',\n name: '随机数字键盘',\n supportShift: false,\n rows,\n data: {\n isRandom: true,\n randomType: 'number',\n },\n }\n}\n\n/**\n * 生成4x3随机数字键盘(密码输入常用)\n */\nexport function createRandomPinLayout(options?: {\n /** 是否包含退格键 */\n includeBackspace?: boolean\n /** 是否包含完成键 */\n includeDone?: boolean\n}): KeyboardConfig {\n const { includeBackspace = true, includeDone = true } = options || {}\n\n const randomNumbers = getRandomNumbers()\n\n // 前9个数字 (3x3)\n const rows: KeyboardRow[] = [\n {\n className: 'numpad-row',\n keys: randomNumbers.slice(0, 3).map((n, i) => createCharKey(n, i)),\n },\n {\n className: 'numpad-row',\n keys: randomNumbers.slice(3, 6).map((n, i) => createCharKey(n, i + 3)),\n },\n {\n className: 'numpad-row',\n keys: randomNumbers.slice(6, 9).map((n, i) => createCharKey(n, i + 6)),\n },\n ]\n\n // 最后一行:功能键 + 0 + 功能键\n const lastRow: KeyDefinition[] = []\n\n if (includeBackspace) {\n lastRow.push({\n id: 'backspace',\n type: 'backspace',\n label: '⌫',\n className: 'key-backspace key-fn',\n })\n } else {\n lastRow.push({\n id: 'empty-1',\n type: 'char',\n label: '',\n value: '',\n disabled: true,\n className: 'key-placeholder',\n })\n }\n\n lastRow.push(createCharKey(randomNumbers[9], 9))\n\n if (includeDone) {\n lastRow.push({\n id: 'done',\n type: 'done',\n label: '确定',\n className: 'key-done key-fn',\n })\n } else {\n lastRow.push({\n id: 'empty-2',\n type: 'char',\n label: '',\n value: '',\n disabled: true,\n className: 'key-placeholder',\n })\n }\n\n rows.push({\n className: 'numpad-row',\n keys: lastRow,\n })\n\n return {\n type: 'custom',\n name: '随机PIN键盘',\n supportShift: false,\n rows,\n data: {\n isRandom: true,\n randomType: 'pin',\n },\n }\n}\n\n/**\n * 生成随机字母键盘布局\n * 默认使用 QWERTY 风格行分布 (10/9/7),看起来像真实键盘\n */\nexport function createRandomLetterLayout(options?: {\n /** 是否大写 */\n uppercase?: boolean\n /** 行分布,默认 [10, 9, 7] 模拟 QWERTY 布局 */\n rowDistribution?: number[]\n}): KeyboardConfig {\n const { uppercase = true, rowDistribution = [10, 9, 7] } = options || {}\n\n let randomLetters = getRandomLetters()\n if (!uppercase) {\n randomLetters = randomLetters.map((l) => l.toLowerCase())\n }\n\n const letterKeys = randomLetters.map((l, i) => createCharKey(l, i))\n\n // 按行分布切割\n const rows: KeyboardRow[] = []\n let offset = 0\n for (const count of rowDistribution) {\n rows.push({\n className: 'numpad-row',\n keys: letterKeys.slice(offset, offset + count),\n })\n offset += count\n }\n // 剩余字母放最后一行\n if (offset < letterKeys.length) {\n rows.push({\n className: 'numpad-row',\n keys: letterKeys.slice(offset),\n })\n }\n\n // 功能键行\n rows.push({\n className: 'numpad-row',\n keys: [\n {\n id: 'clear',\n type: 'clear',\n label: '清空',\n className: 'key-clear key-fn',\n },\n {\n id: 'backspace',\n type: 'backspace',\n label: '⌫',\n className: 'key-backspace key-fn',\n },\n {\n id: 'done',\n type: 'done',\n label: '确定',\n className: 'key-done key-fn',\n },\n ],\n })\n\n return {\n type: 'custom',\n name: '随机字母键盘',\n supportShift: false,\n rows,\n data: {\n isRandom: true,\n randomType: 'letter',\n },\n }\n}\n\n/**\n * 生成混合随机键盘(数字+字母)\n * 默认使用 QWERTY 风格行分布 (10/10/9/7),看起来像真实键盘\n */\nexport function createRandomMixedLayout(options?: {\n /** 行分布,默认 [10, 10, 9, 7] */\n rowDistribution?: number[]\n}): KeyboardConfig {\n const { rowDistribution = [10, 10, 9, 7] } = options || {}\n\n const randomChars = getRandomAlphanumeric()\n const charKeys = randomChars.map((c, i) => createCharKey(c, i))\n\n // 按行分布切割\n const rows: KeyboardRow[] = []\n let offset = 0\n for (const count of rowDistribution) {\n if (offset >= charKeys.length) break\n rows.push({\n className: 'numpad-row',\n keys: charKeys.slice(offset, offset + count),\n })\n offset += count\n }\n // 剩余字符放最后一行\n if (offset < charKeys.length) {\n rows.push({\n className: 'numpad-row',\n keys: charKeys.slice(offset),\n })\n }\n\n // 功能键行\n rows.push({\n className: 'numpad-row',\n keys: [\n {\n id: 'clear',\n type: 'clear',\n label: '清空',\n className: 'key-clear key-fn',\n },\n {\n id: 'backspace',\n type: 'backspace',\n label: '⌫',\n className: 'key-backspace key-fn',\n },\n {\n id: 'done',\n type: 'done',\n label: '确定',\n className: 'key-done key-fn',\n },\n ],\n })\n\n return {\n type: 'custom',\n name: '随机混合键盘',\n supportShift: false,\n rows,\n data: {\n isRandom: true,\n randomType: 'mixed',\n },\n }\n}\n\n/**\n * 刷新随机键盘布局\n * @param layout - 现有布局\n * @returns 新的随机布局\n */\nexport function refreshRandomLayout(layout: KeyboardConfig): KeyboardConfig {\n const randomType = layout.data?.randomType as string | undefined\n\n switch (randomType) {\n case 'number':\n return createRandomNumberLayout()\n case 'pin':\n return createRandomPinLayout()\n case 'letter':\n return createRandomLetterLayout()\n case 'mixed':\n return createRandomMixedLayout()\n default:\n return layout\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"random.js","names":[],"sources":["../../src/utils/random.ts"],"sourcesContent":["/**\r\n * 随机工具函数\r\n * @module utils/random\r\n */\r\n\r\nimport type { KeyboardConfig, KeyboardRow, KeyDefinition } from '../types'\r\n\r\n/**\r\n * Fisher-Yates 洗牌算法\r\n * @param array - 要洗牌的数组\r\n * @returns 洗牌后的新数组\r\n */\r\nexport function shuffleArray<T>(array: T[]): T[] {\r\n const result = [...array]\r\n for (let i = result.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1))\r\n ;[result[i], result[j]] = [result[j], result[i]]\r\n }\r\n return result\r\n}\r\n\r\n/**\r\n * 生成随机数字数组 (0-9)\r\n */\r\nexport function getRandomNumbers(): string[] {\r\n return shuffleArray(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])\r\n}\r\n\r\n/**\r\n * 生成随机字母数组 (A-Z)\r\n */\r\nexport function getRandomLetters(): string[] {\r\n const letters = Array.from({ length: 26 }, (_, i) =>\r\n String.fromCharCode(65 + i)\r\n )\r\n return shuffleArray(letters)\r\n}\r\n\r\n/**\r\n * 生成随机数字+字母数组\r\n */\r\nexport function getRandomAlphanumeric(): string[] {\r\n const numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']\r\n const letters = Array.from({ length: 26 }, (_, i) =>\r\n String.fromCharCode(65 + i)\r\n )\r\n return shuffleArray([...numbers, ...letters])\r\n}\r\n\r\n/**\r\n * 创建字符按键\r\n */\r\nfunction createCharKey(char: string, index: number): KeyDefinition {\r\n return {\r\n id: `random-${char}-${index}`,\r\n type: 'char',\r\n label: char,\r\n value: char,\r\n className: 'key-random',\r\n }\r\n}\r\n\r\n/**\r\n * 将字符数组分成行\r\n */\r\nfunction chunkArray<T>(array: T[], chunkSize: number): T[][] {\r\n const chunks: T[][] = []\r\n for (let i = 0; i < array.length; i += chunkSize) {\r\n chunks.push(array.slice(i, i + chunkSize))\r\n }\r\n return chunks\r\n}\r\n\r\n/**\r\n * 生成随机数字键盘布局\r\n * @param options - 配置选项\r\n * @param options.keysPerRow - Number of keys per row\r\n * @param options.includeBackspace - Whether to include backspace\r\n * @param options.includeDone - Whether to include done\r\n * @param options.includeClear - Whether to include clear\r\n */\r\nexport function createRandomNumberLayout(options?: {\r\n /** 每行按键数 */\r\n keysPerRow?: number\r\n /** 是否包含退格键 */\r\n includeBackspace?: boolean\r\n /** 是否包含完成键 */\r\n includeDone?: boolean\r\n /** 是否包含清空键 */\r\n includeClear?: boolean\r\n}): KeyboardConfig {\r\n const {\r\n keysPerRow = 3,\r\n includeBackspace = true,\r\n includeDone = true,\r\n includeClear = false,\r\n } = options || {}\r\n\r\n const randomNumbers = getRandomNumbers()\r\n const numberKeys = randomNumbers.map((n, i) => createCharKey(n, i))\r\n const chunks = chunkArray(numberKeys, keysPerRow)\r\n\r\n const rows: KeyboardRow[] = chunks.map(keys => ({\r\n className: 'numpad-row',\r\n keys,\r\n }))\r\n\r\n // 添加功能键行\r\n const fnKeys: KeyDefinition[] = []\r\n\r\n if (includeClear) {\r\n fnKeys.push({\r\n id: 'clear',\r\n type: 'clear',\r\n label: '清空',\r\n className: 'key-clear key-fn',\r\n })\r\n }\r\n\r\n if (includeBackspace) {\r\n fnKeys.push({\r\n id: 'backspace',\r\n type: 'backspace',\r\n label: '⌫',\r\n className: 'key-backspace key-fn',\r\n })\r\n }\r\n\r\n if (includeDone) {\r\n fnKeys.push({\r\n id: 'done',\r\n type: 'done',\r\n label: '确定',\r\n className: 'key-done key-fn',\r\n })\r\n }\r\n\r\n if (fnKeys.length > 0) {\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: fnKeys,\r\n })\r\n }\r\n\r\n return {\r\n type: 'custom',\r\n name: '随机数字键盘',\r\n supportShift: false,\r\n rows,\r\n data: {\r\n isRandom: true,\r\n randomType: 'number',\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * 生成4x3随机数字键盘(密码输入常用)\r\n */\r\nexport function createRandomPinLayout(options?: {\r\n /** 是否包含退格键 */\r\n includeBackspace?: boolean\r\n /** 是否包含完成键 */\r\n includeDone?: boolean\r\n}): KeyboardConfig {\r\n const { includeBackspace = true, includeDone = true } = options || {}\r\n\r\n const randomNumbers = getRandomNumbers()\r\n\r\n // 前9个数字 (3x3)\r\n const rows: KeyboardRow[] = [\r\n {\r\n className: 'numpad-row',\r\n keys: randomNumbers.slice(0, 3).map((n, i) => createCharKey(n, i)),\r\n },\r\n {\r\n className: 'numpad-row',\r\n keys: randomNumbers.slice(3, 6).map((n, i) => createCharKey(n, i + 3)),\r\n },\r\n {\r\n className: 'numpad-row',\r\n keys: randomNumbers.slice(6, 9).map((n, i) => createCharKey(n, i + 6)),\r\n },\r\n ]\r\n\r\n // 最后一行:功能键 + 0 + 功能键\r\n const lastRow: KeyDefinition[] = []\r\n\r\n if (includeBackspace) {\r\n lastRow.push({\r\n id: 'backspace',\r\n type: 'backspace',\r\n label: '⌫',\r\n className: 'key-backspace key-fn',\r\n })\r\n } else {\r\n lastRow.push({\r\n id: 'empty-1',\r\n type: 'char',\r\n label: '',\r\n value: '',\r\n disabled: true,\r\n className: 'key-placeholder',\r\n })\r\n }\r\n\r\n lastRow.push(createCharKey(randomNumbers[9], 9))\r\n\r\n if (includeDone) {\r\n lastRow.push({\r\n id: 'done',\r\n type: 'done',\r\n label: '确定',\r\n className: 'key-done key-fn',\r\n })\r\n } else {\r\n lastRow.push({\r\n id: 'empty-2',\r\n type: 'char',\r\n label: '',\r\n value: '',\r\n disabled: true,\r\n className: 'key-placeholder',\r\n })\r\n }\r\n\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: lastRow,\r\n })\r\n\r\n return {\r\n type: 'custom',\r\n name: '随机PIN键盘',\r\n supportShift: false,\r\n rows,\r\n data: {\r\n isRandom: true,\r\n randomType: 'pin',\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * 生成随机字母键盘布局\r\n * 默认使用 QWERTY 风格行分布 (10/9/7),看起来像真实键盘\r\n */\r\nexport function createRandomLetterLayout(options?: {\r\n /** 是否大写 */\r\n uppercase?: boolean\r\n /** 行分布,默认 [10, 9, 7] 模拟 QWERTY 布局 */\r\n rowDistribution?: number[]\r\n}): KeyboardConfig {\r\n const { uppercase = true, rowDistribution = [10, 9, 7] } = options || {}\r\n\r\n let randomLetters = getRandomLetters()\r\n if (!uppercase) {\r\n randomLetters = randomLetters.map(l => l.toLowerCase())\r\n }\r\n\r\n const letterKeys = randomLetters.map((l, i) => createCharKey(l, i))\r\n\r\n // 按行分布切割\r\n const rows: KeyboardRow[] = []\r\n let offset = 0\r\n for (const count of rowDistribution) {\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: letterKeys.slice(offset, offset + count),\r\n })\r\n offset += count\r\n }\r\n // 剩余字母放最后一行\r\n if (offset < letterKeys.length) {\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: letterKeys.slice(offset),\r\n })\r\n }\r\n\r\n // 功能键行\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: [\r\n {\r\n id: 'clear',\r\n type: 'clear',\r\n label: '清空',\r\n className: 'key-clear key-fn',\r\n },\r\n {\r\n id: 'backspace',\r\n type: 'backspace',\r\n label: '⌫',\r\n className: 'key-backspace key-fn',\r\n },\r\n {\r\n id: 'done',\r\n type: 'done',\r\n label: '确定',\r\n className: 'key-done key-fn',\r\n },\r\n ],\r\n })\r\n\r\n return {\r\n type: 'custom',\r\n name: '随机字母键盘',\r\n supportShift: false,\r\n rows,\r\n data: {\r\n isRandom: true,\r\n randomType: 'letter',\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * 生成混合随机键盘(数字+字母)\r\n * 默认使用 QWERTY 风格行分布 (10/10/9/7),看起来像真实键盘\r\n */\r\nexport function createRandomMixedLayout(options?: {\r\n /** 行分布,默认 [10, 10, 9, 7] */\r\n rowDistribution?: number[]\r\n}): KeyboardConfig {\r\n const { rowDistribution = [10, 10, 9, 7] } = options || {}\r\n\r\n const randomChars = getRandomAlphanumeric()\r\n const charKeys = randomChars.map((c, i) => createCharKey(c, i))\r\n\r\n // 按行分布切割\r\n const rows: KeyboardRow[] = []\r\n let offset = 0\r\n for (const count of rowDistribution) {\r\n if (offset >= charKeys.length) break\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: charKeys.slice(offset, offset + count),\r\n })\r\n offset += count\r\n }\r\n // 剩余字符放最后一行\r\n if (offset < charKeys.length) {\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: charKeys.slice(offset),\r\n })\r\n }\r\n\r\n // 功能键行\r\n rows.push({\r\n className: 'numpad-row',\r\n keys: [\r\n {\r\n id: 'clear',\r\n type: 'clear',\r\n label: '清空',\r\n className: 'key-clear key-fn',\r\n },\r\n {\r\n id: 'backspace',\r\n type: 'backspace',\r\n label: '⌫',\r\n className: 'key-backspace key-fn',\r\n },\r\n {\r\n id: 'done',\r\n type: 'done',\r\n label: '确定',\r\n className: 'key-done key-fn',\r\n },\r\n ],\r\n })\r\n\r\n return {\r\n type: 'custom',\r\n name: '随机混合键盘',\r\n supportShift: false,\r\n rows,\r\n data: {\r\n isRandom: true,\r\n randomType: 'mixed',\r\n },\r\n }\r\n}\r\n\r\n/**\r\n * 刷新随机键盘布局\r\n * @param layout - 现有布局\r\n * @returns 新的随机布局\r\n */\r\nexport function refreshRandomLayout(layout: KeyboardConfig): KeyboardConfig {\r\n const randomType = layout.data?.randomType as string | undefined\r\n\r\n switch (randomType) {\r\n case 'number':\r\n return createRandomNumberLayout()\r\n case 'pin':\r\n return createRandomPinLayout()\r\n case 'letter':\r\n return createRandomLetterLayout()\r\n case 'mixed':\r\n return createRandomMixedLayout()\r\n default:\r\n return layout\r\n }\r\n}\r\n"],"mappings":"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"}
package/lib/icons.cjs 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
  * ***********************************
package/lib/icons.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"icons.cjs","names":[],"sources":["../src/icons.ts"],"sourcesContent":["/**\n * 键盘图标 SVG\n * 使用 Lucide 图标的 SVG 路径\n * @module icons\n */\n\n/**\n * 删除图标 (Delete/Backspace)\n */\nexport 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>`\n\n/**\n * 清空图标 (X Circle)\n */\nexport 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>`\n\n/**\n * 关闭图标 (X)\n */\nexport const ICON_CLOSE = `<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=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`\n\n/**\n * 确认图标 (Check)\n */\nexport const ICON_CHECK = `<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=\"M20 6 9 17l-5-5\"/></svg>`\n\n/**\n * 换行图标 (Corner Down Left)\n */\nexport const ICON_ENTER = `<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\"><polyline points=\"9 10 4 15 9 20\"/><path d=\"M20 4v7a4 4 0 0 1-4 4H4\"/></svg>`\n\n/**\n * Shift 图标 (Arrow Big Up)\n */\nexport const ICON_SHIFT = `<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=\"M9 18v-6H5l7-7 7 7h-4v6H9z\"/></svg>`\n\n/**\n * 空格图标 (Space)\n */\nexport const ICON_SPACE = `<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=\"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1\"/></svg>`\n\n/**\n * 键盘图标 (Keyboard)\n */\nexport const ICON_KEYBOARD = `<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\"><rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\"/><path d=\"M6 8h.001\"/><path d=\"M10 8h.001\"/><path d=\"M14 8h.001\"/><path d=\"M18 8h.001\"/><path d=\"M8 12h.001\"/><path d=\"M12 12h.001\"/><path d=\"M16 12h.001\"/><path d=\"M7 16h10\"/></svg>`\n\n/**\n * 语音图标 (Mic)\n */\nexport const ICON_VOICE = `<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=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/></svg>`\n\n/**\n * 所有图标映射\n */\nexport const ICONS = {\n delete: ICON_DELETE,\n clear: ICON_CLEAR,\n close: ICON_CLOSE,\n check: ICON_CHECK,\n enter: ICON_ENTER,\n shift: ICON_SHIFT,\n space: ICON_SPACE,\n keyboard: ICON_KEYBOARD,\n voice: ICON_VOICE,\n} as const\n\n/** IconName - 图标名称类型 */\nexport type IconName = keyof typeof ICONS\n"],"mappings":";;;;;;;;;;;AASO,MAAM,cAAc;AAKpB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,gBAAgB;AAKtB,MAAM,aAAa;AAKnB,MAAM,QAAQ;CACnB,QAAQ;CACR,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,UAAU;CACV,OAAO;AACT"}
1
+ {"version":3,"file":"icons.cjs","names":[],"sources":["../src/icons.ts"],"sourcesContent":["/**\r\n * 键盘图标 SVG\r\n * 使用 Lucide 图标的 SVG 路径\r\n * @module icons\r\n */\r\n\r\n/**\r\n * 删除图标 (Delete/Backspace)\r\n */\r\nexport 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>`\r\n\r\n/**\r\n * 清空图标 (X Circle)\r\n */\r\nexport 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>`\r\n\r\n/**\r\n * 关闭图标 (X)\r\n */\r\nexport const ICON_CLOSE = `<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=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/></svg>`\r\n\r\n/**\r\n * 确认图标 (Check)\r\n */\r\nexport const ICON_CHECK = `<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=\"M20 6 9 17l-5-5\"/></svg>`\r\n\r\n/**\r\n * 换行图标 (Corner Down Left)\r\n */\r\nexport const ICON_ENTER = `<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\"><polyline points=\"9 10 4 15 9 20\"/><path d=\"M20 4v7a4 4 0 0 1-4 4H4\"/></svg>`\r\n\r\n/**\r\n * Shift 图标 (Arrow Big Up)\r\n */\r\nexport const ICON_SHIFT = `<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=\"M9 18v-6H5l7-7 7 7h-4v6H9z\"/></svg>`\r\n\r\n/**\r\n * 空格图标 (Space)\r\n */\r\nexport const ICON_SPACE = `<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=\"M22 17v1c0 .5-.5 1-1 1H3c-.5 0-1-.5-1-1v-1\"/></svg>`\r\n\r\n/**\r\n * 键盘图标 (Keyboard)\r\n */\r\nexport const ICON_KEYBOARD = `<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\"><rect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\"/><path d=\"M6 8h.001\"/><path d=\"M10 8h.001\"/><path d=\"M14 8h.001\"/><path d=\"M18 8h.001\"/><path d=\"M8 12h.001\"/><path d=\"M12 12h.001\"/><path d=\"M16 12h.001\"/><path d=\"M7 16h10\"/></svg>`\r\n\r\n/**\r\n * 语音图标 (Mic)\r\n */\r\nexport const ICON_VOICE = `<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=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"/><path d=\"M19 10v2a7 7 0 0 1-14 0v-2\"/><line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\"/></svg>`\r\n\r\n/**\r\n * 所有图标映射\r\n */\r\nexport const ICONS = {\r\n delete: ICON_DELETE,\r\n clear: ICON_CLEAR,\r\n close: ICON_CLOSE,\r\n check: ICON_CHECK,\r\n enter: ICON_ENTER,\r\n shift: ICON_SHIFT,\r\n space: ICON_SPACE,\r\n keyboard: ICON_KEYBOARD,\r\n voice: ICON_VOICE,\r\n} as const\r\n\r\n/** IconName - 图标名称类型 */\r\nexport type IconName = keyof typeof ICONS\r\n"],"mappings":";;;;;;;;;;;AASO,MAAM,cAAc;AAKpB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,aAAa;AAKnB,MAAM,gBAAgB;AAKtB,MAAM,aAAa;AAKnB,MAAM,QAAQ;CACnB,QAAQ;CACR,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,UAAU;CACV,OAAO;AACT"}
@@ -55,4 +55,3 @@ export declare const ICONS: {
55
55
  };
56
56
  /** IconName - 图标名称类型 */
57
57
  export type IconName = keyof typeof ICONS;
58
- //# sourceMappingURL=icons.d.ts.map
package/lib/index.cjs CHANGED
@@ -2,37 +2,37 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-08-17 11:33:29 *
5
+ * Build time: 2024-10-12 17:29:15 *
6
6
  * Build mode: production *
7
7
  * Minified: No *
8
8
  * ***********************************
9
9
  */
10
10
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
11
11
  const require_icons = require("./icons.cjs");
12
- const require_qwerty = require("./layouts/qwerty.cjs");
13
- const require_number = require("./layouts/number.cjs");
14
- const require_symbol = require("./layouts/symbol.cjs");
15
- const require_license_plate = require("./layouts/license-plate.cjs");
16
- const require_idcard = require("./layouts/idcard.cjs");
17
- const require_phone = require("./layouts/phone.cjs");
18
12
  const require_amount = require("./layouts/amount.cjs");
19
13
  const require_bankcard = require("./layouts/bankcard.cjs");
20
- const require_verifycode = require("./layouts/verifycode.cjs");
21
14
  const require_email = require("./layouts/email.cjs");
22
- const require_url = require("./layouts/url.cjs");
23
- const require_search = require("./layouts/search.cjs");
15
+ const require_idcard = require("./layouts/idcard.cjs");
16
+ const require_license_plate = require("./layouts/license-plate.cjs");
17
+ const require_number = require("./layouts/number.cjs");
18
+ const require_phone = require("./layouts/phone.cjs");
24
19
  const require_punct = require("./layouts/punct.cjs");
20
+ const require_qwerty = require("./layouts/qwerty.cjs");
21
+ const require_search = require("./layouts/search.cjs");
22
+ const require_symbol = require("./layouts/symbol.cjs");
23
+ const require_url = require("./layouts/url.cjs");
24
+ const require_verifycode = require("./layouts/verifycode.cjs");
25
25
  const require_index = require("./layouts/index.cjs");
26
- const require_event_emitter = require("./utils/event-emitter.cjs");
27
26
  const require_device = require("./utils/device.cjs");
28
27
  const require_dom = require("./utils/dom.cjs");
28
+ const require_event_emitter = require("./utils/event-emitter.cjs");
29
29
  const require_keyboard_manager = require("./managers/keyboard-manager.cjs");
30
- const require_position_manager = require("./managers/position-manager.cjs");
31
30
  const require_presets = require("./themes/presets.cjs");
32
31
  const require_index$1 = require("./themes/index.cjs");
32
+ const require_position_manager = require("./managers/position-manager.cjs");
33
33
  const require_keyboard_renderer = require("./managers/keyboard-renderer.cjs");
34
- const require_random = require("./utils/random.cjs");
35
34
  const require_formatters = require("./utils/formatters.cjs");
35
+ const require_random = require("./utils/random.cjs");
36
36
  exports.EMAIL_SUFFIXES = require_email.EMAIL_SUFFIXES;
37
37
  exports.EventEmitter = require_event_emitter.EventEmitter;
38
38
  exports.ICONS = require_icons.ICONS;
@@ -123,6 +123,7 @@ exports.registerFormatter = require_formatters.registerFormatter;
123
123
  exports.registerLayout = require_index.registerLayout;
124
124
  exports.registerLayouts = require_index.registerLayouts;
125
125
  exports.registerTheme = require_index$1.registerTheme;
126
+ exports.sanitizeSvgIcon = require_dom.sanitizeSvgIcon;
126
127
  exports.scrollIntoViewIfNeeded = require_dom.scrollIntoViewIfNeeded;
127
128
  exports.searchLayout = require_search.searchLayout;
128
129
  exports.setCurrentTheme = require_index$1.setCurrentTheme;
@@ -3,10 +3,9 @@
3
3
  * LDesign 虚拟键盘 - 框架无关的核心模块
4
4
  * @packageDocumentation
5
5
  */
6
- export * from './managers';
6
+ export * from './icons';
7
7
  export * from './layouts';
8
+ export * from './managers';
9
+ export * from './themes';
8
10
  export * from './types';
9
11
  export * from './utils';
10
- export * from './themes';
11
- export * from './icons';
12
- //# sourceMappingURL=index.d.ts.map
@@ -2,7 +2,7 @@
2
2
  * ***********************************
3
3
  * @composy/keyboard-core v0.0.1 *
4
4
  * Built with rollup *
5
- * Build time: 2024-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
  * ***********************************