@rebasepro/admin 0.6.1 → 0.8.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/dist/{CollectionEditorDialog-HNXGxXdO.js → CollectionEditorDialog-BO_aQ-Z4.js} +236 -155
- package/dist/CollectionEditorDialog-BO_aQ-Z4.js.map +1 -0
- package/dist/{PropertyEditView-C2wp8kQ9.js → PropertyEditView-DKzOpMjR.js} +190 -50
- package/dist/PropertyEditView-DKzOpMjR.js.map +1 -0
- package/dist/{CollectionsStudioView-B1mN33GB.js → RouterCollectionsStudioView-BQTIExjA.js} +61 -39
- package/dist/RouterCollectionsStudioView-BQTIExjA.js.map +1 -0
- package/dist/collection_editor/_cms_internals.d.ts +1 -1
- package/dist/collection_editor/api/generateCollectionApi.d.ts +3 -2
- package/dist/collection_editor/extensibility_types.d.ts +135 -0
- package/dist/collection_editor/index.d.ts +6 -0
- package/dist/collection_editor/serializable_types.d.ts +370 -0
- package/dist/collection_editor/serializable_utils.d.ts +50 -0
- package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +3 -3
- package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +12 -1
- package/dist/collection_editor/ui/collection_editor/CollectionStudioView.d.ts +13 -1
- package/dist/collection_editor/ui/collection_editor/CollectionsStudioView.d.ts +24 -2
- package/dist/collection_editor/ui/collection_editor/DisplaySettingsForm.d.ts +2 -1
- package/dist/collection_editor/ui/collection_editor/GeneralSettingsForm.d.ts +4 -1
- package/dist/collection_editor/ui/collection_editor/PropertyEditView.d.ts +12 -1
- package/dist/collection_editor/ui/collection_editor/RouterCollectionStudioView.d.ts +15 -0
- package/dist/collection_editor/ui/collection_editor/RouterCollectionsStudioView.d.ts +14 -0
- package/dist/collection_editor/useJsonCollectionsConfigController.d.ts +37 -0
- package/dist/collection_editor/useSafeSnackbarController.d.ts +2 -0
- package/dist/collection_editor_ui.d.ts +4 -0
- package/dist/collection_editor_ui.js +33 -4
- package/dist/collection_editor_ui.js.map +1 -0
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +1 -1
- package/dist/components/EntityCollectionTable/index.d.ts +1 -1
- package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +3 -3
- package/dist/components/EntityCollectionView/hooks/useKanbanDragAndDrop.d.ts +3 -3
- package/dist/components/EntityEditView.d.ts +3 -2
- package/dist/components/RebaseCMS.d.ts +1 -1
- package/dist/components/SelectableTable/SelectionStore.d.ts +12 -21
- package/dist/components/field_configs.d.ts +1 -1
- package/dist/editor/editor.d.ts +0 -6
- package/dist/editor.js +1 -3
- package/dist/editor.js.map +1 -1
- package/dist/form/EntityForm.d.ts +10 -9
- package/dist/form/EntityFormBinding.d.ts +27 -0
- package/dist/form/components/StorageUploadProgress.d.ts +6 -1
- package/dist/form/form_utils.d.ts +8 -0
- package/dist/form/index.d.ts +5 -2
- package/dist/{history-BO6VT85a.js → history-B3cqBzEV.js} +2 -2
- package/dist/{history-BO6VT85a.js.map → history-B3cqBzEV.js.map} +1 -1
- package/dist/hooks/navigation/contexts/CollectionRegistryContext.d.ts +1 -1
- package/dist/hooks/navigation/useAsyncResolver.d.ts +26 -0
- package/dist/hooks/navigation/useBuildCollectionRegistryController.d.ts +2 -0
- package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
- package/dist/hooks/navigation/utils.d.ts +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -2
- package/dist/preview/components/StorageThumbnail.d.ts +3 -1
- package/dist/routes/CustomViewRoute.d.ts +2 -1
- package/dist/{src-BYniefVu.js → src-cxV1ODRG.js} +1731 -1710
- package/dist/src-cxV1ODRG.js.map +1 -0
- package/dist/types/components/EntityFormActionsProps.d.ts +1 -1
- package/dist/types/components/EntityFormProps.d.ts +59 -0
- package/dist/types/components/PropertyPreviewProps.d.ts +2 -1
- package/dist/types/fields.d.ts +10 -7
- package/dist/util/dataControllerAdapter.d.ts +30 -0
- package/dist/util/navigation_utils.d.ts +1 -1
- package/dist/util/propertyConfigMapper.d.ts +17 -0
- package/package.json +8 -8
- package/src/collection_editor/_cms_internals.ts +1 -1
- package/src/collection_editor/api/generateCollectionApi.ts +3 -2
- package/src/collection_editor/extensibility_types.ts +168 -0
- package/src/collection_editor/index.ts +46 -0
- package/src/collection_editor/pgColumnToProperty.ts +2 -2
- package/src/collection_editor/serializable_types.ts +474 -0
- package/src/collection_editor/serializable_utils.ts +576 -0
- package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +6 -6
- package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +170 -89
- package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +95 -78
- package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +23 -20
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +79 -27
- package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +9 -7
- package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +22 -5
- package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +3 -3
- package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +154 -18
- package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
- package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +1 -0
- package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +44 -0
- package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +41 -0
- package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +4 -3
- package/src/collection_editor/ui/collection_editor/properties/StoragePropertyField.tsx +35 -1
- package/src/collection_editor/ui/collection_editor/templates/blog_template.ts +1 -1
- package/src/collection_editor/ui/collection_editor/utils/supported_fields.tsx +0 -1
- package/src/collection_editor/ui/collection_editor/utils/update_property_for_widget.ts +7 -29
- package/src/collection_editor/useJsonCollectionsConfigController.tsx +286 -0
- package/src/collection_editor/useSafeSnackbarController.ts +9 -0
- package/src/collection_editor_ui.ts +6 -0
- package/src/components/DefaultDrawer.tsx +14 -4
- package/src/components/DrawerNavigationGroup.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +11 -9
- package/src/components/EntityCollectionTable/fields/TableMultipleRelationField.tsx +2 -1
- package/src/components/EntityCollectionTable/fields/TableRelationField.tsx +2 -1
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +23 -20
- package/src/components/EntityCollectionTable/index.tsx +1 -1
- package/src/components/EntityCollectionTable/table_bindings.tsx +24 -44
- package/src/components/EntityCollectionView/EntityBoardCard.tsx +6 -6
- package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +13 -12
- package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +40 -190
- package/src/components/EntityCollectionView/EntityCollectionListView.tsx +66 -184
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +8 -25
- package/src/components/EntityCollectionView/hooks/useCollectionInlineEditor.ts +1 -1
- package/src/components/EntityCollectionView/hooks/useKanbanDragAndDrop.ts +6 -6
- package/src/components/EntityCollectionView/useBoardDataController.tsx +12 -40
- package/src/components/EntityCollectionView/useEntityPreviewSlots.ts +3 -3
- package/src/components/EntityDetailView.tsx +2 -2
- package/src/components/EntityEditView.tsx +7 -6
- package/src/components/EntityEditViewFormActions.tsx +35 -29
- package/src/components/HomePage/ContentHomePage.tsx +14 -5
- package/src/components/RebaseCMS.tsx +3 -3
- package/src/components/RebaseNavigation.tsx +74 -9
- package/src/components/RelationSelector.tsx +2 -2
- package/src/components/SelectableTable/SelectableTable.tsx +13 -5
- package/src/components/SelectableTable/SelectionStore.ts +18 -48
- package/src/components/SideDialogs.tsx +0 -7
- package/src/components/common/default_entity_actions.tsx +22 -11
- package/src/components/field_configs.tsx +0 -14
- package/src/contexts/BreacrumbsContext.tsx +15 -6
- package/src/editor/editor.tsx +4 -7
- package/src/editor/selectors/ai-selector.tsx +0 -1
- package/src/form/EntityForm.tsx +191 -428
- package/src/form/EntityFormActions.tsx +35 -49
- package/src/form/EntityFormBinding.tsx +323 -0
- package/src/form/components/FormLayout.tsx +1 -1
- package/src/form/components/StorageUploadProgress.tsx +9 -2
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +12 -1
- package/src/form/field_bindings/MultipleRelationFieldBinding.tsx +4 -4
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -2
- package/src/form/field_bindings/RelationFieldBinding.tsx +5 -7
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +19 -15
- package/src/form/form_utils.ts +163 -0
- package/src/form/index.tsx +14 -6
- package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +2 -2
- package/src/hooks/navigation/contexts/NavigationStateContext.tsx +2 -2
- package/src/hooks/navigation/contexts/UrlContext.tsx +1 -2
- package/src/hooks/navigation/useAsyncResolver.ts +100 -0
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +15 -8
- package/src/hooks/navigation/useBuildNavigationStateController.tsx +12 -8
- package/src/hooks/navigation/useNavigationResolution.ts +33 -0
- package/src/hooks/navigation/useResolvedCollections.ts +29 -71
- package/src/hooks/navigation/useResolvedViews.tsx +40 -70
- package/src/hooks/navigation/useTopLevelNavigation.ts +20 -16
- package/src/hooks/navigation/utils.ts +7 -4
- package/src/index.ts +6 -1
- package/src/preview/PropertyPreview.tsx +1 -16
- package/src/preview/components/StorageThumbnail.tsx +24 -7
- package/src/routes/CustomViewRoute.tsx +14 -6
- package/src/types/components/EntityFormActionsProps.tsx +1 -1
- package/src/types/components/EntityFormProps.tsx +68 -0
- package/src/types/components/PropertyPreviewProps.tsx +1 -1
- package/src/types/fields.tsx +9 -8
- package/src/util/dataControllerAdapter.ts +93 -0
- package/src/util/navigation_utils.ts +1 -1
- package/src/util/previews.ts +20 -0
- package/src/util/propertyConfigMapper.ts +134 -0
- package/src/util/property_utils.tsx +1 -2
- package/dist/CollectionEditorDialog-HNXGxXdO.js.map +0 -1
- package/dist/CollectionsStudioView-B1mN33GB.js.map +0 -1
- package/dist/PropertyEditView-C2wp8kQ9.js.map +0 -1
- package/dist/components/EntityCollectionTable/fields/VirtualTableDateField.d.ts +0 -12
- package/dist/components/EntityCollectionTable/fields/VirtualTableInput.d.ts +0 -10
- package/dist/components/EntityCollectionTable/fields/VirtualTableNumberInput.d.ts +0 -9
- package/dist/components/EntityCollectionTable/fields/VirtualTableSwitch.d.ts +0 -8
- package/dist/components/EntityCollectionView/Board.d.ts +0 -3
- package/dist/components/EntityCollectionView/BoardColumn.d.ts +0 -42
- package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +0 -9
- package/dist/components/EntityCollectionView/BoardSortableList.d.ts +0 -14
- package/dist/components/EntityCollectionView/board_types.d.ts +0 -105
- package/dist/form/field_bindings/ReferenceAsStringFieldBinding.d.ts +0 -11
- package/dist/hooks/navigation/useNavigationRegistry.d.ts +0 -12
- package/dist/hooks/navigation/useNavigationURLs.d.ts +0 -11
- package/dist/src-BYniefVu.js.map +0 -1
- package/src/components/EntityCollectionTable/fields/VirtualTableDateField.tsx +0 -39
- package/src/components/EntityCollectionTable/fields/VirtualTableInput.tsx +0 -90
- package/src/components/EntityCollectionTable/fields/VirtualTableNumberInput.tsx +0 -83
- package/src/components/EntityCollectionTable/fields/VirtualTableSwitch.tsx +0 -32
- package/src/components/EntityCollectionView/Board.tsx +0 -475
- package/src/components/EntityCollectionView/BoardColumn.tsx +0 -165
- package/src/components/EntityCollectionView/BoardColumnTitle.tsx +0 -46
- package/src/components/EntityCollectionView/BoardSortableList.tsx +0 -177
- package/src/components/EntityCollectionView/board_types.ts +0 -113
- package/src/form/field_bindings/ReferenceAsStringFieldBinding.tsx +0 -141
- package/src/hooks/navigation/useNavigationRegistry.ts +0 -164
- package/src/hooks/navigation/useNavigationURLs.ts +0 -56
|
@@ -2,14 +2,14 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
|
|
|
2
2
|
import * as React$1 from "react";
|
|
3
3
|
import React, { Suspense, createContext, createElement, forwardRef, lazy, memo, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { deepEqual } from "fast-equals";
|
|
5
|
-
import { Alert, AlertCircleIcon, AlertTriangleIcon, AlignLeftIcon, ArrowLeftIcon, ArrowRightToLineIcon, Avatar, Badge,
|
|
6
|
-
import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, addInitialSlash, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getCollectionBySlugWithin, getCollectionPathsCombinations, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityImagePreviewPropertyKey, getLabelOrConfigFrom, getLastSegment, getLastSegment as getLastSegment$1, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getReferenceFrom, getRelationFrom, getSubcollections, getTableName, isHidden, isPropertyBuilder, isReadOnly, normalizeToEntityRelation, removeInitialAndTrailingSlashes, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1, removeInitialSlash, removeTrailingSlash, resolveArrayProperties, resolveCollectionPathIds, resolveCollectionPathIds as resolveCollectionPathIds$1, resolveDefaultSelectedView, resolveEnumValues, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString } from "@rebasepro/common";
|
|
7
|
-
import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, ConfirmationDialog, CustomizationControllerContext, DEFAULT_PAGE_SIZE, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, IconForView, LanguageToggle, LoginView, NotFoundPage, PluginProviderStack, RebaseLogo, RebaseRoutes, SchemaDriftBanner, StudioBridgeRegistryProvider, UIReferenceView, UnsavedChangesDialog, UserDisplay, UserSettingsView, buildCollapsedDefaults, buildEnumLabel, createFormexStub, deleteEntityWithCallbacks, flattenKeys, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getFormFieldKeys, getIcon, getRowHeight, iconsSearch, removeEntityFromCache, removeEntityFromMemoryCache, resolveComponentRef, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiConfig, useAuthController, useBridgeRegistration, useBuildLocalConfigurationPersistence, useCollapsedGroups, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataTableController, useEntityFetch, useLargeLayout, useModeController, usePermissions, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useRestoreScroll, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageUploadController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence } from "@rebasepro/core";
|
|
5
|
+
import { Alert, AlertCircleIcon, AlertTriangleIcon, AlignLeftIcon, ArrowLeftIcon, ArrowRightToLineIcon, Avatar, Badge, BooleanSwitchWithLabel, Button, CHIP_COLORS, CalendarIcon, Card, CardView, CenteredView, CheckCircleIcon, CheckIcon, Checkbox, ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, ChevronsLeftIcon, ChevronsRightIcon, ChevronsUpDownIcon, Chip, CircleIcon, CircularProgress, CircularProgressCenter, CodeIcon, Collapse, ColumnsIcon, Container, CopyIcon, DateTimeField, Dialog, DialogActions, DialogContent, DialogTitle, DownloadIcon, ErrorBoundary, ExpandablePanel, ExternalLinkIcon, EyeIcon, EyeOffIcon, FileTextIcon, FileUpload, FilterChip, FilterIcon, FilterXIcon, FlagIcon, FolderUpIcon, FunctionSquareIcon, GlobeIcon, HandleIcon, HashIcon, HistoryIcon, IconButton, ImageOffIcon, KanbanIcon, KanbanView, KeyRoundIcon, Label, LayoutGridIcon, LinkIcon, ListIcon, ListOrderedIcon, ListView, LoaderIcon, LoadingButton, LogOutIcon, MailIcon, Markdown, Maximize2Icon, Menu, MenuIcon, MenuItem, MinusIcon, MoonIcon, MoreVerticalIcon, MultiSelect, MultiSelectItem, PencilIcon, PlusIcon, Popover, PopoverPrimitive, Portal, QuoteIcon, RadioGroup, RadioGroupItem, RefreshCwIcon, RepeatIcon, ResizablePanels, Rows3Icon, SearchBar, SearchIcon, Select, SelectItem, Separator, SettingsIcon, Sheet, Skeleton, StarIcon, SunIcon, SunMoonIcon, Tab, Table, TableBody, TableCell, TableHeader, TableIcon, TableRow, TableView, Tabs, TextField, TextIcon, TextareaAutosize, ToggleButtonGroup, Tooltip, Trash2Icon, Typography, UploadIcon, UserIcon, VirtualTableDateField, VirtualTableInput, VirtualTableInput as VirtualTableInput$1, VirtualTableNumberInput, VirtualTableSwitch, VoteIcon, XCircleIcon, XIcon, cls, coolIconKeys, createVirtualTableSelectionStore, debounce, defaultBorderMixin, fieldBackgroundDisabledMixin, fieldBackgroundHoverMixin, fieldBackgroundInvisibleMixin, fieldBackgroundMixin, focusedDisabled, getColorSchemeForSeed, iconSize, lucideIcons, paperMixin, useDebouncedCallback, useInjectStyles, useOutsideAlerter } from "@rebasepro/ui";
|
|
6
|
+
import { CollectionRegistry, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE, addInitialSlash, buildRoutedRebaseData, canReadCollection, enumToObjectEntries, getArrayResolvedProperties, getCollectionBySlugWithin, getCollectionPathsCombinations, getDefaultValueFor, getDefaultValueFortype, getDefaultValuesFor, getEntityImagePreviewPropertyKey, getLabelOrConfigFrom, getLastSegment, getLastSegment as getLastSegment$1, getLocalChangesBackup, getNavigationEntriesFromPath, getParentReferencesFromPath, getReferenceFrom, getRelationFrom, getSubcollections, getTableName, isHidden, isPropertyBuilder, isReadOnly, normalizeToEntityRelation, removeInitialAndTrailingSlashes, removeInitialAndTrailingSlashes as removeInitialAndTrailingSlashes$1, removeInitialSlash, removeTrailingSlash, resolveArrayProperties, resolveCollectionPathIds, resolveCollectionPathIds as resolveCollectionPathIds$1, resolveDataSource, resolveDefaultSelectedView, resolveEnumValues, resolveProperty, resolveRelationProperty, resolveStorageFilenameString, resolveStoragePathString, resolveStorageSource } from "@rebasepro/common";
|
|
7
|
+
import { ADDITIONAL_TAB_WIDTH, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, ConfirmationDialog, CustomizationControllerContext, DEFAULT_PAGE_SIZE, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, IconForView, LanguageToggle, LoginView, NotFoundPage, PluginProviderStack, RebaseDataContext, RebaseLogo, RebaseRoutes, SchemaDriftBanner, StorageSourceContext, StudioBridgeRegistryProvider, UIReferenceView, UnsavedChangesDialog, UserDisplay, UserSettingsView, buildCollapsedDefaults, buildEnumLabel, createFormexStub, deleteEntityWithCallbacks, flattenKeys, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getFormFieldKeys, getIcon, getRowHeight, iconsSearch, removeEntityFromCache, removeEntityFromMemoryCache, resolveComponentRef, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiConfig, useAuthController, useBridgeRegistration, useBuildLocalConfigurationPersistence, useCollapsedGroups, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useEntityFetch, useLargeLayout, useModeController, usePermissions, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useRestoreScroll, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence } from "@rebasepro/core";
|
|
8
8
|
import { Field, Formex, getIn, setIn, useCreateFormex, useFormex } from "@rebasepro/formex";
|
|
9
9
|
import { z } from "zod";
|
|
10
10
|
import { defaultDateFormat, getArrayValuesCount, getHashValue, getValueInPath, hydrateRegExp, isObject, mergeDeep, prettifyIdentifier, randomString, slugify, toArray } from "@rebasepro/utils";
|
|
11
|
+
import { EntityReference, EntityRelation, GeoPoint, Vector, getCollectionDataPath, getDataSourceCapabilities } from "@rebasepro/types";
|
|
11
12
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
-
import { EntityReference, EntityRelation, GeoPoint, Vector, getDataSourceCapabilities } from "@rebasepro/types";
|
|
13
13
|
import { Link, NavLink, Outlet, Route, useBlocker, useLocation, useNavigate, useSearchParams } from "react-router-dom";
|
|
14
14
|
import { Highlight, themes } from "prism-react-renderer";
|
|
15
15
|
import { Command } from "cmdk";
|
|
@@ -21,7 +21,6 @@ import { SortableContext, arrayMove, defaultAnimateLayoutChanges, horizontalList
|
|
|
21
21
|
import { CSS } from "@dnd-kit/utilities";
|
|
22
22
|
import ExcelJS from "exceljs";
|
|
23
23
|
import { buildEntityPropertiesFromData, inferTypeFromValue } from "@rebasepro/schema-inference";
|
|
24
|
-
import { createPortal } from "react-dom";
|
|
25
24
|
import { generateKeyBetween, generateNKeysBetween } from "fractional-indexing";
|
|
26
25
|
import Fuse from "fuse.js";
|
|
27
26
|
import { format } from "date-fns";
|
|
@@ -293,16 +292,11 @@ function getZodBinarySchema({ property }) {
|
|
|
293
292
|
//#endregion
|
|
294
293
|
//#region src/form/EntityFormActions.tsx
|
|
295
294
|
function EntityFormActions({ path, collection, entity, layout, savingError, formex, disabled, status, pluginActions, openEntityMode, navigateBack, formContext }) {
|
|
296
|
-
const context = useCMSContext();
|
|
297
|
-
const sideEntityController = context.sideEntityController;
|
|
298
|
-
const { t } = useTranslation();
|
|
299
295
|
const bottomActionsProps = {
|
|
300
296
|
path,
|
|
301
297
|
savingError,
|
|
302
298
|
entity,
|
|
303
299
|
collection,
|
|
304
|
-
context,
|
|
305
|
-
sideEntityController,
|
|
306
300
|
disabled,
|
|
307
301
|
status,
|
|
308
302
|
pluginActions,
|
|
@@ -310,7 +304,6 @@ function EntityFormActions({ path, collection, entity, layout, savingError, form
|
|
|
310
304
|
navigateBack,
|
|
311
305
|
formContext,
|
|
312
306
|
formex,
|
|
313
|
-
t,
|
|
314
307
|
className: layout === "responsive" ? "@6xl:hidden" : void 0
|
|
315
308
|
};
|
|
316
309
|
const sideActionsProps = {
|
|
@@ -318,8 +311,6 @@ function EntityFormActions({ path, collection, entity, layout, savingError, form
|
|
|
318
311
|
savingError,
|
|
319
312
|
entity,
|
|
320
313
|
collection,
|
|
321
|
-
context,
|
|
322
|
-
sideEntityController,
|
|
323
314
|
disabled,
|
|
324
315
|
status,
|
|
325
316
|
pluginActions,
|
|
@@ -327,7 +318,6 @@ function EntityFormActions({ path, collection, entity, layout, savingError, form
|
|
|
327
318
|
navigateBack,
|
|
328
319
|
formContext,
|
|
329
320
|
formex,
|
|
330
|
-
t,
|
|
331
321
|
className: layout === "responsive" ? "hidden @6xl:flex" : void 0
|
|
332
322
|
};
|
|
333
323
|
const bottomActions = buildBottomActions$1(bottomActionsProps);
|
|
@@ -335,7 +325,7 @@ function EntityFormActions({ path, collection, entity, layout, savingError, form
|
|
|
335
325
|
if (layout === "responsive") return /* @__PURE__ */ jsxs(Fragment, { children: [bottomActions, sideActions] });
|
|
336
326
|
return layout === "bottom" ? bottomActions : sideActions;
|
|
337
327
|
}
|
|
338
|
-
function buildBottomActions$1({ savingError, entity, path, formActions, collection,
|
|
328
|
+
function buildBottomActions$1({ savingError, entity, path, formActions, collection, disabled, status, pluginActions, openEntityMode, navigateBack, formContext, formex, className }) {
|
|
339
329
|
const hasErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
|
|
340
330
|
return /* @__PURE__ */ jsxs(DialogActions, {
|
|
341
331
|
position: "absolute",
|
|
@@ -357,10 +347,10 @@ function buildBottomActions$1({ savingError, entity, path, formActions, collecti
|
|
|
357
347
|
if (entity) action.onClick({
|
|
358
348
|
view: "form",
|
|
359
349
|
entity,
|
|
360
|
-
path: path ?? collection
|
|
350
|
+
path: path ?? getCollectionDataPath(collection),
|
|
361
351
|
collection,
|
|
362
|
-
context,
|
|
363
|
-
sideEntityController,
|
|
352
|
+
context: void 0,
|
|
353
|
+
sideEntityController: void 0,
|
|
364
354
|
openEntityMode,
|
|
365
355
|
navigateBack,
|
|
366
356
|
formContext
|
|
@@ -375,47 +365,55 @@ function buildBottomActions$1({ savingError, entity, path, formActions, collecti
|
|
|
375
365
|
disabled: disabled || formex.isSubmitting,
|
|
376
366
|
color: "primary",
|
|
377
367
|
type: "reset",
|
|
378
|
-
children: status === "existing" ?
|
|
368
|
+
children: status === "existing" ? "Discard" : "Clear"
|
|
379
369
|
}),
|
|
380
|
-
/* @__PURE__ */
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
370
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
371
|
+
title: hasErrors ? "Fix highlighted errors before saving" : void 0,
|
|
372
|
+
children: /* @__PURE__ */ jsxs(LoadingButton, {
|
|
373
|
+
variant: "filled",
|
|
374
|
+
color: "primary",
|
|
375
|
+
type: "submit",
|
|
376
|
+
loading: formex.isSubmitting,
|
|
377
|
+
disabled: disabled || formex.isSubmitting,
|
|
378
|
+
startIcon: hasErrors ? /* @__PURE__ */ jsx(AlertCircleIcon, {}) : void 0,
|
|
379
|
+
children: [
|
|
380
|
+
status === "existing" && "Save",
|
|
381
|
+
status === "copy" && "Create copy",
|
|
382
|
+
status === "new" && "Create"
|
|
383
|
+
]
|
|
384
|
+
})
|
|
391
385
|
})
|
|
392
386
|
]
|
|
393
387
|
});
|
|
394
388
|
}
|
|
395
|
-
function buildSideActions$1({ savingError, entity, formActions, path, openEntityMode, collection,
|
|
389
|
+
function buildSideActions$1({ savingError, entity, formActions, path, openEntityMode, collection, disabled, status, pluginActions, formex, className }) {
|
|
396
390
|
const hasErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
|
|
397
391
|
return /* @__PURE__ */ jsxs("div", {
|
|
398
392
|
className: cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin, className),
|
|
399
393
|
children: [
|
|
400
|
-
/* @__PURE__ */
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
394
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
395
|
+
title: hasErrors ? "Fix highlighted errors before saving" : void 0,
|
|
396
|
+
children: /* @__PURE__ */ jsxs(LoadingButton, {
|
|
397
|
+
fullWidth: true,
|
|
398
|
+
variant: "filled",
|
|
399
|
+
color: "primary",
|
|
400
|
+
type: "submit",
|
|
401
|
+
loading: formex.isSubmitting,
|
|
402
|
+
startIcon: hasErrors ? /* @__PURE__ */ jsx(AlertCircleIcon, {}) : void 0,
|
|
403
|
+
disabled: disabled || formex.isSubmitting,
|
|
404
|
+
children: [
|
|
405
|
+
status === "existing" && "Save",
|
|
406
|
+
status === "copy" && "Create copy",
|
|
407
|
+
status === "new" && "Create"
|
|
408
|
+
]
|
|
409
|
+
})
|
|
412
410
|
}),
|
|
413
411
|
/* @__PURE__ */ jsx(Button, {
|
|
414
412
|
fullWidth: true,
|
|
415
413
|
variant: "text",
|
|
416
414
|
disabled: disabled || formex.isSubmitting,
|
|
417
415
|
type: "reset",
|
|
418
|
-
children: status === "existing" ?
|
|
416
|
+
children: status === "existing" ? "Discard" : "Clear"
|
|
419
417
|
}),
|
|
420
418
|
pluginActions,
|
|
421
419
|
savingError && /* @__PURE__ */ jsx("div", {
|
|
@@ -786,26 +784,32 @@ function renderSkeletonIcon() {
|
|
|
786
784
|
*/
|
|
787
785
|
var StorageThumbnail = React.memo(StorageThumbnailInternal, areEqual$1);
|
|
788
786
|
function areEqual$1(prevProps, nextProps) {
|
|
789
|
-
return prevProps.size === nextProps.size && prevProps.storagePathOrDownloadUrl === nextProps.storagePathOrDownloadUrl && prevProps.storeUrl === nextProps.storeUrl && prevProps.interactive === nextProps.interactive && prevProps.fill === nextProps.fill;
|
|
787
|
+
return prevProps.size === nextProps.size && prevProps.storagePathOrDownloadUrl === nextProps.storagePathOrDownloadUrl && prevProps.storeUrl === nextProps.storeUrl && prevProps.interactive === nextProps.interactive && prevProps.fill === nextProps.fill && prevProps.storageSourceKey === nextProps.storageSourceKey;
|
|
790
788
|
}
|
|
791
789
|
var URL_CACHE = {};
|
|
792
|
-
function StorageThumbnailInternal({ storeUrl, interactive, storagePathOrDownloadUrl, size, fill }) {
|
|
790
|
+
function StorageThumbnailInternal({ storeUrl, interactive, storagePathOrDownloadUrl, size, fill, storageSourceKey }) {
|
|
793
791
|
const [error, setError] = React.useState(void 0);
|
|
794
|
-
const
|
|
795
|
-
const
|
|
792
|
+
const defaultStorage = useStorageSource();
|
|
793
|
+
const storage = resolveStorageSource({
|
|
794
|
+
sourceKey: storageSourceKey,
|
|
795
|
+
sources: useStorageSources().sources,
|
|
796
|
+
defaultSource: defaultStorage
|
|
797
|
+
});
|
|
798
|
+
const cacheKey = `${storageSourceKey ?? ""}::${storagePathOrDownloadUrl}`;
|
|
799
|
+
const [downloadConfig, setDownloadConfig] = React.useState(URL_CACHE[cacheKey]);
|
|
796
800
|
useEffect(() => {
|
|
797
801
|
if (!storagePathOrDownloadUrl) return;
|
|
798
802
|
let unmounted = false;
|
|
799
803
|
storage.getSignedUrl(storagePathOrDownloadUrl).then(function(downloadConfig) {
|
|
800
804
|
if (!unmounted) {
|
|
801
805
|
setDownloadConfig(downloadConfig);
|
|
802
|
-
URL_CACHE[
|
|
806
|
+
URL_CACHE[cacheKey] = downloadConfig;
|
|
803
807
|
}
|
|
804
808
|
}).catch(setError);
|
|
805
809
|
return () => {
|
|
806
810
|
unmounted = true;
|
|
807
811
|
};
|
|
808
|
-
}, [storagePathOrDownloadUrl]);
|
|
812
|
+
}, [storagePathOrDownloadUrl, cacheKey]);
|
|
809
813
|
if (!storagePathOrDownloadUrl) return null;
|
|
810
814
|
const filetype = downloadConfig?.metadata ? getFiletype(downloadConfig?.metadata.contentType) : void 0;
|
|
811
815
|
const previewType = filetype?.startsWith("image") ? "image" : filetype?.startsWith("video") ? "video" : filetype?.startsWith("audio") ? "audio" : "file";
|
|
@@ -953,7 +957,6 @@ function getDefaultIconForProperty(property) {
|
|
|
953
957
|
if (property.ui?.url) return GlobeIcon;
|
|
954
958
|
if (property.email) return MailIcon;
|
|
955
959
|
if (property.ui?.multiline || property.ui?.markdown) return AlignLeftIcon;
|
|
956
|
-
if (property.reference) return LinkIcon;
|
|
957
960
|
return TextIcon;
|
|
958
961
|
case "number": return HashIcon;
|
|
959
962
|
case "boolean": return FlagIcon;
|
|
@@ -1056,6 +1059,21 @@ function isHiddenProperty(property) {
|
|
|
1056
1059
|
if (!property) return false;
|
|
1057
1060
|
return Boolean(property.ui?.hideFromCollection);
|
|
1058
1061
|
}
|
|
1062
|
+
/**
|
|
1063
|
+
* Returns true when the property holds file-storage content (single image,
|
|
1064
|
+
* array of images, generic upload, …). These properties are rendered by the
|
|
1065
|
+
* dedicated image-slot and should NOT appear as regular preview columns.
|
|
1066
|
+
*/
|
|
1067
|
+
function isStorageProperty$1(property) {
|
|
1068
|
+
if (!property) return false;
|
|
1069
|
+
if (property.type === "string" && property.storage) return true;
|
|
1070
|
+
if (property.type === "string" && property.ui?.url === "image") return true;
|
|
1071
|
+
if (property.type === "array" && property.of && !Array.isArray(property.of)) {
|
|
1072
|
+
const inner = property.of;
|
|
1073
|
+
if (inner.type === "string" && (inner.storage || inner.ui?.url === "image")) return true;
|
|
1074
|
+
}
|
|
1075
|
+
return false;
|
|
1076
|
+
}
|
|
1059
1077
|
function getEntityPreviewKeys(authController, targetCollection, fields, previewProperties, limit = 3) {
|
|
1060
1078
|
const allProperties = Object.keys(targetCollection.properties);
|
|
1061
1079
|
let listProperties = previewProperties?.filter((p) => allProperties.includes(p));
|
|
@@ -1069,7 +1087,7 @@ function getEntityPreviewKeys(authController, targetCollection, fields, previewP
|
|
|
1069
1087
|
return !(prop && typeof prop === "object" && "isId" in prop && Boolean(prop.isId)) && key !== "id";
|
|
1070
1088
|
}).filter((key) => {
|
|
1071
1089
|
const property = targetCollection.properties[key];
|
|
1072
|
-
return property && !isPropertyBuilder(property) && !isReferenceProperty(property) && !isHiddenProperty(property) && (hasExplicitOrder || !isRelationProperty(property));
|
|
1090
|
+
return property && !isPropertyBuilder(property) && !isReferenceProperty(property) && !isHiddenProperty(property) && !isStorageProperty$1(property) && (hasExplicitOrder || !isRelationProperty(property));
|
|
1073
1091
|
}).slice(0, limit);
|
|
1074
1092
|
}
|
|
1075
1093
|
}
|
|
@@ -1578,7 +1596,7 @@ var MAIN_TAB_VALUE = "__main_##Q$SC^#S6";
|
|
|
1578
1596
|
var JSON_TAB_VALUE = "__json";
|
|
1579
1597
|
//#endregion
|
|
1580
1598
|
//#region src/components/EntityDetailView.tsx
|
|
1581
|
-
var EntityHistoryView$1 = lazy(() => import("./history-
|
|
1599
|
+
var EntityHistoryView$1 = lazy(() => import("./history-B3cqBzEV.js").then((m) => ({ default: m.EntityHistoryView })));
|
|
1582
1600
|
function EntityDetailView({ entityId, ...props }) {
|
|
1583
1601
|
const { entity, dataLoading, dataLoadingError } = useEntityFetch({
|
|
1584
1602
|
path: props.path,
|
|
@@ -1770,7 +1788,7 @@ function EntityDetailViewInner({ path, entityId, selectedTab: selectedTabProp, c
|
|
|
1770
1788
|
}, "history_view") : null;
|
|
1771
1789
|
const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
|
|
1772
1790
|
const subcollectionId = subcollection.slug;
|
|
1773
|
-
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection
|
|
1791
|
+
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(getCollectionDataPath(subcollection))}` : void 0;
|
|
1774
1792
|
if (activeTab !== subcollectionId) return null;
|
|
1775
1793
|
return /* @__PURE__ */ jsxs("div", {
|
|
1776
1794
|
className: "relative flex-1 h-full overflow-auto w-full",
|
|
@@ -2060,44 +2078,9 @@ function DetailViewSkeleton({ collection }) {
|
|
|
2060
2078
|
}
|
|
2061
2079
|
//#endregion
|
|
2062
2080
|
//#region src/components/SelectableTable/SelectionStore.ts
|
|
2063
|
-
/**
|
|
2064
|
-
* A ref-based selection store that avoids React context re-renders.
|
|
2065
|
-
*
|
|
2066
|
-
* The problem with putting `selectedCell` in a React context value is that
|
|
2067
|
-
* ANY cell selection change triggers a context value change, which forces
|
|
2068
|
-
* ALL consumers (every PropertyTableCell in the table) to re-render.
|
|
2069
|
-
* This causes the DOM to be replaced between mousedown and click events,
|
|
2070
|
-
* breaking `alwaysInteractive` cells (like relation selectors) where the
|
|
2071
|
-
* user needs to click a button on the very first interaction.
|
|
2072
|
-
*
|
|
2073
|
-
* This store uses `useSyncExternalStore` so only cells whose `selected`
|
|
2074
|
-
* derivation actually changed will re-render.
|
|
2075
|
-
*/
|
|
2076
2081
|
function createSelectionStore() {
|
|
2077
|
-
|
|
2078
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
2079
|
-
function getSnapshot() {
|
|
2080
|
-
return selectedCell;
|
|
2081
|
-
}
|
|
2082
|
-
function subscribe(listener) {
|
|
2083
|
-
listeners.add(listener);
|
|
2084
|
-
return () => listeners.delete(listener);
|
|
2085
|
-
}
|
|
2086
|
-
function select(cell) {
|
|
2087
|
-
selectedCell = cell;
|
|
2088
|
-
listeners.forEach((l) => l());
|
|
2089
|
-
}
|
|
2090
|
-
return {
|
|
2091
|
-
getSnapshot,
|
|
2092
|
-
subscribe,
|
|
2093
|
-
select
|
|
2094
|
-
};
|
|
2082
|
+
return createVirtualTableSelectionStore();
|
|
2095
2083
|
}
|
|
2096
|
-
/**
|
|
2097
|
-
* Hook that subscribes a cell to the selection store and returns
|
|
2098
|
-
* whether THIS cell is selected. Only re-renders when the cell's
|
|
2099
|
-
* `selected` boolean actually changes (not on every store update).
|
|
2100
|
-
*/
|
|
2101
2084
|
function useCellSelected(store, propertyKey, entityPath, entityId) {
|
|
2102
2085
|
const selectorRef = useRef({
|
|
2103
2086
|
propertyKey,
|
|
@@ -2118,69 +2101,6 @@ function useCellSelected(store, propertyKey, entityPath, entityId) {
|
|
|
2118
2101
|
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
2119
2102
|
}
|
|
2120
2103
|
//#endregion
|
|
2121
|
-
//#region src/components/EntityCollectionTable/fields/VirtualTableInput.tsx
|
|
2122
|
-
function VirtualTableInput(props) {
|
|
2123
|
-
const ref = React.useRef(null);
|
|
2124
|
-
const { disabled, value, multiline, updateValue, focused } = props;
|
|
2125
|
-
const prevValue = useRef(value);
|
|
2126
|
-
const [internalValue, setInternalValue] = useState(value);
|
|
2127
|
-
const focusedState = useRef(false);
|
|
2128
|
-
useEffect(() => {
|
|
2129
|
-
if (prevValue.current !== value && value !== internalValue) setInternalValue(value);
|
|
2130
|
-
prevValue.current = value;
|
|
2131
|
-
}, [value]);
|
|
2132
|
-
const doUpdate = React.useCallback(() => {
|
|
2133
|
-
if (!value && !internalValue) return;
|
|
2134
|
-
if (internalValue !== value && internalValue !== prevValue.current) {
|
|
2135
|
-
prevValue.current = internalValue;
|
|
2136
|
-
updateValue(internalValue);
|
|
2137
|
-
}
|
|
2138
|
-
}, [
|
|
2139
|
-
internalValue,
|
|
2140
|
-
updateValue,
|
|
2141
|
-
value
|
|
2142
|
-
]);
|
|
2143
|
-
useDebouncedCallback(internalValue, doUpdate, !focused, 400);
|
|
2144
|
-
useEffect(() => {
|
|
2145
|
-
if (ref.current && focused && !focusedState.current) {
|
|
2146
|
-
focusedState.current = true;
|
|
2147
|
-
ref.current.focus({ preventScroll: true });
|
|
2148
|
-
ref.current.selectionStart = ref.current.value.length;
|
|
2149
|
-
ref.current.selectionEnd = ref.current.value.length;
|
|
2150
|
-
} else focusedState.current = focused;
|
|
2151
|
-
}, [focused, ref]);
|
|
2152
|
-
return /* @__PURE__ */ jsx(TextareaAutosize, {
|
|
2153
|
-
className: focusedDisabled,
|
|
2154
|
-
ref,
|
|
2155
|
-
style: {
|
|
2156
|
-
padding: 0,
|
|
2157
|
-
margin: 0,
|
|
2158
|
-
width: "100%",
|
|
2159
|
-
color: "unset",
|
|
2160
|
-
fontWeight: "unset",
|
|
2161
|
-
fontSize: "unset",
|
|
2162
|
-
fontFamily: "unset",
|
|
2163
|
-
background: "unset",
|
|
2164
|
-
border: "unset",
|
|
2165
|
-
resize: "none",
|
|
2166
|
-
outline: "none"
|
|
2167
|
-
},
|
|
2168
|
-
value: internalValue ?? "",
|
|
2169
|
-
onChange: (evt) => {
|
|
2170
|
-
const newValue = evt.target.value;
|
|
2171
|
-
if (multiline || !newValue.endsWith("\n")) setInternalValue(newValue);
|
|
2172
|
-
},
|
|
2173
|
-
onFocus: () => {
|
|
2174
|
-
focusedState.current = true;
|
|
2175
|
-
},
|
|
2176
|
-
onBlur: () => {
|
|
2177
|
-
focusedState.current = false;
|
|
2178
|
-
doUpdate();
|
|
2179
|
-
if (props.onBlur) props.onBlur();
|
|
2180
|
-
}
|
|
2181
|
-
});
|
|
2182
|
-
}
|
|
2183
|
-
//#endregion
|
|
2184
2104
|
//#region src/components/EntityCollectionTable/fields/VirtualTableSelect.tsx
|
|
2185
2105
|
function VirtualTableSelect(props) {
|
|
2186
2106
|
const { enumValues, internalValue, disabled, small, focused, updateValue, multiple, valueType } = props;
|
|
@@ -2254,78 +2174,6 @@ function VirtualTableSelect(props) {
|
|
|
2254
2174
|
});
|
|
2255
2175
|
}
|
|
2256
2176
|
//#endregion
|
|
2257
|
-
//#region src/components/EntityCollectionTable/fields/VirtualTableNumberInput.tsx
|
|
2258
|
-
function VirtualTableNumberInput(props) {
|
|
2259
|
-
const { align, value, updateValue, focused } = props;
|
|
2260
|
-
const propStringValue = value && typeof value === "number" ? value.toString() : "";
|
|
2261
|
-
const [internalValue, setInternalValue] = useState(propStringValue);
|
|
2262
|
-
const prevValue = useRef(value);
|
|
2263
|
-
useEffect(() => {
|
|
2264
|
-
if (prevValue.current !== value && String(value) !== internalValue) setInternalValue(value ? value.toString() : null);
|
|
2265
|
-
prevValue.current = value;
|
|
2266
|
-
}, [value]);
|
|
2267
|
-
useDebouncedCallback(internalValue, React.useCallback(() => {
|
|
2268
|
-
if (internalValue !== propStringValue) if (internalValue !== void 0 && internalValue !== null) {
|
|
2269
|
-
const numberValue = parseFloat(internalValue);
|
|
2270
|
-
if (isNaN(numberValue)) return;
|
|
2271
|
-
if (numberValue !== void 0 && numberValue !== null) updateValue(numberValue);
|
|
2272
|
-
} else updateValue(null);
|
|
2273
|
-
}, [internalValue, value]), !focused, 400);
|
|
2274
|
-
useEffect(() => {
|
|
2275
|
-
if (!focused && propStringValue !== internalValue) setInternalValue(value !== void 0 && value !== null ? value.toString() : null);
|
|
2276
|
-
}, [value, focused]);
|
|
2277
|
-
const inputRef = React.useRef(null);
|
|
2278
|
-
useEffect(() => {
|
|
2279
|
-
if (inputRef.current && focused) inputRef.current.focus({ preventScroll: true });
|
|
2280
|
-
}, [focused, inputRef]);
|
|
2281
|
-
const regexp = /^-?[0-9]+[,.]?[0-9]*$/;
|
|
2282
|
-
return /* @__PURE__ */ jsx(TextField, {
|
|
2283
|
-
inputRef,
|
|
2284
|
-
invisible: true,
|
|
2285
|
-
size: "small",
|
|
2286
|
-
className: "w-full",
|
|
2287
|
-
inputClassName: cls("p-0 m-0 bg-transparent border-none outline-hidden font-normal leading-normal text-unset", focusedDisabled),
|
|
2288
|
-
inputStyle: { textAlign: align },
|
|
2289
|
-
value: internalValue ?? "",
|
|
2290
|
-
onChange: (evt) => {
|
|
2291
|
-
const newValue = evt.target.value.replace(",", ".");
|
|
2292
|
-
if (newValue.length === 0) setInternalValue(null);
|
|
2293
|
-
if (regexp.test(newValue) || newValue.startsWith("-")) setInternalValue(newValue);
|
|
2294
|
-
}
|
|
2295
|
-
});
|
|
2296
|
-
}
|
|
2297
|
-
//#endregion
|
|
2298
|
-
//#region src/components/EntityCollectionTable/fields/VirtualTableSwitch.tsx
|
|
2299
|
-
function VirtualTableSwitch(props) {
|
|
2300
|
-
const { internalValue, updateValue, focused } = props;
|
|
2301
|
-
const ref = React.useRef(null);
|
|
2302
|
-
useEffect(() => {
|
|
2303
|
-
if (ref.current && focused) ref.current.focus({ preventScroll: true });
|
|
2304
|
-
}, [focused, ref]);
|
|
2305
|
-
return /* @__PURE__ */ jsx(BooleanSwitch, {
|
|
2306
|
-
ref,
|
|
2307
|
-
size: "small",
|
|
2308
|
-
value: Boolean(internalValue),
|
|
2309
|
-
onValueChange: updateValue
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
//#endregion
|
|
2313
|
-
//#region src/components/EntityCollectionTable/fields/VirtualTableDateField.tsx
|
|
2314
|
-
function VirtualTableDateField(props) {
|
|
2315
|
-
const { locale } = useCustomizationController();
|
|
2316
|
-
const { disabled, error, mode, timezone, internalValue, updateValue } = props;
|
|
2317
|
-
return /* @__PURE__ */ jsx(DateTimeField, {
|
|
2318
|
-
value: internalValue ?? void 0,
|
|
2319
|
-
onChange: (dateValue) => updateValue(dateValue ?? null),
|
|
2320
|
-
invisible: true,
|
|
2321
|
-
inputClassName: cls("w-full h-full", focusedDisabled),
|
|
2322
|
-
className: cls("w-full h-full", focusedDisabled),
|
|
2323
|
-
mode,
|
|
2324
|
-
timezone,
|
|
2325
|
-
locale
|
|
2326
|
-
});
|
|
2327
|
-
}
|
|
2328
|
-
//#endregion
|
|
2329
2177
|
//#region src/components/UserSelector.tsx
|
|
2330
2178
|
var DEFAULT_PAGE_SIZE$2 = 10;
|
|
2331
2179
|
/**
|
|
@@ -2657,8 +2505,9 @@ function VirtualTableUserSelect(props) {
|
|
|
2657
2505
|
}
|
|
2658
2506
|
//#endregion
|
|
2659
2507
|
//#region src/form/components/StorageUploadProgress.tsx
|
|
2660
|
-
function StorageUploadProgress({ storagePath, entry, metadata, onFileUploadComplete, imageSize, simple }) {
|
|
2661
|
-
const
|
|
2508
|
+
function StorageUploadProgress({ storagePath, entry, metadata, onFileUploadComplete, imageSize, simple, storageSource: storageSourceProp }) {
|
|
2509
|
+
const defaultStorageSource = useStorageSource();
|
|
2510
|
+
const storageSource = storageSourceProp ?? defaultStorageSource;
|
|
2662
2511
|
const snackbarController = useSnackbarController();
|
|
2663
2512
|
const [error, setError] = React.useState();
|
|
2664
2513
|
const [loading, setLoading] = React.useState(false);
|
|
@@ -2787,7 +2636,7 @@ var rejectDropClasses$1 = "transition-colors duration-200 ease-[cubic-bezier(0,0
|
|
|
2787
2636
|
function TableStorageUpload(props) {
|
|
2788
2637
|
const { propertyKey, error, selected, openPopup, value, disabled, property, entity, path, previewSize, updateValue } = props;
|
|
2789
2638
|
const storageSource = useStorageSource();
|
|
2790
|
-
const { internalValue, setInternalValue, onFilesAdded, storage, onFileUploadComplete, storagePathBuilder, multipleFilesSupported } = useStorageUploadController({
|
|
2639
|
+
const { internalValue, setInternalValue, onFilesAdded, storage, onFileUploadComplete, storagePathBuilder, multipleFilesSupported, resolvedStorageSource } = useStorageUploadController({
|
|
2791
2640
|
entityValues: entity.values,
|
|
2792
2641
|
entityId: entity.id,
|
|
2793
2642
|
path,
|
|
@@ -2798,24 +2647,27 @@ function TableStorageUpload(props) {
|
|
|
2798
2647
|
value,
|
|
2799
2648
|
disabled
|
|
2800
2649
|
});
|
|
2801
|
-
return /* @__PURE__ */ jsx(
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2650
|
+
return /* @__PURE__ */ jsx(StorageSourceContext.Provider, {
|
|
2651
|
+
value: resolvedStorageSource,
|
|
2652
|
+
children: /* @__PURE__ */ jsx(StorageUpload$1, {
|
|
2653
|
+
internalValue,
|
|
2654
|
+
setInternalValue,
|
|
2655
|
+
name: propertyKey,
|
|
2656
|
+
disabled,
|
|
2657
|
+
autoFocus: false,
|
|
2658
|
+
openPopup,
|
|
2659
|
+
error,
|
|
2660
|
+
selected,
|
|
2661
|
+
property,
|
|
2662
|
+
onChange: updateValue,
|
|
2663
|
+
entity,
|
|
2664
|
+
storagePathBuilder,
|
|
2665
|
+
storage,
|
|
2666
|
+
multipleFilesSupported,
|
|
2667
|
+
onFilesAdded,
|
|
2668
|
+
onFileUploadComplete,
|
|
2669
|
+
previewSize
|
|
2670
|
+
})
|
|
2819
2671
|
});
|
|
2820
2672
|
}
|
|
2821
2673
|
function StorageUpload$1({ property, name, internalValue, setInternalValue, openPopup, entity, selected, error, onChange, multipleFilesSupported, previewSize: previewSizeInput, disabled, autoFocus, storage, onFilesAdded, onFileUploadComplete, storagePathBuilder }) {
|
|
@@ -3086,7 +2938,7 @@ var TableMultipleRelationFieldInternal = React.memo(function TableMultipleRelati
|
|
|
3086
2938
|
const selectedEntityIds = value.map((ref) => ref.id);
|
|
3087
2939
|
const relationDialogController = useEntitySelectionDialog({
|
|
3088
2940
|
multiselect: true,
|
|
3089
|
-
path: collection
|
|
2941
|
+
path: getCollectionDataPath(collection),
|
|
3090
2942
|
collection,
|
|
3091
2943
|
onMultipleEntitiesSelected,
|
|
3092
2944
|
selectedEntityIds,
|
|
@@ -3160,7 +3012,7 @@ var TableRelationFieldInternal = React.memo(function TableRelationFieldInternal(
|
|
|
3160
3012
|
const selectedEntityIds = internalValue ? Array.isArray(internalValue) ? internalValue.map((ref) => ref.id) : internalValue.id ? [internalValue.id] : [] : [];
|
|
3161
3013
|
const relationDialogController = useEntitySelectionDialog({
|
|
3162
3014
|
multiselect,
|
|
3163
|
-
path: collection
|
|
3015
|
+
path: getCollectionDataPath(collection),
|
|
3164
3016
|
collection,
|
|
3165
3017
|
onMultipleEntitiesSelected,
|
|
3166
3018
|
onSingleEntitySelected,
|
|
@@ -3248,7 +3100,7 @@ var RelationSelector = React$1.forwardRef(({ value, size = "medium", onValueChan
|
|
|
3248
3100
|
const localSelectionIdsRef = useRef(null);
|
|
3249
3101
|
const pinnedIdsRef = useRef(null);
|
|
3250
3102
|
const { items: availableItems, isLoading, hasMore, search, loadMore, entityToRelationItem } = useRelationSelector({
|
|
3251
|
-
path: collection
|
|
3103
|
+
path: getCollectionDataPath(collection),
|
|
3252
3104
|
collection,
|
|
3253
3105
|
fixedFilter,
|
|
3254
3106
|
pageSize
|
|
@@ -3744,32 +3596,7 @@ function isStorageProperty(property) {
|
|
|
3744
3596
|
return false;
|
|
3745
3597
|
}
|
|
3746
3598
|
function getTableBindingForProperty(property, selected) {
|
|
3747
|
-
|
|
3748
|
-
if (property.type === "string" && property.reference?.path) return {
|
|
3749
|
-
Component: ({ propertyKey, property, internalValue, updateValue, disabled, size, path }) => {
|
|
3750
|
-
const referenceProperty = property.reference;
|
|
3751
|
-
if (!referenceProperty) return null;
|
|
3752
|
-
return /* @__PURE__ */ jsx(TableReferenceField, {
|
|
3753
|
-
name: propertyKey,
|
|
3754
|
-
internalValue: internalValue ? new EntityReference({
|
|
3755
|
-
id: internalValue,
|
|
3756
|
-
path: referenceProperty.path
|
|
3757
|
-
}) : void 0,
|
|
3758
|
-
updateValue: (v) => updateValue(v ? v.id : null),
|
|
3759
|
-
disabled,
|
|
3760
|
-
size,
|
|
3761
|
-
path: referenceProperty.path,
|
|
3762
|
-
multiselect: false,
|
|
3763
|
-
previewProperties: referenceProperty.ui?.previewProperties,
|
|
3764
|
-
includeId: referenceProperty.includeId,
|
|
3765
|
-
includeEntityLink: referenceProperty.includeEntityLink,
|
|
3766
|
-
title: property.name,
|
|
3767
|
-
fixedFilter: referenceProperty.fixedFilter
|
|
3768
|
-
});
|
|
3769
|
-
},
|
|
3770
|
-
allowScroll: false
|
|
3771
|
-
};
|
|
3772
|
-
else if (isAStorageProperty) return {
|
|
3599
|
+
if (isStorageProperty(property)) return {
|
|
3773
3600
|
Component: ({ validationError, error, disabled, selected, openPopup, property, entity, path, internalValue, size, updateValue, propertyKey }) => /* @__PURE__ */ jsx(TableStorageUpload, {
|
|
3774
3601
|
error: validationError ?? error,
|
|
3775
3602
|
disabled,
|
|
@@ -3847,7 +3674,7 @@ function getTableBindingForProperty(property, selected) {
|
|
|
3847
3674
|
}),
|
|
3848
3675
|
fullHeight: true
|
|
3849
3676
|
};
|
|
3850
|
-
else if (stringProperty.ui?.markdown || !stringProperty.storage
|
|
3677
|
+
else if (stringProperty.ui?.markdown || !stringProperty.storage) {
|
|
3851
3678
|
const multiline = Boolean(stringProperty.ui?.multiline) || Boolean(stringProperty.ui?.markdown);
|
|
3852
3679
|
return {
|
|
3853
3680
|
Component: ({ error, validationError, disabled, selected, internalValue, updateValue }) => /* @__PURE__ */ jsx(VirtualTableInput, {
|
|
@@ -3869,15 +3696,19 @@ function getTableBindingForProperty(property, selected) {
|
|
|
3869
3696
|
updateValue
|
|
3870
3697
|
}) };
|
|
3871
3698
|
else if (property.type === "date") return {
|
|
3872
|
-
Component: ({ propertyKey, error, validationError, disabled, selected, property, internalValue, updateValue }) =>
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3699
|
+
Component: ({ propertyKey, error, validationError, disabled, selected, property, internalValue, updateValue }) => {
|
|
3700
|
+
const { locale } = useCustomizationController();
|
|
3701
|
+
return /* @__PURE__ */ jsx(VirtualTableDateField, {
|
|
3702
|
+
name: propertyKey,
|
|
3703
|
+
error: validationError ?? error,
|
|
3704
|
+
disabled,
|
|
3705
|
+
mode: property.mode,
|
|
3706
|
+
focused: selected,
|
|
3707
|
+
internalValue,
|
|
3708
|
+
updateValue,
|
|
3709
|
+
locale
|
|
3710
|
+
});
|
|
3711
|
+
},
|
|
3881
3712
|
fullHeight: true,
|
|
3882
3713
|
hideOverflow: false,
|
|
3883
3714
|
allowScroll: false
|
|
@@ -5164,7 +4995,12 @@ var SelectableTable = function SelectableTable({ onValueChange, cellRenderer, on
|
|
|
5164
4995
|
}, [onEntityClick, inlineEditing]);
|
|
5165
4996
|
const select = useCallback((cell) => {
|
|
5166
4997
|
setLocalSelectedCell(cell);
|
|
5167
|
-
selectionStore.select(
|
|
4998
|
+
if (cell) selectionStore.select({
|
|
4999
|
+
...cell,
|
|
5000
|
+
columnKey: cell.propertyKey,
|
|
5001
|
+
rowId: cell.entityId
|
|
5002
|
+
});
|
|
5003
|
+
else selectionStore.select(void 0);
|
|
5168
5004
|
}, [selectionStore]);
|
|
5169
5005
|
const unselect = useCallback(() => {
|
|
5170
5006
|
setLocalSelectedCell(void 0);
|
|
@@ -5206,7 +5042,7 @@ var SelectableTable = function SelectableTable({ onValueChange, cellRenderer, on
|
|
|
5206
5042
|
children: /* @__PURE__ */ jsx("div", {
|
|
5207
5043
|
className: "h-full w-full flex flex-col bg-white dark:bg-surface-900",
|
|
5208
5044
|
ref,
|
|
5209
|
-
children: /* @__PURE__ */ jsx(
|
|
5045
|
+
children: /* @__PURE__ */ jsx(TableView, {
|
|
5210
5046
|
data,
|
|
5211
5047
|
columns,
|
|
5212
5048
|
cellRenderer: (props) => cellRenderer(props),
|
|
@@ -5350,6 +5186,7 @@ var EntityCollectionTable = function EntityCollectionTable({ className, style, f
|
|
|
5350
5186
|
const customFieldValidator = uniqueFieldValidator;
|
|
5351
5187
|
const propertyCellRenderer = useCallback(({ column, columnIndex, rowData, rowIndex, sortableNodeRef, sortableStyle, sortableAttributes, isDragging, isDraggable, frozen }) => {
|
|
5352
5188
|
const entity = rowData;
|
|
5189
|
+
if (!entity) return null;
|
|
5353
5190
|
const propertyKey = column.key;
|
|
5354
5191
|
const columnCustom = column.custom;
|
|
5355
5192
|
let disabled = columnCustom?.disabled ?? false;
|
|
@@ -5389,6 +5226,7 @@ var EntityCollectionTable = function EntityCollectionTable({ className, style, f
|
|
|
5389
5226
|
]);
|
|
5390
5227
|
const additionalCellRenderer = useCallback(({ column, rowData, width, sortableNodeRef, sortableStyle, sortableAttributes, isDragging, isDraggable, frozen }) => {
|
|
5391
5228
|
const entity = rowData;
|
|
5229
|
+
if (!entity) return null;
|
|
5392
5230
|
const additionalField = additionalFieldsMap[column.key];
|
|
5393
5231
|
const value = additionalField.dependencies ? Object.entries(entity.values).filter(([key, value]) => additionalField.dependencies.includes(key)).reduce((a, b) => ({
|
|
5394
5232
|
...a,
|
|
@@ -5697,7 +5535,7 @@ function buildBottomActions({ savingError, entity, formActions, collection, cont
|
|
|
5697
5535
|
const props = {
|
|
5698
5536
|
view: "form",
|
|
5699
5537
|
entity,
|
|
5700
|
-
path: collection
|
|
5538
|
+
path: getCollectionDataPath(collection),
|
|
5701
5539
|
collection,
|
|
5702
5540
|
context,
|
|
5703
5541
|
sideEntityController,
|
|
@@ -5734,19 +5572,22 @@ function buildBottomActions({ savingError, entity, formActions, collection, cont
|
|
|
5734
5572
|
onClick: navigateBack,
|
|
5735
5573
|
children: t("back_to_detail") ?? "Back to details"
|
|
5736
5574
|
}),
|
|
5737
|
-
/* @__PURE__ */
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5575
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
5576
|
+
title: hasErrors ? t("fix_errors_before_saving") ?? "Fix highlighted errors before saving" : void 0,
|
|
5577
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
5578
|
+
variant: canClose ? "text" : "filled",
|
|
5579
|
+
color: "primary",
|
|
5580
|
+
type: "submit",
|
|
5581
|
+
disabled: disabled || formex.isSubmitting,
|
|
5582
|
+
onClick: () => {
|
|
5583
|
+
sideDialogContext.setPendingClose(false);
|
|
5584
|
+
},
|
|
5585
|
+
children: [
|
|
5586
|
+
status === "existing" && t("save"),
|
|
5587
|
+
status === "copy" && t("create_copy"),
|
|
5588
|
+
status === "new" && t("create")
|
|
5589
|
+
]
|
|
5590
|
+
})
|
|
5750
5591
|
}),
|
|
5751
5592
|
canClose && /* @__PURE__ */ jsxs(LoadingButton, {
|
|
5752
5593
|
variant: "filled",
|
|
@@ -5771,21 +5612,25 @@ function buildSideActions({ savingError, entity, formActions, collection, contex
|
|
|
5771
5612
|
return /* @__PURE__ */ jsxs("div", {
|
|
5772
5613
|
className: cls("overflow-auto h-full flex flex-col gap-2 w-80 2xl:w-96 px-4 py-16 sticky top-0 border-l", defaultBorderMixin, className),
|
|
5773
5614
|
children: [
|
|
5774
|
-
/* @__PURE__ */
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5615
|
+
/* @__PURE__ */ jsx(Tooltip, {
|
|
5616
|
+
title: hasErrors ? t("fix_errors_before_saving") ?? "Fix highlighted errors before saving" : void 0,
|
|
5617
|
+
children: /* @__PURE__ */ jsxs(LoadingButton, {
|
|
5618
|
+
fullWidth: true,
|
|
5619
|
+
variant: "filled",
|
|
5620
|
+
color: "primary",
|
|
5621
|
+
type: "submit",
|
|
5622
|
+
loading: formex.isSubmitting,
|
|
5623
|
+
startIcon: hasErrors ? /* @__PURE__ */ jsx(AlertCircleIcon, {}) : void 0,
|
|
5624
|
+
disabled: disabled || formex.isSubmitting,
|
|
5625
|
+
onClick: () => {
|
|
5626
|
+
sideDialogContext.setPendingClose?.(false);
|
|
5627
|
+
},
|
|
5628
|
+
children: [
|
|
5629
|
+
status === "existing" && t("save"),
|
|
5630
|
+
status === "copy" && t("create_copy"),
|
|
5631
|
+
status === "new" && t("create")
|
|
5632
|
+
]
|
|
5633
|
+
})
|
|
5789
5634
|
}),
|
|
5790
5635
|
/* @__PURE__ */ jsx(Button, {
|
|
5791
5636
|
fullWidth: true,
|
|
@@ -5808,7 +5653,7 @@ function buildSideActions({ savingError, entity, formActions, collection, contex
|
|
|
5808
5653
|
const props = {
|
|
5809
5654
|
view: "form",
|
|
5810
5655
|
entity,
|
|
5811
|
-
path: collection
|
|
5656
|
+
path: getCollectionDataPath(collection),
|
|
5812
5657
|
collection,
|
|
5813
5658
|
context,
|
|
5814
5659
|
sideEntityController,
|
|
@@ -5875,7 +5720,7 @@ function EntityActionButton({ action, enabled, props }) {
|
|
|
5875
5720
|
}
|
|
5876
5721
|
//#endregion
|
|
5877
5722
|
//#region src/components/EntityEditView.tsx
|
|
5878
|
-
var EntityHistoryView = lazy(() => import("./history-
|
|
5723
|
+
var EntityHistoryView = lazy(() => import("./history-B3cqBzEV.js").then((m) => ({ default: m.EntityHistoryView })));
|
|
5879
5724
|
/**
|
|
5880
5725
|
* This is the default view that is used as the content of a side panel when
|
|
5881
5726
|
* an entity is opened.
|
|
@@ -5927,7 +5772,7 @@ function EntityEditView({ entityId, ...props }) {
|
|
|
5927
5772
|
}
|
|
5928
5773
|
function EntityEditViewInner({ path, entityId, selectedTab: selectedTabProp, collection, parentCollectionSlugs, parentEntityIds, onValuesModified, onSaved, onTabChange, navigateBack, entity, initialDirtyValues, dataLoading, layout = "side_panel", barActions, status, setStatus, formProps, canEdit }) {
|
|
5929
5774
|
const ResolvedFormActions = useComponentOverride("Entity.FormActions", EntityEditViewFormActions);
|
|
5930
|
-
const ResolvedEntityForm = useComponentOverride("Entity.Form",
|
|
5775
|
+
const ResolvedEntityForm = useComponentOverride("Entity.Form", EntityFormBinding);
|
|
5931
5776
|
const ResolvedCollectionView = useComponentOverride("Collection.View", EntityCollectionView);
|
|
5932
5777
|
const context = useRebaseContext();
|
|
5933
5778
|
const urlController = useUrlController();
|
|
@@ -6093,7 +5938,7 @@ function EntityEditViewInner({ path, entityId, selectedTab: selectedTabProp, col
|
|
|
6093
5938
|
}, "history_view") : null;
|
|
6094
5939
|
const subCollectionsViews = subcollections && subcollections.map((subcollection) => {
|
|
6095
5940
|
const subcollectionId = subcollection.slug;
|
|
6096
|
-
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(subcollection
|
|
5941
|
+
const newFullPath = usedEntity ? `${path}/${usedEntity?.id}/${removeInitialAndTrailingSlashes(getCollectionDataPath(subcollection))}` : void 0;
|
|
6097
5942
|
if (activeTab !== subcollectionId) return null;
|
|
6098
5943
|
return /* @__PURE__ */ jsxs("div", {
|
|
6099
5944
|
className: "relative flex-1 h-full overflow-auto w-full",
|
|
@@ -7070,8 +6915,9 @@ function useEntityHistory(params) {
|
|
|
7070
6915
|
//#endregion
|
|
7071
6916
|
//#region src/hooks/navigation/useBuildCollectionRegistryController.tsx
|
|
7072
6917
|
function useBuildCollectionRegistryController(props) {
|
|
7073
|
-
const { userConfigPersistence } = props;
|
|
7074
|
-
const collectionRegistryRef = useRef(new CollectionRegistry());
|
|
6918
|
+
const { userConfigPersistence, dataSources } = props;
|
|
6919
|
+
const collectionRegistryRef = useRef(new CollectionRegistry(void 0, dataSources));
|
|
6920
|
+
if (dataSources) collectionRegistryRef.current.setDataSources(dataSources);
|
|
7075
6921
|
const [initialised, setInitialised] = useState(false);
|
|
7076
6922
|
const getCollection = useCallback((slugOrPath, includeUserOverride = false) => {
|
|
7077
6923
|
const registry = collectionRegistryRef.current;
|
|
@@ -7163,7 +7009,9 @@ function useBuildCollectionRegistryController(props) {
|
|
|
7163
7009
|
return paths;
|
|
7164
7010
|
}, []);
|
|
7165
7011
|
const collections = collectionRegistryRef.current.getCollections();
|
|
7166
|
-
|
|
7012
|
+
useEffect(() => {
|
|
7013
|
+
if (!initialised && collections.length > 0) setInitialised(true);
|
|
7014
|
+
}, [initialised, collections.length]);
|
|
7167
7015
|
return useMemo(() => ({
|
|
7168
7016
|
collections,
|
|
7169
7017
|
initialised,
|
|
@@ -7491,7 +7339,7 @@ var editEntityAction = {
|
|
|
7491
7339
|
if (!entity) throw new Error("INTERNAL: editEntityAction: Entity is undefined");
|
|
7492
7340
|
if (!sideEntityController) throw new Error("INTERNAL: editEntityAction: sideEntityController is undefined");
|
|
7493
7341
|
highlightEntity?.(entity);
|
|
7494
|
-
context
|
|
7342
|
+
context?.analyticsController?.onAnalyticsEvent?.("entity_click", {
|
|
7495
7343
|
path: entity.path,
|
|
7496
7344
|
entityId: entity.id
|
|
7497
7345
|
});
|
|
@@ -7501,6 +7349,7 @@ var editEntityAction = {
|
|
|
7501
7349
|
status: "existing",
|
|
7502
7350
|
entityId: entity.id
|
|
7503
7351
|
});
|
|
7352
|
+
if (!context?.urlController) throw new Error("INTERNAL: editEntityAction: urlController is undefined");
|
|
7504
7353
|
navigateToEntity({
|
|
7505
7354
|
openEntityMode,
|
|
7506
7355
|
collection,
|
|
@@ -7523,11 +7372,12 @@ var copyEntityAction = {
|
|
|
7523
7372
|
if (!entity) throw new Error("INTERNAL: copyEntityAction: Entity is undefined");
|
|
7524
7373
|
if (!sideEntityController) throw new Error("INTERNAL: copyEntityAction: sideEntityController is undefined");
|
|
7525
7374
|
highlightEntity?.(entity);
|
|
7526
|
-
context
|
|
7375
|
+
context?.analyticsController?.onAnalyticsEvent?.("copy_entity_click", {
|
|
7527
7376
|
path: entity.path,
|
|
7528
7377
|
entityId: entity.id
|
|
7529
7378
|
});
|
|
7530
7379
|
const usedPath = path ?? collection?.slug ?? entity.path;
|
|
7380
|
+
if (!context?.urlController) throw new Error("INTERNAL: copyEntityAction: urlController is undefined");
|
|
7531
7381
|
navigateToEntity({
|
|
7532
7382
|
openEntityMode,
|
|
7533
7383
|
collection,
|
|
@@ -7548,7 +7398,7 @@ var deleteEntityAction = {
|
|
|
7548
7398
|
isEnabled: ({ entity }) => Boolean(entity),
|
|
7549
7399
|
onClick({ entity, path, collection, context, selectionController, onCollectionChange, navigateBack, openEntityMode }) {
|
|
7550
7400
|
if (!entity) throw new Error("INTERNAL: deleteEntityAction: Entity is undefined");
|
|
7551
|
-
if (!context
|
|
7401
|
+
if (!context?.dialogsController) throw new Error("INTERNAL: deleteEntityAction: context.dialogsController is undefined");
|
|
7552
7402
|
const { closeDialog } = context.dialogsController.open({
|
|
7553
7403
|
key: "delete_entity_dialog_" + entity.id,
|
|
7554
7404
|
Component: ({ open }) => {
|
|
@@ -7560,10 +7410,10 @@ var deleteEntityAction = {
|
|
|
7560
7410
|
callbacks: collection.callbacks,
|
|
7561
7411
|
open,
|
|
7562
7412
|
onEntityDelete: () => {
|
|
7563
|
-
context
|
|
7413
|
+
context?.analyticsController?.onAnalyticsEvent?.("single_entity_deleted", { path });
|
|
7564
7414
|
selectionController?.setSelectedEntities(selectionController.selectedEntities.filter((e) => e.id !== entity.id));
|
|
7565
7415
|
onCollectionChange?.();
|
|
7566
|
-
if (openEntityMode === "full_screen" && context
|
|
7416
|
+
if (openEntityMode === "full_screen" && context?.urlController) {
|
|
7567
7417
|
const collectionUrl = context.urlController.buildUrlCollectionPath(path);
|
|
7568
7418
|
context.urlController.navigate(collectionUrl, { replace: true });
|
|
7569
7419
|
} else navigateBack?.();
|
|
@@ -7643,7 +7493,7 @@ var resetPasswordAction = {
|
|
|
7643
7493
|
isEnabled: ({ entity }) => Boolean(entity),
|
|
7644
7494
|
onClick({ entity, context }) {
|
|
7645
7495
|
if (!entity) throw new Error("INTERNAL: resetPasswordAction: Entity is undefined");
|
|
7646
|
-
if (!context
|
|
7496
|
+
if (!context?.dialogsController) throw new Error("INTERNAL: resetPasswordAction: context.dialogsController is undefined");
|
|
7647
7497
|
const user = {
|
|
7648
7498
|
uid: entity.id,
|
|
7649
7499
|
email: entity.values?.email,
|
|
@@ -7787,12 +7637,34 @@ async function resolveAppViews(baseViews, authController, data, plugins) {
|
|
|
7787
7637
|
if (plugins) {
|
|
7788
7638
|
for (const plugin of plugins) if (plugin.views && plugin.views.length > 0) resolvedViews = [...resolvedViews, ...plugin.views];
|
|
7789
7639
|
}
|
|
7640
|
+
if (process.env.NODE_ENV !== "production") {
|
|
7641
|
+
const slugCounts = /* @__PURE__ */ new Map();
|
|
7642
|
+
resolvedViews.forEach((v) => {
|
|
7643
|
+
slugCounts.set(v.slug, (slugCounts.get(v.slug) ?? 0) + 1);
|
|
7644
|
+
});
|
|
7645
|
+
slugCounts.forEach((count, slug) => {
|
|
7646
|
+
if (count > 1) console.warn(`[Rebase] Duplicate view slug "${slug}" detected (${count} views). Last-write-wins. Ensure unique slugs across CMS views and plugins.`);
|
|
7647
|
+
});
|
|
7648
|
+
}
|
|
7649
|
+
resolvedViews = filterViewsByRole(resolvedViews, authController);
|
|
7790
7650
|
return resolvedViews;
|
|
7791
7651
|
}
|
|
7652
|
+
/**
|
|
7653
|
+
* Filter views by the `roles` field on AppView.
|
|
7654
|
+
* When `roles` is set, the view is only included if the current user
|
|
7655
|
+
* has at least one of the listed roles. Views without `roles` (or with
|
|
7656
|
+
* an empty array) are always included.
|
|
7657
|
+
*/
|
|
7658
|
+
function filterViewsByRole(views, authController) {
|
|
7659
|
+
const userRoles = authController.user?.roles ?? [];
|
|
7660
|
+
return views.filter((view) => {
|
|
7661
|
+
if (!view.roles || view.roles.length === 0) return true;
|
|
7662
|
+
return view.roles.some((role) => userRoles.includes(role));
|
|
7663
|
+
});
|
|
7664
|
+
}
|
|
7792
7665
|
//#endregion
|
|
7793
7666
|
//#region src/hooks/navigation/utils.ts
|
|
7794
7667
|
var NAVIGATION_DEFAULT_GROUP_NAME = "Views";
|
|
7795
|
-
var NAVIGATION_ADMIN_GROUP_NAME = "Admin";
|
|
7796
7668
|
function getGroup(collectionOrView) {
|
|
7797
7669
|
const trimmed = collectionOrView.group?.trim();
|
|
7798
7670
|
if (!trimmed || trimmed === "") return NAVIGATION_DEFAULT_GROUP_NAME;
|
|
@@ -7801,7 +7673,8 @@ function getGroup(collectionOrView) {
|
|
|
7801
7673
|
function computeNavigationGroups({ navigationGroupMappings, collections, views, plugins }) {
|
|
7802
7674
|
let result = navigationGroupMappings ? navigationGroupMappings.map((g) => ({
|
|
7803
7675
|
name: g.name,
|
|
7804
|
-
entries: [...g.entries]
|
|
7676
|
+
entries: [...g.entries],
|
|
7677
|
+
...g.collapsedByDefault !== void 0 && { collapsedByDefault: g.collapsedByDefault }
|
|
7805
7678
|
})) : navigationGroupMappings;
|
|
7806
7679
|
if (plugins) result = plugins.reduce((acc, plugin) => {
|
|
7807
7680
|
if (plugin.hooks?.navigationEntries) plugin.hooks.navigationEntries.forEach((entry) => {
|
|
@@ -7891,48 +7764,38 @@ function areCollectionsEqual(a, b, visitedSlugs = []) {
|
|
|
7891
7764
|
return deepEqual(Object.fromEntries(Object.entries(a).filter(([k, v]) => typeof v !== "function" && k !== "subcollections")), Object.fromEntries(Object.entries(b).filter(([k, v]) => typeof v !== "function" && k !== "subcollections")));
|
|
7892
7765
|
}
|
|
7893
7766
|
//#endregion
|
|
7894
|
-
//#region src/hooks/navigation/
|
|
7767
|
+
//#region src/hooks/navigation/useAsyncResolver.ts
|
|
7895
7768
|
/**
|
|
7896
|
-
*
|
|
7897
|
-
*
|
|
7898
|
-
*
|
|
7899
|
-
* When userManagement is provided, the default users collection is always
|
|
7900
|
-
* prepended. Developer collections override via generic slug-based dedup
|
|
7901
|
-
* (Map keyed by slug, last-write-wins). No hardcoded string checks.
|
|
7769
|
+
* Generic hook that resolves an async value with loading/error/refresh
|
|
7770
|
+
* state management, cancellation on unmount or deps change, and
|
|
7771
|
+
* ref-based change detection to prevent unnecessary state updates.
|
|
7902
7772
|
*
|
|
7903
|
-
*
|
|
7904
|
-
*
|
|
7773
|
+
* Extracted from the common pattern in useResolvedCollections and
|
|
7774
|
+
* useResolvedViews.
|
|
7905
7775
|
*/
|
|
7906
|
-
function
|
|
7907
|
-
const
|
|
7776
|
+
function useAsyncResolver({ resolver, initialValue, isEqual, deps, disabled }) {
|
|
7777
|
+
const [data, setData] = useState(initialValue);
|
|
7908
7778
|
const [loading, setLoading] = useState(true);
|
|
7909
7779
|
const [error, setError] = useState(void 0);
|
|
7910
|
-
const [resolvedCollections, setResolvedCollections] = useState([]);
|
|
7911
7780
|
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
|
7912
7781
|
const refresh = useCallback(() => {
|
|
7913
7782
|
setRefreshTrigger((prev) => prev + 1);
|
|
7914
7783
|
}, []);
|
|
7915
|
-
const dataRef = useRef(
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
|
|
7919
|
-
|
|
7920
|
-
pluginsRef.current = plugins;
|
|
7921
|
-
const resolvedCollectionsRef = useRef([]);
|
|
7922
|
-
const initialLoading = authController.initialLoading;
|
|
7923
|
-
const user = authController.user;
|
|
7784
|
+
const dataRef = useRef(initialValue);
|
|
7785
|
+
const resolverRef = useRef(resolver);
|
|
7786
|
+
resolverRef.current = resolver;
|
|
7787
|
+
const isEqualRef = useRef(isEqual);
|
|
7788
|
+
isEqualRef.current = isEqual;
|
|
7924
7789
|
useEffect(() => {
|
|
7925
|
-
if (disabled
|
|
7790
|
+
if (disabled) return;
|
|
7926
7791
|
let cancelled = false;
|
|
7927
7792
|
(async () => {
|
|
7928
7793
|
try {
|
|
7929
|
-
const
|
|
7794
|
+
const result = await resolverRef.current();
|
|
7930
7795
|
if (cancelled) return;
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
resolvedCollectionsRef.current = deduped;
|
|
7935
|
-
setResolvedCollections(deduped);
|
|
7796
|
+
if (!isEqualRef.current(dataRef.current, result)) {
|
|
7797
|
+
dataRef.current = result;
|
|
7798
|
+
setData(result);
|
|
7936
7799
|
}
|
|
7937
7800
|
setError(void 0);
|
|
7938
7801
|
} catch (e) {
|
|
@@ -7948,20 +7811,66 @@ function useResolvedCollections(props) {
|
|
|
7948
7811
|
cancelled = true;
|
|
7949
7812
|
};
|
|
7950
7813
|
}, [
|
|
7951
|
-
|
|
7952
|
-
disabled,
|
|
7953
|
-
collectionRegistryController.collectionRegistryRef,
|
|
7814
|
+
...deps,
|
|
7954
7815
|
refreshTrigger,
|
|
7955
|
-
|
|
7956
|
-
|
|
7816
|
+
disabled
|
|
7817
|
+
]);
|
|
7818
|
+
return useMemo(() => ({
|
|
7819
|
+
data,
|
|
7820
|
+
loading,
|
|
7821
|
+
error,
|
|
7822
|
+
refresh
|
|
7823
|
+
}), [
|
|
7824
|
+
data,
|
|
7825
|
+
loading,
|
|
7826
|
+
error,
|
|
7827
|
+
refresh
|
|
7957
7828
|
]);
|
|
7829
|
+
}
|
|
7830
|
+
//#endregion
|
|
7831
|
+
//#region src/hooks/navigation/useResolvedCollections.ts
|
|
7832
|
+
/**
|
|
7833
|
+
* Hook that resolves collection props (which may be async builders or arrays)
|
|
7834
|
+
* into concrete EntityCollection[], and registers them with the CollectionRegistry.
|
|
7835
|
+
*
|
|
7836
|
+
* When userManagement is provided, the default users collection is always
|
|
7837
|
+
* prepended. Developer collections override via generic slug-based dedup
|
|
7838
|
+
* (Map keyed by slug, last-write-wins). No hardcoded string checks.
|
|
7839
|
+
*
|
|
7840
|
+
* Uses refs for potentially-unstable dependencies (driver, authController,
|
|
7841
|
+
* plugins) to avoid re-triggering effects when their object identity changes.
|
|
7842
|
+
*/
|
|
7843
|
+
function useResolvedCollections(props) {
|
|
7844
|
+
const { authController, collections: collectionsProp, data, plugins, disabled, collectionRegistryController } = props;
|
|
7845
|
+
const userIdentity = authController.user ? `${authController.user.uid}:${(authController.user.roles ?? []).sort().join(",")}` : null;
|
|
7846
|
+
const dataRef = useRef(data);
|
|
7847
|
+
dataRef.current = data;
|
|
7848
|
+
const authControllerRef = useRef(authController);
|
|
7849
|
+
authControllerRef.current = authController;
|
|
7850
|
+
const pluginsRef = useRef(plugins);
|
|
7851
|
+
pluginsRef.current = plugins;
|
|
7852
|
+
const { data: collections, loading, error, refresh } = useAsyncResolver({
|
|
7853
|
+
resolver: async () => {
|
|
7854
|
+
const deduped = [...await resolveCollections(collectionsProp, authControllerRef.current, dataRef.current, pluginsRef.current)];
|
|
7855
|
+
if (collectionRegistryController.collectionRegistryRef.current.registerMultiple(deduped)) console.debug("Collections have changed", deduped);
|
|
7856
|
+
return deduped;
|
|
7857
|
+
},
|
|
7858
|
+
initialValue: [],
|
|
7859
|
+
isEqual: areCollectionListsEqual,
|
|
7860
|
+
deps: [
|
|
7861
|
+
collectionsProp,
|
|
7862
|
+
userIdentity,
|
|
7863
|
+
disabled
|
|
7864
|
+
],
|
|
7865
|
+
disabled: disabled || authController.initialLoading
|
|
7866
|
+
});
|
|
7958
7867
|
return useMemo(() => ({
|
|
7959
|
-
collections
|
|
7868
|
+
collections,
|
|
7960
7869
|
loading,
|
|
7961
7870
|
error,
|
|
7962
7871
|
refresh
|
|
7963
7872
|
}), [
|
|
7964
|
-
|
|
7873
|
+
collections,
|
|
7965
7874
|
loading,
|
|
7966
7875
|
error,
|
|
7967
7876
|
refresh
|
|
@@ -7982,6 +7891,13 @@ function viewSlugsEqual(a, b) {
|
|
|
7982
7891
|
return true;
|
|
7983
7892
|
}
|
|
7984
7893
|
/**
|
|
7894
|
+
* Equality check for the combined views data.
|
|
7895
|
+
* Compares both views and adminViews by slug identity.
|
|
7896
|
+
*/
|
|
7897
|
+
function areResolvedViewsEqual(a, b) {
|
|
7898
|
+
return viewSlugsEqual(a.views, b.views) && viewSlugsEqual(a.adminViews, b.adminViews);
|
|
7899
|
+
}
|
|
7900
|
+
/**
|
|
7985
7901
|
* Hook that resolves view and admin view props (which may be async builders or arrays)
|
|
7986
7902
|
* into concrete AppView[].
|
|
7987
7903
|
*
|
|
@@ -7990,16 +7906,7 @@ function viewSlugsEqual(a, b) {
|
|
|
7990
7906
|
*/
|
|
7991
7907
|
function useResolvedViews(props) {
|
|
7992
7908
|
const { authController, views: viewsProp, adminViews: adminViewsProp, data, plugins, adminMode = "content", effectiveRoleController } = props;
|
|
7993
|
-
const
|
|
7994
|
-
const [error, setError] = useState(void 0);
|
|
7995
|
-
const [resolvedViews, setResolvedViews] = useState(void 0);
|
|
7996
|
-
const [resolvedAdminViews, setResolvedAdminViews] = useState(void 0);
|
|
7997
|
-
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
|
7998
|
-
const refresh = useCallback(() => {
|
|
7999
|
-
setRefreshTrigger((prev) => prev + 1);
|
|
8000
|
-
}, []);
|
|
8001
|
-
const viewsRef = useRef(void 0);
|
|
8002
|
-
const adminViewsRef = useRef(void 0);
|
|
7909
|
+
const userIdentity = authController.user ? `${authController.user.uid}:${(authController.user.roles ?? []).sort().join(",")}` : null;
|
|
8003
7910
|
const dataRef = useRef(data);
|
|
8004
7911
|
dataRef.current = data;
|
|
8005
7912
|
const authControllerRef = useRef(authController);
|
|
@@ -8023,51 +7930,35 @@ function useResolvedViews(props) {
|
|
|
8023
7930
|
const resolvedAuthControllerRef = useRef(resolvedAuthController);
|
|
8024
7931
|
resolvedAuthControllerRef.current = resolvedAuthController;
|
|
8025
7932
|
const initialLoading = resolvedAuthController.initialLoading;
|
|
8026
|
-
const
|
|
8027
|
-
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
} finally {
|
|
8048
|
-
if (!cancelled) setLoading(false);
|
|
8049
|
-
}
|
|
8050
|
-
})();
|
|
8051
|
-
return () => {
|
|
8052
|
-
cancelled = true;
|
|
8053
|
-
};
|
|
8054
|
-
}, [
|
|
8055
|
-
viewsProp,
|
|
8056
|
-
adminViewsProp,
|
|
8057
|
-
refreshTrigger,
|
|
8058
|
-
adminMode,
|
|
8059
|
-
initialLoading,
|
|
8060
|
-
user
|
|
8061
|
-
]);
|
|
7933
|
+
const { data: resolvedData, loading, error, refresh } = useAsyncResolver({
|
|
7934
|
+
resolver: async () => {
|
|
7935
|
+
const [newViews, newAdminViews] = await Promise.all([resolveAppViews(viewsProp, resolvedAuthControllerRef.current, dataRef.current, pluginsRef.current), resolveAppViews(adminViewsProp, resolvedAuthControllerRef.current, dataRef.current)]);
|
|
7936
|
+
return {
|
|
7937
|
+
views: newViews,
|
|
7938
|
+
adminViews: newAdminViews
|
|
7939
|
+
};
|
|
7940
|
+
},
|
|
7941
|
+
initialValue: {
|
|
7942
|
+
views: void 0,
|
|
7943
|
+
adminViews: void 0
|
|
7944
|
+
},
|
|
7945
|
+
isEqual: areResolvedViewsEqual,
|
|
7946
|
+
deps: [
|
|
7947
|
+
viewsProp,
|
|
7948
|
+
adminViewsProp,
|
|
7949
|
+
adminMode,
|
|
7950
|
+
userIdentity
|
|
7951
|
+
],
|
|
7952
|
+
disabled: initialLoading
|
|
7953
|
+
});
|
|
8062
7954
|
return useMemo(() => ({
|
|
8063
|
-
views:
|
|
8064
|
-
adminViews:
|
|
7955
|
+
views: resolvedData.views,
|
|
7956
|
+
adminViews: resolvedData.adminViews,
|
|
8065
7957
|
loading,
|
|
8066
7958
|
error,
|
|
8067
7959
|
refresh
|
|
8068
7960
|
}), [
|
|
8069
|
-
|
|
8070
|
-
resolvedAdminViews,
|
|
7961
|
+
resolvedData,
|
|
8071
7962
|
loading,
|
|
8072
7963
|
error,
|
|
8073
7964
|
refresh
|
|
@@ -8176,21 +8067,24 @@ function useTopLevelNavigation(props) {
|
|
|
8176
8067
|
}, [])
|
|
8177
8068
|
];
|
|
8178
8069
|
const groupOrderValue = (groupName) => {
|
|
8179
|
-
if (groupName === "Admin") return 1;
|
|
8070
|
+
if (groupName === "Admin" || groupName === "Settings" || groupName === "Admin") return 1;
|
|
8180
8071
|
return 0;
|
|
8181
8072
|
};
|
|
8182
|
-
navigationEntries = navigationEntries.sort((a, b) => {
|
|
8183
|
-
return groupOrderValue(a.group) - groupOrderValue(b.group);
|
|
8184
|
-
});
|
|
8185
8073
|
const usedViewsOrder = viewsOrder ?? navigationEntriesOrder;
|
|
8186
|
-
|
|
8187
|
-
const
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8193
|
-
|
|
8074
|
+
navigationEntries = navigationEntries.sort((a, b) => {
|
|
8075
|
+
const groupDiff = groupOrderValue(a.group) - groupOrderValue(b.group);
|
|
8076
|
+
if (groupDiff !== 0) return groupDiff;
|
|
8077
|
+
if (usedViewsOrder) {
|
|
8078
|
+
const getSortPath = (navEntry) => typeof navEntry.slug === "string" ? navEntry.slug : navEntry.slug[0];
|
|
8079
|
+
const aIndex = usedViewsOrder.indexOf(getSortPath(a));
|
|
8080
|
+
const bIndex = usedViewsOrder.indexOf(getSortPath(b));
|
|
8081
|
+
if (aIndex !== -1 || bIndex !== -1) {
|
|
8082
|
+
if (aIndex === -1) return 1;
|
|
8083
|
+
if (bIndex === -1) return -1;
|
|
8084
|
+
return aIndex - bIndex;
|
|
8085
|
+
}
|
|
8086
|
+
}
|
|
8087
|
+
return 0;
|
|
8194
8088
|
});
|
|
8195
8089
|
const collectedGroupsFromEntries = navigationEntries.map((e) => e.group).filter(Boolean);
|
|
8196
8090
|
const groupsFromMappings = finalNavigationGroupMappings.map((g) => g.name);
|
|
@@ -8201,8 +8095,8 @@ function useTopLevelNavigation(props) {
|
|
|
8201
8095
|
...additionalGroups
|
|
8202
8096
|
];
|
|
8203
8097
|
const uniqueGroupsArray = [...new Set(allDefinedGroups)];
|
|
8204
|
-
const
|
|
8205
|
-
const uniqueGroups = [...uniqueGroupsArray.filter((g) => g !==
|
|
8098
|
+
const lastGroups = uniqueGroupsArray.filter((g) => g === "Admin" || g === "Settings" || g === "Admin");
|
|
8099
|
+
const uniqueGroups = [...uniqueGroupsArray.filter((g) => g !== "Admin" && g !== "Settings" && g !== "Admin"), ...lastGroups];
|
|
8206
8100
|
const computedTopLevelNav = {
|
|
8207
8101
|
allowDragAndDrop: plugins?.some((plugin) => plugin.hooks?.allowDragAndDrop) ?? false,
|
|
8208
8102
|
navigationEntries,
|
|
@@ -8269,9 +8163,15 @@ function useBuildNavigationStateController(props) {
|
|
|
8269
8163
|
adminMode,
|
|
8270
8164
|
collectionRegistryController
|
|
8271
8165
|
});
|
|
8166
|
+
const pendingRefreshRef = useRef(false);
|
|
8272
8167
|
const refreshNavigation = useCallback(() => {
|
|
8273
|
-
|
|
8274
|
-
|
|
8168
|
+
if (pendingRefreshRef.current) return;
|
|
8169
|
+
pendingRefreshRef.current = true;
|
|
8170
|
+
queueMicrotask(() => {
|
|
8171
|
+
pendingRefreshRef.current = false;
|
|
8172
|
+
refreshCollections();
|
|
8173
|
+
refreshViews();
|
|
8174
|
+
});
|
|
8275
8175
|
}, [refreshCollections, refreshViews]);
|
|
8276
8176
|
return useMemo(() => ({
|
|
8277
8177
|
views,
|
|
@@ -8279,8 +8179,7 @@ function useBuildNavigationStateController(props) {
|
|
|
8279
8179
|
topLevelNavigation,
|
|
8280
8180
|
loading: collectionsLoading || viewsLoading,
|
|
8281
8181
|
navigationLoadingError: collectionsError ?? viewsError,
|
|
8282
|
-
refreshNavigation
|
|
8283
|
-
plugins
|
|
8182
|
+
refreshNavigation
|
|
8284
8183
|
}), [
|
|
8285
8184
|
views,
|
|
8286
8185
|
adminViews,
|
|
@@ -8289,8 +8188,7 @@ function useBuildNavigationStateController(props) {
|
|
|
8289
8188
|
viewsLoading,
|
|
8290
8189
|
collectionsError,
|
|
8291
8190
|
viewsError,
|
|
8292
|
-
refreshNavigation
|
|
8293
|
-
plugins
|
|
8191
|
+
refreshNavigation
|
|
8294
8192
|
]);
|
|
8295
8193
|
}
|
|
8296
8194
|
//#endregion
|
|
@@ -8352,14 +8250,18 @@ var BreadcrumbContext = React.createContext({
|
|
|
8352
8250
|
var BreadcrumbsProvider = ({ children }) => {
|
|
8353
8251
|
const [breadcrumbs, setBreadcrumbs] = useState([]);
|
|
8354
8252
|
const set = useCallback((props) => {
|
|
8355
|
-
setBreadcrumbs((prev) =>
|
|
8356
|
-
const
|
|
8357
|
-
|
|
8358
|
-
|
|
8359
|
-
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8253
|
+
setBreadcrumbs((prev) => {
|
|
8254
|
+
const next = props.breadcrumbs.map((newEntry) => {
|
|
8255
|
+
const prevEntry = newEntry.id ? prev.find((p) => p.id === newEntry.id) : void 0;
|
|
8256
|
+
if (prevEntry && newEntry.count === null && typeof prevEntry.count === "number") return {
|
|
8257
|
+
...newEntry,
|
|
8258
|
+
count: prevEntry.count
|
|
8259
|
+
};
|
|
8260
|
+
return newEntry;
|
|
8261
|
+
});
|
|
8262
|
+
if (prev.length === next.length && prev.every((p, i) => p.title === next[i].title && p.url === next[i].url && p.id === next[i].id && p.count === next[i].count)) return prev;
|
|
8263
|
+
return next;
|
|
8264
|
+
});
|
|
8363
8265
|
}, []);
|
|
8364
8266
|
const updateCount = useCallback((id, count) => {
|
|
8365
8267
|
setBreadcrumbs((prev) => prev.map((entry) => entry.id === id ? {
|
|
@@ -8502,11 +8404,7 @@ function SideDialogView({ offsetPosition, panel, panelIndex, isTopPanel }) {
|
|
|
8502
8404
|
overlayStyle: { zIndex: 40 + panelIndex * 10 },
|
|
8503
8405
|
style: { zIndex: 45 + panelIndex * 10 },
|
|
8504
8406
|
onOpenChange: (open) => {
|
|
8505
|
-
if (!open)
|
|
8506
|
-
const suggestionMenu = document.querySelector("[data-suggestion-menu=\"true\"]");
|
|
8507
|
-
if (suggestionMenu && window.getComputedStyle(suggestionMenu).visibility !== "hidden") return;
|
|
8508
|
-
onCloseRequest();
|
|
8509
|
-
}
|
|
8407
|
+
if (!open) onCloseRequest();
|
|
8510
8408
|
},
|
|
8511
8409
|
onPointerDownOutside: !isTopPanel ? preventDismiss : void 0,
|
|
8512
8410
|
onInteractOutside: !isTopPanel ? preventDismiss : void 0,
|
|
@@ -8736,7 +8634,7 @@ function EntitySelectionDialogActions({ collection, path, onClear, onNewClick })
|
|
|
8736
8634
|
//#region src/components/EntityCollectionView/hooks/useCollectionInlineEditor.ts
|
|
8737
8635
|
function useCollectionInlineEditor({ path, collection, dataClient, context }) {
|
|
8738
8636
|
const uniqueFieldValidator = useCallback(async ({ name, value, property, entityId }) => {
|
|
8739
|
-
const conflictingEntities = (await dataClient.collection(path).find({ where: { [name]: "
|
|
8637
|
+
const conflictingEntities = (await dataClient.collection(path).find({ where: { [name]: ["==", value] } })).data;
|
|
8740
8638
|
return conflictingEntities.length === 0 || conflictingEntities.length === 1 && conflictingEntities[0].id === entityId;
|
|
8741
8639
|
}, [path, dataClient]);
|
|
8742
8640
|
const onValueChange = useCallback(({ value, propertyKey, onValueUpdated, setError, data: entity }) => {
|
|
@@ -8969,7 +8867,7 @@ function FormEntry({ propertyKey, widthPercentage = 100, children }) {
|
|
|
8969
8867
|
//#region src/form/components/FormLayout.tsx
|
|
8970
8868
|
function FormLayout({ children, className }) {
|
|
8971
8869
|
return /* @__PURE__ */ jsx("div", {
|
|
8972
|
-
className: cls("flex flex-wrap gap-x-4 w-full space-y-
|
|
8870
|
+
className: cls("flex flex-wrap gap-x-4 w-full space-y-6", className),
|
|
8973
8871
|
children
|
|
8974
8872
|
});
|
|
8975
8873
|
}
|
|
@@ -8994,7 +8892,7 @@ function ReadOnlyFieldBinding({ propertyKey, value, error, showError, minimalist
|
|
|
8994
8892
|
className: "h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"
|
|
8995
8893
|
}),
|
|
8996
8894
|
/* @__PURE__ */ jsx("div", {
|
|
8997
|
-
className: cls(
|
|
8895
|
+
className: cls("w-full overflow-x-scroll no-scrollbar flex items-center", skipCardWrapper ? "" : "rounded-lg border border-surface-200 dark:border-surface-700 px-4 md:px-6 min-h-10 opacity-80"),
|
|
8998
8896
|
children: /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsx(PropertyPreview, {
|
|
8999
8897
|
propertyKey,
|
|
9000
8898
|
value,
|
|
@@ -9281,7 +9179,7 @@ var buildIdsMap = (value) => value && Array.isArray(value) && value.length > 0 ?
|
|
|
9281
9179
|
...a,
|
|
9282
9180
|
...b
|
|
9283
9181
|
}), {}) : {};
|
|
9284
|
-
function SortableItem
|
|
9182
|
+
function SortableItem({ id, index, size, disabled, buildEntry, remove, copy, addInIndex, canAddElements, sortable, storedProps, updateItemCustomProps }) {
|
|
9285
9183
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
|
|
9286
9184
|
return /* @__PURE__ */ jsx(ArrayContainerItem, {
|
|
9287
9185
|
nodeRef: setNodeRef,
|
|
@@ -9510,7 +9408,7 @@ function ArrayContainer({ droppableId, addLabel, value, disabled = false, buildE
|
|
|
9510
9408
|
children: /* @__PURE__ */ jsxs("div", {
|
|
9511
9409
|
className: cls("space-y-1", className),
|
|
9512
9410
|
id: droppableId,
|
|
9513
|
-
children: [hasValue && internalIds.map((internalId, index) => /* @__PURE__ */ jsx(SortableItem
|
|
9411
|
+
children: [hasValue && internalIds.map((internalId, index) => /* @__PURE__ */ jsx(SortableItem, {
|
|
9514
9412
|
id: internalId,
|
|
9515
9413
|
index,
|
|
9516
9414
|
size,
|
|
@@ -10433,8 +10331,18 @@ function MarkdownEditorFieldBinding({ property, propertyKey, value, setValue, in
|
|
|
10433
10331
|
const disabled = disabledProp || isSubmitting;
|
|
10434
10332
|
const highlight = customProps?.highlight;
|
|
10435
10333
|
const editorProps = customProps?.editorProps;
|
|
10436
|
-
const
|
|
10334
|
+
const defaultStorageSource = useStorageSource();
|
|
10437
10335
|
const storage = property.storage;
|
|
10336
|
+
const storageSources = useStorageSources();
|
|
10337
|
+
const storageSource = useMemo(() => resolveStorageSource({
|
|
10338
|
+
sourceKey: storage?.storageSource,
|
|
10339
|
+
sources: storageSources.sources,
|
|
10340
|
+
defaultSource: defaultStorageSource
|
|
10341
|
+
}), [
|
|
10342
|
+
storage?.storageSource,
|
|
10343
|
+
storageSources.sources,
|
|
10344
|
+
defaultStorageSource
|
|
10345
|
+
]);
|
|
10438
10346
|
const entityValues = context.values;
|
|
10439
10347
|
const entityId = context.entityId;
|
|
10440
10348
|
const path = context.path;
|
|
@@ -10645,91 +10553,6 @@ function MultiSelectFieldBinding({ propertyKey, value, setValue, error, showErro
|
|
|
10645
10553
|
})] });
|
|
10646
10554
|
}
|
|
10647
10555
|
//#endregion
|
|
10648
|
-
//#region src/form/field_bindings/ReferenceAsStringFieldBinding.tsx
|
|
10649
|
-
/**
|
|
10650
|
-
* Field that opens a reference selection dialog and stores the entity ID as a string.
|
|
10651
|
-
*
|
|
10652
|
-
* This is one of the internal components that get mapped natively inside forms
|
|
10653
|
-
* and tables to the specified properties.
|
|
10654
|
-
* @group Form fields
|
|
10655
|
-
*/
|
|
10656
|
-
function ReferenceAsStringFieldBinding(props) {
|
|
10657
|
-
if (typeof props.property.reference?.path !== "string") return /* @__PURE__ */ jsx(ReadOnlyFieldBinding, { ...props });
|
|
10658
|
-
return /* @__PURE__ */ jsx(ReferenceAsStringFieldBindingInternal, { ...props });
|
|
10659
|
-
}
|
|
10660
|
-
function ReferenceAsStringFieldBindingInternal({ propertyKey, value, setValue, error, showError, isSubmitting, disabled, minimalistView, property, includeDescription, size = "medium" }) {
|
|
10661
|
-
if (!property.reference?.path) throw new Error("Property path is required for ReferenceAsStringFieldBinding");
|
|
10662
|
-
useClearRestoreValue({
|
|
10663
|
-
property,
|
|
10664
|
-
value,
|
|
10665
|
-
setValue
|
|
10666
|
-
});
|
|
10667
|
-
const collectionRegistryController = useCollectionRegistryController();
|
|
10668
|
-
const path = property.reference.path;
|
|
10669
|
-
const collection = useMemo(() => {
|
|
10670
|
-
return path ? collectionRegistryController.getCollection(path) : void 0;
|
|
10671
|
-
}, [path]);
|
|
10672
|
-
const referenceValue = useMemo(() => {
|
|
10673
|
-
if (value && path) return new EntityReference({
|
|
10674
|
-
id: value,
|
|
10675
|
-
path
|
|
10676
|
-
});
|
|
10677
|
-
}, [value, path]);
|
|
10678
|
-
if (!collection) throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
|
|
10679
|
-
const referenceDialogController = useEntitySelectionDialog({
|
|
10680
|
-
multiselect: false,
|
|
10681
|
-
path,
|
|
10682
|
-
collection,
|
|
10683
|
-
onSingleEntitySelected: useCallback((e) => {
|
|
10684
|
-
setValue(e ? String(e.id) : null);
|
|
10685
|
-
}, [setValue]),
|
|
10686
|
-
selectedEntityIds: value ? [value] : void 0,
|
|
10687
|
-
fixedFilter: property.reference?.fixedFilter
|
|
10688
|
-
});
|
|
10689
|
-
const onEntryClick = (e) => {
|
|
10690
|
-
e.preventDefault();
|
|
10691
|
-
referenceDialogController.open();
|
|
10692
|
-
};
|
|
10693
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10694
|
-
!minimalistView && /* @__PURE__ */ jsx(LabelWithIconAndTooltip, {
|
|
10695
|
-
propertyKey,
|
|
10696
|
-
icon: getIconForProperty(property, "small"),
|
|
10697
|
-
required: property.validation?.required,
|
|
10698
|
-
title: property.name ?? propertyKey,
|
|
10699
|
-
className: "h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"
|
|
10700
|
-
}),
|
|
10701
|
-
!collection && /* @__PURE__ */ jsx(ErrorView, { error: "The specified collection does not exist. Check console" }),
|
|
10702
|
-
collection && /* @__PURE__ */ jsxs(Fragment, { children: [referenceValue && /* @__PURE__ */ jsx(ReferencePreview, {
|
|
10703
|
-
disabled: !path,
|
|
10704
|
-
previewProperties: property.reference?.ui?.previewProperties,
|
|
10705
|
-
hover: !disabled,
|
|
10706
|
-
size,
|
|
10707
|
-
onClick: disabled || isSubmitting ? void 0 : onEntryClick,
|
|
10708
|
-
reference: referenceValue,
|
|
10709
|
-
includeEntityLink: property.reference?.includeEntityLink,
|
|
10710
|
-
includeId: property.reference?.includeId
|
|
10711
|
-
}), !value && /* @__PURE__ */ jsx("div", {
|
|
10712
|
-
className: "justify-center text-left",
|
|
10713
|
-
children: /* @__PURE__ */ jsxs(EntityPreviewContainer, {
|
|
10714
|
-
className: cls("px-6 h-16 text-sm font-medium flex items-center gap-6", disabled || isSubmitting ? "text-surface-accent-500" : "cursor-pointer text-surface-accent-700 dark:text-surface-accent-300 hover:bg-surface-accent-50 dark:hover:bg-surface-800 group-hover:bg-surface-accent-50 dark:group-hover:bg-surface-800"),
|
|
10715
|
-
onClick: onEntryClick,
|
|
10716
|
-
size: "medium",
|
|
10717
|
-
children: [/* @__PURE__ */ jsx(IconForView, {
|
|
10718
|
-
collectionOrView: collection,
|
|
10719
|
-
className: "text-surface-300 dark:text-surface-600"
|
|
10720
|
-
}), `Edit ${property.name}`.toUpperCase()]
|
|
10721
|
-
})
|
|
10722
|
-
})] }),
|
|
10723
|
-
/* @__PURE__ */ jsx(FieldHelperText, {
|
|
10724
|
-
includeDescription,
|
|
10725
|
-
showError,
|
|
10726
|
-
error,
|
|
10727
|
-
disabled,
|
|
10728
|
-
property
|
|
10729
|
-
})
|
|
10730
|
-
] });
|
|
10731
|
-
}
|
|
10732
|
-
//#endregion
|
|
10733
10556
|
//#region src/form/field_bindings/ReferenceFieldBinding.tsx
|
|
10734
10557
|
/**
|
|
10735
10558
|
* Field that opens a reference selection dialog.
|
|
@@ -11022,9 +10845,9 @@ var acceptDropClasses = "transition-colors duration-200 ease-[cubic-bezier(0,0,0
|
|
|
11022
10845
|
var rejectDropClasses = "transition-colors duration-200 ease-[cubic-bezier(0,0,0.2,1)] border-2 border-solid border-red-500";
|
|
11023
10846
|
function StorageUploadFieldBinding({ propertyKey, value, setValue, error, showError, autoFocus, minimalistView, property, includeDescription, context, isSubmitting }) {
|
|
11024
10847
|
useAuthController();
|
|
11025
|
-
const storageSource = useStorageSource(
|
|
10848
|
+
const storageSource = useStorageSource();
|
|
11026
10849
|
const disabled = isReadOnly(property) || !!property.ui?.disabled || isSubmitting || context.disabled;
|
|
11027
|
-
const { internalValue, setInternalValue, onFilesAdded, storage, onFileUploadComplete, storagePathBuilder, multipleFilesSupported } = useStorageUploadController({
|
|
10850
|
+
const { internalValue, setInternalValue, onFilesAdded, storage, onFileUploadComplete, storagePathBuilder, multipleFilesSupported, resolvedStorageSource } = useStorageUploadController({
|
|
11028
10851
|
entityValues: context.values,
|
|
11029
10852
|
entityId: context.entityId,
|
|
11030
10853
|
path: context.path,
|
|
@@ -11048,19 +10871,22 @@ function StorageUploadFieldBinding({ propertyKey, value, setValue, error, showEr
|
|
|
11048
10871
|
title: property.name ?? propertyKey,
|
|
11049
10872
|
className: "h-8 text-text-secondary dark:text-text-secondary-dark ml-3.5"
|
|
11050
10873
|
}),
|
|
11051
|
-
/* @__PURE__ */ jsx(
|
|
11052
|
-
value:
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11058
|
-
|
|
11059
|
-
|
|
11060
|
-
|
|
11061
|
-
|
|
11062
|
-
|
|
11063
|
-
|
|
10874
|
+
/* @__PURE__ */ jsx(StorageSourceContext.Provider, {
|
|
10875
|
+
value: resolvedStorageSource,
|
|
10876
|
+
children: /* @__PURE__ */ jsx(StorageUpload, {
|
|
10877
|
+
value: internalValue,
|
|
10878
|
+
name: propertyKey,
|
|
10879
|
+
disabled: disabled ?? false,
|
|
10880
|
+
autoFocus: autoFocus ?? false,
|
|
10881
|
+
property,
|
|
10882
|
+
onChange: setValue,
|
|
10883
|
+
setInternalValue,
|
|
10884
|
+
onFilesAdded,
|
|
10885
|
+
onFileUploadComplete,
|
|
10886
|
+
storagePathBuilder,
|
|
10887
|
+
storage,
|
|
10888
|
+
multipleFilesSupported
|
|
10889
|
+
})
|
|
11064
10890
|
}),
|
|
11065
10891
|
/* @__PURE__ */ jsx(FieldHelperText, {
|
|
11066
10892
|
includeDescription,
|
|
@@ -11567,7 +11393,7 @@ function MultipleRelationFieldBinding({ propertyKey, value: valueProp, error, sh
|
|
|
11567
11393
|
const { t } = useTranslation();
|
|
11568
11394
|
if (property.type !== "relation") throw Error("RelationFieldBinding expected a property containing a relation");
|
|
11569
11395
|
const parentCollection = context.collection;
|
|
11570
|
-
const capabilities = parentCollection ? getDataSourceCapabilities(parentCollection.
|
|
11396
|
+
const capabilities = parentCollection ? getDataSourceCapabilities(parentCollection.engine) : void 0;
|
|
11571
11397
|
if (!parentCollection || !capabilities?.supportsRelations || !("relations" in parentCollection) || !parentCollection.relations) throw Error("RelationFieldBinding expected a collection with relations support");
|
|
11572
11398
|
const relation = resolveRelationProperty(property, parentCollection.relations, propertyKey).relation;
|
|
11573
11399
|
if (!relation) throw Error("Property relation is required for MultipleRelationFieldBinding");
|
|
@@ -11578,7 +11404,7 @@ function MultipleRelationFieldBinding({ propertyKey, value: valueProp, error, sh
|
|
|
11578
11404
|
}, [setValue]);
|
|
11579
11405
|
const referenceDialogController = useEntitySelectionDialog({
|
|
11580
11406
|
multiselect: true,
|
|
11581
|
-
path: collection
|
|
11407
|
+
path: getCollectionDataPath(collection),
|
|
11582
11408
|
collection,
|
|
11583
11409
|
onMultipleEntitiesSelected,
|
|
11584
11410
|
selectedEntityIds,
|
|
@@ -11664,7 +11490,7 @@ function RelationFieldBinding(props) {
|
|
|
11664
11490
|
const { property, propertyKey, context } = props;
|
|
11665
11491
|
if (property.type !== "relation") throw Error("RelationFieldBinding expected a property containing a relation");
|
|
11666
11492
|
const collection = context.collection;
|
|
11667
|
-
const capabilities = collection ? getDataSourceCapabilities(collection.
|
|
11493
|
+
const capabilities = collection ? getDataSourceCapabilities(collection.engine) : void 0;
|
|
11668
11494
|
if (!collection || !capabilities?.supportsRelations || !("relations" in collection) || !collection.relations) throw Error("RelationFieldBinding expected a collection with relations support");
|
|
11669
11495
|
const relation = resolveRelationProperty(property, collection.relations, propertyKey).relation;
|
|
11670
11496
|
const manyRelation = relation?.cardinality === "many";
|
|
@@ -11724,7 +11550,7 @@ function SingleRelationFieldBinding({ propertyKey, value, size, error, showError
|
|
|
11724
11550
|
}, [setValue]);
|
|
11725
11551
|
const referenceDialogController = useEntitySelectionDialog({
|
|
11726
11552
|
multiselect: false,
|
|
11727
|
-
path: collection
|
|
11553
|
+
path: getCollectionDataPath(collection),
|
|
11728
11554
|
collection,
|
|
11729
11555
|
onSingleEntitySelected,
|
|
11730
11556
|
selectedEntityIds: validValue && normalizedValue ? [normalizedValue.id] : void 0,
|
|
@@ -11996,17 +11822,6 @@ var DEFAULT_FIELD_CONFIGS = {
|
|
|
11996
11822
|
ui: { Field: StorageUploadFieldBinding }
|
|
11997
11823
|
}
|
|
11998
11824
|
},
|
|
11999
|
-
reference_as_string: {
|
|
12000
|
-
key: "reference_as_string",
|
|
12001
|
-
name: "Reference (as string)",
|
|
12002
|
-
description: "The value refers to a different collection (it is saved as a string)",
|
|
12003
|
-
Icon: LinkIcon,
|
|
12004
|
-
color: "#154fb3",
|
|
12005
|
-
property: {
|
|
12006
|
-
type: "string",
|
|
12007
|
-
ui: { Field: ReferenceAsStringFieldBinding }
|
|
12008
|
-
}
|
|
12009
|
-
},
|
|
12010
11825
|
reference: {
|
|
12011
11826
|
key: "reference",
|
|
12012
11827
|
name: "Reference",
|
|
@@ -12152,7 +11967,6 @@ function getDefaultFieldId(property) {
|
|
|
12152
11967
|
else if (property.email) return "email";
|
|
12153
11968
|
else if (property.enum) return "select";
|
|
12154
11969
|
else if (property.userSelect) return "user_select";
|
|
12155
|
-
else if (property.reference) return "reference_as_string";
|
|
12156
11970
|
else return "text_field";
|
|
12157
11971
|
else if (property.type === "number") {
|
|
12158
11972
|
if (property.enum) return "number_select";
|
|
@@ -13237,8 +13051,8 @@ function resolveCollectionSlotKeys(collection, authController, propertyConfigs)
|
|
|
13237
13051
|
const sortedPreviewKeys = hasExplicitOrder ? previewKeys : [...previewKeys].sort((a, b) => {
|
|
13238
13052
|
const propA = collection.properties[a];
|
|
13239
13053
|
const propB = collection.properties[b];
|
|
13240
|
-
const scoreA = propA?.type === "string" ? propA.multiline ? 2 : 1 : 0;
|
|
13241
|
-
return (propB?.type === "string" ? propB.multiline ? 2 : 1 : 0) - scoreA;
|
|
13054
|
+
const scoreA = propA?.type === "string" ? propA.ui?.multiline ? 2 : 1 : 0;
|
|
13055
|
+
return (propB?.type === "string" ? propB.ui?.multiline ? 2 : 1 : 0) - scoreA;
|
|
13242
13056
|
});
|
|
13243
13057
|
return {
|
|
13244
13058
|
titleKey,
|
|
@@ -13431,7 +13245,7 @@ function resolveRelationDisplayName(relation, prop) {
|
|
|
13431
13245
|
return false;
|
|
13432
13246
|
};
|
|
13433
13247
|
const isDisplayCandidate = (p) => {
|
|
13434
|
-
return p.type === "string" && !p.multiline && !p.markdown && !p.storage && !("isId" in p && p.isId) && !isHiddenProp(p);
|
|
13248
|
+
return p.type === "string" && !p.ui?.multiline && !p.ui?.markdown && !p.storage && !("isId" in p && p.isId) && !isHiddenProp(p);
|
|
13435
13249
|
};
|
|
13436
13250
|
for (const pk of [
|
|
13437
13251
|
"name",
|
|
@@ -13592,189 +13406,57 @@ function EntityCard({ entity, collection, onClick, selected, highlighted, onSele
|
|
|
13592
13406
|
//#endregion
|
|
13593
13407
|
//#region src/components/EntityCollectionView/EntityCollectionCardView.tsx
|
|
13594
13408
|
/**
|
|
13595
|
-
* Get grid column classes based on the size.
|
|
13596
|
-
* Smaller size = more columns (smaller cards)
|
|
13597
|
-
* Larger size = fewer columns (larger cards)
|
|
13598
|
-
*/
|
|
13599
|
-
function getGridColumnsClass(size) {
|
|
13600
|
-
switch (size) {
|
|
13601
|
-
case "xs": return "grid-cols-4 sm:grid-cols-5 md:grid-cols-6 lg:grid-cols-8 xl:grid-cols-10";
|
|
13602
|
-
case "s": return "grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 xl:grid-cols-8";
|
|
13603
|
-
case "m": return "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6";
|
|
13604
|
-
case "l": return "grid-cols-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4";
|
|
13605
|
-
case "xl": return "grid-cols-1 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3";
|
|
13606
|
-
default: return "grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6";
|
|
13607
|
-
}
|
|
13608
|
-
}
|
|
13609
|
-
function getScrollParent$1(element) {
|
|
13610
|
-
if (!element) return null;
|
|
13611
|
-
let parent = element.parentElement;
|
|
13612
|
-
while (parent) {
|
|
13613
|
-
const overflowY = window.getComputedStyle(parent).overflowY;
|
|
13614
|
-
if (overflowY === "auto" || overflowY === "scroll") return parent;
|
|
13615
|
-
parent = parent.parentElement;
|
|
13616
|
-
}
|
|
13617
|
-
return document.documentElement;
|
|
13618
|
-
}
|
|
13619
|
-
/**
|
|
13620
13409
|
* Card grid view for displaying entities with infinite scroll.
|
|
13621
13410
|
* Alternative to the EntityCollectionTable for visual browsing.
|
|
13622
13411
|
*/
|
|
13623
13412
|
function EntityCollectionCardView({ collection, tableController, onEntityClick, selectionController, selectionEnabled = true, highlightedEntities, emptyComponent, onScroll, initialScroll, size = "m" }) {
|
|
13624
13413
|
const ResolvedEntityCard = useComponentOverride("Collection.Card", EntityCard);
|
|
13625
|
-
const containerRef = useRef(null);
|
|
13626
|
-
const hasRestoredScroll = useRef(false);
|
|
13627
13414
|
const { data, dataLoading, noMoreToLoad, dataLoadingError, itemCount, setItemCount, pageSize = 50, paginationEnabled } = tableController;
|
|
13628
|
-
const isLoadingMore = useRef(false);
|
|
13629
|
-
const paginationStateRef = useRef({
|
|
13630
|
-
paginationEnabled,
|
|
13631
|
-
noMoreToLoad,
|
|
13632
|
-
dataLoading,
|
|
13633
|
-
itemCount,
|
|
13634
|
-
pageSize
|
|
13635
|
-
});
|
|
13636
|
-
useEffect(() => {
|
|
13637
|
-
paginationStateRef.current = {
|
|
13638
|
-
paginationEnabled,
|
|
13639
|
-
noMoreToLoad,
|
|
13640
|
-
dataLoading,
|
|
13641
|
-
itemCount,
|
|
13642
|
-
pageSize
|
|
13643
|
-
};
|
|
13644
|
-
}, [
|
|
13645
|
-
paginationEnabled,
|
|
13646
|
-
noMoreToLoad,
|
|
13647
|
-
dataLoading,
|
|
13648
|
-
itemCount,
|
|
13649
|
-
pageSize
|
|
13650
|
-
]);
|
|
13651
|
-
useEffect(() => {
|
|
13652
|
-
if (!dataLoading) isLoadingMore.current = false;
|
|
13653
|
-
}, [dataLoading]);
|
|
13654
|
-
useEffect(() => {
|
|
13655
|
-
const el = containerRef.current;
|
|
13656
|
-
if (!el) return;
|
|
13657
|
-
const scrollEl = getScrollParent$1(el);
|
|
13658
|
-
if (!scrollEl) return;
|
|
13659
|
-
let rafId = null;
|
|
13660
|
-
const update = () => {
|
|
13661
|
-
rafId = null;
|
|
13662
|
-
const { paginationEnabled: pe, noMoreToLoad: nm, itemCount: ic, pageSize: ps } = paginationStateRef.current;
|
|
13663
|
-
if (pe && !nm && !isLoadingMore.current && scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight < 400) {
|
|
13664
|
-
isLoadingMore.current = true;
|
|
13665
|
-
setItemCount?.((ic ?? ps) + ps);
|
|
13666
|
-
}
|
|
13667
|
-
};
|
|
13668
|
-
const onScrollEvent = () => {
|
|
13669
|
-
if (rafId === null) rafId = requestAnimationFrame(update);
|
|
13670
|
-
};
|
|
13671
|
-
scrollEl.addEventListener("scroll", onScrollEvent, { passive: true });
|
|
13672
|
-
const ro = new ResizeObserver(() => update());
|
|
13673
|
-
ro.observe(scrollEl);
|
|
13674
|
-
update();
|
|
13675
|
-
return () => {
|
|
13676
|
-
scrollEl.removeEventListener("scroll", onScrollEvent);
|
|
13677
|
-
ro.disconnect();
|
|
13678
|
-
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
13679
|
-
};
|
|
13680
|
-
}, [setItemCount]);
|
|
13681
|
-
useEffect(() => {
|
|
13682
|
-
if (!containerRef.current || !initialScroll || hasRestoredScroll.current || data.length === 0) return;
|
|
13683
|
-
const scrollEl = getScrollParent$1(containerRef.current);
|
|
13684
|
-
if (!scrollEl) return;
|
|
13685
|
-
let attempts = 0;
|
|
13686
|
-
const maxAttempts = 5;
|
|
13687
|
-
const tryRestore = () => {
|
|
13688
|
-
if (scrollEl.scrollHeight >= initialScroll || attempts >= maxAttempts) {
|
|
13689
|
-
scrollEl.scrollTop = initialScroll;
|
|
13690
|
-
hasRestoredScroll.current = true;
|
|
13691
|
-
} else {
|
|
13692
|
-
attempts++;
|
|
13693
|
-
requestAnimationFrame(tryRestore);
|
|
13694
|
-
}
|
|
13695
|
-
};
|
|
13696
|
-
requestAnimationFrame(tryRestore);
|
|
13697
|
-
}, [initialScroll, data.length]);
|
|
13698
|
-
const lastScrollOffset = useRef(0);
|
|
13699
|
-
useEffect(() => {
|
|
13700
|
-
const el = containerRef.current;
|
|
13701
|
-
if (!el || !onScroll) return;
|
|
13702
|
-
const scrollEl = getScrollParent$1(el);
|
|
13703
|
-
if (!scrollEl) return;
|
|
13704
|
-
const handleScroll = () => {
|
|
13705
|
-
const currentOffset = scrollEl.scrollTop;
|
|
13706
|
-
const direction = currentOffset > lastScrollOffset.current ? "forward" : "backward";
|
|
13707
|
-
lastScrollOffset.current = currentOffset;
|
|
13708
|
-
onScroll({
|
|
13709
|
-
scrollDirection: direction,
|
|
13710
|
-
scrollOffset: currentOffset,
|
|
13711
|
-
scrollUpdateWasRequested: false
|
|
13712
|
-
});
|
|
13713
|
-
};
|
|
13714
|
-
scrollEl.addEventListener("scroll", handleScroll, { passive: true });
|
|
13715
|
-
return () => scrollEl.removeEventListener("scroll", handleScroll);
|
|
13716
|
-
}, [onScroll]);
|
|
13717
13415
|
const handleEntityClick = useCallback((entity) => {
|
|
13718
13416
|
onEntityClick?.(entity);
|
|
13719
13417
|
}, [onEntityClick]);
|
|
13720
13418
|
const handleSelectionChange = useCallback((entity, selected) => {
|
|
13721
13419
|
selectionController?.toggleEntitySelection(entity, selected);
|
|
13722
13420
|
}, [selectionController]);
|
|
13723
|
-
const
|
|
13724
|
-
|
|
13725
|
-
|
|
13726
|
-
|
|
13727
|
-
|
|
13728
|
-
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
selectionEnabled,
|
|
13763
|
-
size
|
|
13764
|
-
}, `${entity.path}_${entity.id}`))
|
|
13765
|
-
}), /* @__PURE__ */ jsxs("div", {
|
|
13766
|
-
className: "flex items-center justify-center py-8",
|
|
13767
|
-
children: [dataLoading && /* @__PURE__ */ jsx(CircularProgress, { size: "small" }), !dataLoading && noMoreToLoad && data.length > 0 && /* @__PURE__ */ jsxs(Typography, {
|
|
13768
|
-
variant: "caption",
|
|
13769
|
-
color: "secondary",
|
|
13770
|
-
children: [
|
|
13771
|
-
"All ",
|
|
13772
|
-
data.length,
|
|
13773
|
-
" entries loaded"
|
|
13774
|
-
]
|
|
13775
|
-
})]
|
|
13776
|
-
})]
|
|
13777
|
-
}) })
|
|
13421
|
+
const selectedIds = useMemo(() => new Set(selectionController?.selectedEntities.map((e) => e.id)), [selectionController?.selectedEntities]);
|
|
13422
|
+
const highlightedIds = useMemo(() => new Set(highlightedEntities?.map((e) => e.id)), [highlightedEntities]);
|
|
13423
|
+
const handleRowSelectionChange = useCallback((entity, selected) => {
|
|
13424
|
+
handleSelectionChange(entity, selected);
|
|
13425
|
+
}, [handleSelectionChange]);
|
|
13426
|
+
return /* @__PURE__ */ jsx(CardView, {
|
|
13427
|
+
data,
|
|
13428
|
+
dataLoading,
|
|
13429
|
+
noMoreToLoad,
|
|
13430
|
+
dataLoadingError,
|
|
13431
|
+
itemCount,
|
|
13432
|
+
setItemCount,
|
|
13433
|
+
pageSize,
|
|
13434
|
+
paginationEnabled,
|
|
13435
|
+
onItemClick: handleEntityClick,
|
|
13436
|
+
selectedIds,
|
|
13437
|
+
highlightedIds,
|
|
13438
|
+
selectionEnabled,
|
|
13439
|
+
onSelectionChange: handleRowSelectionChange,
|
|
13440
|
+
onScroll,
|
|
13441
|
+
initialScroll,
|
|
13442
|
+
size,
|
|
13443
|
+
emptyComponent,
|
|
13444
|
+
renderCard: useCallback((entity, { selected, highlighted, onClick }) => /* @__PURE__ */ jsx(ResolvedEntityCard, {
|
|
13445
|
+
entity,
|
|
13446
|
+
collection,
|
|
13447
|
+
onClick,
|
|
13448
|
+
selected,
|
|
13449
|
+
highlighted,
|
|
13450
|
+
onSelectionChange: handleRowSelectionChange,
|
|
13451
|
+
selectionEnabled,
|
|
13452
|
+
size
|
|
13453
|
+
}, `${entity.path}_${entity.id}`), [
|
|
13454
|
+
collection,
|
|
13455
|
+
selectionEnabled,
|
|
13456
|
+
size,
|
|
13457
|
+
handleRowSelectionChange,
|
|
13458
|
+
ResolvedEntityCard
|
|
13459
|
+
])
|
|
13778
13460
|
});
|
|
13779
13461
|
}
|
|
13780
13462
|
//#endregion
|
|
@@ -13792,38 +13474,9 @@ function getRowClasses(size) {
|
|
|
13792
13474
|
default: return "py-3 px-5";
|
|
13793
13475
|
}
|
|
13794
13476
|
}
|
|
13795
|
-
/**
|
|
13796
|
-
* Estimated row height in pixels for virtualization, based on size.
|
|
13797
|
-
*/
|
|
13798
|
-
function getEstimatedRowHeight(size) {
|
|
13799
|
-
switch (size) {
|
|
13800
|
-
case "xs": return 44;
|
|
13801
|
-
case "s": return 52;
|
|
13802
|
-
case "m": return 64;
|
|
13803
|
-
case "l": return 76;
|
|
13804
|
-
case "xl": return 88;
|
|
13805
|
-
default: return 64;
|
|
13806
|
-
}
|
|
13807
|
-
}
|
|
13808
|
-
/** Number of extra rows rendered above/below the viewport. */
|
|
13809
|
-
var OVERSCAN_COUNT = 8;
|
|
13810
|
-
/** Threshold in pixels from the bottom of the scroll area to trigger loading more. */
|
|
13811
|
-
var LOAD_MORE_THRESHOLD = 400;
|
|
13812
13477
|
/** Stable empty array for when no list-view actions are available. */
|
|
13813
13478
|
var EMPTY_LIST_VIEW_ACTIONS = [];
|
|
13814
13479
|
/**
|
|
13815
|
-
* Walk up the DOM from `element` to find the nearest scrollable ancestor.
|
|
13816
|
-
*/
|
|
13817
|
-
function getScrollParent(element) {
|
|
13818
|
-
let parent = element?.parentElement ?? null;
|
|
13819
|
-
while (parent) {
|
|
13820
|
-
const style = getComputedStyle(parent);
|
|
13821
|
-
if (style.overflowY === "auto" || style.overflowY === "scroll" || style.overflow === "auto" || style.overflow === "scroll") return parent;
|
|
13822
|
-
parent = parent.parentElement;
|
|
13823
|
-
}
|
|
13824
|
-
return document.documentElement;
|
|
13825
|
-
}
|
|
13826
|
-
/**
|
|
13827
13480
|
* Returns true if a property type should NOT be rendered via
|
|
13828
13481
|
* PropertyPreview in list row columns (because it would blow up height).
|
|
13829
13482
|
*/
|
|
@@ -14008,7 +13661,17 @@ function EntityCollectionListView({ collection, tableController, onEntityClick,
|
|
|
14008
13661
|
subtitleKey,
|
|
14009
13662
|
"id"
|
|
14010
13663
|
]);
|
|
14011
|
-
allKeys.filter((k) =>
|
|
13664
|
+
allKeys.filter((k) => {
|
|
13665
|
+
if (usedKeys.has(k)) return false;
|
|
13666
|
+
const prop = resolvedCollection.properties[k];
|
|
13667
|
+
if (!prop) return false;
|
|
13668
|
+
if (prop.type === "string" && (prop.storage || prop.ui?.url === "image")) return false;
|
|
13669
|
+
if (prop.type === "array" && prop.of && !Array.isArray(prop.of)) {
|
|
13670
|
+
const inner = prop.of;
|
|
13671
|
+
if (inner.type === "string" && (inner.storage || inner.ui?.url === "image")) return false;
|
|
13672
|
+
}
|
|
13673
|
+
return true;
|
|
13674
|
+
}).forEach((key) => {
|
|
14012
13675
|
const prop = resolvedCollection.properties[key];
|
|
14013
13676
|
cols.push({
|
|
14014
13677
|
key,
|
|
@@ -14077,10 +13740,10 @@ function EntityCollectionListView({ collection, tableController, onEntityClick,
|
|
|
14077
13740
|
const handleSelectionChange = useCallback((entity, selected) => {
|
|
14078
13741
|
selectionController?.toggleEntitySelection(entity, selected);
|
|
14079
13742
|
}, [selectionController]);
|
|
14080
|
-
|
|
13743
|
+
useCallback((entity) => {
|
|
14081
13744
|
return selectionController?.isEntitySelected(entity) ?? false;
|
|
14082
13745
|
}, [selectionController]);
|
|
14083
|
-
|
|
13746
|
+
useCallback((entity) => {
|
|
14084
13747
|
return highlightedEntities?.some((e) => e.id === entity.id && e.path === entity.path) ?? false;
|
|
14085
13748
|
}, [highlightedEntities]);
|
|
14086
13749
|
const getListViewActions = useCallback((entity) => {
|
|
@@ -14095,158 +13758,70 @@ function EntityCollectionListView({ collection, tableController, onEntityClick,
|
|
|
14095
13758
|
customizationController.entityActions
|
|
14096
13759
|
]);
|
|
14097
13760
|
const rowClasses = getRowClasses(size);
|
|
14098
|
-
const
|
|
14099
|
-
const
|
|
14100
|
-
const
|
|
14101
|
-
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
|
|
13761
|
+
const selectedIds = useMemo(() => new Set(selectionController?.selectedEntities.map((e) => e.id)), [selectionController?.selectedEntities]);
|
|
13762
|
+
const highlightedIds = useMemo(() => new Set(highlightedEntities?.map((e) => e.id)), [highlightedEntities]);
|
|
13763
|
+
const handleRowSelectionChange = useCallback((entity, selected) => {
|
|
13764
|
+
handleSelectionChange(entity, selected);
|
|
13765
|
+
}, [handleSelectionChange]);
|
|
13766
|
+
return /* @__PURE__ */ jsx(ListView, {
|
|
13767
|
+
data,
|
|
13768
|
+
dataLoading,
|
|
14105
13769
|
noMoreToLoad,
|
|
13770
|
+
dataLoadingError,
|
|
14106
13771
|
itemCount,
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
useEffect(() => {
|
|
14110
|
-
paginationStateRef.current = {
|
|
14111
|
-
paginationEnabled,
|
|
14112
|
-
noMoreToLoad,
|
|
14113
|
-
itemCount,
|
|
14114
|
-
pageSize
|
|
14115
|
-
};
|
|
14116
|
-
}, [
|
|
13772
|
+
setItemCount,
|
|
13773
|
+
pageSize,
|
|
14117
13774
|
paginationEnabled,
|
|
14118
|
-
|
|
14119
|
-
|
|
14120
|
-
|
|
14121
|
-
|
|
14122
|
-
|
|
14123
|
-
|
|
14124
|
-
|
|
14125
|
-
|
|
14126
|
-
|
|
14127
|
-
|
|
14128
|
-
|
|
14129
|
-
|
|
14130
|
-
|
|
14131
|
-
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
14137
|
-
|
|
14138
|
-
|
|
14139
|
-
|
|
14140
|
-
|
|
14141
|
-
|
|
14142
|
-
|
|
14143
|
-
|
|
14144
|
-
|
|
14145
|
-
|
|
14146
|
-
|
|
14147
|
-
|
|
14148
|
-
|
|
14149
|
-
ro.disconnect();
|
|
14150
|
-
if (rafId !== null) cancelAnimationFrame(rafId);
|
|
14151
|
-
};
|
|
14152
|
-
}, [setItemCount]);
|
|
14153
|
-
const totalHeight = data.length * estimatedRowHeight;
|
|
14154
|
-
const startIndex = Math.max(0, Math.floor(effectiveScrollTop / estimatedRowHeight) - OVERSCAN_COUNT);
|
|
14155
|
-
const endIndex = Math.min(data.length, Math.ceil((effectiveScrollTop + viewportHeight) / estimatedRowHeight) + OVERSCAN_COUNT);
|
|
14156
|
-
const visibleData = data.slice(startIndex, endIndex);
|
|
14157
|
-
const offsetY = startIndex * estimatedRowHeight;
|
|
14158
|
-
const footerHeight = dataLoading ? 48 : !dataLoading && noMoreToLoad && data.length > 0 ? 32 : 0;
|
|
14159
|
-
return /* @__PURE__ */ jsx("div", {
|
|
14160
|
-
ref: containerRef,
|
|
14161
|
-
className: cls("w-full", selectedEntityId === void 0 && "rounded-lg overflow-hidden border " + defaultBorderMixin),
|
|
14162
|
-
children: dataLoadingError && data.length === 0 ? /* @__PURE__ */ jsx("div", {
|
|
14163
|
-
className: "flex items-center justify-center p-8",
|
|
14164
|
-
children: /* @__PURE__ */ jsxs(Typography, {
|
|
14165
|
-
className: "text-red-500",
|
|
14166
|
-
children: ["Error loading data: ", dataLoadingError.message]
|
|
14167
|
-
})
|
|
14168
|
-
}) : isInitialLoading ? /* @__PURE__ */ jsx("div", {
|
|
14169
|
-
className: "flex items-center justify-center py-12 px-8",
|
|
14170
|
-
children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
|
|
14171
|
-
}) : isEmpty ? /* @__PURE__ */ jsx("div", {
|
|
14172
|
-
className: "w-full flex items-center justify-center py-12 px-8",
|
|
14173
|
-
children: emptyComponent ?? /* @__PURE__ */ jsx(Typography, {
|
|
14174
|
-
variant: "label",
|
|
14175
|
-
color: "secondary",
|
|
14176
|
-
children: "No entries found"
|
|
14177
|
-
})
|
|
14178
|
-
}) : /* @__PURE__ */ jsxs("div", {
|
|
14179
|
-
style: {
|
|
14180
|
-
height: totalHeight + footerHeight,
|
|
14181
|
-
position: "relative"
|
|
14182
|
-
},
|
|
14183
|
-
children: [
|
|
14184
|
-
/* @__PURE__ */ jsx("div", {
|
|
14185
|
-
style: {
|
|
14186
|
-
position: "absolute",
|
|
14187
|
-
top: offsetY,
|
|
14188
|
-
left: 0,
|
|
14189
|
-
right: 0
|
|
14190
|
-
},
|
|
14191
|
-
children: visibleData.map((entity, i) => {
|
|
14192
|
-
const isLast = startIndex + i === data.length - 1;
|
|
14193
|
-
return /* @__PURE__ */ jsx("div", {
|
|
14194
|
-
style: { height: estimatedRowHeight },
|
|
14195
|
-
className: cls(!isLast && "border-b", !isLast && defaultBorderMixin),
|
|
14196
|
-
children: /* @__PURE__ */ jsx(ListRow, {
|
|
14197
|
-
entity,
|
|
14198
|
-
collection: resolvedCollection,
|
|
14199
|
-
onClick: handleEntityClick,
|
|
14200
|
-
selected: isEntitySelected(entity),
|
|
14201
|
-
highlighted: isEntityHighlighted(entity),
|
|
14202
|
-
onSelectionChange: handleSelectionChange,
|
|
14203
|
-
selectionEnabled,
|
|
14204
|
-
columns: visibleColumns,
|
|
14205
|
-
slotKeys,
|
|
14206
|
-
rowClasses,
|
|
14207
|
-
showImage,
|
|
14208
|
-
size,
|
|
14209
|
-
isLast,
|
|
14210
|
-
isActive: selectedEntityId !== void 0 && entity.id === selectedEntityId,
|
|
14211
|
-
listViewActions: getListViewActions(entity),
|
|
14212
|
-
context,
|
|
14213
|
-
path,
|
|
14214
|
-
selectionController,
|
|
14215
|
-
openEntityMode
|
|
14216
|
-
})
|
|
14217
|
-
}, entity.id);
|
|
14218
|
-
})
|
|
14219
|
-
}),
|
|
14220
|
-
dataLoading && /* @__PURE__ */ jsx("div", {
|
|
14221
|
-
className: "flex items-center justify-center py-3",
|
|
14222
|
-
style: {
|
|
14223
|
-
position: "absolute",
|
|
14224
|
-
top: totalHeight,
|
|
14225
|
-
left: 0,
|
|
14226
|
-
right: 0
|
|
14227
|
-
},
|
|
14228
|
-
children: /* @__PURE__ */ jsx(CircularProgress, { size: "small" })
|
|
14229
|
-
}),
|
|
14230
|
-
!dataLoading && noMoreToLoad && data.length > 0 && /* @__PURE__ */ jsx("div", {
|
|
14231
|
-
className: "flex items-center justify-center py-2 dark:bg-surface-900",
|
|
14232
|
-
style: {
|
|
14233
|
-
position: "absolute",
|
|
14234
|
-
top: totalHeight,
|
|
14235
|
-
left: 0,
|
|
14236
|
-
right: 0
|
|
14237
|
-
},
|
|
14238
|
-
children: /* @__PURE__ */ jsxs(Typography, {
|
|
14239
|
-
variant: "caption",
|
|
14240
|
-
color: "secondary",
|
|
14241
|
-
children: [
|
|
14242
|
-
"All ",
|
|
14243
|
-
data.length,
|
|
14244
|
-
" entries loaded"
|
|
14245
|
-
]
|
|
14246
|
-
})
|
|
13775
|
+
onItemClick: handleEntityClick,
|
|
13776
|
+
selectedIds,
|
|
13777
|
+
highlightedIds,
|
|
13778
|
+
selectionEnabled,
|
|
13779
|
+
emptyComponent,
|
|
13780
|
+
size,
|
|
13781
|
+
selectedEntityId,
|
|
13782
|
+
renderRow: useCallback(({ item: entity, style, className, selected, highlighted, isLast, onClick, onSelectionChange }) => {
|
|
13783
|
+
return /* @__PURE__ */ jsx("div", {
|
|
13784
|
+
style,
|
|
13785
|
+
className,
|
|
13786
|
+
children: /* @__PURE__ */ jsx(ListRow, {
|
|
13787
|
+
entity,
|
|
13788
|
+
collection: resolvedCollection,
|
|
13789
|
+
onClick: handleEntityClick,
|
|
13790
|
+
selected,
|
|
13791
|
+
highlighted,
|
|
13792
|
+
onSelectionChange: handleRowSelectionChange,
|
|
13793
|
+
selectionEnabled,
|
|
13794
|
+
columns: visibleColumns,
|
|
13795
|
+
slotKeys,
|
|
13796
|
+
rowClasses,
|
|
13797
|
+
showImage,
|
|
13798
|
+
size,
|
|
13799
|
+
isLast,
|
|
13800
|
+
isActive: selectedEntityId !== void 0 && entity.id === selectedEntityId,
|
|
13801
|
+
listViewActions: getListViewActions(entity),
|
|
13802
|
+
context,
|
|
13803
|
+
path,
|
|
13804
|
+
selectionController,
|
|
13805
|
+
openEntityMode
|
|
14247
13806
|
})
|
|
14248
|
-
|
|
14249
|
-
}
|
|
13807
|
+
}, entity.id);
|
|
13808
|
+
}, [
|
|
13809
|
+
resolvedCollection,
|
|
13810
|
+
selectionEnabled,
|
|
13811
|
+
visibleColumns,
|
|
13812
|
+
slotKeys,
|
|
13813
|
+
rowClasses,
|
|
13814
|
+
showImage,
|
|
13815
|
+
size,
|
|
13816
|
+
selectedEntityId,
|
|
13817
|
+
getListViewActions,
|
|
13818
|
+
context,
|
|
13819
|
+
path,
|
|
13820
|
+
selectionController,
|
|
13821
|
+
openEntityMode,
|
|
13822
|
+
handleRowSelectionChange,
|
|
13823
|
+
handleEntityClick
|
|
13824
|
+
])
|
|
14250
13825
|
});
|
|
14251
13826
|
}
|
|
14252
13827
|
/**
|
|
@@ -14682,457 +14257,6 @@ function SplitListView({ collection, tableController, onEntityClick: externalOnE
|
|
|
14682
14257
|
});
|
|
14683
14258
|
}
|
|
14684
14259
|
//#endregion
|
|
14685
|
-
//#region src/components/EntityCollectionView/BoardSortableList.tsx
|
|
14686
|
-
function BoardSortableList({ columnId, items, ItemComponent, isDragging, isDragOverColumn, loading = false, hasMore = false, onLoadMore }) {
|
|
14687
|
-
const { setNodeRef } = useDroppable({
|
|
14688
|
-
id: columnId,
|
|
14689
|
-
data: { type: "ITEM-LIST" }
|
|
14690
|
-
});
|
|
14691
|
-
const sentinelRef = useRef(null);
|
|
14692
|
-
const isLoadingRef = useRef(false);
|
|
14693
|
-
isLoadingRef.current = loading;
|
|
14694
|
-
const lastLoadTimeRef = useRef(0);
|
|
14695
|
-
useEffect(() => {
|
|
14696
|
-
if (!sentinelRef.current || !hasMore || !onLoadMore) return;
|
|
14697
|
-
const sentinel = sentinelRef.current;
|
|
14698
|
-
const observer = new IntersectionObserver((entries) => {
|
|
14699
|
-
const now = Date.now();
|
|
14700
|
-
if (entries[0].isIntersecting && hasMore && !isLoadingRef.current && now - lastLoadTimeRef.current > 500) {
|
|
14701
|
-
lastLoadTimeRef.current = now;
|
|
14702
|
-
onLoadMore();
|
|
14703
|
-
}
|
|
14704
|
-
}, { threshold: .1 });
|
|
14705
|
-
observer.observe(sentinel);
|
|
14706
|
-
const rect = sentinel.getBoundingClientRect();
|
|
14707
|
-
const containerRect = sentinel.parentElement?.getBoundingClientRect();
|
|
14708
|
-
if (containerRect && rect.top < containerRect.bottom && rect.bottom > containerRect.top) {
|
|
14709
|
-
const now = Date.now();
|
|
14710
|
-
if (hasMore && !isLoadingRef.current && now - lastLoadTimeRef.current > 500) {
|
|
14711
|
-
lastLoadTimeRef.current = now;
|
|
14712
|
-
onLoadMore();
|
|
14713
|
-
}
|
|
14714
|
-
}
|
|
14715
|
-
return () => observer.disconnect();
|
|
14716
|
-
}, [hasMore, onLoadMore]);
|
|
14717
|
-
return /* @__PURE__ */ jsx("div", {
|
|
14718
|
-
ref: setNodeRef,
|
|
14719
|
-
className: useMemo(() => cls("flex flex-col p-2 transition-opacity duration-100 transition-bg ease-linear w-full overflow-y-auto no-scrollbar flex-1 rounded-md", isDragging && isDragOverColumn ? "bg-surface-accent-200 dark:bg-surface-900" : isDragging ? "bg-surface-50 dark:bg-surface-900 hover:bg-surface-accent-100 dark:hover:bg-surface-800" : "bg-surface-50 dark:bg-surface-900"), [isDragging, isDragOverColumn]),
|
|
14720
|
-
style: { minHeight: 80 },
|
|
14721
|
-
children: /* @__PURE__ */ jsx(SortableContext, {
|
|
14722
|
-
items: items.map((i) => i.id),
|
|
14723
|
-
strategy: verticalListSortingStrategy,
|
|
14724
|
-
children: items.length === 0 && !loading ? /* @__PURE__ */ jsx("div", {
|
|
14725
|
-
className: "flex-1 flex items-center justify-center",
|
|
14726
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
14727
|
-
className: "text-xs text-surface-400 dark:text-surface-500",
|
|
14728
|
-
children: "No items"
|
|
14729
|
-
})
|
|
14730
|
-
}) : /* @__PURE__ */ jsxs(Fragment, { children: [items.map((item, index) => /* @__PURE__ */ jsx(SortableItem, {
|
|
14731
|
-
item,
|
|
14732
|
-
index,
|
|
14733
|
-
columnId,
|
|
14734
|
-
ItemComponent
|
|
14735
|
-
}, item.id)), (loading || hasMore) && /* @__PURE__ */ jsx("div", {
|
|
14736
|
-
ref: sentinelRef,
|
|
14737
|
-
className: "flex items-center justify-center py-2 min-h-6",
|
|
14738
|
-
children: loading && /* @__PURE__ */ jsx(CircularProgress, { size: "smallest" })
|
|
14739
|
-
})] })
|
|
14740
|
-
})
|
|
14741
|
-
});
|
|
14742
|
-
}
|
|
14743
|
-
var SortableItem = memo(function SortableItem({ item, index, columnId, ItemComponent }) {
|
|
14744
|
-
const { setNodeRef, attributes, listeners, isDragging: isItemBeingDragged, transform, transition } = useSortable({
|
|
14745
|
-
id: item.id,
|
|
14746
|
-
data: {
|
|
14747
|
-
type: "ITEM",
|
|
14748
|
-
columnId
|
|
14749
|
-
}
|
|
14750
|
-
});
|
|
14751
|
-
return /* @__PURE__ */ jsx("div", {
|
|
14752
|
-
ref: setNodeRef,
|
|
14753
|
-
style: useMemo(() => ({
|
|
14754
|
-
transform: CSS.Transform.toString(transform),
|
|
14755
|
-
transition,
|
|
14756
|
-
zIndex: isItemBeingDragged ? 2 : 1,
|
|
14757
|
-
opacity: isItemBeingDragged ? 0 : 1
|
|
14758
|
-
}), [
|
|
14759
|
-
transform,
|
|
14760
|
-
transition,
|
|
14761
|
-
isItemBeingDragged
|
|
14762
|
-
]),
|
|
14763
|
-
...attributes,
|
|
14764
|
-
...listeners,
|
|
14765
|
-
children: /* @__PURE__ */ jsx(ItemComponent, {
|
|
14766
|
-
item,
|
|
14767
|
-
isDragging: isItemBeingDragged,
|
|
14768
|
-
index
|
|
14769
|
-
})
|
|
14770
|
-
});
|
|
14771
|
-
});
|
|
14772
|
-
//#endregion
|
|
14773
|
-
//#region src/components/EntityCollectionView/BoardColumnTitle.tsx
|
|
14774
|
-
function BoardColumnTitle({ children, className, color, ...props }) {
|
|
14775
|
-
const colorScheme = useMemo(() => {
|
|
14776
|
-
if (!color) return void 0;
|
|
14777
|
-
if (typeof color === "string") return getColorSchemeForKey(color);
|
|
14778
|
-
return color;
|
|
14779
|
-
}, [color]);
|
|
14780
|
-
return /* @__PURE__ */ jsxs(Typography, {
|
|
14781
|
-
variant: "subtitle2",
|
|
14782
|
-
component: "h4",
|
|
14783
|
-
className: cls("py-3 px-3 transition-colors duration-200 flex-grow select-none relative outline-none focus:outline focus:outline-2 focus:outline-offset-2 flex items-center gap-3", className),
|
|
14784
|
-
...props,
|
|
14785
|
-
children: [colorScheme && /* @__PURE__ */ jsx("div", {
|
|
14786
|
-
className: "w-3 h-3 rounded-full flex-shrink-0",
|
|
14787
|
-
style: { backgroundColor: colorScheme.darkColor ?? colorScheme.color }
|
|
14788
|
-
}), children]
|
|
14789
|
-
});
|
|
14790
|
-
}
|
|
14791
|
-
//#endregion
|
|
14792
|
-
//#region src/components/EntityCollectionView/BoardColumn.tsx
|
|
14793
|
-
var BoardColumn = memo(function BoardColumn({ id, title, items, ItemComponent, isDragging, isDragOverColumn, allowReorder = false, loading = false, hasMore = false, onLoadMore, onAddItem, totalCount, color, style }) {
|
|
14794
|
-
const { setNodeRef, attributes, listeners, isDragging: isColumnBeingDragged, transform, transition } = useSortable({
|
|
14795
|
-
id,
|
|
14796
|
-
data: { type: "COLUMN" },
|
|
14797
|
-
disabled: !allowReorder
|
|
14798
|
-
});
|
|
14799
|
-
const combinedStyle = useMemo(() => ({
|
|
14800
|
-
...style,
|
|
14801
|
-
transform: CSS.Translate.toString(transform),
|
|
14802
|
-
transition,
|
|
14803
|
-
zIndex: isColumnBeingDragged ? 2 : 1
|
|
14804
|
-
}), [
|
|
14805
|
-
style,
|
|
14806
|
-
transform,
|
|
14807
|
-
transition,
|
|
14808
|
-
isColumnBeingDragged
|
|
14809
|
-
]);
|
|
14810
|
-
const dragListeners = allowReorder ? listeners : {};
|
|
14811
|
-
const columnClassName = useMemo(() => cls("border h-full w-80 min-w-80 mx-2 flex flex-col rounded-md", defaultBorderMixin, isColumnBeingDragged ? "ring-2 ring-primary" : ""), [isColumnBeingDragged]);
|
|
14812
|
-
const headerClassName = useMemo(() => cls("flex items-center justify-between px-2 rounded-t-md transition-colors duration-200 ease-in-out", isColumnBeingDragged ? "bg-surface-100 dark:bg-surface-700" : "bg-surface-50 hover:bg-surface-100 dark:bg-surface-800 dark:hover:bg-surface-700", allowReorder ? "cursor-grab" : ""), [isColumnBeingDragged, allowReorder]);
|
|
14813
|
-
const itemIds = useMemo(() => items.map((i) => i.id), [items]);
|
|
14814
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
14815
|
-
ref: setNodeRef,
|
|
14816
|
-
style: combinedStyle,
|
|
14817
|
-
...attributes,
|
|
14818
|
-
className: columnClassName,
|
|
14819
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
14820
|
-
...dragListeners,
|
|
14821
|
-
className: headerClassName,
|
|
14822
|
-
children: [/* @__PURE__ */ jsxs("div", {
|
|
14823
|
-
className: "flex items-center gap-2",
|
|
14824
|
-
children: [/* @__PURE__ */ jsx(BoardColumnTitle, {
|
|
14825
|
-
"aria-label": `${title} item list`,
|
|
14826
|
-
color,
|
|
14827
|
-
children: title
|
|
14828
|
-
}), totalCount !== void 0 && /* @__PURE__ */ jsx("span", {
|
|
14829
|
-
className: "text-xs text-surface-500 dark:text-surface-400",
|
|
14830
|
-
children: totalCount
|
|
14831
|
-
})]
|
|
14832
|
-
}), onAddItem && /* @__PURE__ */ jsx(IconButton, {
|
|
14833
|
-
size: "small",
|
|
14834
|
-
onClick: (e) => {
|
|
14835
|
-
e.stopPropagation();
|
|
14836
|
-
onAddItem();
|
|
14837
|
-
},
|
|
14838
|
-
className: "opacity-60 hover:opacity-100",
|
|
14839
|
-
children: /* @__PURE__ */ jsx(PlusIcon, { size: iconSize.small })
|
|
14840
|
-
})]
|
|
14841
|
-
}), /* @__PURE__ */ jsx(SortableContext, {
|
|
14842
|
-
items: itemIds,
|
|
14843
|
-
strategy: verticalListSortingStrategy,
|
|
14844
|
-
children: /* @__PURE__ */ jsx(BoardSortableList, {
|
|
14845
|
-
columnId: id,
|
|
14846
|
-
items,
|
|
14847
|
-
ItemComponent,
|
|
14848
|
-
isDragging,
|
|
14849
|
-
isDragOverColumn,
|
|
14850
|
-
loading,
|
|
14851
|
-
hasMore,
|
|
14852
|
-
onLoadMore
|
|
14853
|
-
})
|
|
14854
|
-
})]
|
|
14855
|
-
});
|
|
14856
|
-
});
|
|
14857
|
-
//#endregion
|
|
14858
|
-
//#region src/components/EntityCollectionView/Board.tsx
|
|
14859
|
-
function Board({ data, columns: columnsProp, columnLabels, columnColors, className, assignColumn, allowColumnReorder = false, onColumnReorder, onItemsReorder, ItemComponent, columnLoadingState, onLoadMoreColumn, onAddItemToColumn, AddColumnComponent }) {
|
|
14860
|
-
const [activeItem, setActiveItem] = useState(null);
|
|
14861
|
-
const [activeColumn, setActiveColumn] = useState(null);
|
|
14862
|
-
const [isDragging, setIsDragging] = useState(false);
|
|
14863
|
-
const [dragOverColumnId, setDragOverColumnId] = useState(null);
|
|
14864
|
-
const grabOffsetRef = useRef({
|
|
14865
|
-
x: 0,
|
|
14866
|
-
y: 0
|
|
14867
|
-
});
|
|
14868
|
-
const [overlayPos, setOverlayPos] = useState(null);
|
|
14869
|
-
const handleMouseMove = useCallback((e) => {
|
|
14870
|
-
setOverlayPos({
|
|
14871
|
-
x: e.clientX - grabOffsetRef.current.x,
|
|
14872
|
-
y: e.clientY - grabOffsetRef.current.y
|
|
14873
|
-
});
|
|
14874
|
-
}, []);
|
|
14875
|
-
useEffect(() => {
|
|
14876
|
-
return () => {
|
|
14877
|
-
window.removeEventListener("mousemove", handleMouseMove);
|
|
14878
|
-
window.removeEventListener("pointermove", handleMouseMove);
|
|
14879
|
-
};
|
|
14880
|
-
}, [handleMouseMove]);
|
|
14881
|
-
const [itemMapState, setItemMapState] = useState(() => {
|
|
14882
|
-
const dataColumnMap = data.reduce((prev, item) => ({
|
|
14883
|
-
...prev,
|
|
14884
|
-
[item.id]: assignColumn(item)
|
|
14885
|
-
}), {});
|
|
14886
|
-
return columnsProp.reduce((previous, column) => ({
|
|
14887
|
-
...previous,
|
|
14888
|
-
[column]: data.filter((item) => dataColumnMap[item.id] === column)
|
|
14889
|
-
}), {});
|
|
14890
|
-
});
|
|
14891
|
-
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 5 } }));
|
|
14892
|
-
useEffect(() => {
|
|
14893
|
-
if (isDragging) return;
|
|
14894
|
-
const dataColumnMap = data.reduce((prev, item) => ({
|
|
14895
|
-
...prev,
|
|
14896
|
-
[item.id]: assignColumn(item)
|
|
14897
|
-
}), {});
|
|
14898
|
-
const newItemMap = columnsProp.reduce((previous, column) => ({
|
|
14899
|
-
...previous,
|
|
14900
|
-
[column]: data.filter((item) => dataColumnMap[item.id] === column)
|
|
14901
|
-
}), {});
|
|
14902
|
-
setItemMapState((prevMap) => {
|
|
14903
|
-
let changed = false;
|
|
14904
|
-
for (const col of columnsProp) {
|
|
14905
|
-
const prevItems = prevMap[col] ?? [];
|
|
14906
|
-
const newItems = newItemMap[col] ?? [];
|
|
14907
|
-
if (prevItems.length !== newItems.length) {
|
|
14908
|
-
changed = true;
|
|
14909
|
-
continue;
|
|
14910
|
-
}
|
|
14911
|
-
for (let i = 0; i < prevItems.length; i++) if (prevItems[i].id !== newItems[i].id || prevItems[i].entity !== newItems[i].entity) {
|
|
14912
|
-
changed = true;
|
|
14913
|
-
break;
|
|
14914
|
-
}
|
|
14915
|
-
}
|
|
14916
|
-
if (!changed) return prevMap;
|
|
14917
|
-
const updated = {};
|
|
14918
|
-
for (const col of columnsProp) {
|
|
14919
|
-
const prevItems = prevMap[col] ?? [];
|
|
14920
|
-
const newItems = newItemMap[col] ?? [];
|
|
14921
|
-
const prevById = /* @__PURE__ */ new Map();
|
|
14922
|
-
for (const item of prevItems) prevById.set(item.id, item);
|
|
14923
|
-
updated[col] = newItems.map((newItem) => {
|
|
14924
|
-
const prev = prevById.get(newItem.id);
|
|
14925
|
-
if (prev && prev.entity === newItem.entity) return prev;
|
|
14926
|
-
return newItem;
|
|
14927
|
-
});
|
|
14928
|
-
}
|
|
14929
|
-
return updated;
|
|
14930
|
-
});
|
|
14931
|
-
}, [
|
|
14932
|
-
data,
|
|
14933
|
-
columnsProp,
|
|
14934
|
-
assignColumn
|
|
14935
|
-
]);
|
|
14936
|
-
const findColumnByItemId = (id) => {
|
|
14937
|
-
return Object.keys(itemMapState).find((col) => itemMapState[col]?.some((i) => i.id === id));
|
|
14938
|
-
};
|
|
14939
|
-
const handleDragStart = (event) => {
|
|
14940
|
-
setIsDragging(true);
|
|
14941
|
-
setDragOverColumnId(null);
|
|
14942
|
-
const { active } = event;
|
|
14943
|
-
const activatorEvt = event.activatorEvent;
|
|
14944
|
-
if (activatorEvt) {
|
|
14945
|
-
const target = activatorEvt.target;
|
|
14946
|
-
const rect = (target.closest("[role='button']") ?? target).getBoundingClientRect();
|
|
14947
|
-
grabOffsetRef.current = {
|
|
14948
|
-
x: activatorEvt.clientX - rect.left,
|
|
14949
|
-
y: activatorEvt.clientY - rect.top
|
|
14950
|
-
};
|
|
14951
|
-
setOverlayPos({
|
|
14952
|
-
x: rect.left,
|
|
14953
|
-
y: rect.top
|
|
14954
|
-
});
|
|
14955
|
-
}
|
|
14956
|
-
window.addEventListener("mousemove", handleMouseMove);
|
|
14957
|
-
window.addEventListener("pointermove", handleMouseMove);
|
|
14958
|
-
if (active.data.current?.type === "COLUMN") {
|
|
14959
|
-
const columnId = active.id;
|
|
14960
|
-
const column = columnsProp.find((col) => String(col) === columnId);
|
|
14961
|
-
if (column) setActiveColumn(column);
|
|
14962
|
-
} else if (active.data.current?.type === "ITEM") {
|
|
14963
|
-
const columnId = findColumnByItemId(active.id);
|
|
14964
|
-
if (columnId) {
|
|
14965
|
-
const item = itemMapState[columnId]?.find((i) => i.id === active.id);
|
|
14966
|
-
setActiveItem(item || null);
|
|
14967
|
-
}
|
|
14968
|
-
}
|
|
14969
|
-
};
|
|
14970
|
-
const handleDragOver = (event) => {
|
|
14971
|
-
const { active, over } = event;
|
|
14972
|
-
if (!over) {
|
|
14973
|
-
setDragOverColumnId(null);
|
|
14974
|
-
return;
|
|
14975
|
-
}
|
|
14976
|
-
let currentHoveredColumnId = null;
|
|
14977
|
-
const overId = over.id;
|
|
14978
|
-
const overDataType = over.data.current?.type;
|
|
14979
|
-
if (overDataType === "ITEM-LIST" || overDataType === "COLUMN") currentHoveredColumnId = overId;
|
|
14980
|
-
else if (overDataType === "ITEM") currentHoveredColumnId = findColumnByItemId(overId) || null;
|
|
14981
|
-
else if (columnsProp.includes(overId)) currentHoveredColumnId = overId;
|
|
14982
|
-
setDragOverColumnId(currentHoveredColumnId);
|
|
14983
|
-
if (active.data.current?.type !== "ITEM") return;
|
|
14984
|
-
const activeId = active.id;
|
|
14985
|
-
const activeColumn = findColumnByItemId(activeId);
|
|
14986
|
-
let overColumnForMove = findColumnByItemId(overId);
|
|
14987
|
-
if (!overColumnForMove && overDataType === "ITEM-LIST") overColumnForMove = overId;
|
|
14988
|
-
if (!overColumnForMove && columnsProp.includes(overId)) overColumnForMove = overId;
|
|
14989
|
-
if (!activeColumn || !overColumnForMove) return;
|
|
14990
|
-
if (activeColumn === overColumnForMove) return;
|
|
14991
|
-
if (itemMapState[overColumnForMove]?.some((i) => i.id === activeId)) return;
|
|
14992
|
-
setItemMapState((currentMap) => {
|
|
14993
|
-
const activeItems = [...currentMap[activeColumn] || []];
|
|
14994
|
-
const overItems = [...currentMap[overColumnForMove] || []];
|
|
14995
|
-
const activeIndex = activeItems.findIndex((i) => i.id === activeId);
|
|
14996
|
-
if (activeIndex === -1) return currentMap;
|
|
14997
|
-
let overIndex;
|
|
14998
|
-
if (overDataType === "ITEM-LIST" || columnsProp.includes(overId) && !findColumnByItemId(overId)) overIndex = overItems.length;
|
|
14999
|
-
else {
|
|
15000
|
-
overIndex = overItems.findIndex((i) => i.id === overId);
|
|
15001
|
-
if (overIndex !== -1) {
|
|
15002
|
-
const modifier = (active.rect.current.translated?.top ?? 0) + (active.rect.current.translated?.height ?? 0) / 2 > (over?.rect.top ?? 0) + (over?.rect.height ?? 0) / 2 ? 1 : 0;
|
|
15003
|
-
overIndex = overIndex >= 0 ? overIndex + modifier : overItems.length;
|
|
15004
|
-
} else overIndex = overItems.length;
|
|
15005
|
-
}
|
|
15006
|
-
const newItemMap = { ...currentMap };
|
|
15007
|
-
const [moved] = activeItems.splice(activeIndex, 1);
|
|
15008
|
-
overItems.splice(overIndex, 0, moved);
|
|
15009
|
-
newItemMap[activeColumn] = activeItems;
|
|
15010
|
-
newItemMap[overColumnForMove] = overItems;
|
|
15011
|
-
return newItemMap;
|
|
15012
|
-
});
|
|
15013
|
-
};
|
|
15014
|
-
const handleDragEnd = (event) => {
|
|
15015
|
-
const { active, over } = event;
|
|
15016
|
-
window.removeEventListener("mousemove", handleMouseMove);
|
|
15017
|
-
window.removeEventListener("pointermove", handleMouseMove);
|
|
15018
|
-
setOverlayPos(null);
|
|
15019
|
-
setIsDragging(false);
|
|
15020
|
-
setActiveItem(null);
|
|
15021
|
-
setActiveColumn(null);
|
|
15022
|
-
setDragOverColumnId(null);
|
|
15023
|
-
if (!over) return;
|
|
15024
|
-
const activeId = active.id;
|
|
15025
|
-
const overId = over.id;
|
|
15026
|
-
if (active.data.current?.type === "COLUMN" && over.data.current?.type === "COLUMN" && activeId !== overId) {
|
|
15027
|
-
const oldIndex = columnsProp.findIndex((col) => String(col) === activeId);
|
|
15028
|
-
const newIndex = columnsProp.findIndex((col) => String(col) === overId);
|
|
15029
|
-
if (oldIndex !== -1 && newIndex !== -1 && onColumnReorder && allowColumnReorder) onColumnReorder(arrayMove([...columnsProp], oldIndex, newIndex));
|
|
15030
|
-
} else if (active.data.current?.type === "ITEM" && onItemsReorder) {
|
|
15031
|
-
const originalColumn = data.find((item) => item.id === activeId) ? assignColumn(data.find((item) => item.id === activeId)) : void 0;
|
|
15032
|
-
const currentColumn = findColumnByItemId(activeId);
|
|
15033
|
-
const overColumn = findColumnByItemId(overId) || currentColumn;
|
|
15034
|
-
const finalItemMapState = { ...itemMapState };
|
|
15035
|
-
if (currentColumn && currentColumn === overColumn) {
|
|
15036
|
-
const activeIndex = finalItemMapState[currentColumn].findIndex((i) => i.id === activeId);
|
|
15037
|
-
let overIndex = finalItemMapState[overColumn].findIndex((i) => i.id === overId);
|
|
15038
|
-
const overType = over.data.current?.type;
|
|
15039
|
-
if (overIndex === -1 && (overType === "COLUMN" || overType === "ITEM-LIST")) if (originalColumn === currentColumn) overIndex = finalItemMapState[overColumn].length;
|
|
15040
|
-
else overIndex = activeIndex;
|
|
15041
|
-
else if (overIndex === -1 && typeof over.data.current?.sortable?.index === "number") overIndex = over.data.current.sortable.index;
|
|
15042
|
-
if (activeIndex !== -1 && overIndex !== -1 && activeIndex !== overIndex) {
|
|
15043
|
-
finalItemMapState[currentColumn] = arrayMove(finalItemMapState[currentColumn], activeIndex, overIndex);
|
|
15044
|
-
setItemMapState(finalItemMapState);
|
|
15045
|
-
}
|
|
15046
|
-
}
|
|
15047
|
-
if (originalColumn !== currentColumn && originalColumn && currentColumn) onItemsReorder(finalItemMapState[currentColumn], {
|
|
15048
|
-
itemId: activeId,
|
|
15049
|
-
sourceColumn: originalColumn,
|
|
15050
|
-
targetColumn: currentColumn
|
|
15051
|
-
});
|
|
15052
|
-
else if (currentColumn) onItemsReorder(finalItemMapState[currentColumn], {
|
|
15053
|
-
itemId: activeId,
|
|
15054
|
-
sourceColumn: currentColumn,
|
|
15055
|
-
targetColumn: currentColumn
|
|
15056
|
-
});
|
|
15057
|
-
}
|
|
15058
|
-
};
|
|
15059
|
-
const customCollisionDetection = (args) => {
|
|
15060
|
-
const pointerCollisions = pointerWithin(args);
|
|
15061
|
-
if (pointerCollisions.length > 0) return pointerCollisions;
|
|
15062
|
-
return rectIntersection(args);
|
|
15063
|
-
};
|
|
15064
|
-
return /* @__PURE__ */ jsxs(DndContext, {
|
|
15065
|
-
sensors,
|
|
15066
|
-
collisionDetection: customCollisionDetection,
|
|
15067
|
-
onDragStart: handleDragStart,
|
|
15068
|
-
onDragOver: handleDragOver,
|
|
15069
|
-
onDragEnd: handleDragEnd,
|
|
15070
|
-
children: [
|
|
15071
|
-
/* @__PURE__ */ jsx(DragOverlay, {
|
|
15072
|
-
dropAnimation: null,
|
|
15073
|
-
style: { opacity: 0 },
|
|
15074
|
-
children: activeItem || activeColumn ? /* @__PURE__ */ jsx("div", { style: {
|
|
15075
|
-
width: 1,
|
|
15076
|
-
height: 1
|
|
15077
|
-
} }) : null
|
|
15078
|
-
}),
|
|
15079
|
-
overlayPos && (activeItem || activeColumn) && createPortal(/* @__PURE__ */ jsx("div", {
|
|
15080
|
-
className: document.documentElement.classList.contains("dark") ? "dark" : "",
|
|
15081
|
-
style: {
|
|
15082
|
-
position: "fixed",
|
|
15083
|
-
top: overlayPos.y,
|
|
15084
|
-
left: overlayPos.x,
|
|
15085
|
-
zIndex: 9999,
|
|
15086
|
-
pointerEvents: "none",
|
|
15087
|
-
touchAction: "none"
|
|
15088
|
-
},
|
|
15089
|
-
children: activeItem ? /* @__PURE__ */ jsx(ItemComponent, {
|
|
15090
|
-
item: activeItem,
|
|
15091
|
-
isDragging: true,
|
|
15092
|
-
index: -1,
|
|
15093
|
-
style: {
|
|
15094
|
-
boxShadow: "0 4px 16px rgba(0,0,0,0.15)",
|
|
15095
|
-
opacity: .9,
|
|
15096
|
-
width: "304px"
|
|
15097
|
-
}
|
|
15098
|
-
}) : activeColumn ? /* @__PURE__ */ jsx(BoardColumn, {
|
|
15099
|
-
index: -1,
|
|
15100
|
-
id: String(activeColumn),
|
|
15101
|
-
title: columnLabels?.[activeColumn] ?? String(activeColumn),
|
|
15102
|
-
items: itemMapState[String(activeColumn)] || [],
|
|
15103
|
-
ItemComponent,
|
|
15104
|
-
isDragging: true,
|
|
15105
|
-
isDragOverColumn: false
|
|
15106
|
-
}, String(activeColumn)) : null
|
|
15107
|
-
}), document.body),
|
|
15108
|
-
/* @__PURE__ */ jsx(SortableContext, {
|
|
15109
|
-
items: columnsProp.map(String),
|
|
15110
|
-
strategy: horizontalListSortingStrategy,
|
|
15111
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
15112
|
-
className: cls("p-2 md:p-3 lg:p-4 h-full min-w-full inline-flex", className),
|
|
15113
|
-
children: [columnsProp.map((key, index) => /* @__PURE__ */ jsx(BoardColumn, {
|
|
15114
|
-
index,
|
|
15115
|
-
id: String(key),
|
|
15116
|
-
title: columnLabels?.[key] ?? String(key),
|
|
15117
|
-
color: columnColors?.[key],
|
|
15118
|
-
items: itemMapState[String(key)] || [],
|
|
15119
|
-
ItemComponent,
|
|
15120
|
-
isDragging,
|
|
15121
|
-
isDragOverColumn: String(key) === dragOverColumnId,
|
|
15122
|
-
allowReorder: allowColumnReorder,
|
|
15123
|
-
loading: columnLoadingState?.[String(key)]?.loading,
|
|
15124
|
-
hasMore: columnLoadingState?.[String(key)]?.hasMore,
|
|
15125
|
-
totalCount: columnLoadingState?.[String(key)]?.totalCount,
|
|
15126
|
-
onLoadMore: onLoadMoreColumn ? () => onLoadMoreColumn(key) : void 0,
|
|
15127
|
-
onAddItem: onAddItemToColumn ? () => onAddItemToColumn(key) : void 0,
|
|
15128
|
-
style: { opacity: activeColumn === key ? 0 : 1 }
|
|
15129
|
-
}, String(key))), AddColumnComponent]
|
|
15130
|
-
})
|
|
15131
|
-
})
|
|
15132
|
-
]
|
|
15133
|
-
});
|
|
15134
|
-
}
|
|
15135
|
-
//#endregion
|
|
15136
14260
|
//#region src/components/EntityCollectionView/EntityBoardCard.tsx
|
|
15137
14261
|
/**
|
|
15138
14262
|
* Compact card component for displaying an entity in a Kanban board.
|
|
@@ -15143,7 +14267,7 @@ function Board({ data, columns: columnsProp, columnLabels, columnColors, classNa
|
|
|
15143
14267
|
* layout clean while providing intuitive selection affordance.
|
|
15144
14268
|
*/
|
|
15145
14269
|
function EntityBoardCardInner({ item, isDragging, isGroupedOver, style, collection, onClick, selected, onSelectionChange, selectionEnabled = false }) {
|
|
15146
|
-
const entity = item.
|
|
14270
|
+
const entity = item.data;
|
|
15147
14271
|
const slotKeys = useCollectionSlotKeys(collection, useAuthController(), useCustomizationController().propertyConfigs);
|
|
15148
14272
|
const slots = useMemo(() => resolveEntitySlots(entity, collection, slotKeys), [
|
|
15149
14273
|
entity,
|
|
@@ -15405,18 +14529,10 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
15405
14529
|
}));
|
|
15406
14530
|
return;
|
|
15407
14531
|
}
|
|
15408
|
-
const
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
const postgrestOp = op === "==" ? "eq" : op === "!=" ? "neq" : op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : op === "<=" ? "lte" : op === "in" ? "in" : op === "not-in" ? "nin" : op === "array-contains" ? "cs" : op === "array-contains-any" ? "csa" : "eq";
|
|
15413
|
-
let stringVal;
|
|
15414
|
-
if (Array.isArray(val)) stringVal = `(${val.join(",")})`;
|
|
15415
|
-
else stringVal = String(val);
|
|
15416
|
-
whereMap[key] = `${postgrestOp}.${stringVal}`;
|
|
15417
|
-
}
|
|
15418
|
-
});
|
|
15419
|
-
whereMap[currentColumnProperty] = `eq.${column}`;
|
|
14532
|
+
const whereFilter = {
|
|
14533
|
+
...currentFilterValues ?? {},
|
|
14534
|
+
[currentColumnProperty]: ["==", column]
|
|
14535
|
+
};
|
|
15420
14536
|
const orderByParam = currentOrderProperty ? `${currentOrderProperty}:asc` : void 0;
|
|
15421
14537
|
setColumnData((prev) => ({
|
|
15422
14538
|
...prev,
|
|
@@ -15525,13 +14641,13 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
15525
14641
|
const accessor = currentDataClient.collection(currentResolvedPath);
|
|
15526
14642
|
if (accessor.listen) {
|
|
15527
14643
|
const unsubscribe = accessor.listen({
|
|
15528
|
-
where:
|
|
14644
|
+
where: whereFilter,
|
|
15529
14645
|
limit: itemCount,
|
|
15530
14646
|
orderBy: orderByParam
|
|
15531
14647
|
}, (res) => onUpdate(res.data), onError);
|
|
15532
14648
|
unsubscribersRef.current[column] = unsubscribe;
|
|
15533
14649
|
} else accessor.find({
|
|
15534
|
-
where:
|
|
14650
|
+
where: whereFilter,
|
|
15535
14651
|
limit: itemCount,
|
|
15536
14652
|
orderBy: orderByParam
|
|
15537
14653
|
}).then((res) => onUpdate(res.data)).catch(onError);
|
|
@@ -15560,19 +14676,11 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
15560
14676
|
if (isColumnExcludedByFilter(column, currentFilterValues, currentColumnProperty)) return;
|
|
15561
14677
|
const accessor = currentDataClient.collection(currentResolvedPath);
|
|
15562
14678
|
if (accessor.count) {
|
|
15563
|
-
const
|
|
15564
|
-
|
|
15565
|
-
|
|
15566
|
-
|
|
15567
|
-
|
|
15568
|
-
let stringVal;
|
|
15569
|
-
if (Array.isArray(val)) stringVal = `(${val.join(",")})`;
|
|
15570
|
-
else stringVal = String(val);
|
|
15571
|
-
whereMap[key] = `${postgrestOp}.${stringVal}`;
|
|
15572
|
-
}
|
|
15573
|
-
});
|
|
15574
|
-
whereMap[currentColumnProperty] = `eq.${column}`;
|
|
15575
|
-
accessor.count({ where: whereMap }).then((count) => {
|
|
14679
|
+
const whereFilter = {
|
|
14680
|
+
...currentFilterValues ?? {},
|
|
14681
|
+
[currentColumnProperty]: ["==", column]
|
|
14682
|
+
};
|
|
14683
|
+
accessor.count({ where: whereFilter }).then((count) => {
|
|
15576
14684
|
if (isCleaningUpRef.current) return;
|
|
15577
14685
|
setColumnData((prev) => ({
|
|
15578
14686
|
...prev,
|
|
@@ -15758,7 +14866,7 @@ function useBoardDataController({ fullPath, collection, columnProperty, columns,
|
|
|
15758
14866
|
//#region src/components/EntityCollectionView/hooks/useKanbanDragAndDrop.ts
|
|
15759
14867
|
function useKanbanDragAndDrop({ collection, fullPath, columnProperty, orderProperty, dataClient, context, boardDataController, analyticsController }) {
|
|
15760
14868
|
const handleItemsReorder = useCallback(async (items, moveInfo) => {
|
|
15761
|
-
const entity = items.find((item) => item.id === moveInfo?.itemId)?.
|
|
14869
|
+
const entity = items.find((item) => item.id === moveInfo?.itemId)?.data;
|
|
15762
14870
|
if (!entity || !moveInfo) return;
|
|
15763
14871
|
analyticsController.onAnalyticsEvent?.("kanban_card_moved", {
|
|
15764
14872
|
path: fullPath,
|
|
@@ -15772,8 +14880,8 @@ function useKanbanDragAndDrop({ collection, fullPath, columnProperty, orderPrope
|
|
|
15772
14880
|
if (orderProperty) {
|
|
15773
14881
|
const targetColumnItems = items;
|
|
15774
14882
|
const movedIndex = targetColumnItems.findIndex((item) => item.id === moveInfo.itemId);
|
|
15775
|
-
const prevKey = movedIndex > 0 ? targetColumnItems[movedIndex - 1].
|
|
15776
|
-
const nextKey = movedIndex < targetColumnItems.length - 1 ? targetColumnItems[movedIndex + 1].
|
|
14883
|
+
const prevKey = movedIndex > 0 ? targetColumnItems[movedIndex - 1].data.values?.[orderProperty] ?? null : null;
|
|
14884
|
+
const nextKey = movedIndex < targetColumnItems.length - 1 ? targetColumnItems[movedIndex + 1].data.values?.[orderProperty] ?? null : null;
|
|
15777
14885
|
try {
|
|
15778
14886
|
const a = prevKey;
|
|
15779
14887
|
let b = nextKey;
|
|
@@ -15980,7 +15088,7 @@ function EntityCollectionBoardView({ collection, tableController, fullPath, pare
|
|
|
15980
15088
|
completed++;
|
|
15981
15089
|
if (completed === 2) setMissingOrderCount(Math.max(0, totalCount - orderedCount));
|
|
15982
15090
|
}).catch((e) => console.warn("Failed to get total count:", e));
|
|
15983
|
-
accessor.count({ where: { [orderProperty]: "
|
|
15091
|
+
accessor.count({ where: { [orderProperty]: ["!=", null] } }).then((count) => {
|
|
15984
15092
|
orderedCount = count;
|
|
15985
15093
|
completed++;
|
|
15986
15094
|
if (completed === 2) setMissingOrderCount(Math.max(0, totalCount - orderedCount));
|
|
@@ -16012,7 +15120,7 @@ function EntityCollectionBoardView({ collection, tableController, fullPath, pare
|
|
|
16012
15120
|
const boardItems = useMemo(() => {
|
|
16013
15121
|
return allEntities.map((entity) => ({
|
|
16014
15122
|
id: String(entity.id),
|
|
16015
|
-
entity
|
|
15123
|
+
data: entity
|
|
16016
15124
|
}));
|
|
16017
15125
|
}, [allEntities]);
|
|
16018
15126
|
const columnLoadingState = useMemo(() => {
|
|
@@ -16031,7 +15139,7 @@ function EntityCollectionBoardView({ collection, tableController, fullPath, pare
|
|
|
16031
15139
|
const assignColumn = useCallback((item) => {
|
|
16032
15140
|
const column = entityColumnMap[item.id];
|
|
16033
15141
|
if (column) return column;
|
|
16034
|
-
const value = item.
|
|
15142
|
+
const value = item.data.values?.[columnProperty];
|
|
16035
15143
|
if (value && columns.includes(String(value))) return String(value);
|
|
16036
15144
|
return columns[0] || "";
|
|
16037
15145
|
}, [
|
|
@@ -16125,7 +15233,7 @@ function EntityCollectionBoardView({ collection, tableController, fullPath, pare
|
|
|
16125
15233
|
...props,
|
|
16126
15234
|
collection: collectionRef.current,
|
|
16127
15235
|
onClick: stableOnClick,
|
|
16128
|
-
selected: isEntitySelectedRef.current(props.item.
|
|
15236
|
+
selected: isEntitySelectedRef.current(props.item.data),
|
|
16129
15237
|
onSelectionChange: stableOnSelectionChange,
|
|
16130
15238
|
selectionEnabled: selectionEnabledRef.current
|
|
16131
15239
|
});
|
|
@@ -16227,7 +15335,7 @@ function EntityCollectionBoardView({ collection, tableController, fullPath, pare
|
|
|
16227
15335
|
}),
|
|
16228
15336
|
/* @__PURE__ */ jsx("div", {
|
|
16229
15337
|
className: "flex-1 overflow-auto no-scrollbar",
|
|
16230
|
-
children: /* @__PURE__ */ jsx(
|
|
15338
|
+
children: /* @__PURE__ */ jsx(KanbanView, {
|
|
16231
15339
|
data: boardItems,
|
|
16232
15340
|
columns,
|
|
16233
15341
|
columnLabels,
|
|
@@ -17269,7 +16377,7 @@ var EntityCollectionViewInner = React.memo(function EntityCollectionViewInner({
|
|
|
17269
16377
|
const collectionRegistry = useCollectionRegistryController();
|
|
17270
16378
|
const urlController = useUrlController();
|
|
17271
16379
|
const breadcrumbs = useBreadcrumbsController();
|
|
17272
|
-
const path = pathProp ?? collectionProp
|
|
16380
|
+
const path = pathProp ?? getCollectionDataPath(collectionProp);
|
|
17273
16381
|
const dataClient = useData();
|
|
17274
16382
|
const sideEntityController = useSideEntityController();
|
|
17275
16383
|
useAuthController();
|
|
@@ -17808,7 +16916,7 @@ var EntityCollectionViewInner = React.memo(function EntityCollectionViewInner({
|
|
|
17808
16916
|
collection,
|
|
17809
16917
|
tableController,
|
|
17810
16918
|
path,
|
|
17811
|
-
relativePath: collection
|
|
16919
|
+
relativePath: getCollectionDataPath(collection),
|
|
17812
16920
|
selectionController: usedSelectionController,
|
|
17813
16921
|
collectionEntitiesCount: docsCount ?? void 0,
|
|
17814
16922
|
resolvedProperties: resolvedCollection.properties,
|
|
@@ -17824,7 +16932,7 @@ var EntityCollectionViewInner = React.memo(function EntityCollectionViewInner({
|
|
|
17824
16932
|
onNewClick,
|
|
17825
16933
|
openNewDocument,
|
|
17826
16934
|
path,
|
|
17827
|
-
relativePath: collection
|
|
16935
|
+
relativePath: getCollectionDataPath(collection),
|
|
17828
16936
|
selectionController: usedSelectionController,
|
|
17829
16937
|
selectionEnabled: activeSelectionEnabled,
|
|
17830
16938
|
collectionEntitiesCount: docsCount ?? void 0,
|
|
@@ -18089,18 +17197,7 @@ function EntitiesCount({ path, collection, filter, sortBy, onCountChange }) {
|
|
|
18089
17197
|
return;
|
|
18090
17198
|
}
|
|
18091
17199
|
let cancelled = false;
|
|
18092
|
-
const
|
|
18093
|
-
if (filter) Object.entries(filter).forEach(([key, value]) => {
|
|
18094
|
-
if (value && Array.isArray(value)) {
|
|
18095
|
-
const [op, val] = value;
|
|
18096
|
-
const postgrestOp = op === "==" ? "eq" : op === "!=" ? "neq" : op === ">" ? "gt" : op === ">=" ? "gte" : op === "<" ? "lt" : op === "<=" ? "lte" : op === "in" ? "in" : op === "not-in" ? "nin" : op === "array-contains" ? "cs" : op === "array-contains-any" ? "csa" : "eq";
|
|
18097
|
-
let stringVal;
|
|
18098
|
-
if (Array.isArray(val)) stringVal = `(${val.join(",")})`;
|
|
18099
|
-
else stringVal = String(val);
|
|
18100
|
-
whereMap[key] = `${postgrestOp}.${stringVal}`;
|
|
18101
|
-
}
|
|
18102
|
-
});
|
|
18103
|
-
const whereParams = Object.keys(whereMap).length > 0 ? whereMap : void 0;
|
|
17200
|
+
const whereParams = filter && Object.keys(filter).length > 0 ? filter : void 0;
|
|
18104
17201
|
const orderByParams = sortByProperty ? `${String(sortByProperty)}:${currentSort}` : void 0;
|
|
18105
17202
|
const cacheKey = `${path}|${filterKey}|${sortByProperty ?? ""}|${currentSort ?? ""}`;
|
|
18106
17203
|
let countPromise = inflightCountRequests.get(cacheKey);
|
|
@@ -18733,7 +17830,7 @@ function DrawerNavigationGroup({ group, entries, collapsed, onToggleCollapsed, d
|
|
|
18733
17830
|
})
|
|
18734
17831
|
]
|
|
18735
17832
|
}), /* @__PURE__ */ jsx("div", {
|
|
18736
|
-
className: cls("transition-all duration-200 ease-in-out", "overflow-hidden", !hideHeader && "dark:bg-transparent",
|
|
17833
|
+
className: cls("transition-all duration-200 ease-in-out", "overflow-hidden", !hideHeader && "dark:bg-transparent", "rounded-lg", !hideHeader && collapsed ? "max-h-0 opacity-0" : "max-h-[2000px] opacity-100"),
|
|
18737
17834
|
children: entries.map((entry) => /* @__PURE__ */ jsx(ResolvedDrawerNavigationItem, {
|
|
18738
17835
|
icon: /* @__PURE__ */ jsx(IconForView, {
|
|
18739
17836
|
collectionOrView: entry.collection ?? entry.view,
|
|
@@ -18823,7 +17920,13 @@ function CMSNavigationContent() {
|
|
|
18823
17920
|
const registry = useRebaseRegistry();
|
|
18824
17921
|
const ResolvedDrawerNavigationGroup = useComponentOverride("Shell.DrawerNavigationGroup", DrawerNavigationGroup);
|
|
18825
17922
|
const allNavigationEntries = navigationState.topLevelNavigation?.navigationEntries ?? [];
|
|
18826
|
-
const
|
|
17923
|
+
const studioViewSlugs = useMemo(() => {
|
|
17924
|
+
const slugs = /* @__PURE__ */ new Set();
|
|
17925
|
+
(registry.studioConfig?.devViews ?? []).forEach((v) => slugs.add(v.slug));
|
|
17926
|
+
if (registry.studioConfig && registry.cmsConfig?.collectionEditor) slugs.add("schema");
|
|
17927
|
+
return slugs;
|
|
17928
|
+
}, [registry.studioConfig, registry.cmsConfig?.collectionEditor]);
|
|
17929
|
+
const filteredEntries = adminModeController.mode === "studio" ? allNavigationEntries.filter((e) => e.type === "view" && studioViewSlugs.has(e.slug) || e.type === "admin") : allNavigationEntries.filter((e) => e.type !== "view" || !studioViewSlugs.has(e.slug));
|
|
18827
17930
|
const entryGroups = new Set(filteredEntries.map((e) => e.group).filter(Boolean));
|
|
18828
17931
|
const orderedGroups = navigationState.topLevelNavigation?.groups ?? [];
|
|
18829
17932
|
const groupsToRender = [...orderedGroups.filter((g) => entryGroups.has(g)), ...[...entryGroups].filter((g) => !orderedGroups.includes(g))];
|
|
@@ -19261,15 +18364,15 @@ function DrawerWrapper(props) {
|
|
|
19261
18364
|
* is auto-wired as a native feature (slots, provider, Studio view) without
|
|
19262
18365
|
* needing any external plugin.
|
|
19263
18366
|
*/
|
|
19264
|
-
function RebaseCMS({ collections, homePage, entityViews, entityActions,
|
|
18367
|
+
function RebaseCMS({ collections, views, homePage, entityViews, entityActions, collectionEditor, navigationGroupMappings }) {
|
|
19265
18368
|
const dispatch = useRebaseRegistryDispatch();
|
|
19266
18369
|
useLayoutEffect(() => {
|
|
19267
18370
|
dispatch.registerCMS({
|
|
19268
18371
|
collections,
|
|
18372
|
+
views,
|
|
19269
18373
|
homePage,
|
|
19270
18374
|
entityViews,
|
|
19271
18375
|
entityActions,
|
|
19272
|
-
plugins,
|
|
19273
18376
|
collectionEditor,
|
|
19274
18377
|
navigationGroupMappings
|
|
19275
18378
|
});
|
|
@@ -19277,10 +18380,10 @@ function RebaseCMS({ collections, homePage, entityViews, entityActions, plugins,
|
|
|
19277
18380
|
}, [
|
|
19278
18381
|
dispatch,
|
|
19279
18382
|
collections,
|
|
18383
|
+
views,
|
|
19280
18384
|
homePage,
|
|
19281
18385
|
entityViews,
|
|
19282
18386
|
entityActions,
|
|
19283
|
-
plugins,
|
|
19284
18387
|
collectionEditor,
|
|
19285
18388
|
navigationGroupMappings
|
|
19286
18389
|
]);
|
|
@@ -19416,7 +18519,7 @@ function useLocalCollectionsConfigController(clientOrUrl, baseCollections = [],
|
|
|
19416
18519
|
//#region src/components/RebaseNavigation.tsx
|
|
19417
18520
|
var EMPTY_PLUGINS = [];
|
|
19418
18521
|
var EMPTY_COLLECTIONS = [];
|
|
19419
|
-
var CollectionsStudioView = lazy(() => import("./
|
|
18522
|
+
var CollectionsStudioView = lazy(() => import("./RouterCollectionsStudioView-BQTIExjA.js").then((n) => n.n).then((m) => ({ default: m.RouterCollectionsStudioView })));
|
|
19420
18523
|
/**
|
|
19421
18524
|
* Navigation layer — builds and provides all CMS navigation controllers:
|
|
19422
18525
|
* collection registry, URL controller, navigation state, side entity,
|
|
@@ -19449,7 +18552,19 @@ function RebaseNavigation({ children }) {
|
|
|
19449
18552
|
}, [collectionEditorConfig]);
|
|
19450
18553
|
const cmsCollections = registry.cmsConfig?.collections ?? EMPTY_COLLECTIONS;
|
|
19451
18554
|
const collectionsBuilder = useMemo(() => Array.isArray(cmsCollections) ? () => [...cmsCollections] : cmsCollections, [cmsCollections]);
|
|
19452
|
-
const
|
|
18555
|
+
const dataSources = useDataSources();
|
|
18556
|
+
const collectionRegistryController = useBuildCollectionRegistryController({
|
|
18557
|
+
userConfigPersistence,
|
|
18558
|
+
dataSources: dataSources.registry
|
|
18559
|
+
});
|
|
18560
|
+
const defaultData = useData();
|
|
18561
|
+
const getCollectionRef = useRef(collectionRegistryController.getCollection);
|
|
18562
|
+
getCollectionRef.current = collectionRegistryController.getCollection;
|
|
18563
|
+
const routedData = useMemo(() => buildRoutedRebaseData({
|
|
18564
|
+
defaultData,
|
|
18565
|
+
sources: dataSources.sources,
|
|
18566
|
+
resolveKey: (slugOrPath) => resolveDataSource(getCollectionRef.current(slugOrPath), dataSources.registry).key
|
|
18567
|
+
}), [defaultData, dataSources]);
|
|
19453
18568
|
const urlController = useBuildUrlController({
|
|
19454
18569
|
basePath: "/",
|
|
19455
18570
|
baseCollectionPath: "/c",
|
|
@@ -19457,7 +18572,14 @@ function RebaseNavigation({ children }) {
|
|
|
19457
18572
|
});
|
|
19458
18573
|
const rebaseClient = useRebaseClient();
|
|
19459
18574
|
const authController = useAuthController();
|
|
19460
|
-
const
|
|
18575
|
+
const resolvedCollections = useMemo(() => Array.isArray(cmsCollections) ? cmsCollections : [], [cmsCollections]);
|
|
18576
|
+
useEffect(() => {
|
|
18577
|
+
if (process.env.NODE_ENV === "production") return;
|
|
18578
|
+
const { registry, sources } = dataSources;
|
|
18579
|
+
const missing = Object.values(registry).filter((d) => (d.transport === "direct" || d.transport === "custom") && !sources[d.key]).map((d) => d.key);
|
|
18580
|
+
if (missing.length > 0) console.warn(`[Rebase] These data source(s) declare a direct/custom transport but have no client-side driver and will fall back to the default data source: ${missing.map((k) => `"${k}"`).join(", ")}. Provide a \`driver\` for them in \`dataSources\` on <Rebase>.`);
|
|
18581
|
+
}, [dataSources]);
|
|
18582
|
+
const internalConfigController = useLocalCollectionsConfigController(rebaseClient, resolvedCollections, collectionEditorEnabled ? {
|
|
19461
18583
|
readOnly: collectionEditorOptions?.readOnly ?? process.env.NODE_ENV === "production",
|
|
19462
18584
|
getAuthToken: collectionEditorOptions?.getAuthToken ?? authController?.getAuthToken
|
|
19463
18585
|
} : { readOnly: true });
|
|
@@ -19474,6 +18596,7 @@ function RebaseNavigation({ children }) {
|
|
|
19474
18596
|
return {
|
|
19475
18597
|
slug: "schema",
|
|
19476
18598
|
name: "Edit collections",
|
|
18599
|
+
group: "Database",
|
|
19477
18600
|
icon: "LayoutList",
|
|
19478
18601
|
nestedRoutes: true,
|
|
19479
18602
|
view: /* @__PURE__ */ jsx(Suspense, {
|
|
@@ -19491,18 +18614,27 @@ function RebaseNavigation({ children }) {
|
|
|
19491
18614
|
if (schemaView) return [...base, schemaView];
|
|
19492
18615
|
return base;
|
|
19493
18616
|
}, [registry.studioConfig?.devViews, schemaView]);
|
|
18617
|
+
const cmsViews = registry.cmsConfig?.views;
|
|
18618
|
+
const mergedViews = useMemo(() => {
|
|
18619
|
+
if (!cmsViews) return devViews;
|
|
18620
|
+
if (Array.isArray(cmsViews) && cmsViews.length === 0) return devViews;
|
|
18621
|
+
if (typeof cmsViews === "function") return async (params) => {
|
|
18622
|
+
return [...await cmsViews(params) ?? [], ...devViews];
|
|
18623
|
+
};
|
|
18624
|
+
return [...cmsViews, ...devViews];
|
|
18625
|
+
}, [cmsViews, devViews]);
|
|
18626
|
+
const parentCustomizationController = useCustomizationController();
|
|
19494
18627
|
const navigationStateController = useBuildNavigationStateController({
|
|
19495
|
-
plugins:
|
|
18628
|
+
plugins: parentCustomizationController.plugins ?? EMPTY_PLUGINS,
|
|
19496
18629
|
collections: collectionsBuilder,
|
|
19497
|
-
views:
|
|
18630
|
+
views: mergedViews,
|
|
19498
18631
|
navigationGroupMappings: registry.cmsConfig?.navigationGroupMappings,
|
|
19499
18632
|
authController: context.authController,
|
|
19500
|
-
data:
|
|
18633
|
+
data: routedData,
|
|
19501
18634
|
collectionRegistryController,
|
|
19502
18635
|
urlController,
|
|
19503
18636
|
adminMode: adminModeController?.mode
|
|
19504
18637
|
});
|
|
19505
|
-
const parentCustomizationController = useCustomizationController();
|
|
19506
18638
|
const enrichedCustomizationController = useMemo(() => {
|
|
19507
18639
|
const cmsEntityViews = registry.cmsConfig?.entityViews ?? [];
|
|
19508
18640
|
const cmsEntityActions = registry.cmsConfig?.entityActions ?? [];
|
|
@@ -19517,22 +18649,25 @@ function RebaseNavigation({ children }) {
|
|
|
19517
18649
|
registry.cmsConfig?.entityViews,
|
|
19518
18650
|
registry.cmsConfig?.entityActions
|
|
19519
18651
|
]);
|
|
19520
|
-
const navigationContent = /* @__PURE__ */ jsx(
|
|
19521
|
-
value:
|
|
19522
|
-
children: /* @__PURE__ */ jsx(
|
|
19523
|
-
value:
|
|
19524
|
-
children: /* @__PURE__ */ jsx(
|
|
19525
|
-
value:
|
|
19526
|
-
children: /* @__PURE__ */ jsx(
|
|
19527
|
-
value:
|
|
19528
|
-
children: /* @__PURE__ */
|
|
19529
|
-
|
|
19530
|
-
|
|
19531
|
-
|
|
19532
|
-
|
|
18652
|
+
const navigationContent = /* @__PURE__ */ jsx(RebaseDataContext.Provider, {
|
|
18653
|
+
value: routedData,
|
|
18654
|
+
children: /* @__PURE__ */ jsx(CustomizationControllerContext.Provider, {
|
|
18655
|
+
value: enrichedCustomizationController,
|
|
18656
|
+
children: /* @__PURE__ */ jsx(StudioBridgeRegistryProvider, { children: /* @__PURE__ */ jsx(CollectionRegistryContext.Provider, {
|
|
18657
|
+
value: collectionRegistryController,
|
|
18658
|
+
children: /* @__PURE__ */ jsx(UrlContext.Provider, {
|
|
18659
|
+
value: urlController,
|
|
18660
|
+
children: /* @__PURE__ */ jsx(NavigationStateContext.Provider, {
|
|
18661
|
+
value: navigationStateController,
|
|
18662
|
+
children: /* @__PURE__ */ jsxs(SideEntityProvider, { children: [/* @__PURE__ */ jsx(BridgeAutoRegistrar, {
|
|
18663
|
+
collectionRegistryController,
|
|
18664
|
+
urlController,
|
|
18665
|
+
navigationStateController
|
|
18666
|
+
}), children] })
|
|
18667
|
+
})
|
|
19533
18668
|
})
|
|
19534
|
-
})
|
|
19535
|
-
})
|
|
18669
|
+
}) })
|
|
18670
|
+
})
|
|
19536
18671
|
});
|
|
19537
18672
|
if (collectionEditorEnabled) return /* @__PURE__ */ jsx(ConfigControllerProvider, {
|
|
19538
18673
|
collectionConfigController: internalConfigController,
|
|
@@ -20317,10 +19452,20 @@ function ContentHomePage({ additionalActions, additionalChildrenStart, additiona
|
|
|
20317
19452
|
breadcrumbs.set({ breadcrumbs: [] });
|
|
20318
19453
|
}, [breadcrumbs.set]);
|
|
20319
19454
|
const { allowDragAndDrop = false, navigationEntries: unFilteredNavigationEntries = [], groups: unFilteredGroupOrder = [], onNavigationEntriesUpdate = () => {} } = navigationStateController.topLevelNavigation || {};
|
|
19455
|
+
const studioViewSlugs = useMemo(() => {
|
|
19456
|
+
const slugs = /* @__PURE__ */ new Set();
|
|
19457
|
+
(registry.studioConfig?.devViews ?? []).forEach((v) => slugs.add(v.slug));
|
|
19458
|
+
if (registry.studioConfig && registry.cmsConfig?.collectionEditor) slugs.add("schema");
|
|
19459
|
+
return slugs;
|
|
19460
|
+
}, [registry.studioConfig, registry.cmsConfig?.collectionEditor]);
|
|
20320
19461
|
const rawNavigationEntries = useMemo(() => {
|
|
20321
|
-
if (adminModeController.mode === "studio") return unFilteredNavigationEntries.filter((e) => e.type === "view" || e.type === "admin");
|
|
20322
|
-
return unFilteredNavigationEntries.filter((e) => e.type !== "view");
|
|
20323
|
-
}, [
|
|
19462
|
+
if (adminModeController.mode === "studio") return unFilteredNavigationEntries.filter((e) => e.type === "view" && studioViewSlugs.has(e.slug) || e.type === "admin");
|
|
19463
|
+
return unFilteredNavigationEntries.filter((e) => e.type !== "view" || !studioViewSlugs.has(e.slug));
|
|
19464
|
+
}, [
|
|
19465
|
+
unFilteredNavigationEntries,
|
|
19466
|
+
adminModeController.mode,
|
|
19467
|
+
studioViewSlugs
|
|
19468
|
+
]);
|
|
20324
19469
|
const groupOrderFromNavController = useMemo(() => {
|
|
20325
19470
|
const entryGroups = new Set(rawNavigationEntries.map((e) => e.group).filter(Boolean));
|
|
20326
19471
|
return unFilteredGroupOrder.filter((g) => entryGroups.has(g));
|
|
@@ -20850,18 +19995,24 @@ function EntityFullScreenRoute({ pathname, navigationEntries, isNew, isCopy }) {
|
|
|
20850
19995
|
function CustomViewRoute({ view }) {
|
|
20851
19996
|
const breadcrumbs = useBreadcrumbsController();
|
|
20852
19997
|
const urlController = useUrlController();
|
|
19998
|
+
const breadcrumbsRef = useRef(breadcrumbs);
|
|
19999
|
+
breadcrumbsRef.current = breadcrumbs;
|
|
20853
20000
|
useEffect(() => {
|
|
20854
|
-
|
|
20001
|
+
breadcrumbsRef.current.set({ breadcrumbs: [{
|
|
20855
20002
|
title: view.name,
|
|
20856
20003
|
url: urlController.buildAppUrlPath(view.slug)
|
|
20857
20004
|
}] });
|
|
20858
20005
|
}, [view.slug, urlController]);
|
|
20859
|
-
|
|
20006
|
+
if (typeof view.view === "function") {
|
|
20007
|
+
const ViewComponent = view.view;
|
|
20008
|
+
return /* @__PURE__ */ jsx(ViewComponent, {});
|
|
20009
|
+
}
|
|
20010
|
+
return /* @__PURE__ */ jsx(Fragment, { children: view.view });
|
|
20860
20011
|
}
|
|
20861
20012
|
//#endregion
|
|
20862
20013
|
//#region src/components/CollectionEditorDialogs.tsx
|
|
20863
|
-
var CollectionEditorDialog = lazy(() => import("./CollectionEditorDialog-
|
|
20864
|
-
var PropertyFormDialog = lazy(() => import("./PropertyEditView-
|
|
20014
|
+
var CollectionEditorDialog = lazy(() => import("./CollectionEditorDialog-BO_aQ-Z4.js").then((n) => n.r).then((m) => ({ default: m.CollectionEditorDialog })));
|
|
20015
|
+
var PropertyFormDialog = lazy(() => import("./PropertyEditView-DKzOpMjR.js").then((n) => n.t).then((m) => ({ default: m.PropertyFormDialog })));
|
|
20865
20016
|
/**
|
|
20866
20017
|
* Renders the CollectionEditorDialog and PropertyFormDialog inside
|
|
20867
20018
|
* the RebaseShell tree where CMS-internal contexts
|
|
@@ -21650,6 +20801,7 @@ var PropertyPreview = React.memo(function PropertyPreview(props) {
|
|
|
21650
20801
|
content = /* @__PURE__ */ jsx(StorageThumbnail, {
|
|
21651
20802
|
interactive,
|
|
21652
20803
|
storeUrl: property.storage?.storeUrl ?? false,
|
|
20804
|
+
storageSourceKey: stringProperty.storage.storageSource,
|
|
21653
20805
|
size: props.size,
|
|
21654
20806
|
fill,
|
|
21655
20807
|
storagePathOrDownloadUrl: filePath
|
|
@@ -21677,19 +20829,6 @@ var PropertyPreview = React.memo(function PropertyPreview(props) {
|
|
|
21677
20829
|
propertyKey,
|
|
21678
20830
|
size: props.size
|
|
21679
20831
|
});
|
|
21680
|
-
else if (stringProperty.reference) if (typeof stringProperty.reference.path === "string") content = /* @__PURE__ */ jsx(ReferencePreview, {
|
|
21681
|
-
disabled: !stringProperty.reference.path,
|
|
21682
|
-
previewProperties: stringProperty.reference?.ui?.previewProperties,
|
|
21683
|
-
includeId: stringProperty.reference.includeId,
|
|
21684
|
-
includeEntityLink: stringProperty.reference.includeEntityLink,
|
|
21685
|
-
size: props.size,
|
|
21686
|
-
reference: new EntityReference({
|
|
21687
|
-
id: value,
|
|
21688
|
-
path: stringProperty.reference.path
|
|
21689
|
-
}),
|
|
21690
|
-
textOnly: props.textOnly
|
|
21691
|
-
});
|
|
21692
|
-
else content = /* @__PURE__ */ jsx(EmptyValue, {});
|
|
21693
20832
|
else content = /* @__PURE__ */ jsx(StringPropertyPreview, {
|
|
21694
20833
|
...props,
|
|
21695
20834
|
property: stringProperty,
|
|
@@ -22185,7 +21324,7 @@ function LocalChangesMenu({ cachedData, formex, onClearLocalChanges, cacheKey, p
|
|
|
22185
21324
|
})] });
|
|
22186
21325
|
}
|
|
22187
21326
|
//#endregion
|
|
22188
|
-
//#region src/form/
|
|
21327
|
+
//#region src/form/form_utils.ts
|
|
22189
21328
|
function extractTouchedValues(values, touched) {
|
|
22190
21329
|
let acc = {};
|
|
22191
21330
|
if (!touched || typeof touched !== "object") return acc;
|
|
@@ -22201,7 +21340,7 @@ function extractTouchedValues(values, touched) {
|
|
|
22201
21340
|
* from falsely triggering the unsaved local changes indicator.
|
|
22202
21341
|
*/
|
|
22203
21342
|
/**
|
|
22204
|
-
*
|
|
21343
|
+
* Check if a value is semantically empty (null, undefined, or empty string).
|
|
22205
21344
|
*/
|
|
22206
21345
|
function isSemanticEmpty(v) {
|
|
22207
21346
|
return v === null || v === void 0 || v === "";
|
|
@@ -22245,23 +21384,51 @@ function getChanges(source, comparison) {
|
|
|
22245
21384
|
}
|
|
22246
21385
|
return changes;
|
|
22247
21386
|
}
|
|
22248
|
-
function
|
|
22249
|
-
const
|
|
22250
|
-
|
|
22251
|
-
|
|
22252
|
-
|
|
22253
|
-
|
|
22254
|
-
|
|
22255
|
-
|
|
21387
|
+
function getInitialEntityValues(authController, collection, path, status, entity, propertyConfigs) {
|
|
21388
|
+
const properties = collection.properties;
|
|
21389
|
+
if ((status === "existing" || status === "copy") && entity) {
|
|
21390
|
+
let values;
|
|
21391
|
+
if (!collection.alwaysApplyDefaultValues) values = entity.values ?? getDefaultValuesFor(properties);
|
|
21392
|
+
else values = mergeDeep(getDefaultValuesFor(properties), entity.values ?? {});
|
|
21393
|
+
if (status === "copy") {
|
|
21394
|
+
const result = { ...values };
|
|
21395
|
+
for (const [key, property] of Object.entries(properties)) if (property && "isId" in property && property.isId) delete result[key];
|
|
21396
|
+
return result;
|
|
22256
21397
|
}
|
|
22257
|
-
|
|
22258
|
-
|
|
22259
|
-
|
|
22260
|
-
|
|
22261
|
-
|
|
22262
|
-
|
|
22263
|
-
|
|
22264
|
-
|
|
21398
|
+
return values;
|
|
21399
|
+
} else if (status === "new") return getDefaultValuesFor(properties);
|
|
21400
|
+
else {
|
|
21401
|
+
console.error({
|
|
21402
|
+
status,
|
|
21403
|
+
entity
|
|
21404
|
+
});
|
|
21405
|
+
throw new Error("Form has not been initialised with the correct parameters");
|
|
21406
|
+
}
|
|
21407
|
+
}
|
|
21408
|
+
function zodToFormErrors(zodError) {
|
|
21409
|
+
let errors = {};
|
|
21410
|
+
for (const issue of zodError.issues) {
|
|
21411
|
+
const path = issue.path.join(".");
|
|
21412
|
+
if (path && !getIn(errors, path)) errors = setIn(errors, path, issue.message);
|
|
21413
|
+
}
|
|
21414
|
+
return errors;
|
|
21415
|
+
}
|
|
21416
|
+
//#endregion
|
|
21417
|
+
//#region src/form/EntityForm.tsx
|
|
21418
|
+
/**
|
|
21419
|
+
* Headless entity form component.
|
|
21420
|
+
*
|
|
21421
|
+
* Renders a form for an entity collection without any CMS or backend dependencies.
|
|
21422
|
+
* All backend concerns (save, caching, analytics, plugin slots) are provided via
|
|
21423
|
+
* callback props. For CMS-connected usage, use {@link EntityFormBinding} instead.
|
|
21424
|
+
*
|
|
21425
|
+
* @group Components
|
|
21426
|
+
*/
|
|
21427
|
+
function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified, onIdChange, onSaved, entity, initialDirtyValues, onFormContextReady, forceActionsAtTheBottom, initialStatus, className, onStatusChange, onEntityChange, openEntityMode = "full_screen", formex: formexProp, disabled: disabledProp, Builder, EntityFormActionsComponent = EntityFormActions, showDefaultActions = true, showEntityPath = true, navigateBack: navigateBackProp, children, onSubmit: onSubmitProp, onValuesChangeDeferred: onValuesChangeDeferredProp, onReset: onResetProp, uniqueFieldValidator: uniqueFieldValidatorProp, beforeFields, afterFields, pluginActions: pluginActionsProp, computedInitialValues, hasLocalChanges: hasLocalChangesProp, localChangesData, manualApplyLocalChanges, localChangesCacheKey, onClearLocalChanges }) {
|
|
21428
|
+
const customizationController = useCustomizationController();
|
|
21429
|
+
const navigateBack = useCallback(() => {
|
|
21430
|
+
if (navigateBackProp) navigateBackProp();
|
|
21431
|
+
}, [navigateBackProp]);
|
|
22265
21432
|
const [status, setStatus] = useState(initialStatus);
|
|
22266
21433
|
const updateStatus = (status) => {
|
|
22267
21434
|
setStatus(status);
|
|
@@ -22269,18 +21436,11 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22269
21436
|
};
|
|
22270
21437
|
const [valuesToBeSaved, setValuesToBeSaved] = useState(void 0);
|
|
22271
21438
|
useDebouncedCallback(valuesToBeSaved, () => {
|
|
22272
|
-
if (valuesToBeSaved)
|
|
22273
|
-
|
|
22274
|
-
|
|
22275
|
-
|
|
22276
|
-
values: valuesToBeSaved
|
|
22277
|
-
});
|
|
21439
|
+
if (valuesToBeSaved && onSubmitProp) {
|
|
21440
|
+
setIsSavingAutoSave(true);
|
|
21441
|
+
Promise.resolve(onSubmitProp(valuesToBeSaved, formex)).finally(() => setIsSavingAutoSave(false));
|
|
21442
|
+
}
|
|
22278
21443
|
}, false, 2e3);
|
|
22279
|
-
const dataClient = useData();
|
|
22280
|
-
const snackbarController = useSnackbarController();
|
|
22281
|
-
const customizationController = useCustomizationController();
|
|
22282
|
-
const context = useCMSContext();
|
|
22283
|
-
const analyticsController = useAnalyticsController();
|
|
22284
21444
|
const [underlyingChanges] = useState({});
|
|
22285
21445
|
const initialEntityId = useMemo(() => {
|
|
22286
21446
|
if (status === "new" || status === "copy") return;
|
|
@@ -22289,58 +21449,45 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22289
21449
|
const [entityId, setEntityId] = useState(initialEntityId);
|
|
22290
21450
|
const [entityIdError, setEntityIdError] = useState(false);
|
|
22291
21451
|
const [savingError, setSavingError] = useState();
|
|
21452
|
+
const [isSavingAutoSave, setIsSavingAutoSave] = useState(false);
|
|
21453
|
+
const [discardDialogOpen, setDiscardDialogOpen] = useState(false);
|
|
22292
21454
|
const autoSave = collection.formAutoSave;
|
|
22293
|
-
const baseInitialValues = useMemo(() =>
|
|
22294
|
-
|
|
22295
|
-
collection
|
|
22296
|
-
|
|
21455
|
+
const baseInitialValues = useMemo(() => {
|
|
21456
|
+
if (computedInitialValues !== void 0) return computedInitialValues;
|
|
21457
|
+
if ((status === "existing" || status === "copy") && entity) return entity.values ?? getDefaultValuesFor(collection.properties);
|
|
21458
|
+
return getDefaultValuesFor(collection.properties);
|
|
21459
|
+
}, [
|
|
21460
|
+
computedInitialValues,
|
|
21461
|
+
collection.properties,
|
|
22297
21462
|
status,
|
|
22298
|
-
entity
|
|
22299
|
-
customizationController.propertyConfigs
|
|
21463
|
+
entity
|
|
22300
21464
|
]);
|
|
22301
|
-
const localChangesDataRaw = useMemo(() => entityId ? getEntityFromCache(path + "/" + entityId) : getEntityFromCache(path + "#new"), [entityId, path]);
|
|
22302
21465
|
const [localChangesCleared, setLocalChangesCleared] = useState(false);
|
|
22303
|
-
const
|
|
22304
|
-
const autoApplyLocalChanges = localChangesBackup === "auto_apply";
|
|
22305
|
-
const manualApplyLocalChanges = localChangesBackup === "manual_apply";
|
|
21466
|
+
const hasLocalChanges = hasLocalChangesProp !== void 0 ? hasLocalChangesProp && !localChangesCleared : false;
|
|
22306
21467
|
const onSubmit = (values, formexController) => {
|
|
22307
21468
|
setSavingError(void 0);
|
|
22308
21469
|
setEntityIdError(false);
|
|
22309
21470
|
if (status === "existing") {
|
|
22310
21471
|
if (!entity?.id) throw Error("Form misconfiguration when saving, no id for existing entity");
|
|
22311
21472
|
} else if (status !== "new" && status !== "copy") throw Error("New FormType added, check EntityForm");
|
|
22312
|
-
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22316
|
-
|
|
22317
|
-
|
|
22318
|
-
|
|
22319
|
-
|
|
22320
|
-
|
|
22321
|
-
|
|
22322
|
-
|
|
22323
|
-
}
|
|
21473
|
+
if (!onSubmitProp) {
|
|
21474
|
+
console.warn("EntityForm: no onSubmit callback provided. Form submission has no effect.");
|
|
21475
|
+
formexController.setSubmitting(false);
|
|
21476
|
+
return;
|
|
21477
|
+
}
|
|
21478
|
+
return Promise.resolve(save(values))?.then((savedEntity) => {
|
|
21479
|
+
if (savedEntity) formexController.resetForm({
|
|
21480
|
+
values: savedEntity.values || values,
|
|
21481
|
+
submitCount: 0,
|
|
21482
|
+
touched: {}
|
|
21483
|
+
});
|
|
22324
21484
|
}).finally(() => {
|
|
22325
21485
|
formexController.setSubmitting(false);
|
|
22326
21486
|
});
|
|
22327
21487
|
};
|
|
22328
21488
|
const [initialValues, initialDirty] = useMemo(() => {
|
|
22329
|
-
|
|
22330
|
-
|
|
22331
|
-
}, [
|
|
22332
|
-
autoApplyLocalChanges,
|
|
22333
|
-
localChangesDataRaw,
|
|
22334
|
-
baseInitialValues,
|
|
22335
|
-
initialDirtyValues
|
|
22336
|
-
]);
|
|
22337
|
-
const localChangesData = useMemo(() => {
|
|
22338
|
-
if (!localChangesDataRaw) return;
|
|
22339
|
-
const cleaned = removeEmptyContainers(getChanges(localChangesDataRaw, initialValues));
|
|
22340
|
-
if (cleaned && typeof cleaned === "object" && Object.keys(cleaned).length === 0) return;
|
|
22341
|
-
return cleaned;
|
|
22342
|
-
}, [localChangesDataRaw, initialValues]);
|
|
22343
|
-
const hasLocalChanges = !localChangesCleared && !!localChangesData && Object.keys(localChangesData).length > 0;
|
|
21489
|
+
return [initialDirtyValues ? mergeDeep(baseInitialValues, initialDirtyValues) : baseInitialValues, Boolean(initialDirtyValues) && initialDirtyValues && Object.keys(initialDirtyValues).length > 0];
|
|
21490
|
+
}, [baseInitialValues, initialDirtyValues]);
|
|
22344
21491
|
const internalFormex = useCreateFormex({
|
|
22345
21492
|
initialValues,
|
|
22346
21493
|
initialDirty,
|
|
@@ -22351,17 +21498,10 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22351
21498
|
}), {}) : {},
|
|
22352
21499
|
onSubmit,
|
|
22353
21500
|
onReset: () => {
|
|
22354
|
-
|
|
21501
|
+
onResetProp?.();
|
|
22355
21502
|
onValuesModified?.(false, initialValues);
|
|
22356
21503
|
},
|
|
22357
|
-
onValuesChangeDeferred:
|
|
22358
|
-
const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
|
|
22359
|
-
if (controller.dirty) {
|
|
22360
|
-
const touchedValues = removeEmptyContainers(extractTouchedValues(values, controller.touched));
|
|
22361
|
-
if (touchedValues && Object.keys(touchedValues).length > 0) saveEntityToCache(key, touchedValues);
|
|
22362
|
-
else removeEntityFromCache(key);
|
|
22363
|
-
}
|
|
22364
|
-
},
|
|
21504
|
+
onValuesChangeDeferred: onValuesChangeDeferredProp,
|
|
22365
21505
|
validation: async (values) => {
|
|
22366
21506
|
if (!validationSchema) return {};
|
|
22367
21507
|
const result = await validationSchema.safeParseAsync(values);
|
|
@@ -22374,7 +21514,10 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22374
21514
|
const handleKeyDown = (e) => {
|
|
22375
21515
|
const isUndo = (e.metaKey || e.ctrlKey) && !e.shiftKey && e.key.toLowerCase() === "z";
|
|
22376
21516
|
const isRedo = (e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === "z" || (e.metaKey || e.ctrlKey) && !e.shiftKey && e.key.toLowerCase() === "y";
|
|
22377
|
-
if (
|
|
21517
|
+
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "s" && !disabled && formex.dirty) {
|
|
21518
|
+
e.preventDefault();
|
|
21519
|
+
formex.handleSubmit();
|
|
21520
|
+
} else if (isUndo && formex.canUndo) {
|
|
22378
21521
|
e.preventDefault();
|
|
22379
21522
|
formex.undo();
|
|
22380
21523
|
} else if (isRedo && formex.canRedo) {
|
|
@@ -22385,31 +21528,8 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22385
21528
|
window.addEventListener("keydown", handleKeyDown);
|
|
22386
21529
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
22387
21530
|
}, [formex]);
|
|
22388
|
-
useCallback((e) => {
|
|
22389
|
-
snackbarController.open({
|
|
22390
|
-
type: "error",
|
|
22391
|
-
message: `${t("error_before_saving")}: ${e?.message}`
|
|
22392
|
-
});
|
|
22393
|
-
console.error(e);
|
|
22394
|
-
}, [snackbarController]);
|
|
22395
|
-
useCallback((e) => {
|
|
22396
|
-
snackbarController.open({
|
|
22397
|
-
type: "error",
|
|
22398
|
-
message: `${t("error_after_saving")}: ${e?.message}`
|
|
22399
|
-
});
|
|
22400
|
-
console.error(e);
|
|
22401
|
-
}, [snackbarController]);
|
|
22402
|
-
function clearDirtyCache() {
|
|
22403
|
-
if (status === "new" || status === "copy") {
|
|
22404
|
-
removeEntityFromMemoryCache(path + "#new");
|
|
22405
|
-
removeEntityFromCache(path + "#new");
|
|
22406
|
-
} else {
|
|
22407
|
-
removeEntityFromMemoryCache(path + "/" + entityId);
|
|
22408
|
-
removeEntityFromCache(path + "/" + entityId);
|
|
22409
|
-
}
|
|
22410
|
-
}
|
|
22411
21531
|
const afterSave = (updatedEntity) => {
|
|
22412
|
-
|
|
21532
|
+
onResetProp?.();
|
|
22413
21533
|
onValuesModified?.(false, updatedEntity.values);
|
|
22414
21534
|
onEntityChange?.(updatedEntity);
|
|
22415
21535
|
updateStatus("existing");
|
|
@@ -22422,62 +21542,19 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22422
21542
|
collection
|
|
22423
21543
|
});
|
|
22424
21544
|
};
|
|
22425
|
-
const afterSaveError = useCallback((e) => {
|
|
22426
|
-
snackbarController.open({
|
|
22427
|
-
type: "error",
|
|
22428
|
-
title: t("error_saving_entity"),
|
|
22429
|
-
message: e?.message
|
|
22430
|
-
});
|
|
22431
|
-
console.error("Error saving entity", path, entityId, e);
|
|
22432
|
-
console.error(e);
|
|
22433
|
-
}, [
|
|
22434
|
-
entityId,
|
|
22435
|
-
path,
|
|
22436
|
-
snackbarController
|
|
22437
|
-
]);
|
|
22438
|
-
const saveEntity = ({ values, previousValues, entityId, collection, path }) => {
|
|
22439
|
-
return saveEntityWithCallbacks({
|
|
22440
|
-
path,
|
|
22441
|
-
entityId,
|
|
22442
|
-
values,
|
|
22443
|
-
previousValues,
|
|
22444
|
-
collection,
|
|
22445
|
-
status,
|
|
22446
|
-
data: dataClient,
|
|
22447
|
-
context,
|
|
22448
|
-
afterSave,
|
|
22449
|
-
afterSaveError
|
|
22450
|
-
});
|
|
22451
|
-
};
|
|
22452
|
-
const onSaveEntityRequest = async ({ collection, path, entityId, values, previousValues, autoSave }) => {
|
|
22453
|
-
if (!status) return;
|
|
22454
|
-
if (autoSave) {
|
|
22455
|
-
setValuesToBeSaved(values);
|
|
22456
|
-
return Promise.resolve();
|
|
22457
|
-
} else return saveEntity({
|
|
22458
|
-
collection,
|
|
22459
|
-
path,
|
|
22460
|
-
entityId,
|
|
22461
|
-
values,
|
|
22462
|
-
previousValues
|
|
22463
|
-
});
|
|
22464
|
-
};
|
|
22465
21545
|
const lastSavedValues = useRef(entity?.values);
|
|
22466
21546
|
const save = async (values) => {
|
|
22467
21547
|
const valuesToSave = status === "existing" ? getChanges(values, entity?.values || {}) : values;
|
|
22468
21548
|
if (status === "existing" && Object.keys(valuesToSave).length === 0 && entity) return Promise.resolve(entity);
|
|
22469
21549
|
lastSavedValues.current = values;
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
|
|
22475
|
-
|
|
22476
|
-
|
|
22477
|
-
|
|
22478
|
-
const eventName = status === "new" ? "new_entity_saved" : status === "copy" ? "entity_copied" : status === "existing" ? "entity_edited" : "unmapped_event";
|
|
22479
|
-
analyticsController.onAnalyticsEvent?.(eventName, { path });
|
|
22480
|
-
return savedEntity;
|
|
21550
|
+
if (!onSubmitProp) return;
|
|
21551
|
+
if (autoSave ?? false) {
|
|
21552
|
+
setValuesToBeSaved(values);
|
|
21553
|
+
return Promise.resolve();
|
|
21554
|
+
}
|
|
21555
|
+
return Promise.resolve(onSubmitProp(valuesToSave, formex)).then((result) => {
|
|
21556
|
+
if (result) afterSave(result);
|
|
21557
|
+
return result;
|
|
22481
21558
|
}).catch((e) => {
|
|
22482
21559
|
console.error(e);
|
|
22483
21560
|
setSavingError(e);
|
|
@@ -22508,44 +21585,18 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22508
21585
|
path
|
|
22509
21586
|
]);
|
|
22510
21587
|
const actionsDisabled = disabled || formex.isSubmitting || status === "existing" && !formex.dirty || Boolean(disabledProp);
|
|
22511
|
-
const formActionProps = {
|
|
22512
|
-
entityId,
|
|
22513
|
-
parentCollectionSlugs: collectionRegistryController.getParentCollectionSlugs(path),
|
|
22514
|
-
parentEntityIds: collectionRegistryController.getParentEntityIds(path),
|
|
22515
|
-
path,
|
|
22516
|
-
status,
|
|
22517
|
-
collection,
|
|
22518
|
-
context,
|
|
22519
|
-
formContext,
|
|
22520
|
-
openEntityMode,
|
|
22521
|
-
disabled: actionsDisabled
|
|
22522
|
-
};
|
|
22523
|
-
const pluginFormActions = useSlot("form.actions", formActionProps);
|
|
22524
|
-
const pluginFormBefore = useSlot("form.before", formActionProps);
|
|
22525
|
-
const pluginFormAfter = useSlot("form.after", formActionProps);
|
|
22526
21588
|
const titlePropertyKey = getEntityTitlePropertyKey(collection, customizationController.propertyConfigs);
|
|
22527
21589
|
const rawTitle = formex.values && titlePropertyKey ? getValueInPath(formex.values, titlePropertyKey) : void 0;
|
|
22528
21590
|
const title = rawTitle !== void 0 && rawTitle !== null ? resolveTitleToString(rawTitle) : collection.singularName ?? collection.name;
|
|
21591
|
+
const modified = formex.dirty;
|
|
22529
21592
|
useEffect(() => {
|
|
22530
21593
|
if (!autoSave) onValuesModified?.(modified, formex.values);
|
|
22531
21594
|
}, [formex.dirty]);
|
|
22532
|
-
const
|
|
22533
|
-
const uniqueFieldValidator =
|
|
22534
|
-
|
|
22535
|
-
|
|
22536
|
-
|
|
22537
|
-
limit: 2
|
|
22538
|
-
});
|
|
22539
|
-
return (entityId ? data.filter((e) => e.id !== entityId) : data).length === 0;
|
|
22540
|
-
} catch (e) {
|
|
22541
|
-
console.error("Error checking unique field", e);
|
|
22542
|
-
return true;
|
|
22543
|
-
}
|
|
22544
|
-
}, [
|
|
22545
|
-
dataClient,
|
|
22546
|
-
path,
|
|
22547
|
-
entityId
|
|
22548
|
-
]);
|
|
21595
|
+
const defaultUniqueFieldValidator = useCallback(async () => true, []);
|
|
21596
|
+
const uniqueFieldValidator = useMemo(() => {
|
|
21597
|
+
if (uniqueFieldValidatorProp) return uniqueFieldValidatorProp;
|
|
21598
|
+
return defaultUniqueFieldValidator;
|
|
21599
|
+
}, [uniqueFieldValidatorProp, defaultUniqueFieldValidator]);
|
|
22549
21600
|
const validationSchema = useMemo(() => getEntitySchema(entityId, collection.properties, uniqueFieldValidator), [
|
|
22550
21601
|
entityId,
|
|
22551
21602
|
collection.properties,
|
|
@@ -22577,6 +21628,8 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22577
21628
|
modifiedValues: formex.values,
|
|
22578
21629
|
formContext
|
|
22579
21630
|
});
|
|
21631
|
+
const isNewEntity = status === "new" || status === "copy";
|
|
21632
|
+
let firstFocusableIndex = -1;
|
|
22580
21633
|
return /* @__PURE__ */ jsx(FormLayout, { children: formFieldKeys.map((key) => {
|
|
22581
21634
|
const property = collection.properties?.[key];
|
|
22582
21635
|
if (property) {
|
|
@@ -22585,9 +21638,12 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22585
21638
|
const isIdAndAuto = (property.type === "string" || property.type === "number") && "isId" in property && typeof property.isId === "string" && property.isId !== "manual";
|
|
22586
21639
|
const disabled = disabledProp || !autoSave && formex.isSubmitting || isReadOnly(property) || Boolean(property.ui?.disabled) || !isNew && "isId" in property && Boolean(property.isId) || isNew && isIdAndAuto;
|
|
22587
21640
|
if (isHidden(property)) return null;
|
|
21641
|
+
const widthPercentage = property.ui?.widthPercentage ?? 100;
|
|
21642
|
+
const shouldAutoFocus = isNewEntity && !disabled && firstFocusableIndex === -1;
|
|
21643
|
+
if (shouldAutoFocus) firstFocusableIndex = 0;
|
|
22588
21644
|
return /* @__PURE__ */ jsx(FormEntry, {
|
|
22589
21645
|
propertyKey: key,
|
|
22590
|
-
widthPercentage
|
|
21646
|
+
widthPercentage,
|
|
22591
21647
|
children: /* @__PURE__ */ jsx(PropertyFieldBinding, {
|
|
22592
21648
|
propertyKey: key,
|
|
22593
21649
|
disabled,
|
|
@@ -22597,22 +21653,23 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22597
21653
|
context: formContext,
|
|
22598
21654
|
partOfArray: false,
|
|
22599
21655
|
minimalistView: false,
|
|
22600
|
-
autoFocus:
|
|
21656
|
+
autoFocus: shouldAutoFocus
|
|
22601
21657
|
})
|
|
22602
21658
|
}, `field_${key}`);
|
|
22603
21659
|
}
|
|
22604
21660
|
const additionalField = collection.additionalFields?.find((f) => f.key === key);
|
|
22605
21661
|
if (additionalField && entity) {
|
|
22606
|
-
const
|
|
22607
|
-
if (!
|
|
22608
|
-
const
|
|
21662
|
+
const AdditionalFieldBuilder = additionalField.Builder;
|
|
21663
|
+
if (!AdditionalFieldBuilder && !additionalField.value) throw new Error("When using additional fields you need to provide a Builder or a value");
|
|
21664
|
+
const additionalFieldContext = formContext;
|
|
21665
|
+
const child = AdditionalFieldBuilder ? /* @__PURE__ */ jsx(AdditionalFieldBuilder, {
|
|
22609
21666
|
entity,
|
|
22610
|
-
context
|
|
21667
|
+
context: additionalFieldContext
|
|
22611
21668
|
}) : /* @__PURE__ */ jsx("div", {
|
|
22612
21669
|
className: "w-full",
|
|
22613
21670
|
children: additionalField.value?.({
|
|
22614
21671
|
entity,
|
|
22615
|
-
context
|
|
21672
|
+
context: additionalFieldContext
|
|
22616
21673
|
})?.toString()
|
|
22617
21674
|
});
|
|
22618
21675
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -22633,8 +21690,9 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22633
21690
|
}).filter(Boolean) });
|
|
22634
21691
|
};
|
|
22635
21692
|
const formRef = useRef(null);
|
|
21693
|
+
const hasFormErrors = Object.keys(formex.errors).length > 0 && formex.submitCount > 0;
|
|
22636
21694
|
const formView = /* @__PURE__ */ jsx(ErrorBoundary, { children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
22637
|
-
|
|
21695
|
+
beforeFields,
|
|
22638
21696
|
!Builder && /* @__PURE__ */ jsxs("div", {
|
|
22639
21697
|
className: "w-full flex flex-col items-start my-4 lg:my-6",
|
|
22640
21698
|
children: [
|
|
@@ -22647,7 +21705,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22647
21705
|
color: "warning",
|
|
22648
21706
|
size: "small",
|
|
22649
21707
|
outerClassName: "w-full mb-4 text-xs",
|
|
22650
|
-
children:
|
|
21708
|
+
children: "This entity does not exist yet"
|
|
22651
21709
|
}),
|
|
22652
21710
|
showEntityPath && /* @__PURE__ */ jsx(Alert, {
|
|
22653
21711
|
color: "base",
|
|
@@ -22668,15 +21726,43 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22668
21726
|
initialEntityId && !entity && initialStatus !== "new" && /* @__PURE__ */ jsx(Alert, {
|
|
22669
21727
|
color: "info",
|
|
22670
21728
|
size: "small",
|
|
22671
|
-
children:
|
|
21729
|
+
children: "This entity does not exist yet"
|
|
21730
|
+
}),
|
|
21731
|
+
hasFormErrors && /* @__PURE__ */ jsx(Alert, {
|
|
21732
|
+
color: "error",
|
|
21733
|
+
size: "small",
|
|
21734
|
+
outerClassName: "w-full mt-2",
|
|
21735
|
+
children: "Please fix the highlighted errors before saving."
|
|
22672
21736
|
}),
|
|
22673
21737
|
formContext && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", {
|
|
22674
21738
|
className: "mt-12 flex flex-col gap-8",
|
|
22675
21739
|
ref: formRef,
|
|
22676
21740
|
children: [formFields(), /* @__PURE__ */ jsx(ErrorFocus, { containerRef: formRef })]
|
|
22677
21741
|
}) }),
|
|
22678
|
-
|
|
22679
|
-
forceActionsAtTheBottom && /* @__PURE__ */ jsx("div", { className: "h-16" })
|
|
21742
|
+
afterFields,
|
|
21743
|
+
forceActionsAtTheBottom && /* @__PURE__ */ jsx("div", { className: "h-16" }),
|
|
21744
|
+
/* @__PURE__ */ jsxs(Dialog, {
|
|
21745
|
+
open: discardDialogOpen,
|
|
21746
|
+
onOpenChange: setDiscardDialogOpen,
|
|
21747
|
+
maxWidth: "sm",
|
|
21748
|
+
children: [
|
|
21749
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: status === "existing" ? "Discard changes?" : "Clear form?" }),
|
|
21750
|
+
/* @__PURE__ */ jsx(DialogContent, { children: /* @__PURE__ */ jsx(Typography, { children: status === "existing" ? "All unsaved changes will be lost. This cannot be undone." : "All entered values will be cleared. This cannot be undone." }) }),
|
|
21751
|
+
/* @__PURE__ */ jsxs(DialogActions, { children: [/* @__PURE__ */ jsx(Button, {
|
|
21752
|
+
variant: "text",
|
|
21753
|
+
onClick: () => setDiscardDialogOpen(false),
|
|
21754
|
+
children: "Cancel"
|
|
21755
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
21756
|
+
variant: "filled",
|
|
21757
|
+
color: "error",
|
|
21758
|
+
onClick: () => {
|
|
21759
|
+
setDiscardDialogOpen(false);
|
|
21760
|
+
formex.resetForm({ values: baseInitialValues });
|
|
21761
|
+
},
|
|
21762
|
+
children: status === "existing" ? "Discard" : "Clear"
|
|
21763
|
+
})] })
|
|
21764
|
+
]
|
|
21765
|
+
})
|
|
22680
21766
|
] }) });
|
|
22681
21767
|
useEffect(() => {
|
|
22682
21768
|
if (entityId && onIdChange) onIdChange(entityId);
|
|
@@ -22691,7 +21777,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22691
21777
|
formex,
|
|
22692
21778
|
disabled: actionsDisabled,
|
|
22693
21779
|
status,
|
|
22694
|
-
pluginActions:
|
|
21780
|
+
pluginActions: pluginActionsProp ?? [],
|
|
22695
21781
|
openEntityMode,
|
|
22696
21782
|
showDefaultActions,
|
|
22697
21783
|
navigateBack,
|
|
@@ -22701,7 +21787,11 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22701
21787
|
value: formex,
|
|
22702
21788
|
children: /* @__PURE__ */ jsxs("form", {
|
|
22703
21789
|
onSubmit: formex.handleSubmit,
|
|
22704
|
-
onReset: () =>
|
|
21790
|
+
onReset: (e) => {
|
|
21791
|
+
e.preventDefault();
|
|
21792
|
+
if (formex.dirty) setDiscardDialogOpen(true);
|
|
21793
|
+
else formex.resetForm({ values: baseInitialValues });
|
|
21794
|
+
},
|
|
22705
21795
|
noValidate: true,
|
|
22706
21796
|
className: cls("@container flex-1 flex flex-row w-full overflow-y-auto justify-center", className),
|
|
22707
21797
|
children: [/* @__PURE__ */ jsx("div", {
|
|
@@ -22710,15 +21800,29 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22710
21800
|
children: /* @__PURE__ */ jsxs("div", {
|
|
22711
21801
|
className: cls("flex flex-col w-full", openEntityMode === "dialog" ? "pt-4 pb-12 px-6 sm:px-8" : "pt-12 pb-16 px-4 sm:px-8 md:px-10"),
|
|
22712
21802
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
22713
|
-
className: "flex flex-row gap-4
|
|
22714
|
-
children: [manualApplyLocalChanges && hasLocalChanges && /* @__PURE__ */ jsx(LocalChangesMenu, {
|
|
22715
|
-
cacheKey:
|
|
21803
|
+
className: "flex flex-row gap-4 justify-end h-0 overflow-visible sticky top-4 z-10",
|
|
21804
|
+
children: [manualApplyLocalChanges && hasLocalChanges && localChangesCacheKey && /* @__PURE__ */ jsx(LocalChangesMenu, {
|
|
21805
|
+
cacheKey: localChangesCacheKey,
|
|
22716
21806
|
properties: collection.properties,
|
|
22717
21807
|
cachedData: localChangesData,
|
|
22718
21808
|
formex,
|
|
22719
|
-
onClearLocalChanges: () =>
|
|
22720
|
-
|
|
22721
|
-
|
|
21809
|
+
onClearLocalChanges: () => {
|
|
21810
|
+
setLocalChangesCleared(true);
|
|
21811
|
+
onClearLocalChanges?.();
|
|
21812
|
+
}
|
|
21813
|
+
}), isSavingAutoSave ? /* @__PURE__ */ jsx(Tooltip, {
|
|
21814
|
+
title: "Saving…",
|
|
21815
|
+
children: /* @__PURE__ */ jsx(Chip, {
|
|
21816
|
+
size: "small",
|
|
21817
|
+
className: "py-1",
|
|
21818
|
+
colorScheme: "blueDarker",
|
|
21819
|
+
children: /* @__PURE__ */ jsx(LoaderIcon, {
|
|
21820
|
+
size: iconSize.smallest,
|
|
21821
|
+
className: "animate-spin"
|
|
21822
|
+
})
|
|
21823
|
+
})
|
|
21824
|
+
}) : formex.dirty ? /* @__PURE__ */ jsx(Tooltip, {
|
|
21825
|
+
title: "Form has been modified",
|
|
22722
21826
|
children: /* @__PURE__ */ jsx(Chip, {
|
|
22723
21827
|
size: "small",
|
|
22724
21828
|
className: "py-1",
|
|
@@ -22726,7 +21830,7 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22726
21830
|
children: /* @__PURE__ */ jsx(PencilIcon, { size: iconSize.smallest })
|
|
22727
21831
|
})
|
|
22728
21832
|
}) : /* @__PURE__ */ jsx(Tooltip, {
|
|
22729
|
-
title:
|
|
21833
|
+
title: "Form is in sync",
|
|
22730
21834
|
children: /* @__PURE__ */ jsx(Chip, {
|
|
22731
21835
|
size: "small",
|
|
22732
21836
|
className: "py-1",
|
|
@@ -22739,40 +21843,401 @@ function EntityForm({ path, entityId: entityIdProp, collection, onValuesModified
|
|
|
22739
21843
|
})
|
|
22740
21844
|
});
|
|
22741
21845
|
}
|
|
22742
|
-
function
|
|
22743
|
-
|
|
22744
|
-
|
|
22745
|
-
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
|
|
22749
|
-
|
|
22750
|
-
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
22756
|
-
|
|
22757
|
-
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
21846
|
+
function useOnAutoSave(autoSave, formex, lastSavedValues, save) {
|
|
21847
|
+
useEffect(() => {
|
|
21848
|
+
if (!autoSave) return;
|
|
21849
|
+
if (formex.values && !deepEqual(formex.values, lastSavedValues.current)) save(formex.values);
|
|
21850
|
+
}, [autoSave, formex.values]);
|
|
21851
|
+
}
|
|
21852
|
+
//#endregion
|
|
21853
|
+
//#region src/form/EntityFormBinding.tsx
|
|
21854
|
+
/**
|
|
21855
|
+
* CMS-connected wrapper around EntityForm.
|
|
21856
|
+
*
|
|
21857
|
+
* Provides all backend concerns that the headless EntityForm delegates via callbacks:
|
|
21858
|
+
* - Save via the data layer (`saveEntityWithCallbacks`)
|
|
21859
|
+
* - Unique field validation via the data layer
|
|
21860
|
+
* - Entity caching (local changes backup/restore)
|
|
21861
|
+
* - Plugin slots (form.before, form.after, form.actions)
|
|
21862
|
+
* - Snackbar notifications on save success/error
|
|
21863
|
+
* - Analytics events
|
|
21864
|
+
* - Navigation via side entity controller
|
|
21865
|
+
*
|
|
21866
|
+
* Use this component inside the CMS (`<RebaseShell>`).
|
|
21867
|
+
* For headless usage outside the CMS, use `EntityForm` directly.
|
|
21868
|
+
*
|
|
21869
|
+
* @group Components
|
|
21870
|
+
*/
|
|
21871
|
+
function EntityFormBinding({ path, entityId: entityIdProp, collection, entity, initialStatus, onSaved, onValuesModified, onEntityChange, onStatusChange, openEntityMode = "full_screen", navigateBack: navigateBackProp, initialDirtyValues, ...restProps }) {
|
|
21872
|
+
const { t } = useTranslation();
|
|
21873
|
+
const sideEntityController = useSideEntityController();
|
|
21874
|
+
const collectionRegistryController = useCollectionRegistryController();
|
|
21875
|
+
const authController = useAuthController();
|
|
21876
|
+
const dataClient = useData();
|
|
21877
|
+
const snackbarController = useSnackbarController();
|
|
21878
|
+
const customizationController = useCustomizationController();
|
|
21879
|
+
const context = useCMSContext();
|
|
21880
|
+
const analyticsController = useAnalyticsController();
|
|
21881
|
+
const [status, setStatus] = useState(initialStatus);
|
|
21882
|
+
const [entityId, setEntityId] = useState(() => {
|
|
21883
|
+
if (status === "new" || status === "copy") return void 0;
|
|
21884
|
+
return entityIdProp;
|
|
21885
|
+
});
|
|
21886
|
+
const baseInitialValues = useMemo(() => getInitialEntityValues(authController, collection, path, status, entity, customizationController.propertyConfigs), [
|
|
21887
|
+
authController,
|
|
21888
|
+
collection,
|
|
21889
|
+
path,
|
|
21890
|
+
status,
|
|
21891
|
+
entity,
|
|
21892
|
+
customizationController.propertyConfigs
|
|
21893
|
+
]);
|
|
21894
|
+
const localChangesBackup = getLocalChangesBackup(collection);
|
|
21895
|
+
const autoApplyLocalChanges = localChangesBackup === "auto_apply";
|
|
21896
|
+
const manualApplyLocalChanges = localChangesBackup === "manual_apply";
|
|
21897
|
+
const localChangesDataRaw = useMemo(() => entityId ? getEntityFromCache(path + "/" + entityId) : getEntityFromCache(path + "#new"), [entityId, path]);
|
|
21898
|
+
const [localChangesCleared, setLocalChangesCleared] = useState(false);
|
|
21899
|
+
const computedInitialValues = useMemo(() => {
|
|
21900
|
+
const withLocalChanges = autoApplyLocalChanges && localChangesDataRaw ? mergeDeep(baseInitialValues, localChangesDataRaw) : baseInitialValues;
|
|
21901
|
+
return initialDirtyValues ? mergeDeep(withLocalChanges, initialDirtyValues) : withLocalChanges;
|
|
21902
|
+
}, [
|
|
21903
|
+
autoApplyLocalChanges,
|
|
21904
|
+
localChangesDataRaw,
|
|
21905
|
+
baseInitialValues,
|
|
21906
|
+
initialDirtyValues
|
|
21907
|
+
]);
|
|
21908
|
+
const localChangesData = useMemo(() => {
|
|
21909
|
+
if (!localChangesDataRaw) return void 0;
|
|
21910
|
+
const cleaned = removeEmptyContainers(getChanges(localChangesDataRaw, computedInitialValues));
|
|
21911
|
+
if (cleaned && typeof cleaned === "object" && Object.keys(cleaned).length === 0) return;
|
|
21912
|
+
return cleaned;
|
|
21913
|
+
}, [localChangesDataRaw, computedInitialValues]);
|
|
21914
|
+
const hasLocalChanges = !localChangesCleared && !!localChangesData && Object.keys(localChangesData).length > 0;
|
|
21915
|
+
const localChangesCacheKey = useMemo(() => {
|
|
21916
|
+
return status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
|
|
21917
|
+
}, [
|
|
21918
|
+
status,
|
|
21919
|
+
path,
|
|
21920
|
+
entityId
|
|
21921
|
+
]);
|
|
21922
|
+
const navigateBack = useCallback(() => {
|
|
21923
|
+
if (navigateBackProp) {
|
|
21924
|
+
navigateBackProp();
|
|
21925
|
+
return;
|
|
21926
|
+
}
|
|
21927
|
+
if (openEntityMode === "side_panel" || openEntityMode === "dialog") sideEntityController.close();
|
|
21928
|
+
else window.history.back();
|
|
21929
|
+
}, [
|
|
21930
|
+
navigateBackProp,
|
|
21931
|
+
openEntityMode,
|
|
21932
|
+
sideEntityController
|
|
21933
|
+
]);
|
|
21934
|
+
const clearDirtyCache = useCallback(() => {
|
|
21935
|
+
if (status === "new" || status === "copy") {
|
|
21936
|
+
removeEntityFromMemoryCache(path + "#new");
|
|
21937
|
+
removeEntityFromCache(path + "#new");
|
|
21938
|
+
} else {
|
|
21939
|
+
removeEntityFromMemoryCache(path + "/" + entityId);
|
|
21940
|
+
removeEntityFromCache(path + "/" + entityId);
|
|
21941
|
+
}
|
|
21942
|
+
}, [
|
|
21943
|
+
status,
|
|
21944
|
+
path,
|
|
21945
|
+
entityId
|
|
21946
|
+
]);
|
|
21947
|
+
const onReset = useCallback(() => {
|
|
21948
|
+
clearDirtyCache();
|
|
21949
|
+
}, [clearDirtyCache]);
|
|
21950
|
+
const onValuesChangeDeferred = useCallback((values, controller) => {
|
|
21951
|
+
const key = status === "new" || status === "copy" ? path + "#new" : path + "/" + entityId;
|
|
21952
|
+
if (controller.dirty) {
|
|
21953
|
+
const touchedValues = removeEmptyContainers(extractTouchedValues(values, controller.touched));
|
|
21954
|
+
if (touchedValues && Object.keys(touchedValues).length > 0) saveEntityToCache(key, touchedValues);
|
|
21955
|
+
else removeEntityFromCache(key);
|
|
21956
|
+
}
|
|
21957
|
+
}, [
|
|
21958
|
+
status,
|
|
21959
|
+
path,
|
|
21960
|
+
entityId
|
|
21961
|
+
]);
|
|
21962
|
+
const onSubmit = useCallback(async (values, formex) => {
|
|
21963
|
+
try {
|
|
21964
|
+
const savedEntity = await saveEntityWithCallbacks({
|
|
21965
|
+
path,
|
|
21966
|
+
entityId,
|
|
21967
|
+
values,
|
|
21968
|
+
previousValues: entity?.values,
|
|
21969
|
+
collection,
|
|
21970
|
+
status,
|
|
21971
|
+
data: dataClient,
|
|
21972
|
+
context,
|
|
21973
|
+
afterSave: (updatedEntity) => {
|
|
21974
|
+
clearDirtyCache();
|
|
21975
|
+
onValuesModified?.(false, updatedEntity.values);
|
|
21976
|
+
onEntityChange?.(updatedEntity);
|
|
21977
|
+
setStatus("existing");
|
|
21978
|
+
setEntityId(updatedEntity.id);
|
|
21979
|
+
onStatusChange?.("existing");
|
|
21980
|
+
if (onSaved) onSaved({
|
|
21981
|
+
entity: updatedEntity,
|
|
21982
|
+
status,
|
|
21983
|
+
path,
|
|
21984
|
+
entityId: updatedEntity.id,
|
|
21985
|
+
collection
|
|
21986
|
+
});
|
|
21987
|
+
},
|
|
21988
|
+
afterSaveError: (e) => {
|
|
21989
|
+
snackbarController.open({
|
|
21990
|
+
type: "error",
|
|
21991
|
+
title: t("error_saving_entity"),
|
|
21992
|
+
message: e?.message
|
|
21993
|
+
});
|
|
21994
|
+
console.error("Error saving entity", path, entityId, e);
|
|
21995
|
+
}
|
|
21996
|
+
});
|
|
21997
|
+
if (!collection.formAutoSave) snackbarController.open({
|
|
21998
|
+
type: "success",
|
|
21999
|
+
message: `${collection.singularName ?? collection.name}: ${t("saved_correctly")}`
|
|
22000
|
+
});
|
|
22001
|
+
const eventName = status === "new" ? "new_entity_saved" : status === "copy" ? "entity_copied" : status === "existing" ? "entity_edited" : "unmapped_event";
|
|
22002
|
+
analyticsController.onAnalyticsEvent?.(eventName, { path });
|
|
22003
|
+
return savedEntity;
|
|
22004
|
+
} catch (e) {
|
|
22005
|
+
console.error(e);
|
|
22006
|
+
throw e;
|
|
22007
|
+
}
|
|
22008
|
+
}, [
|
|
22009
|
+
path,
|
|
22010
|
+
entityId,
|
|
22011
|
+
entity,
|
|
22012
|
+
collection,
|
|
22013
|
+
status,
|
|
22014
|
+
dataClient,
|
|
22015
|
+
context,
|
|
22016
|
+
clearDirtyCache,
|
|
22017
|
+
onValuesModified,
|
|
22018
|
+
onEntityChange,
|
|
22019
|
+
onSaved,
|
|
22020
|
+
onStatusChange,
|
|
22021
|
+
snackbarController,
|
|
22022
|
+
analyticsController,
|
|
22023
|
+
t
|
|
22024
|
+
]);
|
|
22025
|
+
const uniqueFieldValidator = useCallback(async ({ name, value }) => {
|
|
22026
|
+
try {
|
|
22027
|
+
const { data } = await dataClient.collection(path).find({
|
|
22028
|
+
where: { [name]: ["==", value] },
|
|
22029
|
+
limit: 2
|
|
22030
|
+
});
|
|
22031
|
+
return (entityId ? data.filter((e) => e.id !== entityId) : data).length === 0;
|
|
22032
|
+
} catch (e) {
|
|
22033
|
+
console.error("Error checking unique field", e);
|
|
22034
|
+
return true;
|
|
22035
|
+
}
|
|
22036
|
+
}, [
|
|
22037
|
+
dataClient,
|
|
22038
|
+
path,
|
|
22039
|
+
entityId
|
|
22040
|
+
]);
|
|
22041
|
+
const parentCollectionSlugs = collectionRegistryController.getParentCollectionSlugs(path);
|
|
22042
|
+
const parentEntityIds = collectionRegistryController.getParentEntityIds(path);
|
|
22043
|
+
const formActionProps = useMemo(() => ({
|
|
22044
|
+
entityId,
|
|
22045
|
+
parentCollectionSlugs,
|
|
22046
|
+
parentEntityIds,
|
|
22047
|
+
path,
|
|
22048
|
+
status,
|
|
22049
|
+
collection,
|
|
22050
|
+
context,
|
|
22051
|
+
formContext: void 0,
|
|
22052
|
+
openEntityMode,
|
|
22053
|
+
disabled: false
|
|
22054
|
+
}), [
|
|
22055
|
+
entityId,
|
|
22056
|
+
parentCollectionSlugs,
|
|
22057
|
+
parentEntityIds,
|
|
22058
|
+
path,
|
|
22059
|
+
status,
|
|
22060
|
+
collection,
|
|
22061
|
+
context,
|
|
22062
|
+
openEntityMode
|
|
22063
|
+
]);
|
|
22064
|
+
const pluginFormActions = useSlot("form.actions", formActionProps);
|
|
22065
|
+
const pluginFormBefore = useSlot("form.before", formActionProps);
|
|
22066
|
+
const pluginFormAfter = useSlot("form.after", formActionProps);
|
|
22067
|
+
return /* @__PURE__ */ jsx(EntityForm, {
|
|
22068
|
+
...restProps,
|
|
22069
|
+
path,
|
|
22070
|
+
entityId: entityIdProp,
|
|
22071
|
+
collection,
|
|
22072
|
+
entity,
|
|
22073
|
+
initialStatus,
|
|
22074
|
+
initialDirtyValues,
|
|
22075
|
+
onSaved,
|
|
22076
|
+
onValuesModified,
|
|
22077
|
+
onEntityChange,
|
|
22078
|
+
onStatusChange,
|
|
22079
|
+
openEntityMode,
|
|
22080
|
+
navigateBack,
|
|
22081
|
+
onSubmit,
|
|
22082
|
+
onValuesChangeDeferred,
|
|
22083
|
+
onReset,
|
|
22084
|
+
uniqueFieldValidator,
|
|
22085
|
+
beforeFields: pluginFormBefore,
|
|
22086
|
+
afterFields: pluginFormAfter,
|
|
22087
|
+
pluginActions: pluginFormActions ?? [],
|
|
22088
|
+
computedInitialValues,
|
|
22089
|
+
hasLocalChanges,
|
|
22090
|
+
localChangesData,
|
|
22091
|
+
manualApplyLocalChanges,
|
|
22092
|
+
localChangesCacheKey,
|
|
22093
|
+
onClearLocalChanges: () => setLocalChangesCleared(true)
|
|
22094
|
+
});
|
|
22095
|
+
}
|
|
22096
|
+
//#endregion
|
|
22097
|
+
//#region src/util/propertyConfigMapper.ts
|
|
22098
|
+
/**
|
|
22099
|
+
* Map Rebase property types to headless-supported types.
|
|
22100
|
+
* `vector` and `binary` are not directly renderable, so we fall back to `string`.
|
|
22101
|
+
*/
|
|
22102
|
+
var TYPE_MAP = {
|
|
22103
|
+
string: "string",
|
|
22104
|
+
number: "number",
|
|
22105
|
+
boolean: "boolean",
|
|
22106
|
+
date: "date",
|
|
22107
|
+
geopoint: "geopoint",
|
|
22108
|
+
reference: "reference",
|
|
22109
|
+
relation: "relation",
|
|
22110
|
+
array: "array",
|
|
22111
|
+
map: "map",
|
|
22112
|
+
vector: "string",
|
|
22113
|
+
binary: "string"
|
|
22114
|
+
};
|
|
22115
|
+
/**
|
|
22116
|
+
* Convert a single Property to a CollectionPropertyConfig.
|
|
22117
|
+
*/
|
|
22118
|
+
function mapPropertyToConfig(property) {
|
|
22119
|
+
const base = {
|
|
22120
|
+
type: TYPE_MAP[property.type] ?? "string",
|
|
22121
|
+
name: property.name,
|
|
22122
|
+
description: property.description,
|
|
22123
|
+
columnWidth: property.ui?.columnWidth,
|
|
22124
|
+
hideFromCollection: property.ui?.hideFromCollection
|
|
22125
|
+
};
|
|
22126
|
+
if (property.type === "string") {
|
|
22127
|
+
if (property.enum) base.enum = mapEnumValues(property.enum);
|
|
22128
|
+
if (property.ui?.multiline) base.multiline = true;
|
|
22129
|
+
if (property.ui?.previewAsTag) base.previewAsTag = true;
|
|
22130
|
+
if (property.ui?.url) base.url = property.ui.url;
|
|
22131
|
+
if (property.ui?.markdown) base.markdown = true;
|
|
22132
|
+
if (property.storage) base.storage = true;
|
|
22133
|
+
if (property.email) base.email = true;
|
|
22134
|
+
}
|
|
22135
|
+
if (property.type === "number" && property.enum) base.enum = mapEnumValues(property.enum);
|
|
22136
|
+
if (property.type === "array" && property.of && !Array.isArray(property.of)) base.of = mapPropertyToConfig(property.of);
|
|
22137
|
+
if (property.type === "map" && property.properties) {
|
|
22138
|
+
base.properties = mapPropertiesToConfigs(property.properties);
|
|
22139
|
+
if (property.propertiesOrder) base.propertiesOrder = property.propertiesOrder;
|
|
22761
22140
|
}
|
|
22141
|
+
if (property.type === "date" && property.mode) base.mode = property.mode;
|
|
22142
|
+
if (property.ui?.Preview) {}
|
|
22143
|
+
return base;
|
|
22762
22144
|
}
|
|
22763
|
-
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
|
|
22145
|
+
/**
|
|
22146
|
+
* Convert a Properties record to a CollectionPropertyConfig record.
|
|
22147
|
+
*/
|
|
22148
|
+
function mapPropertiesToConfigs(properties) {
|
|
22149
|
+
const result = {};
|
|
22150
|
+
for (const [key, property] of Object.entries(properties)) result[key] = mapPropertyToConfig(property);
|
|
22151
|
+
return result;
|
|
22152
|
+
}
|
|
22153
|
+
/**
|
|
22154
|
+
* Convert Rebase EnumValues to CollectionEnumValueConfig format.
|
|
22155
|
+
* Handles both array form (EnumValueConfig[]) and record form.
|
|
22156
|
+
*/
|
|
22157
|
+
function mapEnumValues(enumValues) {
|
|
22158
|
+
if (!enumValues) return void 0;
|
|
22159
|
+
if (Array.isArray(enumValues)) {
|
|
22160
|
+
const result = {};
|
|
22161
|
+
for (const item of enumValues) result[String(item.id)] = normalizeEnumValueConfig(item);
|
|
22162
|
+
return result;
|
|
22768
22163
|
}
|
|
22769
|
-
|
|
22164
|
+
const result = {};
|
|
22165
|
+
for (const [key, value] of Object.entries(enumValues)) result[key] = typeof value === "string" ? value : normalizeEnumValueConfig(value);
|
|
22166
|
+
return result;
|
|
22770
22167
|
}
|
|
22771
|
-
function
|
|
22772
|
-
|
|
22773
|
-
|
|
22774
|
-
|
|
22775
|
-
|
|
22168
|
+
function normalizeEnumValueConfig(config) {
|
|
22169
|
+
return {
|
|
22170
|
+
label: config.label,
|
|
22171
|
+
color: typeof config.color === "string" ? config.color : void 0,
|
|
22172
|
+
disabled: config.disabled
|
|
22173
|
+
};
|
|
22174
|
+
}
|
|
22175
|
+
//#endregion
|
|
22176
|
+
//#region src/util/dataControllerAdapter.ts
|
|
22177
|
+
/**
|
|
22178
|
+
* Adapts an `EntityTableController<M>` (from @rebasepro/types) to a
|
|
22179
|
+
* `CollectionDataController<T>` (from @rebasepro/ui), bridging the
|
|
22180
|
+
* entity-aware data layer to the headless collection view.
|
|
22181
|
+
*/
|
|
22182
|
+
/**
|
|
22183
|
+
* Flatten Entity<M> objects to plain row objects.
|
|
22184
|
+
* The headless view works with flat `Record<string, unknown>` rows,
|
|
22185
|
+
* while the entity system uses `{ id, path, values: M }`.
|
|
22186
|
+
*
|
|
22187
|
+
* The flattened row includes `id` and `path` at the top level,
|
|
22188
|
+
* plus all values spread.
|
|
22189
|
+
*/
|
|
22190
|
+
function flattenEntities(entities) {
|
|
22191
|
+
return entities.map((entity) => ({
|
|
22192
|
+
id: entity.id,
|
|
22193
|
+
path: entity.path,
|
|
22194
|
+
...entity.values
|
|
22195
|
+
}));
|
|
22196
|
+
}
|
|
22197
|
+
/**
|
|
22198
|
+
* React hook that adapts an EntityTableController to a CollectionDataController.
|
|
22199
|
+
*
|
|
22200
|
+
* Usage:
|
|
22201
|
+
* ```tsx
|
|
22202
|
+
* const tableController = useDataTableController({ path, collection });
|
|
22203
|
+
* const dataController = useCollectionDataController(tableController);
|
|
22204
|
+
*
|
|
22205
|
+
* <CollectionView dataController={dataController} ... />
|
|
22206
|
+
* ```
|
|
22207
|
+
*/
|
|
22208
|
+
function useCollectionDataController(tableController) {
|
|
22209
|
+
const flatData = useMemo(() => flattenEntities(tableController.data), [tableController.data]);
|
|
22210
|
+
return useMemo(() => ({
|
|
22211
|
+
data: flatData,
|
|
22212
|
+
loading: tableController.dataLoading,
|
|
22213
|
+
noMoreToLoad: tableController.noMoreToLoad,
|
|
22214
|
+
error: tableController.dataLoadingError,
|
|
22215
|
+
filterValues: tableController.filterValues,
|
|
22216
|
+
setFilterValues: tableController.setFilterValues,
|
|
22217
|
+
sortBy: tableController.sortBy,
|
|
22218
|
+
setSortBy: tableController.setSortBy,
|
|
22219
|
+
searchString: tableController.searchString,
|
|
22220
|
+
setSearchString: tableController.setSearchString,
|
|
22221
|
+
clearFilter: tableController.clearFilter,
|
|
22222
|
+
initialScroll: tableController.initialScroll,
|
|
22223
|
+
onScroll: tableController.onScroll,
|
|
22224
|
+
paginationEnabled: tableController.paginationEnabled,
|
|
22225
|
+
pageSize: tableController.pageSize,
|
|
22226
|
+
itemCount: tableController.itemCount,
|
|
22227
|
+
setItemCount: tableController.setItemCount
|
|
22228
|
+
}), [flatData, tableController]);
|
|
22229
|
+
}
|
|
22230
|
+
/**
|
|
22231
|
+
* Non-hook version for cases where you already have the data
|
|
22232
|
+
* and just need to build a static controller.
|
|
22233
|
+
*/
|
|
22234
|
+
function createStaticDataController(data, options) {
|
|
22235
|
+
return {
|
|
22236
|
+
data,
|
|
22237
|
+
loading: options?.loading ?? false,
|
|
22238
|
+
noMoreToLoad: true,
|
|
22239
|
+
error: options?.error
|
|
22240
|
+
};
|
|
22776
22241
|
}
|
|
22777
22242
|
//#endregion
|
|
22778
22243
|
//#region src/data_import/import/ImportCollectionAction.tsx
|
|
@@ -23606,6 +23071,562 @@ var export_exports = /* @__PURE__ */ __exportAll({
|
|
|
23606
23071
|
*/
|
|
23607
23072
|
var useCollectionsConfigController = () => useContext(ConfigControllerContext);
|
|
23608
23073
|
//#endregion
|
|
23074
|
+
//#region src/collection_editor/serializable_utils.ts
|
|
23075
|
+
/**
|
|
23076
|
+
* Strip non-serializable fields from a `BaseUIConfig`.
|
|
23077
|
+
* Removes `Field` and `Preview` (ComponentRef).
|
|
23078
|
+
*/
|
|
23079
|
+
function toSerializableUIConfig(ui) {
|
|
23080
|
+
if (!ui) return void 0;
|
|
23081
|
+
const { Field, Preview, ...rest } = ui;
|
|
23082
|
+
const result = {};
|
|
23083
|
+
for (const [key, value] of Object.entries(rest)) if (key !== "Field" && key !== "Preview" && value !== void 0) result[key] = value;
|
|
23084
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
23085
|
+
}
|
|
23086
|
+
/**
|
|
23087
|
+
* Convert a `StorageConfig` to its serializable form.
|
|
23088
|
+
* Drops function variants of `storagePath`, `fileName`, and all function fields.
|
|
23089
|
+
*/
|
|
23090
|
+
function toSerializableStorageConfig(storage) {
|
|
23091
|
+
if (!storage) return void 0;
|
|
23092
|
+
const storagePath = typeof storage.storagePath === "string" ? storage.storagePath : void 0;
|
|
23093
|
+
if (!storagePath) return void 0;
|
|
23094
|
+
const result = { storagePath };
|
|
23095
|
+
if (storage.acceptedFiles) result.acceptedFiles = storage.acceptedFiles;
|
|
23096
|
+
if (storage.imageResize) result.imageResize = storage.imageResize;
|
|
23097
|
+
if (storage.metadata) result.metadata = storage.metadata;
|
|
23098
|
+
if (typeof storage.fileName === "string") result.fileName = storage.fileName;
|
|
23099
|
+
if (storage.includeBucketUrl !== void 0) result.includeBucketUrl = storage.includeBucketUrl;
|
|
23100
|
+
if (storage.storeUrl !== void 0) result.storeUrl = storage.storeUrl;
|
|
23101
|
+
if (storage.maxSize !== void 0) result.maxSize = storage.maxSize;
|
|
23102
|
+
if (storage.storageSource !== void 0) result.storageSource = storage.storageSource;
|
|
23103
|
+
return result;
|
|
23104
|
+
}
|
|
23105
|
+
/**
|
|
23106
|
+
* Convert a RegExp or string `matches` validation to a plain string.
|
|
23107
|
+
*/
|
|
23108
|
+
function serializeMatches(matches) {
|
|
23109
|
+
if (matches === void 0) return void 0;
|
|
23110
|
+
if (typeof matches === "string") return matches;
|
|
23111
|
+
return matches.source;
|
|
23112
|
+
}
|
|
23113
|
+
/**
|
|
23114
|
+
* Resolve a relation target to a string slug.
|
|
23115
|
+
* Functions are called to extract the target; EntityCollection objects
|
|
23116
|
+
* use their slug.
|
|
23117
|
+
*/
|
|
23118
|
+
function resolveRelationTarget(target) {
|
|
23119
|
+
if (!target) return void 0;
|
|
23120
|
+
if (typeof target === "string") return target;
|
|
23121
|
+
if (typeof target === "function") try {
|
|
23122
|
+
const resolved = target();
|
|
23123
|
+
if (typeof resolved === "string") return resolved;
|
|
23124
|
+
if (resolved && typeof resolved === "object" && "slug" in resolved) return resolved.slug;
|
|
23125
|
+
} catch {}
|
|
23126
|
+
}
|
|
23127
|
+
/**
|
|
23128
|
+
* Strip non-serializable base property fields.
|
|
23129
|
+
* Returns a clean object with only the common serializable fields from BaseProperty.
|
|
23130
|
+
* Validation is intentionally excluded — each property type handles its own.
|
|
23131
|
+
*/
|
|
23132
|
+
function toSerializableBaseFields(property) {
|
|
23133
|
+
const result = { name: property.name };
|
|
23134
|
+
if (property.description) result.description = property.description;
|
|
23135
|
+
if (property.propertyConfig) result.propertyConfig = property.propertyConfig;
|
|
23136
|
+
if (property.columnName) result.columnName = property.columnName;
|
|
23137
|
+
if (property.defaultValue !== void 0) result.defaultValue = property.defaultValue;
|
|
23138
|
+
if (property.conditions) result.conditions = property.conditions;
|
|
23139
|
+
if (property.metadata) result.metadata = property.metadata;
|
|
23140
|
+
const ui = toSerializableUIConfig(property.ui);
|
|
23141
|
+
if (ui) result.ui = ui;
|
|
23142
|
+
return result;
|
|
23143
|
+
}
|
|
23144
|
+
/**
|
|
23145
|
+
* Convert a single `Property` to its JSON-serializable form.
|
|
23146
|
+
* Strips functions, converts RegExp to string, Date to ISO string.
|
|
23147
|
+
*/
|
|
23148
|
+
function toSerializableProperty(property) {
|
|
23149
|
+
const base = toSerializableBaseFields(property);
|
|
23150
|
+
switch (property.type) {
|
|
23151
|
+
case "string": {
|
|
23152
|
+
const sp = property;
|
|
23153
|
+
const result = {
|
|
23154
|
+
...base,
|
|
23155
|
+
type: "string"
|
|
23156
|
+
};
|
|
23157
|
+
if (sp.columnType) result.columnType = sp.columnType;
|
|
23158
|
+
if (sp.isId !== void 0) result.isId = sp.isId;
|
|
23159
|
+
if (sp.enum) result.enum = sp.enum;
|
|
23160
|
+
if (sp.userSelect) result.userSelect = sp.userSelect;
|
|
23161
|
+
if (sp.email) result.email = sp.email;
|
|
23162
|
+
if (sp.validation) {
|
|
23163
|
+
const { matches, ...restValidation } = sp.validation;
|
|
23164
|
+
const serializedValidation = { ...restValidation };
|
|
23165
|
+
const matchStr = serializeMatches(matches);
|
|
23166
|
+
if (matchStr !== void 0) serializedValidation.matches = matchStr;
|
|
23167
|
+
result.validation = serializedValidation;
|
|
23168
|
+
}
|
|
23169
|
+
const storage = toSerializableStorageConfig(sp.storage);
|
|
23170
|
+
if (storage) result.storage = storage;
|
|
23171
|
+
return result;
|
|
23172
|
+
}
|
|
23173
|
+
case "number": {
|
|
23174
|
+
const np = property;
|
|
23175
|
+
const result = {
|
|
23176
|
+
...base,
|
|
23177
|
+
type: "number"
|
|
23178
|
+
};
|
|
23179
|
+
if (np.columnType) result.columnType = np.columnType;
|
|
23180
|
+
if (np.validation) result.validation = np.validation;
|
|
23181
|
+
if (np.isId !== void 0) result.isId = np.isId;
|
|
23182
|
+
if (np.enum) result.enum = np.enum;
|
|
23183
|
+
return result;
|
|
23184
|
+
}
|
|
23185
|
+
case "boolean": {
|
|
23186
|
+
const bp = property;
|
|
23187
|
+
const result = {
|
|
23188
|
+
...base,
|
|
23189
|
+
type: "boolean"
|
|
23190
|
+
};
|
|
23191
|
+
if (bp.validation) result.validation = bp.validation;
|
|
23192
|
+
return result;
|
|
23193
|
+
}
|
|
23194
|
+
case "date": {
|
|
23195
|
+
const dp = property;
|
|
23196
|
+
const result = {
|
|
23197
|
+
...base,
|
|
23198
|
+
type: "date"
|
|
23199
|
+
};
|
|
23200
|
+
if (dp.columnType) result.columnType = dp.columnType;
|
|
23201
|
+
if (dp.mode) result.mode = dp.mode;
|
|
23202
|
+
if (dp.timezone) result.timezone = dp.timezone;
|
|
23203
|
+
if (dp.autoValue) result.autoValue = dp.autoValue;
|
|
23204
|
+
if (dp.validation) {
|
|
23205
|
+
const serializedValidation = {};
|
|
23206
|
+
if (dp.validation.required !== void 0) serializedValidation.required = dp.validation.required;
|
|
23207
|
+
if (dp.validation.requiredMessage) serializedValidation.requiredMessage = dp.validation.requiredMessage;
|
|
23208
|
+
if (dp.validation.unique) serializedValidation.unique = dp.validation.unique;
|
|
23209
|
+
if (dp.validation.uniqueInArray) serializedValidation.uniqueInArray = dp.validation.uniqueInArray;
|
|
23210
|
+
if (dp.validation.min instanceof Date) serializedValidation.min = dp.validation.min.toISOString();
|
|
23211
|
+
else if (typeof dp.validation.min === "string") serializedValidation.min = dp.validation.min;
|
|
23212
|
+
if (dp.validation.max instanceof Date) serializedValidation.max = dp.validation.max.toISOString();
|
|
23213
|
+
else if (typeof dp.validation.max === "string") serializedValidation.max = dp.validation.max;
|
|
23214
|
+
result.validation = serializedValidation;
|
|
23215
|
+
}
|
|
23216
|
+
return result;
|
|
23217
|
+
}
|
|
23218
|
+
case "geopoint": {
|
|
23219
|
+
const gp = property;
|
|
23220
|
+
const result = {
|
|
23221
|
+
...base,
|
|
23222
|
+
type: "geopoint"
|
|
23223
|
+
};
|
|
23224
|
+
if (gp.validation) result.validation = gp.validation;
|
|
23225
|
+
return result;
|
|
23226
|
+
}
|
|
23227
|
+
case "reference": {
|
|
23228
|
+
const rp = property;
|
|
23229
|
+
const result = {
|
|
23230
|
+
...base,
|
|
23231
|
+
type: "reference"
|
|
23232
|
+
};
|
|
23233
|
+
if (rp.isId !== void 0) result.isId = rp.isId;
|
|
23234
|
+
if (rp.path) result.path = rp.path;
|
|
23235
|
+
if (rp.fixedFilter) result.fixedFilter = rp.fixedFilter;
|
|
23236
|
+
if (rp.includeId !== void 0) result.includeId = rp.includeId;
|
|
23237
|
+
if (rp.includeEntityLink !== void 0) result.includeEntityLink = rp.includeEntityLink;
|
|
23238
|
+
return result;
|
|
23239
|
+
}
|
|
23240
|
+
case "relation": {
|
|
23241
|
+
const rl = property;
|
|
23242
|
+
const result = {
|
|
23243
|
+
...base,
|
|
23244
|
+
type: "relation"
|
|
23245
|
+
};
|
|
23246
|
+
if (rl.isId !== void 0) result.isId = rl.isId;
|
|
23247
|
+
const target = resolveRelationTarget(rl.target);
|
|
23248
|
+
if (target) result.target = target;
|
|
23249
|
+
if (rl.cardinality) result.cardinality = rl.cardinality;
|
|
23250
|
+
if (rl.direction) result.direction = rl.direction;
|
|
23251
|
+
if (rl.inverseRelationName) result.inverseRelationName = rl.inverseRelationName;
|
|
23252
|
+
if (rl.localKey) result.localKey = rl.localKey;
|
|
23253
|
+
if (rl.foreignKeyOnTarget) result.foreignKeyOnTarget = rl.foreignKeyOnTarget;
|
|
23254
|
+
if (rl.through) result.through = rl.through;
|
|
23255
|
+
if (rl.joinPath) result.joinPath = rl.joinPath;
|
|
23256
|
+
if (rl.onUpdate) result.onUpdate = rl.onUpdate;
|
|
23257
|
+
if (rl.onDelete) result.onDelete = rl.onDelete;
|
|
23258
|
+
if (rl.relationName) result.relationName = rl.relationName;
|
|
23259
|
+
if (rl.fixedFilter) result.fixedFilter = rl.fixedFilter;
|
|
23260
|
+
if (rl.includeId !== void 0) result.includeId = rl.includeId;
|
|
23261
|
+
if (rl.includeEntityLink !== void 0) result.includeEntityLink = rl.includeEntityLink;
|
|
23262
|
+
if (rl.widget) result.widget = rl.widget;
|
|
23263
|
+
return result;
|
|
23264
|
+
}
|
|
23265
|
+
case "array": {
|
|
23266
|
+
const ap = property;
|
|
23267
|
+
const result = {
|
|
23268
|
+
...base,
|
|
23269
|
+
type: "array"
|
|
23270
|
+
};
|
|
23271
|
+
if (ap.columnType) result.columnType = ap.columnType;
|
|
23272
|
+
if (ap.validation) result.validation = ap.validation;
|
|
23273
|
+
if (ap.sortable !== void 0) result.sortable = ap.sortable;
|
|
23274
|
+
if (ap.canAddElements !== void 0) result.canAddElements = ap.canAddElements;
|
|
23275
|
+
if (ap.of) if (Array.isArray(ap.of)) result.of = ap.of.map(toSerializableProperty);
|
|
23276
|
+
else result.of = toSerializableProperty(ap.of);
|
|
23277
|
+
if (ap.oneOf) {
|
|
23278
|
+
result.oneOf = { properties: toSerializableProperties(ap.oneOf.properties) };
|
|
23279
|
+
if (ap.oneOf.propertiesOrder) result.oneOf.propertiesOrder = ap.oneOf.propertiesOrder;
|
|
23280
|
+
if (ap.oneOf.typeField) result.oneOf.typeField = ap.oneOf.typeField;
|
|
23281
|
+
if (ap.oneOf.valueField) result.oneOf.valueField = ap.oneOf.valueField;
|
|
23282
|
+
}
|
|
23283
|
+
return result;
|
|
23284
|
+
}
|
|
23285
|
+
case "map": {
|
|
23286
|
+
const mp = property;
|
|
23287
|
+
const result = {
|
|
23288
|
+
...base,
|
|
23289
|
+
type: "map"
|
|
23290
|
+
};
|
|
23291
|
+
if (mp.columnType) result.columnType = mp.columnType;
|
|
23292
|
+
if (mp.validation) result.validation = mp.validation;
|
|
23293
|
+
if (mp.propertiesOrder) result.propertiesOrder = mp.propertiesOrder;
|
|
23294
|
+
if (mp.previewProperties) result.previewProperties = mp.previewProperties;
|
|
23295
|
+
if (mp.keyValue) result.keyValue = mp.keyValue;
|
|
23296
|
+
if (mp.properties) result.properties = toSerializableProperties(mp.properties);
|
|
23297
|
+
return result;
|
|
23298
|
+
}
|
|
23299
|
+
case "vector": {
|
|
23300
|
+
const vp = property;
|
|
23301
|
+
const result = {
|
|
23302
|
+
...base,
|
|
23303
|
+
type: "vector",
|
|
23304
|
+
dimensions: vp.dimensions
|
|
23305
|
+
};
|
|
23306
|
+
if (vp.validation) result.validation = vp.validation;
|
|
23307
|
+
return result;
|
|
23308
|
+
}
|
|
23309
|
+
case "binary": {
|
|
23310
|
+
const bp = property;
|
|
23311
|
+
const result = {
|
|
23312
|
+
...base,
|
|
23313
|
+
type: "binary"
|
|
23314
|
+
};
|
|
23315
|
+
if (bp.validation) result.validation = bp.validation;
|
|
23316
|
+
return result;
|
|
23317
|
+
}
|
|
23318
|
+
default: return base;
|
|
23319
|
+
}
|
|
23320
|
+
}
|
|
23321
|
+
/**
|
|
23322
|
+
* Convert a `Properties` record to its serializable form.
|
|
23323
|
+
*/
|
|
23324
|
+
function toSerializableProperties(properties) {
|
|
23325
|
+
const result = {};
|
|
23326
|
+
for (const [key, property] of Object.entries(properties)) result[key] = toSerializableProperty(property);
|
|
23327
|
+
return result;
|
|
23328
|
+
}
|
|
23329
|
+
/**
|
|
23330
|
+
* Convert an `EntityCollection` to its JSON-serializable form.
|
|
23331
|
+
*
|
|
23332
|
+
* Strips all non-serializable fields (functions, React nodes, class instances)
|
|
23333
|
+
* while preserving the structural schema that the collection editor works with.
|
|
23334
|
+
*
|
|
23335
|
+
* The result is safe for `JSON.stringify()` and database storage.
|
|
23336
|
+
*/
|
|
23337
|
+
function toSerializableCollection(collection) {
|
|
23338
|
+
const result = {
|
|
23339
|
+
slug: collection.slug,
|
|
23340
|
+
name: collection.name,
|
|
23341
|
+
properties: toSerializableProperties(collection.properties)
|
|
23342
|
+
};
|
|
23343
|
+
if (collection.singularName) result.singularName = collection.singularName;
|
|
23344
|
+
if (collection.description) result.description = collection.description;
|
|
23345
|
+
if (collection.group) result.group = collection.group;
|
|
23346
|
+
if (collection.engine) result.engine = collection.engine;
|
|
23347
|
+
if (collection.dataSource) result.dataSource = collection.dataSource;
|
|
23348
|
+
if (collection.databaseId) result.databaseId = collection.databaseId;
|
|
23349
|
+
if (collection.titleProperty) result.titleProperty = collection.titleProperty;
|
|
23350
|
+
if (collection.ownerId) result.ownerId = collection.ownerId;
|
|
23351
|
+
if (collection.metadata) result.metadata = collection.metadata;
|
|
23352
|
+
if (collection.table) result.table = collection.table;
|
|
23353
|
+
if ("schema" in collection && collection.schema) result.schema = collection.schema;
|
|
23354
|
+
if (collection.orderProperty) result.orderProperty = collection.orderProperty;
|
|
23355
|
+
if (typeof collection.icon === "string") result.icon = collection.icon;
|
|
23356
|
+
if (collection.propertiesOrder) result.propertiesOrder = collection.propertiesOrder;
|
|
23357
|
+
if (collection.previewProperties) result.previewProperties = collection.previewProperties;
|
|
23358
|
+
if (collection.listProperties) result.listProperties = collection.listProperties;
|
|
23359
|
+
if (collection.enabledViews) result.enabledViews = collection.enabledViews;
|
|
23360
|
+
if (collection.disableDefaultActions) result.disableDefaultActions = collection.disableDefaultActions;
|
|
23361
|
+
if (collection.securityRules) result.securityRules = collection.securityRules;
|
|
23362
|
+
if (collection.openEntityMode) result.openEntityMode = collection.openEntityMode;
|
|
23363
|
+
if (collection.defaultEntityAction) result.defaultEntityAction = collection.defaultEntityAction;
|
|
23364
|
+
if (collection.defaultViewMode) result.defaultViewMode = collection.defaultViewMode;
|
|
23365
|
+
if (collection.defaultSize) result.defaultSize = collection.defaultSize;
|
|
23366
|
+
if (collection.localChangesBackup !== void 0) result.localChangesBackup = collection.localChangesBackup;
|
|
23367
|
+
if (collection.kanban) result.kanban = collection.kanban;
|
|
23368
|
+
if (collection.fixedFilter) result.fixedFilter = collection.fixedFilter;
|
|
23369
|
+
if (collection.defaultFilter) result.defaultFilter = collection.defaultFilter;
|
|
23370
|
+
if (collection.filterPresets) result.filterPresets = collection.filterPresets;
|
|
23371
|
+
if (collection.sort) result.sort = collection.sort;
|
|
23372
|
+
if (collection.pagination !== void 0) result.pagination = collection.pagination;
|
|
23373
|
+
if (collection.selectionEnabled !== void 0) result.selectionEnabled = collection.selectionEnabled;
|
|
23374
|
+
if (collection.inlineEditing !== void 0) result.inlineEditing = collection.inlineEditing;
|
|
23375
|
+
if (collection.hideFromNavigation !== void 0) result.hideFromNavigation = collection.hideFromNavigation;
|
|
23376
|
+
if (collection.hideIdFromForm !== void 0) result.hideIdFromForm = collection.hideIdFromForm;
|
|
23377
|
+
if (collection.hideIdFromCollection !== void 0) result.hideIdFromCollection = collection.hideIdFromCollection;
|
|
23378
|
+
if (collection.formAutoSave !== void 0) result.formAutoSave = collection.formAutoSave;
|
|
23379
|
+
if (collection.alwaysApplyDefaultValues !== void 0) result.alwaysApplyDefaultValues = collection.alwaysApplyDefaultValues;
|
|
23380
|
+
if (collection.includeJsonView !== void 0) result.includeJsonView = collection.includeJsonView;
|
|
23381
|
+
if (collection.history !== void 0) result.history = collection.history;
|
|
23382
|
+
if (collection.sideDialogWidth !== void 0) result.sideDialogWidth = collection.sideDialogWidth;
|
|
23383
|
+
if (typeof collection.exportable === "boolean") result.exportable = collection.exportable;
|
|
23384
|
+
if (typeof collection.auth === "boolean") result.auth = collection.auth;
|
|
23385
|
+
if (typeof collection.defaultSelectedView === "string") result.defaultSelectedView = collection.defaultSelectedView;
|
|
23386
|
+
return result;
|
|
23387
|
+
}
|
|
23388
|
+
/**
|
|
23389
|
+
* Convert a serializable property back to the original `Property` type.
|
|
23390
|
+
*
|
|
23391
|
+
* This is a mostly pass-through operation since serializable properties
|
|
23392
|
+
* are a subset of the original types. The main conversions are:
|
|
23393
|
+
* - `validation.matches` (string) stays as string (compatible with Property)
|
|
23394
|
+
* - `validation.min/max` on dates: ISO strings → Date objects
|
|
23395
|
+
*/
|
|
23396
|
+
function fromSerializableProperty(serialized) {
|
|
23397
|
+
switch (serialized.type) {
|
|
23398
|
+
case "date": {
|
|
23399
|
+
const { validation: dateValidation, ...dateRest } = serialized;
|
|
23400
|
+
const result = {
|
|
23401
|
+
...dateRest,
|
|
23402
|
+
type: "date"
|
|
23403
|
+
};
|
|
23404
|
+
if (dateValidation) {
|
|
23405
|
+
const convertedValidation = { ...dateValidation };
|
|
23406
|
+
if (typeof dateValidation.min === "string") convertedValidation.min = new Date(dateValidation.min);
|
|
23407
|
+
if (typeof dateValidation.max === "string") convertedValidation.max = new Date(dateValidation.max);
|
|
23408
|
+
result.validation = convertedValidation;
|
|
23409
|
+
}
|
|
23410
|
+
return result;
|
|
23411
|
+
}
|
|
23412
|
+
case "array": {
|
|
23413
|
+
const sp = serialized;
|
|
23414
|
+
const result = { ...sp };
|
|
23415
|
+
if (sp.of) if (Array.isArray(sp.of)) result.of = sp.of.map(fromSerializableProperty);
|
|
23416
|
+
else result.of = fromSerializableProperty(sp.of);
|
|
23417
|
+
if (sp.oneOf) result.oneOf = {
|
|
23418
|
+
...sp.oneOf,
|
|
23419
|
+
properties: fromSerializableProperties(sp.oneOf.properties)
|
|
23420
|
+
};
|
|
23421
|
+
return result;
|
|
23422
|
+
}
|
|
23423
|
+
case "map": {
|
|
23424
|
+
const sp = serialized;
|
|
23425
|
+
const result = { ...sp };
|
|
23426
|
+
if (sp.properties) result.properties = fromSerializableProperties(sp.properties);
|
|
23427
|
+
return result;
|
|
23428
|
+
}
|
|
23429
|
+
case "string": return { ...serialized };
|
|
23430
|
+
default: return serialized;
|
|
23431
|
+
}
|
|
23432
|
+
}
|
|
23433
|
+
/**
|
|
23434
|
+
* Convert a `SerializableProperties` record back to `Properties`.
|
|
23435
|
+
*/
|
|
23436
|
+
function fromSerializableProperties(serialized) {
|
|
23437
|
+
const result = {};
|
|
23438
|
+
for (const [key, property] of Object.entries(serialized)) result[key] = fromSerializableProperty(property);
|
|
23439
|
+
return result;
|
|
23440
|
+
}
|
|
23441
|
+
/**
|
|
23442
|
+
* Convert a `SerializableCollection` back to an `EntityCollection`.
|
|
23443
|
+
*
|
|
23444
|
+
* The result will NOT contain any of the non-serializable fields
|
|
23445
|
+
* (callbacks, entityActions, etc.) — those must be re-attached by the
|
|
23446
|
+
* consumer if needed.
|
|
23447
|
+
*/
|
|
23448
|
+
function fromSerializableCollection(serialized) {
|
|
23449
|
+
const { properties, ...rest } = serialized;
|
|
23450
|
+
return {
|
|
23451
|
+
...rest,
|
|
23452
|
+
properties: fromSerializableProperties(properties)
|
|
23453
|
+
};
|
|
23454
|
+
}
|
|
23455
|
+
//#endregion
|
|
23456
|
+
//#region src/collection_editor/useJsonCollectionsConfigController.tsx
|
|
23457
|
+
/**
|
|
23458
|
+
* A `CollectionsConfigController` implementation that persists collection
|
|
23459
|
+
* configurations as JSON via a pluggable `JsonCollectionStore` adapter.
|
|
23460
|
+
*
|
|
23461
|
+
* Use this when you want to store collection schemas in a database, API,
|
|
23462
|
+
* localStorage, or any other JSON-compatible backend — as opposed to the
|
|
23463
|
+
* default code-based persistence.
|
|
23464
|
+
*
|
|
23465
|
+
* @example
|
|
23466
|
+
* ```tsx
|
|
23467
|
+
* const store: JsonCollectionStore = {
|
|
23468
|
+
* load: async () => { const res = await fetch('/api/schemas'); return res.json(); },
|
|
23469
|
+
* save: async (slug, data) => { await fetch(`/api/schemas/${slug}`, { method: 'PUT', body: JSON.stringify(data) }); },
|
|
23470
|
+
* delete: async (slug) => { await fetch(`/api/schemas/${slug}`, { method: 'DELETE' }); },
|
|
23471
|
+
* };
|
|
23472
|
+
*
|
|
23473
|
+
* const configController = useJsonCollectionsConfigController({ store });
|
|
23474
|
+
* ```
|
|
23475
|
+
*/
|
|
23476
|
+
function useJsonCollectionsConfigController({ store, readOnly = false, autoLoad = true }) {
|
|
23477
|
+
const [collections, setCollections] = useState([]);
|
|
23478
|
+
const [loading, setLoading] = useState(autoLoad);
|
|
23479
|
+
const [navigationEntries, setNavigationEntries] = useState([]);
|
|
23480
|
+
const storeRef = useRef(store);
|
|
23481
|
+
storeRef.current = store;
|
|
23482
|
+
useEffect(() => {
|
|
23483
|
+
if (!autoLoad) return;
|
|
23484
|
+
let cancelled = false;
|
|
23485
|
+
(async () => {
|
|
23486
|
+
try {
|
|
23487
|
+
const [serialized, navEntries] = await Promise.all([storeRef.current.load(), storeRef.current.loadNavigationEntries?.() ?? Promise.resolve([])]);
|
|
23488
|
+
if (cancelled) return;
|
|
23489
|
+
setCollections(serialized.map(fromSerializableCollection));
|
|
23490
|
+
setNavigationEntries(navEntries);
|
|
23491
|
+
} catch (e) {
|
|
23492
|
+
console.error("useJsonCollectionsConfigController: failed to load collections", e);
|
|
23493
|
+
} finally {
|
|
23494
|
+
if (!cancelled) setLoading(false);
|
|
23495
|
+
}
|
|
23496
|
+
})();
|
|
23497
|
+
return () => {
|
|
23498
|
+
cancelled = true;
|
|
23499
|
+
};
|
|
23500
|
+
}, [autoLoad]);
|
|
23501
|
+
/**
|
|
23502
|
+
* Update local state and persist a collection to the store.
|
|
23503
|
+
*/
|
|
23504
|
+
const persistCollection = useCallback(async (collection) => {
|
|
23505
|
+
const serializable = toSerializableCollection(collection);
|
|
23506
|
+
await storeRef.current.save(collection.slug, serializable);
|
|
23507
|
+
}, []);
|
|
23508
|
+
/**
|
|
23509
|
+
* Find collection by id (slug).
|
|
23510
|
+
*/
|
|
23511
|
+
const getCollection = useCallback((id) => {
|
|
23512
|
+
const found = collections.find((c) => c.slug === id);
|
|
23513
|
+
if (found) return found;
|
|
23514
|
+
throw new Error(`Collection "${id}" not found`);
|
|
23515
|
+
}, [collections]);
|
|
23516
|
+
const saveCollection = useCallback(async ({ id, collectionData }) => {
|
|
23517
|
+
const collection = collectionData;
|
|
23518
|
+
await persistCollection(collection);
|
|
23519
|
+
setCollections((prev) => {
|
|
23520
|
+
const idx = prev.findIndex((c) => c.slug === id);
|
|
23521
|
+
if (idx >= 0) {
|
|
23522
|
+
const next = [...prev];
|
|
23523
|
+
next[idx] = collection;
|
|
23524
|
+
return next;
|
|
23525
|
+
}
|
|
23526
|
+
return [...prev, collection];
|
|
23527
|
+
});
|
|
23528
|
+
}, [persistCollection]);
|
|
23529
|
+
const updateCollection = useCallback(async ({ id, collectionData, previousId }) => {
|
|
23530
|
+
setCollections((prev) => {
|
|
23531
|
+
const lookupId = previousId ?? id;
|
|
23532
|
+
const idx = prev.findIndex((c) => c.slug === lookupId);
|
|
23533
|
+
if (idx < 0) return prev;
|
|
23534
|
+
const merged = {
|
|
23535
|
+
...prev[idx],
|
|
23536
|
+
...collectionData
|
|
23537
|
+
};
|
|
23538
|
+
const next = [...prev];
|
|
23539
|
+
next[idx] = merged;
|
|
23540
|
+
persistCollection(merged).catch((e) => console.error("useJsonCollectionsConfigController: failed to update collection", e));
|
|
23541
|
+
if (previousId && previousId !== id) storeRef.current.delete(previousId).catch((e) => console.error("useJsonCollectionsConfigController: failed to delete old slug", e));
|
|
23542
|
+
return next;
|
|
23543
|
+
});
|
|
23544
|
+
}, [persistCollection]);
|
|
23545
|
+
const deleteCollection = useCallback(async ({ id }) => {
|
|
23546
|
+
await storeRef.current.delete(id);
|
|
23547
|
+
setCollections((prev) => prev.filter((c) => c.slug !== id));
|
|
23548
|
+
}, []);
|
|
23549
|
+
const saveProperty = useCallback(async ({ path, propertyKey, property, newPropertiesOrder }) => {
|
|
23550
|
+
setCollections((prev) => {
|
|
23551
|
+
const idx = prev.findIndex((c) => c.slug === path);
|
|
23552
|
+
if (idx < 0) return prev;
|
|
23553
|
+
const collection = { ...prev[idx] };
|
|
23554
|
+
collection.properties = {
|
|
23555
|
+
...collection.properties,
|
|
23556
|
+
[propertyKey]: property
|
|
23557
|
+
};
|
|
23558
|
+
if (newPropertiesOrder) collection.propertiesOrder = newPropertiesOrder;
|
|
23559
|
+
const next = [...prev];
|
|
23560
|
+
next[idx] = collection;
|
|
23561
|
+
persistCollection(next[idx]).catch((e) => console.error("useJsonCollectionsConfigController: failed to save property", e));
|
|
23562
|
+
return next;
|
|
23563
|
+
});
|
|
23564
|
+
}, [persistCollection]);
|
|
23565
|
+
const deleteProperty = useCallback(async ({ path, propertyKey, newPropertiesOrder }) => {
|
|
23566
|
+
setCollections((prev) => {
|
|
23567
|
+
const idx = prev.findIndex((c) => c.slug === path);
|
|
23568
|
+
if (idx < 0) return prev;
|
|
23569
|
+
const collection = { ...prev[idx] };
|
|
23570
|
+
const { [propertyKey]: _removed, ...remaining } = collection.properties;
|
|
23571
|
+
collection.properties = remaining;
|
|
23572
|
+
if (newPropertiesOrder) collection.propertiesOrder = newPropertiesOrder;
|
|
23573
|
+
const next = [...prev];
|
|
23574
|
+
next[idx] = collection;
|
|
23575
|
+
persistCollection(next[idx]).catch((e) => console.error("useJsonCollectionsConfigController: failed to delete property", e));
|
|
23576
|
+
return next;
|
|
23577
|
+
});
|
|
23578
|
+
}, [persistCollection]);
|
|
23579
|
+
const updatePropertiesOrder = useCallback(async ({ collection, newPropertiesOrder }) => {
|
|
23580
|
+
const updated = {
|
|
23581
|
+
...collection,
|
|
23582
|
+
propertiesOrder: newPropertiesOrder
|
|
23583
|
+
};
|
|
23584
|
+
setCollections((prev) => {
|
|
23585
|
+
const idx = prev.findIndex((c) => c.slug === collection.slug);
|
|
23586
|
+
if (idx < 0) return prev;
|
|
23587
|
+
const next = [...prev];
|
|
23588
|
+
next[idx] = updated;
|
|
23589
|
+
return next;
|
|
23590
|
+
});
|
|
23591
|
+
await persistCollection(updated);
|
|
23592
|
+
}, [persistCollection]);
|
|
23593
|
+
const updateKanbanColumnsOrder = useCallback(async ({ collection, kanbanColumnProperty, newColumnsOrder }) => {}, []);
|
|
23594
|
+
const saveNavigationEntriesHandler = useCallback(async (entries) => {
|
|
23595
|
+
setNavigationEntries(entries);
|
|
23596
|
+
if (storeRef.current.saveNavigationEntries) await storeRef.current.saveNavigationEntries(entries);
|
|
23597
|
+
}, []);
|
|
23598
|
+
return useMemo(() => ({
|
|
23599
|
+
loading,
|
|
23600
|
+
readOnly,
|
|
23601
|
+
readOnlyReason: readOnly ? "Configuration is read-only." : void 0,
|
|
23602
|
+
collections,
|
|
23603
|
+
getCollection,
|
|
23604
|
+
saveCollection,
|
|
23605
|
+
updateCollection,
|
|
23606
|
+
deleteCollection,
|
|
23607
|
+
saveProperty,
|
|
23608
|
+
deleteProperty,
|
|
23609
|
+
updatePropertiesOrder,
|
|
23610
|
+
updateKanbanColumnsOrder,
|
|
23611
|
+
navigationEntries,
|
|
23612
|
+
saveNavigationEntries: saveNavigationEntriesHandler
|
|
23613
|
+
}), [
|
|
23614
|
+
loading,
|
|
23615
|
+
readOnly,
|
|
23616
|
+
collections,
|
|
23617
|
+
getCollection,
|
|
23618
|
+
saveCollection,
|
|
23619
|
+
updateCollection,
|
|
23620
|
+
deleteCollection,
|
|
23621
|
+
saveProperty,
|
|
23622
|
+
deleteProperty,
|
|
23623
|
+
updatePropertiesOrder,
|
|
23624
|
+
updateKanbanColumnsOrder,
|
|
23625
|
+
navigationEntries,
|
|
23626
|
+
saveNavigationEntriesHandler
|
|
23627
|
+
]);
|
|
23628
|
+
}
|
|
23629
|
+
//#endregion
|
|
23609
23630
|
//#region src/collection_editor/validateCollectionJson.ts
|
|
23610
23631
|
/**
|
|
23611
23632
|
* Valid dataType values for properties
|
|
@@ -24006,6 +24027,6 @@ function getFullIdPath(propertyKey, propertyNamespace) {
|
|
|
24006
24027
|
return idToPropertiesPath(propertyNamespace ? `${propertyNamespace}.${propertyKey}` : propertyKey);
|
|
24007
24028
|
}
|
|
24008
24029
|
//#endregion
|
|
24009
|
-
export {
|
|
24030
|
+
export { RelationPreview as $, SelectableTable as $n, BlockFieldBinding as $t, useCollectionDataController as A, removeInitialAndTrailingSlashes$1 as An, renderSkeletonIcon as Ar, useImportConfig as At, ArrayOfMapsPreview as B, useUrlController as Bn, getFieldId as Bt, downloadEntitiesExport as C, editEntityAction as Cn, ArrayPropertyPreview as Cr, useCollectionEditorController as Ct, ImportDataPreview as D, getCollectionBySlugWithin as Dn, StorageThumbnailInternal as Dr, flattenEntry as Dt, ImportCollectionAction as E, addInitialSlash as En, StorageThumbnail as Er, convertDataToEntity as Et, extractTouchedValues as F, useEntityHistory as Fn, ImagePreview as Fr, PropertyConfigBadge as Ft, DatePreview as G, useSideDialogsController as Gn, SelectFieldBinding as Gt, UserPreview as H, useNavigationStateController as Hn, TextFieldBinding as Ht, getChanges as I, resolveNavigationFrom as In, EmptyValue as Ir, DEFAULT_FIELD_CONFIGS as It, ArrayOneOfPreview as J, useBuildSideEntityController as Jn, MultiSelectFieldBinding as Jt, KeyValuePreview as K, buildSidePanelsFromUrl as Kn, RepeatFieldBinding as Kt, getInitialEntityValues as L, useResolvedNavigationFrom as Ln, getDefaultFieldConfig as Lt, mapPropertyToConfig as M, removeTrailingSlash as Mn, renderSkeletonText as Mr, ImportFileUpload as Mt, EntityFormBinding as N, resolveCollectionPathIds$1 as Nn, UrlComponentPreview as Nr, convertFileToJson as Nt, PropertySelectEntry as O, getCollectionPathsCombinations as On, SkeletonPropertyComponent as Or, processValueMapping as Ot, EntityForm as P, useBuildCollectionRegistryController as Pn, sanitizeUrl as Pr, unflattenObject as Pt, ArrayOfStorageComponentsPreview as Q, EntityCollectionTable as Qn, DateTimeFieldBinding as Qt, removeEmptyContainers as R, useCMSContext as Rn, getDefaultFieldId as Rt, downloadDataAsCsv as S, deleteEntityAction as Sn, isRelationProperty as Sr, EntityCollectionViewActions as St, getEntityJsonExportableData as T, CreationResultDialog as Tn, EnumValuesChip as Tr, DataNewPropertiesMapping as Tt, NumberPropertyPreview as U, CollectionRegistryContext as Un, SwitchFieldBinding as Ut, PropertyPreview as V, NavigationStateContext as Vn, VectorFieldBinding as Vt, BooleanPreview as W, useCollectionRegistryController as Wn, StorageUploadFieldBinding as Wt, ArrayPropertyEnumPreview as X, useSelectionController as Xn, MapFieldBinding as Xt, ArrayOfStringsPreview as Y, mergeEntityActions as Yn, MarkdownEditorFieldBinding as Yt, ArrayEnumPreview as Z, VirtualTableInput$1 as Zn, KeyValueFieldBinding as Zt, toSerializableProperty as _, useBuildNavigationStateController as _n, getIconForWidget as _r, SideEntityProvider as _t, namespaceToPropertiesPath as a, FormLayout as an, useEntitySelectionDialog as ar, RebaseLayout as at, BasicExportAction as b, useResolvedCollections as bn, getResolvedPropertyInPath as br, EntityCollectionCardView as bt, DEFAULT_COLLECTION_GENERATION_ENDPOINT as c, PropertyIdCopyTooltip as cn, EntityView as cr, RebaseAuthGate as ct, useJsonCollectionsConfigController as d, SearchIconsView as dn, EntityPreview as dr, Drawer as dt, ArrayOfReferencesFieldBinding as en, EntityCollectionRowActions as er, CollectionPanel as et, fromSerializableCollection as f, FieldCaption as fn, getEntityPreviewKeys as fr, DefaultDrawer as ft, toSerializableProperties as g, useBuildUrlController as gn, getIconForProperty as gr, AdminModeSyncer as gt, toSerializableCollection as h, useBreadcrumbsController as hn, getDefaultPropertiesOrder as hr, useApp as ht, namespaceToPropertiesOrderPath as i, ReadOnlyFieldBinding as in, useSideEntityController as ir, RebaseRoute as it, mapPropertiesToConfigs as j, removeInitialSlash as jn, renderSkeletonImageThumbnail as jr, ImportSaveInProgress as jt, createStaticDataController as k, getLastSegment$1 as kn, renderSkeletonCaptionText as kr, getInferenceType as kt, buildCollectionGenerationCallback as l, LabelWithIcon as ln, ArrayOfReferencesPreview as lr, RebaseCMS as lt, fromSerializableProperty as m, SideDialogs as mn, getBracketNotation as mr, AppBar as mt, getFullIdPath as n, ArrayCustomShapedFieldBinding as nn, SelectableTableContext as nr, RebaseRouteDefs as nt, MissingReferenceWidget as o, FormEntry as on, resolveEntityAction as or, RebaseNavigation as ot, fromSerializableProperties as p, EntitySelectionTable as pn, getEntityTitlePropertyKey as pr, DrawerFooterActions as pt, MapPropertyPreview as q, getEntityViewWidth as qn, ReferenceFieldBinding as qt, idToPropertiesPath as r, PropertyFieldBinding as rn, SideEntityControllerContext as rr, CustomViewRoute as rt, CollectionGenerationApiError as s, LabelWithIconAndTooltip as sn, resolveEntityView as sr, useLocalCollectionsConfigController as st, getFullId as t, ArrayContainer as tn, useClearRestoreValue as tr, RebaseShell as tt, validateCollectionJson as u, FieldHelperText as un, ReferencePreview as ur, Scaffold as ut, useCollectionsConfigController as v, useTopLevelNavigation as vn, getPropertiesWithPropertiesOrder as vr, ReferenceWidget as vt, getEntityCSVExportableData as w, resetPasswordAction as wn, StringPropertyPreview as wr, ImportNewPropertyFieldPreview as wt, downloadBlob as x, copyEntityAction as xn, isReferenceProperty as xr, EntityCard as xt, ExportCollectionAction as y, useResolvedViews as yn, getPropertyInPath as yr, EntityCollectionView as yt, zodToFormErrors as z, UrlContext as zn, getFieldConfig as zt };
|
|
24010
24031
|
|
|
24011
|
-
//# sourceMappingURL=src-
|
|
24032
|
+
//# sourceMappingURL=src-cxV1ODRG.js.map
|