@rebasepro/cms 0.17.3 → 0.18.1

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 (449) hide show
  1. package/README.md +4 -0
  2. package/dist/{CollectionEditorDialog-B-akreOU.js → CollectionEditorDialog-BeX4b92r.js} +13 -7
  3. package/dist/{CollectionEditorDialog-B-akreOU.js.map → CollectionEditorDialog-BeX4b92r.js.map} +1 -1
  4. package/dist/{PropertyEditView-CiY84wkR.js → PropertyEditView-C7thfj4L.js} +3 -3
  5. package/dist/{PropertyEditView-CiY84wkR.js.map → PropertyEditView-C7thfj4L.js.map} +1 -1
  6. package/dist/{RouterCollectionsStudioView-DkAWda4N.js → RouterCollectionsStudioView-BtjzvB6E.js} +4 -4
  7. package/dist/{RouterCollectionsStudioView-DkAWda4N.js.map → RouterCollectionsStudioView-BtjzvB6E.js.map} +1 -1
  8. package/dist/{SchemaChangeDialog-DceT8QFX.js → SchemaChangeDialog-CkqK6KOC.js} +4 -2
  9. package/dist/{SchemaChangeDialog-DceT8QFX.js.map → SchemaChangeDialog-CkqK6KOC.js.map} +1 -1
  10. package/dist/collection_editor/api/generateCollectionApi.d.ts +1 -2
  11. package/dist/collection_editor_ui.d.ts +2 -0
  12. package/dist/collection_editor_ui.js +6 -5
  13. package/dist/collection_editor_ui.js.map +1 -1
  14. package/dist/components/DeleteEntityDialog.d.ts +2 -3
  15. package/dist/components/RebaseLayout.d.ts +11 -4
  16. package/dist/components/field_configs.d.ts +1 -1
  17. package/dist/editor/sanitize-html.d.ts +15 -0
  18. package/dist/editor.js +195 -10
  19. package/dist/editor.js.map +1 -1
  20. package/dist/{export-Ben8E355.js → export--arni_VY.js} +2 -2
  21. package/dist/{export-Ben8E355.js.map → export--arni_VY.js.map} +1 -1
  22. package/dist/form/components/LabelWithIcon.d.ts +10 -0
  23. package/dist/{history-DIEVd26f.js → history-C2TpoQwi.js} +2 -2
  24. package/dist/{history-DIEVd26f.js.map → history-C2TpoQwi.js.map} +1 -1
  25. package/dist/hooks/navigation/useBuildNavigationStateController.d.ts +1 -1
  26. package/dist/hooks/navigation/useResolvedViews.d.ts +1 -1
  27. package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
  28. package/dist/{import-D25x7ko-.js → import-DTkXNoBg.js} +2 -2
  29. package/dist/{import-D25x7ko-.js.map → import-DTkXNoBg.js.map} +1 -1
  30. package/dist/index.js +78 -33
  31. package/dist/index.js.map +1 -1
  32. package/dist/util/propertyConfigMapper.d.ts +1 -1
  33. package/dist/util-CfLFggP7.js +57 -0
  34. package/dist/util-CfLFggP7.js.map +1 -0
  35. package/dist/{util-DVqnqlLn.js → util-DE1WQW6h.js} +134 -112
  36. package/dist/util-DE1WQW6h.js.map +1 -0
  37. package/package.json +42 -34
  38. package/dist/util-DVqnqlLn.js.map +0 -1
  39. package/src/collection_editor/CollectionEditorDialogsContext.tsx +0 -25
  40. package/src/collection_editor/ConfigControllerProvider.tsx +0 -395
  41. package/src/collection_editor/_cms_internals.ts +0 -41
  42. package/src/collection_editor/api/generateCollectionApi.ts +0 -126
  43. package/src/collection_editor/api/index.ts +0 -1
  44. package/src/collection_editor/extensibility_types.ts +0 -168
  45. package/src/collection_editor/index.ts +0 -108
  46. package/src/collection_editor/liveSchemaClient.ts +0 -285
  47. package/src/collection_editor/relation_kinds.ts +0 -44
  48. package/src/collection_editor/serializable_types.ts +0 -535
  49. package/src/collection_editor/serializable_utils.ts +0 -729
  50. package/src/collection_editor/types/collection_editor_controller.tsx +0 -62
  51. package/src/collection_editor/types/collection_inference.ts +0 -18
  52. package/src/collection_editor/types/config_controller.tsx +0 -121
  53. package/src/collection_editor/ui/AddKanbanColumnAction.tsx +0 -199
  54. package/src/collection_editor/ui/CollectionViewHeaderAction.tsx +0 -51
  55. package/src/collection_editor/ui/EditorCollectionAction.tsx +0 -54
  56. package/src/collection_editor/ui/EditorCollectionActionStart.tsx +0 -90
  57. package/src/collection_editor/ui/EditorEntityAction.tsx +0 -50
  58. package/src/collection_editor/ui/HomePageEditorCollectionAction.tsx +0 -114
  59. package/src/collection_editor/ui/KanbanSetupAction.tsx +0 -43
  60. package/src/collection_editor/ui/MissingReferenceWidget.tsx +0 -35
  61. package/src/collection_editor/ui/NewCollectionButton.tsx +0 -27
  62. package/src/collection_editor/ui/NewCollectionCard.tsx +0 -48
  63. package/src/collection_editor/ui/PropertyAddColumnComponent.tsx +0 -49
  64. package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +0 -260
  65. package/src/collection_editor/ui/collection_editor/AIModifiedPathsContext.tsx +0 -90
  66. package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +0 -388
  67. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +0 -1098
  68. package/src/collection_editor/ui/collection_editor/CollectionEditorWelcomeView.tsx +0 -301
  69. package/src/collection_editor/ui/collection_editor/CollectionJsonImportDialog.tsx +0 -172
  70. package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +0 -620
  71. package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +0 -530
  72. package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +0 -462
  73. package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +0 -104
  74. package/src/collection_editor/ui/collection_editor/CollectionYupValidation.tsx +0 -9
  75. package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +0 -199
  76. package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +0 -315
  77. package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +0 -164
  78. package/src/collection_editor/ui/collection_editor/EntityActionsSelectDialog.tsx +0 -40
  79. package/src/collection_editor/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +0 -39
  80. package/src/collection_editor/ui/collection_editor/EnumForm.tsx +0 -373
  81. package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +0 -288
  82. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +0 -141
  83. package/src/collection_editor/ui/collection_editor/KanbanConfigSection.tsx +0 -194
  84. package/src/collection_editor/ui/collection_editor/LayoutModeSwitch.tsx +0 -53
  85. package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +0 -980
  86. package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +0 -164
  87. package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +0 -364
  88. package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +0 -44
  89. package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +0 -41
  90. package/src/collection_editor/ui/collection_editor/SchemaChangeDialog.tsx +0 -470
  91. package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +0 -256
  92. package/src/collection_editor/ui/collection_editor/SwitchControl.tsx +0 -39
  93. package/src/collection_editor/ui/collection_editor/ViewModeSwitch.tsx +0 -68
  94. package/src/collection_editor/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +0 -67
  95. package/src/collection_editor/ui/collection_editor/import/CollectionEditorImportMapping.tsx +0 -296
  96. package/src/collection_editor/ui/collection_editor/import/clean_import_data.ts +0 -57
  97. package/src/collection_editor/ui/collection_editor/properties/BlockPropertyField.tsx +0 -147
  98. package/src/collection_editor/ui/collection_editor/properties/BooleanPropertyField.tsx +0 -41
  99. package/src/collection_editor/ui/collection_editor/properties/CommonPropertyFields.tsx +0 -113
  100. package/src/collection_editor/ui/collection_editor/properties/DateTimePropertyField.tsx +0 -250
  101. package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +0 -126
  102. package/src/collection_editor/ui/collection_editor/properties/KeyValuePropertyField.tsx +0 -20
  103. package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +0 -153
  104. package/src/collection_editor/ui/collection_editor/properties/MarkdownPropertyField.tsx +0 -217
  105. package/src/collection_editor/ui/collection_editor/properties/NumberPropertyField.tsx +0 -117
  106. package/src/collection_editor/ui/collection_editor/properties/ReferencePropertyField.tsx +0 -129
  107. package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +0 -335
  108. package/src/collection_editor/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -107
  109. package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +0 -403
  110. package/src/collection_editor/ui/collection_editor/properties/StringPropertyField.tsx +0 -148
  111. package/src/collection_editor/ui/collection_editor/properties/UrlPropertyField.tsx +0 -91
  112. package/src/collection_editor/ui/collection_editor/properties/VectorPropertyField.tsx +0 -34
  113. package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +0 -49
  114. package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +0 -925
  115. package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +0 -28
  116. package/src/collection_editor/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +0 -626
  117. package/src/collection_editor/ui/collection_editor/properties/conditions/index.ts +0 -6
  118. package/src/collection_editor/ui/collection_editor/properties/conditions/property_paths.ts +0 -93
  119. package/src/collection_editor/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +0 -52
  120. package/src/collection_editor/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +0 -63
  121. package/src/collection_editor/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +0 -114
  122. package/src/collection_editor/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +0 -154
  123. package/src/collection_editor/ui/collection_editor/properties/validation/ValidationPanel.tsx +0 -28
  124. package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +0 -112
  125. package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +0 -183
  126. package/src/collection_editor/ui/collection_editor/templates/products_template.ts +0 -88
  127. package/src/collection_editor/ui/collection_editor/templates/users_template.ts +0 -42
  128. package/src/collection_editor/ui/collection_editor/util.ts +0 -28
  129. package/src/collection_editor/ui/collection_editor/utils/strings.ts +0 -1
  130. package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -39
  131. package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +0 -276
  132. package/src/collection_editor/useCollectionEditorController.tsx +0 -9
  133. package/src/collection_editor/useCollectionsConfigController.tsx +0 -9
  134. package/src/collection_editor/useJsonCollectionsConfigController.tsx +0 -297
  135. package/src/collection_editor/useLiveSchemaEditing.tsx +0 -315
  136. package/src/collection_editor/useLocalCollectionsConfigController.tsx +0 -341
  137. package/src/collection_editor/useSafeSnackbarController.ts +0 -9
  138. package/src/collection_editor/validateCollectionJson.ts +0 -440
  139. package/src/collection_editor_ui.ts +0 -21
  140. package/src/components/AdditionalFieldValue.tsx +0 -68
  141. package/src/components/AdminModeSyncer.tsx +0 -56
  142. package/src/components/ArrayContainer.tsx +0 -542
  143. package/src/components/ClearFilterSortButton.tsx +0 -56
  144. package/src/components/CollectionEditorDialogs.tsx +0 -69
  145. package/src/components/CollectionPanel.tsx +0 -179
  146. package/src/components/CollectionTableBinding/CollectionRowActions.tsx +0 -244
  147. package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +0 -394
  148. package/src/components/CollectionTableBinding/CollectionTableBindingProps.tsx +0 -172
  149. package/src/components/CollectionTableBinding/PropertyTableCell.tsx +0 -350
  150. package/src/components/CollectionTableBinding/column_utils.tsx +0 -155
  151. package/src/components/CollectionTableBinding/fields/TableMultipleRelationField.tsx +0 -131
  152. package/src/components/CollectionTableBinding/fields/TableReferenceField.tsx +0 -193
  153. package/src/components/CollectionTableBinding/fields/TableRelationField.tsx +0 -192
  154. package/src/components/CollectionTableBinding/fields/TableRelationSelectorField.tsx +0 -42
  155. package/src/components/CollectionTableBinding/fields/TableStorageUpload.tsx +0 -324
  156. package/src/components/CollectionTableBinding/fields/VirtualTableSelect.tsx +0 -132
  157. package/src/components/CollectionTableBinding/fields/VirtualTableUserSelect.tsx +0 -41
  158. package/src/components/CollectionTableBinding/index.tsx +0 -14
  159. package/src/components/CollectionTableBinding/internal/CollectionTableToolbar.tsx +0 -96
  160. package/src/components/CollectionTableBinding/internal/EntityTableCell.tsx +0 -309
  161. package/src/components/CollectionTableBinding/internal/EntityTableCellActions.tsx +0 -80
  162. package/src/components/CollectionTableBinding/internal/common.tsx +0 -74
  163. package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +0 -422
  164. package/src/components/CollectionTableBinding/internal/popup_field/useDraggable.tsx +0 -99
  165. package/src/components/CollectionTableBinding/internal/popup_field/useWindowSize.tsx +0 -22
  166. package/src/components/CollectionTableBinding/table_bindings.tsx +0 -350
  167. package/src/components/CollectionViewBinding/BoardCardBinding.tsx +0 -263
  168. package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +0 -662
  169. package/src/components/CollectionViewBinding/CollectionCardViewBinding.tsx +0 -159
  170. package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +0 -1430
  171. package/src/components/CollectionViewBinding/CollectionViewActions.tsx +0 -227
  172. package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +0 -1603
  173. package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +0 -240
  174. package/src/components/CollectionViewBinding/EntityCardBinding.tsx +0 -239
  175. package/src/components/CollectionViewBinding/FilterPresetsButton.tsx +0 -298
  176. package/src/components/CollectionViewBinding/FiltersDialog.tsx +0 -218
  177. package/src/components/CollectionViewBinding/SearchExplanation.tsx +0 -106
  178. package/src/components/CollectionViewBinding/SearchHighlight.tsx +0 -311
  179. package/src/components/CollectionViewBinding/SlotValue.tsx +0 -156
  180. package/src/components/CollectionViewBinding/SortButton.tsx +0 -354
  181. package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +0 -49
  182. package/src/components/CollectionViewBinding/SplitListShowButton.tsx +0 -28
  183. package/src/components/CollectionViewBinding/SplitListView.tsx +0 -488
  184. package/src/components/CollectionViewBinding/SplitViewContext.tsx +0 -34
  185. package/src/components/CollectionViewBinding/ViewModeToggle.tsx +0 -250
  186. package/src/components/CollectionViewBinding/hooks/useCollectionInlineEditor.ts +0 -81
  187. package/src/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.ts +0 -181
  188. package/src/components/CollectionViewBinding/useBoardDataController.tsx +0 -802
  189. package/src/components/CollectionViewBinding/usePreviewSlots.ts +0 -703
  190. package/src/components/CollectionViewBinding/useSelectionController.tsx +0 -40
  191. package/src/components/CollectionViewBinding/utils.ts +0 -22
  192. package/src/components/DefaultAppBar.tsx +0 -264
  193. package/src/components/DefaultDrawer.tsx +0 -593
  194. package/src/components/DeleteEntityDialog.tsx +0 -205
  195. package/src/components/DetailViewBinding.tsx +0 -731
  196. package/src/components/DrawerNavigationGroup.tsx +0 -141
  197. package/src/components/DrawerNavigationItem.tsx +0 -115
  198. package/src/components/EditFormActions.tsx +0 -303
  199. package/src/components/EditViewBinding.tsx +0 -1069
  200. package/src/components/EntityDisplayHeader.tsx +0 -170
  201. package/src/components/EntityIdentityBar.tsx +0 -428
  202. package/src/components/EntityInspector.tsx +0 -156
  203. package/src/components/EntityPreviewBinding.tsx +0 -432
  204. package/src/components/EntityPreviewNesting.tsx +0 -50
  205. package/src/components/EntityViewBinding.tsx +0 -374
  206. package/src/components/FieldCaption.tsx +0 -14
  207. package/src/components/HomePage/ContentHomePage.tsx +0 -586
  208. package/src/components/HomePage/FavouritesView.tsx +0 -60
  209. package/src/components/HomePage/HomePageDnD.tsx +0 -704
  210. package/src/components/HomePage/NavigationCard.tsx +0 -81
  211. package/src/components/HomePage/NavigationCardBinding.tsx +0 -107
  212. package/src/components/HomePage/NavigationGroup.tsx +0 -156
  213. package/src/components/HomePage/RenameGroupDialog.tsx +0 -123
  214. package/src/components/HomePage/SmallNavigationCard.tsx +0 -41
  215. package/src/components/HomePage/index.tsx +0 -5
  216. package/src/components/InlineEntityPreview.tsx +0 -149
  217. package/src/components/JsonPreviewBinding.tsx +0 -66
  218. package/src/components/PropertyCollectionView.tsx +0 -336
  219. package/src/components/PropertyConfigBadge.tsx +0 -28
  220. package/src/components/PropertyIdCopyTooltip.tsx +0 -47
  221. package/src/components/RebaseAuthGate.tsx +0 -44
  222. package/src/components/RebaseCMS.tsx +0 -30
  223. package/src/components/RebaseLayout.tsx +0 -80
  224. package/src/components/RebaseNavigation.tsx +0 -369
  225. package/src/components/RebaseRouteDefs.tsx +0 -127
  226. package/src/components/RebaseShell.tsx +0 -83
  227. package/src/components/ReferenceTable/SelectionTableBinding.tsx +0 -389
  228. package/src/components/ReferenceWidget.tsx +0 -156
  229. package/src/components/RelationSelector.tsx +0 -926
  230. package/src/components/SearchIconsView.tsx +0 -78
  231. package/src/components/SelectableTable/SelectableTable.tsx +0 -309
  232. package/src/components/SelectableTable/SelectableTableContext.tsx +0 -7
  233. package/src/components/SelectableTable/SelectionStore.ts +0 -43
  234. package/src/components/SelectableTable/filters/BooleanFilterField.tsx +0 -59
  235. package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +0 -146
  236. package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +0 -190
  237. package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +0 -250
  238. package/src/components/SelectableTable/filters/RelationFilterField.tsx +0 -238
  239. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +0 -282
  240. package/src/components/SelectableTable/filters/null_filter.ts +0 -63
  241. package/src/components/SideDialogs.tsx +0 -238
  242. package/src/components/SidePanelBinding.tsx +0 -361
  243. package/src/components/SidePanelProvider.tsx +0 -80
  244. package/src/components/UserSelector.tsx +0 -513
  245. package/src/components/admin/CreationResultDialog.tsx +0 -138
  246. package/src/components/admin/index.ts +0 -1
  247. package/src/components/app/AppBar.tsx +0 -17
  248. package/src/components/app/Drawer.tsx +0 -38
  249. package/src/components/app/Scaffold.tsx +0 -366
  250. package/src/components/app/index.ts +0 -4
  251. package/src/components/app/useApp.tsx +0 -36
  252. package/src/components/common/default_entity_actions.tsx +0 -440
  253. package/src/components/common/index.ts +0 -1
  254. package/src/components/field_configs.tsx +0 -506
  255. package/src/components/history/EntityHistoryEntry.tsx +0 -203
  256. package/src/components/history/EntityHistoryView.tsx +0 -206
  257. package/src/components/history/LastEditedByFormAction.tsx +0 -24
  258. package/src/components/history/LastEditedByIndicator.tsx +0 -95
  259. package/src/components/history/UserChip.tsx +0 -15
  260. package/src/components/history/index.tsx +0 -5
  261. package/src/components/index.ts +0 -63
  262. package/src/contexts/BreacrumbsContext.tsx +0 -45
  263. package/src/contexts/SideDialogsControllerContext.tsx +0 -5
  264. package/src/contexts/SidePanelControllerContext.tsx +0 -5
  265. package/src/data_export/export/BasicExportAction.tsx +0 -142
  266. package/src/data_export/export/ExportCollectionAction.tsx +0 -321
  267. package/src/data_export/export/export.ts +0 -295
  268. package/src/data_export/export/fetch_export_data.ts +0 -90
  269. package/src/data_export/export/index.ts +0 -4
  270. package/src/data_export/index.ts +0 -1
  271. package/src/data_import/components/DataNewPropertiesMapping.tsx +0 -261
  272. package/src/data_import/components/ImportFileUpload.tsx +0 -42
  273. package/src/data_import/components/ImportNewPropertyFieldPreview.tsx +0 -58
  274. package/src/data_import/components/ImportSaveInProgress.tsx +0 -121
  275. package/src/data_import/components/index.ts +0 -4
  276. package/src/data_import/hooks/index.ts +0 -1
  277. package/src/data_import/hooks/useImportConfig.tsx +0 -34
  278. package/src/data_import/import/ImportCollectionAction.tsx +0 -461
  279. package/src/data_import/import/index.ts +0 -1
  280. package/src/data_import/index.ts +0 -5
  281. package/src/data_import/types/column_mapping.ts +0 -32
  282. package/src/data_import/types/index.ts +0 -1
  283. package/src/data_import/utils/csv.ts +0 -153
  284. package/src/data_import/utils/data.ts +0 -181
  285. package/src/data_import/utils/file_headers.ts +0 -14
  286. package/src/data_import/utils/file_to_json.ts +0 -149
  287. package/src/data_import/utils/get_import_inference_type.ts +0 -33
  288. package/src/data_import/utils/get_properties_mapping.ts +0 -67
  289. package/src/data_import/utils/index.ts +0 -5
  290. package/src/data_import/utils/save_entities.ts +0 -138
  291. package/src/data_import/utils/transforms.ts +0 -70
  292. package/src/editor/components/SlashCommandMenu.tsx +0 -532
  293. package/src/editor/components/editor-bubble-item.tsx +0 -32
  294. package/src/editor/components/editor-bubble.tsx +0 -143
  295. package/src/editor/components/image-bubble.tsx +0 -183
  296. package/src/editor/components/index.ts +0 -14
  297. package/src/editor/components/table-bubble.tsx +0 -233
  298. package/src/editor/editor.tsx +0 -529
  299. package/src/editor/extensions/HighlightDecorationExtension.ts +0 -115
  300. package/src/editor/extensions/Image.ts +0 -159
  301. package/src/editor/extensions/TextLoadingDecorationExtension.tsx +0 -107
  302. package/src/editor/extensions/clipboard.ts +0 -72
  303. package/src/editor/extensions/custom-keymap.ts +0 -25
  304. package/src/editor/extensions/drag-and-drop.tsx +0 -508
  305. package/src/editor/hooks/useProseMirror.ts +0 -126
  306. package/src/editor/hooks/useProseMirrorContext.ts +0 -15
  307. package/src/editor/index.ts +0 -2
  308. package/src/editor/markdown-it-plugins.d.ts +0 -24
  309. package/src/editor/markdown.ts +0 -204
  310. package/src/editor/nodeViews/ImageComponent.tsx +0 -20
  311. package/src/editor/nodeViews/ReactNodeView.tsx +0 -89
  312. package/src/editor/nodeViews/TaskItemComponent.tsx +0 -30
  313. package/src/editor/nodeViews/index.ts +0 -35
  314. package/src/editor/plugins/index.ts +0 -58
  315. package/src/editor/plugins/inputrules.ts +0 -82
  316. package/src/editor/plugins/placeholderPlugin.ts +0 -55
  317. package/src/editor/plugins/slashCommandPlugin.ts +0 -65
  318. package/src/editor/schema.ts +0 -339
  319. package/src/editor/selectors/ai-selector.tsx +0 -110
  320. package/src/editor/selectors/color-selector.tsx +0 -199
  321. package/src/editor/selectors/link-selector.tsx +0 -122
  322. package/src/editor/selectors/node-selector.tsx +0 -159
  323. package/src/editor/selectors/text-buttons.tsx +0 -79
  324. package/src/editor/types.ts +0 -6
  325. package/src/editor/useProseMirror.ts +0 -125
  326. package/src/editor/utils/prosemirror-utils.ts +0 -113
  327. package/src/editor/utils/remove_classes.ts +0 -18
  328. package/src/editor/utils/useDebouncedCallback.ts +0 -25
  329. package/src/form/EntityForm.tsx +0 -761
  330. package/src/form/EntityFormActions.tsx +0 -160
  331. package/src/form/EntityFormBinding.tsx +0 -342
  332. package/src/form/PropertyFieldBinding.tsx +0 -348
  333. package/src/form/components/ErrorFocus.tsx +0 -44
  334. package/src/form/components/FieldBlock.tsx +0 -189
  335. package/src/form/components/FieldHelperText.tsx +0 -84
  336. package/src/form/components/FormRail.tsx +0 -153
  337. package/src/form/components/FormSections.tsx +0 -145
  338. package/src/form/components/LabelWithIcon.tsx +0 -43
  339. package/src/form/components/LabelWithIconAndTooltip.tsx +0 -28
  340. package/src/form/components/LocalChangesMenu.tsx +0 -147
  341. package/src/form/components/StorageItemPreview.tsx +0 -81
  342. package/src/form/components/StorageUploadProgress.tsx +0 -118
  343. package/src/form/components/index.tsx +0 -6
  344. package/src/form/components/useRailVisible.tsx +0 -47
  345. package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +0 -109
  346. package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -172
  347. package/src/form/field_bindings/BinaryFieldBinding.tsx +0 -177
  348. package/src/form/field_bindings/BlockFieldBinding.tsx +0 -282
  349. package/src/form/field_bindings/DateTimeFieldBinding.tsx +0 -78
  350. package/src/form/field_bindings/GeopointFieldBinding.tsx +0 -200
  351. package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -574
  352. package/src/form/field_bindings/MapFieldBinding.tsx +0 -120
  353. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +0 -259
  354. package/src/form/field_bindings/MultiSelectFieldBinding.tsx +0 -130
  355. package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +0 -168
  356. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +0 -94
  357. package/src/form/field_bindings/ReferenceFieldBinding.tsx +0 -146
  358. package/src/form/field_bindings/RelationFieldBinding.tsx +0 -202
  359. package/src/form/field_bindings/RepeatFieldBinding.tsx +0 -143
  360. package/src/form/field_bindings/SelectFieldBinding.tsx +0 -111
  361. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +0 -521
  362. package/src/form/field_bindings/SwitchFieldBinding.tsx +0 -70
  363. package/src/form/field_bindings/TextFieldBinding.tsx +0 -189
  364. package/src/form/field_bindings/UserSelectFieldBinding.tsx +0 -71
  365. package/src/form/field_bindings/VectorFieldBinding.tsx +0 -209
  366. package/src/form/form_utils.ts +0 -243
  367. package/src/form/index.tsx +0 -36
  368. package/src/form/useClearRestoreValue.tsx +0 -36
  369. package/src/form/useUndoableDiscard.ts +0 -39
  370. package/src/form/validation.ts +0 -583
  371. package/src/hooks/index.ts +0 -16
  372. package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +0 -22
  373. package/src/hooks/navigation/contexts/NavigationStateContext.tsx +0 -16
  374. package/src/hooks/navigation/contexts/UrlContext.tsx +0 -24
  375. package/src/hooks/navigation/contexts/index.ts +0 -14
  376. package/src/hooks/navigation/useAsyncResolver.ts +0 -100
  377. package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +0 -218
  378. package/src/hooks/navigation/useBuildNavigationStateController.tsx +0 -134
  379. package/src/hooks/navigation/useBuildUrlController.tsx +0 -66
  380. package/src/hooks/navigation/useNavigationResolution.ts +0 -271
  381. package/src/hooks/navigation/useResolvedCollections.ts +0 -102
  382. package/src/hooks/navigation/useResolvedViews.tsx +0 -139
  383. package/src/hooks/navigation/useTopLevelNavigation.ts +0 -273
  384. package/src/hooks/navigation/utils.ts +0 -200
  385. package/src/hooks/useAdminContext.tsx +0 -83
  386. package/src/hooks/useBreadcrumbsController.tsx +0 -17
  387. package/src/hooks/useBuildSideDialogsController.tsx +0 -145
  388. package/src/hooks/useBuildSidePanel.tsx +0 -315
  389. package/src/hooks/useChildViewSource.ts +0 -46
  390. package/src/hooks/useEntityDisplay.tsx +0 -262
  391. package/src/hooks/useEntityDisplayTitle.tsx +0 -49
  392. package/src/hooks/useHistory.ts +0 -178
  393. package/src/hooks/useRecordActions.tsx +0 -69
  394. package/src/hooks/useResolvedNavigationFrom.tsx +0 -167
  395. package/src/hooks/useResolvedUsers.ts +0 -148
  396. package/src/hooks/useSelectionDialog.tsx +0 -58
  397. package/src/hooks/useSideDialogsController.tsx +0 -25
  398. package/src/hooks/useSidePanel.tsx +0 -13
  399. package/src/index.ts +0 -124
  400. package/src/preview/PropertyPreview.tsx +0 -331
  401. package/src/preview/compact_text.ts +0 -68
  402. package/src/preview/components/ArrayEnumPreview.tsx +0 -39
  403. package/src/preview/components/BooleanPreview.tsx +0 -28
  404. package/src/preview/components/DatePreview.tsx +0 -106
  405. package/src/preview/components/EmptyValue.tsx +0 -10
  406. package/src/preview/components/EnumValuesChip.tsx +0 -39
  407. package/src/preview/components/ImagePreview.tsx +0 -144
  408. package/src/preview/components/InlineEntityListPreview.tsx +0 -42
  409. package/src/preview/components/ReferencePreview.tsx +0 -235
  410. package/src/preview/components/RelationPreview.tsx +0 -271
  411. package/src/preview/components/StorageThumbnail.tsx +0 -147
  412. package/src/preview/components/UrlComponentPreview.tsx +0 -116
  413. package/src/preview/components/UserPreview.tsx +0 -30
  414. package/src/preview/components/date_fns_locales.ts +0 -125
  415. package/src/preview/index.ts +0 -26
  416. package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -73
  417. package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +0 -67
  418. package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +0 -77
  419. package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -49
  420. package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -70
  421. package/src/preview/property_previews/ArrayOneOfPreview.tsx +0 -79
  422. package/src/preview/property_previews/ArrayPropertyEnumPreview.tsx +0 -35
  423. package/src/preview/property_previews/ArrayPropertyPreview.tsx +0 -79
  424. package/src/preview/property_previews/MapPropertyPreview.tsx +0 -198
  425. package/src/preview/property_previews/NumberPropertyPreview.tsx +0 -39
  426. package/src/preview/property_previews/SkeletonPropertyComponent.tsx +0 -276
  427. package/src/preview/property_previews/StringPropertyPreview.tsx +0 -70
  428. package/src/preview/util.ts +0 -39
  429. package/src/routes/CustomViewRoute.tsx +0 -33
  430. package/src/routes/RebaseRoute.tsx +0 -467
  431. package/src/routes/index.ts +0 -2
  432. package/src/types/components/EntityFormActionsProps.tsx +0 -20
  433. package/src/types/components/EntityFormProps.tsx +0 -119
  434. package/src/types/components/PropertyPreviewProps.tsx +0 -85
  435. package/src/types/components/formex.tsx +0 -2
  436. package/src/types/components/index.ts +0 -3
  437. package/src/types/fields.tsx +0 -340
  438. package/src/types/index.ts +0 -4
  439. package/src/util/dataControllerAdapter.ts +0 -88
  440. package/src/util/entity_actions.ts +0 -28
  441. package/src/util/index.ts +0 -5
  442. package/src/util/navigation_utils.ts +0 -89
  443. package/src/util/number_format.ts +0 -46
  444. package/src/util/previews.ts +0 -131
  445. package/src/util/propertyConfigMapper.ts +0 -134
  446. package/src/util/property_utils.tsx +0 -236
  447. package/src/util/resolutions.ts +0 -96
  448. package/src/util/view_constants.ts +0 -33
  449. package/src/util/view_mode.ts +0 -176
