@rebasepro/admin-types 0.10.1-canary.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/LICENSE +21 -0
- package/dist/admin_collection.d.ts +464 -0
- package/dist/augment.d.ts +66 -0
- package/dist/collections.d.ts +226 -0
- package/dist/controllers/analytics_controller.d.ts +7 -0
- package/dist/controllers/auth.d.ts +110 -0
- package/dist/controllers/customization_controller.d.ts +61 -0
- package/dist/controllers/dialogs_controller.d.ts +36 -0
- package/dist/controllers/index.d.ts +10 -0
- package/dist/controllers/local_config_persistence.d.ts +20 -0
- package/dist/controllers/navigation.d.ts +225 -0
- package/dist/controllers/registry.d.ts +80 -0
- package/dist/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/controllers/side_panel_controller.d.ts +97 -0
- package/dist/controllers/snackbar.d.ts +24 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.es.js +92 -0
- package/dist/index.es.js.map +1 -0
- package/dist/react_component_ref.d.ts +43 -0
- package/dist/rebase_context.d.ts +68 -0
- package/dist/types/breadcrumbs.d.ts +26 -0
- package/dist/types/builders.d.ts +15 -0
- package/dist/types/component_overrides.d.ts +196 -0
- package/dist/types/entity_actions.d.ts +105 -0
- package/dist/types/entity_link_builder.d.ts +7 -0
- package/dist/types/entity_views.d.ts +95 -0
- package/dist/types/export_import.d.ts +21 -0
- package/dist/types/formex.d.ts +40 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/locales.d.ts +4 -0
- package/dist/types/modify_collections.d.ts +5 -0
- package/dist/types/plugins.d.ts +277 -0
- package/dist/types/property_config.d.ts +74 -0
- package/dist/types/property_options.d.ts +154 -0
- package/dist/types/slots.d.ts +263 -0
- package/dist/types/translations.d.ts +915 -0
- package/dist/types/user_management_delegate.d.ts +22 -0
- package/package.json +103 -0
- package/src/admin_collection.ts +582 -0
- package/src/augment.ts +60 -0
- package/src/collections.ts +256 -0
- package/src/controllers/analytics_controller.tsx +57 -0
- package/src/controllers/auth.ts +121 -0
- package/src/controllers/customization_controller.tsx +72 -0
- package/src/controllers/dialogs_controller.tsx +37 -0
- package/src/controllers/index.ts +10 -0
- package/src/controllers/local_config_persistence.tsx +22 -0
- package/src/controllers/navigation.ts +264 -0
- package/src/controllers/registry.ts +96 -0
- package/src/controllers/side_dialogs_controller.tsx +82 -0
- package/src/controllers/side_panel_controller.tsx +112 -0
- package/src/controllers/snackbar.ts +29 -0
- package/src/index.ts +20 -0
- package/src/react_component_ref.ts +52 -0
- package/src/rebase_context.ts +81 -0
- package/src/types/breadcrumbs.ts +27 -0
- package/src/types/builders.ts +18 -0
- package/src/types/component_overrides.ts +244 -0
- package/src/types/entity_actions.tsx +127 -0
- package/src/types/entity_link_builder.ts +8 -0
- package/src/types/entity_views.tsx +114 -0
- package/src/types/export_import.ts +26 -0
- package/src/types/formex.ts +45 -0
- package/src/types/index.ts +15 -0
- package/src/types/locales.ts +81 -0
- package/src/types/modify_collections.tsx +6 -0
- package/src/types/plugins.tsx +346 -0
- package/src/types/property_config.tsx +95 -0
- package/src/types/property_options.ts +169 -0
- package/src/types/slots.tsx +309 -0
- package/src/types/translations.ts +1026 -0
- package/src/types/user_management_delegate.ts +23 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The typed admin block, and the type you author a collection against.
|
|
3
|
+
*
|
|
4
|
+
* A collection is one file. Schema, security rules and callbacks sit at the top
|
|
5
|
+
* level, where the backend reads them; everything the admin panel renders sits
|
|
6
|
+
* under `admin`. `@rebasepro/types` does not declare that field at all — naming a
|
|
7
|
+
* kanban column definition would drag `React.ReactNode` back into the BaaS
|
|
8
|
+
* contract, and a server has no use for one. `augment.ts` in this package declares
|
|
9
|
+
* it, by declaration merging, onto core's `CollectionConfig`. So this is the other
|
|
10
|
+
* side of that boundary: the 38 fields, fully typed, in the package where React
|
|
11
|
+
* exists, and reachable only by a program that has opted in.
|
|
12
|
+
*
|
|
13
|
+
* Each field is declared exactly once, here. Core does not carry a React-free
|
|
14
|
+
* skeleton of the same shape; two definitions that agree only by luck is the
|
|
15
|
+
* `WhereFilterOp` mistake, and this block is far bigger than one union.
|
|
16
|
+
*/
|
|
17
|
+
import type React from "react";
|
|
18
|
+
import type {
|
|
19
|
+
CollectionConfig,
|
|
20
|
+
ComponentRef,
|
|
21
|
+
FilterPreset,
|
|
22
|
+
FilterValues,
|
|
23
|
+
FirebaseCollectionConfig,
|
|
24
|
+
FirebaseProperties,
|
|
25
|
+
InferEntityType,
|
|
26
|
+
MongoDBCollectionConfig,
|
|
27
|
+
MongoProperties,
|
|
28
|
+
OrderByTuple,
|
|
29
|
+
PostgresCollectionConfig,
|
|
30
|
+
PostgresProperties,
|
|
31
|
+
User
|
|
32
|
+
} from "@rebasepro/types";
|
|
33
|
+
// A value, not a type: the runtime list core owns.
|
|
34
|
+
import { ADMIN_COLLECTION_KEYS as CORE_ADMIN_COLLECTION_KEYS } from "@rebasepro/types";
|
|
35
|
+
|
|
36
|
+
import type {
|
|
37
|
+
AdditionalFieldDelegate,
|
|
38
|
+
CollectionActionsProps,
|
|
39
|
+
CollectionSize,
|
|
40
|
+
DefaultSelectedViewBuilder,
|
|
41
|
+
KanbanConfig,
|
|
42
|
+
SelectionController,
|
|
43
|
+
ViewMode
|
|
44
|
+
} from "./collections";
|
|
45
|
+
import type { EntityCustomView, FormViewConfig } from "./types/entity_views";
|
|
46
|
+
import type { EntityAction } from "./types/entity_actions";
|
|
47
|
+
import type { ExportConfig } from "./types/export_import";
|
|
48
|
+
import type { CollectionComponentOverrideMap } from "./types/component_overrides";
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Admin-panel presentation and behaviour for a collection.
|
|
52
|
+
*
|
|
53
|
+
* A `type` rather than an `interface`, and that is load-bearing: TypeScript gives
|
|
54
|
+
* an implicit index signature to an object *type alias* but not to an interface.
|
|
55
|
+
* `toAdminCollectionConfig` has to widen a collection carrying this block to
|
|
56
|
+
* `Record<string, unknown>` in order to move the flattened keys back under
|
|
57
|
+
* `admin`, and as an interface that conversion is an error (TS2352, "index
|
|
58
|
+
* signature for type 'string' is missing"). Flipping it and running
|
|
59
|
+
* `pnpm typecheck` reproduces that in one line.
|
|
60
|
+
*
|
|
61
|
+
* Declaration merging is not wanted here anyway; a plugin adding fields to the
|
|
62
|
+
* block would have nothing reading them.
|
|
63
|
+
*
|
|
64
|
+
* @group Models
|
|
65
|
+
*/
|
|
66
|
+
export type AdminCollectionOptions<
|
|
67
|
+
M extends Record<string, unknown> = Record<string, unknown>,
|
|
68
|
+
USER extends User = User
|
|
69
|
+
> = {
|
|
70
|
+
/**
|
|
71
|
+
* Icon for the navigation sidebar or cards.
|
|
72
|
+
*
|
|
73
|
+
* Either a Lucide icon name (`"FileText"`, `"ShoppingCart"`) or a rendered
|
|
74
|
+
* element. Prefer the name: it survives serialization, so the collection file
|
|
75
|
+
* stays loadable by the backend and by `rebase generate-sdk`, and it is what
|
|
76
|
+
* the schema editor writes back.
|
|
77
|
+
*/
|
|
78
|
+
icon?: string | React.ReactNode;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Navigation group for this collection.
|
|
82
|
+
* Collections sharing the same group name will be visually grouped
|
|
83
|
+
* together in the drawer and home page. If not set, the collection
|
|
84
|
+
* falls into the default "Views" group.
|
|
85
|
+
*/
|
|
86
|
+
group?: string;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Array of entity views that this collection has.
|
|
90
|
+
* Can be an array of `EntityCustomView` or a string representing the key of a global `EntityCustomView`.
|
|
91
|
+
*/
|
|
92
|
+
entityViews?: (string | EntityCustomView<Record<string, unknown>>)[];
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Default preview properties displayed when this collection is referenced to.
|
|
96
|
+
*/
|
|
97
|
+
previewProperties?: string[];
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Properties to display as columns in the list view.
|
|
101
|
+
* If not specified, the list view uses a smart default (Title, Status, Date).
|
|
102
|
+
*/
|
|
103
|
+
listProperties?: string[];
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Title property of the entity. This is the property that will be used
|
|
107
|
+
* as the title in entity related views and references.
|
|
108
|
+
* If not specified, the first property simple text property will be used.
|
|
109
|
+
*/
|
|
110
|
+
readonly titleProperty?: Extract<keyof M, string> | (string & {});
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* When editing a entity, you can choose to open the entity in a side dialog
|
|
114
|
+
* or in a full screen dialog. Defaults to `full_screen`.
|
|
115
|
+
*/
|
|
116
|
+
openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Controls what happens when a user clicks on a entity in the collection view.
|
|
120
|
+
* - `"edit"` (default): Opens the entity in the edit form.
|
|
121
|
+
* - `"view"`: Opens a read-only detail view with an "Edit" button.
|
|
122
|
+
*/
|
|
123
|
+
defaultEntityAction?: "view" | "edit";
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Replace the default entity form with a custom component.
|
|
127
|
+
* The Builder receives the same props as entity view tabs
|
|
128
|
+
* (entity, formContext, collection, etc.) and has full control over the UI.
|
|
129
|
+
*
|
|
130
|
+
* Works in both edit mode and read-only mode (when `defaultEntityAction`
|
|
131
|
+
* is `"view"`). In read-only mode, `formContext.readOnly` will be `true`.
|
|
132
|
+
*/
|
|
133
|
+
formView?: FormViewConfig;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Prevent default actions from being displayed or executed on this collection.
|
|
137
|
+
*/
|
|
138
|
+
disableDefaultActions?: ("edit" | "copy" | "delete")[];
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Order in which the properties are displayed.
|
|
142
|
+
* If you are specifying your collection as code, the order is the same as the
|
|
143
|
+
* one you define in `properties`. Additional columns are added at the
|
|
144
|
+
* end of the list, if the order is not specified.
|
|
145
|
+
*
|
|
146
|
+
* You can use this prop to hide some properties from the table view.
|
|
147
|
+
* Note that if you set this prop, other ways to hide fields, like
|
|
148
|
+
* `hidden` in the property definition, will be ignored.
|
|
149
|
+
* `propertiesOrder` has precedence over `hidden`.
|
|
150
|
+
*
|
|
151
|
+
* Supported entry formats:
|
|
152
|
+
* - For properties, use the property key.
|
|
153
|
+
* - For additional fields, use the field key.
|
|
154
|
+
* - Child collections (Firestore subcollections, or Postgres relations
|
|
155
|
+
* with `many` cardinality) each get a column with id
|
|
156
|
+
* `subcollection:<slug>`, e.g. `subcollection:orders`.
|
|
157
|
+
*/
|
|
158
|
+
propertiesOrder?: (Extract<keyof M, string> | (string & {}) | string | `subcollection:${string}`)[];
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* If enabled, content is loaded in batches. If `false` all entities in the
|
|
162
|
+
* collection are loaded. This means that when reaching the end of the
|
|
163
|
+
* collection, the CMS will load more entities.
|
|
164
|
+
* You can specify a number to specify the pagination size (50 by default)
|
|
165
|
+
* Defaults to `true`
|
|
166
|
+
*/
|
|
167
|
+
pagination?: boolean | number;
|
|
168
|
+
|
|
169
|
+
selectionEnabled?: boolean;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Pass your own selection controller if you want to control selected
|
|
173
|
+
* entities externally.
|
|
174
|
+
* @see useSelectionController
|
|
175
|
+
*/
|
|
176
|
+
selectionController?: SelectionController<M>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Force a filter in this view. If applied, the rest of the filters will
|
|
180
|
+
* be disabled. Filters applied with this prop cannot be changed.
|
|
181
|
+
* e.g. `fixedFilter: { age: [">", 18] }`
|
|
182
|
+
* e.g. `fixedFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
183
|
+
*/
|
|
184
|
+
readonly fixedFilter?: FilterValues<Extract<keyof M, string> | (string & {})>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Initial filters applied to the collection this collection is related to.
|
|
188
|
+
* Defaults to none. Filters applied with this prop can be changed.
|
|
189
|
+
* e.g. `defaultFilter: { age: [">", 18] }`
|
|
190
|
+
* e.g. `defaultFilter: { related_user: ["==", new EntityReference("sdc43dsw2", "users")] }`
|
|
191
|
+
*/
|
|
192
|
+
readonly defaultFilter?: FilterValues<Extract<keyof M, string> | (string & {})>; // setting FilterValues<M> can break defining collections by code
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Pre-defined filter presets that appear as quick-access options in the
|
|
196
|
+
* collection toolbar. Each preset applies a set of filters (and
|
|
197
|
+
* optionally a sort order) with a single click.
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* filterPresets: [
|
|
201
|
+
* {
|
|
202
|
+
* label: "Shipped this month",
|
|
203
|
+
* filterValues: {
|
|
204
|
+
* status: ["==", "shipped"],
|
|
205
|
+
* order_date: [">=", new Date(Date.now() - 30 * 86400000)]
|
|
206
|
+
* }
|
|
207
|
+
* }
|
|
208
|
+
* ]
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
readonly filterPresets?: FilterPreset<Extract<keyof M, string> | (string & {})>[];
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Default sort applied to this collection.
|
|
215
|
+
* When setting this prop, entities will have a default order
|
|
216
|
+
* applied in the collection.
|
|
217
|
+
* e.g. `sort: ["order", "asc"]`
|
|
218
|
+
*/
|
|
219
|
+
readonly sort?: OrderByTuple<Extract<keyof M, string> | (string & {})>;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* You can add additional fields to the collection view by implementing
|
|
223
|
+
* an additional field delegate.
|
|
224
|
+
*/
|
|
225
|
+
readonly additionalFields?: AdditionalFieldDelegate<M, USER>[];
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Default size of the rendered collection
|
|
229
|
+
*/
|
|
230
|
+
defaultSize?: CollectionSize;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Can the elements in this collection be edited inline in the collection
|
|
234
|
+
* view. Even when inline editing is disabled, entities can still be
|
|
235
|
+
* edited in the side panel (subject to `securityRules`).
|
|
236
|
+
*/
|
|
237
|
+
inlineEditing?: boolean;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Should this collection be hidden from the main navigation panel, if
|
|
241
|
+
* it is at the root level, or in the entity side panel if it's a
|
|
242
|
+
* subcollection.
|
|
243
|
+
* It will still be accessible if you reach the specified path.
|
|
244
|
+
* You can also use this collection as a reference target.
|
|
245
|
+
*/
|
|
246
|
+
hideFromNavigation?: boolean;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* If you want to open custom views or subcollections by default when opening the edit
|
|
250
|
+
* view of a entity, you can specify the path to the view here.
|
|
251
|
+
* The path is relative to the current collection. For example if you have a collection
|
|
252
|
+
* that has a custom view as well as a subcollection that refers to another entity, you can
|
|
253
|
+
* either specify the path to the custom view or the path to the subcollection.
|
|
254
|
+
*/
|
|
255
|
+
defaultSelectedView?: string | DefaultSelectedViewBuilder;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Should the ID of this collection be hidden from the form view.
|
|
259
|
+
*/
|
|
260
|
+
hideIdFromForm?: boolean;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Should the ID of this collection be hidden from the grid view.
|
|
264
|
+
*/
|
|
265
|
+
hideIdFromCollection?: boolean;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* If set to true, the form will be auto-saved when the user changes
|
|
269
|
+
* the value of a field.
|
|
270
|
+
* Defaults to false.
|
|
271
|
+
* When a new entity is created, this property can be updated to generated a new ID
|
|
272
|
+
*/
|
|
273
|
+
formAutoSave?: boolean;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
*
|
|
277
|
+
*/
|
|
278
|
+
exportable?: boolean | ExportConfig<USER>;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Width of the side dialog (in pixels) when opening a entity in this collection.
|
|
282
|
+
*/
|
|
283
|
+
sideDialogWidth?: number | string;
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* If set to true, the default values of the properties will be applied
|
|
287
|
+
* to the entity every time the entity is updated (not only when created).
|
|
288
|
+
* Defaults to false.
|
|
289
|
+
*/
|
|
290
|
+
alwaysApplyDefaultValues?: boolean;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* If set to true, a tab including the JSON representation of the entity will be included.
|
|
294
|
+
*/
|
|
295
|
+
includeJsonView?: boolean;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Should local changes be backed up in local storage, to prevent data loss on
|
|
299
|
+
* accidental navigations.
|
|
300
|
+
* - `manual_apply`: When the user navigates back to a entity with local changes,
|
|
301
|
+
* they will be prompted to restore the changes.
|
|
302
|
+
* - `auto_apply`: When the user navigates back to a entity with local changes,
|
|
303
|
+
* the changes will be automatically applied.
|
|
304
|
+
* - `false`: Local changes will not be backed up.
|
|
305
|
+
* Defaults to `manual_apply`.
|
|
306
|
+
*/
|
|
307
|
+
localChangesBackup?: "manual_apply" | "auto_apply" | false;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Default view mode for displaying this collection.
|
|
311
|
+
* - "table": Display entities in a table with inline editing (default)
|
|
312
|
+
* - "cards": Display entities as a grid of cards with thumbnails
|
|
313
|
+
* - "kanban": Display entities in a Kanban board grouped by a property
|
|
314
|
+
* Defaults to "table".
|
|
315
|
+
*/
|
|
316
|
+
defaultViewMode?: ViewMode;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Which view modes are available for this collection.
|
|
320
|
+
* Possible values: "table", "cards", "kanban".
|
|
321
|
+
* Defaults to all three: ["table", "cards", "kanban"].
|
|
322
|
+
* Note: "kanban" will only be available if the collection has at least
|
|
323
|
+
* one string property with `enum` defined, regardless of this setting.
|
|
324
|
+
*/
|
|
325
|
+
enabledViews?: ViewMode[];
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Configuration for Kanban board view mode.
|
|
329
|
+
* When set, the Kanban view mode becomes available.
|
|
330
|
+
*/
|
|
331
|
+
kanban?: KanbanConfig<M>;
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Property key to use for ordering items.
|
|
335
|
+
* Must reference a string/text property. When items are reordered,
|
|
336
|
+
* this property will be updated with lexicographic sort keys
|
|
337
|
+
* (e.g. "a0", "a1", "a0V") using string-based fractional indexing.
|
|
338
|
+
* Used by Kanban view for ordering within columns
|
|
339
|
+
* and can be used for general ordering purposes.
|
|
340
|
+
*/
|
|
341
|
+
readonly orderProperty?: Extract<keyof M, string> | (string & {});
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Actions that can be performed on the entities in this collection.
|
|
345
|
+
*/
|
|
346
|
+
entityActions?: EntityAction<M, USER>[];
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Builder for the collection actions rendered in the toolbar
|
|
350
|
+
*/
|
|
351
|
+
Actions?: ComponentRef<CollectionActionsProps>[];
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Collection-scoped component overrides. These take precedence over
|
|
355
|
+
* global overrides set on `<Rebase>`, but only within this collection's
|
|
356
|
+
* views (entity form, detail view, table, empty state, etc.).
|
|
357
|
+
*
|
|
358
|
+
* Only collection-scoped components (like `Entity.Form`, `Collection.EmptyState`,
|
|
359
|
+
* `Collection.Card`, etc.) can be overridden here. App-level components
|
|
360
|
+
* (like `Shell.AppBar`, `HomePage`) can only be overridden at the `<Rebase>` level.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```tsx
|
|
364
|
+
* const productsCollection: PostgresCollectionConfig = {
|
|
365
|
+
* name: "Products",
|
|
366
|
+
* slug: "products",
|
|
367
|
+
* table: "products",
|
|
368
|
+
* components: {
|
|
369
|
+
* "Entity.Form": { Component: ProductCustomForm },
|
|
370
|
+
* "Collection.Card": { Component: ProductCard },
|
|
371
|
+
* },
|
|
372
|
+
* properties: { ... }
|
|
373
|
+
* };
|
|
374
|
+
* ```
|
|
375
|
+
*/
|
|
376
|
+
components?: CollectionComponentOverrideMap;};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* There is deliberately no `AdminCollectionConfig` here any more.
|
|
380
|
+
*
|
|
381
|
+
* It used to be `Omit<CollectionConfig, "admin"> & { admin?: AdminCollectionOptions }`,
|
|
382
|
+
* a wrapper that existed because core typed the block opaquely. Now that `augment.ts`
|
|
383
|
+
* declares `admin` directly on `BaseCollectionConfig`, `CollectionConfig` *is* the
|
|
384
|
+
* authoring type — the wrapper would be an alias of it, and a second name for one thing
|
|
385
|
+
* is what this whole refactor has been removing.
|
|
386
|
+
*
|
|
387
|
+
* A project opts its program in with one line, once:
|
|
388
|
+
*
|
|
389
|
+
* ```ts
|
|
390
|
+
* /// <reference types="@rebasepro/admin-types" />
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* after which `admin` is typed on every collection and every property. Without it,
|
|
394
|
+
* writing one is an error — which is the guarantee a BaaS install depends on.
|
|
395
|
+
*/
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Define a collection with the admin block type-checked.
|
|
399
|
+
*
|
|
400
|
+
* The same identity function as `defineCollection` in `@rebasepro/common` — which
|
|
401
|
+
* is what a BaaS or headless project uses, and where `admin` does not exist at all
|
|
402
|
+
* — with one difference: importing this one brings the augmentation with it, so
|
|
403
|
+
* `admin: { icon, listProperties, kanban }` gets completion and a typo is an
|
|
404
|
+
* error. See {@link AdminCollectionOptions}.
|
|
405
|
+
*
|
|
406
|
+
* Import it from the layer you are in. A project with an admin panel wants this
|
|
407
|
+
* one; a project without one has no `admin` block to check.
|
|
408
|
+
*
|
|
409
|
+
* `const P` captures the literal property types, which is what gives
|
|
410
|
+
* `admin.titleProperty`, `admin.sort` and `admin.propertiesOrder` completion over
|
|
411
|
+
* the collection's own property keys rather than plain `string`.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* export default defineCollection({
|
|
415
|
+
* slug: "posts",
|
|
416
|
+
* table: "posts",
|
|
417
|
+
* properties: {
|
|
418
|
+
* title: { name: "Title", type: "string" },
|
|
419
|
+
* status: { name: "Status", type: "string" }
|
|
420
|
+
* },
|
|
421
|
+
* admin: {
|
|
422
|
+
* icon: "FileText",
|
|
423
|
+
* titleProperty: "title", // completion: "title" | "status"
|
|
424
|
+
* listProperties: ["title", "status"]
|
|
425
|
+
* }
|
|
426
|
+
* });
|
|
427
|
+
*
|
|
428
|
+
* @group Builder
|
|
429
|
+
*/
|
|
430
|
+
export function defineCollection<
|
|
431
|
+
const P extends PostgresProperties,
|
|
432
|
+
USER extends User = User
|
|
433
|
+
>(
|
|
434
|
+
collection: Omit<PostgresCollectionConfig<InferEntityType<P>, USER>, "properties">
|
|
435
|
+
& { properties: P }
|
|
436
|
+
): PostgresCollectionConfig<InferEntityType<P>, USER> & { properties: P };
|
|
437
|
+
|
|
438
|
+
/** Define a Firestore-backed collection with the admin block checked. @group Builder */
|
|
439
|
+
export function defineCollection<
|
|
440
|
+
const P extends FirebaseProperties,
|
|
441
|
+
USER extends User = User
|
|
442
|
+
>(
|
|
443
|
+
collection: Omit<FirebaseCollectionConfig<InferEntityType<P>, USER>, "properties">
|
|
444
|
+
& { properties: P }
|
|
445
|
+
): FirebaseCollectionConfig<InferEntityType<P>, USER> & { properties: P };
|
|
446
|
+
|
|
447
|
+
/** Define a MongoDB-backed collection with the admin block checked. @group Builder */
|
|
448
|
+
export function defineCollection<
|
|
449
|
+
const P extends MongoProperties,
|
|
450
|
+
USER extends User = User
|
|
451
|
+
>(
|
|
452
|
+
collection: Omit<MongoDBCollectionConfig<InferEntityType<P>, USER>, "properties">
|
|
453
|
+
& { properties: P }
|
|
454
|
+
): MongoDBCollectionConfig<InferEntityType<P>, USER> & { properties: P };
|
|
455
|
+
|
|
456
|
+
/** Identity at runtime; the overloads above are the whole point. @group Builder */
|
|
457
|
+
export function defineCollection(collection: CollectionConfig): CollectionConfig {
|
|
458
|
+
return collection;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Re-exported from `@rebasepro/types`, where the list has to live: the ts-morph
|
|
463
|
+
* schema editor in `@rebasepro/server` needs it to know which keys go inside the
|
|
464
|
+
* block when it rewrites a collection file, and a core package may not import
|
|
465
|
+
* this one. The list is plain data, so core is a fine home for it.
|
|
466
|
+
*
|
|
467
|
+
* What core *cannot* do is check the list against the type. That happens here.
|
|
468
|
+
*
|
|
469
|
+
* @group Models
|
|
470
|
+
*/
|
|
471
|
+
export type { AdminCollectionKey } from "@rebasepro/types";
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Core's list, re-exported through a `satisfies` clause that is the agreement
|
|
475
|
+
* check: a key core names that is not an option here fails to compile, and
|
|
476
|
+
* `satisfies` keeps the literal tuple type rather than widening it to `string[]`.
|
|
477
|
+
*
|
|
478
|
+
* The reverse direction — an option missing from core's list — has no type-level
|
|
479
|
+
* expression, since there is no exhaustiveness check over an optional-property
|
|
480
|
+
* keyof. `test/admin_collection.test.ts` counts them instead.
|
|
481
|
+
*/
|
|
482
|
+
export const ADMIN_COLLECTION_KEYS = CORE_ADMIN_COLLECTION_KEYS satisfies readonly (keyof AdminCollectionOptions)[];
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* A collection as the admin panel works with it: the contract with the `admin`
|
|
487
|
+
* block flattened onto the top level.
|
|
488
|
+
*
|
|
489
|
+
* The panel reads presentation fields in a few hundred places, and threading
|
|
490
|
+
* `collection.admin?.propertiesOrder` through all of them would be noise that
|
|
491
|
+
* buys nothing — the panel has already resolved the collection by then, merging
|
|
492
|
+
* the declared config with the user's per-collection overrides from local
|
|
493
|
+
* storage. So the panel gets a flat *view model*, exactly as it already does for
|
|
494
|
+
* entities (`Entity` is an admin view model over flat rows, not a wire type).
|
|
495
|
+
*
|
|
496
|
+
* The distinction that matters is direction:
|
|
497
|
+
*
|
|
498
|
+
* - **Reading** a resolved collection → `AdminCollection` (flat, convenient).
|
|
499
|
+
* - **Authoring or persisting** one → core's `CollectionConfig`, with the `admin`
|
|
500
|
+
* block this package augments onto it (nested, which is what the file on disk
|
|
501
|
+
* and the wire both look like).
|
|
502
|
+
*
|
|
503
|
+
* `admin` is kept alongside the flattened fields so the collection editor can
|
|
504
|
+
* still see the block it has to write back.
|
|
505
|
+
*
|
|
506
|
+
* @group Models
|
|
507
|
+
*/
|
|
508
|
+
export type AdminCollection<
|
|
509
|
+
M extends Record<string, unknown> = Record<string, unknown>,
|
|
510
|
+
USER extends User = User
|
|
511
|
+
> = WithFlatAdmin<CollectionConfig<M, USER>, M, USER>;
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Flatten the admin block onto one member of the collection union at a time.
|
|
515
|
+
*
|
|
516
|
+
* `CollectionConfig` is a union discriminated on `engine`
|
|
517
|
+
* (Postgres | Firestore | MongoDB), and a bare `Omit<Union, "admin">` collapses it
|
|
518
|
+
* into a single object type with the discriminant widened. The result stops being
|
|
519
|
+
* assignable back to `CollectionConfig`, so every call that hands a resolved
|
|
520
|
+
* collection to a core function fails — which is exactly what happened. The
|
|
521
|
+
* `C extends unknown` clause makes the mapping distributive, so each member keeps
|
|
522
|
+
* its literal `engine` and stays assignable to its counterpart.
|
|
523
|
+
*/
|
|
524
|
+
type WithFlatAdmin<C, M extends Record<string, unknown>, USER extends User> =
|
|
525
|
+
C extends unknown
|
|
526
|
+
? Omit<C, "admin"> & AdminCollectionOptions<M, USER> & { admin?: AdminCollectionOptions<M, USER> }
|
|
527
|
+
: never;
|
|
528
|
+
|
|
529
|
+
/** {@link AdminCollection} for a Postgres collection. @group Models */
|
|
530
|
+
export type AdminPostgresCollection<
|
|
531
|
+
M extends Record<string, unknown> = Record<string, unknown>,
|
|
532
|
+
USER extends User = User
|
|
533
|
+
> = Omit<PostgresCollectionConfig<M, USER>, "admin">
|
|
534
|
+
& AdminCollectionOptions<M, USER>
|
|
535
|
+
& { admin?: AdminCollectionOptions<M, USER> };
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Flatten a collection's `admin` block onto it, producing the panel's view model.
|
|
539
|
+
*
|
|
540
|
+
* Shallow by design: the block's fields are independent, so a deep merge would
|
|
541
|
+
* only create opportunities for a nested object to be half from one source and
|
|
542
|
+
* half from the other. `admin` survives on the result.
|
|
543
|
+
*
|
|
544
|
+
* Idempotent — flattening an already-flat collection returns an equivalent one —
|
|
545
|
+
* because the panel resolves collections at more than one entry point (the
|
|
546
|
+
* registry, `<Rebase collections>`, a plugin's `modifyCollection`) and they must
|
|
547
|
+
* not fight over which has run.
|
|
548
|
+
*/
|
|
549
|
+
export function resolveAdminCollection<
|
|
550
|
+
M extends Record<string, unknown> = Record<string, unknown>,
|
|
551
|
+
USER extends User = User
|
|
552
|
+
>(collection: CollectionConfig<M, USER> | AdminCollection<M, USER>): AdminCollection<M, USER> {
|
|
553
|
+
const block = (collection as { admin?: AdminCollectionOptions<M, USER> }).admin;
|
|
554
|
+
if (!block) return collection as AdminCollection<M, USER>;
|
|
555
|
+
return { ...(collection as AdminCollection<M, USER>), ...block, admin: block };
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* The inverse: lift flattened admin fields back into the block.
|
|
560
|
+
*
|
|
561
|
+
* Used on the way out — persisting from the collection editor, or handing a
|
|
562
|
+
* collection to anything that expects the authoring shape. Any key in
|
|
563
|
+
* {@link ADMIN_COLLECTION_KEYS} found at the top level is moved down, so a
|
|
564
|
+
* round trip through the panel does not leave the file flat.
|
|
565
|
+
*/
|
|
566
|
+
export function toAdminCollectionConfig<
|
|
567
|
+
M extends Record<string, unknown> = Record<string, unknown>,
|
|
568
|
+
USER extends User = User
|
|
569
|
+
>(collection: AdminCollection<M, USER> | CollectionConfig<M, USER>): CollectionConfig<M, USER> {
|
|
570
|
+
const source = collection as Record<string, unknown>;
|
|
571
|
+
const top: Record<string, unknown> = {};
|
|
572
|
+
const block: Record<string, unknown> = { ...(source.admin as object ?? {}) };
|
|
573
|
+
|
|
574
|
+
for (const [key, value] of Object.entries(source)) {
|
|
575
|
+
if (key === "admin") continue;
|
|
576
|
+
if ((ADMIN_COLLECTION_KEYS as readonly string[]).includes(key)) block[key] = value;
|
|
577
|
+
else top[key] = value;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (Object.keys(block).length > 0) top.admin = block;
|
|
581
|
+
return top as unknown as CollectionConfig<M, USER>;
|
|
582
|
+
}
|
package/src/augment.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The admin block, added back onto the core types by declaration merging.
|
|
3
|
+
*
|
|
4
|
+
* `@rebasepro/types` declares no `admin` field — not on `BaseCollectionConfig`, not on
|
|
5
|
+
* any property. A BaaS install therefore cannot write one: `admin: { … }` on a property
|
|
6
|
+
* there is a type error, which is the whole point. Installing this package is what makes
|
|
7
|
+
* the surface exist.
|
|
8
|
+
*
|
|
9
|
+
* This works because every target is an `interface`. Interfaces merge; `type` aliases do
|
|
10
|
+
* not, so `BaseProperty` and the ten concrete property types must stay interfaces in core
|
|
11
|
+
* for this file to be possible at all.
|
|
12
|
+
*
|
|
13
|
+
* Two consequences worth knowing:
|
|
14
|
+
*
|
|
15
|
+
* - The augmentation applies to the whole **program**, not to the files that import it.
|
|
16
|
+
* `config/` and `frontend/` are separate tsconfig programs, so a collection file needs
|
|
17
|
+
* this package in *its* program. The templates do that with one line in one file —
|
|
18
|
+
* `config/admin.d.ts`, holding `/// <reference types="@rebasepro/admin-types" />` —
|
|
19
|
+
* which is why no collection file has to import anything from here to write `admin`.
|
|
20
|
+
* That reference resolves through `typeRoots`, so the project must also depend on this
|
|
21
|
+
* package; `scripts/check-templates.mjs` asserts both halves.
|
|
22
|
+
* - Each concrete property narrows the block to its own options type
|
|
23
|
+
* (`AdminStringOptions` on `StringProperty`, and so on). That is legal because each
|
|
24
|
+
* extends `AdminPropertyOptions`, exactly as it was declared when these lived in core.
|
|
25
|
+
*/
|
|
26
|
+
import type {
|
|
27
|
+
AdminArrayOptions,
|
|
28
|
+
AdminDateOptions,
|
|
29
|
+
AdminMapOptions,
|
|
30
|
+
AdminNumberOptions,
|
|
31
|
+
AdminPropertyOptions,
|
|
32
|
+
AdminReferenceOptions,
|
|
33
|
+
AdminRelationOptions,
|
|
34
|
+
AdminStringOptions,
|
|
35
|
+
AdminVectorOptions
|
|
36
|
+
} from "./types/property_options";
|
|
37
|
+
import type { AdminCollectionOptions } from "./admin_collection";
|
|
38
|
+
|
|
39
|
+
declare module "@rebasepro/types" {
|
|
40
|
+
|
|
41
|
+
/** Presentation and behaviour for a collection in the admin panel. */
|
|
42
|
+
interface BaseCollectionConfig {
|
|
43
|
+
admin?: AdminCollectionOptions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface BaseProperty<CustomProps = unknown> {
|
|
47
|
+
admin?: AdminPropertyOptions<CustomProps>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface StringProperty { admin?: AdminStringOptions; }
|
|
51
|
+
interface NumberProperty { admin?: AdminNumberOptions; }
|
|
52
|
+
interface BooleanProperty { admin?: AdminPropertyOptions; }
|
|
53
|
+
interface VectorProperty { admin?: AdminVectorOptions; }
|
|
54
|
+
interface DateProperty { admin?: AdminDateOptions; }
|
|
55
|
+
interface GeopointProperty { admin?: AdminPropertyOptions; }
|
|
56
|
+
interface ReferenceProperty { admin?: AdminReferenceOptions; }
|
|
57
|
+
interface RelationProperty { admin?: AdminRelationOptions; }
|
|
58
|
+
interface ArrayProperty { admin?: AdminArrayOptions; }
|
|
59
|
+
interface MapProperty { admin?: AdminMapOptions; }
|
|
60
|
+
}
|