@developer_tribe/react-builder 1.0.3 → 1.0.5

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 (532) hide show
  1. package/README.md +6 -0
  2. package/dist/android.svg +43 -0
  3. package/dist/apple.svg +16 -0
  4. package/dist/assets/samples/getSamples.d.ts +1 -0
  5. package/dist/attributes-editor/Field.d.ts +2 -1
  6. package/dist/attributes-editor/SizeField.d.ts +9 -0
  7. package/dist/background.jpg +0 -0
  8. package/dist/build-components/BIcon/BIcon.d.ts +5 -0
  9. package/dist/build-components/BIcon/BIconProps.generated.d.ts +55 -0
  10. package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +5 -0
  11. package/dist/build-components/Button/ButtonProps.generated.d.ts +5 -0
  12. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +5 -0
  13. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +5 -0
  14. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +5 -0
  15. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +5 -0
  16. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +5 -0
  17. package/dist/build-components/Image/ImageProps.generated.d.ts +5 -0
  18. package/dist/build-components/Main/Main.d.ts +5 -0
  19. package/dist/build-components/Main/MainProps.generated.d.ts +48 -0
  20. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +5 -0
  21. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +5 -1
  22. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +5 -0
  23. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +6 -3
  24. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +5 -0
  25. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +6 -1
  26. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +5 -0
  27. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +5 -1
  28. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +5 -0
  29. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +5 -0
  30. package/dist/build-components/PaywallBackground/PaywallBackground.d.ts +5 -0
  31. package/dist/build-components/PaywallBackground/PaywallBackgroundProps.generated.d.ts +47 -0
  32. package/dist/build-components/PaywallCloseButton/PaywallCloseButton.d.ts +5 -0
  33. package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +56 -0
  34. package/dist/build-components/PaywallOptions/PaywallOptionButton.d.ts +9 -0
  35. package/dist/build-components/PaywallOptions/PaywallOptions.d.ts +5 -0
  36. package/dist/build-components/PaywallOptions/PaywallOptionsProps.generated.d.ts +47 -0
  37. package/dist/build-components/PaywallOptions/usePaywallOptionParamsFactory.d.ts +10 -0
  38. package/dist/build-components/PaywallProvider/PaywallProvider.d.ts +5 -0
  39. package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +47 -0
  40. package/dist/build-components/PaywallSubscriButton/PaywallSubscriButton.d.ts +5 -0
  41. package/dist/build-components/PaywallSubscriButton/PaywallSubscriButtonProps.generated.d.ts +50 -0
  42. package/dist/build-components/PaywallSubscribeButton/PaywallSubscribeButton.d.ts +5 -0
  43. package/dist/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.d.ts +50 -0
  44. package/dist/build-components/RadioButton/RadioButton.d.ts +11 -0
  45. package/dist/build-components/RadioButton/RadioButtonProps.generated.d.ts +50 -0
  46. package/dist/build-components/RenderNode.generated.d.ts +1 -1
  47. package/dist/build-components/Text/TextProps.generated.d.ts +5 -0
  48. package/dist/build-components/View/ViewProps.generated.d.ts +5 -0
  49. package/dist/build-components/index.d.ts +10 -1
  50. package/dist/build-components/patterns.generated.d.ts +5030 -723
  51. package/dist/components/AttributesEditorPanel.d.ts +2 -1
  52. package/dist/components/Builder.d.ts +1 -1
  53. package/dist/components/BuilderButton.d.ts +1 -1
  54. package/dist/components/BuilderProvider.d.ts +15 -0
  55. package/dist/components/EditorHeader.d.ts +1 -1
  56. package/dist/components/Icon.generated.d.ts +11 -0
  57. package/dist/components/JsonTextEditor.d.ts +9 -0
  58. package/dist/components/LocalizationParamsProvider.d.ts +11 -0
  59. package/dist/components/ParamsProvider.d.ts +14 -0
  60. package/dist/hooks/useLocalizationParams.d.ts +1 -0
  61. package/dist/hooks/useLocalize.d.ts +2 -0
  62. package/dist/hooks/useParams.d.ts +1 -0
  63. package/dist/hooks/useSafeAreaViewStyle.d.ts +1 -0
  64. package/dist/index.cjs.js +5 -5
  65. package/dist/index.cjs.js.map +1 -1
  66. package/dist/index.d.ts +8 -0
  67. package/dist/index.esm.js +5 -5
  68. package/dist/index.esm.js.map +1 -1
  69. package/dist/index.native.cjs.js +29 -0
  70. package/dist/index.native.cjs.js.map +1 -0
  71. package/dist/index.native.d.ts +31 -0
  72. package/dist/index.native.esm.js +29 -0
  73. package/dist/index.native.esm.js.map +1 -0
  74. package/dist/mockOS/components/MockOSRouter.d.ts +1 -1
  75. package/dist/mockOS/context/MockOSContext.d.ts +4 -18
  76. package/dist/mockOS/context/MockOSContextBase.d.ts +21 -0
  77. package/dist/mockOS/index.d.ts +3 -2
  78. package/dist/modals/BenefitEditModal.d.ts +13 -0
  79. package/dist/modals/BenefitPresetsModal.d.ts +9 -0
  80. package/dist/modals/IconPickerModal.d.ts +9 -0
  81. package/dist/modals/MockableFeatureModal.d.ts +6 -0
  82. package/dist/modals/ProductEditModal.d.ts +9 -0
  83. package/dist/modals/ProductPresetsModal.d.ts +9 -0
  84. package/dist/modals/index.d.ts +6 -0
  85. package/dist/pages/ProjectPage.d.ts +3 -2
  86. package/dist/pages/tabs/BuilderPanel.d.ts +1 -1
  87. package/dist/pages/tabs/SideTool.d.ts +3 -2
  88. package/dist/paywall/hooks/index.d.ts +5 -0
  89. package/dist/paywall/hooks/useCalculateLocalizedPrice.d.ts +4 -0
  90. package/dist/paywall/hooks/useCarouselOptionsSeperator.d.ts +6 -0
  91. package/dist/paywall/hooks/useCloseStatusPaywall.d.ts +4 -0
  92. package/dist/paywall/hooks/useDiscountRate.d.ts +4 -0
  93. package/dist/paywall/hooks/usePaywallCounter.d.ts +4 -0
  94. package/dist/paywall/types/benefits.d.ts +14 -0
  95. package/dist/paywall/types/paywall-types.d.ts +43 -0
  96. package/dist/store.d.ts +31 -1
  97. package/dist/styles.css +1 -1
  98. package/dist/types/Icons.d.ts +2 -0
  99. package/dist/types/Project.d.ts +5 -0
  100. package/dist/utils/analyseNode.d.ts +1 -4
  101. package/dist/utils/analyseNodeByPatterns.d.ts +16 -0
  102. package/dist/utils/analyseNodeStructural.d.ts +11 -0
  103. package/dist/utils/extractImageStyle.d.ts +2 -1
  104. package/dist/utils/extractViewStyle.d.ts +1 -2
  105. package/dist/utils/findNodeByKeyNested.d.ts +2 -0
  106. package/dist/utils/nodeGuards.d.ts +5 -0
  107. package/dist/utils/novaToJson.d.ts +5 -0
  108. package/dist/utils/patterns.d.ts +14 -0
  109. package/dist/utils/replaceLocalizationParams.d.ts +1 -0
  110. package/dist/utils/selection.d.ts +7 -0
  111. package/dist/utils/useMergedStyle.d.ts +2 -0
  112. package/package.json +33 -6
  113. package/scripts/prebuild/assets/icon.template +71 -0
  114. package/scripts/prebuild/build-components.js +5 -0
  115. package/scripts/prebuild/icon-generator.js +206 -0
  116. package/scripts/prebuild/prebuild.js +10 -1
  117. package/scripts/prebuild/utils/createComponentTsx.js +6 -3
  118. package/scripts/prebuild/utils/createGeneratedProps.js +4 -2
  119. package/scripts/prebuild/utils/createRenderNodeGenerated.js +43 -8
  120. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +20 -9
  121. package/scripts/prebuild/utils/validatePatternJson.js +3 -2
  122. package/src/.DS_Store +0 -0
  123. package/src/AttributesEditor.tsx +185 -36
  124. package/src/DeviceMockFrame.tsx +41 -43
  125. package/src/RenderPage.tsx +6 -43
  126. package/src/assets/benefits.json +24 -0
  127. package/src/assets/iconset/activity-heart.svg +3 -0
  128. package/src/assets/iconset/activity.svg +3 -0
  129. package/src/assets/iconset/alert-circle.svg +3 -0
  130. package/src/assets/iconset/alert-triangle.svg +3 -0
  131. package/src/assets/iconset/anchor.svg +3 -0
  132. package/src/assets/iconset/archive.svg +3 -0
  133. package/src/assets/iconset/arrow-down.svg +3 -0
  134. package/src/assets/iconset/arrow-left.svg +3 -0
  135. package/src/assets/iconset/arrow-narrow-down-left.svg +3 -0
  136. package/src/assets/iconset/arrow-narrow-up-right.svg +3 -0
  137. package/src/assets/iconset/arrow-right-smooth.svg +4 -0
  138. package/src/assets/iconset/arrow-right.svg +7 -0
  139. package/src/assets/iconset/asterisk-01.svg +3 -0
  140. package/src/assets/iconset/asterisk-02.svg +3 -0
  141. package/src/assets/iconset/at-sign.svg +3 -0
  142. package/src/assets/iconset/award.svg +4 -0
  143. package/src/assets/iconset/battery-charging.svg +6 -0
  144. package/src/assets/iconset/bell-01.svg +5 -0
  145. package/src/assets/iconset/bell-02.svg +3 -0
  146. package/src/assets/iconset/bell-ringing-02.svg +3 -0
  147. package/src/assets/iconset/bookmark-add.svg +3 -0
  148. package/src/assets/iconset/bookmark-check.svg +3 -0
  149. package/src/assets/iconset/bookmark-minus.svg +3 -0
  150. package/src/assets/iconset/bookmark-x.svg +3 -0
  151. package/src/assets/iconset/bookmark.svg +3 -0
  152. package/src/assets/iconset/bubble.svg +5 -0
  153. package/src/assets/iconset/building-01.svg +3 -0
  154. package/src/assets/iconset/building-02.svg +3 -0
  155. package/src/assets/iconset/building-03.svg +3 -0
  156. package/src/assets/iconset/building-04.svg +3 -0
  157. package/src/assets/iconset/building-05.svg +3 -0
  158. package/src/assets/iconset/building-06.svg +3 -0
  159. package/src/assets/iconset/building-07.svg +3 -0
  160. package/src/assets/iconset/building-08.svg +3 -0
  161. package/src/assets/iconset/building-09.svg +3 -0
  162. package/src/assets/iconset/camera-01.svg +8 -0
  163. package/src/assets/iconset/camera-steel.svg +4 -0
  164. package/src/assets/iconset/camera.svg +4 -0
  165. package/src/assets/iconset/check-circle-bold.svg +3 -0
  166. package/src/assets/iconset/check-circle-broken.svg +3 -0
  167. package/src/assets/iconset/check-circle.svg +3 -0
  168. package/src/assets/iconset/check-done-01.svg +3 -0
  169. package/src/assets/iconset/check-done-02.svg +3 -0
  170. package/src/assets/iconset/check-heart.svg +3 -0
  171. package/src/assets/iconset/check-square-broken.svg +3 -0
  172. package/src/assets/iconset/check-square.svg +3 -0
  173. package/src/assets/iconset/check-verified-01.svg +3 -0
  174. package/src/assets/iconset/check-verified-02.svg +3 -0
  175. package/src/assets/iconset/check-verified-03.svg +3 -0
  176. package/src/assets/iconset/check.svg +3 -0
  177. package/src/assets/iconset/checkbox.svg +4 -0
  178. package/src/assets/iconset/checkv.svg +3 -0
  179. package/src/assets/iconset/chevron-down.svg +3 -0
  180. package/src/assets/iconset/chevron-down2.svg +3 -0
  181. package/src/assets/iconset/chevron-left-2.svg +3 -0
  182. package/src/assets/iconset/chevron-left.svg +3 -0
  183. package/src/assets/iconset/chevron-right-empty.svg +3 -0
  184. package/src/assets/iconset/chevron-right-smooth.svg +3 -0
  185. package/src/assets/iconset/chevron-right.svg +3 -0
  186. package/src/assets/iconset/chevron-up.svg +3 -0
  187. package/src/assets/iconset/circle.svg +32 -0
  188. package/src/assets/iconset/clock-fast-forward.svg +10 -0
  189. package/src/assets/iconset/clock.svg +3 -0
  190. package/src/assets/iconset/close-circle.svg +3 -0
  191. package/src/assets/iconset/close.svg +3 -0
  192. package/src/assets/iconset/cloud-01.svg +5 -0
  193. package/src/assets/iconset/cloud-blank-01.svg +3 -0
  194. package/src/assets/iconset/cloud-blank-02.svg +3 -0
  195. package/src/assets/iconset/coin.svg +5 -0
  196. package/src/assets/iconset/coins-02.svg +3 -0
  197. package/src/assets/iconset/colors.svg +3 -0
  198. package/src/assets/iconset/copy-01.svg +3 -0
  199. package/src/assets/iconset/copy-02.svg +3 -0
  200. package/src/assets/iconset/copy-03.svg +3 -0
  201. package/src/assets/iconset/copy-04.svg +3 -0
  202. package/src/assets/iconset/copy-05.svg +3 -0
  203. package/src/assets/iconset/copy-06.svg +3 -0
  204. package/src/assets/iconset/copy-07.svg +3 -0
  205. package/src/assets/iconset/corner-down-right.svg +3 -0
  206. package/src/assets/iconset/crypto-bold.svg +4 -0
  207. package/src/assets/iconset/delete-icon.svg +5 -0
  208. package/src/assets/iconset/diamond.svg +3 -0
  209. package/src/assets/iconset/dice-3.svg +3 -0
  210. package/src/assets/iconset/divide-01.svg +3 -0
  211. package/src/assets/iconset/divide-02.svg +3 -0
  212. package/src/assets/iconset/divide-03.svg +3 -0
  213. package/src/assets/iconset/document-check-bold.svg +4 -0
  214. package/src/assets/iconset/dots-circle.svg +10 -0
  215. package/src/assets/iconset/dots-grid.svg +11 -0
  216. package/src/assets/iconset/dots-horizontal.svg +5 -0
  217. package/src/assets/iconset/dots-vertical.svg +5 -0
  218. package/src/assets/iconset/download-01.svg +3 -0
  219. package/src/assets/iconset/download-02.svg +3 -0
  220. package/src/assets/iconset/download-03.svg +3 -0
  221. package/src/assets/iconset/edit-03.svg +3 -0
  222. package/src/assets/iconset/edit-04.svg +3 -0
  223. package/src/assets/iconset/edit-05.svg +3 -0
  224. package/src/assets/iconset/element-3.svg +6 -0
  225. package/src/assets/iconset/ellipse-127.svg +3 -0
  226. package/src/assets/iconset/exclaimation-circle.svg +8 -0
  227. package/src/assets/iconset/eye-off-line.svg +5 -0
  228. package/src/assets/iconset/face-smile.svg +5 -0
  229. package/src/assets/iconset/file-04.svg +3 -0
  230. package/src/assets/iconset/file-05.svg +3 -0
  231. package/src/assets/iconset/file-check-02.svg +3 -0
  232. package/src/assets/iconset/file-plus-01.svg +5 -0
  233. package/src/assets/iconset/file-shield-02.svg +5 -0
  234. package/src/assets/iconset/filter-funnel-01.svg +3 -0
  235. package/src/assets/iconset/flag-03.svg +3 -0
  236. package/src/assets/iconset/flash.svg +3 -0
  237. package/src/assets/iconset/folder-plus.svg +3 -0
  238. package/src/assets/iconset/folder.svg +3 -0
  239. package/src/assets/iconset/gallery.svg +3 -0
  240. package/src/assets/iconset/globe-01.svg +3 -0
  241. package/src/assets/iconset/globe-04.svg +5 -0
  242. package/src/assets/iconset/globe-bold.svg +4 -0
  243. package/src/assets/iconset/guard.svg +3 -0
  244. package/src/assets/iconset/headphones-01.svg +3 -0
  245. package/src/assets/iconset/headphones-02.svg +5 -0
  246. package/src/assets/iconset/headset-bold.svg +4 -0
  247. package/src/assets/iconset/heart-bold.svg +3 -0
  248. package/src/assets/iconset/heart.svg +3 -0
  249. package/src/assets/iconset/help-circle.svg +5 -0
  250. package/src/assets/iconset/home-2.svg +4 -0
  251. package/src/assets/iconset/home-line.svg +3 -0
  252. package/src/assets/iconset/hourglass-02.svg +5 -0
  253. package/src/assets/iconset/image-01.svg +5 -0
  254. package/src/assets/iconset/image-03.svg +3 -0
  255. package/src/assets/iconset/image.svg +4 -0
  256. package/src/assets/iconset/inbox-01.svg +3 -0
  257. package/src/assets/iconset/inbox-arrow-down.svg +3 -0
  258. package/src/assets/iconset/info-circle.svg +3 -0
  259. package/src/assets/iconset/keyboard-line.svg +9 -0
  260. package/src/assets/iconset/lamp-charge.svg +5 -0
  261. package/src/assets/iconset/layer.svg +3 -0
  262. package/src/assets/iconset/light.svg +6 -0
  263. package/src/assets/iconset/like-dislike.svg +6 -0
  264. package/src/assets/iconset/lock-03.svg +3 -0
  265. package/src/assets/iconset/logout.svg +3 -0
  266. package/src/assets/iconset/magicpen.svg +7 -0
  267. package/src/assets/iconset/mail-01.svg +5 -0
  268. package/src/assets/iconset/mail.svg +3 -0
  269. package/src/assets/iconset/marker.svg +3 -0
  270. package/src/assets/iconset/medal-star.svg +5 -0
  271. package/src/assets/iconset/menu-04.svg +3 -0
  272. package/src/assets/iconset/menu.svg +5 -0
  273. package/src/assets/iconset/message-circle-01.svg +5 -0
  274. package/src/assets/iconset/message-plus-circle.svg +3 -0
  275. package/src/assets/iconset/message-question-circle.svg +5 -0
  276. package/src/assets/iconset/message-text-circle-01.svg +5 -0
  277. package/src/assets/iconset/message-text-square-02.svg +3 -0
  278. package/src/assets/iconset/message-x-square.svg +3 -0
  279. package/src/assets/iconset/microphone-02.svg +3 -0
  280. package/src/assets/iconset/microphone-slash.svg +8 -0
  281. package/src/assets/iconset/mirror.svg +4 -0
  282. package/src/assets/iconset/moon-01.svg +3 -0
  283. package/src/assets/iconset/moon-bold.svg +3 -0
  284. package/src/assets/iconset/mouse-circle.svg +4 -0
  285. package/src/assets/iconset/move.svg +5 -0
  286. package/src/assets/iconset/notification-fill.svg +3 -0
  287. package/src/assets/iconset/notification-text.svg +3 -0
  288. package/src/assets/iconset/notification.svg +5 -0
  289. package/src/assets/iconset/pdf-01.svg +6 -0
  290. package/src/assets/iconset/pencil-01.svg +5 -0
  291. package/src/assets/iconset/phone-01.svg +3 -0
  292. package/src/assets/iconset/phone-arrow-down-left.svg +4 -0
  293. package/src/assets/iconset/phone-arrow-up-right.svg +8 -0
  294. package/src/assets/iconset/phone-hang-up.svg +5 -0
  295. package/src/assets/iconset/phone-hangup2.svg +8 -0
  296. package/src/assets/iconset/phone-incoming-01.svg +3 -0
  297. package/src/assets/iconset/phone-outgoing-01.svg +3 -0
  298. package/src/assets/iconset/phone-plus.svg +3 -0
  299. package/src/assets/iconset/phone-x.svg +3 -0
  300. package/src/assets/iconset/phone.svg +3 -0
  301. package/src/assets/iconset/plus-circle.svg +3 -0
  302. package/src/assets/iconset/plus.svg +4 -0
  303. package/src/assets/iconset/printer.svg +3 -0
  304. package/src/assets/iconset/question-mark-circle.svg +5 -0
  305. package/src/assets/iconset/refresh-ccw-01.svg +3 -0
  306. package/src/assets/iconset/refresh-cw-01.svg +3 -0
  307. package/src/assets/iconset/refresh-cw-04.svg +3 -0
  308. package/src/assets/iconset/refresh-right-square-bold.svg +3 -0
  309. package/src/assets/iconset/remove-circle.svg +12 -0
  310. package/src/assets/iconset/repeat-04.svg +3 -0
  311. package/src/assets/iconset/repeat-bold.svg +3 -0
  312. package/src/assets/iconset/ruler-pen.svg +4 -0
  313. package/src/assets/iconset/search-lg.svg +3 -0
  314. package/src/assets/iconset/search-md.svg +5 -0
  315. package/src/assets/iconset/search-refraction.svg +5 -0
  316. package/src/assets/iconset/search.svg +3 -0
  317. package/src/assets/iconset/send-01.svg +3 -0
  318. package/src/assets/iconset/send-02.svg +5 -0
  319. package/src/assets/iconset/send-diagonal.svg +3 -0
  320. package/src/assets/iconset/setting-2.svg +4 -0
  321. package/src/assets/iconset/settings-02.svg +4 -0
  322. package/src/assets/iconset/settings-04.svg +5 -0
  323. package/src/assets/iconset/settings-2.svg +4 -0
  324. package/src/assets/iconset/settings-cog.svg +3 -0
  325. package/src/assets/iconset/settings.svg +4 -0
  326. package/src/assets/iconset/share-01.svg +4 -0
  327. package/src/assets/iconset/share-03.svg +3 -0
  328. package/src/assets/iconset/share-04.svg +3 -0
  329. package/src/assets/iconset/share-05.svg +5 -0
  330. package/src/assets/iconset/share-06.svg +3 -0
  331. package/src/assets/iconset/share-bold.svg +3 -0
  332. package/src/assets/iconset/shield-01.svg +3 -0
  333. package/src/assets/iconset/shield-bold.svg +3 -0
  334. package/src/assets/iconset/solar-check.svg +3 -0
  335. package/src/assets/iconset/speaker-wave.svg +9 -0
  336. package/src/assets/iconset/speaker.svg +5 -0
  337. package/src/assets/iconset/speedometer-03.svg +3 -0
  338. package/src/assets/iconset/star-rounded.svg +3 -0
  339. package/src/assets/iconset/star.svg +3 -0
  340. package/src/assets/iconset/sun.svg +5 -0
  341. package/src/assets/iconset/target-03.svg +3 -0
  342. package/src/assets/iconset/text-input.svg +3 -0
  343. package/src/assets/iconset/translate.svg +7 -0
  344. package/src/assets/iconset/trash-02.svg +3 -0
  345. package/src/assets/iconset/trash-03.svg +5 -0
  346. package/src/assets/iconset/trash-04.svg +3 -0
  347. package/src/assets/iconset/trash.svg +7 -0
  348. package/src/assets/iconset/trush-square-bold.svg +3 -0
  349. package/src/assets/iconset/unlimited.svg +3 -0
  350. package/src/assets/iconset/user-circle.svg +3 -0
  351. package/src/assets/iconset/user-jogging.svg +3 -0
  352. package/src/assets/iconset/user-plus-01.svg +5 -0
  353. package/src/assets/iconset/user-square.svg +5 -0
  354. package/src/assets/iconset/user-x-01.svg +5 -0
  355. package/src/assets/iconset/user-x-02.svg +3 -0
  356. package/src/assets/iconset/user2.svg +3 -0
  357. package/src/assets/iconset/users-02.svg +5 -0
  358. package/src/assets/iconset/users-speaker.svg +7 -0
  359. package/src/assets/iconset/verify.svg +3 -0
  360. package/src/assets/iconset/voice-cricle.svg +8 -0
  361. package/src/assets/iconset/x-circle.svg +3 -0
  362. package/src/assets/iconset/x-close.svg +3 -0
  363. package/src/assets/iconset/x-sm.svg +3 -0
  364. package/src/assets/iconset/zap.svg +3 -0
  365. package/src/assets/images/background.jpg +0 -0
  366. package/src/assets/loading_animation.json +1 -0
  367. package/src/assets/products.json +98 -0
  368. package/src/assets/samples/carousel-sample.json +1 -0
  369. package/src/assets/samples/getSamples.ts +39 -66
  370. package/src/assets/samples/paywall-1.json +220 -0
  371. package/src/assets/samples/simple-1.json +1 -0
  372. package/src/assets/samples/simple-2.json +1 -0
  373. package/src/assets/samples/vpn-onboard-1.json +324 -720
  374. package/src/assets/samples/vpn-onboard-2.json +299 -683
  375. package/src/assets/samples/vpn-onboard-3.json +270 -680
  376. package/src/assets/samples/vpn-onboard-4.json +270 -681
  377. package/src/assets/samples/vpn-onboard-5.json +408 -893
  378. package/src/assets/samples/vpn-onboard-6.json +279 -594
  379. package/src/attributes-editor/Field.tsx +48 -160
  380. package/src/attributes-editor/SizeField.tsx +184 -0
  381. package/src/attributes-editor/SpecialCategorySection.tsx +11 -4
  382. package/src/build-components/BIcon/BIcon.tsx +56 -0
  383. package/src/build-components/BIcon/BIconProps.generated.ts +82 -0
  384. package/src/build-components/BIcon/pattern.json +47 -0
  385. package/src/build-components/BackgroundImage/BackgroundImage.tsx +7 -17
  386. package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +5 -0
  387. package/src/build-components/BackgroundImage/pattern.json +2 -2
  388. package/src/build-components/Button/Button.tsx +20 -9
  389. package/src/build-components/Button/ButtonProps.generated.ts +5 -0
  390. package/src/build-components/Button/pattern.json +4 -1
  391. package/src/build-components/Carousel/Carousel.tsx +7 -9
  392. package/src/build-components/Carousel/CarouselProps.generated.ts +5 -0
  393. package/src/build-components/Carousel/pattern.json +2 -2
  394. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +5 -0
  395. package/src/build-components/CarouselButtons/pattern.json +1 -1
  396. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +5 -0
  397. package/src/build-components/CarouselDots/pattern.json +1 -1
  398. package/src/build-components/CarouselItem/CarouselItem.tsx +1 -1
  399. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +5 -0
  400. package/src/build-components/CarouselItem/pattern.json +1 -1
  401. package/src/build-components/CarouselProvider/CarouselProvider.tsx +1 -1
  402. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +5 -0
  403. package/src/build-components/CarouselProvider/pattern.json +1 -1
  404. package/src/build-components/Image/Image.tsx +11 -18
  405. package/src/build-components/Image/ImageProps.generated.ts +5 -0
  406. package/src/build-components/Image/pattern.json +2 -10
  407. package/src/build-components/Main/Main.tsx +61 -0
  408. package/src/build-components/Main/MainProps.generated.ts +64 -0
  409. package/src/build-components/Main/pattern.json +35 -0
  410. package/src/build-components/Onboard/OnboardProps.generated.ts +5 -0
  411. package/src/build-components/OnboardButton/OnboardButton.tsx +0 -3
  412. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +5 -1
  413. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +5 -0
  414. package/src/build-components/OnboardDot/OnboardDot.tsx +59 -39
  415. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +6 -3
  416. package/src/build-components/OnboardDot/pattern.json +2 -18
  417. package/src/build-components/OnboardFooter/OnboardFooter.tsx +28 -15
  418. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +5 -0
  419. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +6 -1
  420. package/src/build-components/OnboardItem/OnboardItem.tsx +2 -12
  421. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +5 -0
  422. package/src/build-components/OnboardProvider/OnboardProvider.tsx +1 -8
  423. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +5 -1
  424. package/src/build-components/OnboardProvider/pattern.json +6 -16
  425. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +5 -0
  426. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +5 -0
  427. package/src/build-components/PaywallBackground/PaywallBackground.tsx +47 -0
  428. package/src/build-components/PaywallBackground/PaywallBackgroundProps.generated.ts +63 -0
  429. package/src/build-components/PaywallBackground/pattern.json +16 -0
  430. package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +62 -0
  431. package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +83 -0
  432. package/src/build-components/PaywallCloseButton/pattern.json +23 -0
  433. package/src/build-components/PaywallOptions/PaywallOptionButton.tsx +64 -0
  434. package/src/build-components/PaywallOptions/PaywallOptions.tsx +92 -0
  435. package/src/build-components/PaywallOptions/PaywallOptionsProps.generated.ts +63 -0
  436. package/src/build-components/PaywallOptions/pattern.json +22 -0
  437. package/src/build-components/PaywallOptions/usePaywallOptionParamsFactory.ts +42 -0
  438. package/src/build-components/PaywallProvider/PaywallProvider.tsx +78 -0
  439. package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +63 -0
  440. package/src/build-components/PaywallProvider/pattern.json +24 -0
  441. package/src/build-components/PaywallSubscriButton/PaywallSubscriButton.tsx +10 -0
  442. package/src/build-components/PaywallSubscriButton/PaywallSubscriButtonProps.generated.ts +77 -0
  443. package/src/build-components/PaywallSubscriButton/pattern.json +27 -0
  444. package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButton.tsx +60 -0
  445. package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.ts +77 -0
  446. package/src/build-components/PaywallSubscribeButton/pattern.json +27 -0
  447. package/src/build-components/RadioButton/RadioButton.tsx +123 -0
  448. package/src/build-components/RadioButton/RadioButtonProps.generated.ts +66 -0
  449. package/src/build-components/RadioButton/pattern.json +42 -0
  450. package/src/build-components/RenderNode.generated.tsx +140 -37
  451. package/src/build-components/Text/Text.tsx +12 -18
  452. package/src/build-components/Text/TextProps.generated.ts +5 -0
  453. package/src/build-components/Text/pattern.json +1 -1
  454. package/src/build-components/View/View.tsx +7 -9
  455. package/src/build-components/View/ViewProps.generated.ts +5 -0
  456. package/src/build-components/View/pattern.json +50 -4
  457. package/src/build-components/index.ts +55 -10
  458. package/src/build-components/patterns.generated.ts +5085 -745
  459. package/src/components/AttributesEditorPanel.tsx +3 -1
  460. package/src/components/Builder.tsx +64 -22
  461. package/src/components/BuilderButton.tsx +2 -7
  462. package/src/components/BuilderProvider.tsx +45 -0
  463. package/src/components/DeviceNavigationBar.tsx +1 -2
  464. package/src/components/EditorHeader.tsx +15 -2
  465. package/src/components/Icon.generated.tsx +540 -0
  466. package/src/components/JsonTextEditor.tsx +185 -0
  467. package/src/components/LocalizationParamsProvider.tsx +22 -0
  468. package/src/components/ParamsProvider.tsx +38 -0
  469. package/src/hooks/useLocalizationParams.ts +5 -0
  470. package/src/hooks/useLocalize.ts +23 -0
  471. package/src/hooks/useParams.ts +8 -0
  472. package/src/hooks/useSafeAreaViewStyle.ts +67 -0
  473. package/src/index.native.ts +75 -0
  474. package/src/index.ts +26 -0
  475. package/src/mockOS/components/MockOSRouter.tsx +12 -11
  476. package/src/mockOS/context/MockOSContext.tsx +12 -41
  477. package/src/mockOS/context/MockOSContextBase.ts +35 -0
  478. package/src/mockOS/index.ts +3 -2
  479. package/src/mockOS/managers/mockPermissionManager.ts +0 -4
  480. package/src/mockOS/managers/navigationManager.ts +1 -6
  481. package/src/modals/AddComponentModal.tsx +1 -0
  482. package/src/modals/BenefitEditModal.tsx +160 -0
  483. package/src/modals/BenefitPresetsModal.tsx +166 -0
  484. package/src/modals/ColorModal.tsx +103 -25
  485. package/src/modals/IconPickerModal.tsx +109 -0
  486. package/src/modals/LocalicationModal.tsx +4 -5
  487. package/src/modals/MockableFeatureModal.tsx +292 -0
  488. package/src/modals/Modal.tsx +8 -1
  489. package/src/modals/ProductEditModal.tsx +215 -0
  490. package/src/modals/ProductPresetsModal.tsx +151 -0
  491. package/src/modals/index.ts +6 -0
  492. package/src/pages/ProjectPage.tsx +79 -16
  493. package/src/pages/tabs/BuilderPanel.tsx +1 -1
  494. package/src/pages/tabs/SideTool.tsx +13 -19
  495. package/src/paywall/hooks/index.ts +5 -0
  496. package/src/paywall/hooks/useCalculateLocalizedPrice.ts +6 -0
  497. package/src/paywall/hooks/useCarouselOptionsSeperator.ts +8 -0
  498. package/src/paywall/hooks/useCloseStatusPaywall.ts +6 -0
  499. package/src/paywall/hooks/useDiscountRate.ts +6 -0
  500. package/src/paywall/hooks/usePaywallCounter.ts +6 -0
  501. package/src/paywall/types/benefits.ts +44 -0
  502. package/src/paywall/types/paywall-types.ts +51 -0
  503. package/src/store.ts +157 -38
  504. package/src/styles/base/_global.scss +54 -41
  505. package/src/styles/components/_attributes-editor.scss +40 -5
  506. package/src/styles/components/_editor-shell.scss +5 -9
  507. package/src/styles/components/_ui-components.scss +2 -1
  508. package/src/styles/index.scss +5 -0
  509. package/src/styles/modals/_benefit-edit-modal.scss +17 -0
  510. package/src/styles/modals/_benefit-presets-modal.scss +79 -0
  511. package/src/styles/modals/_color-modal.scss +30 -1
  512. package/src/styles/modals/_mockable-feature-modal.scss +15 -0
  513. package/src/styles/modals/_product-edit-modal.scss +23 -0
  514. package/src/styles/modals/_product-presets-modal.scss +81 -0
  515. package/src/styles/utilities/_carousel.scss +9 -8
  516. package/src/types/Icons.ts +244 -0
  517. package/src/types/Project.ts +5 -0
  518. package/src/types/jest-globals.d.ts +13 -0
  519. package/src/utils/analyseNode.ts +22 -109
  520. package/src/utils/analyseNodeByPatterns.ts +438 -0
  521. package/src/utils/analyseNodeStructural.ts +52 -0
  522. package/src/utils/extractImageStyle.ts +3 -6
  523. package/src/utils/extractTextStyle.ts +2 -81
  524. package/src/utils/extractViewStyle.ts +39 -15
  525. package/src/utils/findNodeByKeyNested.ts +32 -0
  526. package/src/utils/isCarousel.ts +21 -5
  527. package/src/utils/nodeGuards.ts +26 -0
  528. package/src/utils/novaToJson.ts +21 -9
  529. package/src/utils/patterns.ts +62 -3
  530. package/src/utils/replaceLocalizationParams.ts +15 -0
  531. package/src/utils/selection.ts +24 -0
  532. package/src/utils/useMergedStyle.ts +16 -0
