@rebasepro/app 0.22.0 → 0.24.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/auth/api.d.ts +2 -2
- package/dist/auth/session_caches.d.ts +26 -0
- package/dist/auth/types.d.ts +84 -1
- package/dist/collections/entity-display-cache.d.ts +1 -0
- package/dist/collections/filter-operator-resolution.d.ts +18 -0
- package/dist/collections/navigation_utils.d.ts +8 -2
- package/dist/collections/property_presentation.d.ts +12 -15
- package/dist/components/ConnectionLostBanner.d.ts +31 -0
- package/dist/components/LoginView/email-link.d.ts +37 -0
- package/dist/components/LoginView/index.d.ts +2 -0
- package/dist/components/LoginView/mfa-required.d.ts +33 -0
- package/dist/components/UserSelectPopover.d.ts +14 -1
- package/dist/components/common/useDataTableController.d.ts +1 -1
- package/dist/components/common/useScrollRestoration.d.ts +7 -0
- package/dist/components/email-change-message.d.ts +6 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/contexts/CollectionResolverContext.d.ts +11 -1
- package/dist/core/PluginLifecycleManager.d.ts +12 -4
- package/dist/debug.js +47 -40
- package/dist/hooks/data/collectionWindow.d.ts +133 -0
- package/dist/hooks/data/useStableFilterValues.d.ts +9 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/index.es.js +7008 -2951
- package/dist/index.es.js.map +1 -1
- package/dist/util/entity_cache.d.ts +11 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/realtime.d.ts +18 -0
- package/dist/vitePlugin.d.ts +6 -0
- package/dist/vitePlugin.js +138 -15
- package/package.json +7 -7
package/dist/auth/api.d.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* here is the unauthenticated `/api/auth/config` probe used to detect
|
|
9
9
|
* bootstrap mode and enabled providers before a session exists.
|
|
10
10
|
*/
|
|
11
|
-
import type {
|
|
11
|
+
import type { AuthConfigDocument } from "@rebasepro/types";
|
|
12
12
|
/**
|
|
13
13
|
* What `GET /api/auth/config` answers.
|
|
14
14
|
*
|
|
@@ -17,7 +17,7 @@ import type { AuthAdapterCapabilities } from "@rebasepro/types";
|
|
|
17
17
|
* this one listed an `emailServiceEnabled` flag no backend sends, and marked as
|
|
18
18
|
* optional fields every backend always returns.
|
|
19
19
|
*/
|
|
20
|
-
export type AuthConfigResponse =
|
|
20
|
+
export type AuthConfigResponse = AuthConfigDocument;
|
|
21
21
|
/**
|
|
22
22
|
* Cache container for `fetchAuthConfig` — holds both the inflight promise
|
|
23
23
|
* (to deduplicate concurrent calls) and the cached result.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Empty every module-level cache that holds data read as the signed-in user:
|
|
3
|
+
* fetched records, each table's scroll position and the rows kept with it,
|
|
4
|
+
* edits parked for a layout handoff, and resolved display values (the titles
|
|
5
|
+
* on relation chips and breadcrumbs).
|
|
6
|
+
*
|
|
7
|
+
* All of them are keyed by path and id only, with no user in the key, and the
|
|
8
|
+
* views seed their first render from them — so without this the next user to
|
|
9
|
+
* sign in to the tab is shown what the last one could read. Called by both auth
|
|
10
|
+
* controllers on `SIGNED_OUT`.
|
|
11
|
+
*
|
|
12
|
+
* The local-changes backup is not cleared here: a sign-out is also how a
|
|
13
|
+
* rejected refresh token ends a session, and the backup exists for that user
|
|
14
|
+
* coming back. {@link bindSessionCachesToUser} drops it when somebody else
|
|
15
|
+
* signs in instead.
|
|
16
|
+
*/
|
|
17
|
+
export declare function clearSessionCaches(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Record that the tab now acts as `userId`. When that is not who the caches
|
|
20
|
+
* were filled for, everything goes — the session caches, and the drafts the
|
|
21
|
+
* other user left in the local-changes backup.
|
|
22
|
+
*
|
|
23
|
+
* Call it before the new user is rendered: the views read these caches in
|
|
24
|
+
* their first render, before any effect of the auth controller would run.
|
|
25
|
+
*/
|
|
26
|
+
export declare function bindSessionCachesToUser(userId: string): void;
|
package/dist/auth/types.d.ts
CHANGED
|
@@ -21,7 +21,14 @@ export type RebaseAuthController = AuthController & {
|
|
|
21
21
|
/** Login with email and password */
|
|
22
22
|
emailPasswordLogin: (email: string, password: string) => Promise<void>;
|
|
23
23
|
/** Register a new user */
|
|
24
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Create an account. `confirmationRequired` when the backend registers
|
|
26
|
+
* confirm-first (`requireEmailVerification`): nobody is signed in until
|
|
27
|
+
* the address is confirmed.
|
|
28
|
+
*/
|
|
29
|
+
register: (email: string, password: string, displayName?: string) => Promise<{
|
|
30
|
+
confirmationRequired: boolean;
|
|
31
|
+
}>;
|
|
25
32
|
/** Skip login (for anonymous access if enabled) */
|
|
26
33
|
skipLogin: () => void;
|
|
27
34
|
/** Whether login was skipped */
|
|
@@ -36,8 +43,33 @@ export type RebaseAuthController = AuthController & {
|
|
|
36
43
|
forgotPassword: (email: string) => Promise<void>;
|
|
37
44
|
/** Reset password using token from email */
|
|
38
45
|
resetPassword: (token: string, password: string) => Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Confirm an email address using the token from a verification email.
|
|
48
|
+
* Refused with `PROOF_REQUIRED` when the account holds a password this
|
|
49
|
+
* call does not prove: pass `password` to keep it (and sign in), or
|
|
50
|
+
* `removeUnproven` to verify without it.
|
|
51
|
+
*/
|
|
52
|
+
verifyEmail: (token: string, options?: {
|
|
53
|
+
password?: string;
|
|
54
|
+
removeUnproven?: boolean;
|
|
55
|
+
}) => Promise<{
|
|
56
|
+
passwordRemoved: boolean;
|
|
57
|
+
signedIn: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
/** Sign in with a magic link's token. Absent when the client cannot. */
|
|
60
|
+
magicLinkLogin?: (token: string) => Promise<void>;
|
|
39
61
|
/** Change password for authenticated user */
|
|
40
62
|
changePassword: (oldPassword: string, newPassword: string) => Promise<void>;
|
|
63
|
+
/** Ask to move the signed-in account to another address. Absent when the client cannot. */
|
|
64
|
+
changeEmail?: (newEmail: string) => Promise<{
|
|
65
|
+
pendingEmail: string;
|
|
66
|
+
expiresAt: string;
|
|
67
|
+
}>;
|
|
68
|
+
/** Follow an address-change link. Absent when the client cannot. */
|
|
69
|
+
confirmEmailChange?: (token: string) => Promise<{
|
|
70
|
+
email: string;
|
|
71
|
+
removedProviders: string[];
|
|
72
|
+
}>;
|
|
41
73
|
/** Update user profile */
|
|
42
74
|
updateProfile: (displayName?: string, photoURL?: string) => Promise<User>;
|
|
43
75
|
/** Fetch active sessions */
|
|
@@ -52,6 +84,18 @@ export type RebaseAuthController = AuthController & {
|
|
|
52
84
|
clearError: () => void;
|
|
53
85
|
/** Set or clear the auth provider error */
|
|
54
86
|
setAuthProviderError: (error: Error | null) => void;
|
|
87
|
+
/**
|
|
88
|
+
* Open a challenge to finish a sign-in refused with `MFA_REQUIRED`. See
|
|
89
|
+
* `AuthControllerExtended`. Absent when the client has no MFA support.
|
|
90
|
+
*/
|
|
91
|
+
startMfaChallenge?: (mfaToken: string, factorId: string) => Promise<string>;
|
|
92
|
+
/**
|
|
93
|
+
* Answer that challenge; the user is signed in on success. Absent when
|
|
94
|
+
* the client has no MFA support.
|
|
95
|
+
*/
|
|
96
|
+
verifyMfaChallenge?: (mfaToken: string, challengeId: string, code: string) => Promise<void>;
|
|
97
|
+
/** The scopes the signed-in user holds; `undefined` until known. */
|
|
98
|
+
heldScopes?: string[];
|
|
55
99
|
};
|
|
56
100
|
/**
|
|
57
101
|
* Structural type for the subset of `client.auth` (from `@rebasepro/client`)
|
|
@@ -78,7 +122,24 @@ export interface ClientAuth {
|
|
|
78
122
|
signInWithOAuth(providerId: string, payload: Record<string, unknown>): Promise<unknown>;
|
|
79
123
|
resetPasswordForEmail(email: string): Promise<unknown>;
|
|
80
124
|
resetPassword(token: string, password: string): Promise<unknown>;
|
|
125
|
+
/** Optional so a hand-built auth client need not implement it. */
|
|
126
|
+
verifyEmail?(token: string, options?: {
|
|
127
|
+
password?: string;
|
|
128
|
+
removeUnproven?: boolean;
|
|
129
|
+
}): Promise<unknown>;
|
|
81
130
|
changePassword(oldPassword: string, newPassword: string): Promise<unknown>;
|
|
131
|
+
/** Optional so a hand-built auth client need not implement it. */
|
|
132
|
+
verifyMagicLink?(token: string): Promise<unknown>;
|
|
133
|
+
/** Optional so a hand-built auth client need not implement it. */
|
|
134
|
+
changeEmail?(newEmail: string): Promise<{
|
|
135
|
+
pendingEmail: string;
|
|
136
|
+
expiresAt: string;
|
|
137
|
+
}>;
|
|
138
|
+
/** Optional so a hand-built auth client need not implement it. */
|
|
139
|
+
confirmEmailChange?(token: string): Promise<{
|
|
140
|
+
email: string;
|
|
141
|
+
removedProviders: string[];
|
|
142
|
+
}>;
|
|
82
143
|
updateUser(updates: {
|
|
83
144
|
displayName?: string;
|
|
84
145
|
photoURL?: string;
|
|
@@ -86,6 +147,22 @@ export interface ClientAuth {
|
|
|
86
147
|
getSessions(): Promise<DeviceSession[]>;
|
|
87
148
|
revokeSession(sessionId: string): Promise<unknown>;
|
|
88
149
|
revokeAllSessions(): Promise<unknown>;
|
|
150
|
+
/**
|
|
151
|
+
* The challenge that finishes a sign-in refused with `MFA_REQUIRED`: both
|
|
152
|
+
* requests carry that refusal's `mfaToken`, and a verified one signs the
|
|
153
|
+
* user in. Optional so a hand-built auth client need not implement it;
|
|
154
|
+
* without it an account with a second factor cannot sign in here.
|
|
155
|
+
*/
|
|
156
|
+
mfa?: {
|
|
157
|
+
challenge(factorId: string, options: {
|
|
158
|
+
mfaToken: string;
|
|
159
|
+
}): Promise<{
|
|
160
|
+
challengeId: string;
|
|
161
|
+
}>;
|
|
162
|
+
verifyChallenge(challengeId: string, code: string, options: {
|
|
163
|
+
mfaToken: string;
|
|
164
|
+
}): Promise<unknown>;
|
|
165
|
+
};
|
|
89
166
|
}
|
|
90
167
|
/**
|
|
91
168
|
* Props for useRebaseAuthController hook
|
|
@@ -101,6 +178,12 @@ export interface RebaseAuthControllerProps {
|
|
|
101
178
|
setAuthTokenGetter: (getter: () => Promise<string | null>) => void;
|
|
102
179
|
};
|
|
103
180
|
auth?: ClientAuth;
|
|
181
|
+
/** Read for the scopes the signed-in user holds (`GET /auth/scopes`). */
|
|
182
|
+
personalKeys?: {
|
|
183
|
+
listScopes(): Promise<{
|
|
184
|
+
held: string[];
|
|
185
|
+
}>;
|
|
186
|
+
};
|
|
104
187
|
};
|
|
105
188
|
/** Google OAuth client ID (optional, enables Google login) */
|
|
106
189
|
googleClientId?: string;
|
|
@@ -16,6 +16,7 @@ import type { EntityDisplayRole } from "@rebasepro/cms-types";
|
|
|
16
16
|
export type EntityDisplayKey = string;
|
|
17
17
|
/** The identity of one role of one record, as a cache key. */
|
|
18
18
|
export declare function entityDisplayKey(path: string, entityId: string | number | undefined, role: EntityDisplayRole): EntityDisplayKey;
|
|
19
|
+
export declare function getSharedEntityDisplayCache(): EntityDisplayCache;
|
|
19
20
|
export declare class EntityDisplayCache {
|
|
20
21
|
private readonly entries;
|
|
21
22
|
private readonly listeners;
|
|
@@ -55,3 +55,21 @@ export interface ResolveFilterOperatorsParams {
|
|
|
55
55
|
* @group Models
|
|
56
56
|
*/
|
|
57
57
|
export declare function resolveFilterOperators({ property, isArray, engine }: ResolveFilterOperatorsParams): WhereFilterOp[];
|
|
58
|
+
/**
|
|
59
|
+
* Whether a property can be filtered at all — the one answer the table header
|
|
60
|
+
* and the filters dialog both give.
|
|
61
|
+
*
|
|
62
|
+
* They used to answer it with a predicate each, and disagreed: the header
|
|
63
|
+
* allowed an array only of strings, numbers, dates, references and relations;
|
|
64
|
+
* the dialog allowed an array of anything, and listed an array of maps with an
|
|
65
|
+
* empty control. The header, for its part, ignored both the developer's
|
|
66
|
+
* `filterOperators: []` and a custom `admin.Filter`.
|
|
67
|
+
*
|
|
68
|
+
* A property with a custom filter field is filterable: the developer owns that
|
|
69
|
+
* field. Otherwise it is filterable when at least one operator survives
|
|
70
|
+
* {@link resolveFilterOperators} — for an array, on its item type. A tuple
|
|
71
|
+
* array (`of: [a, b]`) has no single item type to filter by.
|
|
72
|
+
*
|
|
73
|
+
* @group Models
|
|
74
|
+
*/
|
|
75
|
+
export declare function isPropertyFilterable(property: Property, engine?: string): boolean;
|
|
@@ -13,8 +13,14 @@ export declare function resolveCollectionPathIds(path: string, allCollections: A
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function getCollectionBySlugWithin(slugOrPath: string, collections: AdminCollection[]): AdminCollection | undefined;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Every leading run of whole segments of a path, longest first: the
|
|
17
|
+
* candidates for the collection the path starts with.
|
|
18
|
+
* "sites/es/locales" => ["sites/es/locales", "sites/es", "sites"]
|
|
19
|
+
*
|
|
20
|
+
* Every length, not only the odd ones. A slug may contain slashes, so
|
|
21
|
+
* `content/podcasts/abc123/episodes` starts with the two-segment collection
|
|
22
|
+
* `content/podcasts`, and a path's own segment count says nothing about where
|
|
23
|
+
* its first collection ends.
|
|
18
24
|
* @param subpaths
|
|
19
25
|
*/
|
|
20
26
|
export declare function getCollectionPathsCombinations(subpaths: string[]): string[];
|
|
@@ -3,23 +3,20 @@
|
|
|
3
3
|
*
|
|
4
4
|
* These read a property's admin block — `readOnly`, `disabled.hidden`, the
|
|
5
5
|
* declarative `conditions` — and lived in `@rebasepro/common`, which is on the
|
|
6
|
-
* backend's dependency path. Nothing in core ever called them: `isReadOnly
|
|
7
|
-
* `isHidden`
|
|
8
|
-
* production caller at all.
|
|
6
|
+
* backend's dependency path. Nothing in core ever called them: `isReadOnly`,
|
|
7
|
+
* `isHidden` and `applyPropertyConditions` are the panel's alone.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* `applyPropertyConditions` is called by the record form, through
|
|
10
|
+
* `resolveFormProperty`, both where a field is laid out and where the form is
|
|
11
|
+
* judged on save — so a rule the collection editor writes hides, disables and
|
|
12
|
+
* requires what it says it does, and a field it hides is not held against the
|
|
13
|
+
* save. Until that was wired, the evaluator was reached only from its own tests.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* deliberate, not an oversight: it is the difference between a condition that
|
|
22
|
-
* needs an entity to be evaluated against and one that does not.
|
|
15
|
+
* A condition may also be a literal: `hidden`/`readOnly`/`disabled` stated as a
|
|
16
|
+
* plain boolean rather than as a rule. A literal needs no context, so
|
|
17
|
+
* `isHidden`/`isReadOnly`/`isDisabled` answer it directly, and those three
|
|
18
|
+
* gates are consulted everywhere a field is laid out — including places with
|
|
19
|
+
* no entity to evaluate a rule against.
|
|
23
20
|
*/
|
|
24
21
|
import type { ConditionContext, Property } from "@rebasepro/types";
|
|
25
22
|
export declare function isReadOnly(property: Property): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { RealtimeConnectionState } from "@rebasepro/types";
|
|
3
|
+
/**
|
|
4
|
+
* The realtime connection's state, re-rendering on every change.
|
|
5
|
+
*
|
|
6
|
+
* `undefined` when there is no client, or it was built without realtime —
|
|
7
|
+
* there is no connection to describe.
|
|
8
|
+
*
|
|
9
|
+
* @group Hooks and utilities
|
|
10
|
+
*/
|
|
11
|
+
export declare function useRealtimeConnectionState(): RealtimeConnectionState | undefined;
|
|
12
|
+
export interface ConnectionLostBannerProps {
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Says that live updates have stopped, while they have.
|
|
17
|
+
*
|
|
18
|
+
* Shown once the realtime client reports the connection `disconnected`: the
|
|
19
|
+
* socket has been down for longer than a blip, so tables, boards and open
|
|
20
|
+
* records are showing what they last received and nothing else will arrive
|
|
21
|
+
* until it is back. They keep their data rather than turning into errors — this
|
|
22
|
+
* banner is how the user learns it may be out of date. It disappears by itself
|
|
23
|
+
* when the connection returns, and every live view then refreshes.
|
|
24
|
+
*
|
|
25
|
+
* A brief drop (`reconnecting`) shows nothing: a deploy blips every socket for
|
|
26
|
+
* a second or two, and a banner that flashes on each one teaches people to
|
|
27
|
+
* ignore it.
|
|
28
|
+
*
|
|
29
|
+
* @group Components
|
|
30
|
+
*/
|
|
31
|
+
export declare function ConnectionLostBanner({ className }: ConnectionLostBannerProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The links the backend puts in its auth emails, as the login view reads them.
|
|
3
|
+
*
|
|
4
|
+
* The server builds them from the configured frontend URL:
|
|
5
|
+
* `<frontend>/reset-password?token=…` for a forgotten password, an admin's
|
|
6
|
+
* "send reset email" and every invitation, `<frontend>/verify-email?token=…`
|
|
7
|
+
* to confirm an address, `<frontend>/confirm-email-change?token=…` to move an
|
|
8
|
+
* account to a new one, and `<frontend>/auth/magic-link?token=…` to sign in.
|
|
9
|
+
* The frontend URL may carry a base path (`/admin`), so the action is read off
|
|
10
|
+
* the *end* of the path, not the whole path.
|
|
11
|
+
*/
|
|
12
|
+
export type EmailLinkAction = {
|
|
13
|
+
kind: "reset-password";
|
|
14
|
+
token: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "verify-email";
|
|
17
|
+
token: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "confirm-email-change";
|
|
20
|
+
token: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "magic-link";
|
|
23
|
+
token: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function readEmailLinkAction(location: {
|
|
26
|
+
pathname: string;
|
|
27
|
+
search: string;
|
|
28
|
+
}): EmailLinkAction | null;
|
|
29
|
+
/**
|
|
30
|
+
* Where the app lives: the link's own address without the action's path and
|
|
31
|
+
* its token. `/admin/reset-password?token=…` becomes `/admin/`, and
|
|
32
|
+
* `/admin/auth/magic-link?token=…` too.
|
|
33
|
+
*/
|
|
34
|
+
export declare function appAddressOfEmailLink(location: {
|
|
35
|
+
origin: string;
|
|
36
|
+
pathname: string;
|
|
37
|
+
}): string;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AuthControllerExtended, MfaFactorSummary } from "@rebasepro/cms-types";
|
|
2
|
+
/**
|
|
3
|
+
* A sign-in that is waiting for its second factor.
|
|
4
|
+
*
|
|
5
|
+
* The server refuses the first factor of an account with MFA enrolled with
|
|
6
|
+
* `401 MFA_REQUIRED`, and puts in `details` what the second step needs: a
|
|
7
|
+
* pending token, good for five minutes and for the two challenge routes only,
|
|
8
|
+
* and the factors the account can answer with.
|
|
9
|
+
*/
|
|
10
|
+
export interface PendingMfaSignIn {
|
|
11
|
+
mfaToken: string;
|
|
12
|
+
factors: MfaFactorSummary[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The pending sign-in an `MFA_REQUIRED` refusal describes, or `null` for any
|
|
16
|
+
* other error — including an `MFA_REQUIRED` without the token or a factor to
|
|
17
|
+
* answer with, which no code step could finish.
|
|
18
|
+
*/
|
|
19
|
+
export declare function readMfaRequired(error: unknown): PendingMfaSignIn | null;
|
|
20
|
+
/** Whether this controller can take a sign-in through its second step. */
|
|
21
|
+
export declare function canAnswerMfa(authController: AuthControllerExtended): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* What a refused challenge means for the code step.
|
|
24
|
+
*
|
|
25
|
+
* - `invalid-code`: the code was wrong; the same challenge takes another.
|
|
26
|
+
* - `exhausted`: the challenge has had its five attempts; the next code needs
|
|
27
|
+
* a new one.
|
|
28
|
+
* - `expired`: the sign-in itself is over. The pending token lasts five
|
|
29
|
+
* minutes from the password, and once it has lapsed both routes refuse the
|
|
30
|
+
* caller as unauthenticated; a challenge that lapsed, or a sign-in revoked
|
|
31
|
+
* meanwhile, ends the same way. Only a new sign-in gets a new token.
|
|
32
|
+
*/
|
|
33
|
+
export declare function classifyMfaRefusal(error: unknown): "invalid-code" | "exhausted" | "expired" | "other";
|
|
@@ -28,6 +28,19 @@ export interface UserSelectPopoverProps {
|
|
|
28
28
|
* Whether users are currently loading.
|
|
29
29
|
*/
|
|
30
30
|
loading?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Called with the search text each time it changes (debounced), for a
|
|
33
|
+
* parent that searches its users on a server rather than passing them all
|
|
34
|
+
* in `users`. The list is still filtered here by the same text.
|
|
35
|
+
*/
|
|
36
|
+
onSearchTextChange?: (searchText: string) => void;
|
|
37
|
+
/**
|
|
38
|
+
* For a parent that lists only the first page of a server search: how many
|
|
39
|
+
* users that search matched in all. Leave it out when `users` holds every
|
|
40
|
+
* match. When set, the header counts the listed users against it and a
|
|
41
|
+
* notice says the others are not listed.
|
|
42
|
+
*/
|
|
43
|
+
totalCount?: number;
|
|
31
44
|
/**
|
|
32
45
|
* The current user (displayed as the "self" option at the top).
|
|
33
46
|
*/
|
|
@@ -60,4 +73,4 @@ export interface UserSelectPopoverProps {
|
|
|
60
73
|
*
|
|
61
74
|
* @group Components
|
|
62
75
|
*/
|
|
63
|
-
export declare function UserSelectPopover({ selectedUser, onUserSelected, users, loading, currentUser, defaultLabel, renderLimit, className, size }: UserSelectPopoverProps): React.JSX.Element;
|
|
76
|
+
export declare function UserSelectPopover({ selectedUser, onUserSelected, users, loading, onSearchTextChange, totalCount, currentUser, defaultLabel, renderLimit, className, size }: UserSelectPopoverProps): React.JSX.Element;
|
|
@@ -42,4 +42,4 @@ export type DataTableControllerProps<M extends Record<string, any> = any> = {
|
|
|
42
42
|
* @param fixedFilterFromProps
|
|
43
43
|
* @param updateUrl
|
|
44
44
|
*/
|
|
45
|
-
export declare function useDataTableController<M extends Record<string, any> = any, USER extends User = User>({ path, collection, scrollRestoration, entitiesDisplayedFirst, lastDeleteTimestamp
|
|
45
|
+
export declare function useDataTableController<M extends Record<string, any> = any, USER extends User = User>({ path, collection, scrollRestoration, entitiesDisplayedFirst, lastDeleteTimestamp, fixedFilter: fixedFilterFromProps, updateUrl }: DataTableControllerProps<M>): EntityTableController<M>;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { Entity, FilterValues } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Forget every collection's scroll position and the rows kept with it.
|
|
4
|
+
*
|
|
5
|
+
* The rows are what the signed-in user was allowed to read, and a table seeds
|
|
6
|
+
* its first render from them, so they must not outlive that user's session.
|
|
7
|
+
*/
|
|
8
|
+
export declare function clearCollectionScrollCache(): void;
|
|
2
9
|
export type ScrollRestorationController = {
|
|
3
10
|
getCollectionScroll: (path: string, filters?: FilterValues<any>) => {
|
|
4
11
|
scrollOffset: number;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the settings screen and the link's screen say about a refused address
|
|
3
|
+
* change: the codes a person can act on get the panel's own, translated words,
|
|
4
|
+
* and anything else keeps the server's message.
|
|
5
|
+
*/
|
|
6
|
+
export declare function emailChangeErrorMessage(error: unknown, t: (key: string) => string): string;
|
|
@@ -5,6 +5,12 @@ import React from "react";
|
|
|
5
5
|
export type CollectionResolver = (slug: string) => {
|
|
6
6
|
properties?: Record<string, unknown>;
|
|
7
7
|
} | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Translate the path a collection is addressed by into the path its driver
|
|
10
|
+
* stores it under — `fs_diagnosis` into `diagnosis`, for a Firestore collection
|
|
11
|
+
* that declares `path: "diagnosis"`. See `EntityDataOptions.resolveDataPath`.
|
|
12
|
+
*/
|
|
13
|
+
export type DataPathResolver = (path: string) => string | undefined;
|
|
8
14
|
/**
|
|
9
15
|
* Lets the layer that owns the collections hand a resolver *up* to the data
|
|
10
16
|
* layer, which needs a collection's primary keys to give its rows an address.
|
|
@@ -15,9 +21,13 @@ export type CollectionResolver = (slug: string) => {
|
|
|
15
21
|
* prop without giving every headless BaaS app a collections argument it has no
|
|
16
22
|
* use for.
|
|
17
23
|
*
|
|
24
|
+
* The same layer knows where each collection is stored, so it hands up the
|
|
25
|
+
* data-path translation alongside: a direct driver has to be given a
|
|
26
|
+
* collection's declared `path`, and only the registry knows it.
|
|
27
|
+
*
|
|
18
28
|
* Registration is a ref write, so it is safe to call during render — and it has
|
|
19
29
|
* to be. The views that fetch rows are *below* the registrar, and child effects
|
|
20
30
|
* run before parent effects, so registering in an effect would bind after the
|
|
21
31
|
* first page of rows had already been converted.
|
|
22
32
|
*/
|
|
23
|
-
export declare const CollectionResolverRegistrationContext: React.Context<(resolver: CollectionResolver | undefined) => void>;
|
|
33
|
+
export declare const CollectionResolverRegistrationContext: React.Context<(resolver: CollectionResolver | undefined, resolveDataPath?: DataPathResolver) => void>;
|
|
@@ -2,16 +2,24 @@ import type { RebasePlugin, RebaseContext } from "@rebasepro/cms-types";
|
|
|
2
2
|
/**
|
|
3
3
|
* Render-less component that manages plugin lifecycle hooks.
|
|
4
4
|
*
|
|
5
|
-
* - Calls `lifecycle.onMount(context)`
|
|
6
|
-
* - Calls `lifecycle.
|
|
7
|
-
* -
|
|
5
|
+
* - Calls `lifecycle.onMount(context)` once, the first time auth is ready.
|
|
6
|
+
* - Calls `lifecycle.onAuthStateChange(user)` on every change of user after
|
|
7
|
+
* that — `null` on sign-out, the new user on the next sign-in.
|
|
8
|
+
* - Calls `lifecycle.onUnmount()` when the Rebase tree unmounts.
|
|
9
|
+
*
|
|
10
|
+
* Mounted for as long as there are plugins, signed in or not. It used to be
|
|
11
|
+
* mounted only while a user was signed in, so a sign-out unmounted it and the
|
|
12
|
+
* next sign-in mounted a fresh one — `onAuthStateChange` could never see the
|
|
13
|
+
* user change, and never fired.
|
|
8
14
|
*
|
|
9
15
|
* Mount this component inside the Rebase tree, below PluginProviderStack,
|
|
10
16
|
* so that the RebaseContext is fully available.
|
|
11
17
|
*
|
|
12
18
|
* @internal
|
|
13
19
|
*/
|
|
14
|
-
export declare function PluginLifecycleManager({ plugins, context }: {
|
|
20
|
+
export declare function PluginLifecycleManager({ plugins, context, authReady }: {
|
|
15
21
|
plugins: RebasePlugin[];
|
|
16
22
|
context: RebaseContext;
|
|
23
|
+
/** Signed in, or sign-in skipped: what `onMount` waits for. */
|
|
24
|
+
authReady: boolean;
|
|
17
25
|
}): null;
|