@dodlhuat/basix 1.0.0

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 (430) hide show
  1. package/README.md +482 -0
  2. package/css/accordion.css +109 -0
  3. package/css/accordion.css.map +1 -0
  4. package/css/accordion.scss +78 -0
  5. package/css/alert.css +57 -0
  6. package/css/alert.css.map +1 -0
  7. package/css/alert.scss +86 -0
  8. package/css/button.css +69 -0
  9. package/css/button.css.map +1 -0
  10. package/css/button.scss +102 -0
  11. package/css/card.css +144 -0
  12. package/css/card.css.map +1 -0
  13. package/css/card.scss +66 -0
  14. package/css/carousel.css +118 -0
  15. package/css/carousel.css.map +1 -0
  16. package/css/carousel.scss +87 -0
  17. package/css/chart.css +159 -0
  18. package/css/chart.css.map +1 -0
  19. package/css/chart.scss +159 -0
  20. package/css/chat-bubbles.css +97 -0
  21. package/css/chat-bubbles.css.map +1 -0
  22. package/css/chat-bubbles.scss +68 -0
  23. package/css/checkbox.css +77 -0
  24. package/css/checkbox.css.map +1 -0
  25. package/css/checkbox.scss +55 -0
  26. package/css/chips.css +72 -0
  27. package/css/chips.css.map +1 -0
  28. package/css/chips.scss +52 -0
  29. package/css/code-viewer.css +97 -0
  30. package/css/code-viewer.css.map +1 -0
  31. package/css/code-viewer.scss +98 -0
  32. package/css/colors.css +63 -0
  33. package/css/colors.css.map +1 -0
  34. package/css/colors.scss +33 -0
  35. package/css/datepicker.css +264 -0
  36. package/css/datepicker.css.map +1 -0
  37. package/css/datepicker.scss +317 -0
  38. package/css/defaults.css +118 -0
  39. package/css/defaults.css.map +1 -0
  40. package/css/defaults.scss +91 -0
  41. package/css/dropdown.css +146 -0
  42. package/css/dropdown.css.map +1 -0
  43. package/css/dropdown.scss +137 -0
  44. package/css/editor.css +413 -0
  45. package/css/editor.scss +458 -0
  46. package/css/file-uploader.css +194 -0
  47. package/css/file-uploader.css.map +1 -0
  48. package/css/file-uploader.scss +195 -0
  49. package/css/flyout-menu.css +345 -0
  50. package/css/flyout-menu.css.map +1 -0
  51. package/css/flyout-menu.scss +355 -0
  52. package/css/form-builder.css +9 -0
  53. package/css/form-builder.css.map +1 -0
  54. package/css/form-builder.scss +11 -0
  55. package/css/form.css +130 -0
  56. package/css/form.css.map +1 -0
  57. package/css/form.scss +115 -0
  58. package/css/gallery.css +91 -0
  59. package/css/gallery.css.map +1 -0
  60. package/css/gallery.scss +63 -0
  61. package/css/grid.css +44 -0
  62. package/css/grid.css.map +1 -0
  63. package/css/grid.scss +41 -0
  64. package/css/guitar-chords.css +251 -0
  65. package/css/icons.css +327 -0
  66. package/css/icons.css.map +1 -0
  67. package/css/icons.scss +331 -0
  68. package/css/modal.css +97 -0
  69. package/css/modal.css.map +1 -0
  70. package/css/modal.scss +72 -0
  71. package/css/parameters.css +1 -0
  72. package/css/parameters.css.map +1 -0
  73. package/css/parameters.scss +4 -0
  74. package/css/placeholder.css +50 -0
  75. package/css/placeholder.css.map +1 -0
  76. package/css/placeholder.scss +28 -0
  77. package/css/progress.css +51 -0
  78. package/css/progress.css.map +1 -0
  79. package/css/progress.scss +32 -0
  80. package/css/properties.css +31 -0
  81. package/css/properties.css.map +1 -0
  82. package/css/properties.scss +31 -0
  83. package/css/push-menu.css +145 -0
  84. package/css/push-menu.css.map +1 -0
  85. package/css/push-menu.scss +127 -0
  86. package/css/radiobutton.css +91 -0
  87. package/css/radiobutton.css.map +1 -0
  88. package/css/radiobutton.scss +64 -0
  89. package/css/reset.css +46 -0
  90. package/css/reset.css.map +1 -0
  91. package/css/reset.scss +40 -0
  92. package/css/scrollbar.css +91 -0
  93. package/css/scrollbar.css.map +1 -0
  94. package/css/scrollbar.scss +69 -0
  95. package/css/spinner.css +118 -0
  96. package/css/spinner.css.map +1 -0
  97. package/css/spinner.scss +91 -0
  98. package/css/style.css +3735 -0
  99. package/css/style.css.map +1 -0
  100. package/css/style.min.css +1 -0
  101. package/css/style.scss +38 -0
  102. package/css/switch.css +66 -0
  103. package/css/switch.css.map +1 -0
  104. package/css/switch.scss +42 -0
  105. package/css/table.css +201 -0
  106. package/css/table.css.map +1 -0
  107. package/css/table.scss +178 -0
  108. package/css/tabs.css +135 -0
  109. package/css/tabs.css.map +1 -0
  110. package/css/tabs.scss +118 -0
  111. package/css/timeline.css +69 -0
  112. package/css/timeline.css.map +1 -0
  113. package/css/timeline.scss +69 -0
  114. package/css/timepicker.scss +72 -0
  115. package/css/toast.css +98 -0
  116. package/css/toast.css.map +1 -0
  117. package/css/toast.scss +81 -0
  118. package/css/tooltip.css +151 -0
  119. package/css/tooltip.css.map +1 -0
  120. package/css/tooltip.scss +122 -0
  121. package/css/tree.css +199 -0
  122. package/css/tree.css.map +1 -0
  123. package/css/tree.scss +192 -0
  124. package/css/typography.css +137 -0
  125. package/css/typography.css.map +1 -0
  126. package/css/typography.scss +100 -0
  127. package/css/virtual-dropdown.css +149 -0
  128. package/css/virtual-dropdown.css.map +1 -0
  129. package/css/virtual-dropdown.scss +142 -0
  130. package/fonts/MaterialSymbolsOutlined.woff2 +0 -0
  131. package/fonts/Outfit-VariableFont_wght.woff +0 -0
  132. package/fonts/Outfit-VariableFont_wght.woff2 +0 -0
  133. package/fonts/material-icons.woff2 +0 -0
  134. package/icons/activity-outline.svg +1 -0
  135. package/icons/alert-circle-outline.svg +1 -0
  136. package/icons/alert-triangle-outline.svg +1 -0
  137. package/icons/archive-outline.svg +1 -0
  138. package/icons/arrow-back-outline.svg +1 -0
  139. package/icons/arrow-circle-down-outline.svg +1 -0
  140. package/icons/arrow-circle-left-outline.svg +1 -0
  141. package/icons/arrow-circle-right-outline.svg +1 -0
  142. package/icons/arrow-circle-up-outline.svg +1 -0
  143. package/icons/arrow-down-outline.svg +1 -0
  144. package/icons/arrow-downward-outline.svg +1 -0
  145. package/icons/arrow-forward-outline.svg +1 -0
  146. package/icons/arrow-ios-back-outline.svg +1 -0
  147. package/icons/arrow-ios-downward-outline.svg +1 -0
  148. package/icons/arrow-ios-forward-outline.svg +1 -0
  149. package/icons/arrow-ios-upward-outline.svg +1 -0
  150. package/icons/arrow-left-outline.svg +1 -0
  151. package/icons/arrow-right-outline.svg +1 -0
  152. package/icons/arrow-up-outline.svg +1 -0
  153. package/icons/arrow-upward-outline.svg +1 -0
  154. package/icons/arrowhead-down-outline.svg +1 -0
  155. package/icons/arrowhead-left-outline.svg +1 -0
  156. package/icons/arrowhead-right-outline.svg +1 -0
  157. package/icons/arrowhead-up-outline.svg +1 -0
  158. package/icons/at-outline.svg +1 -0
  159. package/icons/attach-2-outline.svg +1 -0
  160. package/icons/attach-outline.svg +1 -0
  161. package/icons/award-outline.svg +1 -0
  162. package/icons/backspace-outline.svg +1 -0
  163. package/icons/bar-chart-2-outline.svg +1 -0
  164. package/icons/bar-chart-outline.svg +1 -0
  165. package/icons/battery-outline.svg +1 -0
  166. package/icons/behance-outline.svg +1 -0
  167. package/icons/bell-off-outline.svg +1 -0
  168. package/icons/bell-outline.svg +1 -0
  169. package/icons/bluetooth-outline.svg +1 -0
  170. package/icons/book-open-outline.svg +1 -0
  171. package/icons/book-outline.svg +1 -0
  172. package/icons/bookmark-outline.svg +1 -0
  173. package/icons/briefcase-outline.svg +1 -0
  174. package/icons/browser-outline.svg +1 -0
  175. package/icons/brush-outline.svg +1 -0
  176. package/icons/bulb-outline.svg +1 -0
  177. package/icons/calendar-outline.svg +1 -0
  178. package/icons/camera-outline.svg +1 -0
  179. package/icons/car-outline.svg +1 -0
  180. package/icons/cast-outline.svg +1 -0
  181. package/icons/charging-outline.svg +1 -0
  182. package/icons/checkmark-circle-2-outline.svg +1 -0
  183. package/icons/checkmark-circle-outline.svg +1 -0
  184. package/icons/checkmark-outline.svg +1 -0
  185. package/icons/checkmark-square-2-outline.svg +1 -0
  186. package/icons/checkmark-square-outline.svg +1 -0
  187. package/icons/chevron-down-outline.svg +1 -0
  188. package/icons/chevron-left-outline.svg +1 -0
  189. package/icons/chevron-right-outline.svg +1 -0
  190. package/icons/chevron-up-outline.svg +1 -0
  191. package/icons/clipboard-outline.svg +1 -0
  192. package/icons/clock-outline.svg +1 -0
  193. package/icons/close-circle-outline.svg +1 -0
  194. package/icons/close-outline.svg +1 -0
  195. package/icons/close-square-outline.svg +1 -0
  196. package/icons/cloud-download-outline.svg +1 -0
  197. package/icons/cloud-upload-outline.svg +1 -0
  198. package/icons/code-download-outline.svg +1 -0
  199. package/icons/code-outline.svg +1 -0
  200. package/icons/collapse-outline.svg +1 -0
  201. package/icons/color-palette-outline.svg +1 -0
  202. package/icons/color-picker-outline.svg +1 -0
  203. package/icons/compass-outline.svg +1 -0
  204. package/icons/copy-outline.svg +1 -0
  205. package/icons/corner-down-left-outline.svg +1 -0
  206. package/icons/corner-down-right-outline.svg +1 -0
  207. package/icons/corner-left-down-outline.svg +1 -0
  208. package/icons/corner-left-up-outline.svg +1 -0
  209. package/icons/corner-right-down-outline.svg +1 -0
  210. package/icons/corner-right-up-outline.svg +1 -0
  211. package/icons/corner-up-left-outline.svg +1 -0
  212. package/icons/corner-up-right-outline.svg +1 -0
  213. package/icons/credit-card-outline.svg +1 -0
  214. package/icons/crop-outline.svg +1 -0
  215. package/icons/cube-outline.svg +1 -0
  216. package/icons/diagonal-arrow-left-down-outline.svg +1 -0
  217. package/icons/diagonal-arrow-left-up-outline.svg +1 -0
  218. package/icons/diagonal-arrow-right-down-outline.svg +1 -0
  219. package/icons/diagonal-arrow-right-up-outline.svg +1 -0
  220. package/icons/done-all-outline.svg +1 -0
  221. package/icons/download-outline.svg +1 -0
  222. package/icons/droplet-off-outline.svg +1 -0
  223. package/icons/droplet-outline.svg +1 -0
  224. package/icons/edit-2-outline.svg +1 -0
  225. package/icons/edit-outline.svg +1 -0
  226. package/icons/email-outline.svg +1 -0
  227. package/icons/expand-outline.svg +1 -0
  228. package/icons/external-link-outline.svg +1 -0
  229. package/icons/eye-off-2-outline.svg +1 -0
  230. package/icons/eye-off-outline.svg +1 -0
  231. package/icons/eye-outline.svg +1 -0
  232. package/icons/facebook-outline.svg +1 -0
  233. package/icons/file-add-outline.svg +1 -0
  234. package/icons/file-outline.svg +1 -0
  235. package/icons/file-remove-outline.svg +1 -0
  236. package/icons/file-text-outline.svg +1 -0
  237. package/icons/film-outline.svg +1 -0
  238. package/icons/flag-outline.svg +1 -0
  239. package/icons/flash-off-outline.svg +1 -0
  240. package/icons/flash-outline.svg +1 -0
  241. package/icons/flip-2-outline.svg +1 -0
  242. package/icons/flip-outline.svg +1 -0
  243. package/icons/folder-add-outline.svg +1 -0
  244. package/icons/folder-outline.svg +1 -0
  245. package/icons/folder-remove-outline.svg +1 -0
  246. package/icons/funnel-outline.svg +1 -0
  247. package/icons/gift-outline.svg +1 -0
  248. package/icons/github-outline.svg +1 -0
  249. package/icons/globe-2-outline.svg +1 -0
  250. package/icons/globe-outline.svg +1 -0
  251. package/icons/google-outline.svg +1 -0
  252. package/icons/grid-outline.svg +1 -0
  253. package/icons/hard-drive-outline.svg +1 -0
  254. package/icons/hash-outline.svg +1 -0
  255. package/icons/headphones-outline.svg +1 -0
  256. package/icons/heart-outline.svg +1 -0
  257. package/icons/home-outline.svg +1 -0
  258. package/icons/image-outline.svg +1 -0
  259. package/icons/inbox-outline.svg +1 -0
  260. package/icons/info-outline.svg +1 -0
  261. package/icons/keypad-outline.svg +1 -0
  262. package/icons/layers-outline.svg +1 -0
  263. package/icons/layout-outline.svg +1 -0
  264. package/icons/link-2-outline.svg +1 -0
  265. package/icons/link-outline.svg +1 -0
  266. package/icons/linkedin-outline.svg +1 -0
  267. package/icons/list-outline.svg +1 -0
  268. package/icons/loader-outline.svg +1 -0
  269. package/icons/lock-outline.svg +1 -0
  270. package/icons/log-in-outline.svg +1 -0
  271. package/icons/log-out-outline.svg +1 -0
  272. package/icons/map-outline.svg +1 -0
  273. package/icons/maximize-outline.svg +1 -0
  274. package/icons/menu-2-outline.svg +1 -0
  275. package/icons/menu-arrow-outline.svg +1 -0
  276. package/icons/menu-outline.svg +1 -0
  277. package/icons/message-circle-outline.svg +1 -0
  278. package/icons/message-square-outline.svg +1 -0
  279. package/icons/mic-off-outline.svg +1 -0
  280. package/icons/mic-outline.svg +1 -0
  281. package/icons/minimize-outline.svg +1 -0
  282. package/icons/minus-circle-outline.svg +1 -0
  283. package/icons/minus-outline.svg +1 -0
  284. package/icons/minus-square-outline.svg +1 -0
  285. package/icons/monitor-outline.svg +1 -0
  286. package/icons/moon-outline.svg +1 -0
  287. package/icons/more-horizontal-outline.svg +1 -0
  288. package/icons/more-vertical-outline.svg +1 -0
  289. package/icons/move-outline.svg +1 -0
  290. package/icons/music-outline.svg +1 -0
  291. package/icons/navigation-2-outline.svg +1 -0
  292. package/icons/navigation-outline.svg +1 -0
  293. package/icons/npm-outline.svg +1 -0
  294. package/icons/options-2-outline.svg +1 -0
  295. package/icons/options-outline.svg +1 -0
  296. package/icons/pantone-outline.svg +1 -0
  297. package/icons/paper-plane-outline.svg +1 -0
  298. package/icons/pause-circle-outline.svg +1 -0
  299. package/icons/people-outline.svg +1 -0
  300. package/icons/percent-outline.svg +1 -0
  301. package/icons/person-add-outline.svg +1 -0
  302. package/icons/person-delete-outline.svg +1 -0
  303. package/icons/person-done-outline.svg +1 -0
  304. package/icons/person-outline.svg +1 -0
  305. package/icons/person-remove-outline.svg +1 -0
  306. package/icons/phone-call-outline.svg +1 -0
  307. package/icons/phone-missed-outline.svg +1 -0
  308. package/icons/phone-off-outline.svg +1 -0
  309. package/icons/phone-outline.svg +1 -0
  310. package/icons/pie-chart-outline.svg +1 -0
  311. package/icons/pin-outline.svg +1 -0
  312. package/icons/play-circle-outline.svg +1 -0
  313. package/icons/plus-circle-outline.svg +1 -0
  314. package/icons/plus-outline.svg +1 -0
  315. package/icons/plus-square-outline.svg +1 -0
  316. package/icons/power-outline.svg +1 -0
  317. package/icons/pricetags-outline.svg +1 -0
  318. package/icons/printer-outline.svg +1 -0
  319. package/icons/question-mark-circle-outline.svg +1 -0
  320. package/icons/question-mark-outline.svg +1 -0
  321. package/icons/radio-button-off-outline.svg +1 -0
  322. package/icons/radio-button-on-outline.svg +1 -0
  323. package/icons/radio-outline.svg +1 -0
  324. package/icons/recording-outline.svg +1 -0
  325. package/icons/refresh-outline.svg +1 -0
  326. package/icons/repeat-outline.svg +1 -0
  327. package/icons/rewind-left-outline.svg +1 -0
  328. package/icons/rewind-right-outline.svg +1 -0
  329. package/icons/save-outline.svg +1 -0
  330. package/icons/scissors-outline.svg +1 -0
  331. package/icons/search-outline.svg +1 -0
  332. package/icons/settings-2-outline.svg +1 -0
  333. package/icons/settings-outline.svg +1 -0
  334. package/icons/shake-outline.svg +1 -0
  335. package/icons/share-outline.svg +1 -0
  336. package/icons/shield-off-outline.svg +1 -0
  337. package/icons/shield-outline.svg +1 -0
  338. package/icons/shopping-bag-outline.svg +1 -0
  339. package/icons/shopping-cart-outline.svg +1 -0
  340. package/icons/shuffle-2-outline.svg +1 -0
  341. package/icons/shuffle-outline.svg +1 -0
  342. package/icons/skip-back-outline.svg +1 -0
  343. package/icons/skip-forward-outline.svg +1 -0
  344. package/icons/slash-outline.svg +1 -0
  345. package/icons/smartphone-outline.svg +1 -0
  346. package/icons/smiling-face-outline.svg +1 -0
  347. package/icons/speaker-outline.svg +1 -0
  348. package/icons/square-outline.svg +1 -0
  349. package/icons/star-outline.svg +1 -0
  350. package/icons/stop-circle-outline.svg +1 -0
  351. package/icons/sun-outline.svg +1 -0
  352. package/icons/swap-outline.svg +1 -0
  353. package/icons/sync-outline.svg +1 -0
  354. package/icons/text-outline.svg +1 -0
  355. package/icons/thermometer-minus-outline.svg +1 -0
  356. package/icons/thermometer-outline.svg +1 -0
  357. package/icons/thermometer-plus-outline.svg +1 -0
  358. package/icons/toggle-left-outline.svg +1 -0
  359. package/icons/toggle-right-outline.svg +1 -0
  360. package/icons/trash-2-outline.svg +1 -0
  361. package/icons/trash-outline.svg +1 -0
  362. package/icons/trending-down-outline.svg +1 -0
  363. package/icons/trending-up-outline.svg +1 -0
  364. package/icons/tv-outline.svg +1 -0
  365. package/icons/twitter-outline.svg +1 -0
  366. package/icons/umbrella-outline.svg +1 -0
  367. package/icons/undo-outline.svg +1 -0
  368. package/icons/unlock-outline.svg +1 -0
  369. package/icons/upload-outline.svg +1 -0
  370. package/icons/video-off-outline.svg +1 -0
  371. package/icons/video-outline.svg +1 -0
  372. package/icons/volume-down-outline.svg +1 -0
  373. package/icons/volume-mute-outline.svg +1 -0
  374. package/icons/volume-off-outline.svg +1 -0
  375. package/icons/volume-up-outline.svg +1 -0
  376. package/icons/wifi-off-outline.svg +1 -0
  377. package/icons/wifi-outline.svg +1 -0
  378. package/js/carousel.js +133 -0
  379. package/js/carousel.ts +173 -0
  380. package/js/chart.js +257 -0
  381. package/js/code-viewer.js +148 -0
  382. package/js/code-viewer.ts +188 -0
  383. package/js/datepicker.js +497 -0
  384. package/js/datepicker.ts +619 -0
  385. package/js/dropdown.js +122 -0
  386. package/js/dropdown.ts +180 -0
  387. package/js/editor.js +421 -0
  388. package/js/editor.ts +426 -0
  389. package/js/file-uploader.js +268 -0
  390. package/js/file-uploader.ts +350 -0
  391. package/js/flyout-menu.js +195 -0
  392. package/js/flyout-menu.ts +250 -0
  393. package/js/form-builder.js +107 -0
  394. package/js/gallery.js +177 -0
  395. package/js/gallery.ts +231 -0
  396. package/js/guitar-chords.js +268 -0
  397. package/js/index.js +720 -0
  398. package/js/index.ts +874 -0
  399. package/js/lazy-loader.js +121 -0
  400. package/js/modal.js +113 -0
  401. package/js/modal.ts +167 -0
  402. package/js/push-menu.js +101 -0
  403. package/js/push-menu.ts +130 -0
  404. package/js/request.js +51 -0
  405. package/js/scroll.js +27 -0
  406. package/js/scroll.ts +47 -0
  407. package/js/scrollbar.js +219 -0
  408. package/js/scrollbar.ts +308 -0
  409. package/js/select.js +158 -0
  410. package/js/select.ts +217 -0
  411. package/js/table.js +359 -0
  412. package/js/table.ts +453 -0
  413. package/js/tabs.js +225 -0
  414. package/js/tabs.ts +280 -0
  415. package/js/theme.js +194 -0
  416. package/js/theme.ts +225 -0
  417. package/js/timepicker.js +98 -0
  418. package/js/timepicker.ts +131 -0
  419. package/js/toast.js +93 -0
  420. package/js/toast.ts +138 -0
  421. package/js/tooltip.js +193 -0
  422. package/js/tooltip.ts +252 -0
  423. package/js/tree.js +162 -0
  424. package/js/tree.ts +218 -0
  425. package/js/tsconfig.json +18 -0
  426. package/js/utils.js +69 -0
  427. package/js/utils.ts +84 -0
  428. package/js/virtual-dropdown.js +277 -0
  429. package/js/virtual-dropdown.ts +366 -0
  430. package/package.json +38 -0
