@widgetic/editor 3.11.4 → 4.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 (497) hide show
  1. package/README.md +245 -0
  2. package/dist/assets/fonts.json +331 -0
  3. package/dist/assets/icons/ic-browser/audio.svg +7 -0
  4. package/dist/assets/icons/ic-browser/document.svg +11 -0
  5. package/dist/assets/icons/ic-browser/error.svg +5 -0
  6. package/dist/assets/icons/ic-browser/folder.svg +28 -0
  7. package/dist/assets/icons/ic-browser/player-pause.svg +1 -0
  8. package/dist/assets/icons/ic-browser/player-play.svg +1 -0
  9. package/dist/assets/icons/ic-browser/trash.svg +1 -0
  10. package/dist/assets/icons/ic-fixed-position/background-deselected.svg +3 -0
  11. package/dist/assets/icons/ic-fixed-position/background-selected.svg +3 -0
  12. package/dist/assets/icons/ic-fixed-position/bottom-left-background-deselected.svg +3 -0
  13. package/dist/assets/icons/ic-fixed-position/bottom-left-background-selected.svg +3 -0
  14. package/dist/assets/icons/ic-fixed-position/bottom-left-icon.svg +4 -0
  15. package/dist/assets/icons/ic-fixed-position/bottom-right-background-deselected.svg +3 -0
  16. package/dist/assets/icons/ic-fixed-position/bottom-right-background-selected.svg +3 -0
  17. package/dist/assets/icons/ic-fixed-position/bottom-right-icon.svg +4 -0
  18. package/dist/assets/icons/ic-fixed-position/center-bottom-icon.svg +5 -0
  19. package/dist/assets/icons/ic-fixed-position/center-center-icon.svg +3 -0
  20. package/dist/assets/icons/ic-fixed-position/center-left-icon.svg +5 -0
  21. package/dist/assets/icons/ic-fixed-position/center-right-icon.svg +5 -0
  22. package/dist/assets/icons/ic-fixed-position/center-top-icon.svg +5 -0
  23. package/dist/assets/icons/ic-fixed-position/top-left-background-deselected.svg +3 -0
  24. package/dist/assets/icons/ic-fixed-position/top-left-background-selected.svg +3 -0
  25. package/dist/assets/icons/ic-fixed-position/top-left-icon.svg +4 -0
  26. package/dist/assets/icons/ic-fixed-position/top-right-background-deselected.svg +3 -0
  27. package/dist/assets/icons/ic-fixed-position/top-right-background-selected.svg +3 -0
  28. package/dist/assets/icons/ic-fixed-position/top-right-icon.svg +4 -0
  29. package/dist/assets/icons/trash.svg +1 -0
  30. package/dist/components/MockWidget.svelte +108 -0
  31. package/dist/components/MockWidget.svelte.d.ts +22 -0
  32. package/dist/components/WidgetPreview.svelte +852 -0
  33. package/dist/components/WidgetPreview.svelte.d.ts +100 -0
  34. package/dist/components/color-select/ColorSelect.svelte +258 -0
  35. package/dist/components/color-select/ColorSelect.svelte.d.ts +42 -0
  36. package/dist/components/color-select/color.d.ts +25 -0
  37. package/dist/components/color-select/color.js +21 -0
  38. package/dist/components/color-select/constants.d.ts +5 -0
  39. package/dist/components/color-select/constants.js +5 -0
  40. package/dist/components/color-select/render.d.ts +5 -0
  41. package/dist/components/color-select/render.js +77 -0
  42. package/dist/components/color-select/shaders.d.ts +3 -0
  43. package/dist/components/color-select/shaders.js +8 -0
  44. package/dist/components/editors/EmbedEditor.svelte +700 -0
  45. package/dist/components/editors/EmbedEditor.svelte.d.ts +50 -0
  46. package/dist/components/editors/PropsEditor.svelte +4009 -0
  47. package/dist/components/editors/PropsEditor.svelte.d.ts +62 -0
  48. package/dist/components/editors/props-editor.css +73 -0
  49. package/dist/components/ic/AngleInputController.svelte +276 -0
  50. package/dist/components/ic/AngleInputController.svelte.d.ts +26 -0
  51. package/dist/components/ic/BrowserInputController.svelte +731 -0
  52. package/dist/components/ic/BrowserInputController.svelte.d.ts +39 -0
  53. package/dist/components/ic/CheckboxInputController.svelte +203 -0
  54. package/dist/components/ic/CheckboxInputController.svelte.d.ts +41 -0
  55. package/dist/components/ic/ColorPickerInputController.svelte +1584 -0
  56. package/dist/components/ic/ColorPickerInputController.svelte.d.ts +50 -0
  57. package/dist/components/ic/DateFieldInputController.svelte +453 -0
  58. package/dist/components/ic/DateFieldInputController.svelte.d.ts +35 -0
  59. package/dist/components/ic/DatePickerInputController.svelte +364 -0
  60. package/dist/components/ic/DatePickerInputController.svelte.d.ts +28 -0
  61. package/dist/components/ic/DropdownInputController.svelte +226 -0
  62. package/dist/components/ic/DropdownInputController.svelte.d.ts +43 -0
  63. package/dist/components/ic/FixedPositionInputController.svelte +353 -0
  64. package/dist/components/ic/FixedPositionInputController.svelte.d.ts +23 -0
  65. package/dist/components/ic/FontInputController.svelte +2098 -0
  66. package/dist/components/ic/FontInputController.svelte.d.ts +47 -0
  67. package/dist/components/ic/InputControllerHeader.svelte +147 -0
  68. package/dist/components/ic/InputControllerHeader.svelte.d.ts +23 -0
  69. package/dist/components/ic/NumberControls.svelte +299 -0
  70. package/dist/components/ic/NumberControls.svelte.d.ts +30 -0
  71. package/dist/components/ic/NumberInputController.svelte +397 -0
  72. package/dist/components/ic/NumberInputController.svelte.d.ts +30 -0
  73. package/dist/components/ic/PositionInputController.svelte +252 -0
  74. package/dist/components/ic/PositionInputController.svelte.d.ts +37 -0
  75. package/dist/components/ic/RadioGroupInputController.svelte +162 -0
  76. package/dist/components/ic/RadioGroupInputController.svelte.d.ts +38 -0
  77. package/dist/components/ic/RangeCalendarInputController.svelte +262 -0
  78. package/dist/components/ic/RangeCalendarInputController.svelte.d.ts +32 -0
  79. package/dist/components/ic/RoundedCornersInputController.svelte +469 -0
  80. package/dist/components/ic/RoundedCornersInputController.svelte.d.ts +37 -0
  81. package/dist/components/ic/SliderInputController.svelte +411 -0
  82. package/dist/components/ic/SliderInputController.svelte.d.ts +46 -0
  83. package/dist/components/ic/SliderRangeInputController.svelte +155 -0
  84. package/dist/components/ic/SliderRangeInputController.svelte.d.ts +33 -0
  85. package/dist/components/ic/SwitchInputController.svelte +107 -0
  86. package/dist/components/ic/SwitchInputController.svelte.d.ts +24 -0
  87. package/dist/components/ic/TagsInputController.svelte +125 -0
  88. package/dist/components/ic/TagsInputController.svelte.d.ts +27 -0
  89. package/dist/components/ic/TextAreaInputController.svelte +1932 -0
  90. package/dist/components/ic/TextAreaInputController.svelte.d.ts +14 -0
  91. package/dist/components/ic/TextAreaPreviewInputController.svelte +478 -0
  92. package/dist/components/ic/TextAreaPreviewInputController.svelte.d.ts +28 -0
  93. package/dist/components/ic/TextInputController.svelte +70 -0
  94. package/dist/components/ic/TextInputController.svelte.d.ts +30 -0
  95. package/dist/components/ic/TimeInputController.svelte +434 -0
  96. package/dist/components/ic/TimeInputController.svelte.d.ts +25 -0
  97. package/dist/components/ic/VectorInputController.svelte +1826 -0
  98. package/dist/components/ic/VectorInputController.svelte.d.ts +47 -0
  99. package/dist/components/ic/index.d.ts +39 -0
  100. package/dist/components/ic/index.js +24 -0
  101. package/dist/components/ic/shared/CategoryTabs.svelte +40 -0
  102. package/dist/components/ic/shared/CategoryTabs.svelte.d.ts +25 -0
  103. package/dist/components/ic/shared/ItemGrid.svelte +181 -0
  104. package/dist/components/ic/shared/ItemGrid.svelte.d.ts +35 -0
  105. package/dist/components/ic/shared/RecentItemsSection.svelte +260 -0
  106. package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +35 -0
  107. package/dist/components/ic/shared/index.d.ts +8 -0
  108. package/dist/components/ic/shared/index.js +9 -0
  109. package/dist/components/icons/addnew.svelte +21 -0
  110. package/dist/components/icons/addnew.svelte.d.ts +20 -0
  111. package/dist/components/icons/drag.svelte +29 -0
  112. package/dist/components/icons/drag.svelte.d.ts +20 -0
  113. package/dist/components/icons/drag.svg +1 -0
  114. package/dist/components/icons/locked.svelte +4 -0
  115. package/dist/components/icons/locked.svelte.d.ts +26 -0
  116. package/dist/components/icons/locked.svg +4 -0
  117. package/dist/components/icons/ok.svelte +20 -0
  118. package/dist/components/icons/ok.svelte.d.ts +20 -0
  119. package/dist/components/icons/ok.svg +11 -0
  120. package/dist/components/icons/unlocked.svelte +4 -0
  121. package/dist/components/icons/unlocked.svelte.d.ts +26 -0
  122. package/dist/components/icons/unlocked.svg +4 -0
  123. package/dist/components/icons/x.svelte +14 -0
  124. package/dist/components/icons/x.svelte.d.ts +20 -0
  125. package/dist/components/icons/x.svg +5 -0
  126. package/dist/components/ui/arrows/OpacitySliderArrows.svelte +43 -0
  127. package/dist/components/ui/arrows/OpacitySliderArrows.svelte.d.ts +23 -0
  128. package/dist/components/ui/checkerboard-background/CheckerboardBackground.svelte +15 -0
  129. package/dist/components/ui/checkerboard-background/CheckerboardBackground.svelte.d.ts +20 -0
  130. package/dist/config.d.ts +11 -0
  131. package/dist/config.js +31 -0
  132. package/dist/index.d.ts +5 -0
  133. package/dist/index.js +9 -0
  134. package/dist/stores/userSession.d.ts +7 -0
  135. package/dist/stores/userSession.js +28 -0
  136. package/dist/utils/FontManager.d.ts +278 -0
  137. package/dist/utils/FontManager.js +1302 -0
  138. package/dist/utils/MediaManager.d.ts +35 -0
  139. package/dist/utils/MediaManager.js +59 -0
  140. package/dist/utils/VectorManager.d.ts +190 -0
  141. package/dist/utils/VectorManager.js +754 -0
  142. package/dist/utils/colorAutocomplete.d.ts +10 -0
  143. package/dist/utils/colorAutocomplete.js +28 -0
  144. package/dist/utils/colorNames.d.ts +3 -0
  145. package/dist/utils/colorNames.js +152 -0
  146. package/dist/utils/colorTransparency.d.ts +52 -0
  147. package/dist/utils/colorTransparency.js +114 -0
  148. package/dist/utils/fontUtils.d.ts +34 -0
  149. package/dist/utils/fontUtils.js +132 -0
  150. package/dist/utils/passiveEvents.js +20 -0
  151. package/dist/utils/recentColors.d.ts +10 -0
  152. package/dist/utils/recentColors.js +63 -0
  153. package/dist/utils/skipFonts.d.ts +3 -0
  154. package/dist/utils/skipFonts.js +49 -0
  155. package/dist/utils/vectorUtils.d.ts +34 -0
  156. package/dist/utils/vectorUtils.js +71 -0
  157. package/dist/utils.d.ts +11 -0
  158. package/dist/utils.js +38 -0
  159. package/package.json +134 -74
  160. package/.babelrc +0 -10
  161. package/.bowerrc +0 -4
  162. package/.nvmrc +0 -1
  163. package/Gruntfile.coffee +0 -69
  164. package/bower.json +0 -28
  165. package/css/browser/entries/activateable.styl +0 -29
  166. package/css/browser/entries/audio.styl +0 -39
  167. package/css/browser/entries/folder.styl +0 -79
  168. package/css/browser/entries/image.styl +0 -26
  169. package/css/browser/entries/index.styl +0 -66
  170. package/css/browser/entries/rss.styl +0 -11
  171. package/css/browser/entries/video.styl +0 -11
  172. package/css/browser/index.styl +0 -86
  173. package/css/browser/intent/index.styl +0 -61
  174. package/css/browser/nav/index.styl +0 -73
  175. package/css/browser/social-browser.styl +0 -23
  176. package/css/controls/clock.styl +0 -110
  177. package/css/controls/config.styl +0 -6
  178. package/css/controls/control-audio-details.styl +0 -63
  179. package/css/controls/control-audio.styl +0 -28
  180. package/css/controls/control-browser.styl +0 -48
  181. package/css/controls/control-color.styl +0 -423
  182. package/css/controls/control-date_time.styl +0 -191
  183. package/css/controls/control-dropdown.styl +0 -79
  184. package/css/controls/control-font.styl +0 -147
  185. package/css/controls/control-image-details.styl +0 -35
  186. package/css/controls/control-image.styl +0 -43
  187. package/css/controls/control-order-picker.styl +0 -29
  188. package/css/controls/control-position.styl +0 -390
  189. package/css/controls/control-range.styl +0 -68
  190. package/css/controls/control-scale.styl +0 -30
  191. package/css/controls/control-slider.styl +0 -38
  192. package/css/controls/control-spinner.styl +0 -39
  193. package/css/controls/control-textarea.styl +0 -52
  194. package/css/controls/control-toggle.styl +0 -34
  195. package/css/controls/control-url.styl +0 -32
  196. package/css/controls/control-video-details.styl +0 -85
  197. package/css/controls/control-video.styl +0 -43
  198. package/css/controls/general.styl +0 -98
  199. package/css/controls/index.styl +0 -25
  200. package/css/editor/colors.styl +0 -7
  201. package/css/editor/content/directinput.styl +0 -47
  202. package/css/editor/content/index.styl +0 -111
  203. package/css/editor/content/message.styl +0 -53
  204. package/css/editor/content/tabs.styl +0 -75
  205. package/css/editor/copied-from-old-style.styl +0 -197
  206. package/css/editor/details/bulk-editor.styl +0 -104
  207. package/css/editor/details/index.styl +0 -21
  208. package/css/editor/details/item.styl +0 -153
  209. package/css/editor/embed.styl +0 -106
  210. package/css/editor/footer.styl +0 -180
  211. package/css/editor/full-editor.styl +0 -16
  212. package/css/editor/index.styl +0 -124
  213. package/css/editor/login-popup.styl +0 -13
  214. package/css/editor/preview.styl +0 -257
  215. package/css/editor/skin/editor/index.styl +0 -54
  216. package/css/editor/skin/editor/tabs.styl +0 -111
  217. package/css/editor/skin/index.styl +0 -138
  218. package/css/editor/skin/popup.styl +0 -152
  219. package/css/editor/skin/presets/dropdown.styl +0 -61
  220. package/css/editor/skin/presets/index.styl +0 -86
  221. package/css/editor/skin/presets/list.styl +0 -244
  222. package/css/editor/tooltips.styl +0 -85
  223. package/css/fonts.styl +0 -47
  224. package/css/icons/BlogvioEditor.dev.svg +0 -112
  225. package/css/icons.styl +0 -16
  226. package/css/img/arrow-right-rounded-v-small.png +0 -0
  227. package/css/img/browser-folder-separator.png +0 -0
  228. package/css/img/calendar-input-icons.png +0 -0
  229. package/css/img/check.png +0 -0
  230. package/css/img/close.png +0 -0
  231. package/css/img/composition/edit-icon.png +0 -0
  232. package/css/img/content/audio-icon.png +0 -0
  233. package/css/img/content/folder-icon.png +0 -0
  234. package/css/img/content/messages/error-background.png +0 -0
  235. package/css/img/content/messages/error-icon.png +0 -0
  236. package/css/img/content/messages/notification-background.png +0 -0
  237. package/css/img/content/messages/notification-icon.png +0 -0
  238. package/css/img/content/no-files-audio.png +0 -0
  239. package/css/img/content/no-files-image.png +0 -0
  240. package/css/img/content/no-files-video.png +0 -0
  241. package/css/img/content/no-files.png +0 -0
  242. package/css/img/content/rss-icon.png +0 -0
  243. package/css/img/content/selected.png +0 -0
  244. package/css/img/content/video-icon.png +0 -0
  245. package/css/img/content/with-control-text.png +0 -0
  246. package/css/img/control-dropdown-arrow.png +0 -0
  247. package/css/img/control-image-valid.png +0 -0
  248. package/css/img/control-position-icon.png +0 -0
  249. package/css/img/control-scale-lock-icon.png +0 -0
  250. package/css/img/control-slider-arrows-hover.png +0 -0
  251. package/css/img/control-slider-arrows.png +0 -0
  252. package/css/img/control-spinner-buttons-hover.png +0 -0
  253. package/css/img/control-spinner-buttons.png +0 -0
  254. package/css/img/control-textarea-bold-active.png +0 -0
  255. package/css/img/control-textarea-bold.png +0 -0
  256. package/css/img/control-textarea-italic-active.png +0 -0
  257. package/css/img/control-textarea-italic.png +0 -0
  258. package/css/img/control-textarea-underline-active.png +0 -0
  259. package/css/img/control-textarea-underline.png +0 -0
  260. package/css/img/custom-checkbox-sprite.png +0 -0
  261. package/css/img/details/item-collapse-icon.png +0 -0
  262. package/css/img/details/item-expand-icon.png +0 -0
  263. package/css/img/details/item-sort-icon.png +0 -0
  264. package/css/img/done-arrow.png +0 -0
  265. package/css/img/fb-logo-icon-w.png +0 -0
  266. package/css/img/grid-icons.png +0 -0
  267. package/css/img/icon-reference.png +0 -0
  268. package/css/img/indicator.png +0 -0
  269. package/css/img/loader.gif +0 -0
  270. package/css/img/loaders/audio-white.png +0 -0
  271. package/css/img/loaders/audio.png +0 -0
  272. package/css/img/loaders/photo-white.png +0 -0
  273. package/css/img/loaders/photo.png +0 -0
  274. package/css/img/loaders/util-white.png +0 -0
  275. package/css/img/loaders/util.png +0 -0
  276. package/css/img/loaders/video-white.png +0 -0
  277. package/css/img/loaders/video.png +0 -0
  278. package/css/img/maximize.png +0 -0
  279. package/css/img/minimize.png +0 -0
  280. package/css/img/next-btn.png +0 -0
  281. package/css/img/search-icon-sprite.png +0 -0
  282. package/css/img/services/blogvio.png +0 -0
  283. package/css/img/services/drive.png +0 -0
  284. package/css/img/services/dropbox-login.png +0 -0
  285. package/css/img/services/dropbox.png +0 -0
  286. package/css/img/services/facebook-login.png +0 -0
  287. package/css/img/services/facebook.png +0 -0
  288. package/css/img/services/flickr-login.png +0 -0
  289. package/css/img/services/flickr.png +0 -0
  290. package/css/img/services/instagram-login.png +0 -0
  291. package/css/img/services/instagram.png +0 -0
  292. package/css/img/services/text.png +0 -0
  293. package/css/img/services/uploads.png +0 -0
  294. package/css/img/services/uploads.svg +0 -689
  295. package/css/img/services/webaddress-bw.png +0 -0
  296. package/css/img/services/webaddress.png +0 -0
  297. package/css/img/services/webadress.png +0 -0
  298. package/css/img/services/websearch.png +0 -0
  299. package/css/img/services/wix.png +0 -0
  300. package/css/img/skin/item-active-indicator.png +0 -0
  301. package/css/img/skin/item-hover-icon.png +0 -0
  302. package/css/img/skin/overwrite.png +0 -0
  303. package/css/img/skin/popup-name-icon.png +0 -0
  304. package/css/img/skin/popup-save.png +0 -0
  305. package/css/img/skin/preset.png +0 -0
  306. package/css/img/skin/presets-arrow-purple.png +0 -0
  307. package/css/img/tw-icon-w.png +0 -0
  308. package/css/img/warning.png +0 -0
  309. package/css/img/white-rounded-check.png +0 -0
  310. package/css/index.styl +0 -9
  311. package/css/mixins.styl +0 -62
  312. package/css/scrollable.styl +0 -101
  313. package/karma.conf.js +0 -73
  314. package/lib/control.js +0 -1
  315. package/lib/controls.js +0 -1
  316. package/lib/core.editor.js +0 -4
  317. package/lib/core.js +0 -1
  318. package/lib/core.vendor.js +0 -3
  319. package/lib/dev/control.js +0 -845
  320. package/lib/dev/controls.js +0 -7774
  321. package/lib/dev/core.editor.js +0 -6439
  322. package/lib/dev/core.js +0 -1486
  323. package/lib/dev/core.vendor.js +0 -10708
  324. package/lib/dev/editor.css +0 -166
  325. package/lib/dev/editor.js +0 -29710
  326. package/lib/dev/editor.vendor.js +0 -15119
  327. package/lib/dev/jquery.nanoscroller.js.map +0 -10
  328. package/lib/editor.css +0 -167
  329. package/lib/editor.js +0 -49
  330. package/lib/editor.vendor.js +0 -11
  331. package/src/api/index.coffee +0 -23
  332. package/src/api/mixin.coffee +0 -89
  333. package/src/browser/controller/entry/audio.coffee +0 -38
  334. package/src/browser/controller/entry/button.coffee +0 -16
  335. package/src/browser/controller/entry/file.coffee +0 -35
  336. package/src/browser/controller/entry/folder.coffee +0 -14
  337. package/src/browser/controller/entry/image.coffee +0 -24
  338. package/src/browser/controller/entry/index.coffee +0 -18
  339. package/src/browser/controller/entry/rss.coffee +0 -5
  340. package/src/browser/controller/entry/video.coffee +0 -4
  341. package/src/browser/controller/index.coffee +0 -289
  342. package/src/browser/controller/instagram.coffee +0 -129
  343. package/src/browser/controller/navbar.coffee +0 -48
  344. package/src/browser/controller/social.coffee +0 -213
  345. package/src/browser/model/entry.coffee +0 -18
  346. package/src/browser/model/index.coffee +0 -34
  347. package/src/core/controller/font-loader.coffee +0 -65
  348. package/src/core/controller/lazyload.coffee +0 -14
  349. package/src/core/controller/overlay.coffee +0 -146
  350. package/src/core/controller/popup.coffee +0 -36
  351. package/src/core/controller/scrollable.coffee +0 -68
  352. package/src/core/controls/audio.coffee +0 -56
  353. package/src/core/controls/browser.coffee +0 -271
  354. package/src/core/controls/color.coffee +0 -121
  355. package/src/core/controls/date-time/calendar/index.coffee +0 -99
  356. package/src/core/controls/date-time/calendar/months.coffee +0 -52
  357. package/src/core/controls/date-time/calendar/years.coffee +0 -58
  358. package/src/core/controls/date-time/clock.coffee +0 -62
  359. package/src/core/controls/date-time/helper.coffee +0 -31
  360. package/src/core/controls/date-time/index.coffee +0 -62
  361. package/src/core/controls/date-time/popup.coffee +0 -56
  362. package/src/core/controls/details/audio.coffee +0 -33
  363. package/src/core/controls/details/image.coffee +0 -19
  364. package/src/core/controls/details/video.coffee +0 -14
  365. package/src/core/controls/dropdown.coffee +0 -100
  366. package/src/core/controls/font/font-item.coffee +0 -22
  367. package/src/core/controls/font/font-list.coffee +0 -108
  368. package/src/core/controls/font/fonts-popup.coffee +0 -91
  369. package/src/core/controls/font/index.coffee +0 -66
  370. package/src/core/controls/image.coffee +0 -37
  371. package/src/core/controls/index.coffee +0 -88
  372. package/src/core/controls/orderPicker.coffee +0 -93
  373. package/src/core/controls/position/grid-panel.coffee +0 -75
  374. package/src/core/controls/position/index.coffee +0 -98
  375. package/src/core/controls/position/panel.coffee +0 -68
  376. package/src/core/controls/position/pixel-panel.coffee +0 -54
  377. package/src/core/controls/position/popup.coffee +0 -63
  378. package/src/core/controls/position/reference-panel.coffee +0 -26
  379. package/src/core/controls/range.coffee +0 -191
  380. package/src/core/controls/rss.coffee +0 -16
  381. package/src/core/controls/scale.coffee +0 -78
  382. package/src/core/controls/slider.coffee +0 -122
  383. package/src/core/controls/stepper.coffee +0 -97
  384. package/src/core/controls/text.coffee +0 -54
  385. package/src/core/controls/textarea.coffee +0 -78
  386. package/src/core/controls/toggle.coffee +0 -29
  387. package/src/core/controls/url.coffee +0 -88
  388. package/src/core/controls/video.coffee +0 -9
  389. package/src/core/embed/api.js +0 -178
  390. package/src/core/embed/controller.js +0 -331
  391. package/src/core/models/composition.coffee +0 -178
  392. package/src/core/models/content.coffee +0 -79
  393. package/src/core/models/skin.coffee +0 -91
  394. package/src/core/models/user.coffee +0 -6
  395. package/src/core/models/widget.coffee +0 -88
  396. package/src/core/router/index.coffee +0 -44
  397. package/src/core/services/container/index.js +0 -18
  398. package/src/core/services/container/instance.js +0 -3
  399. package/src/core/services/content.js +0 -80
  400. package/src/core/services/instagram.coffee +0 -47
  401. package/src/core/services/mixin/events.js +0 -17
  402. package/src/core/services/service.js +0 -1
  403. package/src/core/services/sounds.js +0 -81
  404. package/src/core/services/uploads.js +0 -299
  405. package/src/core/utils/popup.coffee +0 -67
  406. package/src/core/utils/should-show.coffee +0 -12
  407. package/src/core/views/audio-details.pug +0 -9
  408. package/src/core/views/audio.pug +0 -7
  409. package/src/core/views/browser.pug +0 -4
  410. package/src/core/views/color.pug +0 -8
  411. package/src/core/views/date-time/calendar.pug +0 -3
  412. package/src/core/views/date-time/clock.pug +0 -12
  413. package/src/core/views/date-time/grid.pug +0 -6
  414. package/src/core/views/date-time/input.pug +0 -4
  415. package/src/core/views/date-time/table.pug +0 -6
  416. package/src/core/views/dropdown.pug +0 -9
  417. package/src/core/views/font/font-item.pug +0 -3
  418. package/src/core/views/font/fonts-list.pug +0 -7
  419. package/src/core/views/font/fonts-popup.pug +0 -5
  420. package/src/core/views/font/index.pug +0 -7
  421. package/src/core/views/image-details.pug +0 -6
  422. package/src/core/views/image.pug +0 -5
  423. package/src/core/views/input-layout.pug +0 -15
  424. package/src/core/views/order-items.pug +0 -7
  425. package/src/core/views/order-picker.pug +0 -7
  426. package/src/core/views/pixelpanel.pug +0 -69
  427. package/src/core/views/position.pug +0 -10
  428. package/src/core/views/range.pug +0 -9
  429. package/src/core/views/scale.pug +0 -7
  430. package/src/core/views/slider.pug +0 -6
  431. package/src/core/views/stepper.pug +0 -6
  432. package/src/core/views/text.pug +0 -4
  433. package/src/core/views/textarea.pug +0 -8
  434. package/src/core/views/toggle.pug +0 -5
  435. package/src/core/views/video-details.pug +0 -7
  436. package/src/core/views/video.pug +0 -4
  437. package/src/core/widget/index.coffee +0 -66
  438. package/src/editor/content/browser/dropzone.html +0 -268
  439. package/src/editor/content/browser/file.html +0 -124
  440. package/src/editor/content/browser/scroll-pane.html +0 -32
  441. package/src/editor/content/browser/service.html +0 -53
  442. package/src/editor/content/browser/upload.coffee +0 -44
  443. package/src/editor/content/browser/upload.html +0 -439
  444. package/src/editor/content/index.coffee +0 -294
  445. package/src/editor/content/input/field.coffee +0 -45
  446. package/src/editor/content/input/index.coffee +0 -115
  447. package/src/editor/content/service/base/photoitem.coffee +0 -49
  448. package/src/editor/content/service/cache.coffee +0 -20
  449. package/src/editor/content/service/default.coffee +0 -59
  450. package/src/editor/content/service/dropbox.coffee +0 -71
  451. package/src/editor/content/service/facebook.coffee +0 -143
  452. package/src/editor/content/service/flickr.coffee +0 -85
  453. package/src/editor/content/service/google-drive-sdk.coffee +0 -18
  454. package/src/editor/content/service/google-drive.coffee +0 -144
  455. package/src/editor/content/service/instagram-social.coffee +0 -84
  456. package/src/editor/content/service/instagram.coffee +0 -168
  457. package/src/editor/content/service/share-popup.coffee +0 -15
  458. package/src/editor/content/service/soundcloud.coffee +0 -163
  459. package/src/editor/content/tab.coffee +0 -27
  460. package/src/editor/details/bulk-editor/attributes-panel.coffee +0 -11
  461. package/src/editor/details/bulk-editor/index.coffee +0 -86
  462. package/src/editor/details/index.coffee +0 -156
  463. package/src/editor/details/item/attributes.coffee +0 -45
  464. package/src/editor/details/item/index.coffee +0 -82
  465. package/src/editor/editor.coffee +0 -274
  466. package/src/editor/footer/breadcrumb.coffee +0 -17
  467. package/src/editor/footer/index.coffee +0 -36
  468. package/src/editor/index.coffee +0 -79
  469. package/src/editor/listeners/embed-listener.coffee +0 -143
  470. package/src/editor/login-popup.coffee +0 -29
  471. package/src/editor/notification-controller.coffee +0 -35
  472. package/src/editor/popup.coffee +0 -68
  473. package/src/editor/remote-preview.coffee +0 -68
  474. package/src/editor/save/index.coffee +0 -62
  475. package/src/editor/skin/delete.coffee +0 -33
  476. package/src/editor/skin/editor/index.coffee +0 -188
  477. package/src/editor/skin/editor/panel.coffee +0 -37
  478. package/src/editor/skin/index.coffee +0 -126
  479. package/src/editor/skin/save.coffee +0 -58
  480. package/src/editor/skin/selector/index.coffee +0 -59
  481. package/src/editor/skin/selector/item.coffee +0 -123
  482. package/src/editor/skin/selector/list.coffee +0 -97
  483. package/src/editor/views/embed.pug +0 -10
  484. package/src/editor/views/loginbutton.pug +0 -8
  485. package/src/editor/views/popup.pug +0 -17
  486. package/src/editor/views/presets.pug +0 -8
  487. package/src/editor/views/presetslist-header.pug +0 -8
  488. package/src/editor/views/skinitem.pug +0 -13
  489. package/src/modules/control.js +0 -3
  490. package/src/modules/controls.js +0 -29
  491. package/src/modules/core.js +0 -19
  492. package/src/modules/editor.js +0 -80
  493. package/test/config.js +0 -3
  494. package/test/core/models/skin-spec.js +0 -209
  495. package/webpack.config.js +0 -46
  496. package/webpack.config.prod.js +0 -9
  497. /package/{src/core/views/date-time/popup.pug → dist/utils/passiveEvents.d.ts} +0 -0
