@lattice-php/lattice 0.18.0 → 0.20.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 (255) hide show
  1. package/README.md +1 -1
  2. package/dist/core/index.js +3 -3
  3. package/dist/core/nodes.d.ts +5 -0
  4. package/dist/core/nodes.js +8 -1
  5. package/dist/core/nodes.js.map +1 -1
  6. package/dist/core/renderer.js +1 -3
  7. package/dist/core/renderer.js.map +1 -1
  8. package/dist/core/types.d.ts +16 -4
  9. package/dist/create-app.d.ts +31 -3
  10. package/dist/create-app.js +48 -10
  11. package/dist/create-app.js.map +1 -1
  12. package/dist/effects/registry.d.ts +3 -3
  13. package/dist/effects/registry.js +4 -4
  14. package/dist/effects/registry.js.map +1 -1
  15. package/dist/events/event-names.d.ts +0 -3
  16. package/dist/events/event-names.js +0 -3
  17. package/dist/events/event-names.js.map +1 -1
  18. package/dist/form/components/base/field.d.ts +2 -2
  19. package/dist/form/components/base/field.js +2 -2
  20. package/dist/form/components/base/field.js.map +1 -1
  21. package/dist/form/components/fields/color-picker-field.d.ts +2 -0
  22. package/dist/form/components/fields/color-picker-field.js +57 -0
  23. package/dist/form/components/fields/color-picker-field.js.map +1 -0
  24. package/dist/form/components/fields/date-input.js +1 -1
  25. package/dist/form/components/fields/date-picker-field.js +1 -1
  26. package/dist/form/components/fields/date-time-input.js +1 -1
  27. package/dist/form/components/fields/file-upload.js +7 -12
  28. package/dist/form/components/fields/file-upload.js.map +1 -1
  29. package/dist/form/components/fields/number-input.js +1 -1
  30. package/dist/form/components/fields/rich-editor-field.js +36 -275
  31. package/dist/form/components/fields/rich-editor-field.js.map +1 -1
  32. package/dist/form/components/fields/row-action-menu.js +2 -2
  33. package/dist/form/components/fields/row-action-menu.js.map +1 -1
  34. package/dist/form/components/fields/row-actions.js +3 -1
  35. package/dist/form/components/fields/row-actions.js.map +1 -1
  36. package/dist/form/components/fields/row-item.js +2 -1
  37. package/dist/form/components/fields/row-item.js.map +1 -1
  38. package/dist/form/components/fields/select.js +59 -16
  39. package/dist/form/components/fields/select.js.map +1 -1
  40. package/dist/form/components/fields/table-rows.js +5 -4
  41. package/dist/form/components/fields/table-rows.js.map +1 -1
  42. package/dist/form/components/fields/text-input.js +8 -1
  43. package/dist/form/components/fields/text-input.js.map +1 -1
  44. package/dist/form/components/fields/time-input.js +1 -1
  45. package/dist/form/components/index.d.ts +1 -0
  46. package/dist/form/components/index.js +2 -1
  47. package/dist/form/embed.d.ts +1 -0
  48. package/dist/form/embed.js +2 -1
  49. package/dist/form/hooks/use-form-resolver.js.map +1 -1
  50. package/dist/form/index.js +2 -1
  51. package/dist/form/lib/conditions.d.ts +4 -10
  52. package/dist/form/lib/conditions.js.map +1 -1
  53. package/dist/form/lib/field-props.d.ts +4 -18
  54. package/dist/form/lib/field-props.js.map +1 -1
  55. package/dist/form/plugin.js +2 -0
  56. package/dist/form/plugin.js.map +1 -1
  57. package/dist/form/rich-editor/builtins.d.ts +6 -0
  58. package/dist/form/rich-editor/builtins.js +400 -0
  59. package/dist/form/rich-editor/builtins.js.map +1 -0
  60. package/dist/form/rich-editor/index.d.ts +7 -0
  61. package/dist/form/rich-editor/index.js +3 -0
  62. package/dist/form/rich-editor/registry.d.ts +67 -0
  63. package/dist/form/rich-editor/registry.js +79 -0
  64. package/dist/form/rich-editor/registry.js.map +1 -0
  65. package/dist/form/rich-editor/toolbar-button.d.ts +12 -0
  66. package/dist/form/rich-editor/toolbar-button.js +28 -0
  67. package/dist/form/rich-editor/toolbar-button.js.map +1 -0
  68. package/dist/form/types.d.ts +1 -6
  69. package/dist/format/date-time.d.ts +3 -2
  70. package/dist/format/date-time.js.map +1 -1
  71. package/dist/i18n/backend.js +1 -1
  72. package/dist/i18n/date-time.d.ts +3 -2
  73. package/dist/i18n/date-time.js.map +1 -1
  74. package/dist/i18n/index.js +3 -2
  75. package/dist/i18n/instance.js +1 -1
  76. package/dist/i18n/page-props.d.ts +3 -2
  77. package/dist/i18n/page-props.js +1 -15
  78. package/dist/i18n/page-props.js.map +1 -1
  79. package/dist/i18n/shared-props.d.ts +7 -0
  80. package/dist/i18n/shared-props.js +25 -0
  81. package/dist/i18n/shared-props.js.map +1 -0
  82. package/dist/icons/icon-renderer.js.map +1 -1
  83. package/dist/index.d.ts +5 -3
  84. package/dist/index.js +7 -6
  85. package/dist/lattice.css +119 -57
  86. package/dist/layout/components/breadcrumbs.js +3 -1
  87. package/dist/layout/components/breadcrumbs.js.map +1 -1
  88. package/dist/layout/components/callouts.js +1 -1
  89. package/dist/lib/color.d.ts +9 -0
  90. package/dist/lib/color.js +48 -0
  91. package/dist/lib/color.js.map +1 -0
  92. package/dist/notifications/api.d.ts +6 -6
  93. package/dist/notifications/api.js.map +1 -1
  94. package/dist/notifications/types.d.ts +2 -10
  95. package/dist/provider-base.js +1 -1
  96. package/dist/realtime/build-effects.js +5 -5
  97. package/dist/realtime/build-effects.js.map +1 -1
  98. package/dist/realtime/listeners.d.ts +2 -2
  99. package/dist/realtime/listeners.js.map +1 -1
  100. package/dist/realtime/subscriptions.d.ts +2 -2
  101. package/dist/realtime/subscriptions.js.map +1 -1
  102. package/dist/registry.js.map +1 -1
  103. package/dist/table/components/bulk-bar.js +5 -5
  104. package/dist/table/components/bulk-bar.js.map +1 -1
  105. package/dist/table/components/cells/badge-cell.js +4 -1
  106. package/dist/table/components/cells/badge-cell.js.map +1 -1
  107. package/dist/table/components/cells/boolean-cell.js +3 -1
  108. package/dist/table/components/cells/boolean-cell.js.map +1 -1
  109. package/dist/table/components/cells/icon-cell.js +5 -2
  110. package/dist/table/components/cells/icon-cell.js.map +1 -1
  111. package/dist/table/components/cells/image-cell.js +8 -9
  112. package/dist/table/components/cells/image-cell.js.map +1 -1
  113. package/dist/table/components/cells/money-cell.js +9 -1
  114. package/dist/table/components/cells/money-cell.js.map +1 -1
  115. package/dist/table/components/cells/number-cell.js +9 -1
  116. package/dist/table/components/cells/number-cell.js.map +1 -1
  117. package/dist/table/components/cells/text-cell.js +4 -1
  118. package/dist/table/components/cells/text-cell.js.map +1 -1
  119. package/dist/table/components/column-filter-control.js +25 -32
  120. package/dist/table/components/column-filter-control.js.map +1 -1
  121. package/dist/table/components/column-header.d.ts +3 -3
  122. package/dist/table/components/column-header.js +5 -3
  123. package/dist/table/components/column-header.js.map +1 -1
  124. package/dist/table/components/column-visibility-menu.js +6 -11
  125. package/dist/table/components/column-visibility-menu.js.map +1 -1
  126. package/dist/table/components/filter-bar.d.ts +2 -2
  127. package/dist/table/components/filter-bar.js +13 -22
  128. package/dist/table/components/filter-bar.js.map +1 -1
  129. package/dist/table/components/filter-controls.d.ts +2 -1
  130. package/dist/table/components/filter-controls.js +36 -35
  131. package/dist/table/components/filter-controls.js.map +1 -1
  132. package/dist/table/components/filter-value-input.d.ts +0 -1
  133. package/dist/table/components/filter-value-input.js +21 -20
  134. package/dist/table/components/filter-value-input.js.map +1 -1
  135. package/dist/table/components/pagination.js +16 -20
  136. package/dist/table/components/pagination.js.map +1 -1
  137. package/dist/table/components/sort-bar.d.ts +3 -3
  138. package/dist/table/components/sort-bar.js +11 -12
  139. package/dist/table/components/sort-bar.js.map +1 -1
  140. package/dist/table/components/table-search.d.ts +9 -0
  141. package/dist/table/components/table-search.js +75 -0
  142. package/dist/table/components/table-search.js.map +1 -0
  143. package/dist/table/components/table.js +72 -19
  144. package/dist/table/components/table.js.map +1 -1
  145. package/dist/table/hooks/use-expanded-rows.d.ts +8 -0
  146. package/dist/table/hooks/use-expanded-rows.js +24 -0
  147. package/dist/table/hooks/use-expanded-rows.js.map +1 -0
  148. package/dist/table/hooks/use-table.d.ts +5 -3
  149. package/dist/table/hooks/use-table.js +56 -44
  150. package/dist/table/hooks/use-table.js.map +1 -1
  151. package/dist/table/lib/payload.d.ts +3 -2
  152. package/dist/table/lib/payload.js +16 -10
  153. package/dist/table/lib/payload.js.map +1 -1
  154. package/dist/table/lib/query.d.ts +5 -5
  155. package/dist/table/lib/query.js +20 -18
  156. package/dist/table/lib/query.js.map +1 -1
  157. package/dist/table/types.d.ts +3 -26
  158. package/dist/toast/callout.js +1 -3
  159. package/dist/toast/callout.js.map +1 -1
  160. package/dist/toast/toast.d.ts +1 -5
  161. package/dist/toast/toast.js +2 -7
  162. package/dist/toast/toast.js.map +1 -1
  163. package/dist/toast/toaster.js +2 -2
  164. package/dist/toast/toaster.js.map +1 -1
  165. package/dist/types/generated.d.ts +263 -188
  166. package/dist/ui/affix-group.d.ts +2 -1
  167. package/dist/ui/affix-group.js +9 -7
  168. package/dist/ui/affix-group.js.map +1 -1
  169. package/dist/ui/avatar.d.ts +3 -0
  170. package/dist/ui/avatar.js +52 -0
  171. package/dist/ui/avatar.js.map +1 -0
  172. package/dist/ui/button.d.ts +3 -1
  173. package/dist/ui/button.js +10 -3
  174. package/dist/ui/button.js.map +1 -1
  175. package/dist/ui/chart-view.js +161 -11
  176. package/dist/ui/chart-view.js.map +1 -1
  177. package/dist/ui/click-behavior.d.ts +1 -1
  178. package/dist/ui/click-behavior.js.map +1 -1
  179. package/dist/ui/collapsible.js +1 -1
  180. package/dist/ui/color-picker.d.ts +8 -0
  181. package/dist/ui/color-picker.js +59 -0
  182. package/dist/ui/color-picker.js.map +1 -0
  183. package/dist/ui/combobox.d.ts +11 -2
  184. package/dist/ui/combobox.js +51 -8
  185. package/dist/ui/combobox.js.map +1 -1
  186. package/dist/ui/copyable-text.d.ts +7 -0
  187. package/dist/ui/copyable-text.js +21 -13
  188. package/dist/ui/copyable-text.js.map +1 -1
  189. package/dist/ui/fragment.js +1 -1
  190. package/dist/ui/fragment.js.map +1 -1
  191. package/dist/ui/heading.js +39 -21
  192. package/dist/ui/heading.js.map +1 -1
  193. package/dist/ui/icon-button.d.ts +22 -0
  194. package/dist/ui/icon-button.js +51 -0
  195. package/dist/ui/icon-button.js.map +1 -0
  196. package/dist/ui/icon.js +10 -11
  197. package/dist/ui/icon.js.map +1 -1
  198. package/dist/ui/image-preview.d.ts +12 -0
  199. package/dist/ui/image-preview.js +65 -0
  200. package/dist/ui/image-preview.js.map +1 -0
  201. package/dist/ui/image.d.ts +3 -0
  202. package/dist/ui/image.js +21 -0
  203. package/dist/ui/image.js.map +1 -0
  204. package/dist/ui/index.d.ts +2 -1
  205. package/dist/ui/index.js +4 -3
  206. package/dist/ui/info-tooltip.js +1 -1
  207. package/dist/ui/info-tooltip.js.map +1 -1
  208. package/dist/ui/input.d.ts +3 -1
  209. package/dist/ui/input.js +2 -2
  210. package/dist/ui/input.js.map +1 -1
  211. package/dist/ui/modal.js +14 -2
  212. package/dist/ui/modal.js.map +1 -1
  213. package/dist/ui/native-select.d.ts +10 -0
  214. package/dist/ui/native-select.js +23 -0
  215. package/dist/ui/native-select.js.map +1 -0
  216. package/dist/ui/plugin.js +11 -1
  217. package/dist/ui/plugin.js.map +1 -1
  218. package/dist/ui/progress.d.ts +3 -0
  219. package/dist/ui/progress.js +114 -0
  220. package/dist/ui/progress.js.map +1 -0
  221. package/dist/ui/section.js +1 -1
  222. package/dist/ui/separator.d.ts +3 -0
  223. package/dist/ui/separator.js +15 -0
  224. package/dist/ui/separator.js.map +1 -0
  225. package/dist/ui/text.js +5 -10
  226. package/dist/ui/text.js.map +1 -1
  227. package/dist/ui/tooltip.js +1 -1
  228. package/dist/ui/tree-context.d.ts +33 -0
  229. package/dist/ui/tree-context.js +137 -0
  230. package/dist/ui/tree-context.js.map +1 -0
  231. package/dist/ui/tree.d.ts +19 -0
  232. package/dist/ui/tree.js +187 -0
  233. package/dist/ui/tree.js.map +1 -0
  234. package/dist-standalone/chunks/chart-view-wJmnVu7L.js +36 -0
  235. package/dist-standalone/chunks/date-picker-field-VbH9xrQO.js +1 -0
  236. package/dist-standalone/chunks/dist-PaT1gYi_.js +2 -0
  237. package/dist-standalone/chunks/jsx-runtime-Drll_-DD.js +1 -0
  238. package/dist-standalone/chunks/locale-CXqr7LPR.js +1 -0
  239. package/dist-standalone/chunks/notifications-echo-DsfNc4Ge.js +1 -0
  240. package/dist-standalone/chunks/page-props-AIa5UR6n.js +1 -0
  241. package/dist-standalone/chunks/react-B8IZ02wI.js +1 -0
  242. package/dist-standalone/chunks/rich-editor-field-DLQTyt36.js +154 -0
  243. package/dist-standalone/chunks/subscriptions-DY2tj9Im.js +1 -0
  244. package/dist-standalone/chunks/test-id-Dh3L6Gfh.js +1 -0
  245. package/dist-standalone/chunks/use-effect-dispatcher-feEs0GCP.js +97 -0
  246. package/dist-standalone/chunks/with-selector-BwDoCrJJ.js +1 -0
  247. package/dist-standalone/lattice.css +3 -0
  248. package/dist-standalone/lattice.js +66 -0
  249. package/dist-standalone/manifest.json +20 -0
  250. package/dist-standalone/sprite.svg +1 -0
  251. package/package.json +11 -2
  252. package/resources/icons/code-xml.svg +17 -0
  253. package/resources/icons/heading.svg +17 -0
  254. package/dist/action/hooks/use-click-behavior.d.ts +0 -3
  255. package/dist/action/hooks/use-click-behavior.js +0 -3
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/form/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { FormNodeType } from \"@lattice-php/lattice/types/generated\";\nimport {\n BuilderComponent,\n CheckboxComponent,\n ChoiceComponent,\n DateInputComponent,\n DateTimeInputComponent,\n FileUploadComponent,\n FormComponent,\n HiddenInputComponent,\n NumberInputComponent,\n OtpInputComponent,\n PasswordInputComponent,\n RepeaterComponent,\n SelectComponent,\n TextareaComponent,\n TextInputComponent,\n TimeInputComponent,\n ToggleComponent,\n} from \"./components\";\nimport { RichEditorComponent } from \"./components/fields/rich-editor\";\n\nexport const formComponents = createPlugin({\n components: {\n form: eagerComponent(FormComponent),\n \"field.builder\": eagerComponent(BuilderComponent),\n \"field.checkbox\": eagerComponent(CheckboxComponent),\n \"field.choice\": eagerComponent(ChoiceComponent),\n \"field.date-input\": eagerComponent(DateInputComponent),\n \"field.date-time-input\": eagerComponent(DateTimeInputComponent),\n \"field.file-upload\": eagerComponent(FileUploadComponent),\n \"field.hidden-input\": eagerComponent(HiddenInputComponent),\n \"field.number-input\": eagerComponent(NumberInputComponent),\n \"field.otp\": eagerComponent(OtpInputComponent),\n \"field.password-input\": eagerComponent(PasswordInputComponent),\n \"field.repeater\": eagerComponent(RepeaterComponent),\n \"field.rich-editor\": eagerComponent(RichEditorComponent),\n \"field.select\": eagerComponent(SelectComponent),\n \"field.textarea\": eagerComponent(TextareaComponent),\n \"field.text-input\": eagerComponent(TextInputComponent),\n \"field.time-input\": eagerComponent(TimeInputComponent),\n \"field.toggle\": eagerComponent(ToggleComponent),\n } satisfies ComponentRegistryFor<FormNodeType>,\n name: \"lattice/form\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,MAAM,eAAe,aAAa;EAClC,iBAAiB,eAAe,gBAAgB;EAChD,kBAAkB,eAAe,iBAAiB;EAClD,gBAAgB,eAAe,eAAe;EAC9C,oBAAoB,eAAe,kBAAkB;EACrD,yBAAyB,eAAe,sBAAsB;EAC9D,qBAAqB,eAAe,mBAAmB;EACvD,sBAAsB,eAAe,oBAAoB;EACzD,sBAAsB,eAAe,oBAAoB;EACzD,aAAa,eAAe,iBAAiB;EAC7C,wBAAwB,eAAe,sBAAsB;EAC7D,kBAAkB,eAAe,iBAAiB;EAClD,qBAAqB,eAAe,mBAAmB;EACvD,gBAAgB,eAAe,eAAe;EAC9C,kBAAkB,eAAe,iBAAiB;EAClD,oBAAoB,eAAe,kBAAkB;EACrD,oBAAoB,eAAe,kBAAkB;EACrD,gBAAgB,eAAe,eAAe;CAChD;CACA,MAAM;AACR,CAAC"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../../resources/js/form/plugin.ts"],"sourcesContent":["import {\n createPlugin,\n eagerComponent,\n type ComponentRegistryFor,\n} from \"@lattice-php/lattice/core/registry\";\nimport type { FormNodeType } from \"@lattice-php/lattice/types/generated\";\nimport {\n BuilderComponent,\n CheckboxComponent,\n ChoiceComponent,\n ColorPickerFieldComponent,\n DateInputComponent,\n DateTimeInputComponent,\n FileUploadComponent,\n FormComponent,\n HiddenInputComponent,\n NumberInputComponent,\n OtpInputComponent,\n PasswordInputComponent,\n RepeaterComponent,\n SelectComponent,\n TextareaComponent,\n TextInputComponent,\n TimeInputComponent,\n ToggleComponent,\n} from \"./components\";\nimport { RichEditorComponent } from \"./components/fields/rich-editor\";\n\nexport const formComponents = createPlugin({\n components: {\n form: eagerComponent(FormComponent),\n \"field.builder\": eagerComponent(BuilderComponent),\n \"field.checkbox\": eagerComponent(CheckboxComponent),\n \"field.choice\": eagerComponent(ChoiceComponent),\n \"field.color-picker\": eagerComponent(ColorPickerFieldComponent),\n \"field.date-input\": eagerComponent(DateInputComponent),\n \"field.date-time-input\": eagerComponent(DateTimeInputComponent),\n \"field.file-upload\": eagerComponent(FileUploadComponent),\n \"field.hidden-input\": eagerComponent(HiddenInputComponent),\n \"field.number-input\": eagerComponent(NumberInputComponent),\n \"field.otp\": eagerComponent(OtpInputComponent),\n \"field.password-input\": eagerComponent(PasswordInputComponent),\n \"field.repeater\": eagerComponent(RepeaterComponent),\n \"field.rich-editor\": eagerComponent(RichEditorComponent),\n \"field.select\": eagerComponent(SelectComponent),\n \"field.textarea\": eagerComponent(TextareaComponent),\n \"field.text-input\": eagerComponent(TextInputComponent),\n \"field.time-input\": eagerComponent(TimeInputComponent),\n \"field.toggle\": eagerComponent(ToggleComponent),\n } satisfies ComponentRegistryFor<FormNodeType>,\n name: \"lattice/form\",\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA4BA,IAAa,iBAAiB,aAAa;CACzC,YAAY;EACV,MAAM,eAAe,aAAa;EAClC,iBAAiB,eAAe,gBAAgB;EAChD,kBAAkB,eAAe,iBAAiB;EAClD,gBAAgB,eAAe,eAAe;EAC9C,sBAAsB,eAAe,yBAAyB;EAC9D,oBAAoB,eAAe,kBAAkB;EACrD,yBAAyB,eAAe,sBAAsB;EAC9D,qBAAqB,eAAe,mBAAmB;EACvD,sBAAsB,eAAe,oBAAoB;EACzD,sBAAsB,eAAe,oBAAoB;EACzD,aAAa,eAAe,iBAAiB;EAC7C,wBAAwB,eAAe,sBAAsB;EAC7D,kBAAkB,eAAe,iBAAiB;EAClD,qBAAqB,eAAe,mBAAmB;EACvD,gBAAgB,eAAe,eAAe;EAC9C,kBAAkB,eAAe,iBAAiB;EAClD,oBAAoB,eAAe,kBAAkB;EACrD,oBAAoB,eAAe,kBAAkB;EACrD,gBAAgB,eAAe,eAAe;CAChD;CACA,MAAM;AACR,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Called by the editor field when its chunk loads. Registration must be an
3
+ * explicitly invoked export: the package ships side-effect-free modules, so a
4
+ * bare `import "./builtins.js"` would be tree-shaken out of production builds.
5
+ */
6
+ export declare function registerBuiltinRichEditorExtensions(): void;
@@ -0,0 +1,400 @@
1
+ import { cn } from "../../lib/utils.js";
2
+ import { useT } from "../../i18n/instance.js";
3
+ import { Popover, PopoverContent, PopoverTrigger } from "../../ui/popover.js";
4
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../ui/dropdown-menu.js";
5
+ import { seedRichEditorExtension } from "./registry.js";
6
+ import { ToolbarIconButton } from "./toolbar-button.js";
7
+ import { useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { Details, DetailsContent, DetailsSummary } from "@tiptap/extension-details";
10
+ import { Highlight } from "@tiptap/extension-highlight";
11
+ import { TableKit } from "@tiptap/extension-table";
12
+ import { TextAlign } from "@tiptap/extension-text-align";
13
+ //#region resources/js/form/rich-editor/builtins.tsx
14
+ var ALL_HEADING_LEVELS = [
15
+ 1,
16
+ 2,
17
+ 3,
18
+ 4,
19
+ 5,
20
+ 6
21
+ ];
22
+ var ALL_ALIGNMENTS = [
23
+ "left",
24
+ "center",
25
+ "right",
26
+ "justify"
27
+ ];
28
+ var DEFAULT_EMOJIS = [
29
+ "😀",
30
+ "😅",
31
+ "😂",
32
+ "🥳",
33
+ "😎",
34
+ "🤔",
35
+ "👍",
36
+ "🙏",
37
+ "🔥",
38
+ "🎉",
39
+ "🚀",
40
+ "💡",
41
+ "✅",
42
+ "❌",
43
+ "⭐",
44
+ "❤️"
45
+ ];
46
+ function headingLevels(levels) {
47
+ const valid = (levels ?? ALL_HEADING_LEVELS).filter((level) => level >= 1 && level <= 6);
48
+ return valid.length > 0 ? valid : ALL_HEADING_LEVELS;
49
+ }
50
+ function HeadingMenu({ editor, levels }) {
51
+ const { t } = useT("lattice");
52
+ const label = t("form.editor.heading", "Heading");
53
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
54
+ asChild: true,
55
+ children: /* @__PURE__ */ jsx(ToolbarIconButton, {
56
+ active: editor.isActive("heading"),
57
+ icon: "heading",
58
+ label,
59
+ testId: "editor-heading"
60
+ })
61
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
62
+ className: "min-w-32",
63
+ children: levels.map((level) => /* @__PURE__ */ jsx(DropdownMenuItem, {
64
+ className: cn(editor.isActive("heading", { level }) && "bg-lt-accent text-lt-accent-fg"),
65
+ "data-test": `editor-heading-${level}`,
66
+ onClick: () => editor.chain().focus().toggleHeading({ level }).run(),
67
+ children: t(`form.editor.heading-${level}`, `Heading ${level}`)
68
+ }, level))
69
+ })] });
70
+ }
71
+ function LinkControl({ editor }) {
72
+ const { t } = useT("lattice");
73
+ const [open, setOpen] = useState(false);
74
+ const [url, setUrl] = useState("");
75
+ const apply = () => {
76
+ if (url) editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
77
+ else editor.chain().focus().extendMarkRange("link").unsetLink().run();
78
+ setOpen(false);
79
+ };
80
+ return /* @__PURE__ */ jsxs(Popover, {
81
+ onOpenChange: (next) => {
82
+ if (next) setUrl(editor.getAttributes("link").href ?? "");
83
+ setOpen(next);
84
+ },
85
+ open,
86
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
87
+ asChild: true,
88
+ children: /* @__PURE__ */ jsx(ToolbarIconButton, {
89
+ active: editor.isActive("link"),
90
+ icon: "link",
91
+ label: t("form.editor.link", "Link"),
92
+ testId: "editor-link"
93
+ })
94
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
95
+ className: "flex w-72 items-center gap-1 p-2",
96
+ children: [
97
+ /* @__PURE__ */ jsx("input", {
98
+ "aria-label": t("form.editor.link-url", "Link URL"),
99
+ className: "h-7 min-w-0 flex-1 rounded-lt-sm border border-lt-input bg-transparent px-2 text-sm outline-none focus:border-lt-ring",
100
+ "data-test": "editor-link-url",
101
+ onChange: (event) => setUrl(event.target.value),
102
+ onKeyDown: (event) => {
103
+ if (event.key === "Enter") {
104
+ event.preventDefault();
105
+ apply();
106
+ }
107
+ },
108
+ placeholder: "https://",
109
+ type: "url",
110
+ value: url
111
+ }),
112
+ /* @__PURE__ */ jsx(ToolbarIconButton, {
113
+ icon: "check",
114
+ label: t("form.editor.link-apply", "Apply link"),
115
+ onClick: apply,
116
+ testId: "editor-link-apply"
117
+ }),
118
+ editor.isActive("link") && /* @__PURE__ */ jsx(ToolbarIconButton, {
119
+ icon: "trash-2",
120
+ label: t("form.editor.link-remove", "Remove link"),
121
+ onClick: () => {
122
+ editor.chain().focus().extendMarkRange("link").unsetLink().run();
123
+ setOpen(false);
124
+ },
125
+ testId: "editor-link-remove"
126
+ })
127
+ ]
128
+ })]
129
+ });
130
+ }
131
+ function EmojiPicker({ editor, emojis }) {
132
+ const { t } = useT("lattice");
133
+ const [open, setOpen] = useState(false);
134
+ return /* @__PURE__ */ jsxs(Popover, {
135
+ onOpenChange: setOpen,
136
+ open,
137
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
138
+ asChild: true,
139
+ children: /* @__PURE__ */ jsx(ToolbarIconButton, {
140
+ icon: "smile",
141
+ label: t("form.editor.insert-emoji", "Insert emoji"),
142
+ testId: "editor-emoji"
143
+ })
144
+ }), /* @__PURE__ */ jsx(PopoverContent, {
145
+ className: "grid grid-cols-8 gap-0.5 p-1",
146
+ children: emojis.map((emoji) => /* @__PURE__ */ jsx("button", {
147
+ className: "inline-flex size-7 items-center justify-center rounded-lt-sm text-base hover:bg-lt-accent",
148
+ onClick: () => {
149
+ editor.chain().focus().insertContent(emoji).run();
150
+ setOpen(false);
151
+ },
152
+ onMouseDown: (event) => event.preventDefault(),
153
+ type: "button",
154
+ children: emoji
155
+ }, emoji))
156
+ })]
157
+ });
158
+ }
159
+ function markButton(key, icon, label, mark) {
160
+ return {
161
+ icon,
162
+ key,
163
+ label,
164
+ isActive: (editor) => editor.isActive(mark),
165
+ run: (editor) => editor.chain().focus().toggleMark(mark).run()
166
+ };
167
+ }
168
+ var registered = false;
169
+ /**
170
+ * Called by the editor field when its chunk loads. Registration must be an
171
+ * explicitly invoked export: the package ships side-effect-free modules, so a
172
+ * bare `import "./builtins"` would be tree-shaken out of production builds.
173
+ */
174
+ function registerBuiltinRichEditorExtensions() {
175
+ if (registered) return;
176
+ registered = true;
177
+ seedRichEditorExtension("bold", {
178
+ group: "marks",
179
+ starterKit: () => ({ bold: {} }),
180
+ toolbar: () => [markButton("bold", "bold", "Bold", "bold")]
181
+ });
182
+ seedRichEditorExtension("italic", {
183
+ group: "marks",
184
+ starterKit: () => ({ italic: {} }),
185
+ toolbar: () => [markButton("italic", "italic", "Italic", "italic")]
186
+ });
187
+ seedRichEditorExtension("strike", {
188
+ group: "marks",
189
+ starterKit: () => ({ strike: {} }),
190
+ toolbar: () => [markButton("strikethrough", "strikethrough", "Strikethrough", "strike")]
191
+ });
192
+ seedRichEditorExtension("underline", {
193
+ group: "marks",
194
+ starterKit: () => ({ underline: {} }),
195
+ toolbar: () => [markButton("underline", "underline", "Underline", "underline")]
196
+ });
197
+ seedRichEditorExtension("highlight", {
198
+ group: "marks",
199
+ extensions: () => [Highlight],
200
+ toolbar: () => [markButton("highlight", "highlighter", "Highlight", "highlight")]
201
+ });
202
+ seedRichEditorExtension("code", {
203
+ group: "marks",
204
+ starterKit: () => ({ code: {} }),
205
+ toolbar: () => [markButton("code", "code-xml", "Code", "code")]
206
+ });
207
+ seedRichEditorExtension("heading", {
208
+ starterKit: (props) => ({ heading: { levels: headingLevels(props.levels) } }),
209
+ toolbar: (props) => {
210
+ const levels = headingLevels(props.levels);
211
+ return [{
212
+ key: "heading",
213
+ component: ({ editor }) => /* @__PURE__ */ jsx(HeadingMenu, {
214
+ editor,
215
+ levels
216
+ })
217
+ }];
218
+ }
219
+ });
220
+ seedRichEditorExtension("bullet-list", {
221
+ group: "blocks",
222
+ starterKit: () => ({
223
+ bulletList: {},
224
+ listItem: {}
225
+ }),
226
+ toolbar: () => [{
227
+ icon: "list",
228
+ key: "bullet-list",
229
+ label: "Bullet list",
230
+ isActive: (editor) => editor.isActive("bulletList"),
231
+ run: (editor) => editor.chain().focus().toggleBulletList().run()
232
+ }]
233
+ });
234
+ seedRichEditorExtension("ordered-list", {
235
+ group: "blocks",
236
+ starterKit: () => ({
237
+ orderedList: {},
238
+ listItem: {}
239
+ }),
240
+ toolbar: () => [{
241
+ icon: "list-ordered",
242
+ key: "ordered-list",
243
+ label: "Ordered list",
244
+ isActive: (editor) => editor.isActive("orderedList"),
245
+ run: (editor) => editor.chain().focus().toggleOrderedList().run()
246
+ }]
247
+ });
248
+ seedRichEditorExtension("blockquote", {
249
+ group: "blocks",
250
+ starterKit: () => ({ blockquote: {} }),
251
+ toolbar: () => [{
252
+ icon: "quote",
253
+ key: "blockquote",
254
+ label: "Blockquote",
255
+ isActive: (editor) => editor.isActive("blockquote"),
256
+ run: (editor) => editor.chain().focus().toggleBlockquote().run()
257
+ }]
258
+ });
259
+ seedRichEditorExtension("code-block", {
260
+ group: "blocks",
261
+ starterKit: () => ({ codeBlock: {} }),
262
+ toolbar: () => [{
263
+ icon: "code",
264
+ key: "code-block",
265
+ label: "Code block",
266
+ isActive: (editor) => editor.isActive("codeBlock"),
267
+ run: (editor) => editor.chain().focus().toggleCodeBlock().run()
268
+ }]
269
+ });
270
+ seedRichEditorExtension("horizontal-rule", {
271
+ group: "blocks",
272
+ starterKit: () => ({ horizontalRule: {} }),
273
+ toolbar: () => [{
274
+ icon: "minus",
275
+ key: "horizontal-rule",
276
+ label: "Horizontal rule",
277
+ isActive: () => false,
278
+ run: (editor) => editor.chain().focus().setHorizontalRule().run()
279
+ }]
280
+ });
281
+ const ALIGNMENT_BUTTONS = {
282
+ left: {
283
+ icon: "align-left",
284
+ key: "align-left",
285
+ label: "Align left"
286
+ },
287
+ center: {
288
+ icon: "align-center",
289
+ key: "align-center",
290
+ label: "Align center"
291
+ },
292
+ right: {
293
+ icon: "align-right",
294
+ key: "align-right",
295
+ label: "Align right"
296
+ },
297
+ justify: {
298
+ icon: "align-justify",
299
+ key: "justify",
300
+ label: "Justify"
301
+ }
302
+ };
303
+ seedRichEditorExtension("text-align", {
304
+ extensions: (props) => [TextAlign.configure({
305
+ alignments: props.alignments ?? ALL_ALIGNMENTS,
306
+ types: ["heading", "paragraph"]
307
+ })],
308
+ toolbar: (props) => (props.alignments ?? ALL_ALIGNMENTS).filter((alignment) => alignment in ALIGNMENT_BUTTONS).map((alignment) => ({
309
+ ...ALIGNMENT_BUTTONS[alignment],
310
+ isActive: (editor) => editor.isActive({ textAlign: alignment }),
311
+ run: (editor) => editor.chain().focus().setTextAlign(alignment).run()
312
+ }))
313
+ });
314
+ seedRichEditorExtension("link", {
315
+ starterKit: (props) => ({ link: {
316
+ openOnClick: props.openOnClick ?? false,
317
+ ...props.protocols ? { protocols: props.protocols } : {}
318
+ } }),
319
+ toolbar: () => [{
320
+ key: "link",
321
+ component: ({ editor }) => /* @__PURE__ */ jsx(LinkControl, { editor })
322
+ }]
323
+ });
324
+ seedRichEditorExtension("table", {
325
+ group: "insert",
326
+ extensions: () => [TableKit.configure({ table: { resizable: false } })],
327
+ toolbar: (props) => [
328
+ {
329
+ icon: "table",
330
+ key: "insert-table",
331
+ label: "Insert table",
332
+ isActive: (editor) => editor.isActive("table"),
333
+ run: (editor) => editor.chain().focus().insertTable({
334
+ rows: props.rows ?? 3,
335
+ cols: props.cols ?? 3,
336
+ withHeaderRow: props.withHeaderRow ?? true
337
+ }).run()
338
+ },
339
+ {
340
+ icon: "columns-3",
341
+ key: "add-column",
342
+ label: "Add column",
343
+ isActive: () => false,
344
+ isDisabled: (editor) => !editor.can().addColumnAfter(),
345
+ run: (editor) => editor.chain().focus().addColumnAfter().run()
346
+ },
347
+ {
348
+ icon: "rows-3",
349
+ key: "add-row",
350
+ label: "Add row",
351
+ isActive: () => false,
352
+ isDisabled: (editor) => !editor.can().addRowAfter(),
353
+ run: (editor) => editor.chain().focus().addRowAfter().run()
354
+ },
355
+ {
356
+ icon: "trash-2",
357
+ key: "delete-table",
358
+ label: "Delete table",
359
+ isActive: () => false,
360
+ isDisabled: (editor) => !editor.can().deleteTable(),
361
+ run: (editor) => editor.chain().focus().deleteTable().run()
362
+ }
363
+ ]
364
+ });
365
+ seedRichEditorExtension("details", {
366
+ group: "insert",
367
+ extensions: () => [
368
+ Details,
369
+ DetailsSummary,
370
+ DetailsContent
371
+ ],
372
+ toolbar: () => [{
373
+ icon: "chevron-right",
374
+ key: "details",
375
+ label: "Details",
376
+ isActive: (editor) => editor.isActive("details"),
377
+ run: (editor) => {
378
+ if (editor.isActive("details")) {
379
+ editor.chain().focus().unsetDetails().run();
380
+ return;
381
+ }
382
+ editor.chain().focus().setDetails().run();
383
+ }
384
+ }]
385
+ });
386
+ seedRichEditorExtension("emoji", { toolbar: (props) => {
387
+ const emojis = props.emojis ?? DEFAULT_EMOJIS;
388
+ return [{
389
+ key: "insert-emoji",
390
+ component: ({ editor }) => /* @__PURE__ */ jsx(EmojiPicker, {
391
+ editor,
392
+ emojis
393
+ })
394
+ }];
395
+ } });
396
+ }
397
+ //#endregion
398
+ export { registerBuiltinRichEditorExtensions };
399
+
400
+ //# sourceMappingURL=builtins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtins.js","names":[],"sources":["../../../resources/js/form/rich-editor/builtins.tsx"],"sourcesContent":["import { Details, DetailsContent, DetailsSummary } from \"@tiptap/extension-details\";\nimport { Highlight } from \"@tiptap/extension-highlight\";\nimport { TableKit } from \"@tiptap/extension-table\";\nimport { TextAlign } from \"@tiptap/extension-text-align\";\nimport type { Editor } from \"@tiptap/core\";\nimport type { StarterKitOptions } from \"@tiptap/starter-kit\";\nimport { useState } from \"react\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { useT } from \"@lattice-php/lattice/i18n\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"@lattice-php/lattice/ui/dropdown-menu\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@lattice-php/lattice/ui/popover\";\nimport { seedRichEditorExtension, type ToolbarButton } from \"./registry\";\nimport { ToolbarIconButton } from \"./toolbar-button\";\n\ntype HeadingLevels = NonNullable<Exclude<StarterKitOptions[\"heading\"], false>[\"levels\"]>;\n\nconst ALL_HEADING_LEVELS = [1, 2, 3, 4, 5, 6];\n\nconst ALL_ALIGNMENTS = [\"left\", \"center\", \"right\", \"justify\"];\n\nconst DEFAULT_EMOJIS = [\n \"😀\",\n \"😅\",\n \"😂\",\n \"🥳\",\n \"😎\",\n \"🤔\",\n \"👍\",\n \"🙏\",\n \"🔥\",\n \"🎉\",\n \"🚀\",\n \"💡\",\n \"✅\",\n \"❌\",\n \"⭐\",\n \"❤️\",\n];\n\nfunction headingLevels(levels: number[] | undefined): HeadingLevels {\n const valid = (levels ?? ALL_HEADING_LEVELS).filter((level) => level >= 1 && level <= 6);\n\n return (valid.length > 0 ? valid : ALL_HEADING_LEVELS) as HeadingLevels;\n}\n\nfunction HeadingMenu({ editor, levels }: { editor: Editor; levels: number[] }) {\n const { t } = useT(\"lattice\");\n const label = t(\"form.editor.heading\", \"Heading\");\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <ToolbarIconButton\n active={editor.isActive(\"heading\")}\n icon=\"heading\"\n label={label}\n testId=\"editor-heading\"\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"min-w-32\">\n {levels.map((level) => (\n <DropdownMenuItem\n className={cn(\n editor.isActive(\"heading\", { level }) && \"bg-lt-accent text-lt-accent-fg\",\n )}\n data-test={`editor-heading-${level}`}\n key={level}\n onClick={() =>\n editor\n .chain()\n .focus()\n .toggleHeading({ level: level as HeadingLevels[number] })\n .run()\n }\n >\n {t(`form.editor.heading-${level}`, `Heading ${level}`)}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n\nfunction LinkControl({ editor }: { editor: Editor }) {\n const { t } = useT(\"lattice\");\n const [open, setOpen] = useState(false);\n const [url, setUrl] = useState(\"\");\n\n const apply = () => {\n if (url) {\n editor.chain().focus().extendMarkRange(\"link\").setLink({ href: url }).run();\n } else {\n editor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n }\n\n setOpen(false);\n };\n\n return (\n <Popover\n onOpenChange={(next) => {\n if (next) {\n setUrl((editor.getAttributes(\"link\").href as string | undefined) ?? \"\");\n }\n\n setOpen(next);\n }}\n open={open}\n >\n <PopoverTrigger asChild>\n <ToolbarIconButton\n active={editor.isActive(\"link\")}\n icon=\"link\"\n label={t(\"form.editor.link\", \"Link\")}\n testId=\"editor-link\"\n />\n </PopoverTrigger>\n <PopoverContent className=\"flex w-72 items-center gap-1 p-2\">\n <input\n aria-label={t(\"form.editor.link-url\", \"Link URL\")}\n className=\"h-7 min-w-0 flex-1 rounded-lt-sm border border-lt-input bg-transparent px-2 text-sm outline-none focus:border-lt-ring\"\n data-test=\"editor-link-url\"\n onChange={(event) => setUrl(event.target.value)}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n apply();\n }\n }}\n placeholder=\"https://\"\n type=\"url\"\n value={url}\n />\n <ToolbarIconButton\n icon=\"check\"\n label={t(\"form.editor.link-apply\", \"Apply link\")}\n onClick={apply}\n testId=\"editor-link-apply\"\n />\n {editor.isActive(\"link\") && (\n <ToolbarIconButton\n icon=\"trash-2\"\n label={t(\"form.editor.link-remove\", \"Remove link\")}\n onClick={() => {\n editor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n setOpen(false);\n }}\n testId=\"editor-link-remove\"\n />\n )}\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction EmojiPicker({ editor, emojis }: { editor: Editor; emojis: string[] }) {\n const { t } = useT(\"lattice\");\n const [open, setOpen] = useState(false);\n\n return (\n <Popover onOpenChange={setOpen} open={open}>\n <PopoverTrigger asChild>\n <ToolbarIconButton\n icon=\"smile\"\n label={t(\"form.editor.insert-emoji\", \"Insert emoji\")}\n testId=\"editor-emoji\"\n />\n </PopoverTrigger>\n <PopoverContent className=\"grid grid-cols-8 gap-0.5 p-1\">\n {emojis.map((emoji) => (\n <button\n className=\"inline-flex size-7 items-center justify-center rounded-lt-sm text-base hover:bg-lt-accent\"\n key={emoji}\n onClick={() => {\n editor.chain().focus().insertContent(emoji).run();\n setOpen(false);\n }}\n onMouseDown={(event) => event.preventDefault()}\n type=\"button\"\n >\n {emoji}\n </button>\n ))}\n </PopoverContent>\n </Popover>\n );\n}\n\nfunction markButton(key: string, icon: string, label: string, mark: string): ToolbarButton {\n return {\n icon,\n key,\n label,\n isActive: (editor) => editor.isActive(mark),\n run: (editor) => editor.chain().focus().toggleMark(mark).run(),\n };\n}\n\nlet registered = false;\n\n/**\n * Called by the editor field when its chunk loads. Registration must be an\n * explicitly invoked export: the package ships side-effect-free modules, so a\n * bare `import \"./builtins\"` would be tree-shaken out of production builds.\n */\nexport function registerBuiltinRichEditorExtensions(): void {\n if (registered) {\n return;\n }\n\n registered = true;\n\n seedRichEditorExtension(\"bold\", {\n group: \"marks\",\n starterKit: () => ({ bold: {} }),\n toolbar: () => [markButton(\"bold\", \"bold\", \"Bold\", \"bold\")],\n });\n\n seedRichEditorExtension(\"italic\", {\n group: \"marks\",\n starterKit: () => ({ italic: {} }),\n toolbar: () => [markButton(\"italic\", \"italic\", \"Italic\", \"italic\")],\n });\n\n seedRichEditorExtension(\"strike\", {\n group: \"marks\",\n starterKit: () => ({ strike: {} }),\n toolbar: () => [markButton(\"strikethrough\", \"strikethrough\", \"Strikethrough\", \"strike\")],\n });\n\n seedRichEditorExtension(\"underline\", {\n group: \"marks\",\n starterKit: () => ({ underline: {} }),\n toolbar: () => [markButton(\"underline\", \"underline\", \"Underline\", \"underline\")],\n });\n\n seedRichEditorExtension(\"highlight\", {\n group: \"marks\",\n extensions: () => [Highlight],\n toolbar: () => [markButton(\"highlight\", \"highlighter\", \"Highlight\", \"highlight\")],\n });\n\n seedRichEditorExtension(\"code\", {\n group: \"marks\",\n starterKit: () => ({ code: {} }),\n toolbar: () => [markButton(\"code\", \"code-xml\", \"Code\", \"code\")],\n });\n\n seedRichEditorExtension(\"heading\", {\n starterKit: (props) => ({ heading: { levels: headingLevels(props.levels) } }),\n toolbar: (props) => {\n const levels = headingLevels(props.levels);\n\n return [\n {\n key: \"heading\",\n component: ({ editor }) => <HeadingMenu editor={editor} levels={levels} />,\n },\n ];\n },\n });\n\n seedRichEditorExtension(\"bullet-list\", {\n group: \"blocks\",\n starterKit: () => ({ bulletList: {}, listItem: {} }),\n toolbar: () => [\n {\n icon: \"list\",\n key: \"bullet-list\",\n label: \"Bullet list\",\n isActive: (editor) => editor.isActive(\"bulletList\"),\n run: (editor) => editor.chain().focus().toggleBulletList().run(),\n },\n ],\n });\n\n seedRichEditorExtension(\"ordered-list\", {\n group: \"blocks\",\n starterKit: () => ({ orderedList: {}, listItem: {} }),\n toolbar: () => [\n {\n icon: \"list-ordered\",\n key: \"ordered-list\",\n label: \"Ordered list\",\n isActive: (editor) => editor.isActive(\"orderedList\"),\n run: (editor) => editor.chain().focus().toggleOrderedList().run(),\n },\n ],\n });\n\n seedRichEditorExtension(\"blockquote\", {\n group: \"blocks\",\n starterKit: () => ({ blockquote: {} }),\n toolbar: () => [\n {\n icon: \"quote\",\n key: \"blockquote\",\n label: \"Blockquote\",\n isActive: (editor) => editor.isActive(\"blockquote\"),\n run: (editor) => editor.chain().focus().toggleBlockquote().run(),\n },\n ],\n });\n\n seedRichEditorExtension(\"code-block\", {\n group: \"blocks\",\n starterKit: () => ({ codeBlock: {} }),\n toolbar: () => [\n {\n icon: \"code\",\n key: \"code-block\",\n label: \"Code block\",\n isActive: (editor) => editor.isActive(\"codeBlock\"),\n run: (editor) => editor.chain().focus().toggleCodeBlock().run(),\n },\n ],\n });\n\n seedRichEditorExtension(\"horizontal-rule\", {\n group: \"blocks\",\n starterKit: () => ({ horizontalRule: {} }),\n toolbar: () => [\n {\n icon: \"minus\",\n key: \"horizontal-rule\",\n label: \"Horizontal rule\",\n isActive: () => false,\n run: (editor) => editor.chain().focus().setHorizontalRule().run(),\n },\n ],\n });\n\n const ALIGNMENT_BUTTONS: Record<string, { icon: string; key: string; label: string }> = {\n left: { icon: \"align-left\", key: \"align-left\", label: \"Align left\" },\n center: { icon: \"align-center\", key: \"align-center\", label: \"Align center\" },\n right: { icon: \"align-right\", key: \"align-right\", label: \"Align right\" },\n justify: { icon: \"align-justify\", key: \"justify\", label: \"Justify\" },\n };\n\n seedRichEditorExtension(\"text-align\", {\n extensions: (props) => [\n TextAlign.configure({\n alignments: props.alignments ?? ALL_ALIGNMENTS,\n types: [\"heading\", \"paragraph\"],\n }),\n ],\n toolbar: (props) =>\n (props.alignments ?? ALL_ALIGNMENTS)\n .filter((alignment) => alignment in ALIGNMENT_BUTTONS)\n .map((alignment) => ({\n ...ALIGNMENT_BUTTONS[alignment],\n isActive: (editor: Editor) => editor.isActive({ textAlign: alignment }),\n run: (editor: Editor) => editor.chain().focus().setTextAlign(alignment).run(),\n })),\n });\n\n seedRichEditorExtension(\"link\", {\n starterKit: (props) => ({\n link: {\n openOnClick: props.openOnClick ?? false,\n ...(props.protocols ? { protocols: props.protocols } : {}),\n },\n }),\n toolbar: () => [\n {\n key: \"link\",\n component: ({ editor }) => <LinkControl editor={editor} />,\n },\n ],\n });\n\n seedRichEditorExtension(\"table\", {\n group: \"insert\",\n extensions: () => [TableKit.configure({ table: { resizable: false } })],\n toolbar: (props) => [\n {\n icon: \"table\",\n key: \"insert-table\",\n label: \"Insert table\",\n isActive: (editor) => editor.isActive(\"table\"),\n run: (editor) =>\n editor\n .chain()\n .focus()\n .insertTable({\n rows: props.rows ?? 3,\n cols: props.cols ?? 3,\n withHeaderRow: props.withHeaderRow ?? true,\n })\n .run(),\n },\n {\n icon: \"columns-3\",\n key: \"add-column\",\n label: \"Add column\",\n isActive: () => false,\n isDisabled: (editor) => !editor.can().addColumnAfter(),\n run: (editor) => editor.chain().focus().addColumnAfter().run(),\n },\n {\n icon: \"rows-3\",\n key: \"add-row\",\n label: \"Add row\",\n isActive: () => false,\n isDisabled: (editor) => !editor.can().addRowAfter(),\n run: (editor) => editor.chain().focus().addRowAfter().run(),\n },\n {\n icon: \"trash-2\",\n key: \"delete-table\",\n label: \"Delete table\",\n isActive: () => false,\n isDisabled: (editor) => !editor.can().deleteTable(),\n run: (editor) => editor.chain().focus().deleteTable().run(),\n },\n ],\n });\n\n seedRichEditorExtension(\"details\", {\n group: \"insert\",\n extensions: () => [Details, DetailsSummary, DetailsContent],\n toolbar: () => [\n {\n icon: \"chevron-right\",\n key: \"details\",\n label: \"Details\",\n isActive: (editor) => editor.isActive(\"details\"),\n run: (editor) => {\n if (editor.isActive(\"details\")) {\n editor.chain().focus().unsetDetails().run();\n\n return;\n }\n\n editor.chain().focus().setDetails().run();\n },\n },\n ],\n });\n\n seedRichEditorExtension(\"emoji\", {\n toolbar: (props) => {\n const emojis = props.emojis ?? DEFAULT_EMOJIS;\n\n return [\n {\n key: \"insert-emoji\",\n component: ({ editor }) => <EmojiPicker editor={editor} emojis={emojis} />,\n },\n ];\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Entry point for registering custom rich-editor extensions outside the
3
+ * package. Modules not re-exported here are internal and may change without
4
+ * notice.
5
+ */
6
+ export { registerRichEditorExtension, type EditorExtensionPayloadOf, type EditorExtensionProps, type RichEditorExtensionDefinition, type ToolbarButton, type ToolbarControl, type ToolbarItem, } from './registry.js';
7
+ export { ToolbarIconButton } from './toolbar-button.js';
@@ -0,0 +1,3 @@
1
+ import { registerRichEditorExtension } from "./registry.js";
2
+ import { ToolbarIconButton } from "./toolbar-button.js";
3
+ export { ToolbarIconButton, registerRichEditorExtension };
@@ -0,0 +1,67 @@
1
+ import { AnyExtension, Editor } from '@tiptap/core';
2
+ import { StarterKitOptions } from '@tiptap/starter-kit';
3
+ import { ComponentType } from 'react';
4
+ import { ResolveProps } from '../../core/types.js';
5
+ import { EditorExtension, EditorExtensionPropsMap } from '../../types/generated.js';
6
+ /**
7
+ * Consumer apps augment this via `declare module "@lattice-php/lattice"` to type
8
+ * their custom extensions' props; built-ins resolve through
9
+ * `EditorExtensionPropsMap`. The editor counterpart of `EffectProps`.
10
+ */
11
+ export interface EditorExtensionProps {
12
+ }
13
+ export type EditorExtensionPayloadOf<TType extends string> = ResolveProps<EditorExtensionProps, EditorExtensionPropsMap, TType, Record<string, unknown>>;
14
+ export type ToolbarButton = {
15
+ icon: string;
16
+ key: string;
17
+ label: string;
18
+ isActive: (editor: Editor) => boolean;
19
+ isDisabled?: (editor: Editor) => boolean;
20
+ run: (editor: Editor) => void;
21
+ };
22
+ /**
23
+ * A toolbar item that renders its own markup (dropdowns, popovers) instead of
24
+ * the standard icon button.
25
+ */
26
+ export type ToolbarControl = {
27
+ key: string;
28
+ component: ComponentType<{
29
+ editor: Editor;
30
+ }>;
31
+ };
32
+ export type ToolbarItem = ToolbarButton | ToolbarControl;
33
+ export type ToolbarEntry = ToolbarItem | "separator";
34
+ export type RichEditorExtensionDefinition<P = Record<string, unknown>> = {
35
+ extensions?: (props: P) => AnyExtension[];
36
+ /** Contribution to the single shared StarterKit configuration. */
37
+ starterKit?: (props: P) => Partial<StarterKitOptions>;
38
+ toolbar?: (props: P) => ToolbarItem[];
39
+ /**
40
+ * Adjacent toolbar contributions from the same group render without a
41
+ * separator between them; defaults to the extension's own wire type.
42
+ */
43
+ group?: string;
44
+ };
45
+ export type ResolvedRichEditorExtension = {
46
+ type: string;
47
+ props: Record<string, unknown>;
48
+ definition: RichEditorExtensionDefinition;
49
+ group: string;
50
+ };
51
+ /**
52
+ * Wire props stay `Partial` at the definition boundary — a client-registered
53
+ * type's props carry no generated shape, so definitions default each field.
54
+ */
55
+ export declare function registerRichEditorExtension<TType extends string>(type: TType, definition: RichEditorExtensionDefinition<Partial<EditorExtensionPayloadOf<TType>>>): void;
56
+ /**
57
+ * Registration that yields to an existing entry. The built-ins load with the
58
+ * lazy editor chunk — after app boot code ran — so seeding (instead of
59
+ * registering) keeps a consumer's deliberate override of a built-in type.
60
+ *
61
+ * @internal
62
+ */
63
+ export declare function seedRichEditorExtension<TType extends string>(type: TType, definition: RichEditorExtensionDefinition<Partial<EditorExtensionPayloadOf<TType>>>): void;
64
+ export declare function resolveRichEditorExtensions(specs: EditorExtension[]): ResolvedRichEditorExtension[];
65
+ export declare function assembleStarterKitOptions(extensions: ResolvedRichEditorExtension[]): Partial<StarterKitOptions>;
66
+ export declare function assembleTiptapExtensions(extensions: ResolvedRichEditorExtension[]): AnyExtension[];
67
+ export declare function assembleToolbar(extensions: ResolvedRichEditorExtension[]): ToolbarEntry[];
@@ -0,0 +1,79 @@
1
+ //#region resources/js/form/rich-editor/registry.ts
2
+ var registry = /* @__PURE__ */ new Map();
3
+ /**
4
+ * Wire props stay `Partial` at the definition boundary — a client-registered
5
+ * type's props carry no generated shape, so definitions default each field.
6
+ */
7
+ function registerRichEditorExtension(type, definition) {
8
+ registry.set(type, definition);
9
+ }
10
+ /**
11
+ * Registration that yields to an existing entry. The built-ins load with the
12
+ * lazy editor chunk — after app boot code ran — so seeding (instead of
13
+ * registering) keeps a consumer's deliberate override of a built-in type.
14
+ *
15
+ * @internal
16
+ */
17
+ function seedRichEditorExtension(type, definition) {
18
+ if (!registry.has(type)) registerRichEditorExtension(type, definition);
19
+ }
20
+ function resolveRichEditorExtensions(specs) {
21
+ const resolved = [];
22
+ for (const spec of specs) {
23
+ const definition = registry.get(spec.type);
24
+ if (!definition) continue;
25
+ resolved.push({
26
+ type: spec.type,
27
+ props: spec.props,
28
+ definition,
29
+ group: definition.group ?? spec.type
30
+ });
31
+ }
32
+ return resolved;
33
+ }
34
+ /**
35
+ * One shared StarterKit for the whole editor: the always-on baseline (document,
36
+ * paragraph, text, hard break, undo/redo, cursors and the invisible list/trailing
37
+ * helpers) plus whatever the active extensions re-enable. Everything else is
38
+ * explicitly disabled — StarterKit turns every feature on unless told otherwise.
39
+ */
40
+ var DISABLED_STARTER_KIT_FEATURES = {
41
+ blockquote: false,
42
+ bold: false,
43
+ bulletList: false,
44
+ code: false,
45
+ codeBlock: false,
46
+ heading: false,
47
+ horizontalRule: false,
48
+ italic: false,
49
+ link: false,
50
+ listItem: false,
51
+ orderedList: false,
52
+ strike: false,
53
+ underline: false
54
+ };
55
+ function assembleStarterKitOptions(extensions) {
56
+ return extensions.reduce((options, extension) => ({
57
+ ...options,
58
+ ...extension.definition.starterKit?.(extension.props)
59
+ }), { ...DISABLED_STARTER_KIT_FEATURES });
60
+ }
61
+ function assembleTiptapExtensions(extensions) {
62
+ return extensions.flatMap((extension) => extension.definition.extensions?.(extension.props) ?? []);
63
+ }
64
+ function assembleToolbar(extensions) {
65
+ const entries = [];
66
+ let previousGroup = null;
67
+ for (const extension of extensions) {
68
+ const items = extension.definition.toolbar?.(extension.props) ?? [];
69
+ if (items.length === 0) continue;
70
+ if (previousGroup !== null && previousGroup !== extension.group) entries.push("separator");
71
+ entries.push(...items);
72
+ previousGroup = extension.group;
73
+ }
74
+ return entries;
75
+ }
76
+ //#endregion
77
+ export { assembleStarterKitOptions, assembleTiptapExtensions, assembleToolbar, registerRichEditorExtension, resolveRichEditorExtensions, seedRichEditorExtension };
78
+
79
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","names":[],"sources":["../../../resources/js/form/rich-editor/registry.ts"],"sourcesContent":["import type { AnyExtension, Editor } from \"@tiptap/core\";\nimport type { StarterKitOptions } from \"@tiptap/starter-kit\";\nimport type { ComponentType } from \"react\";\nimport type { ResolveProps } from \"@lattice-php/lattice/core/types\";\nimport type {\n EditorExtension,\n EditorExtensionPropsMap,\n} from \"@lattice-php/lattice/types/generated\";\n\n/**\n * Consumer apps augment this via `declare module \"@lattice-php/lattice\"` to type\n * their custom extensions' props; built-ins resolve through\n * `EditorExtensionPropsMap`. The editor counterpart of `EffectProps`.\n */\nexport interface EditorExtensionProps {}\n\nexport type EditorExtensionPayloadOf<TType extends string> = ResolveProps<\n EditorExtensionProps,\n EditorExtensionPropsMap,\n TType,\n Record<string, unknown>\n>;\n\nexport type ToolbarButton = {\n icon: string;\n key: string;\n label: string;\n isActive: (editor: Editor) => boolean;\n isDisabled?: (editor: Editor) => boolean;\n run: (editor: Editor) => void;\n};\n\n/**\n * A toolbar item that renders its own markup (dropdowns, popovers) instead of\n * the standard icon button.\n */\nexport type ToolbarControl = {\n key: string;\n component: ComponentType<{ editor: Editor }>;\n};\n\nexport type ToolbarItem = ToolbarButton | ToolbarControl;\n\nexport type ToolbarEntry = ToolbarItem | \"separator\";\n\nexport type RichEditorExtensionDefinition<P = Record<string, unknown>> = {\n extensions?: (props: P) => AnyExtension[];\n /** Contribution to the single shared StarterKit configuration. */\n starterKit?: (props: P) => Partial<StarterKitOptions>;\n toolbar?: (props: P) => ToolbarItem[];\n /**\n * Adjacent toolbar contributions from the same group render without a\n * separator between them; defaults to the extension's own wire type.\n */\n group?: string;\n};\n\nexport type ResolvedRichEditorExtension = {\n type: string;\n props: Record<string, unknown>;\n definition: RichEditorExtensionDefinition;\n group: string;\n};\n\nconst registry = new Map<string, RichEditorExtensionDefinition>();\n\n/**\n * Wire props stay `Partial` at the definition boundary — a client-registered\n * type's props carry no generated shape, so definitions default each field.\n */\nexport function registerRichEditorExtension<TType extends string>(\n type: TType,\n definition: RichEditorExtensionDefinition<Partial<EditorExtensionPayloadOf<TType>>>,\n): void {\n registry.set(type, definition as unknown as RichEditorExtensionDefinition);\n}\n\n/**\n * Registration that yields to an existing entry. The built-ins load with the\n * lazy editor chunk — after app boot code ran — so seeding (instead of\n * registering) keeps a consumer's deliberate override of a built-in type.\n *\n * @internal\n */\nexport function seedRichEditorExtension<TType extends string>(\n type: TType,\n definition: RichEditorExtensionDefinition<Partial<EditorExtensionPayloadOf<TType>>>,\n): void {\n if (!registry.has(type)) {\n registerRichEditorExtension(type, definition);\n }\n}\n\nconst warned = new Set<string>();\n\nexport function resolveRichEditorExtensions(\n specs: EditorExtension[],\n): ResolvedRichEditorExtension[] {\n const resolved: ResolvedRichEditorExtension[] = [];\n\n for (const spec of specs) {\n const definition = registry.get(spec.type);\n\n if (!definition) {\n if (import.meta.env.DEV && !warned.has(spec.type)) {\n warned.add(spec.type);\n console.warn(`[Lattice] Rich-editor extension \"${spec.type}\" is not registered.`);\n }\n\n continue;\n }\n\n resolved.push({\n type: spec.type,\n props: spec.props,\n definition,\n group: definition.group ?? spec.type,\n });\n }\n\n return resolved;\n}\n\n/**\n * One shared StarterKit for the whole editor: the always-on baseline (document,\n * paragraph, text, hard break, undo/redo, cursors and the invisible list/trailing\n * helpers) plus whatever the active extensions re-enable. Everything else is\n * explicitly disabled — StarterKit turns every feature on unless told otherwise.\n */\nconst DISABLED_STARTER_KIT_FEATURES: Partial<StarterKitOptions> = {\n blockquote: false,\n bold: false,\n bulletList: false,\n code: false,\n codeBlock: false,\n heading: false,\n horizontalRule: false,\n italic: false,\n link: false,\n listItem: false,\n orderedList: false,\n strike: false,\n underline: false,\n};\n\nexport function assembleStarterKitOptions(\n extensions: ResolvedRichEditorExtension[],\n): Partial<StarterKitOptions> {\n return extensions.reduce(\n (options, extension) => ({ ...options, ...extension.definition.starterKit?.(extension.props) }),\n { ...DISABLED_STARTER_KIT_FEATURES },\n );\n}\n\nexport function assembleTiptapExtensions(\n extensions: ResolvedRichEditorExtension[],\n): AnyExtension[] {\n return extensions.flatMap(\n (extension) => extension.definition.extensions?.(extension.props) ?? [],\n );\n}\n\nexport function assembleToolbar(extensions: ResolvedRichEditorExtension[]): ToolbarEntry[] {\n const entries: ToolbarEntry[] = [];\n let previousGroup: string | null = null;\n\n for (const extension of extensions) {\n const items = extension.definition.toolbar?.(extension.props) ?? [];\n\n if (items.length === 0) {\n continue;\n }\n\n if (previousGroup !== null && previousGroup !== extension.group) {\n entries.push(\"separator\");\n }\n\n entries.push(...items);\n previousGroup = extension.group;\n }\n\n return entries;\n}\n"],"mappings":";AAgEA,IAAM,2BAAW,IAAI,IAA2C;;;;;AAMhE,SAAgB,4BACd,MACA,YACM;CACN,SAAS,IAAI,MAAM,UAAsD;AAC3E;;;;;;;;AASA,SAAgB,wBACd,MACA,YACM;CACN,IAAI,CAAC,SAAS,IAAI,IAAI,GACpB,4BAA4B,MAAM,UAAU;AAEhD;AAIA,SAAgB,4BACd,OAC+B;CAC/B,MAAM,WAA0C,CAAC;CAEjD,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,aAAa,SAAS,IAAI,KAAK,IAAI;EAEzC,IAAI,CAAC,YAMH;EAGF,SAAS,KAAK;GACZ,MAAM,KAAK;GACX,OAAO,KAAK;GACZ;GACA,OAAO,WAAW,SAAS,KAAK;EAClC,CAAC;CACH;CAEA,OAAO;AACT;;;;;;;AAQA,IAAM,gCAA4D;CAChE,YAAY;CACZ,MAAM;CACN,YAAY;CACZ,MAAM;CACN,WAAW;CACX,SAAS;CACT,gBAAgB;CAChB,QAAQ;CACR,MAAM;CACN,UAAU;CACV,aAAa;CACb,QAAQ;CACR,WAAW;AACb;AAEA,SAAgB,0BACd,YAC4B;CAC5B,OAAO,WAAW,QACf,SAAS,eAAe;EAAE,GAAG;EAAS,GAAG,UAAU,WAAW,aAAa,UAAU,KAAK;CAAE,IAC7F,EAAE,GAAG,8BAA8B,CACrC;AACF;AAEA,SAAgB,yBACd,YACgB;CAChB,OAAO,WAAW,SACf,cAAc,UAAU,WAAW,aAAa,UAAU,KAAK,KAAK,CAAC,CACxE;AACF;AAEA,SAAgB,gBAAgB,YAA2D;CACzF,MAAM,UAA0B,CAAC;CACjC,IAAI,gBAA+B;CAEnC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,QAAQ,UAAU,WAAW,UAAU,UAAU,KAAK,KAAK,CAAC;EAElE,IAAI,MAAM,WAAW,GACnB;EAGF,IAAI,kBAAkB,QAAQ,kBAAkB,UAAU,OACxD,QAAQ,KAAK,WAAW;EAG1B,QAAQ,KAAK,GAAG,KAAK;EACrB,gBAAgB,UAAU;CAC5B;CAEA,OAAO;AACT"}
@@ -0,0 +1,12 @@
1
+ import * as React from "react";
2
+ /**
3
+ * The shared editor toolbar trigger: an {@link IconButton} that keeps focus in
4
+ * the editor (mousedown is prevented so clicks don't blur it, which would
5
+ * otherwise trigger a precognition request).
6
+ */
7
+ export declare function ToolbarIconButton({ active, icon, label, testId, ...props }: React.ComponentProps<"button"> & {
8
+ active?: boolean;
9
+ icon: string;
10
+ label: string;
11
+ testId: string;
12
+ }): React.JSX.Element;