@carlonicora/nextjs-jsonapi 0.0.1 → 1.0.4

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 (303) hide show
  1. package/dist/BlockNoteEditor-VFWG6LXI.js.map +1 -1
  2. package/dist/JsonApiRequest-ZZLSP26T.js.map +1 -1
  3. package/dist/atoms/index.js.map +1 -1
  4. package/dist/chunk-2K3Q24UF.js.map +1 -1
  5. package/dist/chunk-3FBCC4G3.js.map +1 -1
  6. package/dist/chunk-4HCRAOS5.js.map +1 -1
  7. package/dist/chunk-6GKHCVF6.js.map +1 -1
  8. package/dist/chunk-7QVYU63E.js.map +1 -1
  9. package/dist/chunk-A5DDIABK.js.map +1 -1
  10. package/dist/chunk-AWONBQQP.js.map +1 -1
  11. package/dist/chunk-CXQOWQSY.js.map +1 -1
  12. package/dist/chunk-DO2HLAZO.js.map +1 -1
  13. package/dist/chunk-EFJEWLRL.js.map +1 -1
  14. package/dist/chunk-FY4SXJGU.js.map +1 -1
  15. package/dist/chunk-H6FMOA6B.js.map +1 -1
  16. package/dist/chunk-I2REI7OA.js.map +1 -1
  17. package/dist/chunk-IBS6NI7D.js.map +1 -1
  18. package/dist/chunk-J4Q36PMP.js.map +1 -1
  19. package/dist/chunk-JC3WJK65.js.map +1 -1
  20. package/dist/chunk-LXKSUWAV.js.map +1 -1
  21. package/dist/chunk-RAF7PNLG.js.map +1 -1
  22. package/dist/chunk-RUR22SVM.js.map +1 -1
  23. package/dist/chunk-TEGF6ZWG.js.map +1 -1
  24. package/dist/chunk-TMVHSY3Y.js.map +1 -1
  25. package/dist/chunk-V2JJPI7N.js.map +1 -1
  26. package/dist/client/index.js.map +1 -1
  27. package/dist/components/index.js.map +1 -1
  28. package/dist/contexts/index.js.map +1 -1
  29. package/dist/core/index.js.map +1 -1
  30. package/dist/features/index.js.map +1 -1
  31. package/dist/hooks/index.js.map +1 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/interfaces/index.js.map +1 -1
  34. package/dist/permissions/index.js.map +1 -1
  35. package/dist/request-QFS7NEIE.js.map +1 -1
  36. package/dist/request-ZYY6RI5X.js.map +1 -1
  37. package/dist/roles/index.js.map +1 -1
  38. package/dist/server/index.js.map +1 -1
  39. package/dist/shadcnui/index.js.map +1 -1
  40. package/dist/token-MJMC26ON.js.map +1 -1
  41. package/dist/token-UYE7CV6X.js.map +1 -1
  42. package/dist/utils/index.js.map +1 -1
  43. package/package.json +6 -1
  44. package/src/atoms/index.ts +1 -0
  45. package/src/atoms/recentPagesAtom.ts +10 -0
  46. package/src/client/context/JsonApiContext.ts +61 -0
  47. package/src/client/context/JsonApiProvider.tsx +27 -0
  48. package/src/client/context/index.ts +2 -0
  49. package/src/client/hooks/index.ts +3 -0
  50. package/src/client/hooks/useJsonApiGet.ts +188 -0
  51. package/src/client/hooks/useJsonApiMutation.ts +193 -0
  52. package/src/client/hooks/useRehydration.ts +47 -0
  53. package/src/client/index.ts +11 -0
  54. package/src/client/request.ts +97 -0
  55. package/src/client/token.ts +10 -0
  56. package/src/components/containers/PageContainer.tsx +15 -0
  57. package/src/components/containers/ReactMarkdownContainer.tsx +119 -0
  58. package/src/components/containers/TabsContainer.tsx +93 -0
  59. package/src/components/containers/index.ts +3 -0
  60. package/src/components/contents/AttributeElement.tsx +20 -0
  61. package/src/components/contents/index.ts +1 -0
  62. package/src/components/details/AllowedUsersDetails.tsx +23 -0
  63. package/src/components/details/index.ts +1 -0
  64. package/src/components/editors/BlockNoteDiffInlineContent.tsx +152 -0
  65. package/src/components/editors/BlockNoteEditor.tsx +404 -0
  66. package/src/components/editors/BlockNoteEditorContainer.tsx +13 -0
  67. package/src/components/editors/BlockNoteEditorFormattingToolbar.tsx +38 -0
  68. package/src/components/editors/index.ts +1 -0
  69. package/src/components/errors/ErrorDetails.tsx +41 -0
  70. package/src/components/errors/errorToast.ts +9 -0
  71. package/src/components/errors/index.ts +2 -0
  72. package/src/components/forms/CommonAssociationForm.tsx +162 -0
  73. package/src/components/forms/CommonDeleter.tsx +94 -0
  74. package/src/components/forms/CommonEditorButtons.tsx +30 -0
  75. package/src/components/forms/CommonEditorHeader.tsx +35 -0
  76. package/src/components/forms/CommonEditorTrigger.tsx +26 -0
  77. package/src/components/forms/DatePickerPopover.tsx +219 -0
  78. package/src/components/forms/DateRangeSelector.tsx +110 -0
  79. package/src/components/forms/FileUploader.tsx +324 -0
  80. package/src/components/forms/FormCheckbox.tsx +66 -0
  81. package/src/components/forms/FormContainerGeneric.tsx +39 -0
  82. package/src/components/forms/FormDate.tsx +247 -0
  83. package/src/components/forms/FormDateTime.tsx +231 -0
  84. package/src/components/forms/FormInput.tsx +110 -0
  85. package/src/components/forms/FormPassword.tsx +54 -0
  86. package/src/components/forms/FormPlaceAutocomplete.tsx +286 -0
  87. package/src/components/forms/FormSelect.tsx +72 -0
  88. package/src/components/forms/FormSlider.tsx +51 -0
  89. package/src/components/forms/FormSwitch.tsx +25 -0
  90. package/src/components/forms/FormTextarea.tsx +44 -0
  91. package/src/components/forms/MultiFileUploader.tsx +107 -0
  92. package/src/components/forms/PasswordInput.tsx +47 -0
  93. package/src/components/forms/index.ts +21 -0
  94. package/src/components/index.ts +11 -0
  95. package/src/components/navigations/Breadcrumb.tsx +83 -0
  96. package/src/components/navigations/ContentTitle.tsx +39 -0
  97. package/src/components/navigations/Header.tsx +27 -0
  98. package/src/components/navigations/ModeToggleSwitch.tsx +25 -0
  99. package/src/components/navigations/PageSection.tsx +64 -0
  100. package/src/components/navigations/RecentPagesNavigator.tsx +52 -0
  101. package/src/components/navigations/index.ts +6 -0
  102. package/src/components/pages/PageContainerContentDetails.tsx +76 -0
  103. package/src/components/pages/PageContentContainer.tsx +31 -0
  104. package/src/components/pages/index.ts +2 -0
  105. package/src/components/tables/ContentListTable.tsx +165 -0
  106. package/src/components/tables/ContentTableSearch.tsx +105 -0
  107. package/src/components/tables/cells/cell.component.tsx +18 -0
  108. package/src/components/tables/cells/cell.date.tsx +16 -0
  109. package/src/components/tables/cells/cell.id.tsx +27 -0
  110. package/src/components/tables/cells/cell.link.tsx +18 -0
  111. package/src/components/tables/cells/cell.text.tsx +12 -0
  112. package/src/components/tables/cells/cell.url.tsx +13 -0
  113. package/src/components/tables/cells/index.ts +5 -0
  114. package/src/components/tables/index.ts +3 -0
  115. package/src/contexts/SharedContext.tsx +35 -0
  116. package/src/contexts/index.ts +2 -0
  117. package/src/core/abstracts/AbstractApiData.ts +138 -0
  118. package/src/core/abstracts/AbstractService.ts +263 -0
  119. package/src/core/abstracts/index.ts +2 -0
  120. package/src/core/endpoint/EndpointCreator.ts +97 -0
  121. package/src/core/endpoint/index.ts +1 -0
  122. package/src/core/factories/JsonApiDataFactory.ts +12 -0
  123. package/src/core/factories/RehydrationFactory.ts +30 -0
  124. package/src/core/factories/index.ts +2 -0
  125. package/src/core/fields/FieldSelector.ts +15 -0
  126. package/src/core/fields/index.ts +1 -0
  127. package/src/core/index.ts +20 -0
  128. package/src/core/interfaces/ApiData.ts +8 -0
  129. package/src/core/interfaces/ApiDataInterface.ts +15 -0
  130. package/src/core/interfaces/ApiRequestDataTypeInterface.ts +14 -0
  131. package/src/core/interfaces/ApiResponseInterface.ts +17 -0
  132. package/src/core/interfaces/JsonApiHydratedDataInterface.ts +5 -0
  133. package/src/core/interfaces/index.ts +5 -0
  134. package/src/core/registry/DataClassRegistry.ts +51 -0
  135. package/src/core/registry/ModuleRegistrar.ts +43 -0
  136. package/src/core/registry/ModuleRegistry.ts +64 -0
  137. package/src/core/registry/index.ts +3 -0
  138. package/src/core/utils/index.ts +2 -0
  139. package/src/core/utils/rehydrate.ts +24 -0
  140. package/src/core/utils/translateResponse.ts +125 -0
  141. package/src/features/auth/auth.module.ts +9 -0
  142. package/src/features/auth/config.ts +57 -0
  143. package/src/features/auth/data/auth.interface.ts +31 -0
  144. package/src/features/auth/data/auth.service.ts +159 -0
  145. package/src/features/auth/data/auth.ts +54 -0
  146. package/src/features/auth/data/index.ts +3 -0
  147. package/src/features/auth/index.ts +3 -0
  148. package/src/features/company/company.module.ts +10 -0
  149. package/src/features/company/data/company.fields.ts +6 -0
  150. package/src/features/company/data/company.interface.ts +28 -0
  151. package/src/features/company/data/company.service.ts +73 -0
  152. package/src/features/company/data/company.ts +104 -0
  153. package/src/features/company/data/index.ts +4 -0
  154. package/src/features/company/index.ts +2 -0
  155. package/src/features/content/content.module.ts +20 -0
  156. package/src/features/content/data/content.fields.ts +13 -0
  157. package/src/features/content/data/content.interface.ts +23 -0
  158. package/src/features/content/data/content.service.ts +75 -0
  159. package/src/features/content/data/content.ts +85 -0
  160. package/src/features/content/data/index.ts +4 -0
  161. package/src/features/content/index.ts +2 -0
  162. package/src/features/feature/components/forms/FormFeatures.tsx +149 -0
  163. package/src/features/feature/components/index.ts +1 -0
  164. package/src/features/feature/data/feature.interface.ts +9 -0
  165. package/src/features/feature/data/feature.service.ts +19 -0
  166. package/src/features/feature/data/feature.ts +33 -0
  167. package/src/features/feature/data/index.ts +3 -0
  168. package/src/features/feature/feature.module.ts +10 -0
  169. package/src/features/feature/index.ts +3 -0
  170. package/src/features/index.ts +12 -0
  171. package/src/features/module/data/index.ts +2 -0
  172. package/src/features/module/data/module.interface.ts +12 -0
  173. package/src/features/module/data/module.ts +42 -0
  174. package/src/features/module/index.ts +2 -0
  175. package/src/features/module/module.module.ts +10 -0
  176. package/src/features/notification/data/index.ts +4 -0
  177. package/src/features/notification/data/notification.fields.ts +8 -0
  178. package/src/features/notification/data/notification.interface.ts +14 -0
  179. package/src/features/notification/data/notification.service.ts +34 -0
  180. package/src/features/notification/data/notification.ts +51 -0
  181. package/src/features/notification/index.ts +2 -0
  182. package/src/features/notification/notification.module.ts +10 -0
  183. package/src/features/push/data/index.ts +3 -0
  184. package/src/features/push/data/push.interface.ts +8 -0
  185. package/src/features/push/data/push.service.ts +17 -0
  186. package/src/features/push/data/push.ts +18 -0
  187. package/src/features/push/index.ts +2 -0
  188. package/src/features/push/push.module.ts +10 -0
  189. package/src/features/role/data/index.ts +4 -0
  190. package/src/features/role/data/role.fields.ts +8 -0
  191. package/src/features/role/data/role.interface.ts +16 -0
  192. package/src/features/role/data/role.service.ts +117 -0
  193. package/src/features/role/data/role.ts +62 -0
  194. package/src/features/role/index.ts +2 -0
  195. package/src/features/role/role.module.ts +10 -0
  196. package/src/features/s3/data/index.ts +3 -0
  197. package/src/features/s3/data/s3.interface.ts +11 -0
  198. package/src/features/s3/data/s3.service.ts +30 -0
  199. package/src/features/s3/data/s3.ts +60 -0
  200. package/src/features/s3/index.ts +2 -0
  201. package/src/features/s3/s3.module.ts +10 -0
  202. package/src/features/search/index.ts +1 -0
  203. package/src/features/search/interfaces/index.ts +1 -0
  204. package/src/features/search/interfaces/search.result.interface.ts +3 -0
  205. package/src/features/user/author.module.ts +10 -0
  206. package/src/features/user/components/index.ts +2 -0
  207. package/src/features/user/components/lists/ContributorsList.tsx +41 -0
  208. package/src/features/user/components/lists/index.ts +1 -0
  209. package/src/features/user/components/widgets/UserAvatar.tsx +86 -0
  210. package/src/features/user/components/widgets/index.ts +1 -0
  211. package/src/features/user/contexts/CurrentUserContext.tsx +156 -0
  212. package/src/features/user/contexts/index.ts +1 -0
  213. package/src/features/user/data/index.ts +4 -0
  214. package/src/features/user/data/user.fields.ts +8 -0
  215. package/src/features/user/data/user.interface.ts +41 -0
  216. package/src/features/user/data/user.service.ts +246 -0
  217. package/src/features/user/data/user.ts +162 -0
  218. package/src/features/user/index.ts +4 -0
  219. package/src/features/user/user.module.ts +21 -0
  220. package/src/hooks/TableGeneratorRegistry.ts +53 -0
  221. package/src/hooks/index.ts +33 -0
  222. package/src/hooks/types.ts +35 -0
  223. package/src/hooks/url.rewriter.ts +22 -0
  224. package/src/hooks/useCustomD3Graph.tsx +705 -0
  225. package/src/hooks/useDataListRetriever.ts +349 -0
  226. package/src/hooks/useDebounce.ts +33 -0
  227. package/src/hooks/usePageUrlGenerator.ts +50 -0
  228. package/src/hooks/useTableGenerator.ts +16 -0
  229. package/src/i18n/config.ts +73 -0
  230. package/src/i18n/index.ts +18 -0
  231. package/src/index.ts +16 -0
  232. package/src/interfaces/breadcrumb.item.data.interface.ts +4 -0
  233. package/src/interfaces/d3.link.interface.ts +7 -0
  234. package/src/interfaces/d3.node.interface.ts +12 -0
  235. package/src/interfaces/index.ts +3 -0
  236. package/src/permissions/check.ts +127 -0
  237. package/src/permissions/index.ts +2 -0
  238. package/src/permissions/types.ts +109 -0
  239. package/src/roles/config.ts +46 -0
  240. package/src/roles/index.ts +1 -0
  241. package/src/server/cache.ts +28 -0
  242. package/src/server/index.ts +3 -0
  243. package/src/server/request.ts +113 -0
  244. package/src/server/token.ts +10 -0
  245. package/src/shadcnui/custom/kanban.tsx +1001 -0
  246. package/src/shadcnui/custom/link.tsx +18 -0
  247. package/src/shadcnui/custom/multi-select.tsx +382 -0
  248. package/src/shadcnui/index.ts +49 -0
  249. package/src/shadcnui/ui/accordion.tsx +52 -0
  250. package/src/shadcnui/ui/alert-dialog.tsx +141 -0
  251. package/src/shadcnui/ui/alert.tsx +43 -0
  252. package/src/shadcnui/ui/avatar.tsx +50 -0
  253. package/src/shadcnui/ui/badge.tsx +40 -0
  254. package/src/shadcnui/ui/breadcrumb.tsx +115 -0
  255. package/src/shadcnui/ui/button.tsx +51 -0
  256. package/src/shadcnui/ui/calendar.tsx +73 -0
  257. package/src/shadcnui/ui/card.tsx +43 -0
  258. package/src/shadcnui/ui/carousel.tsx +225 -0
  259. package/src/shadcnui/ui/chart.tsx +320 -0
  260. package/src/shadcnui/ui/checkbox.tsx +29 -0
  261. package/src/shadcnui/ui/collapsible.tsx +11 -0
  262. package/src/shadcnui/ui/command.tsx +155 -0
  263. package/src/shadcnui/ui/context-menu.tsx +179 -0
  264. package/src/shadcnui/ui/dialog.tsx +96 -0
  265. package/src/shadcnui/ui/drawer.tsx +89 -0
  266. package/src/shadcnui/ui/dropdown-menu.tsx +205 -0
  267. package/src/shadcnui/ui/form.tsx +138 -0
  268. package/src/shadcnui/ui/hover-card.tsx +29 -0
  269. package/src/shadcnui/ui/input.tsx +21 -0
  270. package/src/shadcnui/ui/label.tsx +26 -0
  271. package/src/shadcnui/ui/navigation-menu.tsx +168 -0
  272. package/src/shadcnui/ui/popover.tsx +33 -0
  273. package/src/shadcnui/ui/progress.tsx +25 -0
  274. package/src/shadcnui/ui/radio-group.tsx +37 -0
  275. package/src/shadcnui/ui/resizable.tsx +47 -0
  276. package/src/shadcnui/ui/scroll-area.tsx +40 -0
  277. package/src/shadcnui/ui/select.tsx +164 -0
  278. package/src/shadcnui/ui/separator.tsx +28 -0
  279. package/src/shadcnui/ui/sheet.tsx +139 -0
  280. package/src/shadcnui/ui/sidebar.tsx +677 -0
  281. package/src/shadcnui/ui/skeleton.tsx +13 -0
  282. package/src/shadcnui/ui/slider.tsx +25 -0
  283. package/src/shadcnui/ui/sonner.tsx +25 -0
  284. package/src/shadcnui/ui/switch.tsx +31 -0
  285. package/src/shadcnui/ui/table.tsx +120 -0
  286. package/src/shadcnui/ui/tabs.tsx +55 -0
  287. package/src/shadcnui/ui/textarea.tsx +24 -0
  288. package/src/shadcnui/ui/toggle.tsx +39 -0
  289. package/src/shadcnui/ui/tooltip.tsx +61 -0
  290. package/src/unified/JsonApiRequest.ts +325 -0
  291. package/src/unified/index.ts +1 -0
  292. package/src/utils/blocknote-diff.util.ts +815 -0
  293. package/src/utils/blocknote-word-diff-renderer.util.ts +413 -0
  294. package/src/utils/cn.ts +6 -0
  295. package/src/utils/compose-refs.ts +61 -0
  296. package/src/utils/date-formatter.ts +53 -0
  297. package/src/utils/exists.ts +7 -0
  298. package/src/utils/index.ts +15 -0
  299. package/src/utils/schemas/entity.object.schema.ts +8 -0
  300. package/src/utils/schemas/index.ts +2 -0
  301. package/src/utils/schemas/user.object.schema.ts +9 -0
  302. package/src/utils/table-options.ts +67 -0
  303. package/src/utils/use-mobile.tsx +21 -0
