@rebasepro/cms 0.17.3 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/{CollectionEditorDialog-B-akreOU.js → CollectionEditorDialog-BeX4b92r.js} +13 -7
- package/dist/{CollectionEditorDialog-B-akreOU.js.map → CollectionEditorDialog-BeX4b92r.js.map} +1 -1
- package/dist/{PropertyEditView-CiY84wkR.js → PropertyEditView-C7thfj4L.js} +3 -3
- package/dist/{PropertyEditView-CiY84wkR.js.map → PropertyEditView-C7thfj4L.js.map} +1 -1
- package/dist/{RouterCollectionsStudioView-DkAWda4N.js → RouterCollectionsStudioView-BtjzvB6E.js} +4 -4
- package/dist/{RouterCollectionsStudioView-DkAWda4N.js.map → RouterCollectionsStudioView-BtjzvB6E.js.map} +1 -1
- package/dist/{SchemaChangeDialog-DceT8QFX.js → SchemaChangeDialog-CkqK6KOC.js} +4 -2
- package/dist/{SchemaChangeDialog-DceT8QFX.js.map → SchemaChangeDialog-CkqK6KOC.js.map} +1 -1
- package/dist/collection_editor/api/generateCollectionApi.d.ts +1 -2
- package/dist/collection_editor_ui.d.ts +2 -0
- package/dist/collection_editor_ui.js +6 -5
- package/dist/collection_editor_ui.js.map +1 -1
- package/dist/components/DeleteEntityDialog.d.ts +2 -3
- package/dist/components/RebaseLayout.d.ts +11 -4
- package/dist/components/field_configs.d.ts +1 -1
- package/dist/editor/sanitize-html.d.ts +15 -0
- package/dist/editor.js +195 -10
- package/dist/editor.js.map +1 -1
- package/dist/{export-Ben8E355.js → export--arni_VY.js} +2 -2
- package/dist/{export-Ben8E355.js.map → export--arni_VY.js.map} +1 -1
- package/dist/form/components/LabelWithIcon.d.ts +10 -0
- package/dist/{history-DIEVd26f.js → history-C2TpoQwi.js} +2 -2
- package/dist/{history-DIEVd26f.js.map → history-C2TpoQwi.js.map} +1 -1
- package/dist/hooks/navigation/useBuildNavigationStateController.d.ts +1 -1
- package/dist/hooks/navigation/useResolvedViews.d.ts +1 -1
- package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
- package/dist/{import-D25x7ko-.js → import-DTkXNoBg.js} +2 -2
- package/dist/{import-D25x7ko-.js.map → import-DTkXNoBg.js.map} +1 -1
- package/dist/index.js +78 -33
- package/dist/index.js.map +1 -1
- package/dist/util/propertyConfigMapper.d.ts +1 -1
- package/dist/util-CfLFggP7.js +57 -0
- package/dist/util-CfLFggP7.js.map +1 -0
- package/dist/{util-DVqnqlLn.js → util-DE1WQW6h.js} +134 -112
- package/dist/util-DE1WQW6h.js.map +1 -0
- package/package.json +42 -34
- package/dist/util-DVqnqlLn.js.map +0 -1
- package/src/collection_editor/CollectionEditorDialogsContext.tsx +0 -25
- package/src/collection_editor/ConfigControllerProvider.tsx +0 -395
- package/src/collection_editor/_cms_internals.ts +0 -41
- package/src/collection_editor/api/generateCollectionApi.ts +0 -126
- package/src/collection_editor/api/index.ts +0 -1
- package/src/collection_editor/extensibility_types.ts +0 -168
- package/src/collection_editor/index.ts +0 -108
- package/src/collection_editor/liveSchemaClient.ts +0 -285
- package/src/collection_editor/relation_kinds.ts +0 -44
- package/src/collection_editor/serializable_types.ts +0 -535
- package/src/collection_editor/serializable_utils.ts +0 -729
- package/src/collection_editor/types/collection_editor_controller.tsx +0 -62
- package/src/collection_editor/types/collection_inference.ts +0 -18
- package/src/collection_editor/types/config_controller.tsx +0 -121
- package/src/collection_editor/ui/AddKanbanColumnAction.tsx +0 -199
- package/src/collection_editor/ui/CollectionViewHeaderAction.tsx +0 -51
- package/src/collection_editor/ui/EditorCollectionAction.tsx +0 -54
- package/src/collection_editor/ui/EditorCollectionActionStart.tsx +0 -90
- package/src/collection_editor/ui/EditorEntityAction.tsx +0 -50
- package/src/collection_editor/ui/HomePageEditorCollectionAction.tsx +0 -114
- package/src/collection_editor/ui/KanbanSetupAction.tsx +0 -43
- package/src/collection_editor/ui/MissingReferenceWidget.tsx +0 -35
- package/src/collection_editor/ui/NewCollectionButton.tsx +0 -27
- package/src/collection_editor/ui/NewCollectionCard.tsx +0 -48
- package/src/collection_editor/ui/PropertyAddColumnComponent.tsx +0 -49
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +0 -260
- package/src/collection_editor/ui/collection_editor/AIModifiedPathsContext.tsx +0 -90
- package/src/collection_editor/ui/collection_editor/CollectionDetailsForm.tsx +0 -388
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +0 -1098
- package/src/collection_editor/ui/collection_editor/CollectionEditorWelcomeView.tsx +0 -301
- package/src/collection_editor/ui/collection_editor/CollectionJsonImportDialog.tsx +0 -172
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +0 -620
- package/src/collection_editor/ui/collection_editor/CollectionRLSTab.tsx +0 -530
- package/src/collection_editor/ui/collection_editor/CollectionRelationsTab.tsx +0 -462
- package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +0 -104
- package/src/collection_editor/ui/collection_editor/CollectionYupValidation.tsx +0 -9
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +0 -199
- package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +0 -315
- package/src/collection_editor/ui/collection_editor/EntityActionsEditTab.tsx +0 -164
- package/src/collection_editor/ui/collection_editor/EntityActionsSelectDialog.tsx +0 -40
- package/src/collection_editor/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +0 -39
- package/src/collection_editor/ui/collection_editor/EnumForm.tsx +0 -373
- package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +0 -288
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +0 -141
- package/src/collection_editor/ui/collection_editor/KanbanConfigSection.tsx +0 -194
- package/src/collection_editor/ui/collection_editor/LayoutModeSwitch.tsx +0 -53
- package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +0 -980
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +0 -164
- package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +0 -364
- package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +0 -44
- package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +0 -41
- package/src/collection_editor/ui/collection_editor/SchemaChangeDialog.tsx +0 -470
- package/src/collection_editor/ui/collection_editor/SubcollectionsEditTab.tsx +0 -256
- package/src/collection_editor/ui/collection_editor/SwitchControl.tsx +0 -39
- package/src/collection_editor/ui/collection_editor/ViewModeSwitch.tsx +0 -68
- package/src/collection_editor/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +0 -67
- package/src/collection_editor/ui/collection_editor/import/CollectionEditorImportMapping.tsx +0 -296
- package/src/collection_editor/ui/collection_editor/import/clean_import_data.ts +0 -57
- package/src/collection_editor/ui/collection_editor/properties/BlockPropertyField.tsx +0 -147
- package/src/collection_editor/ui/collection_editor/properties/BooleanPropertyField.tsx +0 -41
- package/src/collection_editor/ui/collection_editor/properties/CommonPropertyFields.tsx +0 -113
- package/src/collection_editor/ui/collection_editor/properties/DateTimePropertyField.tsx +0 -250
- package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +0 -126
- package/src/collection_editor/ui/collection_editor/properties/KeyValuePropertyField.tsx +0 -20
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +0 -153
- package/src/collection_editor/ui/collection_editor/properties/MarkdownPropertyField.tsx +0 -217
- package/src/collection_editor/ui/collection_editor/properties/NumberPropertyField.tsx +0 -117
- package/src/collection_editor/ui/collection_editor/properties/ReferencePropertyField.tsx +0 -129
- package/src/collection_editor/ui/collection_editor/properties/RelationPropertyField.tsx +0 -335
- package/src/collection_editor/ui/collection_editor/properties/RepeatPropertyField.tsx +0 -107
- package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +0 -403
- package/src/collection_editor/ui/collection_editor/properties/StringPropertyField.tsx +0 -148
- package/src/collection_editor/ui/collection_editor/properties/UrlPropertyField.tsx +0 -91
- package/src/collection_editor/ui/collection_editor/properties/VectorPropertyField.tsx +0 -34
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +0 -49
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +0 -925
- package/src/collection_editor/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +0 -28
- package/src/collection_editor/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +0 -626
- package/src/collection_editor/ui/collection_editor/properties/conditions/index.ts +0 -6
- package/src/collection_editor/ui/collection_editor/properties/conditions/property_paths.ts +0 -93
- package/src/collection_editor/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +0 -52
- package/src/collection_editor/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +0 -63
- package/src/collection_editor/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +0 -114
- package/src/collection_editor/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +0 -154
- package/src/collection_editor/ui/collection_editor/properties/validation/ValidationPanel.tsx +0 -28
- package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +0 -112
- package/src/collection_editor/ui/collection_editor/templates/pages_template.ts +0 -183
- package/src/collection_editor/ui/collection_editor/templates/products_template.ts +0 -88
- package/src/collection_editor/ui/collection_editor/templates/users_template.ts +0 -42
- package/src/collection_editor/ui/collection_editor/util.ts +0 -28
- package/src/collection_editor/ui/collection_editor/utils/strings.ts +0 -1
- package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -39
- package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +0 -276
- package/src/collection_editor/useCollectionEditorController.tsx +0 -9
- package/src/collection_editor/useCollectionsConfigController.tsx +0 -9
- package/src/collection_editor/useJsonCollectionsConfigController.tsx +0 -297
- package/src/collection_editor/useLiveSchemaEditing.tsx +0 -315
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +0 -341
- package/src/collection_editor/useSafeSnackbarController.ts +0 -9
- package/src/collection_editor/validateCollectionJson.ts +0 -440
- package/src/collection_editor_ui.ts +0 -21
- package/src/components/AdditionalFieldValue.tsx +0 -68
- package/src/components/AdminModeSyncer.tsx +0 -56
- package/src/components/ArrayContainer.tsx +0 -542
- package/src/components/ClearFilterSortButton.tsx +0 -56
- package/src/components/CollectionEditorDialogs.tsx +0 -69
- package/src/components/CollectionPanel.tsx +0 -179
- package/src/components/CollectionTableBinding/CollectionRowActions.tsx +0 -244
- package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +0 -394
- package/src/components/CollectionTableBinding/CollectionTableBindingProps.tsx +0 -172
- package/src/components/CollectionTableBinding/PropertyTableCell.tsx +0 -350
- package/src/components/CollectionTableBinding/column_utils.tsx +0 -155
- package/src/components/CollectionTableBinding/fields/TableMultipleRelationField.tsx +0 -131
- package/src/components/CollectionTableBinding/fields/TableReferenceField.tsx +0 -193
- package/src/components/CollectionTableBinding/fields/TableRelationField.tsx +0 -192
- package/src/components/CollectionTableBinding/fields/TableRelationSelectorField.tsx +0 -42
- package/src/components/CollectionTableBinding/fields/TableStorageUpload.tsx +0 -324
- package/src/components/CollectionTableBinding/fields/VirtualTableSelect.tsx +0 -132
- package/src/components/CollectionTableBinding/fields/VirtualTableUserSelect.tsx +0 -41
- package/src/components/CollectionTableBinding/index.tsx +0 -14
- package/src/components/CollectionTableBinding/internal/CollectionTableToolbar.tsx +0 -96
- package/src/components/CollectionTableBinding/internal/EntityTableCell.tsx +0 -309
- package/src/components/CollectionTableBinding/internal/EntityTableCellActions.tsx +0 -80
- package/src/components/CollectionTableBinding/internal/common.tsx +0 -74
- package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +0 -422
- package/src/components/CollectionTableBinding/internal/popup_field/useDraggable.tsx +0 -99
- package/src/components/CollectionTableBinding/internal/popup_field/useWindowSize.tsx +0 -22
- package/src/components/CollectionTableBinding/table_bindings.tsx +0 -350
- package/src/components/CollectionViewBinding/BoardCardBinding.tsx +0 -263
- package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +0 -662
- package/src/components/CollectionViewBinding/CollectionCardViewBinding.tsx +0 -159
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +0 -1430
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +0 -227
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +0 -1603
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +0 -240
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +0 -239
- package/src/components/CollectionViewBinding/FilterPresetsButton.tsx +0 -298
- package/src/components/CollectionViewBinding/FiltersDialog.tsx +0 -218
- package/src/components/CollectionViewBinding/SearchExplanation.tsx +0 -106
- package/src/components/CollectionViewBinding/SearchHighlight.tsx +0 -311
- package/src/components/CollectionViewBinding/SlotValue.tsx +0 -156
- package/src/components/CollectionViewBinding/SortButton.tsx +0 -354
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +0 -49
- package/src/components/CollectionViewBinding/SplitListShowButton.tsx +0 -28
- package/src/components/CollectionViewBinding/SplitListView.tsx +0 -488
- package/src/components/CollectionViewBinding/SplitViewContext.tsx +0 -34
- package/src/components/CollectionViewBinding/ViewModeToggle.tsx +0 -250
- package/src/components/CollectionViewBinding/hooks/useCollectionInlineEditor.ts +0 -81
- package/src/components/CollectionViewBinding/hooks/useKanbanDragAndDrop.ts +0 -181
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +0 -802
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +0 -703
- package/src/components/CollectionViewBinding/useSelectionController.tsx +0 -40
- package/src/components/CollectionViewBinding/utils.ts +0 -22
- package/src/components/DefaultAppBar.tsx +0 -264
- package/src/components/DefaultDrawer.tsx +0 -593
- package/src/components/DeleteEntityDialog.tsx +0 -205
- package/src/components/DetailViewBinding.tsx +0 -731
- package/src/components/DrawerNavigationGroup.tsx +0 -141
- package/src/components/DrawerNavigationItem.tsx +0 -115
- package/src/components/EditFormActions.tsx +0 -303
- package/src/components/EditViewBinding.tsx +0 -1069
- package/src/components/EntityDisplayHeader.tsx +0 -170
- package/src/components/EntityIdentityBar.tsx +0 -428
- package/src/components/EntityInspector.tsx +0 -156
- package/src/components/EntityPreviewBinding.tsx +0 -432
- package/src/components/EntityPreviewNesting.tsx +0 -50
- package/src/components/EntityViewBinding.tsx +0 -374
- package/src/components/FieldCaption.tsx +0 -14
- package/src/components/HomePage/ContentHomePage.tsx +0 -586
- package/src/components/HomePage/FavouritesView.tsx +0 -60
- package/src/components/HomePage/HomePageDnD.tsx +0 -704
- package/src/components/HomePage/NavigationCard.tsx +0 -81
- package/src/components/HomePage/NavigationCardBinding.tsx +0 -107
- package/src/components/HomePage/NavigationGroup.tsx +0 -156
- package/src/components/HomePage/RenameGroupDialog.tsx +0 -123
- package/src/components/HomePage/SmallNavigationCard.tsx +0 -41
- package/src/components/HomePage/index.tsx +0 -5
- package/src/components/InlineEntityPreview.tsx +0 -149
- package/src/components/JsonPreviewBinding.tsx +0 -66
- package/src/components/PropertyCollectionView.tsx +0 -336
- package/src/components/PropertyConfigBadge.tsx +0 -28
- package/src/components/PropertyIdCopyTooltip.tsx +0 -47
- package/src/components/RebaseAuthGate.tsx +0 -44
- package/src/components/RebaseCMS.tsx +0 -30
- package/src/components/RebaseLayout.tsx +0 -80
- package/src/components/RebaseNavigation.tsx +0 -369
- package/src/components/RebaseRouteDefs.tsx +0 -127
- package/src/components/RebaseShell.tsx +0 -83
- package/src/components/ReferenceTable/SelectionTableBinding.tsx +0 -389
- package/src/components/ReferenceWidget.tsx +0 -156
- package/src/components/RelationSelector.tsx +0 -926
- package/src/components/SearchIconsView.tsx +0 -78
- package/src/components/SelectableTable/SelectableTable.tsx +0 -309
- package/src/components/SelectableTable/SelectableTableContext.tsx +0 -7
- package/src/components/SelectableTable/SelectionStore.ts +0 -43
- package/src/components/SelectableTable/filters/BooleanFilterField.tsx +0 -59
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +0 -146
- package/src/components/SelectableTable/filters/FilterFieldBinding.tsx +0 -190
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +0 -250
- package/src/components/SelectableTable/filters/RelationFilterField.tsx +0 -238
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +0 -282
- package/src/components/SelectableTable/filters/null_filter.ts +0 -63
- package/src/components/SideDialogs.tsx +0 -238
- package/src/components/SidePanelBinding.tsx +0 -361
- package/src/components/SidePanelProvider.tsx +0 -80
- package/src/components/UserSelector.tsx +0 -513
- package/src/components/admin/CreationResultDialog.tsx +0 -138
- package/src/components/admin/index.ts +0 -1
- package/src/components/app/AppBar.tsx +0 -17
- package/src/components/app/Drawer.tsx +0 -38
- package/src/components/app/Scaffold.tsx +0 -366
- package/src/components/app/index.ts +0 -4
- package/src/components/app/useApp.tsx +0 -36
- package/src/components/common/default_entity_actions.tsx +0 -440
- package/src/components/common/index.ts +0 -1
- package/src/components/field_configs.tsx +0 -506
- package/src/components/history/EntityHistoryEntry.tsx +0 -203
- package/src/components/history/EntityHistoryView.tsx +0 -206
- package/src/components/history/LastEditedByFormAction.tsx +0 -24
- package/src/components/history/LastEditedByIndicator.tsx +0 -95
- package/src/components/history/UserChip.tsx +0 -15
- package/src/components/history/index.tsx +0 -5
- package/src/components/index.ts +0 -63
- package/src/contexts/BreacrumbsContext.tsx +0 -45
- package/src/contexts/SideDialogsControllerContext.tsx +0 -5
- package/src/contexts/SidePanelControllerContext.tsx +0 -5
- package/src/data_export/export/BasicExportAction.tsx +0 -142
- package/src/data_export/export/ExportCollectionAction.tsx +0 -321
- package/src/data_export/export/export.ts +0 -295
- package/src/data_export/export/fetch_export_data.ts +0 -90
- package/src/data_export/export/index.ts +0 -4
- package/src/data_export/index.ts +0 -1
- package/src/data_import/components/DataNewPropertiesMapping.tsx +0 -261
- package/src/data_import/components/ImportFileUpload.tsx +0 -42
- package/src/data_import/components/ImportNewPropertyFieldPreview.tsx +0 -58
- package/src/data_import/components/ImportSaveInProgress.tsx +0 -121
- package/src/data_import/components/index.ts +0 -4
- package/src/data_import/hooks/index.ts +0 -1
- package/src/data_import/hooks/useImportConfig.tsx +0 -34
- package/src/data_import/import/ImportCollectionAction.tsx +0 -461
- package/src/data_import/import/index.ts +0 -1
- package/src/data_import/index.ts +0 -5
- package/src/data_import/types/column_mapping.ts +0 -32
- package/src/data_import/types/index.ts +0 -1
- package/src/data_import/utils/csv.ts +0 -153
- package/src/data_import/utils/data.ts +0 -181
- package/src/data_import/utils/file_headers.ts +0 -14
- package/src/data_import/utils/file_to_json.ts +0 -149
- package/src/data_import/utils/get_import_inference_type.ts +0 -33
- package/src/data_import/utils/get_properties_mapping.ts +0 -67
- package/src/data_import/utils/index.ts +0 -5
- package/src/data_import/utils/save_entities.ts +0 -138
- package/src/data_import/utils/transforms.ts +0 -70
- package/src/editor/components/SlashCommandMenu.tsx +0 -532
- package/src/editor/components/editor-bubble-item.tsx +0 -32
- package/src/editor/components/editor-bubble.tsx +0 -143
- package/src/editor/components/image-bubble.tsx +0 -183
- package/src/editor/components/index.ts +0 -14
- package/src/editor/components/table-bubble.tsx +0 -233
- package/src/editor/editor.tsx +0 -529
- package/src/editor/extensions/HighlightDecorationExtension.ts +0 -115
- package/src/editor/extensions/Image.ts +0 -159
- package/src/editor/extensions/TextLoadingDecorationExtension.tsx +0 -107
- package/src/editor/extensions/clipboard.ts +0 -72
- package/src/editor/extensions/custom-keymap.ts +0 -25
- package/src/editor/extensions/drag-and-drop.tsx +0 -508
- package/src/editor/hooks/useProseMirror.ts +0 -126
- package/src/editor/hooks/useProseMirrorContext.ts +0 -15
- package/src/editor/index.ts +0 -2
- package/src/editor/markdown-it-plugins.d.ts +0 -24
- package/src/editor/markdown.ts +0 -204
- package/src/editor/nodeViews/ImageComponent.tsx +0 -20
- package/src/editor/nodeViews/ReactNodeView.tsx +0 -89
- package/src/editor/nodeViews/TaskItemComponent.tsx +0 -30
- package/src/editor/nodeViews/index.ts +0 -35
- package/src/editor/plugins/index.ts +0 -58
- package/src/editor/plugins/inputrules.ts +0 -82
- package/src/editor/plugins/placeholderPlugin.ts +0 -55
- package/src/editor/plugins/slashCommandPlugin.ts +0 -65
- package/src/editor/schema.ts +0 -339
- package/src/editor/selectors/ai-selector.tsx +0 -110
- package/src/editor/selectors/color-selector.tsx +0 -199
- package/src/editor/selectors/link-selector.tsx +0 -122
- package/src/editor/selectors/node-selector.tsx +0 -159
- package/src/editor/selectors/text-buttons.tsx +0 -79
- package/src/editor/types.ts +0 -6
- package/src/editor/useProseMirror.ts +0 -125
- package/src/editor/utils/prosemirror-utils.ts +0 -113
- package/src/editor/utils/remove_classes.ts +0 -18
- package/src/editor/utils/useDebouncedCallback.ts +0 -25
- package/src/form/EntityForm.tsx +0 -761
- package/src/form/EntityFormActions.tsx +0 -160
- package/src/form/EntityFormBinding.tsx +0 -342
- package/src/form/PropertyFieldBinding.tsx +0 -348
- package/src/form/components/ErrorFocus.tsx +0 -44
- package/src/form/components/FieldBlock.tsx +0 -189
- package/src/form/components/FieldHelperText.tsx +0 -84
- package/src/form/components/FormRail.tsx +0 -153
- package/src/form/components/FormSections.tsx +0 -145
- package/src/form/components/LabelWithIcon.tsx +0 -43
- package/src/form/components/LabelWithIconAndTooltip.tsx +0 -28
- package/src/form/components/LocalChangesMenu.tsx +0 -147
- package/src/form/components/StorageItemPreview.tsx +0 -81
- package/src/form/components/StorageUploadProgress.tsx +0 -118
- package/src/form/components/index.tsx +0 -6
- package/src/form/components/useRailVisible.tsx +0 -47
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +0 -109
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +0 -172
- package/src/form/field_bindings/BinaryFieldBinding.tsx +0 -177
- package/src/form/field_bindings/BlockFieldBinding.tsx +0 -282
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +0 -78
- package/src/form/field_bindings/GeopointFieldBinding.tsx +0 -200
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +0 -574
- package/src/form/field_bindings/MapFieldBinding.tsx +0 -120
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +0 -259
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +0 -130
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +0 -168
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +0 -94
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +0 -146
- package/src/form/field_bindings/RelationFieldBinding.tsx +0 -202
- package/src/form/field_bindings/RepeatFieldBinding.tsx +0 -143
- package/src/form/field_bindings/SelectFieldBinding.tsx +0 -111
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +0 -521
- package/src/form/field_bindings/SwitchFieldBinding.tsx +0 -70
- package/src/form/field_bindings/TextFieldBinding.tsx +0 -189
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +0 -71
- package/src/form/field_bindings/VectorFieldBinding.tsx +0 -209
- package/src/form/form_utils.ts +0 -243
- package/src/form/index.tsx +0 -36
- package/src/form/useClearRestoreValue.tsx +0 -36
- package/src/form/useUndoableDiscard.ts +0 -39
- package/src/form/validation.ts +0 -583
- package/src/hooks/index.ts +0 -16
- package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +0 -22
- package/src/hooks/navigation/contexts/NavigationStateContext.tsx +0 -16
- package/src/hooks/navigation/contexts/UrlContext.tsx +0 -24
- package/src/hooks/navigation/contexts/index.ts +0 -14
- package/src/hooks/navigation/useAsyncResolver.ts +0 -100
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +0 -218
- package/src/hooks/navigation/useBuildNavigationStateController.tsx +0 -134
- package/src/hooks/navigation/useBuildUrlController.tsx +0 -66
- package/src/hooks/navigation/useNavigationResolution.ts +0 -271
- package/src/hooks/navigation/useResolvedCollections.ts +0 -102
- package/src/hooks/navigation/useResolvedViews.tsx +0 -139
- package/src/hooks/navigation/useTopLevelNavigation.ts +0 -273
- package/src/hooks/navigation/utils.ts +0 -200
- package/src/hooks/useAdminContext.tsx +0 -83
- package/src/hooks/useBreadcrumbsController.tsx +0 -17
- package/src/hooks/useBuildSideDialogsController.tsx +0 -145
- package/src/hooks/useBuildSidePanel.tsx +0 -315
- package/src/hooks/useChildViewSource.ts +0 -46
- package/src/hooks/useEntityDisplay.tsx +0 -262
- package/src/hooks/useEntityDisplayTitle.tsx +0 -49
- package/src/hooks/useHistory.ts +0 -178
- package/src/hooks/useRecordActions.tsx +0 -69
- package/src/hooks/useResolvedNavigationFrom.tsx +0 -167
- package/src/hooks/useResolvedUsers.ts +0 -148
- package/src/hooks/useSelectionDialog.tsx +0 -58
- package/src/hooks/useSideDialogsController.tsx +0 -25
- package/src/hooks/useSidePanel.tsx +0 -13
- package/src/index.ts +0 -124
- package/src/preview/PropertyPreview.tsx +0 -331
- package/src/preview/compact_text.ts +0 -68
- package/src/preview/components/ArrayEnumPreview.tsx +0 -39
- package/src/preview/components/BooleanPreview.tsx +0 -28
- package/src/preview/components/DatePreview.tsx +0 -106
- package/src/preview/components/EmptyValue.tsx +0 -10
- package/src/preview/components/EnumValuesChip.tsx +0 -39
- package/src/preview/components/ImagePreview.tsx +0 -144
- package/src/preview/components/InlineEntityListPreview.tsx +0 -42
- package/src/preview/components/ReferencePreview.tsx +0 -235
- package/src/preview/components/RelationPreview.tsx +0 -271
- package/src/preview/components/StorageThumbnail.tsx +0 -147
- package/src/preview/components/UrlComponentPreview.tsx +0 -116
- package/src/preview/components/UserPreview.tsx +0 -30
- package/src/preview/components/date_fns_locales.ts +0 -125
- package/src/preview/index.ts +0 -26
- package/src/preview/property_previews/ArrayOfMapsPreview.tsx +0 -73
- package/src/preview/property_previews/ArrayOfReferencesPreview.tsx +0 -67
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +0 -77
- package/src/preview/property_previews/ArrayOfStorageComponentsPreview.tsx +0 -49
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +0 -70
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +0 -79
- package/src/preview/property_previews/ArrayPropertyEnumPreview.tsx +0 -35
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +0 -79
- package/src/preview/property_previews/MapPropertyPreview.tsx +0 -198
- package/src/preview/property_previews/NumberPropertyPreview.tsx +0 -39
- package/src/preview/property_previews/SkeletonPropertyComponent.tsx +0 -276
- package/src/preview/property_previews/StringPropertyPreview.tsx +0 -70
- package/src/preview/util.ts +0 -39
- package/src/routes/CustomViewRoute.tsx +0 -33
- package/src/routes/RebaseRoute.tsx +0 -467
- package/src/routes/index.ts +0 -2
- package/src/types/components/EntityFormActionsProps.tsx +0 -20
- package/src/types/components/EntityFormProps.tsx +0 -119
- package/src/types/components/PropertyPreviewProps.tsx +0 -85
- package/src/types/components/formex.tsx +0 -2
- package/src/types/components/index.ts +0 -3
- package/src/types/fields.tsx +0 -340
- package/src/types/index.ts +0 -4
- package/src/util/dataControllerAdapter.ts +0 -88
- package/src/util/entity_actions.ts +0 -28
- package/src/util/index.ts +0 -5
- package/src/util/navigation_utils.ts +0 -89
- package/src/util/number_format.ts +0 -46
- package/src/util/previews.ts +0 -131
- package/src/util/propertyConfigMapper.ts +0 -134
- package/src/util/property_utils.tsx +0 -236
- package/src/util/resolutions.ts +0 -96
- package/src/util/view_constants.ts +0 -33
- package/src/util/view_mode.ts +0 -176
|
@@ -1,1069 +0,0 @@
|
|
|
1
|
-
import type { ComponentRef } from "@rebasepro/types";
|
|
2
|
-
import type { EntityCustomViewParams, FormViewConfig, AdminCollection } from "@rebasepro/cms-types";
|
|
3
|
-
import type { FormContext } from "../types/fields";
|
|
4
|
-
import type { PluginFormActionProps } from "@rebasepro/cms-types";
|
|
5
|
-
import React, { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
|
-
import { Entity, EntityStatus, getCollectionDataPath } from "@rebasepro/types";
|
|
7
|
-
import { PluginProviderStack, resolveComponentRef, useComponentOverride, CollectionScopeProvider, getAdminEntityChildViews } from "@rebasepro/app";
|
|
8
|
-
|
|
9
|
-
import { CollectionViewBinding } from "./CollectionViewBinding/CollectionViewBinding";
|
|
10
|
-
import { EntityViewBinding } from "./EntityViewBinding";
|
|
11
|
-
import { CircularProgressCenter, iconSize } from "@rebasepro/ui";
|
|
12
|
-
import {
|
|
13
|
-
CenteredView,
|
|
14
|
-
CircularProgress,
|
|
15
|
-
cls,
|
|
16
|
-
CodeIcon,
|
|
17
|
-
defaultBorderMixin,
|
|
18
|
-
HistoryIcon,
|
|
19
|
-
IconButton,
|
|
20
|
-
Maximize2Icon,
|
|
21
|
-
MenuItem,
|
|
22
|
-
Skeleton,
|
|
23
|
-
Tab,
|
|
24
|
-
Tabs,
|
|
25
|
-
Tooltip,
|
|
26
|
-
Typography
|
|
27
|
-
} from "@rebasepro/ui";
|
|
28
|
-
import { ErrorBoundary } from "@rebasepro/ui";
|
|
29
|
-
import { ErrorView } from "@rebasepro/app";
|
|
30
|
-
|
|
31
|
-
import { removeInitialAndTrailingSlashes, resolveDefaultSelectedView } from "@rebasepro/app";
|
|
32
|
-
import { resolvedSelectedEntityView } from "../util/resolutions";
|
|
33
|
-
import {
|
|
34
|
-
useCustomizationController,
|
|
35
|
-
useFetch,
|
|
36
|
-
useRebaseContext,
|
|
37
|
-
useLargeLayout,
|
|
38
|
-
useSlot,
|
|
39
|
-
getIcon
|
|
40
|
-
} from "@rebasepro/app";
|
|
41
|
-
import { getEntityFromMemoryCache, removeEntityFromMemoryCache, saveEntityToMemoryCache } from "@rebasepro/app";
|
|
42
|
-
import { EntityFormBinding } from "../form";
|
|
43
|
-
import { getEditHandoffValues } from "../form/form_utils";
|
|
44
|
-
import type { EntityFormBindingProps } from "../form";
|
|
45
|
-
import type { OnUpdateParams } from "../types/components/EntityFormProps";
|
|
46
|
-
import { EditFormActions } from "./EditFormActions";
|
|
47
|
-
import { EntityIdentityBar } from "./EntityIdentityBar";
|
|
48
|
-
import { useUndoableDiscard } from "../form/useUndoableDiscard";
|
|
49
|
-
import { SplitListCloseButton } from "./CollectionViewBinding/SplitListCloseButton";
|
|
50
|
-
import { SplitListShowButton } from "./CollectionViewBinding/SplitListShowButton";
|
|
51
|
-
import { useRecordActions } from "../hooks/useRecordActions";
|
|
52
|
-
import { useSideDialogContext } from "./SideDialogs";
|
|
53
|
-
import { useAdminContext } from "../hooks/useAdminContext";
|
|
54
|
-
import { EntityInspector, InspectorTab } from "./EntityInspector";
|
|
55
|
-
import { useEntityDisplayTitle } from "../hooks/useEntityDisplayTitle";
|
|
56
|
-
import { createFormexStub, getEntityFromCache } from "@rebasepro/app";
|
|
57
|
-
import { usePermissions } from "@rebasepro/app";
|
|
58
|
-
import { useUrlController } from "../hooks/navigation/contexts/UrlContext";
|
|
59
|
-
import { withListState } from "../util/view_mode";
|
|
60
|
-
import { useNavigate } from "react-router";
|
|
61
|
-
|
|
62
|
-
import {
|
|
63
|
-
MAIN_TAB_VALUE,
|
|
64
|
-
JSON_TAB_VALUE,
|
|
65
|
-
HISTORY_TAB_VALUE,
|
|
66
|
-
ENTITY_VIEW_TAB_BAR_CLASS,
|
|
67
|
-
ENTITY_VIEW_TAB_CLASS,
|
|
68
|
-
ENTITY_VIEW_TAB_LABEL_CLASS
|
|
69
|
-
} from "../util/view_constants";
|
|
70
|
-
export { MAIN_TAB_VALUE, JSON_TAB_VALUE, HISTORY_TAB_VALUE };
|
|
71
|
-
|
|
72
|
-
export type BarActionsParams = {
|
|
73
|
-
values: object,
|
|
74
|
-
status: EntityStatus,
|
|
75
|
-
path: string,
|
|
76
|
-
entityId?: string | number;
|
|
77
|
-
/** Whether `values` differ from what is stored. */
|
|
78
|
-
dirty: boolean;
|
|
79
|
-
/**
|
|
80
|
-
* Hand the edit in progress to the layout this action is about to open, so
|
|
81
|
-
* the record arrives showing — and still holding — what the user typed here.
|
|
82
|
-
* Call it from any action that changes layout without leaving the record;
|
|
83
|
-
* it decides on its own whether there is anything worth carrying, so a
|
|
84
|
-
* record nobody edited still arrives clean.
|
|
85
|
-
*
|
|
86
|
-
* Only supplied where there is a form to carry: the detail view passes no
|
|
87
|
-
* such thing.
|
|
88
|
-
*/
|
|
89
|
-
carryEdit?: () => void;
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
export type OnTabChangeParams<M extends Record<string, unknown>> = {
|
|
93
|
-
path: string;
|
|
94
|
-
entityId?: string | number;
|
|
95
|
-
selectedTab?: string;
|
|
96
|
-
collection: AdminCollection<M>;
|
|
97
|
-
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export interface EditViewBindingProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
101
|
-
/**
|
|
102
|
-
* The admin path of the entity, e.g. "users" or "products".
|
|
103
|
-
*/
|
|
104
|
-
path: string;
|
|
105
|
-
collection: AdminCollection<M>;
|
|
106
|
-
entityId?: string | number;
|
|
107
|
-
databaseId?: string;
|
|
108
|
-
copy?: boolean;
|
|
109
|
-
selectedTab?: string;
|
|
110
|
-
parentCollectionSlugs: string[], parentEntityIds: string[];
|
|
111
|
-
onValuesModified?: (modified: boolean, values: M) => void;
|
|
112
|
-
onSaved?: (params: OnUpdateParams) => void;
|
|
113
|
-
onTabChange?: (props: OnTabChangeParams<M>) => void;
|
|
114
|
-
navigateBack?: () => void;
|
|
115
|
-
/**
|
|
116
|
-
* Dismiss this record — the ✕ at the identity bar's trailing edge, and what
|
|
117
|
-
* "save and close" reaches once the save lands.
|
|
118
|
-
*
|
|
119
|
-
* Set by the layouts that own the record's place on screen but are not side
|
|
120
|
-
* dialogs: the split, where dismissing is navigating back to the collection.
|
|
121
|
-
* The side panel and the dialog close through {@link useSideDialogContext}
|
|
122
|
-
* instead, which is what runs their unsaved-changes prompt.
|
|
123
|
-
*/
|
|
124
|
-
onCloseRequest?: () => void;
|
|
125
|
-
layout?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
126
|
-
barActions?: (params: BarActionsParams) => React.ReactNode;
|
|
127
|
-
/**
|
|
128
|
-
* Controls the owning panel puts at the identity bar's leading edge, ahead
|
|
129
|
-
* of the breadcrumb — the side panel's close button.
|
|
130
|
-
*/
|
|
131
|
-
barActionsStart?: React.ReactNode;
|
|
132
|
-
/**
|
|
133
|
-
* Full screen only: bring the list back beside this record. Set when the
|
|
134
|
-
* record was opened out of a split collection, so folding the list away is
|
|
135
|
-
* reversible; it takes the place of the back arrow when present.
|
|
136
|
-
*/
|
|
137
|
-
onShowList?: () => void;
|
|
138
|
-
formProps?: Partial<EntityFormBindingProps<M>>,
|
|
139
|
-
/**
|
|
140
|
-
* Pre-populate the form with these values when creating a new entity.
|
|
141
|
-
* Only applied when the form is in "new" mode (no entityId).
|
|
142
|
-
* Sourced from SidePanelBindingProps (side panel) or location.state (full screen).
|
|
143
|
-
*/
|
|
144
|
-
defaultValues?: Partial<M>;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* This is the default view that is used as the content of a side panel when
|
|
149
|
-
* a record is opened.
|
|
150
|
-
*/
|
|
151
|
-
export function EditViewBinding<M extends Record<string, unknown>>({
|
|
152
|
-
entityId,
|
|
153
|
-
...props
|
|
154
|
-
}: EditViewBindingProps<M>) {
|
|
155
|
-
|
|
156
|
-
const {
|
|
157
|
-
entity,
|
|
158
|
-
dataLoading,
|
|
159
|
-
|
|
160
|
-
dataLoadingError
|
|
161
|
-
} = useFetch<M>({
|
|
162
|
-
path: props.path,
|
|
163
|
-
entityId: entityId,
|
|
164
|
-
collection: props.collection,
|
|
165
|
-
databaseId: props.databaseId,
|
|
166
|
-
useCache: false
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// The edit handed over by another layout of this same record — see
|
|
170
|
-
// {@link carryEdit} below for the sending half.
|
|
171
|
-
//
|
|
172
|
-
// Read once per record and consumed: this is a handoff, and an edit left in
|
|
173
|
-
// the channel after it has been picked up is one that reopens the record
|
|
174
|
-
// dirty on a later, unrelated visit, carrying values the user walked away
|
|
175
|
-
// from. Held in a ref rather than re-read each render, because the read has
|
|
176
|
-
// to survive the clear — {@link EntityFormBinding} measures the
|
|
177
|
-
// local-changes banner against what the form opens showing, and a prop that
|
|
178
|
-
// reverted to `undefined` once the cache was cleared would raise the banner
|
|
179
|
-
// over the very edit that was just carried in.
|
|
180
|
-
//
|
|
181
|
-
// A copy is keyed with the new records, not under the record it was copied
|
|
182
|
-
// from: it is going to be stored as its own row, and both the handoff and
|
|
183
|
-
// the local-changes backup have always keyed it that way from the writing
|
|
184
|
-
// side. Reading it under `path/id` meant a copy in progress was written to
|
|
185
|
-
// one key and looked for under another.
|
|
186
|
-
const carriesAsNew = props.copy || entityId === undefined;
|
|
187
|
-
const handoffKey = carriesAsNew ? props.path + "#new" : props.path + "/" + entityId;
|
|
188
|
-
const handoff = useRef<{ key: string, values: object | undefined } | null>(null);
|
|
189
|
-
if (handoff.current?.key !== handoffKey) {
|
|
190
|
-
handoff.current = {
|
|
191
|
-
key: handoffKey,
|
|
192
|
-
values: getEntityFromMemoryCache(handoffKey)
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
useEffect(() => {
|
|
196
|
-
removeEntityFromMemoryCache(handoffKey);
|
|
197
|
-
}, [handoffKey]);
|
|
198
|
-
|
|
199
|
-
const initialDirtyValues = carriesAsNew
|
|
200
|
-
? (props.defaultValues ?? handoff.current.values)
|
|
201
|
-
: handoff.current.values;
|
|
202
|
-
|
|
203
|
-
const { canEdit: canEditHook } = usePermissions();
|
|
204
|
-
|
|
205
|
-
const initialStatus = props.copy ? "copy" : (entityId ? "existing" : "new");
|
|
206
|
-
const [status, setStatus] = useState<EntityStatus>(initialStatus);
|
|
207
|
-
|
|
208
|
-
const canEdit = useMemo(() => {
|
|
209
|
-
if (status === "new" || status === "copy") {
|
|
210
|
-
return true;
|
|
211
|
-
} else {
|
|
212
|
-
return entity ? canEditHook(props.collection, props.path, entity ?? null) : undefined;
|
|
213
|
-
}
|
|
214
|
-
}, [canEditHook, entity, status, props.collection, props.path]);
|
|
215
|
-
|
|
216
|
-
if (!dataLoading && dataLoadingError) {
|
|
217
|
-
return <CenteredView>
|
|
218
|
-
<ErrorView error={dataLoadingError} />
|
|
219
|
-
</CenteredView>
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (!dataLoading && !initialDirtyValues && !entity && (status === "existing" || status === "copy")) {
|
|
223
|
-
console.error(`Entity with id ${entityId} not found in collection ${props.path}`);
|
|
224
|
-
return <CenteredView>
|
|
225
|
-
<Typography variant="label">Entity not found</Typography>
|
|
226
|
-
</CenteredView>;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const content = (
|
|
230
|
-
<EditViewBindingInner<M> {...props}
|
|
231
|
-
entityId={entityId}
|
|
232
|
-
entity={entity}
|
|
233
|
-
initialDirtyValues={initialDirtyValues as Partial<M>}
|
|
234
|
-
dataLoading={dataLoading}
|
|
235
|
-
status={status}
|
|
236
|
-
setStatus={setStatus}
|
|
237
|
-
canEdit={canEdit}
|
|
238
|
-
/>
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
return (
|
|
242
|
-
<CollectionScopeProvider collection={props.collection}>
|
|
243
|
-
{content}
|
|
244
|
-
</CollectionScopeProvider>
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
export function EditViewBindingInner<M extends Record<string, unknown>>({
|
|
249
|
-
path,
|
|
250
|
-
entityId,
|
|
251
|
-
selectedTab: selectedTabProp,
|
|
252
|
-
collection,
|
|
253
|
-
parentCollectionSlugs, parentEntityIds,
|
|
254
|
-
onValuesModified,
|
|
255
|
-
onSaved,
|
|
256
|
-
onTabChange,
|
|
257
|
-
navigateBack,
|
|
258
|
-
onCloseRequest,
|
|
259
|
-
entity,
|
|
260
|
-
initialDirtyValues,
|
|
261
|
-
dataLoading,
|
|
262
|
-
layout = "side_panel",
|
|
263
|
-
barActions,
|
|
264
|
-
barActionsStart,
|
|
265
|
-
onShowList,
|
|
266
|
-
status,
|
|
267
|
-
setStatus,
|
|
268
|
-
formProps,
|
|
269
|
-
canEdit
|
|
270
|
-
}: EditViewBindingProps<M> & {
|
|
271
|
-
entity?: Entity<M>,
|
|
272
|
-
initialDirtyValues?: Partial<M>, // dirty cached entity in memory
|
|
273
|
-
dataLoading: boolean,
|
|
274
|
-
status: EntityStatus,
|
|
275
|
-
setStatus: (status: EntityStatus) => void,
|
|
276
|
-
canEdit?: boolean,
|
|
277
|
-
}) {
|
|
278
|
-
|
|
279
|
-
const ResolvedFormActions = useComponentOverride("EditView.FormActions", EditFormActions);
|
|
280
|
-
const ResolvedEntityForm = useComponentOverride("Entity.Form", EntityFormBinding) as typeof EntityFormBinding;
|
|
281
|
-
const ResolvedCollectionView = useComponentOverride("Collection.View", CollectionViewBinding);
|
|
282
|
-
|
|
283
|
-
const context = useRebaseContext();
|
|
284
|
-
const adminContext = useAdminContext();
|
|
285
|
-
const urlController = useUrlController();
|
|
286
|
-
const navigate = useNavigate();
|
|
287
|
-
|
|
288
|
-
const [usedEntity, setUsedEntity] = useState<Entity<M> | undefined>(entity);
|
|
289
|
-
|
|
290
|
-
useEffect(() => {
|
|
291
|
-
if (entity)
|
|
292
|
-
setUsedEntity(entity);
|
|
293
|
-
}, [entity]);
|
|
294
|
-
|
|
295
|
-
const [formContext, setFormContext] = useState<FormContext<M> | undefined>(undefined);
|
|
296
|
-
|
|
297
|
-
const largeLayout = useLargeLayout();
|
|
298
|
-
|
|
299
|
-
const discard = useUndoableDiscard();
|
|
300
|
-
|
|
301
|
-
const customizationController = useCustomizationController();
|
|
302
|
-
const plugins = customizationController.plugins;
|
|
303
|
-
|
|
304
|
-
const formActionTopProps: PluginFormActionProps = useMemo(() => ({
|
|
305
|
-
entityId,
|
|
306
|
-
parentCollectionSlugs,
|
|
307
|
-
parentEntityIds,
|
|
308
|
-
path: path,
|
|
309
|
-
status,
|
|
310
|
-
collection: collection!,
|
|
311
|
-
context,
|
|
312
|
-
formContext: formContext as FormContext<Record<string, unknown>> | undefined,
|
|
313
|
-
openEntityMode: layout,
|
|
314
|
-
disabled: false
|
|
315
|
-
}), [entityId, parentCollectionSlugs, parentEntityIds, path, status, collection, context, formContext, layout]);
|
|
316
|
-
const pluginActionsTop = useSlot("form.actions.top", formActionTopProps);
|
|
317
|
-
|
|
318
|
-
const defaultSelectedView = useMemo(() => resolveDefaultSelectedView(
|
|
319
|
-
collection ? collection.defaultSelectedView : undefined,
|
|
320
|
-
{
|
|
321
|
-
status,
|
|
322
|
-
entityId
|
|
323
|
-
}
|
|
324
|
-
), [collection, status, entityId]);
|
|
325
|
-
|
|
326
|
-
// Track whether the user has explicitly clicked a tab in this component
|
|
327
|
-
// instance. When false, we fall back to defaultSelectedView (which may
|
|
328
|
-
// resolve asynchronously if the collection loads from the registry after
|
|
329
|
-
// mount). Once true, selectedTabProp=undefined means the user selected
|
|
330
|
-
// the form tab, so we must NOT re-apply defaultSelectedView.
|
|
331
|
-
const userHasChangedTab = useRef(false);
|
|
332
|
-
|
|
333
|
-
const [selectedTab, setSelectedTab] = useState<string>(() => {
|
|
334
|
-
const val = selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE;
|
|
335
|
-
return val === "edit" ? MAIN_TAB_VALUE : val;
|
|
336
|
-
});
|
|
337
|
-
useEffect(() => {
|
|
338
|
-
const val = userHasChangedTab.current
|
|
339
|
-
? (selectedTabProp ?? MAIN_TAB_VALUE)
|
|
340
|
-
: (selectedTabProp ?? defaultSelectedView ?? MAIN_TAB_VALUE);
|
|
341
|
-
const target = val === "edit" ? MAIN_TAB_VALUE : val;
|
|
342
|
-
if (target !== selectedTab) {
|
|
343
|
-
setSelectedTab(target);
|
|
344
|
-
}
|
|
345
|
-
}, [selectedTabProp, defaultSelectedView]);
|
|
346
|
-
|
|
347
|
-
// See DetailViewBinding: tabs are governed by `hideFromEntityViews`, the
|
|
348
|
-
// drawer by `hideFromNavigation`. They are separate roles.
|
|
349
|
-
const childViews = getAdminEntityChildViews(collection).filter(v => !v.collection.hideFromEntityViews);
|
|
350
|
-
const subcollections = childViews.map(v => v.collection);
|
|
351
|
-
const subcollectionsCount = subcollections?.length ?? 0;
|
|
352
|
-
const customViews = collection.entityViews ?? [];
|
|
353
|
-
const customViewsCount = customViews?.length ?? 0;
|
|
354
|
-
const includeJsonView = collection.includeJsonView === undefined ? true : collection.includeJsonView;
|
|
355
|
-
const includeHistoryView = Boolean(collection.history);
|
|
356
|
-
// The inspector holds the developer tools; the tab strip holds destinations.
|
|
357
|
-
// A strip with only the record in it has nothing to choose between, so it
|
|
358
|
-
// does not render at all.
|
|
359
|
-
const hasAdditionalViews = customViewsCount > 0 || subcollectionsCount > 0;
|
|
360
|
-
|
|
361
|
-
const {
|
|
362
|
-
resolvedEntityViews
|
|
363
|
-
} = resolvedSelectedEntityView(customViews, customizationController, undefined, canEdit);
|
|
364
|
-
|
|
365
|
-
// JSON and history are no longer tab values, but a bookmarked or in-flight
|
|
366
|
-
// URL may still name one. They resolve to the record, and open the
|
|
367
|
-
// inspector on that pane, so an old link keeps working.
|
|
368
|
-
const legacyInspectorTab = selectedTab === JSON_TAB_VALUE
|
|
369
|
-
? "json"
|
|
370
|
-
: selectedTab === HISTORY_TAB_VALUE ? "history" : undefined;
|
|
371
|
-
|
|
372
|
-
const validTabValues = useMemo(() => {
|
|
373
|
-
const set = new Set<string>([
|
|
374
|
-
MAIN_TAB_VALUE,
|
|
375
|
-
...resolvedEntityViews.map(v => v.key),
|
|
376
|
-
...subcollections.map(s => s.slug)
|
|
377
|
-
]);
|
|
378
|
-
return set;
|
|
379
|
-
}, [resolvedEntityViews, subcollections]);
|
|
380
|
-
|
|
381
|
-
const activeTab = validTabValues.has(selectedTab) ? selectedTab : MAIN_TAB_VALUE;
|
|
382
|
-
|
|
383
|
-
const {
|
|
384
|
-
selectedEntityView,
|
|
385
|
-
selectedSecondaryForm
|
|
386
|
-
} = resolvedSelectedEntityView(customViews, customizationController, activeTab, canEdit);
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const mainViewVisible = activeTab === MAIN_TAB_VALUE || Boolean(selectedSecondaryForm);
|
|
390
|
-
|
|
391
|
-
// Track which custom view tabs have been visited so we keep them mounted
|
|
392
|
-
// (preserving their state) but don't eagerly mount tabs never visited.
|
|
393
|
-
const mountedTabsRef = useRef<Set<string>>(new Set());
|
|
394
|
-
if (activeTab) {
|
|
395
|
-
mountedTabsRef.current.add(activeTab);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Memoize the read-only fallback form context to avoid recreating it every render
|
|
399
|
-
const readOnlyFormContext = useMemo<FormContext<M> | undefined>(() => {
|
|
400
|
-
if (formContext) return undefined; // not needed when real formContext exists
|
|
401
|
-
if (!entityId) return undefined;
|
|
402
|
-
const formexStub = createFormexStub<M>(usedEntity?.values ?? {} as M);
|
|
403
|
-
return {
|
|
404
|
-
entityId,
|
|
405
|
-
disabled: false,
|
|
406
|
-
readOnly: true,
|
|
407
|
-
openEntityMode: layout,
|
|
408
|
-
status: status,
|
|
409
|
-
values: usedEntity?.values ?? ({} as M),
|
|
410
|
-
setFieldValue: (key: string, value: unknown) => {
|
|
411
|
-
throw new Error("You can't update values in read only mode");
|
|
412
|
-
},
|
|
413
|
-
save: () => {
|
|
414
|
-
throw new Error("You can't save in read only mode");
|
|
415
|
-
},
|
|
416
|
-
submit: () => {
|
|
417
|
-
throw new Error("You can't submit in read only mode");
|
|
418
|
-
},
|
|
419
|
-
collection,
|
|
420
|
-
path: path,
|
|
421
|
-
entity: usedEntity,
|
|
422
|
-
savingError: undefined,
|
|
423
|
-
formex: formexStub
|
|
424
|
-
};
|
|
425
|
-
}, [formContext, entityId, layout, status, usedEntity, collection, path]);
|
|
426
|
-
|
|
427
|
-
const nonActionCustomViews = useMemo(() =>
|
|
428
|
-
resolvedEntityViews.filter(e => !e.includeActions),
|
|
429
|
-
[resolvedEntityViews]
|
|
430
|
-
);
|
|
431
|
-
|
|
432
|
-
const customViewsView: React.ReactNode[] | undefined = customViews && nonActionCustomViews
|
|
433
|
-
.map((customView) => {
|
|
434
|
-
|
|
435
|
-
if (!customView)
|
|
436
|
-
return null;
|
|
437
|
-
const Builder = resolveComponentRef<EntityCustomViewParams>(customView.Builder);
|
|
438
|
-
if (!Builder) {
|
|
439
|
-
console.error("INTERNAL: customView.Builder is not defined or could not be resolved");
|
|
440
|
-
return null;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (!entityId) {
|
|
444
|
-
return null;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// Only mount tabs that have been visited at least once
|
|
448
|
-
const isActive = activeTab === customView.key;
|
|
449
|
-
const hasBeenMounted = mountedTabsRef.current.has(customView.key);
|
|
450
|
-
if (!isActive && !hasBeenMounted) {
|
|
451
|
-
return null;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
const usedFormContext: FormContext<M> = formContext ?? readOnlyFormContext!;
|
|
455
|
-
|
|
456
|
-
return <div
|
|
457
|
-
className={cls(defaultBorderMixin,
|
|
458
|
-
"relative flex-1 w-full h-full overflow-auto",
|
|
459
|
-
{ "hidden": !isActive }
|
|
460
|
-
)}
|
|
461
|
-
key={`custom_view_${customView.key}`}
|
|
462
|
-
role="tabpanel">
|
|
463
|
-
<ErrorBoundary>
|
|
464
|
-
<Suspense fallback={<CircularProgressCenter />}>
|
|
465
|
-
{usedFormContext && <Builder
|
|
466
|
-
collection={collection}
|
|
467
|
-
parentCollectionSlugs={parentCollectionSlugs} parentEntityIds={parentEntityIds}
|
|
468
|
-
entity={usedEntity}
|
|
469
|
-
modifiedValues={usedFormContext?.formex?.values ?? usedEntity?.values}
|
|
470
|
-
formContext={usedFormContext as FormContext<Record<string, unknown>>}
|
|
471
|
-
/>}
|
|
472
|
-
</Suspense>
|
|
473
|
-
</ErrorBoundary>
|
|
474
|
-
</div>;
|
|
475
|
-
}).filter(Boolean);
|
|
476
|
-
|
|
477
|
-
const globalLoading = (dataLoading && !usedEntity) || (canEdit === undefined && (status === "existing" || status === "copy"));
|
|
478
|
-
|
|
479
|
-
/* ---- identity bar ---------------------------------------------------- */
|
|
480
|
-
|
|
481
|
-
const displayTitle = useEntityDisplayTitle({
|
|
482
|
-
collection,
|
|
483
|
-
entity: usedEntity,
|
|
484
|
-
values: (formContext?.values ?? usedEntity?.values) as Record<string, unknown> | undefined,
|
|
485
|
-
status
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
const formex = formContext?.formex;
|
|
489
|
-
const hasFormErrors = Boolean(formex && Object.keys(formex.errors).length > 0 && formex.submitCount > 0);
|
|
490
|
-
// Same rule the footer used: an untouched existing record has nothing to save.
|
|
491
|
-
const saveDisabled = Boolean(
|
|
492
|
-
!formContext
|
|
493
|
-
|| formContext.disabled
|
|
494
|
-
|| formex?.isSubmitting
|
|
495
|
-
|| (status === "existing" && !formex?.dirty)
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Hand this record's edit in progress to the layout about to replace this
|
|
500
|
-
* one — the split's "hide list", full screen's "show list", the side panel's
|
|
501
|
-
* "open full screen". None of them is a navigation *away* from the record:
|
|
502
|
-
* the same record is about to be shown by a different form, and what the
|
|
503
|
-
* user has typed belongs to the record, not to the pane it was typed in.
|
|
504
|
-
*
|
|
505
|
-
* Every control that changes layout has to call this, and none of them is
|
|
506
|
-
* blocked by the unsaved-changes prompt — the prompt asks about leaving, and
|
|
507
|
-
* these do not leave. What used to happen instead is that the edit reached
|
|
508
|
-
* the next layout only as its local-changes backup, which is the channel for
|
|
509
|
-
* a draft left behind by a closed tab: the record reopened *clean*, with a
|
|
510
|
-
* banner offering to apply changes the user had never walked away from.
|
|
511
|
-
*/
|
|
512
|
-
const carryEdit = useCallback(() => {
|
|
513
|
-
const carried = formex
|
|
514
|
-
? getEditHandoffValues<M>({
|
|
515
|
-
status,
|
|
516
|
-
dirty: formex.dirty,
|
|
517
|
-
values: formex.values,
|
|
518
|
-
touched: formex.touched,
|
|
519
|
-
storedValues: usedEntity?.values
|
|
520
|
-
})
|
|
521
|
-
: undefined;
|
|
522
|
-
if (!carried) return;
|
|
523
|
-
const key = (status === "new" || status === "copy") ? path + "#new" : path + "/" + entityId;
|
|
524
|
-
saveEntityToMemoryCache(key, carried);
|
|
525
|
-
}, [formex, status, path, entityId, usedEntity]);
|
|
526
|
-
|
|
527
|
-
const [inspectorTab, setInspectorTab] = useState<InspectorTab | null>(null);
|
|
528
|
-
// A save adds a revision, and the inspector can be open while it happens:
|
|
529
|
-
// Save lives in the identity bar, above the panel. Counting saves gives the
|
|
530
|
-
// history list something to react to.
|
|
531
|
-
const [savedCount, setSavedCount] = useState(0);
|
|
532
|
-
|
|
533
|
-
/* ---- record actions, in the bar's overflow menu ---------------------- */
|
|
534
|
-
|
|
535
|
-
const sideDialogContext = useSideDialogContext();
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Whether the save in flight was asked to dismiss the record afterwards.
|
|
539
|
-
*
|
|
540
|
-
* A ref, and for the reason {@link SideDialogs} keeps its own as one: the
|
|
541
|
-
* flag is raised and the form submitted in a single click handler, and the
|
|
542
|
-
* `onSaved` that has to act on it was captured before React could re-render.
|
|
543
|
-
* As state it read one save late — the first "save and close" saved and
|
|
544
|
-
* stayed open, and the *next* save closed the record.
|
|
545
|
-
*
|
|
546
|
-
* The side panel and the dialog use the side-dialog controller's flag,
|
|
547
|
-
* which their own close path consumes; this one covers the layouts that
|
|
548
|
-
* close by calling {@link onCloseRequest} instead.
|
|
549
|
-
*/
|
|
550
|
-
const pendingCloseRef = useRef(false);
|
|
551
|
-
|
|
552
|
-
// The split can close after a save the same way the overlays do — through
|
|
553
|
-
// the callback its layout supplies. Without one there is nothing to close.
|
|
554
|
-
const canCloseAfterSave = layout === "side_panel" || layout === "dialog"
|
|
555
|
-
|| (layout === "split" && Boolean(onCloseRequest));
|
|
556
|
-
|
|
557
|
-
const recordActions = useRecordActions({
|
|
558
|
-
collection,
|
|
559
|
-
path,
|
|
560
|
-
entity: usedEntity
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
const recordActionItems = usedEntity && recordActions.length
|
|
564
|
-
? recordActions.map((action, index) => {
|
|
565
|
-
const clickProps = {
|
|
566
|
-
view: "form" as const,
|
|
567
|
-
entity: usedEntity,
|
|
568
|
-
path,
|
|
569
|
-
collection,
|
|
570
|
-
context,
|
|
571
|
-
sidePanelController: adminContext.sidePanelController,
|
|
572
|
-
openEntityMode: layout,
|
|
573
|
-
navigateBack: navigateBack ?? (() => undefined),
|
|
574
|
-
formContext
|
|
575
|
-
};
|
|
576
|
-
const enabled = !action.isEnabled || action.isEnabled(clickProps as never);
|
|
577
|
-
return (
|
|
578
|
-
<MenuItem key={action.key ?? action.name ?? index}
|
|
579
|
-
disabled={!enabled}
|
|
580
|
-
onClick={() => action.onClick(clickProps as never)}>
|
|
581
|
-
{getIcon(action.icon, undefined, undefined, "smallest")}
|
|
582
|
-
{action.name}
|
|
583
|
-
</MenuItem>
|
|
584
|
-
);
|
|
585
|
-
})
|
|
586
|
-
: null;
|
|
587
|
-
|
|
588
|
-
// Plugin form actions (Autofill and friends) render inline in the bar; the
|
|
589
|
-
// form no longer has a footer to put them in.
|
|
590
|
-
const formPluginActions = useSlot("form.actions", formActionTopProps);
|
|
591
|
-
|
|
592
|
-
// A URL still naming the old `json`/`history` tab opens the inspector on
|
|
593
|
-
// that pane instead of 404-ing into the record with nothing shown.
|
|
594
|
-
useEffect(() => {
|
|
595
|
-
if (legacyInspectorTab) setInspectorTab(legacyInspectorTab);
|
|
596
|
-
}, [legacyInspectorTab]);
|
|
597
|
-
|
|
598
|
-
const subCollectionsViews = childViews && childViews.map(({ collection: subcollection }) => {
|
|
599
|
-
const subcollectionId = subcollection.slug;
|
|
600
|
-
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(getCollectionDataPath(subcollection))}` : undefined;
|
|
601
|
-
|
|
602
|
-
if (activeTab !== subcollectionId) return null;
|
|
603
|
-
return (
|
|
604
|
-
<div
|
|
605
|
-
className={"relative flex-1 h-full overflow-auto w-full"}
|
|
606
|
-
key={`subcol_${subcollectionId}`}
|
|
607
|
-
role="tabpanel">
|
|
608
|
-
|
|
609
|
-
{globalLoading && <CircularProgressCenter />}
|
|
610
|
-
|
|
611
|
-
{!globalLoading &&
|
|
612
|
-
(usedEntity && newFullPath
|
|
613
|
-
? <ResolvedCollectionView
|
|
614
|
-
path={newFullPath}
|
|
615
|
-
parentCollectionSlugs={[...parentCollectionSlugs, collection.slug]}
|
|
616
|
-
parentEntityIds={[...parentEntityIds, String(usedEntity?.id)]}
|
|
617
|
-
updateUrl={false}
|
|
618
|
-
{...subcollection}
|
|
619
|
-
openEntityMode={layout} />
|
|
620
|
-
: <div className="flex items-center justify-center w-full h-full p-3">
|
|
621
|
-
<Typography variant={"label"}>
|
|
622
|
-
You need to save your entity before
|
|
623
|
-
adding additional collections
|
|
624
|
-
</Typography>
|
|
625
|
-
</div>)
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
</div>
|
|
629
|
-
);
|
|
630
|
-
}).filter(Boolean);
|
|
631
|
-
|
|
632
|
-
const onSideTabClick = useCallback((value: string) => {
|
|
633
|
-
userHasChangedTab.current = true;
|
|
634
|
-
setSelectedTab(value);
|
|
635
|
-
if (status === "existing") {
|
|
636
|
-
onTabChange?.({
|
|
637
|
-
path: path,
|
|
638
|
-
entityId,
|
|
639
|
-
selectedTab: value === MAIN_TAB_VALUE ? undefined : value,
|
|
640
|
-
collection
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
}, [status, onTabChange, path, entityId, collection]);
|
|
644
|
-
|
|
645
|
-
// Resolve formView.Builder if provided
|
|
646
|
-
const formViewConfig = (collection as AdminCollection<M> & { formView?: FormViewConfig<M> }).formView;
|
|
647
|
-
const FormViewBuilder = formViewConfig?.Builder ? resolveComponentRef<EntityCustomViewParams>(formViewConfig.Builder as ComponentRef<EntityCustomViewParams>) : null;
|
|
648
|
-
const formViewIncludeActions = formViewConfig?.includeActions !== false;
|
|
649
|
-
|
|
650
|
-
// Without edit permission the record is rendered read-only — the same
|
|
651
|
-
// sections, spans and rail as the form, so losing the permission changes the
|
|
652
|
-
// controls and nothing else. The `h4` that used to head this repeated the
|
|
653
|
-
// collection name the identity bar above it already carries.
|
|
654
|
-
const entityReadOnlyView = !canEdit && entity ? (
|
|
655
|
-
FormViewBuilder && readOnlyFormContext
|
|
656
|
-
? <div className={cls(
|
|
657
|
-
"@container/form flex-1 flex flex-row w-full overflow-y-auto justify-center",
|
|
658
|
-
(canEdit || !mainViewVisible || selectedSecondaryForm) ? "hidden" : ""
|
|
659
|
-
)}>
|
|
660
|
-
{/* Same widths as the default form (see EntityForm). */}
|
|
661
|
-
<div className={"w-full max-w-3xl @7xl/form:max-w-4xl 2xl:max-w-4xl flex flex-col pt-6 pb-16 px-5 sm:px-8"}>
|
|
662
|
-
<ErrorBoundary>
|
|
663
|
-
<Suspense fallback={<CircularProgressCenter />}>
|
|
664
|
-
<FormViewBuilder
|
|
665
|
-
collection={collection}
|
|
666
|
-
parentCollectionSlugs={parentCollectionSlugs} parentEntityIds={parentEntityIds}
|
|
667
|
-
entity={usedEntity}
|
|
668
|
-
modifiedValues={usedEntity?.values}
|
|
669
|
-
formContext={readOnlyFormContext as FormContext<Record<string, unknown>>}
|
|
670
|
-
/>
|
|
671
|
-
</Suspense>
|
|
672
|
-
</ErrorBoundary>
|
|
673
|
-
</div>
|
|
674
|
-
</div>
|
|
675
|
-
: <EntityViewBinding
|
|
676
|
-
className={cls((canEdit || !mainViewVisible || selectedSecondaryForm) ? "hidden" : "")}
|
|
677
|
-
entity={entity}
|
|
678
|
-
path={path}
|
|
679
|
-
collection={collection}
|
|
680
|
-
asPage
|
|
681
|
-
openEntityMode={layout}
|
|
682
|
-
formContext={readOnlyFormContext}/>
|
|
683
|
-
) : null;
|
|
684
|
-
|
|
685
|
-
const entityView = FormViewBuilder ? (
|
|
686
|
-
// formView.Builder replaces the default form
|
|
687
|
-
<div className={cls(
|
|
688
|
-
"relative flex-1 w-full h-full overflow-auto",
|
|
689
|
-
(!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : ""
|
|
690
|
-
)}>
|
|
691
|
-
<ErrorBoundary>
|
|
692
|
-
<Suspense fallback={<CircularProgressCenter />}>
|
|
693
|
-
{(formContext ?? readOnlyFormContext) && <FormViewBuilder
|
|
694
|
-
collection={collection}
|
|
695
|
-
parentCollectionSlugs={parentCollectionSlugs} parentEntityIds={parentEntityIds}
|
|
696
|
-
entity={usedEntity}
|
|
697
|
-
modifiedValues={(formContext ?? readOnlyFormContext)?.formex?.values ?? usedEntity?.values}
|
|
698
|
-
formContext={(formContext ?? readOnlyFormContext) as FormContext<Record<string, unknown>>}
|
|
699
|
-
/>}
|
|
700
|
-
</Suspense>
|
|
701
|
-
</ErrorBoundary>
|
|
702
|
-
</div>
|
|
703
|
-
) : (
|
|
704
|
-
<ResolvedEntityForm<M>
|
|
705
|
-
collection={collection}
|
|
706
|
-
path={path}
|
|
707
|
-
entityId={entityId ?? usedEntity?.id}
|
|
708
|
-
onValuesModified={onValuesModified}
|
|
709
|
-
entity={entity}
|
|
710
|
-
initialDirtyValues={initialDirtyValues}
|
|
711
|
-
openEntityMode={layout}
|
|
712
|
-
// Save and Discard live in the identity bar now, where they stay
|
|
713
|
-
// visible while the form scrolls. The form's own footer would be a
|
|
714
|
-
// second copy of them.
|
|
715
|
-
showDefaultActions={false}
|
|
716
|
-
initialStatus={status}
|
|
717
|
-
className={cls((!mainViewVisible || !canEdit) && !selectedSecondaryForm ? "hidden" : "", formProps?.className)}
|
|
718
|
-
EntityFormActionsComponent={ResolvedFormActions as React.FC<typeof ResolvedFormActions extends React.ComponentType<infer P> ? P : never>}
|
|
719
|
-
disabled={!canEdit}
|
|
720
|
-
navigateBack={navigateBack}
|
|
721
|
-
{...formProps}
|
|
722
|
-
onEntityChange={(entity) => {
|
|
723
|
-
setUsedEntity(entity);
|
|
724
|
-
formProps?.onEntityChange?.(entity);
|
|
725
|
-
}}
|
|
726
|
-
onStatusChange={(status) => {
|
|
727
|
-
setStatus(status);
|
|
728
|
-
formProps?.onStatusChange?.(status);
|
|
729
|
-
}}
|
|
730
|
-
onFormContextReady={(formContext) => {
|
|
731
|
-
setFormContext(formContext);
|
|
732
|
-
formProps?.onFormContextReady?.(formContext);
|
|
733
|
-
}}
|
|
734
|
-
onSaved={(params) => {
|
|
735
|
-
setSavedCount(count => count + 1);
|
|
736
|
-
const res = {
|
|
737
|
-
...params,
|
|
738
|
-
selectedTab: MAIN_TAB_VALUE === activeTab ? undefined : activeTab
|
|
739
|
-
};
|
|
740
|
-
// Two caller-supplied callbacks for one save, and either may
|
|
741
|
-
// navigate — the layouts' `onSaved` all do. Where both do, the
|
|
742
|
-
// second one's destination is what survives (docs/bug-classes.md
|
|
743
|
-
// #28), so `formProps.onSaved` is deliberately last: it belongs
|
|
744
|
-
// to whoever embedded this view, which outranks the layout.
|
|
745
|
-
onSaved?.(res);
|
|
746
|
-
formProps?.onSaved?.(res);
|
|
747
|
-
|
|
748
|
-
// After `onSaved`, never instead of it: the layout's own handler
|
|
749
|
-
// is what lowers its dirty flag, and closing is a navigation the
|
|
750
|
-
// unsaved-changes blocker would otherwise stop on values that
|
|
751
|
-
// are already stored.
|
|
752
|
-
//
|
|
753
|
-
// And after this handler returns, not inside it. The layouts
|
|
754
|
-
// that close this way close by navigating, and they have just
|
|
755
|
-
// navigated themselves — the split leaves `…/edit` for the
|
|
756
|
-
// record's own URL. Deferring keeps the close unambiguously
|
|
757
|
-
// last, which is what decides it: of two navigations raised for
|
|
758
|
-
// one action the final one survives, measured in
|
|
759
|
-
// `router_two_navigations_one_handler.test.tsx`. It also lets
|
|
760
|
-
// the state those handlers just set settle before the blocker
|
|
761
|
-
// reads it. See docs/bug-classes.md #28.
|
|
762
|
-
if (pendingCloseRef.current) {
|
|
763
|
-
pendingCloseRef.current = false;
|
|
764
|
-
setTimeout(() => onCloseRequest?.(), 0);
|
|
765
|
-
}
|
|
766
|
-
}}
|
|
767
|
-
Builder={resolveComponentRef(selectedSecondaryForm?.Builder as ComponentRef<EntityCustomViewParams<M>> | undefined) as React.ComponentType<EntityCustomViewParams<M>> | undefined}
|
|
768
|
-
/>
|
|
769
|
-
);
|
|
770
|
-
|
|
771
|
-
const subcollectionTabs = subcollections && subcollections.map((subcollection) => {
|
|
772
|
-
const icon = getIcon(subcollection.icon, undefined, undefined, "smallest");
|
|
773
|
-
return (
|
|
774
|
-
<Tab
|
|
775
|
-
className={cls(ENTITY_VIEW_TAB_CLASS, ENTITY_VIEW_TAB_LABEL_CLASS)}
|
|
776
|
-
value={subcollection.slug}
|
|
777
|
-
key={`entity_detail_collection_tab_${subcollection.name}`}>
|
|
778
|
-
<span className="flex items-center gap-1.5">
|
|
779
|
-
{icon}
|
|
780
|
-
{subcollection.name}
|
|
781
|
-
</span>
|
|
782
|
-
</Tab>
|
|
783
|
-
);
|
|
784
|
-
});
|
|
785
|
-
|
|
786
|
-
const customViewTabsStart = resolvedEntityViews.filter(view => view.position === "start")
|
|
787
|
-
.map((view) => {
|
|
788
|
-
const icon = getIcon(view.icon, undefined, undefined, "smallest");
|
|
789
|
-
return (
|
|
790
|
-
<Tab
|
|
791
|
-
className={cls(ENTITY_VIEW_TAB_CLASS, !view.tabComponent && ENTITY_VIEW_TAB_LABEL_CLASS)}
|
|
792
|
-
value={view.key}
|
|
793
|
-
key={`entity_detail_collection_tab_${view.name}`}>
|
|
794
|
-
{view.tabComponent ?? (
|
|
795
|
-
<span className="flex items-center gap-1.5">
|
|
796
|
-
{icon}
|
|
797
|
-
{view.name}
|
|
798
|
-
</span>
|
|
799
|
-
)}
|
|
800
|
-
</Tab>
|
|
801
|
-
);
|
|
802
|
-
});
|
|
803
|
-
const customViewTabsEnd = resolvedEntityViews.filter(view => !view.position || view.position === "end")
|
|
804
|
-
.map((view) => {
|
|
805
|
-
const icon = getIcon(view.icon, undefined, undefined, "smallest");
|
|
806
|
-
return (
|
|
807
|
-
<Tab
|
|
808
|
-
className={cls(ENTITY_VIEW_TAB_CLASS, !view.tabComponent && ENTITY_VIEW_TAB_LABEL_CLASS)}
|
|
809
|
-
value={view.key}
|
|
810
|
-
key={`entity_detail_collection_tab_${view.name}`}>
|
|
811
|
-
{view.tabComponent ?? (
|
|
812
|
-
<span className="flex items-center gap-1.5">
|
|
813
|
-
{icon}
|
|
814
|
-
{view.name}
|
|
815
|
-
</span>
|
|
816
|
-
)}
|
|
817
|
-
</Tab>
|
|
818
|
-
);
|
|
819
|
-
});
|
|
820
|
-
|
|
821
|
-
const fullScreenButton = !barActions && // Not in split: the list panel beside it carries the close control,
|
|
822
|
-
// and an expand button there competes with it.
|
|
823
|
-
(layout === "side_panel" || layout === "dialog") && entityId ? (
|
|
824
|
-
<Tooltip title={"Open full screen"}>
|
|
825
|
-
<IconButton
|
|
826
|
-
size="small"
|
|
827
|
-
onClick={() => {
|
|
828
|
-
carryEdit();
|
|
829
|
-
const editSuffix = collection.defaultEntityAction === "view" ? "/edit" : "";
|
|
830
|
-
const entityUrl = urlController.buildUrlCollectionPath(`${path}/${entityId}${editSuffix}`);
|
|
831
|
-
navigate(`${entityUrl}#full`);
|
|
832
|
-
}}
|
|
833
|
-
>
|
|
834
|
-
<Maximize2Icon size={iconSize.smallest} />
|
|
835
|
-
</IconButton>
|
|
836
|
-
</Tooltip>
|
|
837
|
-
) : null;
|
|
838
|
-
|
|
839
|
-
let result = <div className="relative flex flex-col h-full w-full bg-white dark:bg-surface-800">
|
|
840
|
-
|
|
841
|
-
<EntityIdentityBar
|
|
842
|
-
collection={collection as AdminCollection}
|
|
843
|
-
collectionUrl={layout === "full_screen" || layout === "split"
|
|
844
|
-
? withListState(urlController.buildUrlCollectionPath(path))
|
|
845
|
-
: undefined}
|
|
846
|
-
title={displayTitle}
|
|
847
|
-
entityId={status === "existing" ? entityId : undefined}
|
|
848
|
-
status={status}
|
|
849
|
-
dirty={Boolean(formContext?.formex?.dirty)}
|
|
850
|
-
saving={Boolean(formContext?.isSaving)}
|
|
851
|
-
hasErrors={hasFormErrors}
|
|
852
|
-
saveDisabled={!canEdit || saveDisabled}
|
|
853
|
-
// Full screen replaces the collection entirely, leaving browser Back
|
|
854
|
-
// as the only route to it. The detail view has carried this arrow
|
|
855
|
-
// for that reason; the edit view is reached the same way. Where the
|
|
856
|
-
// list can be unfolded back beside the record, that chevron reaches
|
|
857
|
-
// the same collection and the arrow beside it is noise.
|
|
858
|
-
onBack={layout === "full_screen" && !onShowList
|
|
859
|
-
? () => navigate(withListState(urlController.buildUrlCollectionPath(path)))
|
|
860
|
-
: undefined}
|
|
861
|
-
onSave={canEdit && formContext ? () => {
|
|
862
|
-
sideDialogContext.setPendingClose?.(false);
|
|
863
|
-
pendingCloseRef.current = false;
|
|
864
|
-
formContext.submit();
|
|
865
|
-
} : undefined}
|
|
866
|
-
onSaveAndClose={canEdit && formContext && canCloseAfterSave ? () => {
|
|
867
|
-
// Lowered again once the submit settles. A submit the form
|
|
868
|
-
// rejects never reaches `onSaved`, so nothing would consume the
|
|
869
|
-
// flag and the *next* save — a keyboard ⌘S, say — would close
|
|
870
|
-
// the panel out from under an edit nobody asked to finish. A
|
|
871
|
-
// save that succeeds has already closed by the time this runs.
|
|
872
|
-
if (layout === "split") {
|
|
873
|
-
pendingCloseRef.current = true;
|
|
874
|
-
Promise.resolve(formContext.submit())
|
|
875
|
-
.finally(() => { pendingCloseRef.current = false; });
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
sideDialogContext.setPendingClose?.(true);
|
|
879
|
-
Promise.resolve(formContext.submit())
|
|
880
|
-
.finally(() => sideDialogContext.setPendingClose?.(false));
|
|
881
|
-
} : undefined}
|
|
882
|
-
// Welded to Save in the split, its own button in the overlays.
|
|
883
|
-
saveAndClosePlacement={layout === "split" ? "menu" : "button"}
|
|
884
|
-
onClose={onCloseRequest}
|
|
885
|
-
// Unlike the form's own Discard, this one does not stop to ask —
|
|
886
|
-
// so it has to be reversible. See {@link useUndoableDiscard}.
|
|
887
|
-
onDiscard={canEdit && formContext
|
|
888
|
-
? () => discard(formContext.formex, status)
|
|
889
|
-
: undefined}
|
|
890
|
-
onInspect={includeJsonView ? () => setInspectorTab("json") : undefined}
|
|
891
|
-
onViewHistory={includeHistoryView ? () => setInspectorTab("history") : undefined}
|
|
892
|
-
externalLink={usedEntity
|
|
893
|
-
? customizationController?.entityLinkBuilder?.({ entity: usedEntity })
|
|
894
|
-
: undefined}
|
|
895
|
-
recordActions={recordActionItems}
|
|
896
|
-
pluginActions={formPluginActions}
|
|
897
|
-
leading={<>
|
|
898
|
-
{barActionsStart}
|
|
899
|
-
{/* Split view: closing the list is opening this record
|
|
900
|
-
full screen, and the control for it belongs at this
|
|
901
|
-
bar's leading edge. Full screen carries its mirror.
|
|
902
|
-
Both change layout, so both hand the edit over. */}
|
|
903
|
-
{layout === "split" && <SplitListCloseButton onBeforeHide={carryEdit}/>}
|
|
904
|
-
{layout === "full_screen" && onShowList && <SplitListShowButton onClick={() => {
|
|
905
|
-
carryEdit();
|
|
906
|
-
onShowList();
|
|
907
|
-
}}/>}
|
|
908
|
-
</>}
|
|
909
|
-
trailing={<>
|
|
910
|
-
{pluginActionsTop}
|
|
911
|
-
{fullScreenButton}
|
|
912
|
-
{barActions?.({
|
|
913
|
-
path,
|
|
914
|
-
entityId,
|
|
915
|
-
values: formContext?.values ?? usedEntity?.values ?? {},
|
|
916
|
-
status,
|
|
917
|
-
dirty: Boolean(formContext?.formex?.dirty),
|
|
918
|
-
carryEdit
|
|
919
|
-
})}
|
|
920
|
-
</>}
|
|
921
|
-
/>
|
|
922
|
-
|
|
923
|
-
{/* Destinations only: the record is the page, so its tab leads rather
|
|
924
|
-
than sitting third behind two unlabelled developer tools. The row
|
|
925
|
-
disappears entirely when there is nowhere else to go. */}
|
|
926
|
-
{hasAdditionalViews && <div className={cls(
|
|
927
|
-
"h-10 shrink-0 flex items-stretch border-b px-2 min-w-0",
|
|
928
|
-
ENTITY_VIEW_TAB_BAR_CLASS,
|
|
929
|
-
defaultBorderMixin
|
|
930
|
-
)}>
|
|
931
|
-
<Tabs
|
|
932
|
-
className={"!w-full items-stretch"}
|
|
933
|
-
innerClassName={"h-full"}
|
|
934
|
-
variant={"boxy"}
|
|
935
|
-
value={activeTab}
|
|
936
|
-
onValueChange={(value) => {
|
|
937
|
-
onSideTabClick(value);
|
|
938
|
-
}}>
|
|
939
|
-
|
|
940
|
-
{/* Before the record, not after it.
|
|
941
|
-
|
|
942
|
-
`position: "start"` used to mean "first among the custom
|
|
943
|
-
views", which put it after a tab it was already after: the
|
|
944
|
-
record's own tab is drawn here unconditionally, so "start"
|
|
945
|
-
and "end" only ever ordered the custom views against each
|
|
946
|
-
other, and no collection could open with anything but the
|
|
947
|
-
form on the left.
|
|
948
|
-
|
|
949
|
-
A cover — a read-only summary of the record, the thing an
|
|
950
|
-
operator opens a row to see — belongs in front of the form
|
|
951
|
-
that edits it. That is what `defaultSelectedView` already
|
|
952
|
-
allowed, and the strip contradicted it by drawing the
|
|
953
|
-
selected tab second. Views that say nothing still land after
|
|
954
|
-
the record, so nothing moves for a collection that never
|
|
955
|
-
asked. */}
|
|
956
|
-
{customViewTabsStart}
|
|
957
|
-
|
|
958
|
-
<Tab value={MAIN_TAB_VALUE}
|
|
959
|
-
className={cls(ENTITY_VIEW_TAB_CLASS, ENTITY_VIEW_TAB_LABEL_CLASS)}>
|
|
960
|
-
<span className="flex items-center gap-1.5">
|
|
961
|
-
{getIcon(collection.icon, undefined, undefined, "smallest")}
|
|
962
|
-
{collection.singularName ?? collection.name}
|
|
963
|
-
</span>
|
|
964
|
-
</Tab>
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
{customViewTabsEnd}
|
|
968
|
-
|
|
969
|
-
{subcollectionTabs}
|
|
970
|
-
</Tabs>
|
|
971
|
-
</div>}
|
|
972
|
-
|
|
973
|
-
{/* The record and the inspector share the space below the bar rather
|
|
974
|
-
than stacking, so the form stays reachable while the panel is open.
|
|
975
|
-
`min-w-0` keeps a wide form from pushing the panel off the edge. */}
|
|
976
|
-
<div className={"flex-1 min-h-0 flex"}>
|
|
977
|
-
|
|
978
|
-
<div className={"flex-1 min-w-0 flex flex-col"}>
|
|
979
|
-
{globalLoading
|
|
980
|
-
? <EntityFormSkeleton collection={collection} />
|
|
981
|
-
: <>
|
|
982
|
-
{entityReadOnlyView}
|
|
983
|
-
{entityView}
|
|
984
|
-
</>}
|
|
985
|
-
|
|
986
|
-
{customViewsView}
|
|
987
|
-
|
|
988
|
-
{subCollectionsViews}
|
|
989
|
-
</div>
|
|
990
|
-
|
|
991
|
-
<EntityInspector
|
|
992
|
-
tab={inspectorTab}
|
|
993
|
-
onTabChange={setInspectorTab}
|
|
994
|
-
onClose={() => setInspectorTab(null)}
|
|
995
|
-
collection={collection as AdminCollection}
|
|
996
|
-
entity={usedEntity as Entity<Record<string, unknown>> | undefined}
|
|
997
|
-
formContext={formContext as FormContext<Record<string, unknown>> | undefined}
|
|
998
|
-
values={formContext?.values ?? usedEntity?.values}
|
|
999
|
-
refreshToken={savedCount}
|
|
1000
|
-
includeHistory={includeHistoryView}/>
|
|
1001
|
-
</div>
|
|
1002
|
-
|
|
1003
|
-
</div>;
|
|
1004
|
-
|
|
1005
|
-
if (plugins && plugins.length > 0) {
|
|
1006
|
-
result = (
|
|
1007
|
-
<PluginProviderStack
|
|
1008
|
-
plugins={plugins}
|
|
1009
|
-
scope="form"
|
|
1010
|
-
scopeProps={{
|
|
1011
|
-
status,
|
|
1012
|
-
path,
|
|
1013
|
-
collection,
|
|
1014
|
-
entity: usedEntity,
|
|
1015
|
-
context,
|
|
1016
|
-
formContext
|
|
1017
|
-
}}>
|
|
1018
|
-
{result}
|
|
1019
|
-
</PluginProviderStack>
|
|
1020
|
-
);
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
return result;
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
function EntityFormSkeleton({ collection }: { collection: AdminCollection }) {
|
|
1027
|
-
return (
|
|
1028
|
-
<div className="flex-1 flex flex-row w-full overflow-y-auto justify-center">
|
|
1029
|
-
<div className="relative flex flex-row max-w-4xl lg:max-w-3xl xl:max-w-4xl 2xl:max-w-6xl w-full h-fit">
|
|
1030
|
-
<div className="flex flex-col w-full pt-12 pb-16 px-4 sm:px-8 md:px-10">
|
|
1031
|
-
|
|
1032
|
-
<div className="flex flex-row gap-4 self-end sticky top-4 z-10">
|
|
1033
|
-
<Skeleton height={24} className="w-8 rounded-full" />
|
|
1034
|
-
</div>
|
|
1035
|
-
|
|
1036
|
-
<div className="w-full flex flex-col items-start my-4 lg:my-6">
|
|
1037
|
-
<div className={`py-1 my-4 w-2/3 ${collection.hideIdFromForm ? "mb-6" : ""}`}>
|
|
1038
|
-
<Skeleton height={28} className="w-full rounded-md" />
|
|
1039
|
-
</div>
|
|
1040
|
-
<Skeleton height={32} className="w-full rounded-md" />
|
|
1041
|
-
</div>
|
|
1042
|
-
|
|
1043
|
-
<div className="mt-12 flex flex-col gap-8">
|
|
1044
|
-
<div className="flex flex-wrap gap-x-4 w-full space-y-8">
|
|
1045
|
-
|
|
1046
|
-
<div className="relative w-full">
|
|
1047
|
-
<Skeleton height={60} className="w-full rounded-md" />
|
|
1048
|
-
</div>
|
|
1049
|
-
|
|
1050
|
-
<div className="relative w-full">
|
|
1051
|
-
<Skeleton height={60} className="w-full rounded-md" />
|
|
1052
|
-
</div>
|
|
1053
|
-
|
|
1054
|
-
<div className="relative w-full">
|
|
1055
|
-
<Skeleton height={60} className="w-full rounded-md" />
|
|
1056
|
-
</div>
|
|
1057
|
-
|
|
1058
|
-
<div className="relative w-full">
|
|
1059
|
-
<Skeleton height={60} className="w-full rounded-md" />
|
|
1060
|
-
</div>
|
|
1061
|
-
|
|
1062
|
-
</div>
|
|
1063
|
-
</div>
|
|
1064
|
-
</div>
|
|
1065
|
-
</div>
|
|
1066
|
-
</div>
|
|
1067
|
-
);
|
|
1068
|
-
}
|
|
1069
|
-
|