@rebasepro/admin 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/README.md +7 -3
  2. package/dist/{CollectionEditorDialog-jA-PLh6N.js → CollectionEditorDialog-iGgS4rLX.js} +233 -152
  3. package/dist/CollectionEditorDialog-iGgS4rLX.js.map +1 -0
  4. package/dist/{PropertyEditView-DEUddmg_.js → PropertyEditView-C9yhLyja.js} +129 -15
  5. package/dist/PropertyEditView-C9yhLyja.js.map +1 -0
  6. package/dist/{CollectionsStudioView-UKouBPOm.js → RouterCollectionsStudioView-Dqx27GD8.js} +61 -39
  7. package/dist/RouterCollectionsStudioView-Dqx27GD8.js.map +1 -0
  8. package/dist/collection_editor/extensibility_types.d.ts +135 -0
  9. package/dist/collection_editor/index.d.ts +6 -0
  10. package/dist/collection_editor/serializable_types.d.ts +374 -0
  11. package/dist/collection_editor/serializable_utils.d.ts +50 -0
  12. package/dist/collection_editor/ui/collection_editor/CollectionEditorDialog.d.ts +2 -1
  13. package/dist/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +12 -1
  14. package/dist/collection_editor/ui/collection_editor/CollectionStudioView.d.ts +13 -1
  15. package/dist/collection_editor/ui/collection_editor/CollectionsStudioView.d.ts +24 -2
  16. package/dist/collection_editor/ui/collection_editor/DisplaySettingsForm.d.ts +2 -1
  17. package/dist/collection_editor/ui/collection_editor/GeneralSettingsForm.d.ts +4 -1
  18. package/dist/collection_editor/ui/collection_editor/PropertyEditView.d.ts +12 -1
  19. package/dist/collection_editor/ui/collection_editor/RouterCollectionStudioView.d.ts +15 -0
  20. package/dist/collection_editor/ui/collection_editor/RouterCollectionsStudioView.d.ts +14 -0
  21. package/dist/collection_editor/useJsonCollectionsConfigController.d.ts +37 -0
  22. package/dist/collection_editor/useSafeSnackbarController.d.ts +2 -0
  23. package/dist/collection_editor_ui.d.ts +4 -0
  24. package/dist/collection_editor_ui.js +33 -4
  25. package/dist/collection_editor_ui.js.map +1 -0
  26. package/dist/components/EntityCollectionView/EntityBoardCard.d.ts +3 -3
  27. package/dist/components/EntityCollectionView/hooks/useKanbanDragAndDrop.d.ts +3 -3
  28. package/dist/components/EntityEditView.d.ts +3 -2
  29. package/dist/components/RebaseCMS.d.ts +1 -1
  30. package/dist/editor/editor.d.ts +10 -4
  31. package/dist/editor.js +6 -4
  32. package/dist/editor.js.map +1 -1
  33. package/dist/form/EntityForm.d.ts +10 -9
  34. package/dist/form/EntityFormBinding.d.ts +27 -0
  35. package/dist/form/field_bindings/MarkdownEditorFieldBinding.d.ts +2 -2
  36. package/dist/form/form_utils.d.ts +8 -0
  37. package/dist/form/index.d.ts +5 -1
  38. package/dist/{history-BL49TIDb.js → history-ChHgyyvM.js} +2 -2
  39. package/dist/{history-BL49TIDb.js.map → history-ChHgyyvM.js.map} +1 -1
  40. package/dist/hooks/navigation/contexts/CollectionRegistryContext.d.ts +1 -1
  41. package/dist/hooks/navigation/useAsyncResolver.d.ts +26 -0
  42. package/dist/hooks/navigation/useBuildCollectionRegistryController.d.ts +2 -0
  43. package/dist/hooks/navigation/useTopLevelNavigation.d.ts +1 -1
  44. package/dist/hooks/navigation/utils.d.ts +1 -1
  45. package/dist/index.d.ts +3 -1
  46. package/dist/index.js +2 -2
  47. package/dist/routes/CustomViewRoute.d.ts +2 -1
  48. package/dist/{src-BJ8fkM8y.js → src-DOX2nOyh.js} +1634 -1330
  49. package/dist/src-DOX2nOyh.js.map +1 -0
  50. package/dist/types/components/EntityFormActionsProps.d.ts +1 -1
  51. package/dist/types/components/EntityFormProps.d.ts +59 -0
  52. package/dist/types/fields.d.ts +6 -1
  53. package/dist/util/dataControllerAdapter.d.ts +30 -0
  54. package/dist/util/navigation_utils.d.ts +1 -1
  55. package/dist/util/propertyConfigMapper.d.ts +17 -0
  56. package/package.json +18 -18
  57. package/src/collection_editor/extensibility_types.ts +168 -0
  58. package/src/collection_editor/index.ts +46 -0
  59. package/src/collection_editor/serializable_types.ts +487 -0
  60. package/src/collection_editor/serializable_utils.ts +588 -0
  61. package/src/collection_editor/ui/AddKanbanColumnAction.tsx +2 -0
  62. package/src/collection_editor/ui/collection_editor/AICollectionGeneratorPopover.tsx +4 -4
  63. package/src/collection_editor/ui/collection_editor/CollectionEditorDialog.tsx +134 -70
  64. package/src/collection_editor/ui/collection_editor/CollectionPropertiesEditorForm.tsx +95 -78
  65. package/src/collection_editor/ui/collection_editor/CollectionStudioView.tsx +23 -20
  66. package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +79 -27
  67. package/src/collection_editor/ui/collection_editor/DisplaySettingsForm.tsx +9 -7
  68. package/src/collection_editor/ui/collection_editor/GeneralSettingsForm.tsx +22 -5
  69. package/src/collection_editor/ui/collection_editor/GetCodeDialog.tsx +3 -3
  70. package/src/collection_editor/ui/collection_editor/PropertyEditView.tsx +155 -10
  71. package/src/collection_editor/ui/collection_editor/PropertyFieldPreview.tsx +6 -6
  72. package/src/collection_editor/ui/collection_editor/PropertyTree.tsx +1 -0
  73. package/src/collection_editor/ui/collection_editor/RouterCollectionStudioView.tsx +44 -0
  74. package/src/collection_editor/ui/collection_editor/RouterCollectionsStudioView.tsx +41 -0
  75. package/src/collection_editor/ui/collection_editor/properties/EnumPropertyField.tsx +4 -3
  76. package/src/collection_editor/useJsonCollectionsConfigController.tsx +286 -0
  77. package/src/collection_editor/useSafeSnackbarController.ts +9 -0
  78. package/src/collection_editor_ui.ts +6 -0
  79. package/src/components/ArrayContainer.tsx +5 -5
  80. package/src/components/DefaultDrawer.tsx +13 -3
  81. package/src/components/DrawerNavigationGroup.tsx +2 -2
  82. package/src/components/DrawerNavigationItem.tsx +4 -4
  83. package/src/components/EntityCollectionView/EntityBoardCard.tsx +6 -6
  84. package/src/components/EntityCollectionView/EntityCollectionBoardView.tsx +14 -11
  85. package/src/components/EntityCollectionView/EntityCollectionCardView.tsx +40 -190
  86. package/src/components/EntityCollectionView/EntityCollectionListView.tsx +54 -183
  87. package/src/components/EntityCollectionView/hooks/useKanbanDragAndDrop.ts +6 -6
  88. package/src/components/EntityEditView.tsx +5 -4
  89. package/src/components/EntityEditViewFormActions.tsx +32 -26
  90. package/src/components/HomePage/ContentHomePage.tsx +13 -4
  91. package/src/components/HomePage/NavigationCard.tsx +8 -14
  92. package/src/components/HomePage/NavigationGroup.tsx +1 -1
  93. package/src/components/HomePage/SmallNavigationCard.tsx +1 -5
  94. package/src/components/RebaseCMS.tsx +3 -2
  95. package/src/components/RebaseNavigation.tsx +71 -7
  96. package/src/components/SelectableTable/SelectableTable.tsx +2 -2
  97. package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +28 -21
  98. package/src/components/SideDialogs.tsx +0 -7
  99. package/src/components/common/default_entity_actions.tsx +18 -10
  100. package/src/contexts/BreacrumbsContext.tsx +15 -6
  101. package/src/editor/editor.tsx +14 -8
  102. package/src/editor/selectors/ai-selector.tsx +0 -1
  103. package/src/form/EntityForm.tsx +191 -428
  104. package/src/form/EntityFormActions.tsx +33 -47
  105. package/src/form/EntityFormBinding.tsx +323 -0
  106. package/src/form/components/FormLayout.tsx +1 -1
  107. package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +4 -4
  108. package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +7 -2
  109. package/src/form/field_bindings/StorageUploadFieldBinding.tsx +1 -1
  110. package/src/form/form_utils.ts +163 -0
  111. package/src/form/index.tsx +14 -5
  112. package/src/hooks/navigation/contexts/CollectionRegistryContext.tsx +2 -2
  113. package/src/hooks/navigation/contexts/NavigationStateContext.tsx +2 -2
  114. package/src/hooks/navigation/contexts/UrlContext.tsx +1 -2
  115. package/src/hooks/navigation/useAsyncResolver.ts +100 -0
  116. package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +15 -8
  117. package/src/hooks/navigation/useBuildNavigationStateController.tsx +12 -8
  118. package/src/hooks/navigation/useNavigationResolution.ts +33 -0
  119. package/src/hooks/navigation/useResolvedCollections.ts +29 -71
  120. package/src/hooks/navigation/useResolvedViews.tsx +40 -70
  121. package/src/hooks/navigation/useTopLevelNavigation.ts +20 -16
  122. package/src/hooks/navigation/utils.ts +7 -4
  123. package/src/index.ts +7 -2
  124. package/src/routes/CustomViewRoute.tsx +14 -6
  125. package/src/types/components/EntityFormActionsProps.tsx +1 -1
  126. package/src/types/components/EntityFormProps.tsx +68 -0
  127. package/src/types/fields.tsx +7 -1
  128. package/src/util/dataControllerAdapter.ts +93 -0
  129. package/src/util/navigation_utils.ts +1 -1
  130. package/src/util/propertyConfigMapper.ts +134 -0
  131. package/LICENSE +0 -21
  132. package/dist/CollectionEditorDialog-jA-PLh6N.js.map +0 -1
  133. package/dist/CollectionsStudioView-UKouBPOm.js.map +0 -1
  134. package/dist/PropertyEditView-DEUddmg_.js.map +0 -1
  135. package/dist/components/EntityCollectionView/Board.d.ts +0 -3
  136. package/dist/components/EntityCollectionView/BoardColumn.d.ts +0 -42
  137. package/dist/components/EntityCollectionView/BoardColumnTitle.d.ts +0 -9
  138. package/dist/components/EntityCollectionView/BoardSortableList.d.ts +0 -14
  139. package/dist/components/EntityCollectionView/board_types.d.ts +0 -105
  140. package/dist/hooks/navigation/useNavigationRegistry.d.ts +0 -12
  141. package/dist/hooks/navigation/useNavigationURLs.d.ts +0 -11
  142. package/dist/src-BJ8fkM8y.js.map +0 -1
  143. package/src/components/EntityCollectionView/Board.tsx +0 -475
  144. package/src/components/EntityCollectionView/BoardColumn.tsx +0 -165
  145. package/src/components/EntityCollectionView/BoardColumnTitle.tsx +0 -46
  146. package/src/components/EntityCollectionView/BoardSortableList.tsx +0 -177
  147. package/src/components/EntityCollectionView/board_types.ts +0 -113
  148. package/src/hooks/navigation/useNavigationRegistry.ts +0 -164
  149. package/src/hooks/navigation/useNavigationURLs.ts +0 -56