@@ -0,0 +1,1584 @@
1
+ <!--
2
+ * ColorPickerInputController
3
+ *
4
+ * A sophisticated color picker component that manages complex color state and user interactions.
5
+ * Handles multiple color formats, opacity control, and recent color history.
6
+ *
7
+ * Key Features:
8
+ * - Color format conversion (HEX, RGB, HSV)
9
+ * - Opacity control with visual feedback
10
+ * - Color name suggestions with autocomplete
11
+ * - Recent colors history with preview
12
+ * - Keyboard navigation support
13
+ *
14
+ * Edge Cases:
15
+ * - Handles invalid color inputs
16
+ * - Preserves opacity across color changes
17
+ * - Manages concurrent drag operations
18
+ * - Prevents recursive updates
19
+ -->
20
+
21
+ <script lang="ts">
22
+ // Svelte imports
23
+ import { onMount, onDestroy, tick } from 'svelte';
24
+ import { createEventDispatcher } from 'svelte';
25
+ const dispatch = createEventDispatcher();
26
+
27
+ // Config imports
28
+ import type { InputControllerConfig } from './index';
29
+
30
+ // Shadcn UI Components
31
+ import { Input } from '@widgetic/design-system/components';
32
+ import { InputControllerHeader } from './index';
33
+
34
+ // Icons from Widgetic Design System
35
+ import { CheckerBoardBackground, OpacitySliderArrows } from '@widgetic/design-system/icons';
36
+
37
+ // Third party imports
38
+ import { Portal } from '@widgetic/design-system/components';
39
+ // Remove ColorSelect import from dependencies, use local forked version only
40
+ // import ColorSelect from 'svelte-color-select';
41
+ import ColorSelect from '../color-select/ColorSelect.svelte';
42
+
43
+ // Utils imports
44
+ import { ColorTransparencyHandler, type RGB } from '../../utils/colorTransparency';
45
+ import { colorNames } from '../../utils/colorNames';
46
+ import { getColorSuggestions, getNextSuggestion, type AutocompleteResult } from '../../utils/colorAutocomplete';
47
+ import { recentColorsUtil } from '../../utils/recentColors';
48
+
49
+
50
+
51
+ /**
52
+ * State Management
53
+ * Core state variables controlling component behavior and UI
54
+ * Handles keyboard modifiers, visibility states, and color history
55
+ */
56
+ let isShiftPressed = false;
57
+ let isSpacePressed = false;
58
+ let showNoRecentMessage = false;
59
+ let recentColors = recentColorsUtil.getRecentColors();
60
+
61
+ /**
62
+ * Component Props
63
+ * External properties that configure the color picker's behavior
64
+ */
65
+ export let label: string = ''; // Display label
66
+ export let value = '#000000'; // Current color value
67
+ export let onChange: (newValue: string) => void; // Value change callback
68
+ export let controllerId: string = '';
69
+ export let id: string = '';
70
+ export let openDropdowns: any;
71
+ export let toggleDropdown: (id: string) => void;
72
+ export let config: InputControllerConfig = {}; // Additional configuration
73
+ export let helpText: string | undefined = undefined; // Help text for the input
74
+
75
+ // Use config values with fallbacks
76
+ export let saveRecentColors = true;
77
+ export let showOpacityControls = true;
78
+ export let autofocus: boolean = false;
79
+ export let portalTarget = 'editor-portal';
80
+
81
+ // Sizing props for easy adjustment (can be overridden by parent)
82
+ export let pickerSize = 200; // width/height of color area
83
+ export let sliderWidth = 24; // width of hue bar and opacity input
84
+ $: swatchGap = 4;
85
+ $: swatchCount = 5;
86
+ $: swatchAreaWidth = pickerSize + sliderWidth + 14;
87
+ $: swatchSize = Math.floor((swatchAreaWidth - (swatchCount - 1) * swatchGap) / swatchCount);
88
+
89
+ // Exported opacity with default handling
90
+ export let opacity = config.defaultTransparency !== undefined ? config.defaultTransparency / 100 : 1;
91
+
92
+ // Tracks the last known opacity to maintain consistency
93
+ let lastKnownOpacity = opacity;
94
+
95
+ // Reactive statement to keep lastKnownOpacity in sync with opacity changes
96
+ $: lastKnownOpacity = opacity;
97
+
98
+ // Initialize the transparency handler with the current opacity
99
+ const transparencyHandler = new ColorTransparencyHandler(lastKnownOpacity);
100
+
101
+ /**
102
+ * Color State
103
+ * Maintains synchronized state between different color representations
104
+ * Uses isInternalUpdate flag to prevent recursive updates during state changes
105
+ */
106
+ let rgb = { r: 0, g: 0, b: 0, a: lastKnownOpacity };
107
+ let opacityValue = lastKnownOpacity;
108
+ let opacityPercentage = Math.round(lastKnownOpacity * 100);
109
+
110
+ // Unique instance ID for this color picker (use controllerId, id, or generate one)
111
+ const instanceId = controllerId || id || `cpicker-${Math.random().toString(36).slice(2, 8)}`;
112
+
113
+ // Local open state — not derived from store
114
+ let open = false;
115
+
116
+ // Listen for "close all color pickers except me" events from other instances
117
+ function onCloseOtherPickers(e: CustomEvent<string>) {
118
+ if (e.detail !== instanceId && open) {
119
+ open = false;
120
+ saveToRecentColors(value, lastKnownOpacity);
121
+ if (isColorSelectOpen) {
122
+ dispatch('colorselectmodalclose');
123
+ isColorSelectOpen = false;
124
+ }
125
+ }
126
+ }
127
+
128
+ let opacitySliderRef: HTMLDivElement;
129
+ let isDragging = false;
130
+ let isHueDragging = false;
131
+
132
+ let hexInput: string = value;
133
+ let previewColor: string = value;
134
+ let persistentOpacity: number = 1;
135
+
136
+ let isInternalUpdate = false;
137
+
138
+ // Tracks the position of the opacity indicator for visual feedback
139
+ $: indicatorLeft = `calc(${lastKnownOpacity * 100}% - ${parseInt(opacityLineWidth) / 2}px)`;
140
+
141
+ // Opacity bar dimensions for slider calculations
142
+ const opacityBarWidth = "257px";
143
+ const opacityBarHeight = "33px";
144
+
145
+ // Opacity line aesthetics and dimensions
146
+ const opacityLineStrokeColor = "rgb(0, 0, 0)";
147
+ const opacityLineStrokeOpacity = 0.1;
148
+ const opacityLineWidth = "2px";
149
+ const opacityLineColor = "rgba(255, 255, 255, 0)";
150
+
151
+ // Add to props section
152
+ export let showRing = true;
153
+
154
+ /**
155
+ * Generates RGBA border string based on color and opacity
156
+ * @param color - Base color in RGB format
157
+ * @param opacity - Opacity value to apply
158
+ * @returns CSS border string with RGBA color
159
+ */
160
+ const getRGBABorder = (color: string, opacity: number) => {
161
+ return `1px solid ${color.replace('rgb', 'rgba').replace(')', `, ${opacity})`)}`;
162
+ };
163
+
164
+ /**
165
+ * Converts hex color to RGBA while preserving opacity
166
+ * Handles both 3 and 6 character hex codes
167
+ * Returns normalized RGB values (0-1 range)
168
+ * @param hex - Hexadecimal color string
169
+ * @returns RGB object with normalized values
170
+ */
171
+ function hexToRgba(hex: string): RGB {
172
+ // Remove hash and handle both 3 and 6 character hex
173
+ const digits = hex.replace('#', '');
174
+ let r, g, b;
175
+
176
+ if (digits.length === 3) {
177
+ // For 3-character hex, duplicate each character
178
+ r = parseInt(digits[0] + digits[0], 16) / 255;
179
+ g = parseInt(digits[1] + digits[1], 16) / 255;
180
+ b = parseInt(digits[2] + digits[2], 16) / 255;
181
+ } else {
182
+ // For 6-character hex
183
+ r = parseInt(digits.slice(0, 2), 16) / 255;
184
+ g = parseInt(digits.slice(2, 4), 16) / 255;
185
+ b = parseInt(digits.slice(4, 6), 16) / 255;
186
+ }
187
+
188
+ return {
189
+ r,
190
+ g,
191
+ b,
192
+ a: lastKnownOpacity
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Converts RGB values to hex format
198
+ * Delegates to transparency handler for consistent color conversion
199
+ * Preserves alpha channel information
200
+ * @param r - Red component (0-1)
201
+ * @param g - Green component (0-1)
202
+ * @param b - Blue component (0-1)
203
+ * @param a - Alpha component (0-1)
204
+ * @returns Hexadecimal color string
205
+ */
206
+ function rgbaToHex(r: number, g: number, b: number, a: number): string {
207
+ return transparencyHandler.rgbaToHex(r, g, b, a);
208
+ }
209
+
210
+ // Reactive statement to synchronize value changes with internal state
211
+ $: {
212
+ if (value !== hexInput) {
213
+ const newRgb = hexToRgba(value);
214
+ rgb = newRgb; // The handler will preserve the opacity
215
+ hexInput = value;
216
+ previewColor = value;
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Updates color state and triggers change events
222
+ * - Validates hex format
223
+ * - Updates preview color
224
+ * - Triggers onChange callback only for valid hex values
225
+ * - Maintains 6-digit hex format
226
+ * @param newColor - New hex color value
227
+ */
228
+ function updateColor(newColor: string) {
229
+ previewColor = newColor;
230
+ if (/^#[0-9A-Fa-f]{6,8}$/.test(newColor)) {
231
+ value = newColor;
232
+ onChange(newColor);
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Handles color changes from the color picker
238
+ * - Updates hex value while preserving opacity
239
+ * - Prevents recursive updates with isInternalUpdate flag
240
+ * - Maintains consistent state across all color representations
241
+ * - Triggers onChange with clean hex value
242
+ */
243
+ function handleColorChange(eventOrDetail?: CustomEvent | Record<string, any>) {
244
+ try {
245
+ isInternalUpdate = true;
246
+ const detail = eventOrDetail instanceof CustomEvent ? eventOrDetail.detail : eventOrDetail;
247
+ if (detail && detail.rgb) {
248
+ rgb = detail.rgb;
249
+ if (typeof detail.a === 'number') {
250
+ lastKnownOpacity = detail.a;
251
+ }
252
+ }
253
+ // Only use the 6-character hex without alpha
254
+ const hexValue = `#${Math.round(rgb.r * 255).toString(16).padStart(2, '0')}${Math.round(rgb.g * 255).toString(16).padStart(2, '0')}${Math.round(rgb.b * 255).toString(16).padStart(2, '0')}`;
255
+ value = hexValue;
256
+ hexInput = hexValue;
257
+ previewColor = getRgbaColor();
258
+ onChange(hexValue);
259
+ } finally {
260
+ isInternalUpdate = false;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Normalizes hex color input
266
+ * - Handles color names conversion
267
+ * - Validates hex characters
268
+ * - Supports both 3 and 6 character formats
269
+ * - Returns standardized hex format
270
+ * @param input - User input string
271
+ * @returns Normalized hex color string
272
+ */
273
+ function normalizeHexInput(input: string): string {
274
+ // Remove hash if present and convert to lowercase
275
+ let color = input.toLowerCase().replace('#', '');
276
+
277
+ // Check if it's a color name
278
+ if (colorNames[color]) {
279
+ return colorNames[color];
280
+ }
281
+
282
+ // Only validate the characters, don't expand
283
+ if (!/^[0-9a-f]*$/i.test(color)) {
284
+ return value; // Return current value if invalid characters
285
+ }
286
+
287
+ // Return the current input with # prefix
288
+ return '#' + color;
289
+ }
290
+
291
+ /**
292
+ * Validates if a string is a valid hex color
293
+ * @param hex - Hexadecimal color string
294
+ * @returns Boolean indicating validity
295
+ */
296
+ function isValidHex(hex: string): boolean {
297
+ const digits = hex.replace('#', '');
298
+ return (digits.length === 3 || digits.length === 6) && /^[0-9a-f]+$/i.test(digits);
299
+ }
300
+
301
+ let currentSuggestion: AutocompleteResult | null = null;
302
+ let suggestionVisible = false;
303
+
304
+ /**
305
+ * Handles input events for hex input field
306
+ * - Processes valid hex inputs
307
+ * - Manages color name suggestions
308
+ * @param event - Input event from the text field
309
+ */
310
+ function handleHexInput(event: Event) {
311
+ const input = event.target as HTMLInputElement;
312
+ let newValue = input.value.toLowerCase();
313
+
314
+ // validate the new value
315
+ if (newValue.startsWith('#') || /^[0-9a-f]+$/i.test(newValue)) {
316
+ // if the value starts with #, keep it, otherwise add it
317
+ const processedValue = newValue.startsWith('#') ? newValue : `#${newValue}`;
318
+
319
+ // console.log('handleHexInput processedValue:', processedValue);
320
+
321
+ // if new value is a hex color(starts with # and has 3 or 6 characters): call the onChange callback
322
+ const hexRegex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i;
323
+ if (hexRegex.test(processedValue)) {
324
+ // console.log('Valid hex color detected:', processedValue);
325
+ try {
326
+ // set the internal update flag to true
327
+ isInternalUpdate = true;
328
+
329
+ // update the component's value
330
+ value = processedValue;
331
+
332
+ // update the rgb value
333
+ const newRgb = hexToRgba(processedValue);
334
+ rgb = { ...newRgb, a: lastKnownOpacity };
335
+
336
+ // update the preview color
337
+ previewColor = getRgbaColor();
338
+
339
+ // call the onChange callback
340
+ onChange(processedValue);
341
+ } finally {
342
+ isInternalUpdate = false;
343
+ }
344
+ return;
345
+ }
346
+ }
347
+
348
+ // console.log('handleHexInput newValue:', !/[^a-z\s]/.test(newValue));
349
+
350
+ // if newValue is empty or contains invalid characters, clear the suggestions
351
+ if (!newValue || /[^a-z\s]/.test(newValue)) {
352
+ console.log('Clearing suggestions due to invalid input');
353
+ suggestionVisible = false;
354
+ currentSuggestion = null;
355
+ return;
356
+ }
357
+
358
+ // if newValue is a valid color name, get the suggestions and show them
359
+ const suggestions = getColorSuggestions(newValue, colorNames);
360
+ console.log('Color suggestions:', { input: newValue, suggestions });
361
+ if (suggestions) {
362
+ currentSuggestion = suggestions;
363
+ suggestionVisible = true;
364
+ } else {
365
+ suggestionVisible = false;
366
+ currentSuggestion = null;
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Handles the selection of a color from suggestions
372
+ * @param colorName - Selected color name
373
+ * @param isShiftPressed - Indicates if the shift key was held during selection
374
+ */
375
+ function handleColorSelection(colorName: string, isShiftPressed: boolean) {
376
+ console.log('Handling color selection:', {
377
+ colorName,
378
+ isShiftPressed,
379
+ currentOpacity: lastKnownOpacity
380
+ });
381
+
382
+ const colorValue = colorNames[colorName];
383
+ value = colorValue;
384
+ const newRgb = hexToRgba(colorValue);
385
+
386
+ // If Shift is pressed, set opacity to 100%
387
+ if (isShiftPressed) {
388
+ console.log('Shift pressed: Setting opacity to 100%');
389
+ lastKnownOpacity = 1;
390
+ rgb = { ...newRgb, a: 1 };
391
+ opacityPercentage = 100;
392
+ } else {
393
+ // Keep current opacity
394
+ rgb = { ...newRgb, a: lastKnownOpacity };
395
+ }
396
+
397
+ previewColor = getRgbaColor();
398
+ onChange(colorValue);
399
+ suggestionVisible = false;
400
+ currentSuggestion = null;
401
+
402
+ // Save to recent colors
403
+ saveToRecentColors(colorValue, rgb.a);
404
+ }
405
+
406
+ /**
407
+ * Handles keydown events for keyboard navigation and interactions
408
+ * @param event - Keyboard event
409
+ */
410
+ function handleKeyDown(event: KeyboardEvent) {
411
+ const input = event.target as HTMLInputElement;
412
+ const currentInputValue = input.value;
413
+
414
+ // Only prevent default for special keys when popup is open
415
+ if (open && (event.key === 'Shift' || event.key === ' ')) {
416
+ event.preventDefault();
417
+ return;
418
+ }
419
+
420
+ // console.log('Key pressed:', {
421
+ // key: event.key,
422
+ // actualInputValue: currentInputValue,
423
+ // boundHexInput: hexInput,
424
+ // isEmpty: !currentInputValue.trim(),
425
+ // currentValue: value
426
+ // });
427
+
428
+ if (event.key === 'Escape') {
429
+ event.preventDefault();
430
+ closeAutosuggestions();
431
+ if (!currentInputValue.trim()) {
432
+ restoreCurrentColor();
433
+ }
434
+ return;
435
+ }
436
+
437
+ if (event.key === 'Enter') {
438
+ if (!currentInputValue.trim() || currentInputValue === '#') {
439
+ restoreCurrentColor();
440
+ return;
441
+ }
442
+ }
443
+
444
+ // if the suggestions are visible, handle the key events to navigate the suggestions
445
+ if (suggestionVisible && currentSuggestion) {
446
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
447
+ event.preventDefault();
448
+
449
+ const direction = event.key === 'ArrowDown' ? 'down' : 'up';
450
+
451
+ const result = getNextSuggestion(currentSuggestion, direction);
452
+ if (result) {
453
+ currentSuggestion = result;
454
+ }
455
+ } else if (event.key === 'Enter') {
456
+ event.preventDefault();
457
+ handleColorSelection(currentSuggestion.suggestion, event.shiftKey);
458
+ }
459
+ }
460
+ }
461
+
462
+ /**
463
+ * Handles popover open/close state changes
464
+ * - Saves current color when closing
465
+ * - Closes autosuggestions if opening
466
+ * - Calculates fixed position when opening
467
+ */
468
+ function handleToggle(isOpen: boolean) {
469
+ // console.warn('[ColorPicker] handleToggle called:', { isOpen, currentOpen: open, instanceId, portalTarget });
470
+ if (isOpen === open) return;
471
+
472
+ if (!isOpen) {
473
+ saveToRecentColors(value, lastKnownOpacity);
474
+ if (isColorSelectOpen) {
475
+ dispatch('colorselectmodalclose');
476
+ isColorSelectOpen = false;
477
+ }
478
+ } else {
479
+ // Close all other color pickers before opening this one
480
+ window.dispatchEvent(new CustomEvent('widgetic:close-color-pickers', { detail: instanceId }));
481
+ closeAutosuggestions();
482
+ dispatch('colorselectmodalopen');
483
+ isColorSelectOpen = true;
484
+ updatePopoverPosition();
485
+ // Re-position after portal is mounted and layout stabilizes
486
+ tick().then(() => {
487
+ requestAnimationFrame(updatePopoverPosition);
488
+ });
489
+ }
490
+
491
+ open = isOpen;
492
+ }
493
+
494
+ /**
495
+ * Closes the color picker dropdown and autosuggestions
496
+ */
497
+ function closeDropdown() {
498
+ if (open) {
499
+ handleToggle(false);
500
+ }
501
+
502
+ closeAutosuggestions();
503
+ }
504
+
505
+
506
+ function closeAutosuggestions() {
507
+ // Close autosuggestions if they're open
508
+ if (suggestionVisible) {
509
+ suggestionVisible = false;
510
+ currentSuggestion = null;
511
+ // Set the input to the current hex value
512
+ hexInput = value.slice(0, 7); // Only take the 6-character hex
513
+ }
514
+ }
515
+
516
+ /**
517
+ * Handles scroll events to save color and close dropdown if necessary
518
+ * @param event - Scroll event
519
+ */
520
+ function handleScroll(event: Event) {
521
+ // Don't close if scrolling happens within allowed elements
522
+ if ((event.target as HTMLElement).closest('[data-allow-scroll="true"]')) {
523
+ return;
524
+ }
525
+
526
+ // Close both suggestions and color picker
527
+ if (suggestionVisible || open) {
528
+ suggestionVisible = false;
529
+ currentSuggestion = null;
530
+ closeDropdown();
531
+ }
532
+ }
533
+
534
+ /**
535
+ * Manages opacity slider interactions
536
+ * - Handles mouse drag events
537
+ * - Updates opacity state
538
+ * - Maintains color consistency
539
+ * - Prevents event bubbling
540
+ * @param event - Mouse event
541
+ */
542
+ function handleOpacityMouseDown(event: MouseEvent) {
543
+ isDragging = true;
544
+ document.body.style.cursor = 'grabbing';
545
+ updateOpacity(event);
546
+
547
+ const handleMouseMove = (e: MouseEvent) => {
548
+ if (isDragging) {
549
+ updateOpacity(e);
550
+ }
551
+ };
552
+
553
+ const handleMouseUp = () => {
554
+ isDragging = false;
555
+ document.body.style.cursor = '';
556
+ window.removeEventListener('mousemove', handleMouseMove);
557
+ window.removeEventListener('mouseup', handleMouseUp);
558
+ };
559
+
560
+ window.addEventListener('mousemove', handleMouseMove);
561
+ window.addEventListener('mouseup', handleMouseUp);
562
+ }
563
+
564
+ // Reactive statement to handle external value changes
565
+ $: {
566
+ if (value !== hexInput && !isInternalUpdate) {
567
+ const newRgb = hexToRgba(value);
568
+ rgb = { ...newRgb, a: lastKnownOpacity }; // Preserve opacity
569
+ hexInput = value;
570
+ previewColor = value;
571
+ }
572
+ }
573
+
574
+ // Reactive statement to update the style of the opacity indicator
575
+ $: verticalLineStyle = `
576
+ left: ${indicatorLeft};
577
+ width: ${opacityLineWidth};
578
+ background-color: ${opacityLineColor};
579
+ `;
580
+
581
+ /**
582
+ * Applies opacity to a given RGB color
583
+ * @param color - RGB color object
584
+ * @param opacity - Opacity value to apply
585
+ * @returns RGB color object with updated opacity
586
+ */
587
+ function applyOpacityToColor(color: RGB, opacity: number): RGB {
588
+ return {
589
+ r: color.r,
590
+ g: color.g,
591
+ b: color.b,
592
+ a: opacity
593
+ };
594
+ }
595
+
596
+ // Reactive statement to apply opacity changes
597
+ $: {
598
+ if (value !== hexInput && !isInternalUpdate) {
599
+ const newRgb = hexToRgba(value);
600
+ rgb = applyOpacityToColor(newRgb, lastKnownOpacity);
601
+ hexInput = value;
602
+ previewColor = rgbaToHex(rgb.r, rgb.g, rgb.b, lastKnownOpacity);
603
+ }
604
+ }
605
+
606
+ /**
607
+ * Updates opacity based on slider position
608
+ * - Calculates normalized opacity value
609
+ * - Updates all color representations
610
+ * - Triggers change callbacks
611
+ * - Handles edge cases
612
+ * @param event - Mouse event from slider interaction
613
+ */
614
+ function updateOpacity(event: MouseEvent) {
615
+ if (!opacitySliderRef) return;
616
+
617
+ const rect = opacitySliderRef.getBoundingClientRect();
618
+ const x = event.clientX - rect.left;
619
+ const width = rect.width;
620
+
621
+ let newOpacity = Math.max(0, Math.min(x, width)) / width;
622
+ newOpacity = Math.round(newOpacity * 100) / 100;
623
+
624
+ try {
625
+ isInternalUpdate = true;
626
+ opacity = newOpacity; // This will trigger the reactive statement
627
+ rgb = { ...rgb, a: newOpacity };
628
+ lastKnownOpacity = newOpacity;
629
+ const hexValue = `#${Math.round(rgb.r * 255).toString(16).padStart(2, '0')}${Math.round(rgb.g * 255).toString(16).padStart(2, '0')}${Math.round(rgb.b * 255).toString(16).padStart(2, '0')}`;
630
+ value = hexValue;
631
+ hexInput = hexValue;
632
+ previewColor = getRgbaColor();
633
+ onChange(hexValue);
634
+ onOpacityChange(newOpacity); // Call the opacity change handler
635
+ } finally {
636
+ isInternalUpdate = false;
637
+ }
638
+ }
639
+
640
+ /**
641
+ * Updates opacity from numeric input
642
+ * - Validates input range (0-100)
643
+ * - Converts percentage to decimal
644
+ * - Updates all color states
645
+ * - Maintains color consistency
646
+ * - Prevents invalid states
647
+ * @param event - Input event from the numeric opacity field
648
+ */
649
+ function updateOpacityFromInput(event: Event) {
650
+ const input = event.target as HTMLInputElement;
651
+ input.value = input.value.replace(/[^\d]/g, '').slice(0, 3);
652
+
653
+ let num = parseInt(input.value);
654
+ if (isNaN(num) || input.value === '') num = 0;
655
+ if (num > 100) num = 100;
656
+
657
+ input.value = num.toString();
658
+ opacityPercentage = num;
659
+
660
+ try {
661
+ isInternalUpdate = true;
662
+ lastKnownOpacity = num / 100;
663
+ rgb = { ...rgb, a: num / 100 };
664
+ // Only use the 6-character hex without alpha
665
+ const hexValue = `#${Math.round(rgb.r * 255).toString(16).padStart(2, '0')}${Math.round(rgb.g * 255).toString(16).padStart(2, '0')}${Math.round(rgb.b * 255).toString(16).padStart(2, '0')}`;
666
+ value = hexValue;
667
+ hexInput = hexValue;
668
+ previewColor = getRgbaColor();
669
+ onChange(hexValue);
670
+ } finally {
671
+ isInternalUpdate = false;
672
+ }
673
+ }
674
+
675
+ // Reactive statement to calculate opacity percentage
676
+ $: opacityPercentage = Math.round(lastKnownOpacity * 100);
677
+
678
+ let lastKnownHue = 0;
679
+ let colorSelectRef: ColorSelect; // Reference to ColorSelect component
680
+
681
+ /**
682
+ * Converts HSV to RGB values
683
+ * @param h - Hue value
684
+ * @param s - Saturation value
685
+ * @param v - Value (brightness) value
686
+ * @returns RGB color object with normalized values
687
+ */
688
+ function hsvToRgb(h: number, s: number, v: number) {
689
+ const f = (n: number, k = (n + h/60) % 6) => v - v * s * Math.max(Math.min(k, 4 - k, 1), 0);
690
+ return {
691
+ r: Math.round(f(5) * 255) / 255,
692
+ g: Math.round(f(3) * 255) / 255,
693
+ b: Math.round(f(1) * 255) / 255,
694
+ a: lastKnownOpacity
695
+ };
696
+ }
697
+
698
+ /**
699
+ * Logs color-related events for debugging purposes
700
+ * @param event - Custom event from color picker interactions
701
+ */
702
+ function onColorEvent(event: CustomEvent) {
703
+ // Safe logging that won't throw errors
704
+ console.log('Color event received:', {
705
+ type: event.type,
706
+ rgb: event.detail?.rgb || rgb,
707
+ mode: event.detail?.mode,
708
+ rawEvent: {
709
+ detail: event.detail,
710
+ target: event.target ? 'exists' : 'null',
711
+ currentTarget: event.currentTarget ? 'exists' : 'null'
712
+ }
713
+ });
714
+ }
715
+
716
+ /**
717
+ * Handles direct hue changes from the color picker
718
+ * @param event - Custom event containing hue information
719
+ */
720
+ function handleHueChange(event: CustomEvent) {
721
+ console.log('Hue change event:', event.detail);
722
+ try {
723
+ isInternalUpdate = true;
724
+ lastKnownHue = event.detail.h;
725
+
726
+ // Convert HSV to RGB using current saturation and value
727
+ const newRgb = hsvToRgb(
728
+ lastKnownHue,
729
+ 1, // Full saturation for testing
730
+ 1 // Full value for testing
731
+ );
732
+
733
+ rgb = newRgb;
734
+ handleColorChange();
735
+
736
+ console.log('After hue change:', {
737
+ hue: lastKnownHue,
738
+ rgb: newRgb,
739
+ hexValue: rgbaToHex(newRgb.r, newRgb.g, newRgb.b, lastKnownOpacity)
740
+ });
741
+ } finally {
742
+ isInternalUpdate = false;
743
+ }
744
+ }
745
+
746
+ let dragState = {
747
+ isHueDragging: false,
748
+ isOpacityDragging: false,
749
+ isColorDragging: false
750
+ };
751
+
752
+ /**
753
+ * Manages drag state across different interactions
754
+ * Prevents race conditions and maintains consistent state
755
+ * Handles cleanup on component destruction
756
+ * @param event - Mouse event from global interactions
757
+ */
758
+ function handleGlobalMouseUp(event: MouseEvent) {
759
+ if (!open) return;
760
+
761
+ // console.log('Global mouseup triggered', dragState);
762
+ if (dragState.isHueDragging || dragState.isOpacityDragging || dragState.isColorDragging) {
763
+ dragState = {
764
+ isHueDragging: false,
765
+ isOpacityDragging: false,
766
+ isColorDragging: false
767
+ };
768
+ handleColorChange();
769
+ // console.log('Dragging stopped');
770
+ }
771
+ }
772
+
773
+ /**
774
+ * Handles global mouse move events during dragging
775
+ * @param event - Mouse move event
776
+ */
777
+ function handleGlobalMouseMove(event: MouseEvent) {
778
+ if (dragState.isHueDragging || dragState.isOpacityDragging || dragState.isColorDragging) {
779
+ // console.log('Moving while dragging:', {
780
+ // type: dragState.isHueDragging ? 'hue' :
781
+ // dragState.isOpacityDragging ? 'opacity' : 'color',
782
+ // event: event
783
+ // });
784
+ handleColorChange();
785
+ }
786
+ }
787
+
788
+ /**
789
+ * Determines the type of drag based on mouse interaction
790
+ * @param event - Mouse down event initiating drag
791
+ */
792
+ function handleDragStart(event: MouseEvent) {
793
+ const target = event.target as HTMLElement;
794
+ const rect = target.getBoundingClientRect();
795
+ const parentRect = target.parentElement?.getBoundingClientRect();
796
+
797
+ // Log all relevant information for debugging
798
+ // console.log('Mousedown details:', {
799
+ // element: target,
800
+ // rect: rect,
801
+ // parentRect: parentRect,
802
+ // style: target.getAttribute('style'),
803
+ // parentStyle: target.parentElement?.getAttribute('style')
804
+ // });
805
+
806
+ // Check if it's a canvas element
807
+ if (target.tagName.toLowerCase() === 'canvas') {
808
+ // Get the computed style to check positioning
809
+ const computedStyle = window.getComputedStyle(target);
810
+ const top = parseInt(computedStyle.top);
811
+
812
+ if (rect.width === 31 && rect.height === 256) {
813
+ if (top < 50) { // Hue slider is typically at the top
814
+ dragState.isHueDragging = true;
815
+ console.log('Hue drag started (top slider)');
816
+ } else { // Opacity slider is typically at the bottom
817
+ dragState.isOpacityDragging = true;
818
+ console.log('Opacity drag started (bottom slider)');
819
+ }
820
+ } else {
821
+ dragState.isColorDragging = true;
822
+ // console.log('Color drag started (main area)');
823
+ }
824
+ }
825
+
826
+ // console.log('Current drag state:', dragState);
827
+ }
828
+
829
+ /**
830
+ * Initializes component state
831
+ * - Normalizes initial color value
832
+ * - Sets up event listeners
833
+ * - Configures opacity handling
834
+ * - Initializes recent colors
835
+ */
836
+ function initializeColor() {
837
+ const normalizedHex = normalizeHexInput(value);
838
+ if (normalizedHex.startsWith('#')) {
839
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(normalizedHex);
840
+ if (result) {
841
+ const r = parseInt(result[1], 16) / 255;
842
+ const g = parseInt(result[2], 16) / 255;
843
+ const b = parseInt(result[3], 16) / 255;
844
+
845
+ rgb = {
846
+ // r: r === 0 ? 0.000001 : r, // Prevent zero opacity issues
847
+ r: r,
848
+ g: g,
849
+ b: b,
850
+ a: lastKnownOpacity
851
+ };
852
+
853
+ // Set initial display values
854
+ hexInput = normalizedHex;
855
+ previewColor = getRgbaColor();
856
+
857
+ // console.log('Color initialized:', {
858
+ // hex: normalizedHex,
859
+ // rgb,
860
+ // opacity: lastKnownOpacity,
861
+ // percentage: opacityPercentage
862
+ // });
863
+ }
864
+ }
865
+ }
866
+
867
+ /**
868
+ * Generates an RGBA color string for CSS
869
+ * @returns RGBA color string based on current RGB and opacity
870
+ */
871
+ function getRgbaColor() {
872
+ const r = Math.round(rgb.r * 255);
873
+ const g = Math.round(rgb.g * 255);
874
+ const b = Math.round(rgb.b * 255);
875
+ return `rgba(${r}, ${g}, ${b}, ${lastKnownOpacity})`;
876
+ }
877
+
878
+ // Make previewColor reactive so it updates instantly whenever rgb or opacity change
879
+ $: previewColor = getRgbaColor();
880
+
881
+ /**
882
+ * Handles keydown events within the popover for modifier keys
883
+ * @param event - Keyboard event
884
+ */
885
+ function handlePopupKeyDown(event: KeyboardEvent) {
886
+ // Only handle keys if the popup is open
887
+ if (!open) return;
888
+
889
+ if (event.key === 'Shift' || event.key === ' ') {
890
+ event.preventDefault(); // Prevent space from scrolling
891
+
892
+ if (event.key === 'Shift') {
893
+ isShiftPressed = true;
894
+ } else if (event.key === ' ') {
895
+ isSpacePressed = true;
896
+ }
897
+
898
+ if (recentColors.length === 0) {
899
+ showNoRecentMessage = true;
900
+ setTimeout(() => {
901
+ showNoRecentMessage = false;
902
+ }, 2000);
903
+ }
904
+ }
905
+ }
906
+
907
+ /**
908
+ * Handles keyup events within the popover to reset modifier keys
909
+ * @param event - Keyboard event
910
+ */
911
+ function handlePopupKeyUp(event: KeyboardEvent) {
912
+ // Only handle keys if the popup is open
913
+ if (!open) return;
914
+
915
+ if (event.key === 'Shift' || event.key === ' ') {
916
+ if (event.key === 'Shift') {
917
+ isShiftPressed = false;
918
+ } else if (event.key === ' ') {
919
+ isSpacePressed = false;
920
+ }
921
+ }
922
+ }
923
+
924
+ // Sets up event listeners when the component mounts
925
+ onMount(() => {
926
+ initializeColor();
927
+ window.addEventListener('mouseup', handleGlobalMouseUp);
928
+ window.addEventListener('mousemove', handleGlobalMouseMove);
929
+ window.addEventListener('keydown', handlePopupKeyDown);
930
+ window.addEventListener('keyup', handlePopupKeyUp);
931
+ window.addEventListener('scroll', handleScroll, true);
932
+ window.addEventListener('widgetic:close-color-pickers', onCloseOtherPickers as EventListener);
933
+ });
934
+
935
+ // Cleans up event listeners when the component is destroyed
936
+ onDestroy(() => {
937
+ window.removeEventListener('mouseup', handleGlobalMouseUp);
938
+ window.removeEventListener('mousemove', handleGlobalMouseMove);
939
+ window.removeEventListener('keydown', handlePopupKeyDown);
940
+ window.removeEventListener('keyup', handlePopupKeyUp);
941
+ window.removeEventListener('scroll', handleScroll, true);
942
+ window.removeEventListener('widgetic:close-color-pickers', onCloseOtherPickers as EventListener);
943
+ });
944
+
945
+ // Then handle opacity updates
946
+ $: {
947
+ if (value) {
948
+ if (value.startsWith('rgba')) {
949
+ const matches = value.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*([\d.]+)\)/);
950
+ if (matches) {
951
+ const [_, r, g, b, a] = matches;
952
+ rgb = {
953
+ r: parseInt(r) / 255,
954
+ g: parseInt(g) / 255,
955
+ b: parseInt(b) / 255,
956
+ a: parseFloat(a)
957
+ };
958
+ opacityValue = parseFloat(a);
959
+ opacityPercentage = Math.round(opacityValue * 100);
960
+ }
961
+ }
962
+ else if (value.startsWith('#') && value.length === 9) {
963
+ const alpha = parseInt(value.slice(7), 16);
964
+ opacityValue = alpha / 255;
965
+ opacityPercentage = Math.round(opacityValue * 100);
966
+ rgb.a = opacityValue;
967
+ }
968
+ }
969
+ }
970
+
971
+ /**
972
+ * Saves a color to the recent colors list
973
+ * - Prevents duplicates by checking the most recent entry
974
+ * - Maintains storage limits through utility functions
975
+ * @param color - Hexadecimal color string
976
+ * @param alpha - Opacity value of the color
977
+ */
978
+ function saveToRecentColors(color: string, alpha: number) {
979
+ if (!saveRecentColors) {
980
+ console.log('Recent colors saving is disabled');
981
+ return;
982
+ }
983
+
984
+ // Check if this exact color+alpha combination is already the most recent
985
+ const currentRecents = recentColorsUtil.getRecentColors();
986
+ // console.log('Current recent colors:', currentRecents);
987
+
988
+ const mostRecent = currentRecents[0];
989
+
990
+ if (mostRecent &&
991
+ mostRecent.color.toLowerCase() === color.toLowerCase() &&
992
+ mostRecent.alpha === alpha) {
993
+ console.log('Color already most recent, skipping save:', { color, alpha });
994
+ return;
995
+ }
996
+
997
+ // console.log('Saving new color to recent colors:', { color, alpha });
998
+ recentColors = recentColorsUtil.addRecentColor(color, alpha);
999
+ // console.log('Updated recent colors list:', recentColors);
1000
+ }
1001
+
1002
+ /**
1003
+ * Handles the selection of a color name from suggestions
1004
+ * @param colorName - Selected color name
1005
+ */
1006
+ function handleColorNameSelection(colorName: string) {
1007
+ const colorValue = colorNames[colorName];
1008
+ value = colorValue;
1009
+ const newRgb = hexToRgba(colorValue);
1010
+ rgb = { ...newRgb, a: lastKnownOpacity };
1011
+ previewColor = getRgbaColor();
1012
+ onChange(colorValue);
1013
+ suggestionVisible = false;
1014
+ currentSuggestion = null;
1015
+
1016
+ // Save the selected color name
1017
+ saveToRecentColors(colorValue, lastKnownOpacity);
1018
+ }
1019
+
1020
+ /**
1021
+ * Handles click events on color suggestions
1022
+ * @param match - The matched color name clicked by the user
1023
+ */
1024
+ function handleSuggestionClick(match: string) {
1025
+ console.log('Color name selected:', match);
1026
+ hexInput = match;
1027
+ const colorValue = colorNames[match];
1028
+ console.log('Converting color name to hex:', { name: match, hex: colorValue });
1029
+
1030
+ value = colorValue;
1031
+ const newRgb = hexToRgba(colorValue);
1032
+ rgb = { ...newRgb, a: lastKnownOpacity };
1033
+ previewColor = getRgbaColor();
1034
+ onChange(colorValue);
1035
+ suggestionVisible = false;
1036
+ currentSuggestion = null;
1037
+
1038
+ // Save the hex value of the selected color name
1039
+ saveToRecentColors(colorValue, lastKnownOpacity);
1040
+ }
1041
+
1042
+ /**
1043
+ * Handles blur events on the hex input field to restore color if input is empty
1044
+ * @param event - Focus event from the input field
1045
+ */
1046
+ function handleInputBlur(event: FocusEvent) {
1047
+ // Get the related target (element receiving focus)
1048
+ const relatedTarget = event.relatedTarget as HTMLElement;
1049
+
1050
+ // Don't close if focus is moving to a suggestion item
1051
+ if (relatedTarget?.closest('.color-suggestions')) {
1052
+ return;
1053
+ }
1054
+
1055
+ const input = event.target as HTMLInputElement;
1056
+ const currentInputValue = input.value;
1057
+
1058
+ // Close suggestions
1059
+ closeAutosuggestions();
1060
+
1061
+ // Check actual input value
1062
+ if (!currentInputValue.trim()) {
1063
+ restoreCurrentColor();
1064
+ }
1065
+ }
1066
+
1067
+ let inputRef: HTMLDivElement;
1068
+
1069
+ /**
1070
+ * Restores the current color value when input is invalid or empty
1071
+ * - Resets the hex input to the last valid color value
1072
+ * - Forces the input field to update and regain focus
1073
+ */
1074
+ function restoreCurrentColor() {
1075
+ console.log('Restoring current color:', {
1076
+ currentValue: value,
1077
+ hexToRestore: value.slice(0, 7),
1078
+ currentHexInput: hexInput
1079
+ });
1080
+
1081
+ if (value && value.startsWith('#')) {
1082
+ const colorToRestore = value.slice(0, 7);
1083
+
1084
+ // Force immediate update
1085
+ hexInput = colorToRestore;
1086
+ value = colorToRestore;
1087
+
1088
+ // Force input focus and update
1089
+ setTimeout(() => {
1090
+ const inputElement = inputRef?.querySelector('input') as HTMLInputElement;
1091
+ if (inputElement) {
1092
+ inputElement.value = colorToRestore;
1093
+ inputElement.focus();
1094
+ inputElement.blur();
1095
+ }
1096
+ }, 0);
1097
+ }
1098
+ }
1099
+
1100
+ // Exported onFocus handler with default no-op
1101
+ export let onFocus: () => void = () => {};
1102
+
1103
+ // Sets autofocus on mount if enabled
1104
+ onMount(() => {
1105
+ if (autofocus && inputRef) {
1106
+ inputRef.focus();
1107
+ console.log('ColorPickerInputController focused');
1108
+ }
1109
+ });
1110
+
1111
+ /**
1112
+ * Focus method to programmatically focus the input field
1113
+ */
1114
+ export function focus() {
1115
+ if (inputRef) {
1116
+ inputRef.focus();
1117
+ console.log('ColorPickerInputController focus method called');
1118
+ }
1119
+ }
1120
+
1121
+ // Popover visibility is now handled by {#if open} conditional rendering
1122
+
1123
+ // Exported opacity change handler with default no-op
1124
+ export let onOpacityChange: (opacity: number) => void = () => {};
1125
+
1126
+ // Add these variables to track input position
1127
+ let inputComponent: any;
1128
+ let inputRect: DOMRect | null = null;
1129
+
1130
+ // Track trigger position for fixed-position popover
1131
+ let triggerRef: HTMLDivElement;
1132
+ let popoverStyle = '';
1133
+
1134
+ function updatePopoverPosition() {
1135
+ if (!triggerRef) return;
1136
+ const rect = triggerRef.getBoundingClientRect();
1137
+ // Guard: if trigger hasn't been laid out yet, defer
1138
+ if (rect.width === 0 && rect.height === 0) {
1139
+ requestAnimationFrame(updatePopoverPosition);
1140
+ return;
1141
+ }
1142
+ const popoverHeight = 420;
1143
+ const spaceBelow = window.innerHeight - rect.bottom;
1144
+ const spaceAbove = rect.top;
1145
+ const openAbove = spaceBelow < popoverHeight + 8 && spaceAbove > popoverHeight + 8;
1146
+ const top = openAbove
1147
+ ? Math.round(rect.top - popoverHeight - 4)
1148
+ : Math.round(rect.bottom + 4);
1149
+ // Align popup's right edge with the right edge of the full input container
1150
+ const inputContainer = triggerRef.closest('.color-input-main-container') as HTMLElement | null;
1151
+ const rightRef = inputContainer ? inputContainer.getBoundingClientRect().right : rect.right;
1152
+ const right = Math.round(window.innerWidth - rightRef);
1153
+ popoverStyle = `top: ${top}px; right: ${right}px;`;
1154
+ }
1155
+
1156
+ // Position is calculated in handleToggle after tick()
1157
+
1158
+ // Update the position function to get the actual input element
1159
+ function updateInputPosition() {
1160
+ // Get the actual input element from the component
1161
+ // console.log('updateInputPosition', inputRef);
1162
+ const inputElement = inputRef?.querySelector('input');
1163
+ if (inputElement) {
1164
+ inputRect = inputElement.getBoundingClientRect();
1165
+ // console.log('inputRect', inputRect);
1166
+ }
1167
+ }
1168
+
1169
+ // Update the suggestions position when they become visible
1170
+ $: if (suggestionVisible) {
1171
+ updateInputPosition();
1172
+ }
1173
+
1174
+ // Close suggestions when picker is closed
1175
+ $: if (!open) {
1176
+ suggestionVisible = false;
1177
+ currentSuggestion = null;
1178
+ if (isColorSelectOpen) {
1179
+ dispatch('colorselectmodalclose');
1180
+ isColorSelectOpen = false;
1181
+ }
1182
+ }
1183
+
1184
+ // Add this near the top with other state declarations
1185
+ let isColorSelectOpen = false;
1186
+
1187
+ // Add these computed styles for layout
1188
+ $: opacityBarWidthPx = `${pickerSize}px`;
1189
+ $: recentColorsMaxWidth = `${pickerSize + sliderWidth}px`;
1190
+ </script>
1191
+
1192
+ <!-- Color Picker Input Controller Container -->
1193
+ <div class="color-picker-input-controller-ct flex flex-col gap-2 widgetic-colors">
1194
+ <div class="flex items-center w-full relative">
1195
+
1196
+ <!-- Input Controller Header -->
1197
+ <div class="input-controller-header-ct flex-1 min-w-0">
1198
+ <InputControllerHeader {label} helpText={helpText} />
1199
+ </div>
1200
+
1201
+ <!-- Input Controller Container -->
1202
+ <div class="input-controller-ct absolute right-0">
1203
+ <div class="color-input-main-container rounded-large flex items-center transition-colors duration-200"
1204
+ style="height: 48px; width: 185px; background: hsl(var(--grey1)); {showRing ? 'border: 1px solid hsl(var(--grey2));' : ''}">
1205
+ <div class="flex items-center w-full justify-between" style="padding-right: 12px;">
1206
+
1207
+ <!-- Hex Input Container -->
1208
+ <div class="hex-input-container flex-1" bind:this={inputRef}>
1209
+ <Input
1210
+ bind:this={inputComponent}
1211
+ type="text"
1212
+ class="h-9 border-0 bg-transparent subtext text-grey7 placeholder:text-grey3 focus-visible:ring-0 focus-visible:ring-offset-0 pl-4"
1213
+ value={hexInput}
1214
+ onfocus={(e) => {
1215
+ dispatch('focus', e);
1216
+ onFocus();
1217
+ }}
1218
+ onblur={(e) => {
1219
+ dispatch('blur', e);
1220
+ const input = e.target as HTMLInputElement;
1221
+ if (!input.value.trim()) {
1222
+ restoreCurrentColor();
1223
+ } else {
1224
+ handleInputBlur(e);
1225
+ }
1226
+ }}
1227
+ oninput={(e) => {
1228
+ const input = e.target as HTMLInputElement;
1229
+ if (!input.value) {
1230
+ hexInput = '';
1231
+ }
1232
+ handleHexInput(e);
1233
+ updateInputPosition();
1234
+ }}
1235
+ onkeydown={(e) => {
1236
+ const input = e.target as HTMLInputElement;
1237
+ if ((e.key === 'Enter' || e.key === 'Escape') && !input.value.trim()) {
1238
+ e.preventDefault();
1239
+ restoreCurrentColor();
1240
+ } else {
1241
+ handleKeyDown(e);
1242
+ }
1243
+ }}
1244
+ />
1245
+ </div>
1246
+
1247
+ <!-- Color Preview Button -->
1248
+ <div class="color-preview-button-container relative flex items-center" style="height: 48px;">
1249
+ <div class="color-picker-button-content-ct relative">
1250
+ <!-- Color Picker Trigger (manual, no bits-ui Popover) -->
1251
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
1252
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
1253
+ <div bind:this={triggerRef}
1254
+ class="color-preview-button flex items-center justify-center rounded-small cursor-pointer relative overflow-hidden {showRing ? 'ring-1 ring-grey2' : ''}"
1255
+ style="width: 24px; height: 24px; min-width: 24px;"
1256
+ role="button"
1257
+ tabindex="0"
1258
+ onclick={(e) => { e.stopPropagation(); handleToggle(!open); }}
1259
+ >
1260
+ <CheckerBoardBackground class="absolute inset-0 w-full h-full pointer-events-none rounded-[inherit]" />
1261
+ <div class="absolute inset-0 w-full h-full rounded-[inherit] pointer-events-none" style="background-color: {previewColor};"></div>
1262
+ </div>
1263
+
1264
+ <!-- Color Suggestions Portal -->
1265
+ {#if suggestionVisible && currentSuggestion}
1266
+ <Portal target={portalTarget} class="color-suggestions-portal">
1267
+ {#if currentSuggestion.allMatches.length > 0 && inputRect}
1268
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
1269
+ <div
1270
+ class="color-suggestions fixed bg-white border rounded-md shadow-lg overflow-y-auto"
1271
+ style="
1272
+ pointer-events: auto !important;
1273
+ z-index: 99999;
1274
+ max-height: 12rem;
1275
+ top: {inputRect.bottom + 4}px;
1276
+ left: {inputRect.left}px;
1277
+ width: {inputRect.width}px;
1278
+ "
1279
+ data-allow-scroll="true"
1280
+ onscroll={(e) => e.stopPropagation()}
1281
+ onmousedown={(event) => {
1282
+ event.preventDefault();
1283
+ event.stopPropagation();
1284
+ const target = event.target as HTMLElement;
1285
+ if (target.closest('.color-suggestion-item')) {
1286
+ event.preventDefault();
1287
+ }
1288
+ }}
1289
+ onfocusout={(event) => {
1290
+ const relatedTarget = event.relatedTarget as HTMLElement;
1291
+ // Only close if focus is moving outside the suggestions
1292
+ if (!relatedTarget?.closest('.color-suggestions')) {
1293
+ closeAutosuggestions();
1294
+ }
1295
+ }}
1296
+ >
1297
+ {#each currentSuggestion.allMatches as match, i}
1298
+ <div
1299
+ class="color-suggestion-item px-2 py-1 cursor-pointer hover:bg-gray-100"
1300
+ style="position: relative; z-index: 99999; pointer-events: auto !important;"
1301
+ class:bg-gray-100={i === currentSuggestion?.currentIndex}
1302
+ onmousedown={(event) => {
1303
+ event.preventDefault();
1304
+ event.stopPropagation();
1305
+ handleColorSelection(match, event.shiftKey);
1306
+ }}
1307
+ >
1308
+ {match}
1309
+ </div>
1310
+ {/each}
1311
+ </div>
1312
+ {/if}
1313
+ </Portal>
1314
+ {/if}
1315
+
1316
+ <!-- Color Picker — portaled to a unique body-level div -->
1317
+ {#if open}
1318
+ <Portal target="color-picker-portal" class="color-picker-portal-ct" style="position: fixed; inset: 0; z-index: 99999; pointer-events: none;">
1319
+
1320
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
1321
+ <div class="color-picker-backdrop" style="z-index: 1;" onmousedown={() => handleToggle(false)}></div>
1322
+
1323
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
1324
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
1325
+ <div class="color-picker-popover-content w-auto pt-2 pb-2 pl-2 pr-0 widgetic-colors widgetic-radius modal-shadow"
1326
+ style="{popoverStyle} border: 1px solid hsl(var(--grey2)); z-index: 2;"
1327
+ onmousedown={(e) => e.stopPropagation()}
1328
+ onclick={(e) => e.stopPropagation()}
1329
+ >
1330
+ <!-- Color Picker Popover Content Wrapper -->
1331
+ <div class="color-picker-popover-content-wrapper flex flex-col">
1332
+
1333
+ <!-- Recent Colors Container -->
1334
+ {#if saveRecentColors && (isShiftPressed || isSpacePressed)}
1335
+ <div class="recent-colors-container" style="width: {pickerSize + sliderWidth + 14}px; margin-left: 10px; padding-top: 11px; padding-bottom: 8px;">
1336
+ {#if recentColors.length > 0}
1337
+ <div class="recent-colors-grid" style="display: grid; grid-template-columns: repeat(5, 1fr); width: 100%; gap: {swatchGap}px;">
1338
+ {#each recentColors.slice(0, 5) as {color, alpha}, i}
1339
+ <button
1340
+ class="widgetic-colors rounded-medium relative overflow-hidden animate-recent-color"
1341
+ style="width: {swatchSize}px; height: {swatchSize}px; border: 1px solid hsl(var(--forest-green)); animation-delay: {i * 0.05}s"
1342
+ onclick={() => {
1343
+ try {
1344
+ isInternalUpdate = true;
1345
+ lastKnownOpacity = showOpacityControls ? alpha : 1;
1346
+ const newRgb = hexToRgba(color);
1347
+ rgb = { ...newRgb, a: showOpacityControls ? alpha : 1 };
1348
+ opacityPercentage = Math.round((showOpacityControls ? alpha : 1) * 100);
1349
+ value = color;
1350
+ hexInput = color;
1351
+ previewColor = getRgbaColor();
1352
+ onChange(color);
1353
+ } finally {
1354
+ isInternalUpdate = false;
1355
+ }
1356
+ }}
1357
+ >
1358
+ <CheckerBoardBackground />
1359
+ <!-- svelte-ignore element_invalid_self_closing_tag -->
1360
+ <div
1361
+ class="absolute inset-0"
1362
+ style="background-color: {color}; opacity: {showOpacityControls ? alpha : 1};"
1363
+ />
1364
+ </button>
1365
+ {/each}
1366
+ </div>
1367
+ {:else}
1368
+ <div class="text-sm text-gray-500 text-center py-2">
1369
+ No recent colors
1370
+ </div>
1371
+ {/if}
1372
+ </div>
1373
+ {/if}
1374
+
1375
+ <!-- Color Picker Container (block layout, padded) -->
1376
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
1377
+ <div
1378
+ class="color-picker-wrapper relative flex flex-col"
1379
+ role="application"
1380
+ aria-label="Color picker"
1381
+ onmousedown={handleDragStart}
1382
+ style="padding: 0;"
1383
+ >
1384
+ <!-- Picker row: 8px left/top padding, 8px right padding for hue bar -->
1385
+ <div style="display: flex; flex-direction: row; align-items: flex-start;">
1386
+ <ColorSelect
1387
+ bind:this={colorSelectRef}
1388
+ bind:rgb
1389
+ bind:a={lastKnownOpacity}
1390
+ onchange={handleColorChange}
1391
+ alpha={true}
1392
+ {pickerSize}
1393
+ {sliderWidth}
1394
+ />
1395
+ <!-- 8px right padding for hue bar is handled by ColorSelect's sliderWidth prop -->
1396
+ </div>
1397
+
1398
+ <!-- Opacity bar and number input: horizontal flex row under picker/hue bar -->
1399
+ <div style="display: flex; flex-direction: row; align-items: center; margin-top: 4px;">
1400
+ <!-- Opacity bar under color picker -->
1401
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
1402
+ <div
1403
+ bind:this={opacitySliderRef}
1404
+ class="relative cursor-pointer opacity-slider"
1405
+ style="width: {pickerSize}px; height: {sliderWidth}px; margin-left: 10px;"
1406
+ onmousedown={handleOpacityMouseDown}
1407
+ >
1408
+ <div class="absolute inset-0 overflow-hidden">
1409
+ <CheckerBoardBackground />
1410
+ <div
1411
+ class="absolute inset-0"
1412
+ style="background: linear-gradient(to right,
1413
+ rgba({Math.round(rgb.r * 255)}, {Math.round(rgb.g * 255)}, {Math.round(rgb.b * 255)}, 0) 0%,
1414
+ rgba({Math.round(rgb.r * 255)}, {Math.round(rgb.g * 255)}, {Math.round(rgb.b * 255)}, 1) 100%
1415
+ );"
1416
+ ></div>
1417
+ </div>
1418
+ <div
1419
+ class="opacity-scrub absolute top-0 bottom-0 w-0.5 bg-white cursor-pointer z-20"
1420
+ style={verticalLineStyle}
1421
+ >
1422
+ <OpacitySliderArrows class="opacity-slider-arrows" />
1423
+ </div>
1424
+ </div>
1425
+ <!-- Opacity number input — square, matching swatch size -->
1426
+ <div class="opacity-number-ct flex items-center justify-center" style="width: {swatchSize + 6}px; min-width: {swatchSize + 6}px; height: {swatchSize}px; margin-left: 8px;">
1427
+ <Input
1428
+ type="number"
1429
+ min="0"
1430
+ max="100"
1431
+ class="w-full h-full text-center [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none ring-0 focus:ring-0 shadow-none widgetic-colors subtext"
1432
+ style="border: 1px solid hsl(var(--forest-green)); color: hsl(var(--grey7)); padding-left: 8px; padding-right: 0;"
1433
+ value={opacityPercentage}
1434
+ oninput={(e) => {
1435
+ e.stopPropagation();
1436
+ updateOpacityFromInput(e);
1437
+ }}
1438
+ onkeydown={(e) => {
1439
+ if (e.key === 'Shift') isShiftPressed = true;
1440
+ e.stopPropagation();
1441
+ }}
1442
+ onkeyup={(e) => {
1443
+ if (e.key === 'Shift') isShiftPressed = false;
1444
+ e.stopPropagation();
1445
+ }}
1446
+ onclick={(e) => {
1447
+ e.stopPropagation();
1448
+ }}
1449
+ />
1450
+ </div>
1451
+ </div>
1452
+ </div> <!-- end of color-picker -->
1453
+
1454
+ </div> <!-- end of color-picker-popover-content-wrapper -->
1455
+ </div>
1456
+ </Portal>
1457
+ {/if}
1458
+ </div>
1459
+ </div> <!-- end of color-preview-button-container -->
1460
+
1461
+ </div>
1462
+ </div>
1463
+ </div>
1464
+ </div>
1465
+ </div>
1466
+
1467
+ <style>
1468
+ /**
1469
+ * Component Styling
1470
+ * - Manages z-index stacking
1471
+ * - Handles drag interactions
1472
+ * - Controls visibility states
1473
+ * - Ensures proper event handling
1474
+ */
1475
+
1476
+ .color-input-main-container:hover {
1477
+ border-color: hsl(var(--forest-green)) !important;
1478
+ }
1479
+
1480
+ /* Add cursor styles for dragging */
1481
+ :global(.color-picker-wrapper canvas),
1482
+ .opacity-slider {
1483
+ cursor: grab !important;
1484
+ user-select: none !important;
1485
+ }
1486
+
1487
+ :global(.color-picker-wrapper canvas:active),
1488
+ .opacity-slider:active {
1489
+ cursor: grabbing !important;
1490
+ user-select: none !important;
1491
+ }
1492
+
1493
+ /* Prevent cursor from changing during drag */
1494
+ :global(.color-picker-wrapper *),
1495
+ .opacity-slider * {
1496
+ cursor: inherit !important;
1497
+ }
1498
+
1499
+ /* Add a class when dragging starts */
1500
+ .opacity-slider:active * {
1501
+ cursor: grabbing !important;
1502
+ }
1503
+
1504
+ /* Backdrop covers the entire viewport to catch outside clicks */
1505
+ .color-picker-backdrop {
1506
+ position: fixed;
1507
+ inset: 0;
1508
+ z-index: 99998;
1509
+ pointer-events: auto;
1510
+ }
1511
+
1512
+ /* Popover content — fixed position relative to viewport */
1513
+ .color-picker-popover-content {
1514
+ position: fixed;
1515
+ z-index: 99999;
1516
+ margin: 0;
1517
+ padding: 8px;
1518
+ background: hsl(var(--grey1));
1519
+ border: 1px solid hsl(var(--forest-green));
1520
+ border-radius: var(--radius-small);
1521
+ box-shadow: 0px 0px 15px 3px rgba(0, 0, 0, 0.1);
1522
+ pointer-events: auto;
1523
+ }
1524
+
1525
+ /* Update input styles */
1526
+ :global(input.bg-transparent) {
1527
+ background: transparent !important;
1528
+ border: none !important;
1529
+ }
1530
+
1531
+ :global(input.bg-transparent:focus),
1532
+ :global(input.bg-transparent:focus-visible) {
1533
+ outline: none !important;
1534
+ box-shadow: none !important;
1535
+ }
1536
+
1537
+ /* Update button styles */
1538
+ :global(button.rounded-md) {
1539
+ border-radius: var(--radius) !important;
1540
+ }
1541
+
1542
+ /* Ensure opacity slider border is visible */
1543
+ :global(.opacity-slider) {
1544
+ border: 1px solid hsl(var(--forest-green)) !important;
1545
+ }
1546
+
1547
+ /* Updated animation for individual recent colors */
1548
+ @keyframes zoomIn {
1549
+ from {
1550
+ transform: scale(0.6);
1551
+ opacity: 0;
1552
+ }
1553
+ to {
1554
+ transform: scale(1);
1555
+ opacity: 1;
1556
+ }
1557
+ }
1558
+
1559
+ :global(.animate-recent-color) {
1560
+ animation: zoomIn 0.2s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
1561
+ transform-origin: center center;
1562
+ }
1563
+
1564
+ /* Add these styles */
1565
+ .color-preview-button {
1566
+ outline: none !important;
1567
+ box-shadow: none !important;
1568
+ }
1569
+
1570
+ .color-preview-button:focus {
1571
+ outline: none !important;
1572
+ box-shadow: none !important;
1573
+ }
1574
+
1575
+ .color-preview-button::after {
1576
+ content: '';
1577
+ position: absolute;
1578
+ inset: 0;
1579
+ border-radius: inherit;
1580
+ border: 1px solid hsl(var(--grey3));
1581
+ pointer-events: none;
1582
+ }
1583
+ </style>
1584
+