@rebasepro/app 0.9.1-canary.ff338b5 → 0.10.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/contexts/AdminModeController.d.ts +1 -1
- package/dist/contexts/ModeController.d.ts +1 -1
- package/dist/hooks/useBuildAdminModeController.d.ts +1 -1
- package/dist/hooks/useBuildModeController.d.ts +1 -1
- package/dist/hooks/useNavigationBlocker.d.ts +29 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +89 -3
- package/dist/index.es.js.map +1 -1
- package/package.json +6 -6
- package/src/components/UnsavedChangesDialog.tsx +1 -1
- package/src/contexts/AdminModeController.tsx +1 -1
- package/src/contexts/ModeController.tsx +1 -1
- package/src/core/Rebase.tsx +1 -0
- package/src/hooks/useBuildAdminModeController.tsx +1 -1
- package/src/hooks/useBuildModeController.tsx +1 -1
- package/src/hooks/useNavigationBlocker.tsx +108 -0
- package/src/hooks/useUnsavedChangesDialog.tsx +7 -4
- package/src/index.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { AdminModeController } from "../hooks";
|
|
2
|
+
import type { AdminModeController } from "../hooks/useAdminModeController";
|
|
3
3
|
export declare const AdminModeControllerContext: React.Context<AdminModeController>;
|
|
4
4
|
export declare const AdminModeControllerProvider: React.Provider<AdminModeController>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { ModeController } from "../hooks";
|
|
2
|
+
import type { ModeController } from "../hooks/useModeController";
|
|
3
3
|
export declare const ModeControllerContext: React.Context<ModeController>;
|
|
4
4
|
export declare const ModeControllerProvider: React.Provider<ModeController>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Blocker, BlockerFunction } from "react-router-dom";
|
|
3
|
+
/**
|
|
4
|
+
* Owns the single React Router blocker for the whole app.
|
|
5
|
+
*
|
|
6
|
+
* React Router only honours **one** blocker at a time: `shouldBlockNavigation`
|
|
7
|
+
* picks the last-registered blocker function and silently ignores every other
|
|
8
|
+
* one (it only logs a dev warning). Because blockers register in a `useEffect`,
|
|
9
|
+
* the winner is whichever surface mounted most recently — so an unsaved-changes
|
|
10
|
+
* guard could be disabled by an unrelated component mounting after it.
|
|
11
|
+
*
|
|
12
|
+
* This provider registers the only `useBlocker` in the tree and multiplexes it,
|
|
13
|
+
* so every surface that needs to guard navigation gets a say regardless of
|
|
14
|
+
* mount order. Surfaces register through {@link useNavigationBlocker}.
|
|
15
|
+
*/
|
|
16
|
+
export declare function NavigationBlockerProvider({ children }: {
|
|
17
|
+
children: React.ReactNode;
|
|
18
|
+
}): React.JSX.Element;
|
|
19
|
+
/**
|
|
20
|
+
* Guard navigation with `predicate`, sharing the app-wide blocker.
|
|
21
|
+
*
|
|
22
|
+
* Returns a {@link Blocker} that is only ever in the `blocked` state when *this*
|
|
23
|
+
* registration is what blocked the navigation — so several guards can coexist
|
|
24
|
+
* without each of them popping a dialog.
|
|
25
|
+
*
|
|
26
|
+
* Returns an idle blocker when no {@link NavigationBlockerProvider} is mounted
|
|
27
|
+
* above, rather than competing for React Router's single blocker slot.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useNavigationBlocker(predicate: BlockerFunction): Blocker;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./contexts";
|
|
|
7
7
|
export { CONTAINER_FULL_WIDTH, ADDITIONAL_TAB_WIDTH, FORM_CONTAINER_WIDTH } from "./internal/common";
|
|
8
8
|
export { useRestoreScroll } from "./internal/useRestoreScroll";
|
|
9
9
|
export { useUnsavedChangesDialog } from "./hooks/useUnsavedChangesDialog";
|
|
10
|
+
export { NavigationBlockerProvider, useNavigationBlocker } from "./hooks/useNavigationBlocker";
|
|
10
11
|
export type { UnsavedChangesDialogProps } from "./components/UnsavedChangesDialog";
|
|
11
12
|
export { UnsavedChangesDialog } from "./components/UnsavedChangesDialog";
|
|
12
13
|
export * from "./i18n/RebaseI18nProvider";
|
package/dist/index.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, lazy, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import React, { createContext, lazy, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { Alert, AlertCircleIcon, AlertTriangleIcon, AppWindow, ArrowLeftIcon, Avatar, BooleanSwitch, Button, CHIP_COLORS, Card, CenteredView, CheckIcon, Checkbox, ChevronDownIcon, ChevronsLeftIcon, ChevronsRightIcon, Chip, CircleUserIcon, CircularProgress, ColumnsIcon, Container, Dialog, DialogActions, DialogContent, DialogTitle, ErrorBoundary, FileIcon, FileTextIcon, FilterChip, FilterIcon, FolderIcon, IconButton, KanbanIcon, KanbanView, LanguagesIcon, LayoutGridIcon, ListIcon, LoadingButton, LogOutIcon, MailIcon, Menu, MenuItem, MoonIcon, MultiSelect, MultiSelectItem, PanelLeftIcon, Paper, PencilIcon, PlusIcon, Popover, SearchBar, Select, SelectItem, Separator, SettingsIcon, Skeleton, SunIcon, SunMoonIcon, Tab, Table, TableBody, TableCell, TableHeader, TableRow, Tabs, TagIcon, TextField, ToggleButtonGroup, Tooltip, Trash2Icon, TypeIcon, Typography, UserIcon, Wand2Icon, cls, colorClassesMapping, coolIconKeys, defaultBorderMixin, getColorSchemeForSeed, iconKeys, iconSize, lucideIcons } from "@rebasepro/ui";
|
|
3
3
|
import { DEFAULT_DATA_SOURCE_KEY, DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, GeoPoint, RebaseApiError, Vector, isLazyComponentRef } from "@rebasepro/types";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1080,6 +1080,91 @@ async function deleteEntityWithCallbacks({ data, entity, collection, callbacks,
|
|
|
1080
1080
|
});
|
|
1081
1081
|
}
|
|
1082
1082
|
//#endregion
|
|
1083
|
+
//#region src/hooks/useNavigationBlocker.tsx
|
|
1084
|
+
var IDLE_BLOCKER = {
|
|
1085
|
+
state: "unblocked",
|
|
1086
|
+
proceed: void 0,
|
|
1087
|
+
reset: void 0,
|
|
1088
|
+
location: void 0
|
|
1089
|
+
};
|
|
1090
|
+
var NavigationBlockerContext = createContext(null);
|
|
1091
|
+
/**
|
|
1092
|
+
* Owns the single React Router blocker for the whole app.
|
|
1093
|
+
*
|
|
1094
|
+
* React Router only honours **one** blocker at a time: `shouldBlockNavigation`
|
|
1095
|
+
* picks the last-registered blocker function and silently ignores every other
|
|
1096
|
+
* one (it only logs a dev warning). Because blockers register in a `useEffect`,
|
|
1097
|
+
* the winner is whichever surface mounted most recently — so an unsaved-changes
|
|
1098
|
+
* guard could be disabled by an unrelated component mounting after it.
|
|
1099
|
+
*
|
|
1100
|
+
* This provider registers the only `useBlocker` in the tree and multiplexes it,
|
|
1101
|
+
* so every surface that needs to guard navigation gets a say regardless of
|
|
1102
|
+
* mount order. Surfaces register through {@link useNavigationBlocker}.
|
|
1103
|
+
*/
|
|
1104
|
+
function NavigationBlockerProvider({ children }) {
|
|
1105
|
+
const predicates = useRef(/* @__PURE__ */ new Map());
|
|
1106
|
+
const [blockedBy, setBlockedBy] = useState(null);
|
|
1107
|
+
const blocker = useBlocker(useCallback((args) => {
|
|
1108
|
+
for (const [id, predicate] of predicates.current) if (predicate(args)) {
|
|
1109
|
+
setBlockedBy(id);
|
|
1110
|
+
return true;
|
|
1111
|
+
}
|
|
1112
|
+
setBlockedBy(null);
|
|
1113
|
+
return false;
|
|
1114
|
+
}, []));
|
|
1115
|
+
const register = useCallback((id, predicate) => {
|
|
1116
|
+
predicates.current.set(id, predicate);
|
|
1117
|
+
}, []);
|
|
1118
|
+
const unregister = useCallback((id) => {
|
|
1119
|
+
predicates.current.delete(id);
|
|
1120
|
+
setBlockedBy((current) => current === id ? null : current);
|
|
1121
|
+
}, []);
|
|
1122
|
+
const value = useMemo(() => ({
|
|
1123
|
+
register,
|
|
1124
|
+
unregister,
|
|
1125
|
+
blocker,
|
|
1126
|
+
blockedBy
|
|
1127
|
+
}), [
|
|
1128
|
+
register,
|
|
1129
|
+
unregister,
|
|
1130
|
+
blocker,
|
|
1131
|
+
blockedBy
|
|
1132
|
+
]);
|
|
1133
|
+
return /* @__PURE__ */ jsx(NavigationBlockerContext.Provider, {
|
|
1134
|
+
value,
|
|
1135
|
+
children
|
|
1136
|
+
});
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Guard navigation with `predicate`, sharing the app-wide blocker.
|
|
1140
|
+
*
|
|
1141
|
+
* Returns a {@link Blocker} that is only ever in the `blocked` state when *this*
|
|
1142
|
+
* registration is what blocked the navigation — so several guards can coexist
|
|
1143
|
+
* without each of them popping a dialog.
|
|
1144
|
+
*
|
|
1145
|
+
* Returns an idle blocker when no {@link NavigationBlockerProvider} is mounted
|
|
1146
|
+
* above, rather than competing for React Router's single blocker slot.
|
|
1147
|
+
*/
|
|
1148
|
+
function useNavigationBlocker(predicate) {
|
|
1149
|
+
const context = useContext(NavigationBlockerContext);
|
|
1150
|
+
const id = useId();
|
|
1151
|
+
const predicateRef = useRef(predicate);
|
|
1152
|
+
predicateRef.current = predicate;
|
|
1153
|
+
const register = context?.register;
|
|
1154
|
+
const unregister = context?.unregister;
|
|
1155
|
+
useEffect(() => {
|
|
1156
|
+
if (!register || !unregister) return;
|
|
1157
|
+
register(id, (args) => predicateRef.current(args));
|
|
1158
|
+
return () => unregister(id);
|
|
1159
|
+
}, [
|
|
1160
|
+
register,
|
|
1161
|
+
unregister,
|
|
1162
|
+
id
|
|
1163
|
+
]);
|
|
1164
|
+
if (!context) return IDLE_BLOCKER;
|
|
1165
|
+
return context.blockedBy === id ? context.blocker : IDLE_BLOCKER;
|
|
1166
|
+
}
|
|
1167
|
+
//#endregion
|
|
1083
1168
|
//#region src/hooks/useUnsavedChangesDialog.tsx
|
|
1084
1169
|
/**
|
|
1085
1170
|
* A single, unified hook to prevent navigation when there are unsaved changes.
|
|
@@ -1090,7 +1175,7 @@ async function deleteEntityWithCallbacks({ data, entity, collection, callbacks,
|
|
|
1090
1175
|
*/
|
|
1091
1176
|
function useUnsavedChangesDialog(when, onOk) {
|
|
1092
1177
|
const [manualDialogOpen, setManualDialogOpen] = useState(false);
|
|
1093
|
-
const blocker =
|
|
1178
|
+
const blocker = useNavigationBlocker(useCallback(({ currentLocation, nextLocation }) => when && currentLocation.pathname !== nextLocation.pathname, [when]));
|
|
1094
1179
|
useEffect(() => {
|
|
1095
1180
|
if (!when) return;
|
|
1096
1181
|
const handleBeforeUnload = (e) => {
|
|
@@ -15570,6 +15655,7 @@ function Rebase(props) {
|
|
|
15570
15655
|
executeSql: wsAdmin.executeSql.bind(wsAdmin),
|
|
15571
15656
|
fetchAvailableDatabases: wsAdmin.fetchAvailableDatabases?.bind(wsAdmin),
|
|
15572
15657
|
fetchAvailableRoles: wsAdmin.fetchAvailableRoles?.bind(wsAdmin),
|
|
15658
|
+
fetchApplicationRoles: wsAdmin.fetchApplicationRoles?.bind(wsAdmin),
|
|
15573
15659
|
fetchCurrentDatabase: wsAdmin.fetchCurrentDatabase?.bind(wsAdmin),
|
|
15574
15660
|
fetchUnmappedTables: wsAdmin.fetchUnmappedTables?.bind(wsAdmin),
|
|
15575
15661
|
fetchTableMetadata: wsAdmin.fetchTableMetadata?.bind(wsAdmin),
|
|
@@ -16888,6 +16974,6 @@ function useBridgeRegistration(key, value) {
|
|
|
16888
16974
|
]);
|
|
16889
16975
|
}
|
|
16890
16976
|
//#endregion
|
|
16891
|
-
export { ADDITIONAL_TAB_WIDTH, AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthApiError, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, STUDIO_NAVIGATION_GROUPS, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, buildCollapsedDefaults, buildEnumLabel, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, en, es, fetchAuthConfig, flattenKeys, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, iconsSearch, isEnumValueDisabled, isSchemaDriftError, populateFetchCache, removeEntityFromCache, removeEntityFromMemoryCache, resolveComponentRef, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
|
|
16977
|
+
export { ADDITIONAL_TAB_WIDTH, AIIcon, AIModifiedIndicator, AdminModeControllerContext, AdminModeControllerProvider, AnalyticsContext, ApiConfigProvider, AuthApiError, AuthControllerContext, CONTAINER_FULL_WIDTH, CollectionComponentOverrideProvider, CollectionResolverRegistrationContext, CollectionScopeContext, CollectionScopeProvider, ComponentOverrideContext, ConfirmationDialog, CrmDashboardDemo, CustomizationControllerContext, DEFAULT_PAGE_SIZE, DataDriverContext, DataSourcesContext, DialogsControllerContext, DialogsProvider, EffectiveRoleControllerContext, EffectiveRoleControllerProvider, ErrorTooltip, ErrorView, FORM_CONTAINER_WIDTH, GlobalComponentOverrideProvider, IconForView, LanguageToggle, LoginView, ModeControllerContext, ModeControllerProvider, NavigationBlockerProvider, NotFoundPage, PluginProviderStack, REBASE_LOCALE_STORAGE_KEY, Rebase, RebaseAuth, RebaseClientInstanceContext, RebaseDataContext, RebaseI18nProvider, RebaseLogo, RebaseRegistryProvider, RebaseRouter, RebaseRoutes, STUDIO_NAVIGATION_GROUPS, SchemaDriftBanner, SchemaDriftProvider, SnackbarProvider, StorageSourceContext, StorageSourcesContext, StudioBridgeContext, StudioBridgeProvider, StudioBridgeRegistryContext, StudioBridgeRegistryProvider, UIReferenceView, UIStyleGuide, UnsavedChangesDialog, UserConfigurationPersistenceContext, UserDisplay, UserSelectPopover, UserSettingsView, buildCollapsedDefaults, buildEnumLabel, clearAuthConfigCache, clearFetchCache, createAuthConfigCache, createFormexStub, deleteEntityWithCallbacks, en, es, fetchAuthConfig, flattenKeys, getColorScheme, getColumnKeysForProperty, getEntityFromCache, getEntityFromMemoryCache, getEntityPreviewKeys, getEntityTitlePropertyKey, getFormFieldKeys, getIcon, getRelationIncludeParams, getRowHeight, getSubcollectionColumnId, iconsSearch, isEnumValueDisabled, isSchemaDriftError, populateFetchCache, removeEntityFromCache, removeEntityFromMemoryCache, resolveComponentRef, saveEntityToCache, saveEntityToMemoryCache, saveEntityWithCallbacks, useAdminModeController, useAnalyticsController, useApiConfig, useAuthController, useAuthSubscription, useBackendStorageSource, useBridgeRegistration, useBrowserTitleAndIcon, useBuildAdminModeController, useBuildEffectiveRoleController, useBuildLocalConfigurationPersistence, useBuildModeController, useClipboard, useCollapsedGroups, useCollection, useCollectionScope, useColumnIds, useComponentOverride, useCustomizationController, useData, useDataSources, useDataTableController, useDebouncedData, useDialogsController, useEffectiveRoleController, useFetch, useLargeLayout, useModeController, useNavigationBlocker, usePermissions, useRebaseAuthController, useRebaseClient, useRebaseContext, useRebaseRegistry, useRebaseRegistryDispatch, useRelationSelector, useResolvedComponent, useRestoreScroll, useSchemaDriftContext, useScrollRestoration, useSlot, useSnackbarController, useStorageSource, useStorageSources, useStorageUploadController, useStudioBreadcrumbs, useStudioCollectionRegistry, useStudioNavigationState, useStudioSidePanelController, useStudioUrlController, useTranslation, useUnsavedChangesDialog, useUserConfigurationPersistence };
|
|
16892
16978
|
|
|
16893
16979
|
//# sourceMappingURL=index.es.js.map
|