@@ -1,177 +0,0 @@
1
- import React, { memo, useEffect, useMemo, useRef } from "react";
2
- import { useDroppable } from "@dnd-kit/core";
3
- import { useSortable, SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
4
- import { CSS } from "@dnd-kit/utilities";
5
- import { CircularProgress, cls } from "@rebasepro/ui";
6
- import { BoardItem, BoardItemViewProps } from "./board_types";
7
-
8
- interface BoardSortableListProps<M extends Record<string, unknown>> {
9
- columnId: string;
10
- items: BoardItem<M>[];
11
- ItemComponent: React.ComponentType<BoardItemViewProps<M>>;
12
- isDragging: boolean;
13
- isDragOverColumn: boolean;
14
- loading?: boolean;
15
- hasMore?: boolean;
16
- onLoadMore?: () => void;
17
- }
18
-
19
- export function BoardSortableList<M extends Record<string, unknown>>({
20
- columnId,
21
- items,
22
- ItemComponent,
23
- isDragging,
24
- isDragOverColumn,
25
- loading = false,
26
- hasMore = false,
27
- onLoadMore
28
- }: BoardSortableListProps<M>) {
29
- const {
30
- setNodeRef
31
- } = useDroppable({
32
- id: columnId,
33
- data: { type: "ITEM-LIST" }
34
- });
35
-
36
- // Infinite scroll sentinel ref - must be inside scrollable container
37
- const sentinelRef = useRef<HTMLDivElement>(null);
38
- const isLoadingRef = useRef(false);
39
- isLoadingRef.current = loading;
40
- const lastLoadTimeRef = useRef(0);
41
-
42
- // Set up IntersectionObserver for infinite scroll
43
- useEffect(() => {
44
- if (!sentinelRef.current || !hasMore || !onLoadMore) return;
45
-
46
- const sentinel = sentinelRef.current;
47
-
48
- const observer = new IntersectionObserver(
49
- (entries) => {
50
- const now = Date.now();
51
- if (
52
- entries[0].isIntersecting &&
53
- hasMore &&
54
- !isLoadingRef.current &&
55
- now - lastLoadTimeRef.current > 500
56
- ) {
57
- lastLoadTimeRef.current = now;
58
- onLoadMore();
59
- }
60
- },
61
- { threshold: 0.1 }
62
- );
63
-
64
- observer.observe(sentinel);
65
-
66
- // Check if sentinel is already visible when effect runs
67
- // This handles the case where the sentinel was visible before the observer was created
68
- const rect = sentinel.getBoundingClientRect();
69
- const containerRect = sentinel.parentElement?.getBoundingClientRect();
70
- if (containerRect && rect.top < containerRect.bottom && rect.bottom > containerRect.top) {
71
- const now = Date.now();
72
- if (hasMore && !isLoadingRef.current && now - lastLoadTimeRef.current > 500) {
73
- lastLoadTimeRef.current = now;
74
- onLoadMore();
75
- }
76
- }
77
-
78
- return () => observer.disconnect();
79
- }, [hasMore, onLoadMore]);
80
-
81
- // Memoize className to avoid recomputation on every render
82
- const containerClassName = useMemo(() => cls(
83
- "flex flex-col p-2 transition-opacity duration-100 transition-bg ease-linear w-full overflow-y-auto no-scrollbar flex-1 rounded-md",
84
- isDragging && isDragOverColumn
85
- ? "bg-surface-accent-200 dark:bg-surface-900"
86
- : isDragging
87
- ? "bg-surface-50 dark:bg-surface-900 hover:bg-surface-accent-100 dark:hover:bg-surface-800"
88
- : "bg-surface-50 dark:bg-surface-900"
89
- ), [isDragging, isDragOverColumn]);
90
-
91
- return (
92
- <div
93
- ref={setNodeRef}
94
- className={containerClassName}
95
- style={{ minHeight: 80 }}
96
- >
97
- <SortableContext
98
- items={items.map(i => i.id)}
99
- strategy={verticalListSortingStrategy}
100
- >
101
- {items.length === 0 && !loading ? (
102
- <div className="flex-1 flex items-center justify-center">
103
- <span className="text-xs text-surface-400 dark:text-surface-500">
104
- No items
105
- </span>
106
- </div>
107
- ) : (
108
- <>
109
- {items.map((item, index) => (
110
- <SortableItem
111
- key={item.id}
112
- item={item}
113
- index={index}
114
- columnId={columnId}
115
- ItemComponent={ItemComponent}
116
- />
117
- ))}
118
- {/* Infinite scroll sentinel - inside scrollable container */}
119
- {(loading || hasMore) && (
120
- <div ref={sentinelRef} className="flex items-center justify-center py-2 min-h-6">
121
- {loading && <CircularProgress size="smallest"/>}
122
- </div>
123
- )}
124
- </>
125
- )}
126
- </SortableContext>
127
- </div>
128
- );
129
- }
130
-
131
- interface SortableItemProps<M extends Record<string, unknown>> {
132
- item: BoardItem<M>;
133
- index: number;
134
- columnId: string;
135
- ItemComponent: React.ComponentType<BoardItemViewProps<M>>;
136
- }
137
-
138
- // Memoized to prevent unnecessary re-renders when other items in the list change
139
- const SortableItem = memo(function SortableItem<M extends Record<string, unknown>>({
140
- item,
141
- index,
142
- columnId,
143
- ItemComponent
144
- }: SortableItemProps<M>) {
145
- const {
146
- setNodeRef,
147
- attributes,
148
- listeners,
149
- isDragging: isItemBeingDragged,
150
- transform,
151
- transition
152
- } = useSortable({
153
- id: item.id,
154
- data: {
155
- type: "ITEM",
156
- columnId
157
- }
158
- });
159
-
160
- // Memoize style object to prevent object recreation on each render
161
- const sortableStyle = useMemo(() => ({
162
- transform: CSS.Transform.toString(transform),
163
- transition,
164
- zIndex: isItemBeingDragged ? 2 : 1,
165
- opacity: isItemBeingDragged ? 0 : 1
166
- }), [transform, transition, isItemBeingDragged]);
167
-
168
- return (
169
- <div ref={setNodeRef} style={sortableStyle} {...attributes} {...listeners}>
170
- <ItemComponent
171
- item={item}
172
- isDragging={isItemBeingDragged}
173
- index={index}
174
- />
175
- </div>
176
- );
177
- }) as <M extends Record<string, unknown>>(props: SortableItemProps<M>) => React.ReactElement;
@@ -1,113 +0,0 @@
1
- import { CSSProperties } from "react";
2
- import { ChipColorKey, ChipColorScheme } from "@rebasepro/ui";
3
- import { Entity } from "@rebasepro/types";
4
-
5
- /**
6
- * Item wrapper for entities in the Board component
7
- */
8
- export interface BoardItem<M extends Record<string, unknown> = Record<string, unknown>> {
9
- id: string;
10
- entity: Entity<M>;
11
- }
12
-
13
- /**
14
- * Map of column keys to arrays of board items
15
- */
16
- export interface BoardItemMap<M extends Record<string, unknown> = Record<string, unknown>> {
17
- [columnKey: string]: BoardItem<M>[];
18
- }
19
-
20
- /**
21
- * Props passed to custom item render components
22
- */
23
- export interface BoardItemViewProps<M extends Record<string, unknown> = Record<string, unknown>> {
24
- item: BoardItem<M>;
25
- isDragging: boolean;
26
- isClone?: boolean;
27
- isGroupedOver?: boolean;
28
- style?: CSSProperties;
29
- index?: number;
30
- }
31
-
32
- /**
33
- * Per-column loading state
34
- */
35
- export interface ColumnLoadingState {
36
- [columnKey: string]: {
37
- loading: boolean;
38
- hasMore: boolean;
39
- itemCount: number;
40
- /** Total count of entities in column (may differ from itemCount if some lack orderProperty) */
41
- totalCount?: number;
42
- };
43
- }
44
-
45
- /**
46
- * Props for the Board component
47
- */
48
- export interface BoardProps<M extends Record<string, unknown>, COLUMN extends string> {
49
- /**
50
- * Array of board items (entities wrapped with id)
51
- */
52
- data: BoardItem<M>[];
53
- /**
54
- * Array of column keys/identifiers
55
- */
56
- columns: COLUMN[];
57
- /**
58
- * Labels for each column (optional, uses column key if not provided)
59
- */
60
- columnLabels?: Record<COLUMN, string>;
61
- /**
62
- * Colors for each column from enum values (optional)
63
- */
64
- columnColors?: Record<COLUMN, ChipColorKey | ChipColorScheme | undefined>;
65
- /**
66
- * CSS class name for the board container
67
- */
68
- className?: string;
69
- /**
70
- * Function to determine which column an item belongs to
71
- */
72
- assignColumn: (item: BoardItem<M>) => COLUMN;
73
- /**
74
- * Whether column reordering is allowed.
75
- * Set to true only when a plugin provides persistence for column order.
76
- */
77
- allowColumnReorder?: boolean;
78
- /**
79
- * Callback when columns are reordered
80
- */
81
- onColumnReorder?: (columns: COLUMN[]) => void;
82
- /**
83
- * Callback when items are reordered or moved between columns
84
- */
85
- onItemsReorder?: (
86
- items: BoardItem<M>[],
87
- moveInfo?: {
88
- itemId: string;
89
- sourceColumn: COLUMN;
90
- targetColumn: COLUMN;
91
- }
92
- ) => void;
93
- /**
94
- * Component to render individual items
95
- */
96
- ItemComponent: React.ComponentType<BoardItemViewProps<M>>;
97
- /**
98
- * Per-column loading state for pagination
99
- */
100
- columnLoadingState?: ColumnLoadingState;
101
- /**
102
- * Callback to load more items for a column
103
- */
104
- onLoadMoreColumn?: (column: COLUMN) => void;
105
- /**
106
- * Callback to add a new item to a specific column
107
- */
108
- onAddItemToColumn?: (column: COLUMN) => void;
109
- /**
110
- * Optional component to render at the end of the board for adding new columns
111
- */
112
- AddColumnComponent?: React.ReactNode;
113
- }
@@ -1,164 +0,0 @@
1
- import type { EntityCollection } from "@rebasepro/types";
2
- import { useCallback, useRef, useMemo } from "react";
3
- import { CollectionRegistry, getParentReferencesFromPath, getSubcollections, removeInitialAndTrailingSlashes } from "@rebasepro/common";
4
- import { EntityReference, UserConfigurationPersistence } from "@rebasepro/types";
5
- import { mergeDeep } from "@rebasepro/utils";
6
-
7
- export function useNavigationRegistry(userConfigPersistence?: UserConfigurationPersistence) {
8
- const collectionRegistryRef = useRef<CollectionRegistry>(new CollectionRegistry());
9
-
10
- const getCollection = useCallback((
11
- slugOrPath: string,
12
- includeUserOverride = false
13
- ): EntityCollection | undefined => {
14
-
15
- const registry = collectionRegistryRef.current;
16
-
17
- const cleanedPath = removeInitialAndTrailingSlashes(slugOrPath);
18
- if (!cleanedPath) return undefined;
19
-
20
- const pathSegments = cleanedPath.split("/");
21
-
22
- let collectionPath = cleanedPath;
23
- // If the path has an even number of segments, it points to an entity, so we get the parent collection
24
- if (pathSegments.length > 0 && pathSegments.length % 2 === 0) {
25
- collectionPath = pathSegments.slice(0, -1).join("/");
26
- }
27
-
28
- if (!collectionPath) return undefined;
29
-
30
- let collection: EntityCollection | undefined;
31
- try {
32
- collection = registry.resolvePathToCollections(collectionPath).finalCollection;
33
- } catch (e) {
34
- console.debug(`Could not resolve path to collection: ${collectionPath}`, e);
35
- return undefined;
36
- }
37
-
38
- if (!collection) {
39
- return undefined;
40
- }
41
-
42
- const userOverride = includeUserOverride ? userConfigPersistence?.getCollectionConfig(slugOrPath) : undefined;
43
- const overriddenCollection = collection ? mergeDeep(collection, userOverride ?? {}) : undefined;
44
-
45
- if (!overriddenCollection) return undefined;
46
-
47
- let result: Partial<EntityCollection> | undefined = overriddenCollection;
48
- const subcollections = "subcollections" in overriddenCollection ? overriddenCollection.subcollections : undefined;
49
- const callbacks = overriddenCollection.callbacks;
50
- result = {
51
- ...result,
52
- callbacks: result?.callbacks ?? callbacks
53
- };
54
- if (subcollections && "subcollections" in result) {
55
- (result as Record<string, unknown>).subcollections = (result as Record<string, unknown>).subcollections ?? subcollections;
56
- }
57
-
58
- return { ...overriddenCollection,
59
- ...result } as EntityCollection;
60
-
61
- }, [userConfigPersistence]);
62
-
63
- const getRawCollection = useCallback((slugOrPath: string): EntityCollection | undefined => {
64
- const registry = collectionRegistryRef.current;
65
- if (registry === undefined) return undefined;
66
-
67
- const cleanedPath = removeInitialAndTrailingSlashes(slugOrPath);
68
- if (!cleanedPath) return undefined;
69
-
70
- return registry.getRaw(cleanedPath) as EntityCollection | undefined;
71
- }, []);
72
-
73
- const getCollectionFromPaths = useCallback(<EC extends EntityCollection>(pathSegments: string[]): EC | undefined => {
74
- const registry = collectionRegistryRef.current;
75
- if (registry === undefined)
76
- throw Error("getCollectionFromPaths: Collections have not been initialised yet");
77
-
78
- if (!pathSegments?.length) {
79
- return undefined;
80
- }
81
-
82
- const path = pathSegments.reduce((acc, segment, i) => {
83
- if (i === 0) return segment;
84
- return `${acc}/fake_id/${segment}`;
85
- }, "");
86
-
87
- try {
88
- const { finalCollection } = registry.resolvePathToCollections(path);
89
- return finalCollection as EC | undefined;
90
- } catch (e) {
91
- console.debug(`Could not resolve path segments to collection: ${pathSegments.join("/")}`, e);
92
- return undefined;
93
- }
94
- }, []);
95
-
96
- const getAllParentReferencesForPath = useCallback((path: string): EntityReference[] => {
97
- const registry = collectionRegistryRef.current;
98
- if (!registry) {
99
- return [];
100
- }
101
- return getParentReferencesFromPath({
102
- path,
103
- collections: registry.getCollections()
104
- });
105
- }, []);
106
-
107
- const getParentCollectionSlugs = useCallback((path: string): string[] => {
108
- const strings = path.split("/");
109
- const oddPathSegments = strings.filter((_, i) => i % 2 === 0);
110
- oddPathSegments.pop();
111
-
112
- const result: string[][] = [];
113
-
114
- for (let i = 1; i <= oddPathSegments.length; i++) {
115
- result.push(oddPathSegments.slice(0, i));
116
- }
117
-
118
- return result.map(r => getCollectionFromPaths(r)?.slug).filter(Boolean) as string[];
119
- }, [getAllParentReferencesForPath, getCollectionFromPaths]);
120
-
121
- const getParentEntityIds = useCallback((path: string): string[] => {
122
- const strings = path.split("/");
123
- const evenPathSegments = strings.filter((_, i) => i % 2 !== 0);
124
- if (strings.length % 2 === 0) {
125
- evenPathSegments.pop();
126
- }
127
- return evenPathSegments;
128
- }, []);
129
-
130
- const convertIdsToPaths = useCallback((ids: string[]): string[] => {
131
- const registry = collectionRegistryRef.current;
132
- if (!registry) {
133
- throw new Error("convertIdsToPaths: collectionRegistryRef not initialised");
134
- }
135
- let currentCollections: EntityCollection[] = registry.getCollections();
136
- const paths: string[] = [];
137
- for (let i = 0; i < ids.length; i++) {
138
- const id = ids[i];
139
- const collection: EntityCollection | undefined = currentCollections.find(c => c.slug === id);
140
- if (!collection)
141
- throw Error(`Collection with id ${id} not found`);
142
- paths.push(collection.slug);
143
- currentCollections = getSubcollections(collection) ?? [];
144
- }
145
- return paths;
146
- }, []);
147
-
148
- return useMemo(() => ({
149
- collectionRegistryRef,
150
- getCollection,
151
- getRawCollection,
152
- getParentReferencesFromPath: getAllParentReferencesForPath,
153
- getParentCollectionSlugs,
154
- getParentEntityIds,
155
- convertIdsToPaths
156
- }), [
157
- getCollection,
158
- getRawCollection,
159
- getAllParentReferencesForPath,
160
- getParentCollectionSlugs,
161
- getParentEntityIds,
162
- convertIdsToPaths
163
- ]);
164
- }
@@ -1,56 +0,0 @@
1
- import { useCallback, useMemo } from "react";
2
- import { removeInitialAndTrailingSlashes, resolveCollectionPathIds } from "@rebasepro/common";
3
- import { CollectionRegistry } from "@rebasepro/common";
4
-
5
- export function encodePath(input: string) {
6
- return encodeURIComponent(removeInitialAndTrailingSlashes(input))
7
- .replaceAll("%2F", "/")
8
- .replaceAll("%23", "#");
9
- }
10
-
11
- export function useNavigationURLs(basePath: string, baseCollectionPath: string, collectionRegistryRef: React.MutableRefObject<CollectionRegistry>) {
12
-
13
- const cleanBasePath = removeInitialAndTrailingSlashes(basePath);
14
- const cleanBaseCollectionPath = removeInitialAndTrailingSlashes(baseCollectionPath);
15
-
16
- const homeUrl = cleanBasePath ? `/${cleanBasePath}` : "/";
17
- const fullCollectionPath = cleanBasePath ? `/${cleanBasePath}/${cleanBaseCollectionPath}` : `/${cleanBaseCollectionPath}`;
18
-
19
- const buildAppUrlPath = useCallback((path: string): string => {
20
- // Strip trailing /* wildcard from paths (used for nested routes in React Router)
21
- const cleanPath = path.replace(/\/\*$/, "");
22
- return cleanBasePath ? `/${cleanBasePath}/${encodePath(cleanPath)}` : `/${encodePath(cleanPath)}`;
23
- }, [cleanBasePath]);
24
-
25
- const buildUrlCollectionPath = useCallback((path: string): string => `${removeInitialAndTrailingSlashes(baseCollectionPath)}/${encodePath(path)}`,
26
- [baseCollectionPath]);
27
-
28
- const isUrlCollectionPath = useCallback(
29
- (path: string): boolean => removeInitialAndTrailingSlashes(path + "/").startsWith(removeInitialAndTrailingSlashes(fullCollectionPath) + "/"),
30
- [fullCollectionPath]);
31
-
32
- const urlPathToDataPath = useCallback((path: string): string => {
33
- const decodedPath = decodeURIComponent(path);
34
- if (decodedPath.startsWith(fullCollectionPath))
35
- return decodedPath.replace(fullCollectionPath, "");
36
- throw Error("Expected path starting with " + fullCollectionPath);
37
- }, [fullCollectionPath]);
38
-
39
- const resolveDatabasePathsFrom = useCallback((path: string): string => {
40
- const registry = collectionRegistryRef.current;
41
- if (!registry) {
42
- return resolveCollectionPathIds(path, []);
43
- }
44
- return resolveCollectionPathIds(path, registry.getCollections());
45
- }, [collectionRegistryRef]);
46
-
47
- return useMemo(() => ({
48
- homeUrl,
49
- fullCollectionPath,
50
- buildAppUrlPath,
51
- buildUrlCollectionPath,
52
- isUrlCollectionPath,
53
- urlPathToDataPath,
54
- resolveDatabasePathsFrom
55
- }), [homeUrl, fullCollectionPath, buildAppUrlPath, buildUrlCollectionPath, isUrlCollectionPath, urlPathToDataPath, resolveDatabasePathsFrom]);
56
- }