@drawnagency/primitives 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. package/README.md +28 -0
  2. package/dist/auth/cookies.d.ts +8 -0
  3. package/dist/auth/cookies.d.ts.map +1 -0
  4. package/dist/auth/cookies.js +44 -0
  5. package/dist/auth/errors.d.ts +8 -0
  6. package/dist/auth/errors.d.ts.map +1 -0
  7. package/dist/auth/errors.js +10 -0
  8. package/dist/auth/index.d.ts +5 -0
  9. package/dist/auth/index.d.ts.map +1 -0
  10. package/dist/auth/index.js +3 -0
  11. package/dist/auth/security.d.ts +13 -0
  12. package/dist/auth/security.d.ts.map +1 -0
  13. package/dist/auth/security.js +48 -0
  14. package/dist/auth/types.d.ts +64 -0
  15. package/dist/auth/types.d.ts.map +1 -0
  16. package/dist/auth/types.js +1 -0
  17. package/dist/components/brandguide/ColorSwatchSettings.d.ts +8 -0
  18. package/dist/components/brandguide/ColorSwatchSettings.d.ts.map +1 -0
  19. package/dist/components/brandguide/ColorSwatchSettings.js +10 -0
  20. package/dist/components/brandguide/Colors.d.ts +15 -0
  21. package/dist/components/brandguide/Colors.d.ts.map +1 -0
  22. package/dist/components/brandguide/Colors.js +79 -0
  23. package/dist/components/brandguide/DoDontList.d.ts +16 -0
  24. package/dist/components/brandguide/DoDontList.d.ts.map +1 -0
  25. package/dist/components/brandguide/DoDontList.js +22 -0
  26. package/dist/components/brandguide/DoDontMediaGrid.d.ts +16 -0
  27. package/dist/components/brandguide/DoDontMediaGrid.d.ts.map +1 -0
  28. package/dist/components/brandguide/DoDontMediaGrid.js +5 -0
  29. package/dist/components/editor/AudiencePicker.d.ts +9 -0
  30. package/dist/components/editor/AudiencePicker.d.ts.map +1 -0
  31. package/dist/components/editor/AudiencePicker.js +24 -0
  32. package/dist/components/editor/DeleteButton.d.ts +6 -0
  33. package/dist/components/editor/DeleteButton.d.ts.map +1 -0
  34. package/dist/components/editor/DeleteButton.js +6 -0
  35. package/dist/components/editor/DragHandle.d.ts +2 -0
  36. package/dist/components/editor/DragHandle.d.ts.map +1 -0
  37. package/dist/components/editor/DragHandle.js +8 -0
  38. package/dist/components/editor/InsertButton.d.ts +7 -0
  39. package/dist/components/editor/InsertButton.d.ts.map +1 -0
  40. package/dist/components/editor/InsertButton.js +7 -0
  41. package/dist/components/editor/SectionWrapper.d.ts +3 -0
  42. package/dist/components/editor/SectionWrapper.d.ts.map +1 -0
  43. package/dist/components/editor/SectionWrapper.js +135 -0
  44. package/dist/components/editor/SettingsButton.d.ts +6 -0
  45. package/dist/components/editor/SettingsButton.d.ts.map +1 -0
  46. package/dist/components/editor/SettingsButton.js +6 -0
  47. package/dist/components/editor/SettingsForm.d.ts +13 -0
  48. package/dist/components/editor/SettingsForm.d.ts.map +1 -0
  49. package/dist/components/editor/SettingsForm.js +64 -0
  50. package/dist/components/editor/StatusBadge.d.ts +7 -0
  51. package/dist/components/editor/StatusBadge.d.ts.map +1 -0
  52. package/dist/components/editor/StatusBadge.js +10 -0
  53. package/dist/components/editor/StatusPicker.d.ts +9 -0
  54. package/dist/components/editor/StatusPicker.d.ts.map +1 -0
  55. package/dist/components/editor/StatusPicker.js +30 -0
  56. package/dist/components/editor/index.d.ts +8 -0
  57. package/dist/components/editor/index.d.ts.map +1 -0
  58. package/dist/components/editor/index.js +7 -0
  59. package/dist/components/primitives/CustomParagraph.d.ts +2 -0
  60. package/dist/components/primitives/CustomParagraph.d.ts.map +1 -0
  61. package/dist/components/primitives/CustomParagraph.js +24 -0
  62. package/dist/components/primitives/EditableGrid.d.ts +19 -0
  63. package/dist/components/primitives/EditableGrid.d.ts.map +1 -0
  64. package/dist/components/primitives/EditableGrid.js +90 -0
  65. package/dist/components/primitives/EditableList.d.ts +15 -0
  66. package/dist/components/primitives/EditableList.d.ts.map +1 -0
  67. package/dist/components/primitives/EditableList.js +54 -0
  68. package/dist/components/primitives/EditablePlainText.d.ts +12 -0
  69. package/dist/components/primitives/EditablePlainText.d.ts.map +1 -0
  70. package/dist/components/primitives/EditablePlainText.js +52 -0
  71. package/dist/components/primitives/EditableRichText.d.ts +11 -0
  72. package/dist/components/primitives/EditableRichText.d.ts.map +1 -0
  73. package/dist/components/primitives/EditableRichText.js +86 -0
  74. package/dist/components/primitives/HeadingSection.d.ts +10 -0
  75. package/dist/components/primitives/HeadingSection.d.ts.map +1 -0
  76. package/dist/components/primitives/HeadingSection.js +7 -0
  77. package/dist/components/primitives/IconPicker.d.ts +9 -0
  78. package/dist/components/primitives/IconPicker.d.ts.map +1 -0
  79. package/dist/components/primitives/IconPicker.js +21 -0
  80. package/dist/components/primitives/LinkPopover.d.ts +8 -0
  81. package/dist/components/primitives/LinkPopover.d.ts.map +1 -0
  82. package/dist/components/primitives/LinkPopover.js +48 -0
  83. package/dist/components/primitives/MediaSettingsForms.d.ts +19 -0
  84. package/dist/components/primitives/MediaSettingsForms.d.ts.map +1 -0
  85. package/dist/components/primitives/MediaSettingsForms.js +42 -0
  86. package/dist/components/primitives/ResolvedMedia.d.ts +8 -0
  87. package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -0
  88. package/dist/components/primitives/ResolvedMedia.js +9 -0
  89. package/dist/components/primitives/RichTextToolbar.d.ts +9 -0
  90. package/dist/components/primitives/RichTextToolbar.d.ts.map +1 -0
  91. package/dist/components/primitives/RichTextToolbar.js +26 -0
  92. package/dist/components/primitives/tiptap-presets.d.ts +4 -0
  93. package/dist/components/primitives/tiptap-presets.d.ts.map +1 -0
  94. package/dist/components/primitives/tiptap-presets.js +44 -0
  95. package/dist/components/primitives/useEditableCollection.d.ts +19 -0
  96. package/dist/components/primitives/useEditableCollection.d.ts.map +1 -0
  97. package/dist/components/primitives/useEditableCollection.js +61 -0
  98. package/dist/components/primitives/useEditablePlainText.d.ts +14 -0
  99. package/dist/components/primitives/useEditablePlainText.d.ts.map +1 -0
  100. package/dist/components/primitives/useEditablePlainText.js +27 -0
  101. package/dist/components/primitives/useEditableRichText.d.ts +16 -0
  102. package/dist/components/primitives/useEditableRichText.d.ts.map +1 -0
  103. package/dist/components/primitives/useEditableRichText.js +52 -0
  104. package/dist/components/sections/Button/CTAButton.d.ts +11 -0
  105. package/dist/components/sections/Button/CTAButton.d.ts.map +1 -0
  106. package/dist/components/sections/Button/CTAButton.js +18 -0
  107. package/dist/components/sections/Button/index.d.ts +11 -0
  108. package/dist/components/sections/Button/index.d.ts.map +1 -0
  109. package/dist/components/sections/Button/index.js +28 -0
  110. package/dist/components/sections/Colors/index.d.ts +22 -0
  111. package/dist/components/sections/Colors/index.d.ts.map +1 -0
  112. package/dist/components/sections/Colors/index.js +34 -0
  113. package/dist/components/sections/DoDontList/index.d.ts +21 -0
  114. package/dist/components/sections/DoDontList/index.d.ts.map +1 -0
  115. package/dist/components/sections/DoDontList/index.js +33 -0
  116. package/dist/components/sections/DoDontMediaGrid/index.d.ts +55 -0
  117. package/dist/components/sections/DoDontMediaGrid/index.d.ts.map +1 -0
  118. package/dist/components/sections/DoDontMediaGrid/index.js +41 -0
  119. package/dist/components/sections/IconList/IconList.d.ts +20 -0
  120. package/dist/components/sections/IconList/IconList.d.ts.map +1 -0
  121. package/dist/components/sections/IconList/IconList.js +131 -0
  122. package/dist/components/sections/IconList/IconListSettings.d.ts +11 -0
  123. package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -0
  124. package/dist/components/sections/IconList/IconListSettings.js +22 -0
  125. package/dist/components/sections/IconList/index.d.ts +17 -0
  126. package/dist/components/sections/IconList/index.d.ts.map +1 -0
  127. package/dist/components/sections/IconList/index.js +27 -0
  128. package/dist/components/sections/LinkHeading/index.d.ts +8 -0
  129. package/dist/components/sections/LinkHeading/index.d.ts.map +1 -0
  130. package/dist/components/sections/LinkHeading/index.js +15 -0
  131. package/dist/components/sections/MediaGrid/MediaGrid.d.ts +17 -0
  132. package/dist/components/sections/MediaGrid/MediaGrid.d.ts.map +1 -0
  133. package/dist/components/sections/MediaGrid/MediaGrid.js +62 -0
  134. package/dist/components/sections/MediaGrid/index.d.ts +55 -0
  135. package/dist/components/sections/MediaGrid/index.d.ts.map +1 -0
  136. package/dist/components/sections/MediaGrid/index.js +35 -0
  137. package/dist/components/sections/Prose/Prose.d.ts +8 -0
  138. package/dist/components/sections/Prose/Prose.d.ts.map +1 -0
  139. package/dist/components/sections/Prose/Prose.js +11 -0
  140. package/dist/components/sections/Prose/index.d.ts +8 -0
  141. package/dist/components/sections/Prose/index.d.ts.map +1 -0
  142. package/dist/components/sections/Prose/index.js +15 -0
  143. package/dist/components/sections/SectionLayout.d.ts +11 -0
  144. package/dist/components/sections/SectionLayout.d.ts.map +1 -0
  145. package/dist/components/sections/SectionLayout.js +15 -0
  146. package/dist/components/sections/SplitContent/SplitContent.d.ts +11 -0
  147. package/dist/components/sections/SplitContent/SplitContent.d.ts.map +1 -0
  148. package/dist/components/sections/SplitContent/SplitContent.js +31 -0
  149. package/dist/components/sections/SplitContent/SplitContentSettings.d.ts +9 -0
  150. package/dist/components/sections/SplitContent/SplitContentSettings.d.ts.map +1 -0
  151. package/dist/components/sections/SplitContent/SplitContentSettings.js +17 -0
  152. package/dist/components/sections/SplitContent/index.d.ts +13 -0
  153. package/dist/components/sections/SplitContent/index.d.ts.map +1 -0
  154. package/dist/components/sections/SplitContent/index.js +27 -0
  155. package/dist/components/sections/SubHeading/index.d.ts +9 -0
  156. package/dist/components/sections/SubHeading/index.d.ts.map +1 -0
  157. package/dist/components/sections/SubHeading/index.js +18 -0
  158. package/dist/components/sections/SubSubHeading/index.d.ts +9 -0
  159. package/dist/components/sections/SubSubHeading/index.d.ts.map +1 -0
  160. package/dist/components/sections/SubSubHeading/index.js +18 -0
  161. package/dist/components/sections/ViewRenderer.d.ts +8 -0
  162. package/dist/components/sections/ViewRenderer.d.ts.map +1 -0
  163. package/dist/components/sections/ViewRenderer.js +13 -0
  164. package/dist/components/sections/register-schemas.d.ts +2 -0
  165. package/dist/components/sections/register-schemas.d.ts.map +1 -0
  166. package/dist/components/sections/register-schemas.js +15 -0
  167. package/dist/components/sections/register.d.ts +2 -0
  168. package/dist/components/sections/register.d.ts.map +1 -0
  169. package/dist/components/sections/register.js +15 -0
  170. package/dist/components/shared/Button.d.ts +15 -0
  171. package/dist/components/shared/Button.d.ts.map +1 -0
  172. package/dist/components/shared/Button.js +27 -0
  173. package/dist/components/shared/Checkbox.d.ts +14 -0
  174. package/dist/components/shared/Checkbox.d.ts.map +1 -0
  175. package/dist/components/shared/Checkbox.js +10 -0
  176. package/dist/components/shared/ColorPicker.d.ts +9 -0
  177. package/dist/components/shared/ColorPicker.d.ts.map +1 -0
  178. package/dist/components/shared/ColorPicker.js +5 -0
  179. package/dist/components/shared/ErrorBoundary.d.ts +24 -0
  180. package/dist/components/shared/ErrorBoundary.d.ts.map +1 -0
  181. package/dist/components/shared/ErrorBoundary.js +30 -0
  182. package/dist/components/shared/FontPicker.d.ts +8 -0
  183. package/dist/components/shared/FontPicker.d.ts.map +1 -0
  184. package/dist/components/shared/FontPicker.js +190 -0
  185. package/dist/components/shared/FormLabel.d.ts +8 -0
  186. package/dist/components/shared/FormLabel.d.ts.map +1 -0
  187. package/dist/components/shared/FormLabel.js +5 -0
  188. package/dist/components/shared/IconButton.d.ts +12 -0
  189. package/dist/components/shared/IconButton.d.ts.map +1 -0
  190. package/dist/components/shared/IconButton.js +16 -0
  191. package/dist/components/shared/Input.d.ts +8 -0
  192. package/dist/components/shared/Input.d.ts.map +1 -0
  193. package/dist/components/shared/Input.js +8 -0
  194. package/dist/components/shared/Navigation.d.ts +9 -0
  195. package/dist/components/shared/Navigation.d.ts.map +1 -0
  196. package/dist/components/shared/Navigation.js +71 -0
  197. package/dist/components/shared/PasswordInput.d.ts +13 -0
  198. package/dist/components/shared/PasswordInput.d.ts.map +1 -0
  199. package/dist/components/shared/PasswordInput.js +11 -0
  200. package/dist/components/shared/Popover.d.ts +12 -0
  201. package/dist/components/shared/Popover.d.ts.map +1 -0
  202. package/dist/components/shared/Popover.js +33 -0
  203. package/dist/components/shared/PopoverItem.d.ts +7 -0
  204. package/dist/components/shared/PopoverItem.d.ts.map +1 -0
  205. package/dist/components/shared/PopoverItem.js +6 -0
  206. package/dist/components/shared/Select.d.ts +16 -0
  207. package/dist/components/shared/Select.d.ts.map +1 -0
  208. package/dist/components/shared/Select.js +9 -0
  209. package/dist/components/shared/Textarea.d.ts +8 -0
  210. package/dist/components/shared/Textarea.d.ts.map +1 -0
  211. package/dist/components/shared/Textarea.js +8 -0
  212. package/dist/components/shared/Toggle.d.ts +10 -0
  213. package/dist/components/shared/Toggle.d.ts.map +1 -0
  214. package/dist/components/shared/Toggle.js +5 -0
  215. package/dist/components/shared/Tooltip.d.ts +12 -0
  216. package/dist/components/shared/Tooltip.d.ts.map +1 -0
  217. package/dist/components/shared/Tooltip.js +8 -0
  218. package/dist/components/shared/icons.d.ts +17 -0
  219. package/dist/components/shared/icons.d.ts.map +1 -0
  220. package/dist/components/shared/icons.js +23 -0
  221. package/dist/components/shell/AudienceAddForm.d.ts +11 -0
  222. package/dist/components/shell/AudienceAddForm.d.ts.map +1 -0
  223. package/dist/components/shell/AudienceAddForm.js +43 -0
  224. package/dist/components/shell/AudienceRow.d.ts +15 -0
  225. package/dist/components/shell/AudienceRow.d.ts.map +1 -0
  226. package/dist/components/shell/AudienceRow.js +74 -0
  227. package/dist/components/shell/EditorContext.d.ts +14 -0
  228. package/dist/components/shell/EditorContext.d.ts.map +1 -0
  229. package/dist/components/shell/EditorContext.js +24 -0
  230. package/dist/components/shell/EditorLoginForm.d.ts +13 -0
  231. package/dist/components/shell/EditorLoginForm.d.ts.map +1 -0
  232. package/dist/components/shell/EditorLoginForm.js +46 -0
  233. package/dist/components/shell/EditorModal.d.ts +13 -0
  234. package/dist/components/shell/EditorModal.d.ts.map +1 -0
  235. package/dist/components/shell/EditorModal.js +43 -0
  236. package/dist/components/shell/EditorModalContext.d.ts +16 -0
  237. package/dist/components/shell/EditorModalContext.d.ts.map +1 -0
  238. package/dist/components/shell/EditorModalContext.js +20 -0
  239. package/dist/components/shell/EditorShell.d.ts +22 -0
  240. package/dist/components/shell/EditorShell.d.ts.map +1 -0
  241. package/dist/components/shell/EditorShell.js +483 -0
  242. package/dist/components/shell/MediaLibraryContext.d.ts +14 -0
  243. package/dist/components/shell/MediaLibraryContext.d.ts.map +1 -0
  244. package/dist/components/shell/MediaLibraryContext.js +5 -0
  245. package/dist/components/shell/MediaLibraryModal.d.ts +14 -0
  246. package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -0
  247. package/dist/components/shell/MediaLibraryModal.js +145 -0
  248. package/dist/components/shell/ProcessingIndicator.d.ts +7 -0
  249. package/dist/components/shell/ProcessingIndicator.d.ts.map +1 -0
  250. package/dist/components/shell/ProcessingIndicator.js +15 -0
  251. package/dist/components/shell/SectionSkeleton.d.ts +9 -0
  252. package/dist/components/shell/SectionSkeleton.d.ts.map +1 -0
  253. package/dist/components/shell/SectionSkeleton.js +22 -0
  254. package/dist/components/shell/SectionTypePicker.d.ts +14 -0
  255. package/dist/components/shell/SectionTypePicker.d.ts.map +1 -0
  256. package/dist/components/shell/SectionTypePicker.js +15 -0
  257. package/dist/components/shell/SiteSettingsDisplay.d.ts +8 -0
  258. package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -0
  259. package/dist/components/shell/SiteSettingsDisplay.js +28 -0
  260. package/dist/components/shell/SiteSettingsModal.d.ts +24 -0
  261. package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -0
  262. package/dist/components/shell/SiteSettingsModal.js +40 -0
  263. package/dist/components/shell/SiteSettingsUsers.d.ts +9 -0
  264. package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -0
  265. package/dist/components/shell/SiteSettingsUsers.js +87 -0
  266. package/dist/components/shell/SiteSettingsViewerAccess.d.ts +9 -0
  267. package/dist/components/shell/SiteSettingsViewerAccess.d.ts.map +1 -0
  268. package/dist/components/shell/SiteSettingsViewerAccess.js +94 -0
  269. package/dist/components/shell/ViewerLoginForm.d.ts +10 -0
  270. package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -0
  271. package/dist/components/shell/ViewerLoginForm.js +40 -0
  272. package/dist/data/google-fonts.json +7718 -0
  273. package/dist/hooks/index.d.ts +7 -0
  274. package/dist/hooks/index.d.ts.map +1 -0
  275. package/dist/hooks/index.js +6 -0
  276. package/dist/hooks/useActiveHeadings.d.ts +7 -0
  277. package/dist/hooks/useActiveHeadings.d.ts.map +1 -0
  278. package/dist/hooks/useActiveHeadings.js +99 -0
  279. package/dist/hooks/useEditorPersistence.d.ts +13 -0
  280. package/dist/hooks/useEditorPersistence.d.ts.map +1 -0
  281. package/dist/hooks/useEditorPersistence.js +73 -0
  282. package/dist/hooks/useEditorPublish.d.ts +24 -0
  283. package/dist/hooks/useEditorPublish.d.ts.map +1 -0
  284. package/dist/hooks/useEditorPublish.js +145 -0
  285. package/dist/hooks/useFocusTrap.d.ts +3 -0
  286. package/dist/hooks/useFocusTrap.d.ts.map +1 -0
  287. package/dist/hooks/useFocusTrap.js +51 -0
  288. package/dist/hooks/useMediaPipeline.d.ts +65 -0
  289. package/dist/hooks/useMediaPipeline.d.ts.map +1 -0
  290. package/dist/hooks/useMediaPipeline.js +253 -0
  291. package/dist/hooks/useResolvedMedia.d.ts +9 -0
  292. package/dist/hooks/useResolvedMedia.d.ts.map +1 -0
  293. package/dist/hooks/useResolvedMedia.js +39 -0
  294. package/dist/index.d.ts +5 -0
  295. package/dist/index.d.ts.map +1 -0
  296. package/dist/index.js +4 -0
  297. package/dist/lib/cn.d.ts +3 -0
  298. package/dist/lib/cn.d.ts.map +1 -0
  299. package/dist/lib/cn.js +5 -0
  300. package/dist/lib/contrast.d.ts +2 -0
  301. package/dist/lib/contrast.d.ts.map +1 -0
  302. package/dist/lib/contrast.js +11 -0
  303. package/dist/lib/dexie.d.ts +41 -0
  304. package/dist/lib/dexie.d.ts.map +1 -0
  305. package/dist/lib/dexie.js +236 -0
  306. package/dist/lib/events.d.ts +12 -0
  307. package/dist/lib/events.d.ts.map +1 -0
  308. package/dist/lib/events.js +15 -0
  309. package/dist/lib/google-fonts.d.ts +2 -0
  310. package/dist/lib/google-fonts.d.ts.map +1 -0
  311. package/dist/lib/google-fonts.js +11 -0
  312. package/dist/lib/grid.d.ts +2 -0
  313. package/dist/lib/grid.d.ts.map +1 -0
  314. package/dist/lib/grid.js +7 -0
  315. package/dist/lib/icons.d.ts +9 -0
  316. package/dist/lib/icons.d.ts.map +1 -0
  317. package/dist/lib/icons.js +27 -0
  318. package/dist/lib/index.d.ts +13 -0
  319. package/dist/lib/index.d.ts.map +1 -0
  320. package/dist/lib/index.js +12 -0
  321. package/dist/lib/loader.d.ts +28 -0
  322. package/dist/lib/loader.d.ts.map +1 -0
  323. package/dist/lib/loader.js +57 -0
  324. package/dist/lib/nav.d.ts +15 -0
  325. package/dist/lib/nav.d.ts.map +1 -0
  326. package/dist/lib/nav.js +58 -0
  327. package/dist/lib/registry.d.ts +110 -0
  328. package/dist/lib/registry.d.ts.map +1 -0
  329. package/dist/lib/registry.js +64 -0
  330. package/dist/lib/safeRedirect.d.ts +7 -0
  331. package/dist/lib/safeRedirect.d.ts.map +1 -0
  332. package/dist/lib/safeRedirect.js +11 -0
  333. package/dist/lib/sanitize.d.ts +2 -0
  334. package/dist/lib/sanitize.d.ts.map +1 -0
  335. package/dist/lib/sanitize.js +6 -0
  336. package/dist/lib/timestamp.d.ts +2 -0
  337. package/dist/lib/timestamp.d.ts.map +1 -0
  338. package/dist/lib/timestamp.js +28 -0
  339. package/dist/media/github.d.ts +3 -0
  340. package/dist/media/github.d.ts.map +1 -0
  341. package/dist/media/github.js +60 -0
  342. package/dist/media/index.d.ts +8 -0
  343. package/dist/media/index.d.ts.map +1 -0
  344. package/dist/media/index.js +9 -0
  345. package/dist/media/queue.d.ts +74 -0
  346. package/dist/media/queue.d.ts.map +1 -0
  347. package/dist/media/queue.js +116 -0
  348. package/dist/media/resolve.d.ts +14 -0
  349. package/dist/media/resolve.d.ts.map +1 -0
  350. package/dist/media/resolve.js +50 -0
  351. package/dist/media/types.d.ts +42 -0
  352. package/dist/media/types.d.ts.map +1 -0
  353. package/dist/media/types.js +1 -0
  354. package/dist/media/utils.d.ts +7 -0
  355. package/dist/media/utils.d.ts.map +1 -0
  356. package/dist/media/utils.js +41 -0
  357. package/dist/media/videoPoster.d.ts +6 -0
  358. package/dist/media/videoPoster.d.ts.map +1 -0
  359. package/dist/media/videoPoster.js +44 -0
  360. package/dist/media/worker.d.ts +36 -0
  361. package/dist/media/worker.d.ts.map +1 -0
  362. package/dist/media/worker.js +73 -0
  363. package/dist/schemas/audience.d.ts +12 -0
  364. package/dist/schemas/audience.d.ts.map +1 -0
  365. package/dist/schemas/audience.js +19 -0
  366. package/dist/schemas/auth.d.ts +36 -0
  367. package/dist/schemas/auth.d.ts.map +1 -0
  368. package/dist/schemas/auth.js +22 -0
  369. package/dist/schemas/index.d.ts +8 -0
  370. package/dist/schemas/index.d.ts.map +1 -0
  371. package/dist/schemas/index.js +7 -0
  372. package/dist/schemas/media-grid-options.d.ts +8 -0
  373. package/dist/schemas/media-grid-options.d.ts.map +1 -0
  374. package/dist/schemas/media-grid-options.js +7 -0
  375. package/dist/schemas/media.d.ts +63 -0
  376. package/dist/schemas/media.d.ts.map +1 -0
  377. package/dist/schemas/media.js +28 -0
  378. package/dist/schemas/sections.d.ts +12 -0
  379. package/dist/schemas/sections.d.ts.map +1 -0
  380. package/dist/schemas/sections.js +12 -0
  381. package/dist/schemas/shared.d.ts +98 -0
  382. package/dist/schemas/shared.d.ts.map +1 -0
  383. package/dist/schemas/shared.js +71 -0
  384. package/dist/schemas/site-config.d.ts +48 -0
  385. package/dist/schemas/site-config.d.ts.map +1 -0
  386. package/dist/schemas/site-config.js +26 -0
  387. package/package.json +57 -0
  388. package/src/auth/cookies.ts +51 -0
  389. package/src/auth/errors.ts +10 -0
  390. package/src/auth/index.ts +15 -0
  391. package/src/auth/security.ts +44 -0
  392. package/src/auth/types.ts +61 -0
  393. package/src/components/brandguide/ColorSwatchSettings.tsx +34 -0
  394. package/src/components/brandguide/Colors.tsx +277 -0
  395. package/src/components/brandguide/DoDontList.tsx +67 -0
  396. package/src/components/brandguide/DoDontMediaGrid.tsx +19 -0
  397. package/src/components/editor/AudiencePicker.tsx +103 -0
  398. package/src/components/editor/DeleteButton.tsx +18 -0
  399. package/src/components/editor/DragHandle.tsx +18 -0
  400. package/src/components/editor/InsertButton.tsx +22 -0
  401. package/src/components/editor/SectionWrapper.tsx +279 -0
  402. package/src/components/editor/SettingsButton.tsx +17 -0
  403. package/src/components/editor/SettingsForm.tsx +159 -0
  404. package/src/components/editor/StatusBadge.tsx +30 -0
  405. package/src/components/editor/StatusPicker.tsx +86 -0
  406. package/src/components/editor/index.ts +7 -0
  407. package/src/components/primitives/CustomParagraph.ts +25 -0
  408. package/src/components/primitives/EditableGrid.tsx +260 -0
  409. package/src/components/primitives/EditableList.tsx +160 -0
  410. package/src/components/primitives/EditablePlainText.tsx +99 -0
  411. package/src/components/primitives/EditableRichText.tsx +132 -0
  412. package/src/components/primitives/HeadingSection.tsx +25 -0
  413. package/src/components/primitives/IconPicker.tsx +60 -0
  414. package/src/components/primitives/LinkPopover.tsx +109 -0
  415. package/src/components/primitives/MediaSettingsForms.tsx +109 -0
  416. package/src/components/primitives/ResolvedMedia.tsx +36 -0
  417. package/src/components/primitives/RichTextToolbar.tsx +149 -0
  418. package/src/components/primitives/tiptap-presets.ts +50 -0
  419. package/src/components/primitives/useEditableCollection.ts +104 -0
  420. package/src/components/primitives/useEditablePlainText.ts +49 -0
  421. package/src/components/primitives/useEditableRichText.ts +74 -0
  422. package/src/components/sections/Button/CTAButton.tsx +50 -0
  423. package/src/components/sections/Button/index.tsx +37 -0
  424. package/src/components/sections/Colors/index.tsx +45 -0
  425. package/src/components/sections/DoDontList/index.tsx +43 -0
  426. package/src/components/sections/DoDontMediaGrid/index.tsx +53 -0
  427. package/src/components/sections/IconList/IconList.tsx +462 -0
  428. package/src/components/sections/IconList/IconListSettings.tsx +81 -0
  429. package/src/components/sections/IconList/index.tsx +36 -0
  430. package/src/components/sections/LinkHeading/index.tsx +24 -0
  431. package/src/components/sections/MediaGrid/MediaGrid.tsx +219 -0
  432. package/src/components/sections/MediaGrid/index.tsx +47 -0
  433. package/src/components/sections/Prose/Prose.tsx +34 -0
  434. package/src/components/sections/Prose/index.tsx +18 -0
  435. package/src/components/sections/SectionLayout.tsx +31 -0
  436. package/src/components/sections/SplitContent/SplitContent.tsx +77 -0
  437. package/src/components/sections/SplitContent/SplitContentSettings.tsx +42 -0
  438. package/src/components/sections/SplitContent/index.tsx +36 -0
  439. package/src/components/sections/SubHeading/index.tsx +27 -0
  440. package/src/components/sections/SubSubHeading/index.tsx +27 -0
  441. package/src/components/sections/ViewRenderer.tsx +29 -0
  442. package/src/components/sections/register-schemas.ts +16 -0
  443. package/src/components/sections/register.ts +16 -0
  444. package/src/components/shared/Button.tsx +77 -0
  445. package/src/components/shared/Checkbox.tsx +73 -0
  446. package/src/components/shared/ColorPicker.tsx +28 -0
  447. package/src/components/shared/ErrorBoundary.tsx +92 -0
  448. package/src/components/shared/FontPicker.tsx +300 -0
  449. package/src/components/shared/FormLabel.tsx +18 -0
  450. package/src/components/shared/IconButton.tsx +45 -0
  451. package/src/components/shared/Input.tsx +34 -0
  452. package/src/components/shared/Navigation.tsx +196 -0
  453. package/src/components/shared/PasswordInput.tsx +64 -0
  454. package/src/components/shared/Popover.tsx +61 -0
  455. package/src/components/shared/PopoverItem.tsx +24 -0
  456. package/src/components/shared/Select.tsx +60 -0
  457. package/src/components/shared/Textarea.tsx +34 -0
  458. package/src/components/shared/Toggle.tsx +35 -0
  459. package/src/components/shared/Tooltip.tsx +28 -0
  460. package/src/components/shared/icons.tsx +28 -0
  461. package/src/components/shell/AudienceAddForm.tsx +94 -0
  462. package/src/components/shell/AudienceRow.tsx +171 -0
  463. package/src/components/shell/EditorContext.tsx +44 -0
  464. package/src/components/shell/EditorLoginForm.tsx +130 -0
  465. package/src/components/shell/EditorModal.tsx +93 -0
  466. package/src/components/shell/EditorModalContext.tsx +40 -0
  467. package/src/components/shell/EditorShell.tsx +910 -0
  468. package/src/components/shell/MediaLibraryContext.tsx +19 -0
  469. package/src/components/shell/MediaLibraryModal.tsx +371 -0
  470. package/src/components/shell/ProcessingIndicator.tsx +84 -0
  471. package/src/components/shell/SectionSkeleton.tsx +54 -0
  472. package/src/components/shell/SectionTypePicker.tsx +47 -0
  473. package/src/components/shell/SiteSettingsDisplay.tsx +78 -0
  474. package/src/components/shell/SiteSettingsModal.tsx +129 -0
  475. package/src/components/shell/SiteSettingsUsers.tsx +190 -0
  476. package/src/components/shell/SiteSettingsViewerAccess.tsx +171 -0
  477. package/src/components/shell/ViewerLoginForm.tsx +95 -0
  478. package/src/data/google-fonts.json +7718 -0
  479. package/src/env.d.ts +3 -0
  480. package/src/hooks/index.ts +6 -0
  481. package/src/hooks/useActiveHeadings.ts +120 -0
  482. package/src/hooks/useEditorPersistence.ts +103 -0
  483. package/src/hooks/useEditorPublish.ts +187 -0
  484. package/src/hooks/useFocusTrap.ts +57 -0
  485. package/src/hooks/useMediaPipeline.ts +309 -0
  486. package/src/hooks/useResolvedMedia.ts +54 -0
  487. package/src/index.ts +4 -0
  488. package/src/lib/cn.ts +6 -0
  489. package/src/lib/contrast.ts +11 -0
  490. package/src/lib/dexie.ts +347 -0
  491. package/src/lib/events.ts +23 -0
  492. package/src/lib/google-fonts.ts +10 -0
  493. package/src/lib/grid.ts +7 -0
  494. package/src/lib/icons.ts +58 -0
  495. package/src/lib/index.ts +35 -0
  496. package/src/lib/loader.ts +81 -0
  497. package/src/lib/nav.ts +64 -0
  498. package/src/lib/registry.ts +202 -0
  499. package/src/lib/safeRedirect.ts +11 -0
  500. package/src/lib/sanitize.ts +6 -0
  501. package/src/lib/timestamp.ts +31 -0
  502. package/src/media/github.ts +72 -0
  503. package/src/media/index.ts +12 -0
  504. package/src/media/queue.ts +166 -0
  505. package/src/media/resolve.ts +63 -0
  506. package/src/media/types.ts +58 -0
  507. package/src/media/utils.ts +48 -0
  508. package/src/media/videoPoster.ts +53 -0
  509. package/src/media/worker.ts +122 -0
  510. package/src/schemas/audience.ts +28 -0
  511. package/src/schemas/auth.ts +31 -0
  512. package/src/schemas/index.ts +7 -0
  513. package/src/schemas/media-grid-options.ts +8 -0
  514. package/src/schemas/media.ts +40 -0
  515. package/src/schemas/sections.ts +24 -0
  516. package/src/schemas/shared.ts +96 -0
  517. package/src/schemas/site-config.ts +38 -0