@@ -0,0 +1,413 @@
1
+ import { PartialBlock } from "@blocknote/core";
2
+ import { DiffBlock, WordDiff } from "./blocknote-diff.util";
3
+
4
+ export class BlockNoteWordDiffRendererUtil {
5
+ static renderWordDiffs(
6
+ diffBlocks: DiffBlock[],
7
+ onAcceptChange?: (diffId: string) => void,
8
+ onRejectChange?: (diffId: string) => void,
9
+ acceptedChanges?: Set<string>,
10
+ rejectedChanges?: Set<string>,
11
+ ): PartialBlock[] {
12
+ if (acceptedChanges || rejectedChanges) {
13
+ diffBlocks = this.updateWordDiffStates(diffBlocks, acceptedChanges, rejectedChanges);
14
+ }
15
+ return diffBlocks.map((block) => this.renderDiffBlock(block, onAcceptChange, onRejectChange));
16
+ }
17
+
18
+ private static updateWordDiffStates(
19
+ diffBlocks: DiffBlock[],
20
+ acceptedChanges?: Set<string>,
21
+ rejectedChanges?: Set<string>,
22
+ ): DiffBlock[] {
23
+ return diffBlocks.map((block) => {
24
+ const updatedBlock = { ...block };
25
+
26
+ if (updatedBlock.diffId) {
27
+ updatedBlock.accepted = acceptedChanges?.has(updatedBlock.diffId) || false;
28
+ updatedBlock.rejected = rejectedChanges?.has(updatedBlock.diffId) || false;
29
+ }
30
+
31
+ if (updatedBlock.wordDiffs) {
32
+ updatedBlock.wordDiffs = updatedBlock.wordDiffs.map((wordDiff) => ({
33
+ ...wordDiff,
34
+ accepted: acceptedChanges?.has(wordDiff.diffId) || false,
35
+ rejected: rejectedChanges?.has(wordDiff.diffId) || false,
36
+ }));
37
+ }
38
+
39
+ if (updatedBlock.children) {
40
+ updatedBlock.children = this.updateWordDiffStates(
41
+ updatedBlock.children as DiffBlock[],
42
+ acceptedChanges,
43
+ rejectedChanges,
44
+ );
45
+ }
46
+
47
+ return updatedBlock;
48
+ });
49
+ }
50
+
51
+ private static renderDiffBlock(
52
+ block: DiffBlock,
53
+ onAcceptChange?: (diffId: string) => void,
54
+ onRejectChange?: (diffId: string) => void,
55
+ ): PartialBlock {
56
+ if (block.diffType === "modified" && block.wordDiffs) {
57
+ return this.renderWordLevelDiff(block, onAcceptChange, onRejectChange);
58
+ }
59
+
60
+ if (block.diffType === "added" || block.diffType === "removed") {
61
+ return this.renderBlockLevelDiff(block, onAcceptChange, onRejectChange);
62
+ }
63
+
64
+ const baseBlock: PartialBlock = {
65
+ id: block.id || crypto.randomUUID(),
66
+ type: (block.type as any) || "paragraph",
67
+ props: this.getBlockProps(block),
68
+ content: Array.isArray(block.content) ? block.content : [],
69
+ children: block.children?.map((child) =>
70
+ this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange),
71
+ ),
72
+ };
73
+
74
+ return baseBlock;
75
+ }
76
+
77
+ private static renderBlockLevelDiff(
78
+ block: DiffBlock,
79
+ onAcceptChange?: (diffId: string) => void,
80
+ onRejectChange?: (diffId: string) => void,
81
+ ): PartialBlock {
82
+ if (!block.diffId) {
83
+ return {
84
+ id: block.id || crypto.randomUUID(),
85
+ type: (block.type as any) || "paragraph",
86
+ props: block.props || {},
87
+ content: Array.isArray(block.content) ? block.content : [],
88
+ children: block.children?.map((child) =>
89
+ this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange),
90
+ ),
91
+ };
92
+ }
93
+
94
+ const blockAccepted = block.accepted || false;
95
+ const blockRejected = block.rejected || false;
96
+
97
+ let content = Array.isArray(block.content) ? [...block.content] : [];
98
+
99
+ if (block.diffType === "added") {
100
+ if (blockRejected) {
101
+ return {
102
+ id: block.id || crypto.randomUUID(),
103
+ type: "paragraph",
104
+ props: {},
105
+ content: [],
106
+ children: [],
107
+ };
108
+ } else if (!blockAccepted) {
109
+ content = content.map((item: any) => ({
110
+ ...item,
111
+ styles: { ...item.styles, bold: true },
112
+ }));
113
+
114
+ content.push({
115
+ type: "diffActions",
116
+ props: { diffIds: block.diffId },
117
+ });
118
+ }
119
+ } else if (block.diffType === "removed") {
120
+ if (blockAccepted) {
121
+ return {
122
+ id: block.id || crypto.randomUUID(),
123
+ type: "paragraph",
124
+ props: {},
125
+ content: [],
126
+ children: [],
127
+ };
128
+ } else if (!blockRejected) {
129
+ content = content.map((item: any) => ({
130
+ ...item,
131
+ styles: { ...item.styles, strike: true },
132
+ }));
133
+
134
+ content.push({
135
+ type: "diffActions",
136
+ props: { diffIds: block.diffId },
137
+ });
138
+ }
139
+ }
140
+
141
+ const baseBlock: PartialBlock = {
142
+ id: block.id || crypto.randomUUID(),
143
+ type: (block.type as any) || "paragraph",
144
+ props: this.getBlockProps(block),
145
+ content: content,
146
+ children: block.children?.map((child) =>
147
+ this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange),
148
+ ),
149
+ };
150
+
151
+ return baseBlock;
152
+ }
153
+
154
+ private static renderWordLevelDiff(
155
+ block: DiffBlock,
156
+ onAcceptChange?: (diffId: string) => void,
157
+ onRejectChange?: (diffId: string) => void,
158
+ ): PartialBlock {
159
+ if (!block.wordDiffs) {
160
+ return {
161
+ id: block.id || crypto.randomUUID(),
162
+ type: (block.type as any) || "paragraph",
163
+ props: block.props || {},
164
+ content: Array.isArray(block.content) ? block.content : [],
165
+ children: [],
166
+ };
167
+ }
168
+
169
+ const content = this.groupAndRenderWordDiffs(block.wordDiffs);
170
+
171
+ return {
172
+ id: block.id || crypto.randomUUID(),
173
+ type: (block.type as any) || "paragraph",
174
+ props: block.props || {},
175
+ content: Array.isArray(content) ? content : [],
176
+ children:
177
+ block.children?.map((child) => this.renderDiffBlock(child as DiffBlock, onAcceptChange, onRejectChange)) || [],
178
+ };
179
+ }
180
+
181
+ private static groupAndRenderWordDiffs(wordDiffs: WordDiff[]): any[] {
182
+ const content: any[] = [];
183
+
184
+ for (let i = 0; i < wordDiffs.length; i++) {
185
+ const wordDiff = wordDiffs[i];
186
+
187
+ const isLastOfGroup = this.isLastDiffInGroup(wordDiffs, i);
188
+
189
+ const textContent = this.createTextContent(wordDiff, isLastOfGroup);
190
+ if (textContent) {
191
+ if (Array.isArray(textContent)) {
192
+ content.push(...textContent);
193
+ } else {
194
+ content.push(textContent);
195
+ }
196
+ }
197
+ }
198
+
199
+ const cleanedContent = this.cleanupSpaces(content);
200
+
201
+ return cleanedContent;
202
+ }
203
+
204
+ private static cleanupSpaces(content: any[]): any[] {
205
+ const filtered = content.filter((item) => item !== null && item !== undefined);
206
+
207
+ const cleaned: any[] = [];
208
+
209
+ for (let i = 0; i < filtered.length; i++) {
210
+ const current = filtered[i];
211
+
212
+ if (current.type === "text" && current.text === " ") {
213
+ const lastItem = cleaned[cleaned.length - 1];
214
+
215
+ if (
216
+ i === 0 ||
217
+ i === filtered.length - 1 ||
218
+ (lastItem && lastItem.type === "text" && lastItem.text === " ") ||
219
+ (lastItem && lastItem.type === "diffActions")
220
+ ) {
221
+ continue;
222
+ }
223
+ }
224
+
225
+ if (current.type === "diffActions") {
226
+ const nextItem = i + 1 < filtered.length ? filtered[i + 1] : null;
227
+
228
+ cleaned.push(current);
229
+
230
+ if (nextItem && nextItem.type === "text" && nextItem.text !== " " && nextItem.type !== "diffActions") {
231
+ cleaned.push({
232
+ type: "text",
233
+ text: " ",
234
+ styles: {},
235
+ });
236
+ }
237
+
238
+ continue;
239
+ }
240
+
241
+ cleaned.push(current);
242
+ }
243
+
244
+ return cleaned;
245
+ }
246
+
247
+ private static isLastDiffInGroup(wordDiffs: WordDiff[], currentIndex: number): boolean {
248
+ const currentDiff = wordDiffs[currentIndex];
249
+
250
+ if (currentDiff.type === "unchanged") {
251
+ return false;
252
+ }
253
+
254
+ for (let i = currentIndex + 1; i < wordDiffs.length; i++) {
255
+ const nextDiff = wordDiffs[i];
256
+
257
+ if (nextDiff.diffId === currentDiff.diffId && (nextDiff.type === "added" || nextDiff.type === "removed")) {
258
+ return false;
259
+ }
260
+
261
+ if (nextDiff.type !== "unchanged" && nextDiff.diffId !== currentDiff.diffId) {
262
+ break;
263
+ }
264
+ }
265
+
266
+ return true;
267
+ }
268
+
269
+ private static createTextContent(wordDiff: WordDiff, isLastOfGroup?: boolean): any[] | any {
270
+ switch (wordDiff.type) {
271
+ case "added":
272
+ if (wordDiff.accepted) {
273
+ return {
274
+ type: "text",
275
+ text: wordDiff.text,
276
+ styles: {},
277
+ };
278
+ } else if (wordDiff.rejected) {
279
+ return null;
280
+ } else {
281
+ if (wordDiff.text.trim() === "") {
282
+ return {
283
+ type: "text",
284
+ text: wordDiff.text,
285
+ styles: { backgroundColor: "#dcfce7" },
286
+ };
287
+ } else {
288
+ const baseContent = {
289
+ type: "text",
290
+ text: wordDiff.text,
291
+ styles: { bold: true },
292
+ };
293
+
294
+ if (isLastOfGroup) {
295
+ return [
296
+ baseContent,
297
+ {
298
+ type: "diffActions",
299
+ props: { diffIds: wordDiff.diffId },
300
+ },
301
+ ];
302
+ } else {
303
+ return baseContent;
304
+ }
305
+ }
306
+ }
307
+
308
+ case "removed":
309
+ if (wordDiff.accepted) {
310
+ return null;
311
+ } else if (wordDiff.rejected) {
312
+ return {
313
+ type: "text",
314
+ text: wordDiff.text,
315
+ styles: {},
316
+ };
317
+ } else {
318
+ if (wordDiff.text.trim() === "") {
319
+ return {
320
+ type: "text",
321
+ text: wordDiff.text,
322
+ styles: { strike: true },
323
+ };
324
+ } else {
325
+ const baseContent = {
326
+ type: "text",
327
+ text: wordDiff.text,
328
+ styles: { strike: true },
329
+ };
330
+
331
+ if (isLastOfGroup) {
332
+ return [
333
+ baseContent,
334
+ {
335
+ type: "diffActions",
336
+ props: { diffIds: wordDiff.diffId },
337
+ },
338
+ ];
339
+ } else {
340
+ return baseContent;
341
+ }
342
+ }
343
+ }
344
+
345
+ case "unchanged":
346
+ default:
347
+ return {
348
+ type: "text",
349
+ text: wordDiff.text,
350
+ styles: {},
351
+ };
352
+ }
353
+ }
354
+
355
+ private static getBlockProps(block: DiffBlock): any {
356
+ const baseProps = block.props || {};
357
+
358
+ return baseProps;
359
+ }
360
+
361
+ static generateChangeSummary(diffBlocks: DiffBlock[]): {
362
+ totalWords: number;
363
+ addedWords: number;
364
+ removedWords: number;
365
+ acceptedChanges: number;
366
+ rejectedChanges: number;
367
+ pendingChanges: number;
368
+ } {
369
+ let totalWords = 0;
370
+ let addedWords = 0;
371
+ let removedWords = 0;
372
+ let acceptedChanges = 0;
373
+ let rejectedChanges = 0;
374
+ let pendingChanges = 0;
375
+
376
+ const processBlock = (block: DiffBlock) => {
377
+ if (block.wordDiffs) {
378
+ block.wordDiffs.forEach((wordDiff) => {
379
+ totalWords++;
380
+
381
+ if (wordDiff.type === "added") {
382
+ addedWords++;
383
+ } else if (wordDiff.type === "removed") {
384
+ removedWords++;
385
+ }
386
+
387
+ if (wordDiff.accepted) {
388
+ acceptedChanges++;
389
+ } else if (wordDiff.rejected) {
390
+ rejectedChanges++;
391
+ } else if (wordDiff.type !== "unchanged") {
392
+ pendingChanges++;
393
+ }
394
+ });
395
+ }
396
+
397
+ if (block.children) {
398
+ block.children.forEach((child) => processBlock(child as DiffBlock));
399
+ }
400
+ };
401
+
402
+ diffBlocks.forEach(processBlock);
403
+
404
+ return {
405
+ totalWords,
406
+ addedWords,
407
+ removedWords,
408
+ acceptedChanges,
409
+ rejectedChanges,
410
+ pendingChanges,
411
+ };
412
+ }
413
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,61 @@
1
+ import * as React from "react";
2
+
3
+ type PossibleRef<T> = React.Ref<T> | undefined;
4
+
5
+ /**
6
+ * Set a given ref to a given value
7
+ * This utility takes care of different types of refs: callback refs and RefObject(s)
8
+ */
9
+ function setRef<T>(ref: PossibleRef<T>, value: T) {
10
+ if (typeof ref === "function") {
11
+ return ref(value);
12
+ }
13
+
14
+ if (ref !== null && ref !== undefined) {
15
+ ref.current = value;
16
+ }
17
+ }
18
+
19
+ /**
20
+ * A utility to compose multiple refs together
21
+ * Accepts callback refs and RefObject(s)
22
+ */
23
+ function composeRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
24
+ return (node) => {
25
+ let hasCleanup = false;
26
+ const cleanups = refs.map((ref) => {
27
+ const cleanup = setRef(ref, node);
28
+ if (!hasCleanup && typeof cleanup === "function") {
29
+ hasCleanup = true;
30
+ }
31
+ return cleanup;
32
+ });
33
+
34
+ // React <19 will log an error to the console if a callback ref returns a
35
+ // value. We don't use ref cleanups internally so this will only happen if a
36
+ // user's ref callback returns a value, which we only expect if they are
37
+ // using the cleanup functionality added in React 19.
38
+ if (hasCleanup) {
39
+ return () => {
40
+ for (let i = 0; i < cleanups.length; i++) {
41
+ const cleanup = cleanups[i];
42
+ if (typeof cleanup === "function") {
43
+ cleanup();
44
+ } else {
45
+ setRef(refs[i], null);
46
+ }
47
+ }
48
+ };
49
+ }
50
+ };
51
+ }
52
+
53
+ /**
54
+ * A custom hook that composes multiple refs
55
+ * Accepts callback refs and RefObject(s)
56
+ */
57
+ function useComposedRefs<T>(...refs: PossibleRef<T>[]): React.RefCallback<T> {
58
+ return React.useCallback(composeRefs(...refs), refs);
59
+ }
60
+
61
+ export { composeRefs, useComposedRefs };
@@ -0,0 +1,53 @@
1
+ type FormatOption = "date" | "time" | "dateTime" | "timeSince" | "default";
2
+
3
+ export const formatDate = (eventDate: Date, formatOption: FormatOption): string => {
4
+ const browserLocale = navigator.language || "en-US";
5
+
6
+ const formatPart = (date: Date, options: Intl.DateTimeFormatOptions): string =>
7
+ new Intl.DateTimeFormat(browserLocale, options).format(date);
8
+
9
+ const now = new Date();
10
+ const diff = now.getTime() - eventDate.getTime();
11
+
12
+ if (formatOption === "default") {
13
+ if (diff < 24 * 60 * 60 * 1000) formatOption = "timeSince";
14
+ else formatOption = "dateTime";
15
+ }
16
+
17
+ if (formatOption === "timeSince") {
18
+ const seconds = Math.floor(diff / 1000);
19
+ const minutes = Math.floor(seconds / 60);
20
+ const hours = Math.floor(minutes / 60);
21
+ const days = Math.floor(hours / 24);
22
+
23
+ if (days > 0) return `${days} days ago`;
24
+ if (hours > 0) return `${hours} hours ago`;
25
+ if (minutes > 0) return `${minutes} minutes ago`;
26
+ return `${seconds} seconds ago`;
27
+ }
28
+
29
+ // Define formatting options
30
+ const dateOptions: Intl.DateTimeFormatOptions = {
31
+ year: "numeric",
32
+ month: "2-digit",
33
+ day: "2-digit",
34
+ };
35
+ const timeOptions: Intl.DateTimeFormatOptions = {
36
+ hour: "2-digit",
37
+ minute: "2-digit",
38
+ };
39
+
40
+ // Format based on the option
41
+ switch (formatOption) {
42
+ case "date":
43
+ return formatPart(eventDate, dateOptions);
44
+ case "time":
45
+ return formatPart(eventDate, timeOptions);
46
+ case "dateTime":
47
+ return `${formatPart(eventDate, dateOptions)} ${formatPart(eventDate, timeOptions)}`;
48
+ default:
49
+ throw new Error("Invalid format option");
50
+ }
51
+ };
52
+
53
+ export type { FormatOption };
@@ -0,0 +1,7 @@
1
+ export const exists = <T>(itemOrArray: T | T[] | null | undefined): boolean => {
2
+ if (!itemOrArray) return false;
3
+ if (Array.isArray(itemOrArray)) {
4
+ return itemOrArray.length > 0;
5
+ }
6
+ return true;
7
+ };
@@ -0,0 +1,15 @@
1
+ export type { ClassValue } from "clsx";
2
+ export { cn } from "./cn";
3
+ export { composeRefs, useComposedRefs } from "./compose-refs";
4
+ export { useIsMobile } from "./use-mobile";
5
+
6
+ // New utilities
7
+ export { formatDate, type FormatOption } from "./date-formatter";
8
+ export { exists } from "./exists";
9
+ export { getTableComponents, getTableOptions, TableOptions } from "./table-options";
10
+
11
+ // Schemas
12
+ export { entityObjectSchema, userObjectSchema, type EntityObject, type UserObject } from "./schemas";
13
+
14
+ export * from "./blocknote-diff.util";
15
+ export * from "./blocknote-word-diff-renderer.util";
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+
3
+ export const entityObjectSchema = z.object({
4
+ id: z.uuidv4(),
5
+ name: z.string(),
6
+ });
7
+
8
+ export type EntityObject = z.infer<typeof entityObjectSchema>;
@@ -0,0 +1,2 @@
1
+ export { userObjectSchema, type UserObject } from "./user.object.schema";
2
+ export { entityObjectSchema, type EntityObject } from "./entity.object.schema";
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+
3
+ export const userObjectSchema = z.object({
4
+ id: z.string(),
5
+ name: z.string(),
6
+ avatar: z.string().optional(),
7
+ });
8
+
9
+ export type UserObject = z.infer<typeof userObjectSchema>;
@@ -0,0 +1,67 @@
1
+ import { Action, ModuleWithPermissions } from "../permissions";
2
+ import { cloneElement, createElement, Fragment, ReactElement } from "react";
3
+
4
+ export class TableOptions {
5
+ private _components: ReactElement<any>[];
6
+ private _hasPermissionToModule: <M extends ModuleWithPermissions>(params: {
7
+ module: M;
8
+ action: Action;
9
+ data?: any;
10
+ }) => boolean;
11
+
12
+ constructor(
13
+ hasPermissionToModule: <M extends ModuleWithPermissions>(params: {
14
+ module: M;
15
+ action: Action;
16
+ data?: any;
17
+ }) => boolean,
18
+ ) {
19
+ this._hasPermissionToModule = hasPermissionToModule;
20
+ this._components = [];
21
+ }
22
+
23
+ addOption<M extends ModuleWithPermissions>(component: ReactElement<any> | null, module?: M, action?: Action): void {
24
+ if (!component || (module && action && !this._hasPermissionToModule({ module, action }))) return;
25
+ this._components.push(component);
26
+ }
27
+
28
+ getComponents(): ReactElement<any>[] {
29
+ return this._components;
30
+ }
31
+
32
+ getOptions(): ReactElement<any> | null {
33
+ if (this._components.length === 0) return null;
34
+
35
+ const response: ReactElement<any>[] = this._components.map((option, index) =>
36
+ cloneElement(option, { key: option.key ?? index }),
37
+ );
38
+
39
+ return createElement(Fragment, {}, response);
40
+ }
41
+ }
42
+
43
+ export function getTableOptions(params: {
44
+ hasPermissionToModule: <M extends ModuleWithPermissions>(params: {
45
+ module: M;
46
+ action: Action;
47
+ data?: any;
48
+ }) => boolean;
49
+ options: { component: ReactElement<any> | null; module?: ModuleWithPermissions; action?: Action }[];
50
+ }): ReactElement<any> | null {
51
+ const tableOptions = new TableOptions(params.hasPermissionToModule);
52
+ params.options.forEach((option) => tableOptions.addOption(option.component, option.module, option.action));
53
+ return tableOptions.getOptions();
54
+ }
55
+
56
+ export function getTableComponents(params: {
57
+ hasPermissionToModule: <M extends ModuleWithPermissions>(params: {
58
+ module: M;
59
+ action: Action;
60
+ data?: any;
61
+ }) => boolean;
62
+ options: { component: ReactElement<any> | null; module?: ModuleWithPermissions; action?: Action }[];
63
+ }): ReactElement<any>[] {
64
+ const tableOptions = new TableOptions(params.hasPermissionToModule);
65
+ params.options.forEach((option) => tableOptions.addOption(option.component, option.module, option.action));
66
+ return tableOptions.getComponents();
67
+ }
@@ -0,0 +1,21 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ const MOBILE_BREAKPOINT = 768;
6
+
7
+ export function useIsMobile() {
8
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
9
+
10
+ React.useEffect(() => {
11
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
12
+ const onChange = () => {
13
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
14
+ };
15
+ mql.addEventListener("change", onChange);
16
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
17
+ return () => mql.removeEventListener("change", onChange);
18
+ }, []);
19
+
20
+ return !!isMobile;
21
+ }