@rebasepro/admin 0.13.0 → 0.13.1-canary.g06dbe5b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{CollectionEditorDialog-CjXkWZ64.js → CollectionEditorDialog-C3DbFNqp.js} +3 -3
- package/dist/{CollectionEditorDialog-CjXkWZ64.js.map → CollectionEditorDialog-C3DbFNqp.js.map} +1 -1
- package/dist/{PropertyEditView-W8I3YV89.js → PropertyEditView-DFH8rXRN.js} +5 -5
- package/dist/PropertyEditView-DFH8rXRN.js.map +1 -0
- package/dist/{RouterCollectionsStudioView-BsLTNoqX.js → RouterCollectionsStudioView-DiUDOu4b.js} +9 -13
- package/dist/RouterCollectionsStudioView-DiUDOu4b.js.map +1 -0
- package/dist/collection_editor/serializable_types.d.ts +1 -0
- package/dist/collection_editor_ui.js +4 -4
- package/dist/components/AdditionalFieldValue.d.ts +25 -0
- package/dist/components/ClearFilterSortButton.d.ts +7 -1
- package/dist/components/CollectionViewBinding/CollectionViewBinding.d.ts +16 -0
- package/dist/components/CollectionViewBinding/EntityCardBinding.d.ts +3 -1
- package/dist/components/CollectionViewBinding/SearchExplanation.d.ts +48 -0
- package/dist/components/CollectionViewBinding/SearchHighlight.d.ts +101 -0
- package/dist/components/CollectionViewBinding/SlotValue.d.ts +63 -0
- package/dist/components/CollectionViewBinding/SortButton.d.ts +7 -1
- package/dist/components/CollectionViewBinding/SplitListCloseButton.d.ts +17 -4
- package/dist/components/CollectionViewBinding/SplitListShowButton.d.ts +12 -0
- package/dist/components/CollectionViewBinding/usePreviewSlots.d.ts +55 -5
- package/dist/components/DetailViewBinding.d.ts +15 -0
- package/dist/components/DrawerNavigationGroup.d.ts +9 -6
- package/dist/components/DrawerNavigationItem.d.ts +16 -6
- package/dist/components/EditViewBinding.d.ts +30 -1
- package/dist/components/EntityDisplayHeader.d.ts +53 -0
- package/dist/components/EntityIdentityBar.d.ts +43 -1
- package/dist/components/EntityViewBinding.d.ts +60 -6
- package/dist/components/RebaseLayout.d.ts +1 -1
- package/dist/components/RebaseShell.d.ts +1 -1
- package/dist/components/RelationSelector.d.ts +8 -1
- package/dist/components/UserSelector.d.ts +8 -1
- package/dist/components/app/Scaffold.d.ts +9 -3
- package/dist/components/index.d.ts +2 -0
- package/dist/data_export/export/export.d.ts +7 -0
- package/dist/data_export/export/fetch_export_data.d.ts +48 -0
- package/dist/data_export/export/index.d.ts +1 -0
- package/dist/data_import/utils/csv.d.ts +30 -0
- package/dist/data_import/utils/index.d.ts +2 -0
- package/dist/data_import/utils/save_entities.d.ts +38 -0
- package/dist/data_import/utils/transforms.d.ts +9 -0
- package/dist/{export-J79rMoNj.js → export-Bd5i6Uvm.js} +175 -39
- package/dist/export-Bd5i6Uvm.js.map +1 -0
- package/dist/form/components/FieldBlock.d.ts +58 -4
- package/dist/form/components/FormRail.d.ts +13 -0
- package/dist/form/components/FormSections.d.ts +15 -1
- package/dist/form/components/useRailVisible.d.ts +5 -4
- package/dist/form/form_utils.d.ts +38 -1
- package/dist/{history-D7L_JlLO.js → history-D9R6LUrH.js} +4 -4
- package/dist/{history-D7L_JlLO.js.map → history-D9R6LUrH.js.map} +1 -1
- package/dist/hooks/useEntityDisplay.d.ts +102 -0
- package/dist/hooks/useEntityDisplayTitle.d.ts +18 -9
- package/dist/{import-BvYl--U4.js → import-pt_j-0G3.js} +6 -5
- package/dist/import-pt_j-0G3.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +158 -60
- package/dist/index.js.map +1 -1
- package/dist/preview/compact_text.d.ts +23 -0
- package/dist/preview/components/BooleanPreview.d.ts +3 -1
- package/dist/preview/property_previews/ArrayOfStringsPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayOneOfPreview.d.ts +1 -1
- package/dist/preview/property_previews/ArrayPropertyPreview.d.ts +1 -1
- package/dist/preview/property_previews/MapPropertyPreview.d.ts +1 -1
- package/dist/preview/property_previews/StringPropertyPreview.d.ts +1 -1
- package/dist/routes/RebaseRoute.d.ts +1 -1
- package/dist/types/components/PropertyPreviewProps.d.ts +19 -0
- package/dist/util/number_format.d.ts +14 -0
- package/dist/util/previews.d.ts +9 -2
- package/dist/util/view_constants.d.ts +24 -0
- package/dist/util/view_mode.d.ts +18 -0
- package/dist/{util-GiwPgxnL.js → util-nX_-JRuF.js} +3984 -1774
- package/dist/util-nX_-JRuF.js.map +1 -0
- package/package.json +10 -9
- package/src/collection_editor/serializable_types.ts +1 -0
- package/src/collection_editor/ui/collection_editor/CollectionsStudioView.tsx +9 -11
- package/src/collection_editor/ui/collection_editor/properties/MapPropertyField.tsx +1 -1
- package/src/collection_editor/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +5 -2
- package/src/collection_editor/useLocalCollectionsConfigController.tsx +10 -3
- package/src/components/AdditionalFieldValue.tsx +68 -0
- package/src/components/ClearFilterSortButton.tsx +16 -7
- package/src/components/CollectionTableBinding/CollectionTableBinding.tsx +7 -8
- package/src/components/CollectionTableBinding/PropertyTableCell.tsx +2 -2
- package/src/components/CollectionTableBinding/column_utils.tsx +15 -6
- package/src/components/CollectionTableBinding/internal/popup_field/PopupFormField.tsx +2 -2
- package/src/components/CollectionTableBinding/table_bindings.tsx +7 -1
- package/src/components/CollectionViewBinding/BoardCardBinding.tsx +26 -27
- package/src/components/CollectionViewBinding/CollectionBoardViewBinding.tsx +13 -7
- package/src/components/CollectionViewBinding/CollectionCardViewBinding.tsx +2 -1
- package/src/components/CollectionViewBinding/CollectionListViewBinding.tsx +822 -354
- package/src/components/CollectionViewBinding/CollectionViewActions.tsx +1 -1
- package/src/components/CollectionViewBinding/CollectionViewBinding.tsx +51 -12
- package/src/components/CollectionViewBinding/CollectionViewStartActions.tsx +9 -0
- package/src/components/CollectionViewBinding/EntityCardBinding.tsx +51 -39
- package/src/components/CollectionViewBinding/SearchExplanation.tsx +106 -0
- package/src/components/CollectionViewBinding/SearchHighlight.tsx +311 -0
- package/src/components/CollectionViewBinding/SlotValue.tsx +156 -0
- package/src/components/CollectionViewBinding/SortButton.tsx +16 -4
- package/src/components/CollectionViewBinding/SplitListCloseButton.tsx +23 -7
- package/src/components/CollectionViewBinding/SplitListShowButton.tsx +28 -0
- package/src/components/CollectionViewBinding/SplitListView.tsx +47 -7
- package/src/components/CollectionViewBinding/ViewModeToggle.tsx +10 -5
- package/src/components/CollectionViewBinding/useBoardDataController.tsx +23 -6
- package/src/components/CollectionViewBinding/usePreviewSlots.ts +183 -64
- package/src/components/CollectionViewBinding/utils.ts +7 -4
- package/src/components/DefaultDrawer.tsx +19 -23
- package/src/components/DetailViewBinding.tsx +109 -62
- package/src/components/DrawerNavigationGroup.tsx +16 -39
- package/src/components/DrawerNavigationItem.tsx +44 -16
- package/src/components/EditViewBinding.tsx +233 -50
- package/src/components/EntityDisplayHeader.tsx +170 -0
- package/src/components/EntityIdentityBar.tsx +163 -23
- package/src/components/EntityInspector.tsx +24 -6
- package/src/components/EntityPreviewBinding.tsx +58 -18
- package/src/components/EntityViewBinding.tsx +344 -41
- package/src/components/InlineEntityPreview.tsx +10 -10
- package/src/components/RebaseLayout.tsx +2 -2
- package/src/components/RebaseShell.tsx +2 -2
- package/src/components/ReferenceTable/SelectionTableBinding.tsx +4 -2
- package/src/components/RelationSelector.tsx +35 -10
- package/src/components/SideDialogs.tsx +15 -3
- package/src/components/SidePanelBinding.tsx +122 -66
- package/src/components/UserSelector.tsx +67 -13
- package/src/components/app/Scaffold.tsx +112 -10
- package/src/components/history/LastEditedByIndicator.tsx +7 -12
- package/src/components/index.ts +3 -0
- package/src/data_export/export/ExportCollectionAction.tsx +101 -48
- package/src/data_export/export/export.ts +63 -16
- package/src/data_export/export/fetch_export_data.ts +90 -0
- package/src/data_export/export/index.ts +1 -0
- package/src/data_import/components/ImportSaveInProgress.tsx +35 -36
- package/src/data_import/import/ImportCollectionAction.tsx +13 -2
- package/src/data_import/utils/csv.ts +153 -0
- package/src/data_import/utils/data.ts +16 -2
- package/src/data_import/utils/file_to_json.ts +51 -5
- package/src/data_import/utils/index.ts +2 -0
- package/src/data_import/utils/save_entities.ts +138 -0
- package/src/data_import/utils/transforms.ts +23 -0
- package/src/form/EntityForm.tsx +74 -53
- package/src/form/EntityFormBinding.tsx +24 -11
- package/src/form/PropertyFieldBinding.tsx +2 -2
- package/src/form/components/FieldBlock.tsx +80 -10
- package/src/form/components/FormRail.tsx +19 -2
- package/src/form/components/FormSections.tsx +32 -5
- package/src/form/components/StorageUploadProgress.tsx +9 -3
- package/src/form/components/useRailVisible.tsx +5 -4
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -2
- package/src/form/field_bindings/BinaryFieldBinding.tsx +10 -5
- package/src/form/field_bindings/BlockFieldBinding.tsx +3 -3
- package/src/form/field_bindings/GeopointFieldBinding.tsx +23 -16
- package/src/form/field_bindings/MapFieldBinding.tsx +2 -2
- package/src/form/field_bindings/MarkdownEditorFieldBinding.tsx +13 -7
- package/src/form/field_bindings/MultiSelectFieldBinding.tsx +7 -1
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +21 -2
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +1 -1
- package/src/form/field_bindings/RelationFieldBinding.tsx +5 -2
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +2 -2
- package/src/form/field_bindings/UserSelectFieldBinding.tsx +4 -1
- package/src/form/field_bindings/VectorFieldBinding.tsx +6 -4
- package/src/form/form_utils.ts +78 -0
- package/src/hooks/navigation/useBuildCollectionRegistryController.tsx +29 -5
- package/src/hooks/useEntityDisplay.tsx +262 -0
- package/src/hooks/useEntityDisplayTitle.tsx +27 -38
- package/src/index.ts +9 -1
- package/src/preview/PropertyPreview.tsx +25 -5
- package/src/preview/compact_text.ts +68 -0
- package/src/preview/components/BooleanPreview.tsx +5 -2
- package/src/preview/components/ReferencePreview.tsx +5 -8
- package/src/preview/components/RelationPreview.tsx +8 -7
- package/src/preview/components/StorageThumbnail.tsx +5 -2
- package/src/preview/property_previews/ArrayOfRelationsPreview.tsx +6 -3
- package/src/preview/property_previews/ArrayOfStringsPreview.tsx +26 -1
- package/src/preview/property_previews/ArrayOneOfPreview.tsx +10 -1
- package/src/preview/property_previews/ArrayPropertyPreview.tsx +10 -1
- package/src/preview/property_previews/MapPropertyPreview.tsx +52 -1
- package/src/preview/property_previews/NumberPropertyPreview.tsx +9 -1
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -5
- package/src/routes/RebaseRoute.tsx +62 -4
- package/src/types/components/PropertyPreviewProps.tsx +21 -0
- package/src/util/navigation_utils.ts +4 -2
- package/src/util/number_format.ts +46 -0
- package/src/util/previews.ts +8 -55
- package/src/util/property_utils.tsx +6 -1
- package/src/util/view_constants.ts +27 -0
- package/src/util/view_mode.ts +36 -0
- package/dist/PropertyEditView-W8I3YV89.js.map +0 -1
- package/dist/RouterCollectionsStudioView-BsLTNoqX.js.map +0 -1
- package/dist/export-J79rMoNj.js.map +0 -1
- package/dist/import-BvYl--U4.js.map +0 -1
- package/dist/util-GiwPgxnL.js.map +0 -1
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Props of a drawer navigation row, named so an app overriding
|
|
4
|
+
* `Shell.DrawerNavigationItem` can type its wrapper against them.
|
|
5
|
+
*/
|
|
6
|
+
export type DrawerNavigationItemProps = {
|
|
3
7
|
icon: React.ReactElement;
|
|
4
8
|
name: string;
|
|
5
|
-
|
|
9
|
+
/** @deprecated No longer read: the tooltip is uncontrolled. */
|
|
10
|
+
tooltipsOpen?: boolean;
|
|
6
11
|
drawerOpen: boolean;
|
|
12
|
+
/** @deprecated No longer read: the tooltip is uncontrolled. */
|
|
7
13
|
adminMenuOpen?: boolean;
|
|
8
14
|
url: string;
|
|
9
15
|
onClick?: () => void;
|
|
10
16
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
17
|
+
* Drop the row's icon and indent the label into the space it occupied, so the
|
|
18
|
+
* row reads as a child of the group header above it.
|
|
19
|
+
*
|
|
20
|
+
* Nothing in the framework sets this: the stock drawer always passes `false`,
|
|
21
|
+
* and an app that wants the categorised look opts into it by overriding
|
|
22
|
+
* `Shell.DrawerNavigationItem` and deciding for itself which rows get it.
|
|
14
23
|
*/
|
|
15
24
|
indented?: boolean;
|
|
16
|
-
}
|
|
25
|
+
};
|
|
26
|
+
export declare function DrawerNavigationItem({ name, icon, drawerOpen, url, onClick, indented }: DrawerNavigationItemProps): React.JSX.Element;
|
|
@@ -10,6 +10,19 @@ export type BarActionsParams = {
|
|
|
10
10
|
status: EntityStatus;
|
|
11
11
|
path: string;
|
|
12
12
|
entityId?: string | number;
|
|
13
|
+
/** Whether `values` differ from what is stored. */
|
|
14
|
+
dirty: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Hand the edit in progress to the layout this action is about to open, so
|
|
17
|
+
* the record arrives showing — and still holding — what the user typed here.
|
|
18
|
+
* Call it from any action that changes layout without leaving the record;
|
|
19
|
+
* it decides on its own whether there is anything worth carrying, so a
|
|
20
|
+
* record nobody edited still arrives clean.
|
|
21
|
+
*
|
|
22
|
+
* Only supplied where there is a form to carry: the detail view passes no
|
|
23
|
+
* such thing.
|
|
24
|
+
*/
|
|
25
|
+
carryEdit?: () => void;
|
|
13
26
|
};
|
|
14
27
|
export type OnTabChangeParams<M extends Record<string, unknown>> = {
|
|
15
28
|
path: string;
|
|
@@ -33,6 +46,16 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
|
|
|
33
46
|
onSaved?: (params: OnUpdateParams) => void;
|
|
34
47
|
onTabChange?: (props: OnTabChangeParams<M>) => void;
|
|
35
48
|
navigateBack?: () => void;
|
|
49
|
+
/**
|
|
50
|
+
* Dismiss this record — the ✕ at the identity bar's trailing edge, and what
|
|
51
|
+
* "save and close" reaches once the save lands.
|
|
52
|
+
*
|
|
53
|
+
* Set by the layouts that own the record's place on screen but are not side
|
|
54
|
+
* dialogs: the split, where dismissing is navigating back to the collection.
|
|
55
|
+
* The side panel and the dialog close through {@link useSideDialogContext}
|
|
56
|
+
* instead, which is what runs their unsaved-changes prompt.
|
|
57
|
+
*/
|
|
58
|
+
onCloseRequest?: () => void;
|
|
36
59
|
layout?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
37
60
|
barActions?: (params: BarActionsParams) => React.ReactNode;
|
|
38
61
|
/**
|
|
@@ -40,6 +63,12 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
|
|
|
40
63
|
* of the breadcrumb — the side panel's close button.
|
|
41
64
|
*/
|
|
42
65
|
barActionsStart?: React.ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* Full screen only: bring the list back beside this record. Set when the
|
|
68
|
+
* record was opened out of a split collection, so folding the list away is
|
|
69
|
+
* reversible; it takes the place of the back arrow when present.
|
|
70
|
+
*/
|
|
71
|
+
onShowList?: () => void;
|
|
43
72
|
formProps?: Partial<EntityFormBindingProps<M>>;
|
|
44
73
|
/**
|
|
45
74
|
* Pre-populate the form with these values when creating a new entity.
|
|
@@ -53,7 +82,7 @@ export interface EditViewBindingProps<M extends Record<string, unknown> = Record
|
|
|
53
82
|
* a record is opened.
|
|
54
83
|
*/
|
|
55
84
|
export declare function EditViewBinding<M extends Record<string, unknown>>({ entityId, ...props }: EditViewBindingProps<M>): React.JSX.Element;
|
|
56
|
-
export declare function EditViewBindingInner<M extends Record<string, unknown>>({ path, entityId, selectedTab: selectedTabProp, collection, parentCollectionSlugs, parentEntityIds, onValuesModified, onSaved, onTabChange, navigateBack, entity, initialDirtyValues, dataLoading, layout, barActions, barActionsStart, status, setStatus, formProps, canEdit }: EditViewBindingProps<M> & {
|
|
85
|
+
export declare function EditViewBindingInner<M extends Record<string, unknown>>({ path, entityId, selectedTab: selectedTabProp, collection, parentCollectionSlugs, parentEntityIds, onValuesModified, onSaved, onTabChange, navigateBack, onCloseRequest, entity, initialDirtyValues, dataLoading, layout, barActions, barActionsStart, onShowList, status, setStatus, formProps, canEdit }: EditViewBindingProps<M> & {
|
|
57
86
|
entity?: Entity<M>;
|
|
58
87
|
initialDirtyValues?: Partial<M>;
|
|
59
88
|
dataLoading: boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import type { Entity } from "@rebasepro/types";
|
|
3
|
+
import React from "react";
|
|
4
|
+
/**
|
|
5
|
+
* The roles whose property the read-only view takes out of its grid.
|
|
6
|
+
*
|
|
7
|
+
* A role rendered here and left in the grid prints the value twice, so the
|
|
8
|
+
* header's own rendering and this list have to agree — but the list is *not*
|
|
9
|
+
* simply "every role the header draws". Two are deliberately absent:
|
|
10
|
+
*
|
|
11
|
+
* `title`, because the heading above a record and a labelled field are not the
|
|
12
|
+
* same thing. `Order #` is a column with a name, a description and a format that
|
|
13
|
+
* someone reads the record to find; `ORD-2026-0043` sitting in the identity bar
|
|
14
|
+
* beside the breadcrumb is a label for the page. Removing the field because the
|
|
15
|
+
* page is named after it takes a value out of the record on the grounds that its
|
|
16
|
+
* text appears elsewhere, which is not a reason — a value in the database is
|
|
17
|
+
* shown.
|
|
18
|
+
*
|
|
19
|
+
* `date`, because the rail's record block already carries the timestamps, and a
|
|
20
|
+
* collection whose `date` role points at a business date — an order date, a due
|
|
21
|
+
* date — wants that field where it declared it.
|
|
22
|
+
*/
|
|
23
|
+
export declare const HEADER_DISPLAY_ROLES: readonly ["subtitle", "image", "status", "tags"];
|
|
24
|
+
export interface EntityDisplayHeaderProps<M extends Record<string, unknown>> {
|
|
25
|
+
entity: Entity<M>;
|
|
26
|
+
collection: AdminCollection<M>;
|
|
27
|
+
/**
|
|
28
|
+
* Render the title. Off when an identity bar above already shows it — which
|
|
29
|
+
* is every full-page use — and on inside a dialog that has no bar.
|
|
30
|
+
*/
|
|
31
|
+
showTitle?: boolean;
|
|
32
|
+
className?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* What the record *is*, above the fields that say what it holds.
|
|
36
|
+
*
|
|
37
|
+
* A record's picture, name, state and labels are the things you opened the page
|
|
38
|
+
* to see; on the plain grid they were fields like any other, so an order's
|
|
39
|
+
* status sat in the second cell of the first row at the same size and weight as
|
|
40
|
+
* its shipping cost, and its tags were a wrapped list of grey text.
|
|
41
|
+
*
|
|
42
|
+
* Every role here comes from `admin.display` and nothing is derived except the
|
|
43
|
+
* title, which was already derived for the identity bar. A collection that
|
|
44
|
+
* declares no display block therefore gets no header at all rather than a
|
|
45
|
+
* heuristic guess at which of its enums is a "status" — the panel does not know
|
|
46
|
+
* what these records are, and a wrong guess here is louder than no guess.
|
|
47
|
+
*
|
|
48
|
+
* A role filled by a *property* renders through {@link PropertyPreview}, so a
|
|
49
|
+
* status keeps its enum's colour and an image stays a storage thumbnail. A role
|
|
50
|
+
* filled by a resolver has no property behind it and renders as what it
|
|
51
|
+
* returned.
|
|
52
|
+
*/
|
|
53
|
+
export declare function EntityDisplayHeader<M extends Record<string, unknown>>({ entity, collection, showTitle, className }: EntityDisplayHeaderProps<M>): React.JSX.Element | null;
|
|
@@ -4,6 +4,15 @@ import React from "react";
|
|
|
4
4
|
export interface EntityIdentityBarProps {
|
|
5
5
|
/** Plural collection name, shown as the breadcrumb ahead of the title. */
|
|
6
6
|
collection: AdminCollection;
|
|
7
|
+
/**
|
|
8
|
+
* Where the breadcrumb points. Without it the collection name is inert text
|
|
9
|
+
* that still reads as a breadcrumb — worse than no breadcrumb at all, and
|
|
10
|
+
* the only way back once a layout drops its back arrow.
|
|
11
|
+
*
|
|
12
|
+
* Left unset by the overlays (side panel, dialog), where the collection is
|
|
13
|
+
* already underneath and navigating would dismiss the record instead.
|
|
14
|
+
*/
|
|
15
|
+
collectionUrl?: string;
|
|
7
16
|
/** Resolved record title, already guarded against showing a field default. */
|
|
8
17
|
title: string;
|
|
9
18
|
entityId?: string | number;
|
|
@@ -20,10 +29,43 @@ export interface EntityIdentityBarProps {
|
|
|
20
29
|
* action is "done with this record" rather than "keep editing".
|
|
21
30
|
*/
|
|
22
31
|
onSaveAndClose?: () => void;
|
|
32
|
+
/**
|
|
33
|
+
* Where {@link onSaveAndClose} is offered.
|
|
34
|
+
*
|
|
35
|
+
* `"button"` — its own filled button, and Save demotes to text. For the
|
|
36
|
+
* overlays, where you opened the record to do one thing to it and closing is
|
|
37
|
+
* the dominant intent.
|
|
38
|
+
*
|
|
39
|
+
* `"menu"` — a `▾` welded to the Save button. For the split, where the list
|
|
40
|
+
* is right there and `j`/`k` walk from record to record: there, saving and
|
|
41
|
+
* *staying* is the common case, so it keeps the filled button and dismissing
|
|
42
|
+
* is one step further in. The gesture still exists, so muscle memory carried
|
|
43
|
+
* over from the side panel finds it.
|
|
44
|
+
*/
|
|
45
|
+
saveAndClosePlacement?: "button" | "menu";
|
|
46
|
+
/**
|
|
47
|
+
* Dismiss this record, from the ✕ at the bar's trailing edge.
|
|
48
|
+
*
|
|
49
|
+
* Fenced off from Save by a separator: it is chrome, not the last item in
|
|
50
|
+
* the action row. The panel that supplies it decides what dismissing means —
|
|
51
|
+
* in the split it is a navigation, which the layout's unsaved-changes
|
|
52
|
+
* blocker already guards, so the ✕ cannot drop an edit without asking.
|
|
53
|
+
*/
|
|
54
|
+
onClose?: () => void;
|
|
23
55
|
onBack?: () => void;
|
|
24
56
|
onInspect?: () => void;
|
|
25
57
|
/** Opens the inspector straight on its history tab. */
|
|
26
58
|
onViewHistory?: () => void;
|
|
59
|
+
/**
|
|
60
|
+
* Where this record appears on the live site, from `entityLinkBuilder`.
|
|
61
|
+
*
|
|
62
|
+
* Here rather than above the record, where it used to be a single unlabelled
|
|
63
|
+
* icon alone on its own right-aligned row — the only thing between the bar
|
|
64
|
+
* and the first field, and the only control in the view that did not say
|
|
65
|
+
* what it was. It is a thing you do *to* a record, so it belongs with the
|
|
66
|
+
* others.
|
|
67
|
+
*/
|
|
68
|
+
externalLink?: string;
|
|
27
69
|
/**
|
|
28
70
|
* Actions performed *on* the record — copy, delete, whatever the collection
|
|
29
71
|
* adds — as menu items under the overflow button. They live here rather
|
|
@@ -50,4 +92,4 @@ export interface EntityIdentityBarProps {
|
|
|
50
92
|
* editing. They live here instead, and the left half of a bar that was
|
|
51
93
|
* previously empty now does the work.
|
|
52
94
|
*/
|
|
53
|
-
export declare function EntityIdentityBar({ collection, title, entityId, status, dirty, saving, onSave, onDiscard, saveDisabled, hasErrors, onSaveAndClose, onBack, onInspect, onViewHistory, recordActions, pluginActions, trailing, leading }: EntityIdentityBarProps): React.JSX.Element;
|
|
95
|
+
export declare function EntityIdentityBar({ collection, collectionUrl, title, entityId, status, dirty, saving, onSave, onDiscard, saveDisabled, hasErrors, onSaveAndClose, saveAndClosePlacement, onClose, onBack, onInspect, onViewHistory, externalLink, recordActions, pluginActions, trailing, leading }: EntityIdentityBarProps): React.JSX.Element;
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
2
|
+
import type { FormContext } from "../types/fields";
|
|
2
3
|
import React from "react";
|
|
3
4
|
import { Entity } from "@rebasepro/types";
|
|
5
|
+
/**
|
|
6
|
+
* The fields the read-only view lays out, in render order.
|
|
7
|
+
*
|
|
8
|
+
* The same set the form uses, minus two groups. Both are dropped *before* the
|
|
9
|
+
* layout resolves rather than skipped while rendering, because the resolver has
|
|
10
|
+
* by then given each one a place on the grid — skipping later leaves that place
|
|
11
|
+
* as a hole.
|
|
12
|
+
*
|
|
13
|
+
* - The `additionalFields` when there is no form context to hand them: those
|
|
14
|
+
* entries are arbitrary components, not values, and one rendered against no
|
|
15
|
+
* context is nothing at all.
|
|
16
|
+
* - `headerKeys` — the properties {@link EntityDisplayHeader} has already
|
|
17
|
+
* rendered as the record's picture, subtitle, state and labels. Leaving one in
|
|
18
|
+
* prints its value twice on one screen.
|
|
19
|
+
*
|
|
20
|
+
* The record's **title property is not among them**, deliberately. It names the
|
|
21
|
+
* page in the identity bar, but a heading and a labelled field are different
|
|
22
|
+
* things: `Order #` is a column with a name and a description that someone opens
|
|
23
|
+
* the record to read, and dropping it because its text also appears in the
|
|
24
|
+
* breadcrumb removes a value from the record on the grounds that it is legible
|
|
25
|
+
* somewhere else. See {@link HEADER_DISPLAY_ROLES}.
|
|
26
|
+
*/
|
|
27
|
+
export declare function readOnlyFieldKeys(collection: AdminCollection, hasFormContext: boolean, headerKeys?: ReadonlySet<string>): string[];
|
|
4
28
|
/**
|
|
5
29
|
* @group Components
|
|
6
30
|
*/
|
|
@@ -9,14 +33,44 @@ export interface EntityViewBindingProps<M extends Record<string, unknown>> {
|
|
|
9
33
|
collection: AdminCollection<M>;
|
|
10
34
|
path: string;
|
|
11
35
|
className?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Lay the record out as a page: a centred, scrolling column with the
|
|
38
|
+
* metadata rail beside it, exactly as the form does. Off for the inline
|
|
39
|
+
* uses — the delete dialog renders the record inside a box it already owns.
|
|
40
|
+
*/
|
|
41
|
+
asPage?: boolean;
|
|
42
|
+
/** Padding preset, matching the form's. Only read when `asPage`. */
|
|
43
|
+
openEntityMode?: "side_panel" | "full_screen" | "split" | "dialog";
|
|
44
|
+
/**
|
|
45
|
+
* Needed only to render `additionalFields`, which are arbitrary components
|
|
46
|
+
* that take a form context. Without one those entries are left out rather
|
|
47
|
+
* than rendered against a fake context.
|
|
48
|
+
*/
|
|
49
|
+
formContext?: FormContext<M>;
|
|
12
50
|
}
|
|
13
51
|
/**
|
|
14
52
|
* The read-only rendering of a record's values.
|
|
15
53
|
*
|
|
16
|
-
* This
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
54
|
+
* This is the same layout as {@link EntityForm}: the sections, the grid spans,
|
|
55
|
+
* the labels and the rail all come from the one resolver, so the record does
|
|
56
|
+
* not rearrange itself when you press Edit. Only the controls differ — each
|
|
57
|
+
* field renders its {@link PropertyPreview} instead of an input.
|
|
58
|
+
*
|
|
59
|
+
* It used to be a flat two-column table of every value in the record: no
|
|
60
|
+
* grouping, no widths, a label column sized for short names and a value column
|
|
61
|
+
* that wrapped emails over two lines while the pane beside it stayed empty. A
|
|
62
|
+
* boolean and a markdown body were given the same room.
|
|
63
|
+
*
|
|
64
|
+
* The synthetic `Id` row it used to prepend is gone with the same reasoning the
|
|
65
|
+
* form applies: between that row, the collection's own id property and the
|
|
66
|
+
* `path/id` chip above them, one UUID appeared three times. The id is a
|
|
67
|
+
* copyable chip in the identity bar, and the rail's record block, once each.
|
|
68
|
+
*
|
|
69
|
+
* The labels are the form's labels, icon and all: a field is the same field
|
|
70
|
+
* whether or not you are editing it. What `mode: "read"` drops is the per-field
|
|
71
|
+
* *description*, which is input help rather than part of the label — and what it
|
|
72
|
+
* adds is room. A control is a bordered box that separates itself from the next
|
|
73
|
+
* one; a value is bare text, so the rows and sections are opened up rather than
|
|
74
|
+
* inheriting spacing sized for inputs.
|
|
21
75
|
*/
|
|
22
|
-
export declare function EntityViewBinding<M extends Record<string, unknown>>({ entity, collection,
|
|
76
|
+
export declare function EntityViewBinding<M extends Record<string, unknown>>({ entity, collection, className, asPage, openEntityMode, formContext }: EntityViewBindingProps<M>): React.JSX.Element;
|
|
@@ -7,7 +7,7 @@ export interface RebaseLayoutProps {
|
|
|
7
7
|
appBar?: React.ReactNode;
|
|
8
8
|
/** Custom Drawer to override the default. */
|
|
9
9
|
drawer?: React.ReactNode;
|
|
10
|
-
/** Expand the drawer while the pointer is over it. Not a load-time default — that is `defaultDrawerOpen`. */
|
|
10
|
+
/** Expand the drawer while the pointer is over it. On by default; pass `false` to opt out. Not a load-time default — that is `defaultDrawerOpen`. */
|
|
11
11
|
autoOpenDrawer?: boolean;
|
|
12
12
|
/** Start with the drawer expanded instead of collapsed to icons. Ignored on small layouts. */
|
|
13
13
|
defaultDrawerOpen?: boolean;
|
|
@@ -4,7 +4,7 @@ export interface RebaseShellProps {
|
|
|
4
4
|
/** Top bar. Only rendered if provided — pass `<AppBar/>` for the default one. */
|
|
5
5
|
appBar?: React.ReactNode;
|
|
6
6
|
drawer?: React.ReactNode;
|
|
7
|
-
/** Expand the drawer while the pointer is over it. Not a load-time default — that is `defaultDrawerOpen`. */
|
|
7
|
+
/** Expand the drawer while the pointer is over it. On by default; pass `false` to opt out. Not a load-time default — that is `defaultDrawerOpen`. */
|
|
8
8
|
autoOpenDrawer?: boolean;
|
|
9
9
|
/** Start with the drawer expanded instead of collapsed to icons. Ignored on small layouts. */
|
|
10
10
|
defaultDrawerOpen?: boolean;
|
|
@@ -15,7 +15,14 @@ export interface RelationSelectorProps {
|
|
|
15
15
|
/** Callback returning selected EntityRelation(s) */
|
|
16
16
|
onValueChange?: (updatedValue: EntityRelation | EntityRelation[] | undefined) => void;
|
|
17
17
|
placeholder?: React.ReactNode;
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* `large` is the form's size and is on the shared 28/32/40/48 control
|
|
20
|
+
* scale, so a relation picker lines up with the text field beside it.
|
|
21
|
+
*
|
|
22
|
+
* `small` and `medium` predate that scale (42 and 56) and are kept as they
|
|
23
|
+
* are because the table cells and the filter row are built around them.
|
|
24
|
+
*/
|
|
25
|
+
size?: "small" | "medium" | "large";
|
|
19
26
|
useChips?: boolean;
|
|
20
27
|
disabled?: boolean;
|
|
21
28
|
error?: boolean;
|
|
@@ -6,7 +6,14 @@ export interface UserSelectorProps {
|
|
|
6
6
|
value?: string | null;
|
|
7
7
|
onValueChange?: (userId: string | null) => void;
|
|
8
8
|
placeholder?: React.ReactNode;
|
|
9
|
-
|
|
9
|
+
/**
|
|
10
|
+
* `large` is the form's size and is on the shared 28/32/40/48 control
|
|
11
|
+
* scale, so a user picker lines up with the text field beside it.
|
|
12
|
+
*
|
|
13
|
+
* `small` and `medium` predate that scale (42 and 56) and are kept as they
|
|
14
|
+
* are because the table cells and the filter row are built around them.
|
|
15
|
+
*/
|
|
16
|
+
size?: "small" | "medium" | "large";
|
|
10
17
|
disabled?: boolean;
|
|
11
18
|
invisible?: boolean;
|
|
12
19
|
clearable?: boolean;
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
export declare const DRAWER_WIDTH = 280;
|
|
3
|
+
/**
|
|
4
|
+
* Namespaced by base path: two admins served from one origin are two different
|
|
5
|
+
* navigations, and a single key would have them overwrite each other's drawer.
|
|
6
|
+
*/
|
|
7
|
+
export declare function drawerOpenStorageKey(basePath: string | undefined): string;
|
|
3
8
|
/**
|
|
4
9
|
* @group Core
|
|
5
10
|
*/
|
|
6
11
|
export interface ScaffoldProps {
|
|
7
12
|
/**
|
|
8
|
-
*
|
|
13
|
+
* Expand the collapsed drawer while the pointer is over it. On by default —
|
|
14
|
+
* pass `false` to keep the rail a rail until the user clicks the toggle.
|
|
9
15
|
*/
|
|
10
16
|
autoOpenDrawer?: boolean;
|
|
11
17
|
/**
|
|
12
18
|
* Start with the drawer expanded rather than collapsed to icons.
|
|
13
19
|
*
|
|
14
20
|
* Distinct from {@link autoOpenDrawer}, which is a hover behaviour: this one
|
|
15
|
-
* only seeds the
|
|
16
|
-
*
|
|
21
|
+
* only seeds the very first visit. Once the user toggles the drawer, that
|
|
22
|
+
* choice is stored in `localStorage` and wins over this prop on every load.
|
|
17
23
|
*
|
|
18
24
|
* Ignored on small layouts, where an expanded drawer covers the content it is
|
|
19
25
|
* meant to navigate.
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export type { EntityViewBindingProps } from "./EntityViewBinding";
|
|
2
2
|
export { EntityViewBinding } from "./EntityViewBinding";
|
|
3
|
+
export type { EntityDisplayHeaderProps } from "./EntityDisplayHeader";
|
|
4
|
+
export { EntityDisplayHeader, HEADER_DISPLAY_ROLES } from "./EntityDisplayHeader";
|
|
3
5
|
export * from "./DetailViewBinding";
|
|
4
6
|
export type { SelectionProps } from "./ReferenceTable/SelectionTableBinding";
|
|
5
7
|
export { SelectionTableBinding } from "./ReferenceTable/SelectionTableBinding";
|
|
@@ -17,6 +17,13 @@ export interface DownloadEntitiesExportParams<M extends Record<string, unknown>>
|
|
|
17
17
|
export declare function downloadEntitiesExport<M extends Record<string, unknown>>({ data, additionalData, properties, propertiesOrder, name, flattenArrays, additionalHeaders, exportType, dateExportType }: DownloadEntitiesExportParams<M>): void;
|
|
18
18
|
export declare function getEntityCSVExportableData(data: Entity<Record<string, unknown>>[], additionalData: Record<string, unknown>[] | undefined, properties: Properties, headers: Header[], dateExportType: "timestamp" | "string"): unknown[][];
|
|
19
19
|
export declare function getEntityJsonExportableData(data: Entity<Record<string, unknown>>[], additionalData: Record<string, unknown>[] | undefined, properties: Properties, dateExportType: "timestamp" | "string"): Record<string, unknown>[];
|
|
20
|
+
/**
|
|
21
|
+
* Neutralise a cell that a spreadsheet would otherwise evaluate, by prefixing a
|
|
22
|
+
* single quote — the standard OWASP mitigation. The apostrophe is consumed by
|
|
23
|
+
* the spreadsheet as "treat the rest as text", so the value reads back intact.
|
|
24
|
+
*/
|
|
25
|
+
export declare function escapeCsvFormula(value: string): string;
|
|
26
|
+
export declare function entryToCSVRow(entry: unknown[]): string;
|
|
20
27
|
export declare function downloadBlob(content: BlobPart[], filename: string, contentType: string): void;
|
|
21
28
|
export declare function downloadDataAsCsv(data: object[], name: string): void;
|
|
22
29
|
export {};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Entity, FindParams, FindResponse } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Rows asked for per request.
|
|
4
|
+
*
|
|
5
|
+
* Kept at or below the server's `MAX_LIST_LIMIT`, which is now the largest page
|
|
6
|
+
* a read will serve *and refuses* anything larger — it used to clamp silently,
|
|
7
|
+
* and asking for 100 000 while receiving 1 000 looked exactly like a collection
|
|
8
|
+
* with 1 000 rows in it. The walk still never trusts the page size it asked for:
|
|
9
|
+
* it advances by the rows it was actually handed.
|
|
10
|
+
*/
|
|
11
|
+
export declare const EXPORT_PAGE_SIZE = 500;
|
|
12
|
+
/**
|
|
13
|
+
* Most rows one browser-side export will materialise.
|
|
14
|
+
*
|
|
15
|
+
* Everything here is built in memory and handed to a Blob, so there is a
|
|
16
|
+
* ceiling whether or not it is named. Naming it means going over it **throws**
|
|
17
|
+
* instead of writing a short file that reads like the whole collection.
|
|
18
|
+
*/
|
|
19
|
+
export declare const MAX_EXPORT_ROWS = 100000;
|
|
20
|
+
export interface FetchAllForExportParams<M extends Record<string, unknown>> {
|
|
21
|
+
/** The collection accessor to read through — `dataClient.collection(path)`. */
|
|
22
|
+
accessor: {
|
|
23
|
+
find(params?: FindParams<M>): Promise<FindResponse<M>>;
|
|
24
|
+
};
|
|
25
|
+
/** Query to export. The window (`limit` / `offset` / `page`) is owned by the walk. */
|
|
26
|
+
params?: Omit<FindParams<M>, "limit" | "offset" | "page">;
|
|
27
|
+
pageSize?: number;
|
|
28
|
+
maxRows?: number;
|
|
29
|
+
/** Called after every page with the rows so far, and the total when the server reports one. */
|
|
30
|
+
onProgress?: (loaded: number, total: number | undefined) => void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Read **every** row matching a query, one page at a time.
|
|
34
|
+
*
|
|
35
|
+
* `find({})` sends no `limit`, and an absent limit is resolved server-side to
|
|
36
|
+
* `DEFAULT_LIST_LIMIT` — 50 rows. The export used to hand those 50 rows back as
|
|
37
|
+
* `<collection>.csv` with nothing saying it was a sample, next to a dialog
|
|
38
|
+
* warning that the collection had 100 000 documents.
|
|
39
|
+
*
|
|
40
|
+
* Termination is driven by the server's `meta.hasMore` where it is reported and
|
|
41
|
+
* by an empty page otherwise — never by comparing a page's length against the
|
|
42
|
+
* requested limit, since a final page that happens to be exactly full is
|
|
43
|
+
* indistinguishable that way and a walk that stops there drops rows.
|
|
44
|
+
*
|
|
45
|
+
* @throws When more than `maxRows` rows match. A truncated export is worse than
|
|
46
|
+
* no export: it is indistinguishable from a complete one.
|
|
47
|
+
*/
|
|
48
|
+
export declare function fetchAllEntitiesForExport<M extends Record<string, unknown>>({ accessor, params, pageSize, maxRows, onProgress }: FetchAllForExportParams<M>): Promise<Entity<M>[]>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Split CSV text into rows of raw cells, RFC 4180 style.
|
|
3
|
+
*
|
|
4
|
+
* Quoted fields may contain the delimiter, newlines and doubled quotes; this is
|
|
5
|
+
* the exact inverse of what the export's `entryToCSVRow` writes, so a file this
|
|
6
|
+
* admin produced reads back into the cells it was built from.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseCsvRows(text: string, delimiter?: string): string[][];
|
|
9
|
+
/**
|
|
10
|
+
* Pick the delimiter that splits the header row into the most cells. A
|
|
11
|
+
* single-column file matches nothing and stays on the comma.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectCsvDelimiter(text: string): string;
|
|
14
|
+
export interface ParsedCsv {
|
|
15
|
+
headers: string[];
|
|
16
|
+
data: Record<string, string>[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse CSV text into one object per row, keyed by the header row.
|
|
20
|
+
*
|
|
21
|
+
* The picker accepts `.csv` and the dialog promises "Upload a CSV, Excel or JSON
|
|
22
|
+
* file", but every non-JSON file used to be handed to ExcelJS'
|
|
23
|
+
* `workbook.xlsx.load`, which is a zip reader — so every CSV failed with
|
|
24
|
+
* "Can't find end of central directory : is this a zip file ?". The export
|
|
25
|
+
* writes CSV by default, so the round trip could not close.
|
|
26
|
+
*
|
|
27
|
+
* A blank header keeps its column rather than collapsing it, so the cells to its
|
|
28
|
+
* right stay under their own names.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseCsvToObjects(text: string): ParsedCsv;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Entity, RebaseData } from "@rebasepro/types";
|
|
2
|
+
/** Rows written per request, and per transaction where the data source has one. */
|
|
3
|
+
export declare const IMPORT_BATCH_SIZE = 25;
|
|
4
|
+
/**
|
|
5
|
+
* An import failure that says where it stopped.
|
|
6
|
+
*
|
|
7
|
+
* "Error saving data / null value in column "sku" violates not-null constraint"
|
|
8
|
+
* on a ten-thousand-row import named no row, and the Retry button restarted at
|
|
9
|
+
* zero — where the rows already written now conflicted, so it could never
|
|
10
|
+
* succeed. Both halves of that are what this carries.
|
|
11
|
+
*/
|
|
12
|
+
export interface ImportSaveError extends Error {
|
|
13
|
+
/** Rows written and confirmed before the failure. Where a retry resumes. */
|
|
14
|
+
committed: number;
|
|
15
|
+
/** Index of the first row that was not written, 0-based. */
|
|
16
|
+
failedFrom: number;
|
|
17
|
+
/** Index after the last row of the batch that was not written. */
|
|
18
|
+
failedTo: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Write imported entities in batches, reporting what was committed.
|
|
22
|
+
*
|
|
23
|
+
* Resumable on purpose: a failure at row 4 000 leaves rows 1–3 999 in the table
|
|
24
|
+
* whatever this does, so the only useful thing to report is where to carry on
|
|
25
|
+
* from. `offset` is that point — the Retry button passes back the `committed`
|
|
26
|
+
* count of the error it is retrying, instead of starting from zero and
|
|
27
|
+
* conflicting on every row that already exists.
|
|
28
|
+
*
|
|
29
|
+
* @throws {ImportSaveError} naming the rows that were not written.
|
|
30
|
+
*/
|
|
31
|
+
export declare function saveImportedEntities(dataClient: RebaseData, path: string, data: Partial<Entity<any>>[], { offset, batchSize, onBatchCommitted, bulkUnsupported }?: {
|
|
32
|
+
offset?: number;
|
|
33
|
+
batchSize?: number;
|
|
34
|
+
onBatchCommitted?: (rowsWritten: number) => void;
|
|
35
|
+
bulkUnsupported?: {
|
|
36
|
+
current: boolean;
|
|
37
|
+
};
|
|
38
|
+
}): Promise<void>;
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Take an object with keys of type `address.street`, `address.city` and
|
|
3
3
|
* convert it to an object with nested objects like `{ address: { street: ..., city: ... } }`
|
|
4
|
+
*
|
|
5
|
+
* Keys here are the **header row of an uploaded file**, so they are attacker
|
|
6
|
+
* data. `res["__proto__"] = …` is the prototype setter rather than an own
|
|
7
|
+
* property, so a column named `__proto__.polluted` walked out of the accumulator
|
|
8
|
+
* and wrote onto `Object.prototype` for the life of the tab — and
|
|
9
|
+
* `constructor.prototype.x` threw a `TypeError` that surfaced as an unreadable
|
|
10
|
+
* file. Such a column is refused, not sanitised, the way `setIn` settled it
|
|
11
|
+
* (`docs/bug-classes.md` class 22).
|
|
12
|
+
*
|
|
4
13
|
* @param flatObj
|
|
5
14
|
*/
|
|
6
15
|
export declare function unflattenObject(flatObj: {
|