@@ -0,0 +1,77 @@
1
+ import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ export type ButtonVariant = "primary" | "brand" | "secondary" | "destructive" | "ghost";
5
+ export type ButtonSize = "sm" | "md";
6
+ export type ButtonTone = "neutral" | "destructive";
7
+
8
+ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
9
+ variant?: ButtonVariant;
10
+ size?: ButtonSize;
11
+ tone?: ButtonTone;
12
+ fullWidth?: boolean;
13
+ isLoading?: boolean;
14
+ loadingLabel?: string;
15
+ children?: ReactNode;
16
+ }
17
+
18
+ const boxSize: Record<ButtonSize, string> = {
19
+ sm: "rounded px-3 py-1.5 text-xs font-medium",
20
+ md: "rounded px-4 py-2 text-sm font-medium",
21
+ };
22
+
23
+ const ghostSize: Record<ButtonSize, string> = {
24
+ sm: "text-xs font-medium",
25
+ md: "text-sm font-medium",
26
+ };
27
+
28
+ const variantClasses: Record<Exclude<ButtonVariant, "ghost">, string> = {
29
+ primary: "bg-base-contrast text-base-accent hover:bg-base-contrast/90",
30
+ brand: "bg-primary text-primary-contrast hover:opacity-90",
31
+ secondary: "border border-base-200 text-base-contrast hover:bg-base-accent",
32
+ destructive:
33
+ "border border-red-600 text-red-600 hover:bg-red-600 hover:text-white",
34
+ };
35
+
36
+ const ghostToneClasses: Record<ButtonTone, string> = {
37
+ neutral: "text-base-contrast-light hover:text-base-contrast",
38
+ destructive: "text-red-600 hover:text-red-800",
39
+ };
40
+
41
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
42
+ {
43
+ variant = "primary",
44
+ size = "sm",
45
+ tone = "neutral",
46
+ fullWidth = false,
47
+ isLoading = false,
48
+ loadingLabel,
49
+ disabled,
50
+ className,
51
+ children,
52
+ ...rest
53
+ },
54
+ ref,
55
+ ) {
56
+ const isGhost = variant === "ghost";
57
+ const resolvedDisabled = disabled || isLoading;
58
+ const label = isLoading && loadingLabel ? loadingLabel : children;
59
+
60
+ return (
61
+ <button
62
+ ref={ref}
63
+ disabled={resolvedDisabled}
64
+ className={cn(
65
+ "inline-flex items-center justify-center transition-colors",
66
+ isGhost ? ghostSize[size] : boxSize[size],
67
+ isGhost ? ghostToneClasses[tone] : variantClasses[variant],
68
+ fullWidth && "w-full",
69
+ resolvedDisabled ? "cursor-not-allowed opacity-50" : "cursor-pointer",
70
+ className,
71
+ )}
72
+ {...rest}
73
+ >
74
+ {label}
75
+ </button>
76
+ );
77
+ });
@@ -0,0 +1,73 @@
1
+ import { useId, type ReactNode } from "react";
2
+ import { Check } from "lucide-react";
3
+ import { cn } from "../../lib/cn";
4
+
5
+ interface CheckboxProps {
6
+ checked: boolean;
7
+ onChange: (checked: boolean) => void;
8
+ label: string;
9
+ description?: string;
10
+ disabled?: boolean;
11
+ className?: string;
12
+ startAdornment?: ReactNode;
13
+ align?: "start" | "center";
14
+ }
15
+
16
+ export function Checkbox({
17
+ checked,
18
+ onChange,
19
+ label,
20
+ description,
21
+ disabled,
22
+ className,
23
+ startAdornment,
24
+ align = "start",
25
+ }: CheckboxProps) {
26
+ const id = useId();
27
+ return (
28
+ <label
29
+ htmlFor={id}
30
+ className={cn(
31
+ "flex cursor-pointer gap-3 select-none hover:opacity-80",
32
+ align === "start" ? "items-start" : "items-center",
33
+ disabled && "cursor-not-allowed opacity-50",
34
+ className,
35
+ )}
36
+ >
37
+ <span
38
+ className={cn(
39
+ "relative flex h-5 w-5 shrink-0 items-center justify-center",
40
+ align === "start" && "mt-0.5",
41
+ )}
42
+ >
43
+ <input
44
+ id={id}
45
+ type="checkbox"
46
+ checked={checked}
47
+ disabled={disabled}
48
+ onChange={(e) => onChange(e.target.checked)}
49
+ className="sr-only"
50
+ />
51
+ <span
52
+ aria-hidden="true"
53
+ className={cn(
54
+ "flex h-5 w-5 items-center justify-center rounded border transition-colors",
55
+ checked
56
+ ? "border-primary bg-primary text-primary-contrast"
57
+ : "border-base-300 bg-base hover:border-primary",
58
+ disabled && "pointer-events-none",
59
+ )}
60
+ >
61
+ {checked && <Check size={14} strokeWidth={3} />}
62
+ </span>
63
+ </span>
64
+ {startAdornment}
65
+ <div className="text-sm">
66
+ <span className="font-medium text-base-contrast">{label}</span>
67
+ {description && (
68
+ <span className="block text-base-contrast-light">{description}</span>
69
+ )}
70
+ </div>
71
+ </label>
72
+ );
73
+ }
@@ -0,0 +1,28 @@
1
+ import { cn } from "../../lib/cn";
2
+
3
+ interface ColorPickerProps {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ label?: string;
7
+ className?: string;
8
+ }
9
+
10
+ export function ColorPicker({ value, onChange, label = "Color", className }: ColorPickerProps) {
11
+ return (
12
+ <label
13
+ className={cn(
14
+ "inline-flex h-9 w-9 shrink-0 cursor-pointer items-center justify-center rounded-full border border-base-200 focus-within:outline focus-within:outline-2 focus-within:outline-base-contrast",
15
+ className,
16
+ )}
17
+ style={{ backgroundColor: value }}
18
+ >
19
+ <input
20
+ type="color"
21
+ aria-label={label}
22
+ value={value}
23
+ onChange={(e) => onChange(e.target.value)}
24
+ className="sr-only"
25
+ />
26
+ </label>
27
+ );
28
+ }
@@ -0,0 +1,92 @@
1
+ import { Component, type ReactNode, type ErrorInfo } from "react";
2
+ import { cn } from "../../lib/cn";
3
+ import { Button } from "./Button";
4
+
5
+ interface ErrorBoundaryProps {
6
+ /** Shown in the fallback UI to help identify which section failed */
7
+ label?: string;
8
+ children: ReactNode;
9
+ }
10
+
11
+ interface ErrorBoundaryState {
12
+ hasError: boolean;
13
+ error: Error | null;
14
+ }
15
+
16
+ /**
17
+ * Catches render errors in children and shows a recoverable fallback UI.
18
+ * Designed to wrap individual section components so a single broken section
19
+ * doesn't take down the entire editor.
20
+ */
21
+ export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
22
+ constructor(props: ErrorBoundaryProps) {
23
+ super(props);
24
+ this.state = { hasError: false, error: null };
25
+ }
26
+
27
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
28
+ return { hasError: true, error };
29
+ }
30
+
31
+ componentDidCatch(error: Error, info: ErrorInfo) {
32
+ console.error(
33
+ `[ErrorBoundary${this.props.label ? `: ${this.props.label}` : ""}] Render error:`,
34
+ error,
35
+ info.componentStack,
36
+ );
37
+ }
38
+
39
+ private handleRetry = () => {
40
+ this.setState({ hasError: false, error: null });
41
+ };
42
+
43
+ render() {
44
+ if (this.state.hasError) {
45
+ return (
46
+ <div
47
+ role="alert"
48
+ className={cn(
49
+ "flex flex-col items-center justify-center gap-3 rounded border border-red-200 bg-red-50 px-6 py-10 text-center",
50
+ "dark:border-red-900/40 dark:bg-red-950/20",
51
+ )}
52
+ >
53
+ <svg
54
+ xmlns="http://www.w3.org/2000/svg"
55
+ viewBox="0 0 20 20"
56
+ fill="currentColor"
57
+ className="h-6 w-6 text-red-500"
58
+ aria-hidden="true"
59
+ >
60
+ <path
61
+ fillRule="evenodd"
62
+ d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z"
63
+ clipRule="evenodd"
64
+ />
65
+ </svg>
66
+
67
+ <p className="text-sm font-medium text-red-700 dark:text-red-400">
68
+ This section couldn&apos;t be rendered
69
+ </p>
70
+
71
+ {this.props.label && (
72
+ <p className="text-xs text-red-500 dark:text-red-500/80">
73
+ Section: {this.props.label}
74
+ </p>
75
+ )}
76
+
77
+ {this.state.error && (
78
+ <p className="max-w-md text-xs text-red-400 dark:text-red-500/60">
79
+ {this.state.error.message}
80
+ </p>
81
+ )}
82
+
83
+ <Button variant="secondary" size="sm" onClick={this.handleRetry} className="mt-1">
84
+ Try again
85
+ </Button>
86
+ </div>
87
+ );
88
+ }
89
+
90
+ return this.props.children;
91
+ }
92
+ }
@@ -0,0 +1,300 @@
1
+ import { useState, useRef, useEffect, useId, useCallback, useMemo } from "react";
2
+ import { createPortal } from "react-dom";
3
+ import { ChevronDown } from "lucide-react";
4
+ import { cn } from "../../lib/cn";
5
+ import { FormLabel } from "./FormLabel";
6
+ import fontCatalog from "../../data/google-fonts.json";
7
+
8
+ interface FontEntry {
9
+ family: string;
10
+ category: string;
11
+ }
12
+
13
+ interface FontPickerProps {
14
+ label: string;
15
+ value: string;
16
+ onChange: (family: string) => void;
17
+ }
18
+
19
+ const fonts = fontCatalog as FontEntry[];
20
+
21
+ function displayValue(value: string): string {
22
+ return value === "system-ui" ? "System Default" : value;
23
+ }
24
+
25
+ function loadFontPreview(families: string[]) {
26
+ if (families.length === 0) return;
27
+ const params = families.map((f) => `family=${f.replace(/ /g, "+")}:wght@400;700`).join("&");
28
+ const href = `https://fonts.googleapis.com/css2?${params}&display=swap`;
29
+
30
+ if (document.querySelector(`link[href="${href}"]`)) return;
31
+
32
+ const link = document.createElement("link");
33
+ link.rel = "stylesheet";
34
+ link.href = href;
35
+ document.head.appendChild(link);
36
+ }
37
+
38
+ export function FontPicker({ label, value, onChange }: FontPickerProps) {
39
+ const id = useId();
40
+ const [open, setOpen] = useState(false);
41
+ const [query, setQuery] = useState("");
42
+ const [debouncedQuery, setDebouncedQuery] = useState("");
43
+ const [highlightIndex, setHighlightIndex] = useState(0);
44
+ const containerRef = useRef<HTMLDivElement>(null);
45
+ const inputWrapRef = useRef<HTMLDivElement>(null);
46
+ const listRef = useRef<HTMLUListElement>(null);
47
+ const loadedRef = useRef(new Set<string>());
48
+ const batchRef = useRef<string[]>([]);
49
+ const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
50
+ const debounceRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
51
+ const [dropdownPos, setDropdownPos] = useState<{ top: number; left: number; width: number }>({
52
+ top: 0,
53
+ left: 0,
54
+ width: 0,
55
+ });
56
+
57
+ useEffect(() => {
58
+ clearTimeout(debounceRef.current);
59
+ if (!query) {
60
+ setDebouncedQuery("");
61
+ return;
62
+ }
63
+ debounceRef.current = setTimeout(() => setDebouncedQuery(query), 150);
64
+ return () => clearTimeout(debounceRef.current);
65
+ }, [query]);
66
+
67
+ const filtered = useMemo(
68
+ () =>
69
+ debouncedQuery
70
+ ? fonts.filter((f) => f.family.toLowerCase().includes(debouncedQuery.toLowerCase()))
71
+ : [],
72
+ [debouncedQuery],
73
+ );
74
+
75
+ const options: { family: string; display: string }[] = [
76
+ { family: "system-ui", display: "System Default" },
77
+ ...filtered.map((f) => ({ family: f.family, display: f.family })),
78
+ ];
79
+
80
+ const scheduleLoad = useCallback((family: string) => {
81
+ if (family === "system-ui" || loadedRef.current.has(family)) return;
82
+ loadedRef.current.add(family);
83
+ batchRef.current.push(family);
84
+ clearTimeout(timerRef.current);
85
+ timerRef.current = setTimeout(() => {
86
+ const batch = batchRef.current.splice(0);
87
+ if (batch.length > 0) loadFontPreview(batch);
88
+ }, 100);
89
+ }, []);
90
+
91
+ const observerRef = useRef<IntersectionObserver | undefined>(undefined);
92
+
93
+ useEffect(() => {
94
+ return () => {
95
+ clearTimeout(timerRef.current);
96
+ clearTimeout(debounceRef.current);
97
+ };
98
+ }, []);
99
+
100
+ useEffect(() => {
101
+ if (!open || typeof IntersectionObserver === "undefined") return;
102
+
103
+ observerRef.current = new IntersectionObserver(
104
+ (entries) => {
105
+ for (const entry of entries) {
106
+ if (entry.isIntersecting) {
107
+ const family = (entry.target as HTMLElement).dataset.family;
108
+ if (family) scheduleLoad(family);
109
+ }
110
+ }
111
+ },
112
+ { root: listRef.current, threshold: 0 },
113
+ );
114
+
115
+ const items = listRef.current?.querySelectorAll("[data-family]");
116
+ items?.forEach((el) => observerRef.current!.observe(el));
117
+
118
+ return () => observerRef.current?.disconnect();
119
+ }, [open, debouncedQuery, scheduleLoad]);
120
+
121
+ const dropdownRef = useRef<HTMLDivElement>(null);
122
+
123
+ useEffect(() => {
124
+ if (!open || !inputWrapRef.current) return;
125
+ function updatePosition() {
126
+ const rect = inputWrapRef.current!.getBoundingClientRect();
127
+ setDropdownPos({
128
+ top: rect.bottom + 4,
129
+ left: rect.left,
130
+ width: rect.width,
131
+ });
132
+ }
133
+ updatePosition();
134
+ window.addEventListener("scroll", updatePosition, true);
135
+ window.addEventListener("resize", updatePosition);
136
+ return () => {
137
+ window.removeEventListener("scroll", updatePosition, true);
138
+ window.removeEventListener("resize", updatePosition);
139
+ };
140
+ }, [open]);
141
+
142
+ useEffect(() => {
143
+ if (!open) return;
144
+ function handleClickOutside(e: MouseEvent) {
145
+ const target = e.target as Node;
146
+ if (
147
+ containerRef.current && !containerRef.current.contains(target) &&
148
+ dropdownRef.current && !dropdownRef.current.contains(target)
149
+ ) {
150
+ setOpen(false);
151
+ setQuery("");
152
+ }
153
+ }
154
+ document.addEventListener("mousedown", handleClickOutside);
155
+ return () => document.removeEventListener("mousedown", handleClickOutside);
156
+ }, [open]);
157
+
158
+ useEffect(() => {
159
+ setHighlightIndex(0);
160
+ }, [debouncedQuery]);
161
+
162
+ function handleSelect(family: string) {
163
+ onChange(family);
164
+ setOpen(false);
165
+ setQuery("");
166
+ }
167
+
168
+ function handleKeyDown(e: React.KeyboardEvent) {
169
+ if (!open) {
170
+ if (e.key === "ArrowDown" || e.key === "Enter") {
171
+ setOpen(true);
172
+ e.preventDefault();
173
+ }
174
+ return;
175
+ }
176
+ switch (e.key) {
177
+ case "ArrowDown":
178
+ e.preventDefault();
179
+ setHighlightIndex((i) => Math.min(i + 1, options.length - 1));
180
+ break;
181
+ case "ArrowUp":
182
+ e.preventDefault();
183
+ setHighlightIndex((i) => Math.max(i - 1, 0));
184
+ break;
185
+ case "Enter":
186
+ e.preventDefault();
187
+ if (options[highlightIndex]) handleSelect(options[highlightIndex].family);
188
+ break;
189
+ case "Escape":
190
+ e.preventDefault();
191
+ setOpen(false);
192
+ setQuery("");
193
+ break;
194
+ }
195
+ }
196
+
197
+ useEffect(() => {
198
+ if (!open || !listRef.current) return;
199
+ const el = listRef.current.children[highlightIndex] as HTMLElement | undefined;
200
+ el?.scrollIntoView?.({ block: "nearest" });
201
+ }, [highlightIndex, open]);
202
+
203
+ return (
204
+ <div ref={containerRef} className="relative">
205
+ <FormLabel htmlFor={id}>{label}</FormLabel>
206
+ <div ref={inputWrapRef} className="relative">
207
+ <input
208
+ id={id}
209
+ type="text"
210
+ role="combobox"
211
+ autoComplete="off"
212
+ aria-expanded={open}
213
+ aria-haspopup="listbox"
214
+ aria-autocomplete="list"
215
+ aria-controls={`${id}-listbox`}
216
+ aria-activedescendant={open ? `${id}-option-${highlightIndex}` : undefined}
217
+ value={open ? query : displayValue(value)}
218
+ onChange={(e) => {
219
+ setQuery(e.target.value);
220
+ if (!open) setOpen(true);
221
+ }}
222
+ onFocus={() => {
223
+ setOpen(true);
224
+ setQuery("");
225
+ }}
226
+ onKeyDown={handleKeyDown}
227
+ placeholder="Search fonts..."
228
+ className={cn(
229
+ "w-full rounded border border-base-200 bg-base py-2 pr-8 pl-3 text-sm text-base-contrast",
230
+ "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",
231
+ )}
232
+ style={
233
+ !open && value !== "system-ui" ? { fontFamily: `"${value}", system-ui, sans-serif` } : undefined
234
+ }
235
+ />
236
+ <ChevronDown
237
+ size={14}
238
+ className="pointer-events-none absolute right-2.5 top-1/2 -translate-y-1/2 text-base-contrast-light"
239
+ />
240
+ </div>
241
+ {open &&
242
+ createPortal(
243
+ <div
244
+ ref={dropdownRef}
245
+ className="rounded border border-base-200 bg-base shadow-lg"
246
+ style={{
247
+ position: "fixed",
248
+ top: dropdownPos.top,
249
+ left: dropdownPos.left,
250
+ width: dropdownPos.width,
251
+ zIndex: 9999,
252
+ }}
253
+ >
254
+ <ul
255
+ ref={listRef}
256
+ id={`${id}-listbox`}
257
+ role="listbox"
258
+ className="max-h-64 overflow-y-auto py-1"
259
+ >
260
+ {options.map((opt, i) => (
261
+ <li
262
+ key={opt.family}
263
+ id={`${id}-option-${i}`}
264
+ role="option"
265
+ aria-selected={opt.family === value}
266
+ data-family={opt.family}
267
+ onMouseDown={(e) => {
268
+ e.preventDefault();
269
+ handleSelect(opt.family);
270
+ }}
271
+ onMouseEnter={() => setHighlightIndex(i)}
272
+ className={cn(
273
+ "cursor-pointer px-3 py-1.5 text-sm",
274
+ i === highlightIndex && "bg-base-100",
275
+ opt.family === value && "font-medium text-base-contrast",
276
+ )}
277
+ style={
278
+ opt.family !== "system-ui"
279
+ ? { fontFamily: `"${opt.family}", system-ui, sans-serif` }
280
+ : undefined
281
+ }
282
+ >
283
+ {opt.display}
284
+ </li>
285
+ ))}
286
+ {debouncedQuery && filtered.length === 0 && (
287
+ <li className="px-3 py-1.5 text-sm text-base-contrast-light">No fonts found</li>
288
+ )}
289
+ {!debouncedQuery && (
290
+ <li className="px-3 py-1.5 text-sm text-base-contrast-light">
291
+ Type to search fonts...
292
+ </li>
293
+ )}
294
+ </ul>
295
+ </div>,
296
+ document.body,
297
+ )}
298
+ </div>
299
+ );
300
+ }
@@ -0,0 +1,18 @@
1
+ import { cn } from "../../lib/cn";
2
+
3
+ interface FormLabelProps {
4
+ htmlFor?: string;
5
+ children: React.ReactNode;
6
+ className?: string;
7
+ }
8
+
9
+ export function FormLabel({ htmlFor, children, className }: FormLabelProps) {
10
+ return (
11
+ <label
12
+ htmlFor={htmlFor}
13
+ className={cn("mb-1.5 block text-sm font-medium text-base-contrast", className)}
14
+ >
15
+ {children}
16
+ </label>
17
+ );
18
+ }
@@ -0,0 +1,45 @@
1
+ import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
2
+ import { cn } from "../../lib/cn";
3
+
4
+ type Size = "sm" | "md" | "lg";
5
+ type Intent = "default" | "destructive" | "primary";
6
+
7
+ interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
8
+ icon: ReactNode;
9
+ label: string;
10
+ size?: Size;
11
+ intent?: Intent;
12
+ }
13
+
14
+ const sizeClasses: Record<Size, string> = {
15
+ sm: "h-chrome-sm w-chrome-sm",
16
+ md: "h-chrome-md w-chrome-md",
17
+ lg: "h-chrome-lg w-chrome-lg",
18
+ };
19
+
20
+ const intentClasses: Record<Intent, string> = {
21
+ default: "text-base-contrast-light hover:text-base-contrast",
22
+ destructive: "text-destructive hover:text-destructive-hover",
23
+ primary: "text-primary/60 hover:text-primary",
24
+ };
25
+
26
+ export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
27
+ function IconButton({ icon, label, size = "md", intent = "default", className, ...rest }, ref) {
28
+ return (
29
+ <button
30
+ ref={ref}
31
+ type="button"
32
+ className={cn(
33
+ "cursor-pointer flex items-center justify-center rounded transition-colors",
34
+ sizeClasses[size],
35
+ intentClasses[intent],
36
+ className,
37
+ )}
38
+ aria-label={label}
39
+ {...rest}
40
+ >
41
+ {icon}
42
+ </button>
43
+ );
44
+ },
45
+ );
@@ -0,0 +1,34 @@
1
+ import { forwardRef, useId, type InputHTMLAttributes } from "react";
2
+ import { cn } from "../../lib/cn";
3
+ import { FormLabel } from "./FormLabel";
4
+
5
+ interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange"> {
6
+ label: string;
7
+ onChange: (value: string) => void;
8
+ }
9
+
10
+ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
11
+ { label, value, onChange, type = "text", className, disabled, ...rest },
12
+ ref,
13
+ ) {
14
+ const id = useId();
15
+ return (
16
+ <div className={className}>
17
+ <FormLabel htmlFor={id}>{label}</FormLabel>
18
+ <input
19
+ ref={ref}
20
+ id={id}
21
+ type={type}
22
+ value={value}
23
+ onChange={(e) => onChange(e.target.value)}
24
+ disabled={disabled}
25
+ className={cn(
26
+ "w-full rounded border border-base-200 bg-base px-3 py-2 text-sm text-base-contrast",
27
+ "focus:border-base-contrast focus:outline-none focus:ring-1 focus:ring-base-contrast",
28
+ disabled && "cursor-not-allowed opacity-50",
29
+ )}
30
+ {...rest}
31
+ />
32
+ </div>
33
+ );
34
+ });