@@ -1,926 +0,0 @@
1
-
2
- import {
3
- ArrowRightToLineIcon,
4
- CheckIcon,
5
- ChevronDownIcon,
6
- Chip,
7
- CircularProgress,
8
- cls,
9
- defaultBorderMixin,
10
- fieldBackgroundDisabledMixin,
11
- fieldBackgroundHoverMixin,
12
- fieldBackgroundInvisibleMixin,
13
- fieldBackgroundMixin,
14
- focusedDisabled,
15
- IconButton,
16
- iconSize,
17
- PlusIcon,
18
- PopoverPrimitive,
19
- SearchIcon,
20
- Separator,
21
- Tooltip,
22
- usePortalContainer,
23
- XIcon
24
- } from "@rebasepro/ui";
25
- import * as React from "react";
26
- import { useCallback, useEffect, useRef, useState } from "react";
27
- import { Command as CommandPrimitive } from "cmdk";
28
- import { Entity, EntityRelation, FilterValues, Relation, getCollectionDataPath } from "@rebasepro/types";
29
- import { EntityPreviewBindingData } from "./EntityPreviewBinding";
30
- import { getTitlePropertyKey, useData, usePermissions, useRelationSelector, useTranslation } from "@rebasepro/app";
31
- import { useSidePanel } from "../hooks/useSidePanel";
32
- import { normalizeToEntityRelation } from "@rebasepro/common";
33
-
34
- /** Whether an authored relation yields many rows. Derived from its kind. */
35
- function relationCardinality(relation: { kind?: string; cardinality?: string } | undefined): "one" | "many" | undefined {
36
- if (!relation) return undefined;
37
- if (relation.kind === "via") return relation.cardinality as "one" | "many" | undefined;
38
- if (relation.kind === "hasMany" || relation.kind === "manyToMany") return "many";
39
- if (relation.kind === "belongsTo" || relation.kind === "hasOne") return "one";
40
- return relation.cardinality as "one" | "many" | undefined;
41
- }
42
-
43
-
44
- export interface RelationItem {
45
- id: string | number;
46
- label: string;
47
- description?: string;
48
- data?: Entity;
49
- relation: EntityRelation;
50
- }
51
-
52
- export interface RelationSelectorProps {
53
- className?: string;
54
- name?: string;
55
- id?: string;
56
- value?: EntityRelation | EntityRelation[] | null;
57
- /** Callback returning selected EntityRelation(s) */
58
-
59
- onValueChange?: (updatedValue: EntityRelation | EntityRelation[] | undefined) => void;
60
- placeholder?: React.ReactNode;
61
- /**
62
- * `large` is the form's size and is on the shared 28/32/40/48 control
63
- * scale, so a relation picker lines up with the text field beside it.
64
- *
65
- * `small` and `medium` predate that scale (42 and 56) and are kept as they
66
- * are because the table cells and the filter row are built around them.
67
- */
68
- size?: "small" | "medium" | "large";
69
- useChips?: boolean;
70
- disabled?: boolean;
71
- invisible?: boolean;
72
-
73
- relation: Relation;
74
- /**
75
- * Whether the selector picks many rows or one.
76
- *
77
- * Defaults to what the relation implies — a `hasMany` or `manyToMany`
78
- * picks many — which is right wherever the selector *edits* the relation,
79
- * because there the cardinality is the shape of the stored value.
80
- *
81
- * A filter is the case where it is not. `tags == <one tag>` asks a single
82
- * question about a to-many link, and the operator is what decides how many
83
- * values it takes. Deriving multiplicity from the relation there meant a
84
- * to-many relation could only ever emit a list, so the filter field had to
85
- * hide every single-value operator to avoid handing `==` an array.
86
- */
87
- multiple?: boolean;
88
- fixedFilter?: FilterValues<string>;
89
- pageSize?: number;
90
- emptyPlaceholder?: string;
91
- searchPlaceholder?: string;
92
- /** Shown in the open list when a search matched nothing. */
93
- noResultsText?: string;
94
- /**
95
- * Shown on the closed trigger when nothing is selected. Says only that the
96
- * field is empty — see the note by `resolvedPlaceholder` for why it cannot
97
- * say whether there is anything to pick.
98
- */
99
- emptyText?: string;
100
- /**
101
- * Shown in the open list when the target collection turned out to hold no
102
- * rows at all, as opposed to a search simply not matching. Defaults to a
103
- * sentence naming the target collection.
104
- */
105
- emptyCollectionText?: string;
106
- loadingText?: string;
107
- /**
108
- * Whether the open list offers to create a row in the target collection.
109
- * Defaults to `true`. The offer is shown only when the user could actually
110
- * insert into that collection — the same permission check the selection
111
- * dialog's "Add" button makes — so turning this off is for the cases where
112
- * creating is possible but not wanted, like a filter.
113
- */
114
- allowCreate?: boolean;
115
- }
116
-
117
- export const RelationSelector = React.forwardRef<
118
- HTMLButtonElement,
119
- RelationSelectorProps
120
- >(
121
- (
122
- {
123
- value,
124
- size = "medium",
125
- onValueChange,
126
- invisible,
127
- disabled,
128
- placeholder,
129
- useChips = true,
130
- className,
131
- relation,
132
- multiple: multipleOverride,
133
- fixedFilter,
134
- pageSize,
135
- emptyPlaceholder,
136
- searchPlaceholder = "Search...",
137
- noResultsText = "No matches.",
138
- emptyText = "Select…",
139
- emptyCollectionText,
140
- loadingText = "Loading...",
141
- allowCreate = true
142
- },
143
- ref
144
- ) => {
145
-
146
- const collection = relation.target();
147
- const dataPath = getCollectionDataPath(collection);
148
- const dataClient = useData();
149
- const sidePanelController = useSidePanel();
150
- const { canCreate } = usePermissions();
151
- const { t } = useTranslation();
152
- const contextPortalContainer = usePortalContainer();
153
- const multiple = multipleOverride ?? relationCardinality(relation) === "many";
154
-
155
- const [isPopoverOpen, setIsPopoverOpen] = useState(false);
156
- const isPopoverOpenRef = useRef(false);
157
- // The list is only worth fetching once the picker has been opened. A
158
- // table cell mounts one of these per row, and the selected value is
159
- // resolved by its own effect below, not from the list. Latches on the
160
- // first open so a closed popover keeps its results.
161
- const [listRequested, setListRequested] = useState(false);
162
- const [selectedItems, setSelectedItems] = useState<RelationItem[]>([]);
163
- const [isLoadingSelectedItems, setIsLoadingSelectedItems] = useState(false);
164
- const [searchString, setSearchString] = useState<string>("");
165
- // Track IDs that were set via local user interaction (onItemClick / handleClear / handleRemoveItem).
166
- // When an incoming value change matches these IDs exactly, we skip async re-resolution.
167
- const localSelectionIdsRef = useRef<string | null>(null);
168
- // Entity of selected IDs captured when the popover opens.
169
- // Used to sort the dropdown list so selected items appear at the top.
170
- // Stays stable for the entire popover session so items don't jump around.
171
- const pinnedIdsRef = useRef<Set<string> | null>(null);
172
-
173
- const {
174
- items: availableItems,
175
- isLoading,
176
- hasMore,
177
- search,
178
- loadMore,
179
- entityToRelationItem
180
- } = useRelationSelector({
181
- path: dataPath,
182
- collection,
183
- fixedFilter,
184
- pageSize,
185
- enabled: listRequested
186
- });
187
-
188
- const scrollContainerRef = useRef<HTMLDivElement>(null);
189
- const sentinelRef = useRef<HTMLDivElement>(null);
190
- const observerRef = useRef<IntersectionObserver | null>(null);
191
- const localTriggerRef = useRef<HTMLButtonElement | null>(null);
192
-
193
- const handleButtonRef = useCallback((node: HTMLButtonElement | null) => {
194
- localTriggerRef.current = node;
195
- if (typeof ref === "function") {
196
- ref(node);
197
- } else if (ref) {
198
- ref.current = node;
199
- }
200
- }, [ref]);
201
- const contentRef = useRef<HTMLDivElement | null>(null);
202
- const searchInputRef = useRef<HTMLInputElement | null>(null);
203
-
204
- // Keep stable refs for dependencies used in the value-resolution effect
205
- // so the effect only re-fires when the actual `value` identity changes.
206
- const dataClientRef = useRef(dataClient);
207
- dataClientRef.current = dataClient;
208
- const entityToRelationItemRef = useRef(entityToRelationItem);
209
- entityToRelationItemRef.current = entityToRelationItem;
210
- const collectionSlugRef = useRef(collection.slug);
211
- collectionSlugRef.current = collection.slug;
212
-
213
- // Stable ref to track which IDs we've already resolved
214
- const resolvedIdsRef = useRef<string>("");
215
- // Track whether we have resolved items (used for skip guard without adding to deps)
216
- const hasResolvedItemsRef = useRef(false);
217
-
218
- const selectedItemsRef = useRef(selectedItems);
219
- selectedItemsRef.current = selectedItems;
220
-
221
- // Normalize incoming values — plain { __type: "relation" } objects
222
- // from the server are converted to proper EntityRelation instances.
223
- const rawArray = !value ? [] : Array.isArray(value) ? value : [value];
224
- const relationsArray = rawArray.map(rel => {
225
- if (typeof rel === "string" || typeof rel === "number") return rel;
226
- return normalizeToEntityRelation(rel) ?? rel;
227
- });
228
-
229
- // Build a fingerprint of the incoming value's IDs
230
- const incomingIds = relationsArray
231
- .map(rel => {
232
- const isPrimitive = typeof rel === "string" || typeof rel === "number";
233
- return String(isPrimitive ? rel : (rel as EntityRelation).id);
234
- })
235
- .sort()
236
- .join(",");
237
-
238
- // CheckIcon if every relation already has embedded data
239
- const allHaveData = relationsArray.length > 0 && relationsArray.every(rel => {
240
- if (typeof rel === "string" || typeof rel === "number") return false;
241
- return !!(rel as EntityRelation)?.data;
242
- });
243
-
244
- const relationsArrayRef = useRef(relationsArray);
245
- relationsArrayRef.current = relationsArray;
246
-
247
- useEffect(() => {
248
- let active = true;
249
- const currentRelationsArray = relationsArrayRef.current;
250
-
251
- // If the IDs haven't changed and we already resolved them, skip entirely
252
- if (incomingIds === resolvedIdsRef.current && (allHaveData || hasResolvedItemsRef.current)) {
253
- return;
254
- }
255
-
256
- // FAST PATH: If the incoming IDs match what we just set locally via user interaction,
257
- // our selectedItems already have the correct data — no re-resolution needed.
258
- if (localSelectionIdsRef.current !== null && incomingIds === localSelectionIdsRef.current) {
259
- resolvedIdsRef.current = incomingIds;
260
- hasResolvedItemsRef.current = true;
261
- // Do NOT call setSelectedItems or setIsLoadingSelectedItems — they are already correct.
262
- return;
263
- }
264
-
265
- const currentSelected = selectedItemsRef.current;
266
- const currentSelectedMap = new Map(currentSelected.map(item => [String(item.id), item]));
267
-
268
- let allCovered = true;
269
- const newResolvedItems: RelationItem[] = [];
270
- const toRelationItem = entityToRelationItemRef.current;
271
- const slug = collectionSlugRef.current;
272
-
273
- for (const rel of currentRelationsArray) {
274
- const isPrimitive = typeof rel === "string" || typeof rel === "number";
275
- const relId = String(isPrimitive ? rel : (rel as EntityRelation).id);
276
- const hasEmbeddedData = !isPrimitive && !!(rel as EntityRelation).data;
277
-
278
- if (hasEmbeddedData) {
279
- const r = rel as EntityRelation;
280
- // admin wire format embeds relation data as { id, path, values }
281
- newResolvedItems.push(toRelationItem(r.data as unknown as Entity, r));
282
- } else if (currentSelectedMap.has(relId)) {
283
- // We already have it in currentSelected
284
- const existingItem = currentSelectedMap.get(relId)!;
285
- newResolvedItems.push(existingItem);
286
- } else {
287
- allCovered = false;
288
- break;
289
- }
290
- }
291
-
292
- // FAST PATH: We have all necessary data embedded or in local cache
293
- if (allCovered) {
294
- setSelectedItems(newResolvedItems);
295
- hasResolvedItemsRef.current = true;
296
- resolvedIdsRef.current = incomingIds;
297
- setIsLoadingSelectedItems(false);
298
- return;
299
- }
300
-
301
- // SLOW PATH: fetch missing data — but don't show loading if popover is open
302
- // (user is actively interacting; showing a spinner is disruptive)
303
- if (!isPopoverOpenRef.current) {
304
- setIsLoadingSelectedItems(true);
305
- }
306
- const client = dataClientRef.current;
307
-
308
- Promise.all(
309
- currentRelationsArray.map(async (rel) => {
310
- const isPrimitive = typeof rel === "string" || typeof rel === "number";
311
- const relId = isPrimitive ? rel : (rel as EntityRelation).id;
312
- const path = isPrimitive ? slug : (rel as EntityRelation).path;
313
- const relation = isPrimitive ? new EntityRelation(relId, path) : rel as EntityRelation;
314
-
315
- if (!isPrimitive && (rel as EntityRelation).data) {
316
- // admin wire format embeds relation data as { id, path, values }
317
- return toRelationItem((rel as EntityRelation).data as unknown as Entity, relation);
318
- }
319
- if (currentSelectedMap.has(String(relId))) {
320
- return currentSelectedMap.get(String(relId))!;
321
- }
322
-
323
- try {
324
- const entity = await client.collection(path).findById(relId);
325
- if (entity) return toRelationItem(entity, relation);
326
- } catch (e) {
327
- console.warn("RelationSelector: could not fetch entity for relation", rel, e);
328
- }
329
- return { id: relId,
330
- label: String(relId),
331
- relation } as RelationItem;
332
- })
333
- ).then(resolved => {
334
- if (active) {
335
- setSelectedItems(resolved);
336
- setIsLoadingSelectedItems(false);
337
- hasResolvedItemsRef.current = true;
338
- resolvedIdsRef.current = incomingIds;
339
- }
340
- });
341
-
342
- return () => {
343
- active = false;
344
- };
345
- }, [incomingIds, allHaveData]);
346
-
347
- const sentinelCallbackRef = useCallback((node: HTMLDivElement | null) => {
348
- if (observerRef.current) {
349
- observerRef.current.disconnect();
350
- observerRef.current = null;
351
- }
352
- if (sentinelRef.current !== node) {
353
- (sentinelRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
354
- }
355
- if (!node || !hasMore || isLoading || !loadMore) return;
356
- const observer = new IntersectionObserver(
357
- (entries) => {
358
- const entry = entries[0];
359
- if (entry.isIntersecting && hasMore && !isLoading) loadMore();
360
- },
361
- {
362
- root: scrollContainerRef.current,
363
- rootMargin: "20px",
364
- threshold: 0
365
- }
366
- );
367
- observer.observe(node);
368
- observerRef.current = observer;
369
- }, [hasMore, isLoading, loadMore]);
370
-
371
- useEffect(() => () => {
372
- if (observerRef.current) observerRef.current.disconnect();
373
- }, []);
374
-
375
- // Enhanced scroll event listener specifically for dialog contexts
376
- useEffect(() => {
377
- const scrollContainer = scrollContainerRef.current;
378
- if (!scrollContainer || !hasMore || isLoading || !isPopoverOpen) return;
379
-
380
- const handleScroll = () => {
381
- const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
382
- const isNearBottom = scrollTop + clientHeight >= scrollHeight - 100;
383
-
384
- if (isNearBottom && hasMore && !isLoading) {
385
- loadMore();
386
- }
387
- };
388
-
389
- // Add scroll listener directly to the container
390
- scrollContainer.addEventListener("scroll", handleScroll, { passive: true });
391
-
392
- return () => {
393
- scrollContainer.removeEventListener("scroll", handleScroll);
394
- };
395
- }, [hasMore, isLoading, loadMore, isPopoverOpen]);
396
-
397
- const handleSearchChange = useCallback((newSearchString: string) => {
398
- setSearchString(newSearchString);
399
- search(newSearchString);
400
- }, [search]);
401
-
402
- const emitValueChange = useCallback((selected: RelationItem[]) => {
403
- if (multiple) onValueChange?.(selected.length ? selected.map(i => i.relation) : undefined);
404
- else onValueChange?.(selected[0]?.relation);
405
- }, [onValueChange, multiple]);
406
-
407
- // The picker can hand its selection over long after the render that
408
- // built the callback — a row created in the side panel arrives whenever
409
- // the person saves it — so the emitter is read from a ref rather than
410
- // captured.
411
- const emitValueChangeRef = useRef(emitValueChange);
412
- emitValueChangeRef.current = emitValueChange;
413
-
414
- // Helper: compute the "IDs fingerprint" for a set of RelationItems
415
- // so we can mark it in localSelectionIdsRef and skip re-resolution.
416
- const computeSelectionFingerprint = useCallback((items: RelationItem[]): string => {
417
- return items.map(i => String(i.id)).sort().join(",");
418
- }, []);
419
-
420
- const onItemClick = useCallback((item: RelationItem) => {
421
- let newSelected: RelationItem[];
422
- if (multiple) {
423
- const isSelected = selectedItems.some(v => String(v.id) === String(item.id));
424
- newSelected = isSelected
425
- ? selectedItems.filter(v => String(v.id) !== String(item.id))
426
- : [...selectedItems, item];
427
- } else {
428
- newSelected = [item];
429
- setIsPopoverOpen(false);
430
- isPopoverOpenRef.current = false;
431
- pinnedIdsRef.current = null;
432
- }
433
- setSelectedItems(newSelected);
434
- // Mark this fingerprint so the resolution effect skips async work
435
- // when the parent echoes the same IDs back via props.
436
- localSelectionIdsRef.current = computeSelectionFingerprint(newSelected);
437
- emitValueChange(newSelected);
438
- }, [multiple, selectedItems, emitValueChange, computeSelectionFingerprint]);
439
-
440
- const handleClear = useCallback(() => {
441
- setSelectedItems([]);
442
- localSelectionIdsRef.current = "";
443
- onValueChange?.(undefined);
444
- }, [onValueChange]);
445
-
446
- const handleRemoveItem = useCallback((itemToRemove: RelationItem) => {
447
- const newSelected = selectedItems.filter(v => String(v.id) !== String(itemToRemove.id));
448
- setSelectedItems(newSelected);
449
- localSelectionIdsRef.current = computeSelectionFingerprint(newSelected);
450
- emitValueChange(newSelected);
451
- }, [selectedItems, emitValueChange, computeSelectionFingerprint]);
452
-
453
- const handleRootOpenChange = useCallback((next: boolean) => {
454
- if (disabled) return;
455
- // We control open manually; only allow opening attempts from Radix (e.g. trigger press)
456
- if (next) {
457
- // Capture current selection so we can pin those items to the top of the list
458
- pinnedIdsRef.current = new Set(selectedItems.map(i => String(i.id)));
459
- setListRequested(true);
460
- setIsPopoverOpen(true);
461
- isPopoverOpenRef.current = true;
462
- }
463
- // Ignore close attempts here; outside click/Escape handled manually; single select closes explicitly on selection.
464
- }, [disabled, selectedItems]);
465
-
466
- // Outside click + Escape handling (simple and reliable)
467
- useEffect(() => {
468
- if (!isPopoverOpen) return;
469
-
470
- function handlePointerDown(ev: MouseEvent) {
471
- const target = ev.target as Node;
472
- const triggerEl = localTriggerRef.current;
473
- const contentEl = contentRef.current;
474
- if (triggerEl?.contains(target)) return;
475
- if (contentEl?.contains(target)) return;
476
- // Outside
477
- setIsPopoverOpen(false);
478
- isPopoverOpenRef.current = false;
479
- pinnedIdsRef.current = null;
480
- }
481
-
482
- function handleKey(ev: KeyboardEvent) {
483
- if (ev.key === "Escape") {
484
- setIsPopoverOpen(false);
485
- isPopoverOpenRef.current = false;
486
- pinnedIdsRef.current = null;
487
- }
488
- }
489
-
490
- document.addEventListener("mousedown", handlePointerDown, true);
491
- document.addEventListener("keydown", handleKey, true);
492
- return () => {
493
- document.removeEventListener("mousedown", handlePointerDown, true);
494
- document.removeEventListener("keydown", handleKey, true);
495
- };
496
- }, [isPopoverOpen]);
497
-
498
- const closePopover = useCallback(() => {
499
- setIsPopoverOpen(false);
500
- isPopoverOpenRef.current = false;
501
- pinnedIdsRef.current = null;
502
- }, []);
503
-
504
- // Pointing at a row that does not exist yet.
505
- //
506
- // Until now the only way out of that was to abandon the form, go to the
507
- // other collection, create the row, come back and start again — and on
508
- // a new record that meant losing everything typed so far. The side
509
- // panel already stacks (the selection dialog creates from inside itself
510
- // this way), so the picker can open a create form over the form it
511
- // belongs to and select what comes back.
512
- const canCreateTarget = allowCreate && canCreate(collection, dataPath);
513
-
514
- // A search that matched nothing is the name the person was looking for,
515
- // so it seeds the new row. Only when the title lands on a plain string
516
- // property: putting free text into an enum, a relation or a computed
517
- // property would be worse than not prefilling at all.
518
- const buildDefaultValues = useCallback((searchText: string): Record<string, unknown> | undefined => {
519
- const trimmed = searchText.trim();
520
- if (!trimmed) return undefined;
521
- const titleKey = getTitlePropertyKey(collection);
522
- if (!titleKey || titleKey.includes(".")) return undefined;
523
- const titleProperty = collection.properties?.[titleKey];
524
- // A property builder is a function, and it decides its own value.
525
- if (!titleProperty || typeof titleProperty === "function") return undefined;
526
- if (titleProperty.type !== "string" || titleProperty.enum) return undefined;
527
- return { [titleKey]: trimmed };
528
- }, [collection]);
529
-
530
- const handleCreateNew = useCallback(() => {
531
- const defaultValues = buildDefaultValues(searchString);
532
- closePopover();
533
- sidePanelController.open({
534
- path: dataPath,
535
- collection,
536
- // No URL. This panel is a detour inside the form you are
537
- // filling in, not somewhere you navigated to, and its address
538
- // would restore nothing anyway — a record that does not exist
539
- // yet has none.
540
- //
541
- // Pushing one made closing it a *pathname* change, which is
542
- // exactly what the unsaved-changes blocker watches
543
- // (`useUnsavedChangesDialog`). So saving the new row raced the
544
- // panel clearing its own dirty flag, and often enough lost:
545
- // "There are unsaved changes" over a record that had just been
546
- // saved, with the URL stranded on the target collection. Worst
547
- // of all in the case this feature exists for — the outer form
548
- // has content in it, which is the whole reason not to leave.
549
- updateUrl: false,
550
- // Saving is the whole point of the detour; the panel goes away
551
- // and the picker is left holding the row it made.
552
- closeOnSave: true,
553
- defaultValues,
554
- onUpdate: ({ entity }) => {
555
- if (!entity) return;
556
- const item = entityToRelationItemRef.current(entity, new EntityRelation(entity.id, dataPath));
557
- const current = selectedItemsRef.current;
558
- const newSelected = multiple
559
- ? [...current.filter(i => String(i.id) !== String(item.id)), item]
560
- : [item];
561
- setSelectedItems(newSelected);
562
- // Same bookkeeping `onItemClick` does: mark the fingerprint
563
- // so the resolution effect does not re-fetch the row we
564
- // already hold when the form echoes the value back.
565
- localSelectionIdsRef.current = newSelected.map(i => String(i.id)).sort().join(",");
566
- emitValueChangeRef.current(newSelected);
567
- }
568
- });
569
- }, [buildDefaultValues, searchString, closePopover, sidePanelController, dataPath, collection, multiple]);
570
-
571
- // Text, not `EmptyValue`. That component draws a small grey rounded bar
572
- // with no text in it, which is the same shape the `Skeleton` component
573
- // uses while data is in flight — so a relation you simply had not filled
574
- // in was indistinguishable from one that was still loading, and an empty
575
- // Author or Tags field read as permanently stuck. It also left the trigger
576
- // button with no accessible name, since the bar contains no text at all.
577
- //
578
- // This says "not filled in" rather than "nothing to choose from": which of
579
- // those it is cannot be known here. The list is deliberately not fetched
580
- // until the picker is first opened (`enabled: listRequested`, guarded by
581
- // relation_cell_picker_gating.test.tsx, because a table renders one of
582
- // these per row), so before that `availableItems` is empty because nobody
583
- // asked, not because the target collection is. The genuine "nothing to
584
- // pick yet" state is reported inside the popover below, where the fetch
585
- // has actually happened.
586
- const resolvedPlaceholder = placeholder || emptyPlaceholder || emptyText;
587
-
588
- // Whatever dialog, side dialog or sheet we are inside offers as its
589
- // portal host, falling back to `document.body`. This is not cosmetic:
590
- // those are modal, and a modal locks scrolling everywhere outside its
591
- // own content — a list portaled to the body opens and then refuses to
592
- // scroll, because every wheel event over it is cancelled.
593
- const portalContainer = (contextPortalContainer ?? (typeof document !== "undefined" ? document.body : undefined)) ?? undefined;
594
-
595
- return (
596
- <>
597
- <PopoverPrimitive.Root open={isPopoverOpen} onOpenChange={handleRootOpenChange} modal={false}>
598
- <PopoverPrimitive.Trigger asChild>
599
- <button
600
- ref={handleButtonRef}
601
- type="button"
602
- aria-haspopup="listbox"
603
- aria-expanded={isPopoverOpen}
604
- data-relation-selector-trigger
605
- disabled={disabled}
606
- onClick={() => {
607
- if (disabled) return;
608
- // Unconditional: the toggle can only close a
609
- // popover that was opened, which requested the
610
- // list already.
611
- setListRequested(true);
612
- setIsPopoverOpen(o => {
613
- const next = !o;
614
- isPopoverOpenRef.current = next;
615
- if (next) {
616
- pinnedIdsRef.current = new Set(selectedItems.map(i => String(i.id)));
617
- } else {
618
- pinnedIdsRef.current = null;
619
- }
620
- return next;
621
- });
622
- }}
623
- className={cls(
624
- {
625
- "min-h-[42px] py-1 px-2": size === "small",
626
- "min-h-[56px] py-2 px-4": size === "medium",
627
- "min-h-[48px] py-1 px-4": size === "large"
628
- },
629
- // `rounded-lg` like every other field box —
630
- // `rounded-md` made this the one control in a
631
- // row with a different corner.
632
- "w-full select-none rounded-lg text-sm relative flex items-center",
633
- invisible ? fieldBackgroundInvisibleMixin : fieldBackgroundMixin,
634
- disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
635
- className
636
- )}
637
- >
638
- <div className="flex justify-between items-center w-full">
639
- {isLoadingSelectedItems ? (
640
- <div className="flex items-center gap-2">
641
- <CircularProgress size="smallest"/>
642
- <span className="text-sm text-text-secondary dark:text-text-secondary-dark">{loadingText}</span>
643
- </div>
644
- ) : selectedItems.length > 0 ? (
645
- <div
646
- className="flex flex-wrap items-center gap-1.5 text-start flex-1 min-w-0 mr-2">
647
- {selectedItems.map((item) => {
648
- if (!useChips || !multiple) {
649
-
650
- return (
651
- <div key={String(item.id)}
652
- className="flex flex-row items-center gap-1 truncate">
653
- {item.data ? (
654
- <EntityPreviewBindingData size={"medium"}
655
- entity={item.data}
656
- includeEntityLink={false}
657
- includeId={false}
658
- onSidePanelClick={closePopover}
659
- />
660
- ) : (
661
- <span className="text-sm truncate">{item.label}</span>
662
- )}
663
- </div>
664
- );
665
- }
666
- return (
667
- <Chip
668
- size={"small"}
669
- key={String(item.id)}
670
- className={cls("flex flex-row items-center gap-1 truncate")}
671
- >
672
- {item.data ? (
673
- <EntityPreviewBindingData size={"smallest"} entity={item.data}
674
- includeEntityLink={false}
675
- includeId={false}/>
676
- ) : (
677
- <span className="text-sm truncate">{item.label}</span>
678
- )}
679
- <XIcon
680
- size={iconSize.smallest}
681
- onClick={(event) => {
682
- event.stopPropagation();
683
- handleRemoveItem(item);
684
- }}
685
- />
686
- </Chip>
687
- );
688
- })}
689
- </div>
690
- ) :
691
- (
692
- <span className="text-sm text-text-secondary dark:text-text-secondary-dark">
693
- {resolvedPlaceholder}
694
- </span>
695
- )}
696
-
697
- <div className="flex items-center flex-shrink-0">
698
- {!multiple && selectedItems.length === 1 && selectedItems[0]?.data && (
699
- <Tooltip title={`Open ${selectedItems[0].label}`}>
700
- <IconButton
701
- component={"div"}
702
- size={"small"}
703
- color={"inherit"}
704
- className="opacity-60 hover:opacity-100"
705
- onClick={(e) => {
706
- e.stopPropagation();
707
- e.preventDefault();
708
- const entity = selectedItems[0].data!;
709
- setIsPopoverOpen(false);
710
- sidePanelController.open({
711
- entityId: entity.id,
712
- path: entity.path,
713
- collection,
714
- updateUrl: true
715
- });
716
- }}>
717
- <ArrowRightToLineIcon size={iconSize.small}/>
718
- </IconButton>
719
- </Tooltip>
720
- )}
721
- <ChevronDownIcon
722
- size={size === "small" ? iconSize.small : iconSize.medium}
723
- className={cls("transition", isPopoverOpen ? "rotate-180" : "")}
724
- />
725
- </div>
726
- </div>
727
-
728
- </button>
729
- </PopoverPrimitive.Trigger>
730
- <PopoverPrimitive.Portal container={portalContainer}>
731
- <PopoverPrimitive.Content
732
- ref={contentRef}
733
- data-relation-selector-content
734
- className={cls("z-50 overflow-hidden border bg-white dark:bg-surface-800 rounded-lg min-w-72", defaultBorderMixin)}
735
- align="start"
736
- sideOffset={8}
737
- side="bottom"
738
- avoidCollisions={true}
739
- collisionPadding={16}
740
- // Allow default auto focus (we manually refocus anyway)
741
- onOpenAutoFocus={(_e) => { /* leave default or custom manual focus */
742
- }}
743
- onCloseAutoFocus={(e) => {
744
- e.preventDefault();
745
- }}
746
- style={{ width: "var(--radix-popover-trigger-width)" }}
747
- >
748
- <CommandPrimitive shouldFilter={false}>
749
- <div className="flex flex-row items-center">
750
- <div className="relative flex-1">
751
- <SearchIcon
752
- className="absolute left-3 top-1/2 transform -translate-y-1/2 text-text-secondary dark:text-text-secondary-dark"
753
- size={iconSize.smallest}/>
754
- <CommandPrimitive.Input
755
- ref={searchInputRef}
756
- className={cls(
757
- focusedDisabled,
758
- "bg-transparent outline-hidden flex-1 h-full w-full pl-10 pr-4 py-3 text-text-primary dark:text-text-primary-dark placeholder:text-text-secondary dark:placeholder:text-text-secondary-dark"
759
- )}
760
- placeholder={searchPlaceholder}
761
- value={searchString}
762
- onValueChange={handleSearchChange}
763
- />
764
- </div>
765
- {isLoading && (
766
- <div className="flex items-center justify-center px-3">
767
- <CircularProgress size="smallest"/>
768
- </div>
769
- )}
770
- {selectedItems.length > 0 && (
771
- <div
772
- onClick={handleClear}
773
- className="text-sm justify-center cursor-pointer py-3 px-4 text-text-secondary dark:text-text-secondary-dark hover:text-text-primary dark:hover:text-text-primary-dark"
774
- >
775
- Clear
776
- </div>
777
- )}
778
- </div>
779
- <Separator orientation="horizontal" className="my-0"/>
780
- <CommandPrimitive.List
781
- ref={scrollContainerRef}
782
- style={{
783
- maxHeight: "40vh",
784
- overflowY: "auto"
785
- }}
786
- >
787
- {isLoading && availableItems.length === 0 && (
788
- <div className="flex items-center justify-center py-6">
789
- <CircularProgress size="small"/>
790
- <span
791
- className="ml-2 text-sm text-text-secondary dark:text-text-secondary-dark">{loadingText}</span>
792
- </div>
793
- )}
794
- {/* Two different nothings, which used to share one
795
- message ("No relations found.") even though the
796
- fix for each is the opposite: clear the search, or
797
- go and create the first row. With the fetch done,
798
- the search string is what tells them apart. */}
799
- {!isLoading && availableItems.length === 0 && (
800
- <CommandPrimitive.Empty
801
- className="px-4 py-6 text-center text-text-secondary dark:text-text-secondary-dark">
802
- {searchString
803
- ? noResultsText
804
- : (emptyCollectionText ?? `No ${collection.name ?? "entries"} yet.`)}
805
- </CommandPrimitive.Empty>
806
- )}
807
- <CommandPrimitive.Group>
808
- {(() => {
809
- // Sort items so that initially-selected (pinned) items appear first.
810
- // We use the entity taken when the popover opened so items don't
811
- // jump around as the user checks/unchecks.
812
- const pinned = pinnedIdsRef.current;
813
- const sortedItems = pinned && pinned.size > 0
814
- ? [...availableItems].sort((a, b) => {
815
- const aP = pinned.has(String(a.id)) ? 0 : 1;
816
- const bP = pinned.has(String(b.id)) ? 0 : 1;
817
- return aP - bP;
818
- })
819
- : availableItems;
820
- return sortedItems;
821
- })().map((item) => {
822
- const isSelected = selectedItems.some(v => String(v.id) === String(item.id));
823
- return (
824
- <CommandPrimitive.Item
825
- key={String(item.id)}
826
- value={String(item.id)}
827
- onMouseDown={(e) => {
828
- e.preventDefault();
829
- e.stopPropagation();
830
- }}
831
- onSelect={() => onItemClick(item)}
832
- className={cls(
833
- "flex flex-row items-center gap-1.5 m-1 p-1 rounded-xs cursor-pointer ring-offset-transparent",
834
- isSelected && "bg-surface-accent-200 dark:bg-surface-accent-950",
835
- "aria-selected:outline-hidden aria-selected:ring-2 aria-selected:ring-primary/75 aria-selected:ring-offset-2 aria-selected:bg-surface-accent-100 dark:aria-selected:bg-surface-accent-900"
836
- )}
837
- >
838
- {multiple && (<InnerCheckBox checked={isSelected}/>)}
839
- {item.data ? (
840
- <div
841
- className="flex flex-row items-center gap-2 min-w-0 w-full">
842
- <EntityPreviewBindingData
843
- size={multiple ? "smallest" : "medium"}
844
- entity={item.data}
845
- includeId={false}
846
- includeEntityLink={true}
847
- />
848
- </div>
849
- ) : (
850
- <div>
851
- <div
852
- className="text-sm font-medium text-text-primary dark:text-text-primary-dark">{item.label}</div>
853
- {item.description && (
854
- <div
855
- className="text-xs text-text-secondary dark:text-text-secondary-dark">{item.description}</div>
856
- )}
857
- </div>
858
- )}
859
- </CommandPrimitive.Item>
860
- );
861
- })}
862
- {availableItems.length > 0 && hasMore && (
863
- <div ref={sentinelCallbackRef} className="h-1 w-full"
864
- style={{ visibility: "hidden" }}/>
865
- )}
866
- {isLoading && availableItems.length > 0 && (
867
- <div className="flex items-center justify-center py-4">
868
- <CircularProgress size="smallest"/>
869
- <span
870
- className="ml-2 text-xs text-text-secondary dark:text-text-secondary-dark">Loading...</span>
871
- </div>
872
- )}
873
- </CommandPrimitive.Group>
874
- </CommandPrimitive.List>
875
- {canCreateTarget && <>
876
- <Separator orientation="horizontal" className="my-0"/>
877
- {/* Outside the scrolling list on purpose: it
878
- is most wanted exactly when the list is
879
- long and nothing in it matched. */}
880
- <button
881
- type="button"
882
- data-relation-selector-create
883
- onMouseDown={(e) => {
884
- e.preventDefault();
885
- e.stopPropagation();
886
- }}
887
- onClick={handleCreateNew}
888
- className={cls(
889
- "w-full flex flex-row items-center gap-2 px-4 py-3 text-sm text-left",
890
- "text-primary hover:bg-surface-accent-50 dark:hover:bg-surface-800"
891
- )}>
892
- <PlusIcon size={iconSize.smallest}/>
893
- <span className="truncate">
894
- {searchString.trim()
895
- ? t("add_named", { name: searchString.trim() })
896
- : t("add_specific", { name: collection.singularName ?? collection.name })}
897
- </span>
898
- </button>
899
- </>}
900
- </CommandPrimitive>
901
- </PopoverPrimitive.Content>
902
- </PopoverPrimitive.Portal>
903
- </PopoverPrimitive.Root>
904
- </>
905
- );
906
- }
907
- );
908
-
909
- RelationSelector.displayName = "RelationSelector";
910
-
911
- function InnerCheckBox({ checked }: { checked: boolean }) {
912
- return (
913
- <div className={cls(
914
- "p-2 w-8 h-8 inline-flex items-center justify-center text-sm font-medium focus:outline-hidden transition-colors ease-in-out duration-150"
915
- )}>
916
- <div
917
- className={cls(
918
- "border-2 relative transition-colors ease-in-out duration-150 w-4 h-4 rounded-xs flex items-center justify-center",
919
- checked ? "bg-primary text-surface-accent-100 dark:text-surface-accent-900 border-transparent" : "bg-white dark:bg-surface-accent-900 border-surface-accent-800 dark:border-surface-accent-200"
920
- )}
921
- >
922
- {checked && <CheckIcon size={iconSize.smallest} className="absolute"/>}
923
- </div>
924
- </div>
925
- );
926
- }