@firecms/core 3.0.0-canary.4 → 3.0.0-canary.40
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/README.md +2 -2
- package/dist/components/ClearFilterSortButton.d.ts +5 -0
- package/dist/components/EntityCollectionTable/EntityCollectionRowActions.d.ts +1 -1
- package/dist/components/EntityCollectionTable/EntityCollectionTable.d.ts +2 -2
- package/dist/components/EntityCollectionTable/PropertyTableCell.d.ts +2 -2
- package/dist/components/EntityCollectionTable/internal/CollectionTableToolbar.d.ts +1 -4
- package/dist/components/EntityCollectionView/EntityCollectionView.d.ts +1 -2
- package/dist/components/EntityCollectionView/EntityCollectionViewStartActions.d.ts +11 -0
- package/dist/components/EntityCollectionView/useSelectionController.d.ts +2 -0
- package/dist/components/EntityPreview.d.ts +25 -7
- package/dist/components/EntityView.d.ts +11 -0
- package/dist/components/FieldCaption.d.ts +5 -0
- package/dist/components/HomePage/NavigationCard.d.ts +8 -0
- package/dist/components/HomePage/{NavigationCollectionCard.d.ts → NavigationCardBinding.d.ts} +2 -2
- package/dist/components/HomePage/SmallNavigationCard.d.ts +6 -0
- package/dist/components/HomePage/index.d.ts +3 -1
- package/dist/components/VirtualTable/VirtualTableProps.d.ts +1 -1
- package/dist/components/index.d.ts +4 -3
- package/dist/contexts/AuthControllerContext.d.ts +1 -1
- package/dist/{internal/EntityView.d.ts → core/EntityEditView.d.ts} +2 -2
- package/dist/core/SideEntityView.d.ts +7 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/form/EntityForm.d.ts +1 -1
- package/dist/form/components/StorageItemPreview.d.ts +3 -2
- package/dist/form/components/StorageUploadProgress.d.ts +1 -1
- package/dist/form/components/index.d.ts +1 -0
- package/dist/form/field_bindings/KeyValueFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/MapFieldBinding.d.ts +1 -1
- package/dist/form/field_bindings/StorageUploadFieldBinding.d.ts +4 -3
- package/dist/form/field_bindings/TextFieldBinding.d.ts +2 -2
- package/dist/form/index.d.ts +1 -0
- package/dist/form/validation.d.ts +1 -1
- package/dist/hooks/data/delete.d.ts +2 -2
- package/dist/hooks/data/save.d.ts +1 -1
- package/dist/hooks/data/useDataSource.d.ts +2 -2
- package/dist/hooks/data/useEntityFetch.d.ts +3 -3
- package/dist/hooks/index.d.ts +3 -1
- package/dist/{core → hooks}/useBuildModeController.d.ts +1 -1
- package/dist/hooks/useBuildNavigationController.d.ts +6 -4
- package/dist/hooks/useProjectLog.d.ts +6 -2
- package/dist/hooks/useStorageSource.d.ts +2 -2
- package/dist/hooks/useValidateAuthenticator.d.ts +25 -0
- package/dist/index.es.js +8343 -7846
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +5 -5
- package/dist/index.umd.js.map +1 -1
- package/dist/internal/useBuildDataSource.d.ts +4 -0
- package/dist/preview/PropertyPreview.d.ts +1 -1
- package/dist/preview/PropertyPreviewProps.d.ts +1 -4
- package/dist/preview/components/BooleanPreview.d.ts +5 -1
- package/dist/preview/components/EnumValuesChip.d.ts +1 -1
- package/dist/preview/components/ReferencePreview.d.ts +1 -7
- package/dist/types/analytics.d.ts +1 -1
- package/dist/types/auth.d.ts +37 -1
- package/dist/types/collections.d.ts +22 -5
- package/dist/types/datasource.d.ts +1 -1
- package/dist/types/entities.d.ts +1 -1
- package/dist/types/entity_callbacks.d.ts +2 -2
- package/dist/types/entity_overrides.d.ts +6 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/navigation.d.ts +14 -13
- package/dist/types/permissions.d.ts +5 -1
- package/dist/types/plugins.d.ts +20 -20
- package/dist/types/properties.d.ts +2 -2
- package/dist/types/property_config.d.ts +2 -2
- package/dist/types/roles.d.ts +31 -0
- package/dist/types/storage.d.ts +11 -3
- package/dist/types/user.d.ts +5 -0
- package/dist/util/collections.d.ts +9 -1
- package/dist/util/entities.d.ts +1 -1
- package/dist/util/icons.d.ts +8 -2
- package/dist/util/permissions.d.ts +4 -4
- package/dist/util/references.d.ts +4 -2
- package/dist/util/resolutions.d.ts +1 -1
- package/dist/util/useTraceUpdate.d.ts +1 -0
- package/package.json +24 -24
- package/src/components/ClearFilterSortButton.tsx +41 -0
- package/src/components/DeleteEntityDialog.tsx +4 -4
- package/src/components/EntityCollectionTable/EntityCollectionRowActions.tsx +2 -2
- package/src/components/EntityCollectionTable/EntityCollectionTable.tsx +268 -277
- package/src/components/EntityCollectionTable/EntityCollectionTableProps.tsx +1 -1
- package/src/components/EntityCollectionTable/PropertyTableCell.tsx +13 -13
- package/src/components/EntityCollectionTable/fields/TableReferenceField.tsx +9 -16
- package/src/components/EntityCollectionTable/fields/TableStorageUpload.tsx +3 -3
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +27 -32
- package/src/components/EntityCollectionTable/internal/default_entity_actions.tsx +9 -5
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +39 -49
- package/src/components/EntityCollectionView/EntityCollectionViewActions.tsx +5 -6
- package/src/components/EntityCollectionView/EntityCollectionViewStartActions.tsx +68 -0
- package/src/components/EntityCollectionView/useSelectionController.tsx +30 -0
- package/src/components/EntityPreview.tsx +207 -70
- package/src/components/EntityView.tsx +84 -0
- package/src/components/FieldCaption.tsx +14 -0
- package/src/components/FireCMSAppBar.tsx +8 -0
- package/src/components/HomePage/DefaultHomePage.tsx +14 -10
- package/src/components/HomePage/NavigationCard.tsx +69 -0
- package/src/components/HomePage/NavigationCardBinding.tsx +116 -0
- package/src/components/HomePage/SmallNavigationCard.tsx +45 -0
- package/src/components/HomePage/index.tsx +3 -1
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +3 -4
- package/src/components/ReferenceWidget.tsx +4 -4
- package/src/components/SelectableTable/filters/DateTimeFilterField.tsx +23 -8
- package/src/components/SelectableTable/filters/ReferenceFilterField.tsx +35 -24
- package/src/components/SelectableTable/filters/StringNumberFilterField.tsx +35 -15
- package/src/components/VirtualTable/VirtualTableProps.tsx +1 -1
- package/src/components/VirtualTable/fields/VirtualTableDateField.tsx +1 -1
- package/src/components/common/useDataSourceEntityCollectionTableController.tsx +1 -1
- package/src/components/index.tsx +4 -3
- package/src/contexts/AuthControllerContext.tsx +1 -1
- package/src/core/Drawer.tsx +66 -39
- package/src/{internal/EntityView.tsx → core/EntityEditView.tsx} +22 -39
- package/src/core/EntitySidePanel.tsx +2 -2
- package/src/core/FireCMS.tsx +18 -2
- package/src/core/NavigationRoutes.tsx +8 -0
- package/src/core/SideEntityView.tsx +38 -0
- package/src/core/field_configs.tsx +1 -2
- package/src/core/index.tsx +0 -2
- package/src/form/EntityForm.tsx +20 -12
- package/src/form/components/StorageItemPreview.tsx +5 -3
- package/src/form/components/StorageUploadProgress.tsx +6 -5
- package/src/form/components/index.tsx +1 -0
- package/src/form/field_bindings/ArrayCustomShapedFieldBinding.tsx +2 -3
- package/src/form/field_bindings/ArrayOfReferencesFieldBinding.tsx +12 -15
- package/src/form/field_bindings/BlockFieldBinding.tsx +2 -3
- package/src/form/field_bindings/DateTimeFieldBinding.tsx +4 -4
- package/src/form/field_bindings/KeyValueFieldBinding.tsx +18 -18
- package/src/form/field_bindings/MapFieldBinding.tsx +17 -17
- package/src/form/field_bindings/MarkdownFieldBinding.tsx +1 -2
- package/src/form/field_bindings/MultiSelectBinding.tsx +2 -3
- package/src/form/field_bindings/ReadOnlyFieldBinding.tsx +3 -3
- package/src/form/field_bindings/ReferenceFieldBinding.tsx +5 -3
- package/src/form/field_bindings/RepeatFieldBinding.tsx +3 -3
- package/src/form/field_bindings/SelectFieldBinding.tsx +2 -3
- package/src/form/field_bindings/StorageUploadFieldBinding.tsx +15 -6
- package/src/form/field_bindings/SwitchFieldBinding.tsx +2 -3
- package/src/form/field_bindings/TextFieldBinding.tsx +10 -9
- package/src/form/index.tsx +1 -0
- package/src/form/validation.ts +3 -4
- package/src/hooks/data/delete.ts +3 -3
- package/src/hooks/data/save.ts +1 -1
- package/src/hooks/data/useCollectionFetch.tsx +1 -1
- package/src/hooks/data/useDataSource.tsx +8 -3
- package/src/hooks/data/useEntityFetch.tsx +4 -4
- package/src/hooks/index.tsx +5 -1
- package/src/{core → hooks}/useBuildLocalConfigurationPersistence.tsx +9 -10
- package/src/{core → hooks}/useBuildModeController.tsx +12 -6
- package/src/hooks/useBuildNavigationController.tsx +190 -72
- package/src/hooks/useProjectLog.tsx +16 -6
- package/src/hooks/useReferenceDialog.tsx +2 -2
- package/src/hooks/useStorageSource.tsx +7 -2
- package/src/hooks/useValidateAuthenticator.tsx +135 -0
- package/src/internal/useBuildDataSource.ts +6 -1
- package/src/internal/useBuildSideEntityController.tsx +18 -12
- package/src/preview/PropertyPreview.tsx +1 -1
- package/src/preview/PropertyPreviewProps.tsx +1 -11
- package/src/preview/components/BooleanPreview.tsx +19 -4
- package/src/preview/components/EnumValuesChip.tsx +1 -1
- package/src/preview/components/ReferencePreview.tsx +55 -147
- package/src/preview/property_previews/StringPropertyPreview.tsx +8 -7
- package/src/types/analytics.ts +1 -0
- package/src/types/auth.tsx +50 -1
- package/src/types/collections.ts +24 -5
- package/src/types/datasource.ts +1 -1
- package/src/types/entities.ts +1 -1
- package/src/types/entity_actions.tsx +4 -0
- package/src/types/entity_callbacks.ts +2 -2
- package/src/types/entity_overrides.tsx +7 -0
- package/src/types/firecms.tsx +0 -1
- package/src/types/index.ts +2 -0
- package/src/types/navigation.ts +17 -16
- package/src/types/permissions.ts +6 -1
- package/src/types/plugins.tsx +26 -28
- package/src/types/properties.ts +3 -2
- package/src/types/property_config.tsx +2 -2
- package/src/types/roles.ts +41 -0
- package/src/types/side_entity_controller.tsx +1 -0
- package/src/types/storage.ts +12 -3
- package/src/types/user.ts +7 -0
- package/src/util/collections.ts +22 -0
- package/src/util/entities.ts +1 -1
- package/src/util/icons.tsx +11 -3
- package/src/util/permissions.ts +11 -8
- package/src/util/references.ts +36 -5
- package/src/util/strings.ts +2 -2
- package/src/util/useTraceUpdate.tsx +2 -1
- package/src/components/HomePage/NavigationCollectionCard.tsx +0 -146
- /package/dist/{components → form/components}/LabelWithIcon.d.ts +0 -0
- /package/dist/{core → hooks}/useBuildLocalConfigurationPersistence.d.ts +0 -0
- /package/src/{components → form/components}/LabelWithIcon.tsx +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ArrowForwardIcon, cardClickableMixin, cardMixin, cn, focusedMixin, Typography, } from "@firecms/ui";
|
|
2
|
+
|
|
3
|
+
import { Link as ReactLink } from "react-router-dom";
|
|
4
|
+
|
|
5
|
+
export type SmallNavigationCardProps = {
|
|
6
|
+
name: string,
|
|
7
|
+
url: string;
|
|
8
|
+
icon: React.ReactElement;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function SmallNavigationCard({
|
|
12
|
+
name,
|
|
13
|
+
url,
|
|
14
|
+
icon,
|
|
15
|
+
}: SmallNavigationCardProps) {
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
|
|
20
|
+
<ReactLink
|
|
21
|
+
tabIndex={0}
|
|
22
|
+
className={cn(cardMixin,
|
|
23
|
+
cardClickableMixin,
|
|
24
|
+
focusedMixin,
|
|
25
|
+
"cursor-pointer flex flex-row items-center px-4 py-2 text-inherit dark:text-inherit visited:text-inherit visited:dark:text-inherit hover:text-inherit hover:dark:text-inherit ")}
|
|
26
|
+
to={url}
|
|
27
|
+
>
|
|
28
|
+
|
|
29
|
+
<div className="flex flex-row items-center flex-grow gap-2 ">
|
|
30
|
+
{icon}
|
|
31
|
+
|
|
32
|
+
<Typography gutterBottom variant="h5"
|
|
33
|
+
component="h2"
|
|
34
|
+
className="mb-0 ml-4">
|
|
35
|
+
{name}
|
|
36
|
+
</Typography>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div className={"p-4"}>
|
|
40
|
+
<ArrowForwardIcon color="primary"/>
|
|
41
|
+
</div>
|
|
42
|
+
</ReactLink>
|
|
43
|
+
|
|
44
|
+
</>);
|
|
45
|
+
}
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { ErrorView } from "../ErrorView";
|
|
18
18
|
import { AddIcon, Button, DialogActions, Typography } from "@firecms/ui";
|
|
19
19
|
import { canCreateEntity, fullPathToCollectionSegments, resolveCollection } from "../../util";
|
|
20
|
-
import { useSelectionController } from "../EntityCollectionView/
|
|
20
|
+
import { useSelectionController } from "../EntityCollectionView/useSelectionController";
|
|
21
21
|
import { useColumnIds, useTableSearchHelper } from "../common";
|
|
22
22
|
import { useSideDialogContext } from "../../core";
|
|
23
23
|
import { useAnalyticsController } from "../../hooks/useAnalyticsController";
|
|
@@ -109,7 +109,7 @@ export function ReferenceSelectionTable<M extends Record<string, any>>(
|
|
|
109
109
|
|
|
110
110
|
const fullPath = navigation.resolveAliasesFrom(pathInput);
|
|
111
111
|
|
|
112
|
-
const dataSource = useDataSource();
|
|
112
|
+
const dataSource = useDataSource(collection);
|
|
113
113
|
|
|
114
114
|
const [entitiesDisplayedFirst, setEntitiesDisplayedFirst] = useState<Entity<any>[]>([]);
|
|
115
115
|
|
|
@@ -349,7 +349,7 @@ function ReferenceDialogActions({
|
|
|
349
349
|
onNewClick();
|
|
350
350
|
}
|
|
351
351
|
: undefined;
|
|
352
|
-
const addButton = canCreateEntity(collection, authController,
|
|
352
|
+
const addButton = canCreateEntity(collection, authController, path, null) &&
|
|
353
353
|
onClick && (largeLayout
|
|
354
354
|
? <Button
|
|
355
355
|
onClick={onClick}
|
|
@@ -360,7 +360,6 @@ function ReferenceDialogActions({
|
|
|
360
360
|
</Button>
|
|
361
361
|
: <Button
|
|
362
362
|
onClick={onClick}
|
|
363
|
-
size="medium"
|
|
364
363
|
variant="outlined"
|
|
365
364
|
color="primary"
|
|
366
365
|
>
|
|
@@ -50,11 +50,11 @@ export function ReferenceWidget<M extends Record<string, any>>({
|
|
|
50
50
|
|
|
51
51
|
const collection: EntityCollection | undefined = useMemo(() => {
|
|
52
52
|
return navigationController.getCollection(path);
|
|
53
|
-
}, [path, navigationController]);
|
|
53
|
+
}, [path, navigationController.getCollection]);
|
|
54
54
|
|
|
55
|
-
if (!collection) {
|
|
56
|
-
|
|
57
|
-
}
|
|
55
|
+
// if (!collection) {
|
|
56
|
+
// throw Error(`Couldn't find the corresponding collection for the path: ${path}`);
|
|
57
|
+
// }
|
|
58
58
|
|
|
59
59
|
const onSingleEntitySelected = useCallback((entity: Entity<M> | null) => {
|
|
60
60
|
if (disabled)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { VirtualTableWhereFilterOp } from "../../VirtualTable";
|
|
3
|
-
import { DateTimeField, Select, SelectItem } from "@firecms/ui";
|
|
3
|
+
import { Checkbox, DateTimeField, Label, Select, SelectItem } from "@firecms/ui";
|
|
4
4
|
import { useCustomizationController } from "../../../hooks";
|
|
5
5
|
|
|
6
6
|
interface DateTimeFilterFieldProps {
|
|
@@ -43,10 +43,10 @@ export function DateTimeFilterField({
|
|
|
43
43
|
|
|
44
44
|
const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
|
|
45
45
|
const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
|
|
46
|
-
const [internalValue, setInternalValue] = useState<Date | undefined>(fieldValue);
|
|
46
|
+
const [internalValue, setInternalValue] = useState<Date | null | undefined>(fieldValue);
|
|
47
47
|
|
|
48
|
-
function updateFilter(op: VirtualTableWhereFilterOp, val: Date | undefined) {
|
|
49
|
-
let newValue: Date | undefined = val;
|
|
48
|
+
function updateFilter(op: VirtualTableWhereFilterOp, val: Date | undefined | null) {
|
|
49
|
+
let newValue: Date | null | undefined = val;
|
|
50
50
|
const prevOpIsArray = multipleSelectOperations.includes(operation);
|
|
51
51
|
const newOpIsArray = multipleSelectOperations.includes(op);
|
|
52
52
|
if (prevOpIsArray !== newOpIsArray) {
|
|
@@ -73,7 +73,7 @@ export function DateTimeFilterField({
|
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
75
|
|
|
76
|
-
<div className="flex w-[440px]
|
|
76
|
+
<div className="flex w-[440px]">
|
|
77
77
|
<div className="w-[80px]">
|
|
78
78
|
<Select value={operation}
|
|
79
79
|
onValueChange={(value) => {
|
|
@@ -88,19 +88,34 @@ export function DateTimeFilterField({
|
|
|
88
88
|
</Select>
|
|
89
89
|
</div>
|
|
90
90
|
|
|
91
|
-
<div className="flex-grow ml-2">
|
|
91
|
+
<div className="flex-grow ml-2 flex flex-col gap-2">
|
|
92
92
|
|
|
93
93
|
<DateTimeField
|
|
94
94
|
mode={mode}
|
|
95
95
|
size={"medium"}
|
|
96
96
|
locale={locale}
|
|
97
|
-
value={internalValue}
|
|
98
|
-
onChange={(dateValue: Date |
|
|
97
|
+
value={internalValue ?? undefined}
|
|
98
|
+
onChange={(dateValue: Date | undefined) => {
|
|
99
99
|
updateFilter(operation, dateValue === null ? undefined : dateValue);
|
|
100
100
|
}}
|
|
101
101
|
clearable={true}
|
|
102
102
|
/>
|
|
103
103
|
|
|
104
|
+
<Label
|
|
105
|
+
className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
|
|
106
|
+
htmlFor="null-filter"
|
|
107
|
+
>
|
|
108
|
+
<Checkbox id="null-filter"
|
|
109
|
+
checked={internalValue === null}
|
|
110
|
+
size={"small"}
|
|
111
|
+
onCheckedChange={(checked) => {
|
|
112
|
+
if (internalValue !== null)
|
|
113
|
+
updateFilter(operation, null);
|
|
114
|
+
else updateFilter(operation, undefined);
|
|
115
|
+
}}/>
|
|
116
|
+
Filter for null values
|
|
117
|
+
</Label>
|
|
118
|
+
|
|
104
119
|
</div>
|
|
105
120
|
|
|
106
121
|
</div>
|
|
@@ -4,7 +4,7 @@ import { Entity, EntityCollection, EntityReference } from "../../../types";
|
|
|
4
4
|
import { ReferencePreview } from "../../../preview";
|
|
5
5
|
import { getReferenceFrom } from "../../../util";
|
|
6
6
|
import { useNavigationController, useReferenceDialog } from "../../../hooks";
|
|
7
|
-
import { Button, Select, SelectItem } from "@firecms/ui";
|
|
7
|
+
import { Button, Checkbox, Label, Select, SelectItem } from "@firecms/ui";
|
|
8
8
|
|
|
9
9
|
interface ReferenceFilterFieldProps {
|
|
10
10
|
name: string,
|
|
@@ -48,33 +48,31 @@ export function ReferenceFilterField({
|
|
|
48
48
|
? ["array-contains"]
|
|
49
49
|
: ["==", "!=", ">", "<", ">=", "<="];
|
|
50
50
|
|
|
51
|
-
const [onHover, setOnHover] = React.useState(false);
|
|
52
|
-
|
|
53
51
|
isArray
|
|
54
52
|
? possibleOperations.push("array-contains-any")
|
|
55
53
|
: possibleOperations.push("in", "not-in");
|
|
56
54
|
|
|
57
55
|
const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
|
|
58
56
|
const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
|
|
59
|
-
const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined>(fieldValue);
|
|
57
|
+
const [internalValue, setInternalValue] = useState<EntityReference | EntityReference[] | undefined | null>(fieldValue);
|
|
60
58
|
|
|
61
59
|
const selectedEntityIds = internalValue
|
|
62
60
|
? (Array.isArray(internalValue) ? internalValue.map((ref) => {
|
|
63
|
-
if (!(ref
|
|
61
|
+
if (!(ref?.isEntityReference && ref?.isEntityReference())) {
|
|
64
62
|
return null;
|
|
65
63
|
}
|
|
66
64
|
return ref.id;
|
|
67
65
|
}).filter(Boolean) as string[] : [internalValue.id])
|
|
68
66
|
: [];
|
|
69
67
|
|
|
70
|
-
function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[]) {
|
|
68
|
+
function updateFilter(op: VirtualTableWhereFilterOp, val?: EntityReference | EntityReference[] | null) {
|
|
71
69
|
|
|
72
70
|
const prevOpIsArray = multipleSelectOperations.includes(operation);
|
|
73
71
|
const newOpIsArray = multipleSelectOperations.includes(op);
|
|
74
72
|
let newValue = val;
|
|
75
73
|
if (prevOpIsArray !== newOpIsArray) {
|
|
76
74
|
// @ts-ignore
|
|
77
|
-
newValue = newOpIsArray ? (newValue
|
|
75
|
+
newValue = newOpIsArray ? (newValue?.isEntityReference && newValue?.isEntityReference() ? [newValue] : []) : undefined
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
setOperation(op);
|
|
@@ -129,28 +127,22 @@ export function ReferenceFilterField({
|
|
|
129
127
|
|
|
130
128
|
const buildEntry = (reference: EntityReference) => {
|
|
131
129
|
return (
|
|
132
|
-
<
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
onClick={doOpenDialog}
|
|
142
|
-
reference={reference}
|
|
143
|
-
onHover={onHover}
|
|
144
|
-
allowEntityNavigation={false}
|
|
145
|
-
/>
|
|
146
|
-
</div>
|
|
130
|
+
<ReferencePreview
|
|
131
|
+
disabled={!path}
|
|
132
|
+
previewProperties={previewProperties}
|
|
133
|
+
size={"medium"}
|
|
134
|
+
onClick={doOpenDialog}
|
|
135
|
+
reference={reference}
|
|
136
|
+
hover={true}
|
|
137
|
+
allowEntityNavigation={false}
|
|
138
|
+
/>
|
|
147
139
|
);
|
|
148
140
|
};
|
|
149
141
|
|
|
150
142
|
return (
|
|
151
143
|
|
|
152
144
|
<div className="flex w-[440px] flex-row">
|
|
153
|
-
<div className="w-[
|
|
145
|
+
<div className="w-[140px]">
|
|
154
146
|
<Select value={operation}
|
|
155
147
|
onValueChange={(value) => {
|
|
156
148
|
updateFilter(value as VirtualTableWhereFilterOp, internalValue);
|
|
@@ -164,21 +156,40 @@ export function ReferenceFilterField({
|
|
|
164
156
|
</Select>
|
|
165
157
|
</div>
|
|
166
158
|
|
|
167
|
-
<div className="flex-grow ml-2 h-full">
|
|
159
|
+
<div className="flex-grow ml-2 h-full gap-2 flex flex-col">
|
|
168
160
|
|
|
169
161
|
{internalValue && Array.isArray(internalValue) && <div>
|
|
170
162
|
{internalValue.map((ref, index) => buildEntry(ref))}
|
|
171
163
|
</div>}
|
|
164
|
+
|
|
172
165
|
{internalValue && !Array.isArray(internalValue) && <div>
|
|
173
166
|
{buildEntry(internalValue)}
|
|
174
167
|
</div>}
|
|
168
|
+
|
|
175
169
|
{(!internalValue || (Array.isArray(internalValue) && internalValue.length === 0)) &&
|
|
176
170
|
<Button onClick={doOpenDialog}
|
|
177
171
|
variant={"outlined"}
|
|
172
|
+
size={"large"}
|
|
178
173
|
className="h-full w-full">
|
|
179
174
|
{multiple ? "Select references" : "Select reference"}
|
|
180
175
|
</Button>
|
|
181
176
|
}
|
|
177
|
+
|
|
178
|
+
{!isArray && <Label
|
|
179
|
+
className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
|
|
180
|
+
htmlFor="null-filter"
|
|
181
|
+
>
|
|
182
|
+
<Checkbox id="null-filter"
|
|
183
|
+
checked={internalValue === null}
|
|
184
|
+
size={"small"}
|
|
185
|
+
onCheckedChange={(checked) => {
|
|
186
|
+
if (internalValue !== null)
|
|
187
|
+
updateFilter(operation, null);
|
|
188
|
+
else updateFilter(operation, undefined);
|
|
189
|
+
}}/>
|
|
190
|
+
Filter for null values
|
|
191
|
+
</Label>}
|
|
192
|
+
|
|
182
193
|
</div>
|
|
183
194
|
|
|
184
195
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { EnumValuesChip } from "../../../preview";
|
|
3
3
|
import { VirtualTableWhereFilterOp } from "../../VirtualTable";
|
|
4
|
-
import { ClearIcon, IconButton, Select, SelectItem, TextField } from "@firecms/ui";
|
|
4
|
+
import { Checkbox, ClearIcon, IconButton, Label, Select, SelectItem, TextField } from "@firecms/ui";
|
|
5
5
|
import { EnumValueConfig } from "../../../types";
|
|
6
6
|
|
|
7
7
|
interface StringNumberFilterFieldProps {
|
|
@@ -50,15 +50,15 @@ export function StringNumberFilterField({
|
|
|
50
50
|
|
|
51
51
|
const [fieldOperation, fieldValue] = value || [possibleOperations[0], undefined];
|
|
52
52
|
const [operation, setOperation] = useState<VirtualTableWhereFilterOp>(fieldOperation);
|
|
53
|
-
const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | undefined>(fieldValue);
|
|
53
|
+
const [internalValue, setInternalValue] = useState<string | number | string[] | number[] | null | undefined>(fieldValue);
|
|
54
54
|
|
|
55
|
-
function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | undefined) {
|
|
55
|
+
function updateFilter(op: VirtualTableWhereFilterOp, val: string | number | string[] | number[] | null | undefined) {
|
|
56
56
|
let newValue = val;
|
|
57
57
|
const prevOpIsArray = multipleSelectOperations.includes(operation);
|
|
58
58
|
const newOpIsArray = multipleSelectOperations.includes(op);
|
|
59
59
|
if (prevOpIsArray !== newOpIsArray) {
|
|
60
60
|
// @ts-ignore
|
|
61
|
-
newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) :
|
|
61
|
+
newValue = newOpIsArray ? (typeof val === "string" || typeof val === "number" ? [val] : []) : undefined;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
if (typeof newValue === "number" && isNaN(newValue))
|
|
@@ -84,7 +84,7 @@ export function StringNumberFilterField({
|
|
|
84
84
|
const multiple = multipleSelectOperations.includes(operation);
|
|
85
85
|
return (
|
|
86
86
|
|
|
87
|
-
<div className="flex w-[440px]
|
|
87
|
+
<div className="flex w-[440px]">
|
|
88
88
|
<div className={"w-[80px]"}>
|
|
89
89
|
<Select value={operation}
|
|
90
90
|
position={"item-aligned"}
|
|
@@ -100,11 +100,11 @@ export function StringNumberFilterField({
|
|
|
100
100
|
</Select>
|
|
101
101
|
</div>
|
|
102
102
|
|
|
103
|
-
<div className="flex-grow ml-2">
|
|
103
|
+
<div className="flex-grow ml-2 flex flex-col gap-2">
|
|
104
104
|
|
|
105
105
|
{!enumValues && <TextField
|
|
106
106
|
type={dataType === "number" ? "number" : undefined}
|
|
107
|
-
value={internalValue !== undefined ? String(internalValue) : ""}
|
|
107
|
+
value={internalValue !== undefined && internalValue != null ? String(internalValue) : ""}
|
|
108
108
|
onChange={(evt) => {
|
|
109
109
|
const val = dataType === "number"
|
|
110
110
|
? parseFloat(evt.target.value)
|
|
@@ -118,26 +118,31 @@ export function StringNumberFilterField({
|
|
|
118
118
|
/>}
|
|
119
119
|
|
|
120
120
|
{enumValues &&
|
|
121
|
-
|
|
122
121
|
<Select
|
|
123
122
|
position={"item-aligned"}
|
|
124
123
|
value={internalValue !== undefined
|
|
125
124
|
? (Array.isArray(internalValue) ? internalValue.map(e => String(e)) : String(internalValue))
|
|
126
125
|
: isArray ? [] : ""}
|
|
127
126
|
onValueChange={(value) => {
|
|
128
|
-
|
|
127
|
+
if (value !== "")
|
|
128
|
+
updateFilter(operation, dataType === "number" ? parseInt(value as string) : value as string)
|
|
129
129
|
}}
|
|
130
130
|
multiple={multiple}
|
|
131
131
|
endAdornment={internalValue && <IconButton
|
|
132
|
-
className="absolute right-
|
|
132
|
+
className="absolute right-2 top-3"
|
|
133
133
|
onClick={(e) => updateFilter(operation, undefined)}>
|
|
134
134
|
<ClearIcon/>
|
|
135
135
|
</IconButton>}
|
|
136
|
-
renderValue={(enumKey) =>
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
136
|
+
renderValue={(enumKey) => {
|
|
137
|
+
if (enumKey === null)
|
|
138
|
+
return "Filter for null values";
|
|
139
|
+
|
|
140
|
+
return <EnumValuesChip
|
|
141
|
+
key={`select_value_${name}_${enumKey}`}
|
|
142
|
+
enumKey={enumKey}
|
|
143
|
+
enumValues={enumValues}
|
|
144
|
+
size={"small"}/>;
|
|
145
|
+
}}>
|
|
141
146
|
{enumValues.map((enumConfig) => (
|
|
142
147
|
<SelectItem key={`select_value_${name}_${enumConfig.id}`}
|
|
143
148
|
value={String(enumConfig.id)}>
|
|
@@ -150,6 +155,21 @@ export function StringNumberFilterField({
|
|
|
150
155
|
</Select>
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
{!isArray && <Label
|
|
159
|
+
className="border cursor-pointer rounded-md p-2 flex items-center gap-2 [&:has(:checked)]:bg-gray-100 dark:[&:has(:checked)]:bg-gray-800"
|
|
160
|
+
htmlFor="null-filter"
|
|
161
|
+
>
|
|
162
|
+
<Checkbox id="null-filter"
|
|
163
|
+
checked={internalValue === null}
|
|
164
|
+
size={"small"}
|
|
165
|
+
onCheckedChange={(checked) => {
|
|
166
|
+
if (internalValue !== null)
|
|
167
|
+
updateFilter(operation, null);
|
|
168
|
+
else updateFilter(operation, undefined);
|
|
169
|
+
}}/>
|
|
170
|
+
Filter for null values
|
|
171
|
+
</Label>}
|
|
172
|
+
|
|
153
173
|
</div>
|
|
154
174
|
|
|
155
175
|
</div>
|
|
@@ -252,7 +252,7 @@ export type VirtualTableFilterValues<Key extends string> = Partial<Record<Key, [
|
|
|
252
252
|
|
|
253
253
|
/**
|
|
254
254
|
* Filter conditions in a `Query.where()` clause are specified using the
|
|
255
|
-
* strings
|
|
255
|
+
* strings `<`, `<=`, `==`, `>=`, `>`, `array-contains`, `in`, and `array-contains-any`.
|
|
256
256
|
* @see Table
|
|
257
257
|
* @group Models
|
|
258
258
|
*/
|
|
@@ -25,7 +25,7 @@ export function VirtualTableDateField(props: {
|
|
|
25
25
|
return (
|
|
26
26
|
<DateTimeField
|
|
27
27
|
value={internalValue ?? undefined}
|
|
28
|
-
onChange={(dateValue) => updateValue(dateValue)}
|
|
28
|
+
onChange={(dateValue) => updateValue(dateValue ?? null)}
|
|
29
29
|
size={"medium"}
|
|
30
30
|
invisible={true}
|
|
31
31
|
className={"w-full h-full"}
|
|
@@ -65,7 +65,7 @@ export function useDataSourceEntityCollectionTableController<M extends Record<st
|
|
|
65
65
|
|
|
66
66
|
const [popupCell, setPopupCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
|
|
67
67
|
const navigation = useNavigationController();
|
|
68
|
-
const dataSource = useDataSource();
|
|
68
|
+
const dataSource = useDataSource(collection);
|
|
69
69
|
const resolvedPath = useMemo(() => navigation.resolveAliasesFrom(fullPath), [fullPath, navigation.resolveAliasesFrom]);
|
|
70
70
|
|
|
71
71
|
const forceFilter = forceFilterFromProps ?? forceFilterFromCollection;
|
package/src/components/index.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export type { ErrorViewProps } from "./ErrorView";
|
|
2
2
|
export { ErrorView } from "./ErrorView";
|
|
3
3
|
|
|
4
|
-
export type {
|
|
5
|
-
export {
|
|
4
|
+
export type { EntityViewProps } from "./EntityView";
|
|
5
|
+
export { EntityView } from "./EntityView";
|
|
6
6
|
|
|
7
7
|
export type { ReferenceSelectionInnerProps } from "./ReferenceTable/ReferenceSelectionTable";
|
|
8
8
|
export { ReferenceSelectionTable } from "./ReferenceTable/ReferenceSelectionTable";
|
|
@@ -15,6 +15,7 @@ export * from "./HomePage";
|
|
|
15
15
|
export * from "./SelectableTable/SelectableTable";
|
|
16
16
|
export * from "./EntityCollectionView/EntityCollectionView";
|
|
17
17
|
export * from "./EntityCollectionView/EntityCollectionViewActions";
|
|
18
|
+
export * from "./EntityCollectionView/useSelectionController";
|
|
18
19
|
|
|
19
20
|
export * from "./PropertyConfigBadge";
|
|
20
21
|
|
|
@@ -31,5 +32,5 @@ export * from "./FireCMSAppBar";
|
|
|
31
32
|
|
|
32
33
|
export * from "./ArrayContainer";
|
|
33
34
|
export * from "./ReferenceWidget";
|
|
34
|
-
export * from "./LabelWithIcon";
|
|
35
35
|
export * from "./SearchIconsView";
|
|
36
|
+
export * from "./FieldCaption";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { AuthController } from "../types";
|
|
3
3
|
|
|
4
|
-
export const AuthControllerContext = React.createContext<AuthController
|
|
4
|
+
export const AuthControllerContext = React.createContext<AuthController<any, any>>({} as AuthController<any, any>);
|
package/src/core/Drawer.tsx
CHANGED
|
@@ -2,10 +2,10 @@ import React, { useCallback } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { useLargeLayout, useNavigationController } from "../hooks";
|
|
4
4
|
|
|
5
|
-
import { NavLink } from "react-router-dom";
|
|
5
|
+
import { NavLink, useNavigate } from "react-router-dom";
|
|
6
6
|
import { CMSAnalyticsEvent, TopNavigationEntry, TopNavigationResult } from "../types";
|
|
7
7
|
import { IconForView } from "../util";
|
|
8
|
-
import { cn, Tooltip, Typography } from "@firecms/ui";
|
|
8
|
+
import { cn, IconButton, Menu, MenuItem, MoreVertIcon, Tooltip, Typography } from "@firecms/ui";
|
|
9
9
|
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -33,6 +33,9 @@ export function Drawer({
|
|
|
33
33
|
|
|
34
34
|
const tooltipsOpen = hovered && !drawerOpen;
|
|
35
35
|
const largeLayout = useLargeLayout();
|
|
36
|
+
const navigate = useNavigate();
|
|
37
|
+
|
|
38
|
+
const [adminMenuOpen, setAdminMenuOpen] = React.useState(false);
|
|
36
39
|
|
|
37
40
|
if (!navigation.topLevelNavigation)
|
|
38
41
|
throw Error("Navigation not ready in Drawer");
|
|
@@ -42,7 +45,8 @@ export function Drawer({
|
|
|
42
45
|
groups
|
|
43
46
|
}: TopNavigationResult = navigation.topLevelNavigation;
|
|
44
47
|
|
|
45
|
-
const
|
|
48
|
+
const adminViews = navigationEntries.filter(e => e.type === "admin") ?? [];
|
|
49
|
+
const groupsWithoutAdmin = groups.filter(g => g !== "Admin");
|
|
46
50
|
|
|
47
51
|
const buildGroupHeader = useCallback((group?: string) => {
|
|
48
52
|
if (!drawerOpen) return <div className="h-12 w-full"/>;
|
|
@@ -67,41 +71,64 @@ export function Drawer({
|
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
return (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
74
|
+
<>
|
|
75
|
+
|
|
76
|
+
<div className={"flex-grow overflow-scroll no-scrollbar"}>
|
|
77
|
+
|
|
78
|
+
{groupsWithoutAdmin.map((group) => (
|
|
79
|
+
<React.Fragment
|
|
80
|
+
key={`drawer_group_${group}`}>
|
|
81
|
+
{buildGroupHeader(group)}
|
|
82
|
+
{Object.values(navigationEntries)
|
|
83
|
+
.filter(e => e.group === group)
|
|
84
|
+
.map((view, index) =>
|
|
85
|
+
<DrawerNavigationItem
|
|
86
|
+
key={`navigation_${index}`}
|
|
87
|
+
icon={<IconForView collectionOrView={view.collection ?? view.view}/>}
|
|
88
|
+
tooltipsOpen={tooltipsOpen}
|
|
89
|
+
drawerOpen={drawerOpen}
|
|
90
|
+
onClick={() => onClick(view)}
|
|
91
|
+
url={view.url}
|
|
92
|
+
name={view.name}/>)}
|
|
93
|
+
</React.Fragment>
|
|
94
|
+
))}
|
|
95
|
+
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
{adminViews.length > 0 && <Menu
|
|
99
|
+
open={adminMenuOpen}
|
|
100
|
+
onOpenChange={setAdminMenuOpen}
|
|
101
|
+
trigger={
|
|
102
|
+
<IconButton
|
|
103
|
+
shape={"square"}
|
|
104
|
+
className={"m-4 text-gray-900 dark:text-white w-fit"}>
|
|
105
|
+
<Tooltip title={"Admin"}
|
|
106
|
+
open={tooltipsOpen}
|
|
107
|
+
side={"right"} sideOffset={28}>
|
|
108
|
+
<MoreVertIcon/>
|
|
109
|
+
</Tooltip>
|
|
110
|
+
{drawerOpen && <div
|
|
111
|
+
className={cn(
|
|
112
|
+
drawerOpen ? "opacity-100" : "opacity-0 hidden",
|
|
113
|
+
"mx-4 font-inherit text-inherit"
|
|
114
|
+
)}>
|
|
115
|
+
ADMIN
|
|
116
|
+
</div>}
|
|
117
|
+
</IconButton>}
|
|
118
|
+
>
|
|
119
|
+
{adminViews.map((entry, index) =>
|
|
120
|
+
<MenuItem
|
|
121
|
+
onClick={(event) => {
|
|
122
|
+
event.preventDefault();
|
|
123
|
+
navigate(entry.path);
|
|
124
|
+
}}
|
|
125
|
+
key={`navigation_${index}`}>
|
|
126
|
+
{<IconForView collectionOrView={entry.view}/>}
|
|
127
|
+
{entry.name}
|
|
128
|
+
</MenuItem>)}
|
|
129
|
+
|
|
130
|
+
</Menu>}
|
|
131
|
+
</>
|
|
105
132
|
);
|
|
106
133
|
}
|
|
107
134
|
|
|
@@ -133,7 +160,7 @@ export function DrawerNavigationItem({
|
|
|
133
160
|
transition: drawerOpen ? "width 150ms ease-in" : undefined
|
|
134
161
|
}}
|
|
135
162
|
className={({ isActive }: any) => cn("rounded-r-xl truncate",
|
|
136
|
-
"hover:bg-slate-300 hover:bg-opacity-75 dark:hover:bg-gray-700 dark:hover:bg-opacity-75 text-gray-800 dark:text-gray-200 hover:text-gray-900 hover:dark:text-
|
|
163
|
+
"hover:bg-slate-300 hover:bg-opacity-75 dark:hover:bg-gray-700 dark:hover:bg-opacity-75 text-gray-800 dark:text-gray-200 hover:text-gray-900 hover:dark:text-white",
|
|
137
164
|
"flex flex-row items-center mr-8",
|
|
138
165
|
// "transition-all ease-in-out delay-100 duration-300",
|
|
139
166
|
// drawerOpen ? "w-full" : "w-18",
|