@@ -26,7 +26,6 @@ export class MockPermissionManager {
26
26
  return 'not-determined';
27
27
  }
28
28
 
29
- console.log(`[Mock OS] Permission requested: ${permission}`);
30
29
  // Set permission to trigger modal display
31
30
  this.context.setPermission(permission);
32
31
  // Default behavior: grant all permissions in mock environment
@@ -39,7 +38,6 @@ export class MockPermissionManager {
39
38
  return 'not-determined';
40
39
  }
41
40
 
42
- console.log(`[Mock OS] Permission checked: ${permission}`);
43
41
  return 'granted';
44
42
  }
45
43
 
@@ -48,7 +46,5 @@ export class MockPermissionManager {
48
46
  alert('Opening Settings\n(Mock OS context not available)');
49
47
  return;
50
48
  }
51
-
52
- console.log('[Mock OS] Opening Settings');
53
49
  }
54
50
  }
@@ -31,7 +31,6 @@ export class MockNavigationManager {
31
31
  timestamp: Date.now(),
32
32
  };
33
33
  this.stack.push(item);
34
- console.log('[Mock OS] Navigate to Home', { stack: this.stack });
35
34
  }
36
35
 
37
36
  goToSubscriptions(): void {
@@ -45,7 +44,6 @@ export class MockNavigationManager {
45
44
  timestamp: Date.now(),
46
45
  };
47
46
  this.stack.push(item);
48
- console.log('[Mock OS] Navigate to Subscriptions', { stack: this.stack });
49
47
  }