package/js/editor.js ADDED
@@ -0,0 +1,421 @@
1
+ class Editor {
2
+ constructor() {
3
+ this.undoStack = [];
4
+ this.redoStack = [];
5
+
6
+ const editable = document.getElementById('editable');
7
+ const code = document.getElementById('code');
8
+ const preview = document.getElementById('preview');
9
+ const sidePanel = document.getElementById('sidePanel');
10
+ const wordCount = document.getElementById('wordCount');
11
+
12
+ if (!editable || !code || !preview || !sidePanel) {
13
+ throw new Error('Editor: Required elements not found');
14
+ }
15
+
16
+ this.editable = editable;
17
+ this.code = code;
18
+ this.preview = preview;
19
+ this.sidePanel = sidePanel;
20
+ this.wordCount = wordCount;
21
+
22
+ this.bindToolbar();
23
+ this.bindActions();
24
+ this.bindKeyboard();
25
+ this.bindEditable();
26
+ this.bindTabs();
27
+ this.syncViews();
28
+ this.saveState();
29
+
30
+ // Start with side panel hidden
31
+ this.sidePanel.classList.add('hidden');
32
+ }
33
+
34
+ bindToolbar() {
35
+ document.querySelectorAll('[data-cmd]').forEach(btn => {
36
+ btn.addEventListener('click', () => {
37
+ const cmd = btn.dataset.cmd;
38
+ const val = btn.dataset.value ?? null;
39
+ this.exec(cmd, val);
40
+ this.editable.focus();
41
+ });
42
+ });
43
+ }
44
+
45
+ bindActions() {
46
+ document.getElementById('linkBtn')?.addEventListener('click', () => {
47
+ const url = prompt('Enter URL:', 'https://');
48
+ if (url) this.exec('createLink', url);
49
+ });
50
+
51
+ const imageFile = document.getElementById('imageFile');
52
+ document.getElementById('imageBtn')?.addEventListener('click', () => imageFile.click());
53
+ imageFile?.addEventListener('change', () => {
54
+ const file = imageFile.files?.[0];
55
+ if (!file) return;
56
+ const reader = new FileReader();
57
+ reader.onload = () => {
58
+ if (typeof reader.result === 'string') {
59
+ this.insertImage(reader.result);
60
+ }
61
+ };
62
+ reader.readAsDataURL(file);
63
+ imageFile.value = '';
64
+ });
65
+
66
+ document.getElementById('cleanBtn')?.addEventListener('click', () => {
67
+ const sel = window.getSelection();
68
+ if (!sel || sel.rangeCount === 0) return;
69
+ const range = sel.getRangeAt(0);
70
+ const text = range.toString();
71
+ range.deleteContents();
72
+ range.insertNode(document.createTextNode(text));
73
+ this.onContentChange();
74
+ });
75
+
76
+ document.getElementById('undoBtn')?.addEventListener('click', () => this.undo());
77
+ document.getElementById('redoBtn')?.addEventListener('click', () => this.redo());
78
+
79
+ document.getElementById('toggleCodeBtn')?.addEventListener('click', () => {
80
+ this.sidePanel.classList.toggle('hidden');
81
+ this.syncViews();
82
+ });
83
+
84
+ // Code action buttons — matched by position within .code-actions
85
+ const codeActions = document.querySelectorAll('.code-actions button');
86
+ codeActions[0]?.addEventListener('click', () => {
87
+ this.editable.innerHTML = this.sanitizeHTML(this.code.value);
88
+ this.onContentChange();
89
+ });
90
+ codeActions[1]?.addEventListener('click', () => {
91
+ this.code.value = this.sanitizeHTML(this.code.value);
92
+ this.editable.innerHTML = this.code.value;
93
+ this.onContentChange();
94
+ });
95
+ codeActions[2]?.addEventListener('click', () => {
96
+ this.code.value = this.code.value
97
+ .replace(/\n/g, '')
98
+ .replace(/>\s+</g, '><')
99
+ .trim();
100
+ });
101
+
102
+ const saveBtn = document.getElementById('saveBtn');
103
+ saveBtn?.addEventListener('click', () => this.downloadHTML());
104
+
105
+ document.getElementById('clearBtn')?.addEventListener('click', () => {
106
+ if (confirm('Clear all content?')) {
107
+ this.editable.innerHTML = '';
108
+ this.onContentChange();
109
+ }
110
+ });
111
+ }
112
+
113
+ bindKeyboard() {
114
+ const saveBtn = document.getElementById('saveBtn');
115
+
116
+ window.addEventListener('keydown', (e) => {
117
+ const mod = e.ctrlKey || e.metaKey;
118
+ if (!mod) return;
119
+
120
+ const key = e.key.toLowerCase();
121
+
122
+ if (key === 'b') { e.preventDefault(); this.exec('bold'); }
123
+ else if (key === 'i') { e.preventDefault(); this.exec('italic'); }
124
+ else if (key === 'u') { e.preventDefault(); this.exec('underline'); }
125
+ else if (key === 'k') {
126
+ e.preventDefault();
127
+ const url = prompt('Enter URL:', 'https://');
128
+ if (url) this.exec('createLink', url);
129
+ }
130
+ else if (key === 's') { e.preventDefault(); saveBtn?.click(); }
131
+ else if (key === 'z' && !e.shiftKey) { e.preventDefault(); this.undo(); }
132
+ else if (key === 'y' || (key === 'z' && e.shiftKey)) { e.preventDefault(); this.redo(); }
133
+ });
134
+ }
135
+
136
+ bindEditable() {
137
+ this.editable.addEventListener('input', () => this.onContentChange());
138
+
139
+ this.editable.addEventListener('paste', (e) => {
140
+ e.preventDefault();
141
+ const text = e.clipboardData?.getData('text/plain') ?? '';
142
+ this.insertText(text);
143
+ });
144
+
145
+ this.editable.addEventListener('keyup', () => this.refreshActiveState());
146
+ this.editable.addEventListener('mouseup', () => this.refreshActiveState());
147
+ }
148
+
149
+ bindTabs() {
150
+ document.querySelectorAll('.side-tab[data-tab]').forEach(tab => {
151
+ tab.addEventListener('click', () => {
152
+ const targetId = tab.dataset.tab;
153
+
154
+ document.querySelectorAll('.side-tab').forEach(t => t.classList.remove('active'));
155
+ document.querySelectorAll('.side-panel').forEach(p => p.classList.remove('active'));
156
+
157
+ tab.classList.add('active');
158
+ document.getElementById(targetId)?.classList.add('active');
159
+ });
160
+ });
161
+ }
162
+
163
+ onContentChange() {
164
+ this.saveState();
165
+ this.syncViews();
166
+ }
167
+
168
+ syncViews() {
169
+ this.code.value = this.editable.innerHTML.trim();
170
+ this.preview.innerHTML = this.editable.innerHTML;
171
+ this.updateWordCount();
172
+ }
173
+
174
+ updateWordCount() {
175
+ if (!this.wordCount) return;
176
+ const text = this.editable.innerText || '';
177
+ const words = text.trim().split(/\s+/).filter(w => w.length > 0);
178
+ const count = words.length;
179
+ this.wordCount.textContent = `${count} word${count !== 1 ? 's' : ''}`;
180
+ }
181
+
182
+ saveState() {
183
+ this.undoStack.push(this.editable.innerHTML);
184
+ if (this.undoStack.length > 100) this.undoStack.shift();
185
+ this.redoStack = [];
186
+ }
187
+
188
+ undo() {
189
+ if (this.undoStack.length <= 1) return;
190
+ this.redoStack.push(this.undoStack.pop());
191
+ this.editable.innerHTML = this.undoStack[this.undoStack.length - 1];
192
+ this.syncViews();
193
+ }
194
+
195
+ redo() {
196
+ if (this.redoStack.length === 0) return;
197
+ const state = this.redoStack.pop();
198
+ this.undoStack.push(state);
199
+ this.editable.innerHTML = state;
200
+ this.syncViews();
201
+ }
202
+
203
+ exec(command, value = null) {
204
+ switch (command) {
205
+ case 'bold': this.toggleInlineStyle('strong'); break;
206
+ case 'italic': this.toggleInlineStyle('em'); break;
207
+ case 'underline': this.toggleInlineStyle('u'); break;
208
+ case 'strikeThrough': this.toggleInlineStyle('s'); break;
209
+ case 'createLink': if (value) this.createLink(value); break;
210
+ case 'formatBlock': if (value) this.formatBlock(value); break;
211
+ case 'insertUnorderedList': this.insertList('ul'); break;
212
+ case 'insertOrderedList': this.insertList('ol'); break;
213
+ }
214
+ }
215
+
216
+ insertText(text) {
217
+ const sel = window.getSelection();
218
+ if (!sel || sel.rangeCount === 0) return;
219
+
220
+ const range = sel.getRangeAt(0);
221
+ range.deleteContents();
222
+ range.insertNode(document.createTextNode(text));
223
+ range.collapse(false);
224
+ sel.removeAllRanges();
225
+ sel.addRange(range);
226
+
227
+ this.onContentChange();
228
+ }
229
+
230
+ insertImage(dataUrl) {
231
+ const sel = window.getSelection();
232
+ if (!sel || sel.rangeCount === 0) return;
233
+
234
+ const range = sel.getRangeAt(0);
235
+ const img = document.createElement('img');
236
+ img.src = dataUrl;
237
+ img.style.maxWidth = '100%';
238
+ range.deleteContents();
239
+ range.insertNode(img);
240
+
241
+ range.setStartAfter(img);
242
+ range.collapse(true);
243
+ sel.removeAllRanges();
244
+ sel.addRange(range);
245
+
246
+ this.onContentChange();
247
+ }
248
+
249
+ toggleInlineStyle(tagName) {
250
+ const sel = window.getSelection();
251
+ if (!sel || sel.rangeCount === 0) return;
252
+
253
+ const range = sel.getRangeAt(0);
254
+ const container = range.commonAncestorContainer;
255
+ let current = container.nodeType === Node.TEXT_NODE
256
+ ? container.parentElement
257
+ : container;
258
+
259
+ let wrapper = null;
260
+ while (current && current !== this.editable) {
261
+ if (current.tagName === tagName.toUpperCase()) {
262
+ wrapper = current;
263
+ break;
264
+ }
265
+ current = current.parentElement;
266
+ }
267
+
268
+ if (wrapper) {
269
+ const parent = wrapper.parentNode;
270
+ while (wrapper.firstChild) {
271
+ parent?.insertBefore(wrapper.firstChild, wrapper);
272
+ }
273
+ parent?.removeChild(wrapper);
274
+ } else {
275
+ const contents = range.extractContents();
276
+ const el = document.createElement(tagName);
277
+ el.appendChild(contents);
278
+ range.insertNode(el);
279
+ range.selectNodeContents(el);
280
+ sel.removeAllRanges();
281
+ sel.addRange(range);
282
+ }
283
+
284
+ this.onContentChange();
285
+ }
286
+
287
+ createLink(url) {
288
+ const sel = window.getSelection();
289
+ if (!sel || sel.rangeCount === 0) return;
290
+
291
+ const range = sel.getRangeAt(0);
292
+ const contents = range.extractContents();
293
+ const link = document.createElement('a');
294
+ link.href = url;
295
+ link.appendChild(contents);
296
+ range.insertNode(link);
297
+
298
+ this.onContentChange();
299
+ }
300
+
301
+ formatBlock(tag) {
302
+ const sel = window.getSelection();
303
+ if (!sel || sel.rangeCount === 0) return;
304
+
305
+ const range = sel.getRangeAt(0);
306
+ const container = range.commonAncestorContainer;
307
+ let blockElement = container.nodeType === Node.TEXT_NODE
308
+ ? container.parentElement
309
+ : container;
310
+
311
+ while (blockElement && blockElement !== this.editable && blockElement.parentElement !== this.editable) {
312
+ blockElement = blockElement.parentElement;
313
+ }
314
+
315
+ if (blockElement && blockElement !== this.editable) {
316
+ const newBlock = document.createElement(tag);
317
+ newBlock.innerHTML = blockElement.innerHTML;
318
+ blockElement.parentNode?.replaceChild(newBlock, blockElement);
319
+ this.onContentChange();
320
+ }
321
+ }
322
+
323
+ insertList(listTag) {
324
+ const sel = window.getSelection();
325
+ if (!sel || sel.rangeCount === 0) return;
326
+
327
+ const range = sel.getRangeAt(0);
328
+ const text = range.toString();
329
+
330
+ const list = document.createElement(listTag);
331
+ const lines = text ? text.split('\n').filter(l => l.trim()) : [''];
332
+
333
+ for (const line of lines) {
334
+ const li = document.createElement('li');
335
+ li.textContent = line.trim() || '\u200B';
336
+ list.appendChild(li);
337
+ }
338
+
339
+ range.deleteContents();
340
+ range.insertNode(list);
341
+
342
+ const lastLi = list.lastElementChild;
343
+ if (lastLi) {
344
+ range.setStart(lastLi, lastLi.childNodes.length);
345
+ range.collapse(true);
346
+ sel.removeAllRanges();
347
+ sel.addRange(range);
348
+ }
349
+
350
+ this.onContentChange();
351
+ }
352
+
353
+ sanitizeHTML(html) {
354
+ const parser = new DOMParser();
355
+ const doc = parser.parseFromString(html, 'text/html');
356
+
357
+ doc.querySelectorAll('script, style, iframe, object, embed').forEach(el => el.remove());
358
+
359
+ doc.querySelectorAll('*').forEach(el => {
360
+ for (const attr of Array.from(el.attributes)) {
361
+ if (attr.name.startsWith('on') || attr.value.trim().toLowerCase().startsWith('javascript:')) {
362
+ el.removeAttribute(attr.name);
363
+ }
364
+ }
365
+ });
366
+
367
+ return doc.body.innerHTML;
368
+ }
369
+
370
+ downloadHTML() {
371
+ const content = this.sanitizeHTML(this.editable.innerHTML);
372
+ const html = `<!doctype html>
373
+ <html lang="en">
374
+ <head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Export</title></head>
375
+ <body>
376
+ ${content}
377
+ </body>
378
+ </html>`;
379
+ const blob = new Blob([html], { type: 'text/html' });
380
+ const a = document.createElement('a');
381
+ a.href = URL.createObjectURL(blob);
382
+ a.download = 'document.html';
383
+ a.click();
384
+ URL.revokeObjectURL(a.href);
385
+ }
386
+
387
+ refreshActiveState() {
388
+ const sel = window.getSelection();
389
+ if (!sel || sel.rangeCount === 0) return;
390
+
391
+ const range = sel.getRangeAt(0);
392
+ const container = range.commonAncestorContainer;
393
+ const element = container.nodeType === Node.TEXT_NODE
394
+ ? container.parentElement
395
+ : container;
396
+
397
+ document.querySelectorAll('[data-cmd]').forEach(btn => {
398
+ const cmd = btn.dataset.cmd;
399
+ let active = false;
400
+
401
+ let current = element;
402
+ while (current && current !== this.editable) {
403
+ const tag = current.tagName?.toLowerCase();
404
+ if (
405
+ (cmd === 'bold' && (tag === 'strong' || tag === 'b')) ||
406
+ (cmd === 'italic' && (tag === 'em' || tag === 'i')) ||
407
+ (cmd === 'underline' && tag === 'u') ||
408
+ (cmd === 'strikeThrough' && tag === 's')
409
+ ) {
410
+ active = true;
411
+ break;
412
+ }
413
+ current = current.parentElement;
414
+ }
415
+
416
+ btn.classList.toggle('active', active);
417
+ });
418
+ }
419
+ }
420
+
421
+ export { Editor };