@firecms/core 3.0.0-beta.2-pre.4 → 3.0.0-beta.2
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/components/PropertyIdCopyTooltipContent.d.ts +3 -0
- package/dist/form/PropertyFieldBinding.d.ts +1 -1
- package/dist/hooks/useProjectLog.d.ts +4 -0
- package/dist/index.es.js +2308 -2240
- 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/util/index.d.ts +1 -1
- package/dist/util/useDebouncedCallback.d.ts +1 -0
- package/package.json +7 -7
- package/src/components/ArrayContainer.tsx +3 -9
- package/src/components/EntityCollectionTable/internal/CollectionTableToolbar.tsx +17 -14
- package/src/components/EntityCollectionTable/internal/popup_field/PopupFormField.tsx +10 -1
- package/src/components/EntityCollectionView/EntityCollectionView.tsx +6 -5
- package/src/components/PropertyIdCopyTooltipContent.tsx +28 -0
- package/src/components/ReferenceTable/ReferenceSelectionTable.tsx +0 -1
- package/src/components/SelectableTable/SelectableTable.tsx +2 -2
- package/src/components/VirtualTable/fields/VirtualTableInput.tsx +2 -2
- package/src/components/VirtualTable/fields/VirtualTableNumberInput.tsx +2 -2
- package/src/core/Drawer.tsx +17 -15
- package/src/core/FireCMS.tsx +4 -1
- package/src/core/NavigationRoutes.tsx +10 -8
- package/src/core/Scaffold.tsx +5 -5
- package/src/core/field_configs.tsx +11 -11
- package/src/form/EntityForm.tsx +16 -4
- package/src/form/PropertyFieldBinding.tsx +3 -8
- package/src/form/field_bindings/MapFieldBinding.tsx +8 -6
- package/src/form/field_bindings/RepeatFieldBinding.tsx +4 -2
- package/src/hooks/useProjectLog.tsx +31 -0
- package/src/internal/EntityView.tsx +2 -2
- package/src/util/index.ts +1 -1
- package/src/util/{useDebounce.ts → useDebouncedCallback.ts} +1 -1
- package/dist/hooks/useVerifyLicense.d.ts +0 -0
- package/dist/util/useDebounce.d.ts +0 -1
- package/src/hooks/useVerifyLicense.tsx +0 -1
package/dist/util/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export * from "./objects";
|
|
|
7
7
|
export * from "./paths";
|
|
8
8
|
export * from "./regexp";
|
|
9
9
|
export * from "./navigation_utils";
|
|
10
|
-
export * from "./
|
|
10
|
+
export * from "./useDebouncedCallback";
|
|
11
11
|
export * from "./property_utils";
|
|
12
12
|
export * from "./resolutions";
|
|
13
13
|
export * from "./permissions";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useDebouncedCallback<T>(value: T, callback: () => void, immediate: boolean, timeoutMs?: number): void;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-beta.2
|
|
4
|
+
"version": "3.0.0-beta.2",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -38,15 +38,15 @@
|
|
|
38
38
|
"generateIcons": "ts-node --esm src/icons/generateIcons.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@firecms/formex": "^3.0.0-beta.2
|
|
42
|
-
"@firecms/ui": "^3.0.0-beta.2
|
|
41
|
+
"@firecms/formex": "^3.0.0-beta.2",
|
|
42
|
+
"@firecms/ui": "^3.0.0-beta.2",
|
|
43
43
|
"@fontsource/ibm-plex-mono": "^5.0.8",
|
|
44
44
|
"@fontsource/roboto": "^5.0.8",
|
|
45
45
|
"@hello-pangea/dnd": "^16.5.0",
|
|
46
46
|
"date-fns": "^3.3.1",
|
|
47
47
|
"history": "^5.3.0",
|
|
48
48
|
"js-search": "^2.0.1",
|
|
49
|
-
"markdown-it": "^
|
|
49
|
+
"markdown-it": "^14.0.0",
|
|
50
50
|
"notistack": "^3.0.1",
|
|
51
51
|
"object-hash": "^3.0.0",
|
|
52
52
|
"react-image-file-resizer": "^0.4.8",
|
|
@@ -99,15 +99,15 @@
|
|
|
99
99
|
"react-router-dom": "^6.22.0",
|
|
100
100
|
"ts-jest": "^29.1.2",
|
|
101
101
|
"ts-node": "^10.9.2",
|
|
102
|
-
"tsd": "^0.
|
|
102
|
+
"tsd": "^0.30.4",
|
|
103
103
|
"typescript": "^5.3.3",
|
|
104
|
-
"vite": "^
|
|
104
|
+
"vite": "^5.1.1"
|
|
105
105
|
},
|
|
106
106
|
"files": [
|
|
107
107
|
"dist",
|
|
108
108
|
"src"
|
|
109
109
|
],
|
|
110
|
-
"gitHead": "
|
|
110
|
+
"gitHead": "9c3561bc09311f2339bb6fa224c88a62d3b19617",
|
|
111
111
|
"publishConfig": {
|
|
112
112
|
"access": "public"
|
|
113
113
|
}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
AddIcon,
|
|
8
8
|
Button,
|
|
9
9
|
ContentCopyIcon,
|
|
10
|
-
fieldBackgroundHoverMixin,
|
|
10
|
+
fieldBackgroundHoverMixin, HandleIcon,
|
|
11
11
|
IconButton,
|
|
12
12
|
Menu,
|
|
13
13
|
MenuItem,
|
|
@@ -253,6 +253,7 @@ export function ArrayContainerItem({
|
|
|
253
253
|
</div>;
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
|
|
256
257
|
export function ArrayItemOptions({
|
|
257
258
|
direction,
|
|
258
259
|
disabled,
|
|
@@ -291,14 +292,7 @@ export function ArrayItemOptions({
|
|
|
291
292
|
setMenuOpen(false);
|
|
292
293
|
}}
|
|
293
294
|
className={`cursor-${disabled ? "inherit" : "grab"}`}>
|
|
294
|
-
<
|
|
295
|
-
<circle cx="28" cy="50" r="9" fill={"currentColor"}/>
|
|
296
|
-
<circle cx="28" cy="21" r="9" fill={"currentColor"}/>
|
|
297
|
-
<circle cx="71" cy="21" r="9" fill={"currentColor"}/>
|
|
298
|
-
<circle cx="71" cy="50" r="9" fill={"currentColor"}/>
|
|
299
|
-
<circle cx="71" cy="78" r="9" fill={"currentColor"}/>
|
|
300
|
-
<circle cx="28" cy="78" r="9" fill={"currentColor"}/>
|
|
301
|
-
</svg>
|
|
295
|
+
<HandleIcon/>
|
|
302
296
|
</IconButton>
|
|
303
297
|
|
|
304
298
|
<Menu
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
FilterListOffIcon,
|
|
9
9
|
SearchBar,
|
|
10
10
|
Select,
|
|
11
|
-
SelectItem
|
|
11
|
+
SelectItem,
|
|
12
|
+
Tooltip
|
|
12
13
|
} from "@firecms/ui";
|
|
13
14
|
import { CollectionSize } from "../../../types";
|
|
14
15
|
import { useLargeLayout } from "../../../hooks";
|
|
@@ -54,19 +55,21 @@ export function CollectionTableToolbar(props: CollectionTableToolbarProps) {
|
|
|
54
55
|
</Button>;
|
|
55
56
|
|
|
56
57
|
const sizeSelect = (
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{size
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
<Tooltip title={"Table row size"} side={"right"} sideOffset={4}>
|
|
59
|
+
<Select
|
|
60
|
+
value={props.size as string}
|
|
61
|
+
className="w-16 h-10"
|
|
62
|
+
size={"small"}
|
|
63
|
+
onValueChange={(v) => props.onSizeChanged(v as CollectionSize)}
|
|
64
|
+
renderValue={(v) => <div className={"font-medium"}>{v.toUpperCase()}</div>}
|
|
65
|
+
>
|
|
66
|
+
{["xs", "s", "m", "l", "xl"].map((size) => (
|
|
67
|
+
<SelectItem key={size} value={size} className={"w-12 font-medium text-center"}>
|
|
68
|
+
{size.toUpperCase()}
|
|
69
|
+
</SelectItem>
|
|
70
|
+
))}
|
|
71
|
+
</Select>
|
|
72
|
+
</Tooltip>
|
|
70
73
|
);
|
|
71
74
|
|
|
72
75
|
return (
|
|
@@ -224,7 +224,15 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
|
|
|
224
224
|
const formex = useCreateFormex<M>({
|
|
225
225
|
initialValues: (entity?.values ?? {}) as EntityValues<M>,
|
|
226
226
|
validation: (values) => {
|
|
227
|
-
return validationSchema?.validate(values)
|
|
227
|
+
return validationSchema?.validate(values, { abortEarly: false })
|
|
228
|
+
.then(() => ({}))
|
|
229
|
+
.catch((e) => {
|
|
230
|
+
const errors: Record<string, string> = {};
|
|
231
|
+
e.inner.forEach((error: any) => {
|
|
232
|
+
errors[error.path] = error.message;
|
|
233
|
+
});
|
|
234
|
+
return errors;
|
|
235
|
+
});
|
|
228
236
|
},
|
|
229
237
|
validateOnInitialRender: true,
|
|
230
238
|
onSubmit: (values, actions) => {
|
|
@@ -339,6 +347,7 @@ export function PopupFormFieldInternal<M extends Record<string, any>>({
|
|
|
339
347
|
}
|
|
340
348
|
const form = <div
|
|
341
349
|
className={`text-gray-900 dark:text-white overflow-auto rounded rounded-md bg-white dark:bg-gray-950 ${!open ? "hidden" : ""} cursor-grab max-w-[100vw]`}>
|
|
350
|
+
|
|
342
351
|
{internalForm}
|
|
343
352
|
|
|
344
353
|
{savingError &&
|
|
@@ -53,7 +53,6 @@ import {
|
|
|
53
53
|
Popover,
|
|
54
54
|
SearchIcon,
|
|
55
55
|
Skeleton,
|
|
56
|
-
TextField,
|
|
57
56
|
Tooltip,
|
|
58
57
|
Typography
|
|
59
58
|
} from "@firecms/ui";
|
|
@@ -670,6 +669,7 @@ export const EntityCollectionView = React.memo(
|
|
|
670
669
|
equal(a.isSubCollection, b.isSubCollection) &&
|
|
671
670
|
equal(a.className, b.className) &&
|
|
672
671
|
equal(a.properties, b.properties) &&
|
|
672
|
+
equal(a.propertiesOrder, b.propertiesOrder) &&
|
|
673
673
|
equal(a.hideIdFromCollection, b.hideIdFromCollection) &&
|
|
674
674
|
equal(a.inlineEditing, b.inlineEditing) &&
|
|
675
675
|
equal(a.selectionEnabled, b.selectionEnabled) &&
|
|
@@ -804,13 +804,14 @@ function EntityIdHeaderWidget({
|
|
|
804
804
|
}}
|
|
805
805
|
className={"text-gray-900 dark:text-white w-96 max-w-full"}>
|
|
806
806
|
|
|
807
|
-
<div className="flex p-
|
|
808
|
-
<
|
|
807
|
+
<div className="flex p-2 w-full gap-4">
|
|
808
|
+
<input
|
|
809
|
+
autoFocus={openPopup}
|
|
809
810
|
placeholder={"Find entity by ID"}
|
|
810
|
-
size={"small"}
|
|
811
|
+
// size={"small"}
|
|
811
812
|
onChange={(e) => setSearchString(e.target.value)}
|
|
812
813
|
value={searchString}
|
|
813
|
-
className={"flex-grow"}/>
|
|
814
|
+
className={"flex-grow bg-transparent outline-none p-1"}/>
|
|
814
815
|
<Button variant={"outlined"}
|
|
815
816
|
disabled={!searchString}
|
|
816
817
|
type={"submit"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ContentPasteIcon, IconButton, Typography } from "@firecms/ui";
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
|
|
4
|
+
export function PropertyIdCopyTooltipContent({ propertyId }: { propertyId: string }) {
|
|
5
|
+
|
|
6
|
+
const [copied, setCopied] = useState(false);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className={"flex flex-row gap-2 items-center justify-center text-white"}>
|
|
10
|
+
<div>
|
|
11
|
+
<Typography variant={"caption"} className={"min-w-20 text-slate-400"}
|
|
12
|
+
color={"disabled"}>{copied ? "Copied" : "Property ID"}</Typography>
|
|
13
|
+
<Typography variant={"caption"} className={"text-white"}><code>{propertyId}</code></Typography>
|
|
14
|
+
</div>
|
|
15
|
+
{/* Copy to clipboard button*/}
|
|
16
|
+
<IconButton size={"small"}>
|
|
17
|
+
<ContentPasteIcon size={"smallest"}
|
|
18
|
+
className={"text-white"}
|
|
19
|
+
onClick={useCallback(() => {
|
|
20
|
+
navigator.clipboard.writeText(propertyId);
|
|
21
|
+
setCopied(true);
|
|
22
|
+
setTimeout(() => setCopied(false), 2000);
|
|
23
|
+
}, [propertyId])}
|
|
24
|
+
/>
|
|
25
|
+
</IconButton>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -115,6 +115,7 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
115
115
|
onEntityClick,
|
|
116
116
|
onColumnResize,
|
|
117
117
|
hoverRow = true,
|
|
118
|
+
size,
|
|
118
119
|
inlineEditing = false,
|
|
119
120
|
tableController:
|
|
120
121
|
{
|
|
@@ -145,7 +146,6 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
145
146
|
}: SelectableTableProps<M>) {
|
|
146
147
|
|
|
147
148
|
const ref = useRef<HTMLDivElement>(null);
|
|
148
|
-
const [size, setSize] = React.useState<CollectionSize>("m");
|
|
149
149
|
|
|
150
150
|
const [selectedCell, setSelectedCell] = React.useState<SelectedCellProps<M> | undefined>(undefined);
|
|
151
151
|
|
|
@@ -207,7 +207,7 @@ export const SelectableTable = React.memo<SelectableTableProps<any>>(
|
|
|
207
207
|
setPopupCell: setPopupCell as ((cell?: SelectedCellProps<M>) => void),
|
|
208
208
|
select,
|
|
209
209
|
onValueChange,
|
|
210
|
-
size,
|
|
210
|
+
size: size ?? "m",
|
|
211
211
|
selectedCell,
|
|
212
212
|
}}
|
|
213
213
|
>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useDebouncedCallback } from "../../../util";
|
|
4
4
|
import { TextareaAutosize } from "@firecms/ui";
|
|
5
5
|
|
|
6
6
|
export function VirtualTableInput(props: {
|
|
@@ -43,7 +43,7 @@ export function VirtualTableInput(props: {
|
|
|
43
43
|
}
|
|
44
44
|
}, [internalValue, updateValue, value]);
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
useDebouncedCallback(internalValue, doUpdate, !focused, 2000);
|
|
47
47
|
|
|
48
48
|
useEffect(() => {
|
|
49
49
|
if (ref.current && focused && !focusedState.current) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useDebouncedCallback } from "../../../util";
|
|
3
3
|
|
|
4
4
|
export function VirtualTableNumberInput(props: {
|
|
5
5
|
error: Error | undefined;
|
|
@@ -42,7 +42,7 @@ export function VirtualTableNumberInput(props: {
|
|
|
42
42
|
|
|
43
43
|
}, [internalValue, value]);
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
useDebouncedCallback(internalValue, doUpdate, !focused, 2000);
|
|
46
46
|
|
|
47
47
|
useEffect(
|
|
48
48
|
() => {
|
package/src/core/Drawer.tsx
CHANGED
|
@@ -45,12 +45,12 @@ export function Drawer({
|
|
|
45
45
|
const ungroupedNavigationViews = Object.values(navigationEntries).filter(e => !e.group);
|
|
46
46
|
|
|
47
47
|
const buildGroupHeader = useCallback((group?: string) => {
|
|
48
|
-
if (!drawerOpen) return <div className="h-
|
|
48
|
+
if (!drawerOpen) return <div className="h-12 w-full"/>;
|
|
49
49
|
return <div
|
|
50
50
|
className="pt-8 pl-6 pr-8 pb-2 flex flex-row items-center">
|
|
51
51
|
<Typography variant={"caption"}
|
|
52
52
|
color={"secondary"}
|
|
53
|
-
className="font-medium flex-grow">
|
|
53
|
+
className="font-medium flex-grow line-clamp-1">
|
|
54
54
|
{group ? group.toUpperCase() : "Views".toUpperCase()}
|
|
55
55
|
</Typography>
|
|
56
56
|
|
|
@@ -128,12 +128,18 @@ export function DrawerNavigationItem({
|
|
|
128
128
|
|
|
129
129
|
const listItem = <NavLink
|
|
130
130
|
onClick={onClick}
|
|
131
|
+
style={{
|
|
132
|
+
width: !drawerOpen ? "72px" : "280px",
|
|
133
|
+
transition: drawerOpen ? "width 150ms ease-in" : undefined
|
|
134
|
+
}}
|
|
131
135
|
className={({ isActive }: any) => cn("rounded-r-xl truncate",
|
|
132
|
-
"hover:bg-
|
|
133
|
-
"flex flex-row items-center
|
|
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-gray-100",
|
|
137
|
+
"flex flex-row items-center mr-8",
|
|
138
|
+
// "transition-all ease-in-out delay-100 duration-300",
|
|
139
|
+
// drawerOpen ? "w-full" : "w-18",
|
|
134
140
|
drawerOpen ? "pl-8 h-12" : "pl-6 h-11",
|
|
135
141
|
"font-medium text-sm",
|
|
136
|
-
isActive ? "bg-
|
|
142
|
+
isActive ? "bg-slate-200 bg-opacity-75 dark:bg-gray-800" : ""
|
|
137
143
|
)}
|
|
138
144
|
to={url}
|
|
139
145
|
>
|
|
@@ -149,14 +155,10 @@ export function DrawerNavigationItem({
|
|
|
149
155
|
</div>
|
|
150
156
|
</NavLink>;
|
|
151
157
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
side="right"
|
|
159
|
-
title={name}>
|
|
160
|
-
{listItem}
|
|
161
|
-
</Tooltip>;
|
|
158
|
+
return <Tooltip
|
|
159
|
+
open={drawerOpen ? false : tooltipsOpen}
|
|
160
|
+
side="right"
|
|
161
|
+
title={name}>
|
|
162
|
+
{listItem}
|
|
163
|
+
</Tooltip>;
|
|
162
164
|
}
|
package/src/core/FireCMS.tsx
CHANGED
|
@@ -19,6 +19,7 @@ import { useBuildDataSource } from "../internal/useBuildDataSource";
|
|
|
19
19
|
import { useBuildCustomizationController } from "../internal/useBuildCustomizationController";
|
|
20
20
|
import { CustomizationControllerContext } from "../contexts/CustomizationControllerContext";
|
|
21
21
|
import { AnalyticsContext } from "../contexts/AnalyticsContext";
|
|
22
|
+
import { useProjectLog } from "../hooks/useProjectLog";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* If you are using independent components of the CMS
|
|
@@ -78,12 +79,14 @@ export function FireCMS<UserType extends User, EC extends EntityCollection>(prop
|
|
|
78
79
|
entityViews: entityViews ?? [],
|
|
79
80
|
propertyConfigs: propertyConfigs ?? {},
|
|
80
81
|
components
|
|
81
|
-
})
|
|
82
|
+
});
|
|
82
83
|
|
|
83
84
|
const analyticsController = useMemo(() => ({
|
|
84
85
|
onAnalyticsEvent
|
|
85
86
|
}), []);
|
|
86
87
|
|
|
88
|
+
useProjectLog(authController);
|
|
89
|
+
|
|
87
90
|
if (navigationController.navigationLoadingError) {
|
|
88
91
|
return (
|
|
89
92
|
<CenteredView maxWidth={"md"}>
|
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
|
|
3
3
|
import { Route, Routes, useLocation } from "react-router-dom";
|
|
4
4
|
import { CMSView } from "../types";
|
|
5
|
-
import { DefaultHomePage, EntityCollectionView, NotFoundPage } from "../components";
|
|
5
|
+
import { DefaultHomePage, EntityCollectionView, ErrorBoundary, NotFoundPage } from "../components";
|
|
6
6
|
import { useNavigationController } from "../hooks";
|
|
7
7
|
import { toArray } from "../util/arrays";
|
|
8
8
|
|
|
@@ -70,13 +70,15 @@ export const NavigationRoutes = React.memo<NavigationRoutesProps>(
|
|
|
70
70
|
return <Route path={urlPath + "/*"}
|
|
71
71
|
key={`navigation_${collection.id ?? collection.path}`}
|
|
72
72
|
element={
|
|
73
|
-
<
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
<ErrorBoundary>
|
|
74
|
+
<EntityCollectionView
|
|
75
|
+
key={`collection_view_${collection.id ?? collection.path}`}
|
|
76
|
+
isSubCollection={false}
|
|
77
|
+
parentCollectionIds={[]}
|
|
78
|
+
fullPath={collection.id ?? collection.path}
|
|
79
|
+
{...collection}
|
|
80
|
+
Actions={toArray(collection.Actions)}/>
|
|
81
|
+
</ErrorBoundary>
|
|
80
82
|
}/>;
|
|
81
83
|
}
|
|
82
84
|
);
|
package/src/core/Scaffold.tsx
CHANGED
|
@@ -176,7 +176,7 @@ function StyledDrawer(props: {
|
|
|
176
176
|
className={"relative h-full no-scrollbar overflow-y-auto overflow-x-hidden"}
|
|
177
177
|
style={{
|
|
178
178
|
width,
|
|
179
|
-
transition: "left
|
|
179
|
+
transition: "left 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms, opacity 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms, width 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms"
|
|
180
180
|
}}
|
|
181
181
|
>
|
|
182
182
|
|
|
@@ -201,8 +201,8 @@ function StyledDrawer(props: {
|
|
|
201
201
|
<div className={"flex flex-col h-full"}>
|
|
202
202
|
<div
|
|
203
203
|
style={{
|
|
204
|
-
transition: "padding
|
|
205
|
-
padding: props.open ? "32px
|
|
204
|
+
transition: "padding 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms",
|
|
205
|
+
padding: props.open ? "32px 144px 0px 24px" : "72px 16px 0px"
|
|
206
206
|
}}
|
|
207
207
|
className={cn("cursor-pointer")}>
|
|
208
208
|
|
|
@@ -215,7 +215,7 @@ function StyledDrawer(props: {
|
|
|
215
215
|
? <img src={props.logo}
|
|
216
216
|
alt="Logo"
|
|
217
217
|
className={cn("max-w-full max-h-full",
|
|
218
|
-
props.open ?? "w-[
|
|
218
|
+
props.open ?? "w-[112px] h-[112px]")}/>
|
|
219
219
|
: <FireCMSLogo/>}
|
|
220
220
|
|
|
221
221
|
</Link>
|
|
@@ -259,7 +259,7 @@ function StyledDrawer(props: {
|
|
|
259
259
|
onMouseLeave={props.onMouseLeave}
|
|
260
260
|
style={{
|
|
261
261
|
width,
|
|
262
|
-
transition: "left
|
|
262
|
+
transition: "left 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms, opacity 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms, width 100ms cubic-bezier(0.4, 0, 0.6, 1) 0ms"
|
|
263
263
|
}}>
|
|
264
264
|
|
|
265
265
|
{innerDrawer}
|
|
@@ -104,6 +104,17 @@ export const DEFAULT_FIELD_CONFIGS: Record<string, PropertyConfig<any>> = {
|
|
|
104
104
|
Field: TextFieldBinding
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
|
+
switch: {
|
|
108
|
+
key: "switch",
|
|
109
|
+
name: "Switch",
|
|
110
|
+
description: "Boolean true or false field (or yes or no, 0 or 1...)",
|
|
111
|
+
Icon: FlagIcon,
|
|
112
|
+
color: "#20d9d2",
|
|
113
|
+
property: {
|
|
114
|
+
dataType: "boolean",
|
|
115
|
+
Field: SwitchFieldBinding
|
|
116
|
+
}
|
|
117
|
+
},
|
|
107
118
|
select: {
|
|
108
119
|
key: "select",
|
|
109
120
|
name: "Select/enum",
|
|
@@ -225,17 +236,6 @@ export const DEFAULT_FIELD_CONFIGS: Record<string, PropertyConfig<any>> = {
|
|
|
225
236
|
Field: ArrayOfReferencesFieldBinding
|
|
226
237
|
}
|
|
227
238
|
},
|
|
228
|
-
switch: {
|
|
229
|
-
key: "switch",
|
|
230
|
-
name: "Switch",
|
|
231
|
-
description: "Boolean true or false field (or yes or no, 0 or 1...)",
|
|
232
|
-
Icon: FlagIcon,
|
|
233
|
-
color: "#20d9d2",
|
|
234
|
-
property: {
|
|
235
|
-
dataType: "boolean",
|
|
236
|
-
Field: SwitchFieldBinding
|
|
237
|
-
}
|
|
238
|
-
},
|
|
239
239
|
date_time: {
|
|
240
240
|
key: "date_time",
|
|
241
241
|
name: "Date/time",
|
package/src/form/EntityForm.tsx
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
} from "../hooks";
|
|
35
35
|
import { ErrorFocus } from "./components/ErrorFocus";
|
|
36
36
|
import { CustomIdField } from "./components/CustomIdField";
|
|
37
|
-
import { Alert, Button, cn, DialogActions, IconButton, Typography } from "@firecms/ui";
|
|
37
|
+
import { Alert, Button, cn, DialogActions, IconButton, Tooltip, Typography } from "@firecms/ui";
|
|
38
38
|
import { ErrorBoundary } from "../components";
|
|
39
39
|
import {
|
|
40
40
|
copyEntityAction,
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
} from "../components/EntityCollectionTable/internal/default_entity_actions";
|
|
43
43
|
import { useAnalyticsController } from "../hooks/useAnalyticsController";
|
|
44
44
|
import { ValidationError } from "yup";
|
|
45
|
+
import { PropertyIdCopyTooltipContent } from "../components/PropertyIdCopyTooltipContent";
|
|
45
46
|
|
|
46
47
|
/**
|
|
47
48
|
* @group Components
|
|
@@ -388,7 +389,10 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
388
389
|
|
|
389
390
|
const authController = useAuthController();
|
|
390
391
|
|
|
391
|
-
const getActionsForEntity = useCallback(({ entity, customEntityActions }: {
|
|
392
|
+
const getActionsForEntity = useCallback(({ entity, customEntityActions }: {
|
|
393
|
+
entity?: Entity<M>,
|
|
394
|
+
customEntityActions?: EntityAction[]
|
|
395
|
+
}): EntityAction[] => {
|
|
392
396
|
const createEnabled = canCreateEntity(inputCollection, authController, fullPathToCollectionSegments(path), null);
|
|
393
397
|
const deleteEnabled = entity ? canDeleteEntity(inputCollection, authController, fullPathToCollectionSegments(path), entity) : true;
|
|
394
398
|
const actions: EntityAction[] = [];
|
|
@@ -481,7 +485,10 @@ function EntityFormInternal<M extends Record<string, any>>({
|
|
|
481
485
|
savingError={savingError}
|
|
482
486
|
closeAfterSaveRef={closeAfterSaveRef}
|
|
483
487
|
autoSave={autoSave}
|
|
484
|
-
entityActions={getActionsForEntity({
|
|
488
|
+
entityActions={getActionsForEntity({
|
|
489
|
+
entity,
|
|
490
|
+
customEntityActions: inputCollection.entityActions
|
|
491
|
+
})}/>}
|
|
485
492
|
|
|
486
493
|
</div>
|
|
487
494
|
</div>
|
|
@@ -588,7 +595,12 @@ function InnerForm<M extends Record<string, any>>(props: FormexController<M> & {
|
|
|
588
595
|
<div id={`form_field_${key}`}
|
|
589
596
|
key={`field_${resolvedCollection.name}_${key}`}>
|
|
590
597
|
<ErrorBoundary>
|
|
591
|
-
<
|
|
598
|
+
<Tooltip title={<PropertyIdCopyTooltipContent propertyId={key}/>}
|
|
599
|
+
delayDuration={800}
|
|
600
|
+
side={"left"}
|
|
601
|
+
sideOffset={16}>
|
|
602
|
+
<PropertyFieldBinding {...cmsFormFieldProps}/>
|
|
603
|
+
</Tooltip>
|
|
592
604
|
</ErrorBoundary>
|
|
593
605
|
</div>
|
|
594
606
|
);
|
|
@@ -67,7 +67,7 @@ export const PropertyFieldBinding = React.memo(PropertyFieldBindingInternal, (a:
|
|
|
67
67
|
return false;
|
|
68
68
|
}) as typeof PropertyFieldBindingInternal;
|
|
69
69
|
|
|
70
|
-
function PropertyFieldBindingInternal<T extends CMSType = CMSType,
|
|
70
|
+
function PropertyFieldBindingInternal<T extends CMSType = CMSType, M extends Record<string, any> = Record<string, any>>
|
|
71
71
|
({
|
|
72
72
|
propertyKey,
|
|
73
73
|
property,
|
|
@@ -83,13 +83,8 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
|
|
|
83
83
|
|
|
84
84
|
const customizationController = useCustomizationController();
|
|
85
85
|
|
|
86
|
-
const shouldAlwaysRerender = shouldPropertyReRender(property, customizationController.plugins);
|
|
87
|
-
// we use the standard Field for user defined fields, since it rebuilds
|
|
88
|
-
// when there are changes in other values, in contrast to Field
|
|
89
|
-
const FieldComponent = Field;
|
|
90
|
-
|
|
91
86
|
return (
|
|
92
|
-
<
|
|
87
|
+
<Field
|
|
93
88
|
name={propertyKey}
|
|
94
89
|
>
|
|
95
90
|
{(fieldProps) => {
|
|
@@ -155,7 +150,7 @@ function PropertyFieldBindingInternal<T extends CMSType = CMSType, CustomProps =
|
|
|
155
150
|
componentProps={componentProps}
|
|
156
151
|
fieldProps={fieldProps}/>;
|
|
157
152
|
}}
|
|
158
|
-
</
|
|
153
|
+
</Field>
|
|
159
154
|
);
|
|
160
155
|
|
|
161
156
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { FieldProps, Properties, ResolvedProperties } from "../../types";
|
|
3
3
|
|
|
4
|
-
import { LabelWithIcon } from "../../components";
|
|
4
|
+
import { ErrorBoundary, LabelWithIcon } from "../../components";
|
|
5
5
|
import { getIconForProperty, isHidden, pick } from "../../util";
|
|
6
6
|
import { FieldHelperText } from "../components";
|
|
7
7
|
import { PropertyFieldBinding } from "../PropertyFieldBinding";
|
|
@@ -70,8 +70,10 @@ export function MapFieldBinding<T extends Record<string, any>>({
|
|
|
70
70
|
};
|
|
71
71
|
return (
|
|
72
72
|
<div key={`map-${propertyKey}-${index}`}>
|
|
73
|
-
<
|
|
74
|
-
|
|
73
|
+
<ErrorBoundary>
|
|
74
|
+
<PropertyFieldBinding
|
|
75
|
+
{...fieldProps}/>
|
|
76
|
+
</ErrorBoundary>
|
|
75
77
|
</div>
|
|
76
78
|
);
|
|
77
79
|
}
|
|
@@ -90,10 +92,10 @@ export function MapFieldBinding<T extends Record<string, any>>({
|
|
|
90
92
|
);
|
|
91
93
|
|
|
92
94
|
return (
|
|
93
|
-
|
|
95
|
+
<ErrorBoundary>
|
|
94
96
|
|
|
95
97
|
{!tableMode && !partOfBlock && <ExpandablePanel initiallyExpanded={expanded}
|
|
96
|
-
className={"px-2 md:px-4 pb-2 md:pb-4 pt-1 md:pt-2"}
|
|
98
|
+
className={"px-2 md:px-4 pb-2 md:pb-4 pt-1 md:pt-2 bg-slate-50 bg-opacity-50 dark:bg-gray-900"}
|
|
97
99
|
title={title}>{mapFormView}</ExpandablePanel>}
|
|
98
100
|
|
|
99
101
|
{(tableMode || partOfBlock) && mapFormView}
|
|
@@ -104,7 +106,7 @@ export function MapFieldBinding<T extends Record<string, any>>({
|
|
|
104
106
|
disabled={disabled}
|
|
105
107
|
property={property}/>
|
|
106
108
|
|
|
107
|
-
|
|
109
|
+
</ErrorBoundary>
|
|
108
110
|
);
|
|
109
111
|
}
|
|
110
112
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { CMSType, FieldProps, ResolvedProperty } from "../../types";
|
|
3
3
|
import { FieldHelperText, FormikArrayContainer } from "../components";
|
|
4
|
-
import { LabelWithIcon } from "../../components";
|
|
4
|
+
import { ErrorBoundary, LabelWithIcon } from "../../components";
|
|
5
5
|
import { useClearRestoreValue } from "../../hooks";
|
|
6
6
|
import { getIconForProperty } from "../../util";
|
|
7
7
|
import { PropertyFieldBinding } from "../PropertyFieldBinding";
|
|
@@ -62,7 +62,9 @@ export function RepeatFieldBinding<T extends Array<any>>({
|
|
|
62
62
|
partOfBlock: false,
|
|
63
63
|
autoFocus: internalId === lastAddedId
|
|
64
64
|
};
|
|
65
|
-
return <
|
|
65
|
+
return <ErrorBoundary>
|
|
66
|
+
<PropertyFieldBinding {...fieldProps}/>
|
|
67
|
+
</ErrorBoundary>;
|
|
66
68
|
};
|
|
67
69
|
|
|
68
70
|
const arrayContainer = <FormikArrayContainer value={value}
|