50
48
 
51
49
  goToLaunchApp(): void {
@@ -59,7 +57,6 @@ export class MockNavigationManager {
59
57
  timestamp: Date.now(),
60
58
  };
61
59
  this.stack.push(item);
62
- console.log('[Mock OS] Navigate to Launch App', { stack: this.stack });
63
60
  }
64
61
 
65
62
  goBack(): boolean {
@@ -70,11 +67,9 @@ export class MockNavigationManager {
70
67
 
71
68
  if (this.stack.length > 0) {
72
69
  const popped = this.stack.pop();
73
- console.log('[Mock OS] Go Back', { popped, stack: this.stack });
74
70
  return true;
75
71
  }
76
72
 
77
- console.log('[Mock OS] Cannot go back - stack is empty');
78
73
  return false;
79
74
  }
80
75
 
@@ -85,7 +80,7 @@ export class MockNavigationManager {
85
80
  clearStack(): void {
86
81
  this.stack = [];
87
82
  if (this.context) {
88
- console.log('[Mock OS] Navigation stack cleared');
83
+ console.info('[Mock OS] Navigation stack cleared');
89
84
  }
90
85
  }
91
86
  }
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
2
  import Modal from './Modal';
3
3
  import { getPatternByType } from '../utils/patterns';
4
+ //TODO: filtreleme kısmları karıştığı için snapshot testi ekle
4
5
 
5
6
  type AddComponentModalProps = {
6
7
  allowedChildTypes: string[];
@@ -0,0 +1,160 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import Modal from './Modal';
3
+ import type { PaywallBenefitValue } from '../paywall/types/benefits';
4
+
5
+ type ValueKind = 'string' | 'number' | 'boolean';
6
+
7
+ type BenefitEditModalProps = {
8
+ benefitKey: string;
9
+ benefitValue: PaywallBenefitValue;
10
+ index: number;
11
+ onSave: (next: { key: string; value: PaywallBenefitValue }) => void;
12
+ onClose: () => void;
13
+ };
14
+
15
+ function detectKind(value: PaywallBenefitValue): ValueKind {
16
+ if (typeof value === 'number') return 'number';
17
+ if (typeof value === 'boolean') return 'boolean';
18
+ return 'string';
19
+ }
20
+
21
+ function parseValue(
22
+ kind: ValueKind,
23
+ raw: string,
24
+ fallback: PaywallBenefitValue,
25
+ ) {
26
+ if (kind === 'boolean') return raw === 'true';
27
+ if (kind === 'number') {
28
+ const trimmed = raw.trim();
29
+ if (!trimmed) return fallback;
30
+ const normalized = trimmed.replace(',', '.');
31
+ const parsed = Number.parseFloat(normalized);
32
+ return Number.isFinite(parsed) ? parsed : fallback;
33
+ }
34
+ return raw;
35
+ }
36
+
37
+ export function BenefitEditModal({
38
+ benefitKey,
39
+ benefitValue,
40
+ index,
41
+ onSave,
42
+ onClose,
43
+ }: BenefitEditModalProps) {
44
+ const initial = useMemo(() => {
45
+ const key = (benefitKey ?? '').toString();
46
+ const kind = detectKind(benefitValue);
47
+ const raw =
48
+ kind === 'boolean'
49
+ ? benefitValue
50
+ ? 'true'
51
+ : 'false'
52
+ : kind === 'number'
53
+ ? `${benefitValue ?? ''}`
54
+ : (benefitValue ?? '').toString();
55
+ return { key, kind, raw };
56
+ }, [benefitKey, benefitValue]);
57
+
58
+ const [draft, setDraft] = useState(initial);
59
+ const canSave = draft.key.trim().length > 0;
60
+
61
+ return (
62
+ <Modal
63
+ onClose={onClose}
64
+ ariaLabelledBy="benefit-edit-modal-title"
65
+ contentClassName="benefit-edit-modal"
66
+ >
67
+ <div className="modal__header">
68
+ <h3 id="benefit-edit-modal-title" className="modal__title">
69
+ Edit benefit #{index + 1}
70
+ </h3>
71
+ <button type="button" className="editor-button" onClick={onClose}>
72
+ Close
73
+ </button>
74
+ </div>
75
+
76
+ <div className="benefit-edit-modal__body">
77
+ <div className="form-row">
78
+ <label htmlFor="benefitKey">key</label>
79
+ <input
80
+ id="benefitKey"
81
+ className="input"
82
+ value={draft.key}
83
+ onChange={(e) =>
84
+ setDraft((prev) => ({ ...prev, key: e.target.value }))
85
+ }
86
+ placeholder="unlimited_access"
87
+ />
88
+ </div>
89
+
90
+ <div className="form-row">
91
+ <label htmlFor="benefitType">type</label>
92
+ <select
93
+ id="benefitType"
94
+ className="input"
95
+ value={draft.kind}
96
+ onChange={(e) =>
97
+ setDraft((prev) => ({
98
+ ...prev,
99
+ kind: e.target.value as ValueKind,
100
+ }))
101
+ }
102
+ >
103
+ <option value="string">string</option>
104
+ <option value="number">number</option>
105
+ <option value="boolean">boolean</option>
106
+ </select>
107
+ </div>
108
+
109
+ <div className="form-row">
110
+ <label htmlFor="benefitValue">value</label>
111
+ {draft.kind === 'boolean' ? (
112
+ <select
113
+ id="benefitValue"
114
+ className="input"
115
+ value={draft.raw}
116
+ onChange={(e) =>
117
+ setDraft((prev) => ({ ...prev, raw: e.target.value }))
118
+ }
119
+ >
120
+ <option value="true">true</option>
121
+ <option value="false">false</option>
122
+ </select>
123
+ ) : (
124
+ <input
125
+ id="benefitValue"
126
+ className="input"
127
+ value={draft.raw}
128
+ onChange={(e) =>
129
+ setDraft((prev) => ({ ...prev, raw: e.target.value }))
130
+ }
131
+ placeholder={draft.kind === 'number' ? '7' : 'Unlock everything'}
132
+ />
133
+ )}
134
+ </div>
135
+
136
+ <div className="form-actions">
137
+ <button
138
+ type="button"
139
+ className="editor-button"
140
+ onClick={() =>
141
+ onSave({
142
+ key: draft.key,
143
+ value: parseValue(draft.kind, draft.raw, benefitValue),
144
+ })
145
+ }
146
+ disabled={!canSave}
147
+ title={canSave ? 'Save' : 'key is required'}
148
+ >
149
+ Save
150
+ </button>
151
+ <button type="button" className="editor-button" onClick={onClose}>
152
+ Cancel
153
+ </button>
154
+ </div>
155
+ </div>
156
+ </Modal>
157
+ );
158
+ }
159
+
160
+ export default BenefitEditModal;
@@ -0,0 +1,166 @@
1
+ import React, { useMemo } from 'react';
2
+ import Modal from './Modal';
3
+ import type {
4
+ PaywallBenefits,
5
+ PaywallBenefitValue,
6
+ } from '../paywall/types/benefits';
7
+ import presetsJson from '../assets/benefits.json';
8
+
9
+ type PresetMap = Record<string, PaywallBenefits>;
10
+ const DEFAULT_PRESET_KEY = 'Embeded Paywall';
11
+
12
+ type BenefitPresetsModalProps = {
13
+ current: PaywallBenefits;
14
+ onReplace: (next: PaywallBenefits) => void;
15
+ onMerge: (next: PaywallBenefits) => void;
16
+ onClose: () => void;
17
+ };
18
+
19
+ function normalizeBenefits(raw: unknown): PaywallBenefits {
20
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
21
+ const out: PaywallBenefits = {};
22
+ Object.entries(raw as Record<string, unknown>).forEach(([key, value]) => {
23
+ const normalizedKey = typeof key === 'string' ? key.trim() : '';
24
+ if (!normalizedKey) return;
25
+ if (
26
+ typeof value === 'string' ||
27
+ typeof value === 'number' ||
28
+ typeof value === 'boolean'
29
+ ) {
30
+ out[normalizedKey] = value as PaywallBenefitValue;
31
+ }
32
+ });
33
+ return out;
34
+ }
35
+
36
+ function getPresetMap(): PresetMap {
37
+ const raw = presetsJson as unknown as PresetMap;
38
+ const safe: PresetMap = {};
39
+ Object.entries(raw ?? {}).forEach(([key, map]) => {
40
+ const normalizedKey = typeof key === 'string' ? key.trim() : '';
41
+ if (!normalizedKey) return;
42
+ safe[normalizedKey] = normalizeBenefits(map);
43
+ });
44
+ return safe;
45
+ }
46
+
47
+ function countBenefits(map: PaywallBenefits): number {
48
+ if (!map || typeof map !== 'object') return 0;
49
+ return Object.keys(map).length;
50
+ }
51
+
52
+ export function BenefitPresetsModal({
53
+ current,
54
+ onReplace,
55
+ onMerge,
56
+ onClose,
57
+ }: BenefitPresetsModalProps) {
58
+ const presetMap = useMemo(() => getPresetMap(), []);
59
+ const presetEntries = useMemo(
60
+ () =>
61
+ Object.entries(presetMap).sort((a, b) => {
62
+ const aKey = a[0];
63
+ const bKey = b[0];
64
+ if (aKey === DEFAULT_PRESET_KEY && bKey !== DEFAULT_PRESET_KEY)
65
+ return -1;
66
+ if (bKey === DEFAULT_PRESET_KEY && aKey !== DEFAULT_PRESET_KEY)
67
+ return 1;
68
+ return aKey.localeCompare(bKey);
69
+ }),
70
+ [presetMap],
71
+ );
72
+
73
+ return (
74
+ <Modal
75
+ onClose={onClose}
76
+ ariaLabelledBy="benefit-presets-modal-title"
77
+ contentClassName="benefit-presets-modal"
78
+ >
79
+ <div className="modal__header">
80
+ <h3 id="benefit-presets-modal-title" className="modal__title">
81
+ Benefit presets
82
+ </h3>
83
+ <button type="button" className="editor-button" onClick={onClose}>
84
+ Close
85
+ </button>
86
+ </div>
87
+
88
+ <div className="benefit-presets-modal__body">
89
+ <section className="benefit-presets-modal__section">
90
+ <p className="benefit-presets-modal__section-title">
91
+ Current ({countBenefits(current)})
92
+ </p>
93
+ <p className="benefit-presets-modal__muted">
94
+ Pick a preset below to replace or merge into the current map.
95
+ </p>
96
+ </section>
97
+
98
+ <section className="benefit-presets-modal__section">
99
+ <p className="benefit-presets-modal__section-title">Presets</p>
100
+
101
+ {presetEntries.length ? (
102
+ <div className="benefit-presets-modal__grid">
103
+ {presetEntries.map(([key, map]) => {
104
+ const entries = Object.entries(map);
105
+ return (
106
+ <div key={key} className="benefit-presets-modal__card">
107
+ <div className="benefit-presets-modal__card-header">
108
+ <p className="benefit-presets-modal__card-title">{key}</p>
109
+ <p className="benefit-presets-modal__card-count">
110
+ {entries.length} benefits
111
+ </p>
112
+ </div>
113
+
114
+ <ul className="benefit-presets-modal__card-list">
115
+ {entries.slice(0, 3).map(([k, v]) => (
116
+ <li
117
+ key={k}
118
+ className="benefit-presets-modal__card-item"
119
+ >
120
+ {k}: {String(v)}
121
+ </li>
122
+ ))}
123
+ {entries.length > 3 ? (
124
+ <li className="benefit-presets-modal__card-item benefit-presets-modal__muted">
125
+ +{entries.length - 3} more…
126
+ </li>
127
+ ) : null}
128
+ </ul>
129
+
130
+ <div className="benefit-presets-modal__actions">
131
+ <button
132
+ type="button"
133
+ className="editor-button"
134
+ onClick={() => {
135
+ onReplace(map);
136
+ onClose();
137
+ }}
138
+ >
139
+ Replace
140
+ </button>
141
+ <button
142
+ type="button"
143
+ className="editor-button"
144
+ onClick={() => {
145
+ onMerge(map);
146
+ onClose();
147
+ }}
148
+ title="Merges preset into current (preset values overwrite existing keys)"
149
+ >
150
+ Merge
151
+ </button>
152
+ </div>
153
+ </div>
154
+ );
155
+ })}
156
+ </div>
157
+ ) : (
158
+ <p className="benefit-presets-modal__muted">No presets found.</p>
159
+ )}
160
+ </section>
161
+ </div>
162
+ </Modal>
163
+ );
164
+ }
165
+
166
+ export default BenefitPresetsModal;
@@ -65,7 +65,11 @@ const readSavedColors = (): string[] => {
65
65
  const stored = window.localStorage.getItem(SAVED_COLORS_KEY);
66
66
  if (!stored) return [];
67
67
  const parsed = JSON.parse(stored);
68
- return Array.isArray(parsed) ? parsed : [];
68
+ if (!Array.isArray(parsed)) return [];
69
+ return parsed
70
+ .filter((value) => typeof value === 'string')
71
+ .map((value) => value.trim().toLowerCase())
72
+ .filter(Boolean);
69
73
  } catch {
70
74
  return [];
71
75
  }
@@ -143,6 +147,7 @@ export function ColorModal({
143
147
  const [useColorNames, setUseColorNames] = useState(true);
144
148
  const colorInputRef = useRef<HTMLInputElement | null>(null);
145
149
  const colorNameToggleId = useId();
150
+ const colorPickerInputId = useId();
146
151
 
147
152
  const selectedColorPreview = useMemo(
148
153
  () => resolveProjectColorValue(value, projectColors) ?? value,
@@ -195,21 +200,10 @@ export function ColorModal({
195
200
  [savedColors],
196
201
  );
197
202
 
198
- const handleAddColorClick = () => {
199
- colorInputRef.current?.click();
200
- };
201
-
202
- const handlePreviewKeyDown = (
203
- event: React.KeyboardEvent<HTMLSpanElement>,
204
- ) => {
205
- if (event.key !== 'Enter' && event.key !== ' ') return;
206
- event.preventDefault();
207
- handleAddColorClick();
208
- };
209
-
210
- const handleColorPicked = (event: React.ChangeEvent<HTMLInputElement>) => {
211
- const picked = event.target.value;
203
+ const applyPickedColor = (raw: string | undefined) => {
204
+ const picked = raw?.trim().toLowerCase();
212
205
  if (!picked) return;
206
+
213
207
  setSavedColors((prev) => {
214
208
  if (prev.includes(picked)) return prev;
215
209
  const next = [...prev, picked];
@@ -218,7 +212,85 @@ export function ColorModal({
218
212
  });
219
213
  onSelect(picked);
220
214
  onClose();
221
- event.target.value = '';
215
+ };
216
+
217
+ const openPickerWithTempInput = () => {
218
+ if (typeof document === 'undefined') return;
219
+
220
+ const temp = document.createElement('input');
221
+ temp.type = 'color';
222
+ temp.value = selectedColorPreview?.toString() || '#000000';
223
+
224
+ // Keep it in the DOM and "not display:none" so Safari/iOS reliably opens it.
225
+ temp.style.position = 'fixed';
226
+ temp.style.left = '-1000px';
227
+ temp.style.top = '0';
228
+ temp.style.width = '40px';
229
+ temp.style.height = '40px';
230
+ temp.style.opacity = '0';
231
+
232
+ const cleanup = () => {
233
+ temp.removeEventListener('change', onChange);
234
+ temp.removeEventListener('input', onChange);
235
+ temp.removeEventListener('blur', cleanup);
236
+ temp.remove();
237
+ };
238
+
239
+ const onChange = () => {
240
+ applyPickedColor(temp.value);
241
+ cleanup();
242
+ };
243
+
244
+ temp.addEventListener('change', onChange);
245
+ temp.addEventListener('input', onChange);
246
+ temp.addEventListener('blur', cleanup);
247
+
248
+ document.body.appendChild(temp);
249
+ try {
250
+ temp.focus({ preventScroll: true });
251
+ } catch {
252
+ // no-op
253
+ }
254
+ // Next tick helps some browsers recognize it as a user-gesture flow.
255
+ requestAnimationFrame(() => temp.click());
256
+ };
257
+
258
+ const handleAddColorClick = () => {
259
+ const input = colorInputRef.current;
260
+
261
+ // Prefer showPicker when available (Chromium).
262
+ const maybeShowPicker = input
263
+ ? (
264
+ input as HTMLInputElement & {
265
+ showPicker?: () => void;
266
+ }
267
+ ).showPicker
268
+ : undefined;
269
+
270
+ if (input && typeof maybeShowPicker === 'function') {
271
+ try {
272
+ input.focus({ preventScroll: true });
273
+ } catch {
274
+ // no-op
275
+ }
276
+ maybeShowPicker.call(input);
277
+ return;
278
+ }
279
+
280
+ // Fallback: temporary input for Safari/iOS and browsers that block click on hidden inputs.
281
+ openPickerWithTempInput();
282
+ };
283
+
284
+ const handlePreviewKeyDown = (event: React.KeyboardEvent<HTMLElement>) => {
285
+ if (event.key !== 'Enter' && event.key !== ' ') return;
286
+ event.preventDefault();
287
+ handleAddColorClick();
288
+ };
289
+
290
+ const handleColorPicked = (event: React.ChangeEvent<HTMLInputElement>) => {
291
+ applyPickedColor(event.target.value);
292
+ // Keep the input value valid for type="color" (empty string can be invalid).
293
+ event.target.value = '#000000';
222
294
  };
223
295
 
224
296
  const handleSelectColor = (option: ColorOption) => {
@@ -245,16 +317,16 @@ export function ColorModal({
245
317
 
246
318
  <div className="color-modal__selected">
247
319
  <div className="color-modal__selected-info">
248
- <span
320
+ <label
321
+ htmlFor={colorPickerInputId}
249
322
  role="button"
250
323
  aria-label="Open color picker"
251
324
  tabIndex={0}
252
325
  title="Pick a color"
253
326
  className="color-modal__selected-preview"
254
327
  style={{ background: selectedColorPreview ?? 'transparent' }}
255
- onClick={handleAddColorClick}
256
328
  onKeyDown={handlePreviewKeyDown}
257
- />
329
+ ></label>
258
330
  <div>
259
331
  <p className="color-modal__selected-label">Selected color</p>
260
332
  <p className="color-modal__selected-value">{value ?? 'None'}</p>
@@ -316,13 +388,16 @@ export function ColorModal({
316
388
  options={savedColorOptions}
317
389
  emptyMessage="Add colors you use often for quick access."
318
390
  action={
319
- <button
320
- type="button"
321
- className="color-modal__link-button"
322
- onClick={handleAddColorClick}
323
- >
391
+ <span className="color-modal__link-button color-modal__add-color">
324
392
  Add color
325
- </button>
393
+ <input
394
+ type="color"
395
+ className="color-modal__add-color-input"
396
+ onChange={handleColorPicked}
397
+ defaultValue="#000000"
398
+ aria-label="Add color"
399
+ />
400
+ </span>
326
401
  }
327
402
  activeValue={value}
328
403
  onSelect={handleSelectColor}
@@ -330,9 +405,12 @@ export function ColorModal({
330
405
 
331
406
  <input
332
407
  ref={colorInputRef}
408
+ id={colorPickerInputId}
333
409
  type="color"
334
410
  className="color-modal__input"
335
411
  onChange={handleColorPicked}
412
+ defaultValue="#000000"
413
+ tabIndex={-1}
336
414
  />
337
415
  </Modal>
338
416
  );
@@ -0,0 +1,109 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import Modal from './Modal';
3
+ import { Icons, type IconsType } from '../types/Icons';
4
+ import { Icon } from '../components/Icon.generated';
5
+
6
+ type IconPickerModalProps = {
7
+ value?: IconsType;
8
+ onSelect: (icon: IconsType) => void;
9
+ onClose: () => void;
10
+ onClear?: () => void;
11
+ };
12
+
13
+ export function IconPickerModal({
14
+ value,
15
+ onSelect,
16
+ onClose,
17
+ onClear,
18
+ }: IconPickerModalProps) {
19
+ const [query, setQuery] = useState('');
20
+
21
+ const filtered = useMemo(() => {
22
+ const q = query.trim().toLowerCase();
23
+ if (!q) return Icons;
24
+ return Icons.filter((name) => name.toLowerCase().includes(q));
25
+ }, [query]);
26
+
27
+ return (
28
+ <Modal onClose={onClose} ariaLabelledBy="icon-picker-title">
29
+ <div style={{ display: 'grid', gap: 12 }}>
30
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
31
+ <p id="icon-picker-title" style={{ margin: 0, fontWeight: 700 }}>
32
+ Select Icon
33
+ </p>
34
+ <div style={{ flex: 1 }} />
35
+ {onClear ? (
36
+ <button type="button" className="editor-button" onClick={onClear}>
37
+ Clear
38
+ </button>
39
+ ) : null}
40
+ <button type="button" className="editor-button" onClick={onClose}>
41
+ Close
42
+ </button>
43
+ </div>
44
+
45
+ <input
46
+ type="text"
47
+ className="input"
48
+ placeholder="Search icons..."
49
+ value={query}
50
+ onChange={(e) => setQuery(e.target.value)}
51
+ autoFocus
52
+ />
53
+
54
+ <div style={{ fontSize: 12, color: '#666' }}>
55
+ {filtered.length} / {Icons.length}
56
+ {value ? ` • Selected: ${value}` : ''}
57
+ </div>
58
+
59
+ <div
60
+ style={{
61
+ display: 'grid',
62
+ gridTemplateColumns: 'repeat(auto-fill, minmax(120px, 1fr))',
63
+ gap: 8,
64
+ maxHeight: '60vh',
65
+ overflow: 'auto',
66
+ paddingRight: 4,
67
+ }}
68
+ >
69
+ {filtered.map((name) => {
70
+ const isActive = value === name;
71
+ return (
72
+ <button
73
+ key={name}
74
+ type="button"
75
+ onClick={() => onSelect(name)}
76
+ style={{
77
+ display: 'flex',
78
+ alignItems: 'center',
79
+ gap: 8,
80
+ borderRadius: 8,
81
+ border: isActive ? '2px solid #222' : '1px solid #ddd',
82
+ padding: '8px 10px',
83
+ background: '#fff',
84
+ cursor: 'pointer',
85
+ }}
86
+ aria-label={`Select icon ${name}`}
87
+ >
88
+ <Icon iconType={name} size={18} />
89
+ <span
90
+ style={{
91
+ fontSize: 12,
92
+ fontWeight: isActive ? 700 : 500,
93
+ textAlign: 'left',
94
+ overflow: 'hidden',
95
+ textOverflow: 'ellipsis',
96
+ whiteSpace: 'nowrap',
97
+ }}
98
+ title={name}
99
+ >
100
+ {name}
101
+ </span>
102
+ </button>
103
+ );
104
+ })}
105
+ </div>
106
+ </div>
107
+ </Modal>
108
+ );
109
+ }