@firecms/collection_editor 3.0.1 → 3.1.0-canary.02232f4
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/ConfigControllerProvider.d.ts +6 -0
- package/dist/api/generateCollectionApi.d.ts +71 -0
- package/dist/api/index.d.ts +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.es.js +15260 -8173
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +15257 -8170
- package/dist/index.umd.js.map +1 -1
- package/dist/locales/de.d.ts +120 -0
- package/dist/locales/en.d.ts +120 -0
- package/dist/locales/es.d.ts +120 -0
- package/dist/locales/fr.d.ts +120 -0
- package/dist/locales/hi.d.ts +120 -0
- package/dist/locales/it.d.ts +120 -0
- package/dist/locales/pt.d.ts +120 -0
- package/dist/types/collection_editor_controller.d.ts +14 -0
- package/dist/types/collection_inference.d.ts +8 -2
- package/dist/types/config_controller.d.ts +23 -2
- package/dist/ui/AddKanbanColumnAction.d.ts +11 -0
- package/dist/ui/KanbanSetupAction.d.ts +10 -0
- package/dist/ui/collection_editor/AICollectionGeneratorPopover.d.ts +37 -0
- package/dist/ui/collection_editor/AIModifiedPathsContext.d.ts +20 -0
- package/dist/ui/collection_editor/CollectionDetailsForm.d.ts +2 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +24 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +4 -1
- package/dist/ui/collection_editor/CollectionJsonImportDialog.d.ts +7 -0
- package/dist/ui/collection_editor/CollectionYupValidation.d.ts +9 -13
- package/dist/ui/collection_editor/DisplaySettingsForm.d.ts +3 -0
- package/dist/ui/collection_editor/EntityActionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ExtendSettingsForm.d.ts +14 -0
- package/dist/ui/collection_editor/GeneralSettingsForm.d.ts +7 -0
- package/dist/ui/collection_editor/KanbanConfigSection.d.ts +4 -0
- package/dist/ui/collection_editor/PropertyEditView.d.ts +6 -1
- package/dist/ui/collection_editor/PropertyTree.d.ts +2 -1
- package/dist/ui/collection_editor/SubcollectionsEditTab.d.ts +2 -1
- package/dist/ui/collection_editor/ViewModeSwitch.d.ts +6 -0
- package/dist/ui/collection_editor/properties/EnumPropertyField.d.ts +2 -1
- package/dist/ui/collection_editor/properties/conditions/ConditionsEditor.d.ts +10 -0
- package/dist/ui/collection_editor/properties/conditions/ConditionsPanel.d.ts +2 -0
- package/dist/ui/collection_editor/properties/conditions/EnumConditionsEditor.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/index.d.ts +6 -0
- package/dist/ui/collection_editor/properties/conditions/property_paths.d.ts +19 -0
- package/dist/useCollectionEditorPlugin.d.ts +7 -1
- package/dist/utils/validateCollectionJson.d.ts +22 -0
- package/package.json +15 -15
- package/src/ConfigControllerProvider.tsx +82 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- package/src/locales/de.ts +125 -0
- package/src/locales/en.ts +145 -0
- package/src/locales/es.ts +125 -0
- package/src/locales/fr.ts +125 -0
- package/src/locales/hi.ts +125 -0
- package/src/locales/it.ts +125 -0
- package/src/locales/pt.ts +125 -0
- package/src/types/collection_editor_controller.tsx +16 -3
- package/src/types/collection_inference.ts +15 -2
- package/src/types/config_controller.tsx +27 -2
- package/src/ui/AddKanbanColumnAction.tsx +203 -0
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/EditorCollectionActionStart.tsx +1 -2
- package/src/ui/EditorEntityAction.tsx +3 -2
- package/src/ui/HomePageEditorCollectionAction.tsx +41 -13
- package/src/ui/KanbanSetupAction.tsx +38 -0
- package/src/ui/MissingReferenceWidget.tsx +1 -1
- package/src/ui/NewCollectionButton.tsx +4 -2
- package/src/ui/NewCollectionCard.tsx +7 -4
- package/src/ui/PropertyAddColumnComponent.tsx +4 -3
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +243 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +222 -267
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +270 -198
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +138 -71
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +202 -101
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +335 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -97
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +8 -10
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +5 -7
- package/src/ui/collection_editor/EnumForm.tsx +153 -102
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +94 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +63 -41
- package/src/ui/collection_editor/KanbanConfigSection.tsx +209 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +27 -43
- package/src/ui/collection_editor/PropertyEditView.tsx +272 -199
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +1 -1
- package/src/ui/collection_editor/PropertyTree.tsx +130 -58
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +169 -163
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +43 -0
- package/src/ui/collection_editor/import/CollectionEditorImportDataPreview.tsx +6 -3
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +5 -2
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +4 -1
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +6 -4
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +126 -42
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +32 -24
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +8 -9
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +128 -53
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +5 -4
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +47 -52
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +3 -1
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +12 -10
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +23 -4
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +866 -0
- package/src/ui/collection_editor/properties/conditions/ConditionsPanel.tsx +28 -0
- package/src/ui/collection_editor/properties/conditions/EnumConditionsEditor.tsx +599 -0
- package/src/ui/collection_editor/properties/conditions/index.ts +6 -0
- package/src/ui/collection_editor/properties/conditions/property_paths.ts +92 -0
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +5 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +7 -5
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +10 -7
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +11 -9
- package/src/ui/collection_editor/properties/validation/ValidationPanel.tsx +5 -2
- package/src/useCollectionEditorPlugin.tsx +53 -22
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -1,10 +1,63 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { getIn, useFormex } from "@firecms/formex";
|
|
3
|
-
import { FieldCaption, NumberProperty, StringProperty } from "@firecms/core";
|
|
3
|
+
import { FieldCaption, NumberProperty, StringProperty, useTranslation } from "@firecms/core";
|
|
4
4
|
import { Select, SelectItem } from "@firecms/ui";
|
|
5
5
|
import { GeneralPropertyValidation } from "./validation/GeneralPropertyValidation";
|
|
6
6
|
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
7
7
|
|
|
8
|
+
// Common IANA timezones with human-readable labels
|
|
9
|
+
const TIMEZONES = [
|
|
10
|
+
// UTC
|
|
11
|
+
{ value: "UTC", label: "UTC (Coordinated Universal Time)" },
|
|
12
|
+
// Americas
|
|
13
|
+
{ value: "America/New_York", label: "New York (US Eastern)" },
|
|
14
|
+
{ value: "America/Chicago", label: "Chicago (US Central)" },
|
|
15
|
+
{ value: "America/Denver", label: "Denver (US Mountain)" },
|
|
16
|
+
{ value: "America/Los_Angeles", label: "Los Angeles (US Pacific)" },
|
|
17
|
+
{ value: "America/Anchorage", label: "Anchorage (Alaska)" },
|
|
18
|
+
{ value: "America/Toronto", label: "Toronto (Canada Eastern)" },
|
|
19
|
+
{ value: "America/Vancouver", label: "Vancouver (Canada Pacific)" },
|
|
20
|
+
{ value: "America/Mexico_City", label: "Mexico City" },
|
|
21
|
+
{ value: "America/Sao_Paulo", label: "São Paulo (Brazil)" },
|
|
22
|
+
{ value: "America/Buenos_Aires", label: "Buenos Aires (Argentina)" },
|
|
23
|
+
// Europe
|
|
24
|
+
{ value: "Europe/London", label: "London (UK)" },
|
|
25
|
+
{ value: "Europe/Paris", label: "Paris (France)" },
|
|
26
|
+
{ value: "Europe/Berlin", label: "Berlin (Germany)" },
|
|
27
|
+
{ value: "Europe/Madrid", label: "Madrid (Spain)" },
|
|
28
|
+
{ value: "Europe/Rome", label: "Rome (Italy)" },
|
|
29
|
+
{ value: "Europe/Amsterdam", label: "Amsterdam (Netherlands)" },
|
|
30
|
+
{ value: "Europe/Brussels", label: "Brussels (Belgium)" },
|
|
31
|
+
{ value: "Europe/Zurich", label: "Zurich (Switzerland)" },
|
|
32
|
+
{ value: "Europe/Stockholm", label: "Stockholm (Sweden)" },
|
|
33
|
+
{ value: "Europe/Vienna", label: "Vienna (Austria)" },
|
|
34
|
+
{ value: "Europe/Warsaw", label: "Warsaw (Poland)" },
|
|
35
|
+
{ value: "Europe/Prague", label: "Prague (Czech Republic)" },
|
|
36
|
+
{ value: "Europe/Athens", label: "Athens (Greece)" },
|
|
37
|
+
{ value: "Europe/Moscow", label: "Moscow (Russia)" },
|
|
38
|
+
{ value: "Europe/Istanbul", label: "Istanbul (Turkey)" },
|
|
39
|
+
// Asia
|
|
40
|
+
{ value: "Asia/Dubai", label: "Dubai (UAE)" },
|
|
41
|
+
{ value: "Asia/Kolkata", label: "Mumbai / Delhi (India)" },
|
|
42
|
+
{ value: "Asia/Bangkok", label: "Bangkok (Thailand)" },
|
|
43
|
+
{ value: "Asia/Singapore", label: "Singapore" },
|
|
44
|
+
{ value: "Asia/Hong_Kong", label: "Hong Kong" },
|
|
45
|
+
{ value: "Asia/Shanghai", label: "Shanghai (China)" },
|
|
46
|
+
{ value: "Asia/Tokyo", label: "Tokyo (Japan)" },
|
|
47
|
+
{ value: "Asia/Seoul", label: "Seoul (South Korea)" },
|
|
48
|
+
{ value: "Asia/Jakarta", label: "Jakarta (Indonesia)" },
|
|
49
|
+
// Oceania
|
|
50
|
+
{ value: "Australia/Sydney", label: "Sydney (Australia Eastern)" },
|
|
51
|
+
{ value: "Australia/Melbourne", label: "Melbourne (Australia)" },
|
|
52
|
+
{ value: "Australia/Brisbane", label: "Brisbane (Australia)" },
|
|
53
|
+
{ value: "Australia/Perth", label: "Perth (Australia Western)" },
|
|
54
|
+
{ value: "Pacific/Auckland", label: "Auckland (New Zealand)" },
|
|
55
|
+
// Africa
|
|
56
|
+
{ value: "Africa/Cairo", label: "Cairo (Egypt)" },
|
|
57
|
+
{ value: "Africa/Johannesburg", label: "Johannesburg (South Africa)" },
|
|
58
|
+
{ value: "Africa/Lagos", label: "Lagos (Nigeria)" },
|
|
59
|
+
];
|
|
60
|
+
|
|
8
61
|
export function DateTimePropertyField({ disabled }: {
|
|
9
62
|
disabled: boolean;
|
|
10
63
|
}) {
|
|
@@ -16,6 +69,8 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
16
69
|
setFieldValue
|
|
17
70
|
} = useFormex<StringProperty | NumberProperty>();
|
|
18
71
|
|
|
72
|
+
const { t } = useTranslation();
|
|
73
|
+
|
|
19
74
|
const modePath = "mode";
|
|
20
75
|
const modeValue: string | undefined = getIn(values, modePath);
|
|
21
76
|
const modeError: string | undefined = getIn(touched, modePath) && getIn(errors, modePath);
|
|
@@ -24,30 +79,34 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
24
79
|
const autoValueValue: string | undefined = getIn(values, autoValuePath);
|
|
25
80
|
const autoValueError: string | undefined = getIn(touched, autoValuePath) && getIn(errors, autoValuePath);
|
|
26
81
|
|
|
82
|
+
const timezonePath = "timezone";
|
|
83
|
+
const timezoneValue: string | undefined = getIn(values, timezonePath);
|
|
84
|
+
const timezoneError: string | undefined = getIn(touched, timezonePath) && getIn(errors, timezonePath);
|
|
85
|
+
|
|
27
86
|
return (
|
|
28
87
|
<>
|
|
29
88
|
<div className={"flex flex-col col-span-12 gap-2"}>
|
|
30
89
|
<div>
|
|
31
90
|
<Select name={modePath}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<SelectItem value={"date_time"}>
|
|
50
|
-
<SelectItem value={"date"}>
|
|
91
|
+
value={modeValue ?? "date"}
|
|
92
|
+
error={Boolean(modeError)}
|
|
93
|
+
size={"large"}
|
|
94
|
+
onValueChange={(v) => setFieldValue(modePath, v)}
|
|
95
|
+
label={t("mode_label")}
|
|
96
|
+
fullWidth={true}
|
|
97
|
+
renderValue={(v) => {
|
|
98
|
+
switch (v) {
|
|
99
|
+
case "date_time":
|
|
100
|
+
return t("date_time_mode");
|
|
101
|
+
case "date":
|
|
102
|
+
return t("date_mode");
|
|
103
|
+
default:
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
}}
|
|
107
|
+
disabled={disabled}>
|
|
108
|
+
<SelectItem value={"date_time"}> {t("date_time_mode")} </SelectItem>
|
|
109
|
+
<SelectItem value={"date"}> {t("date_mode")} </SelectItem>
|
|
51
110
|
</Select>
|
|
52
111
|
<FieldCaption error={Boolean(modeError)}>
|
|
53
112
|
{modeError}
|
|
@@ -55,29 +114,54 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
55
114
|
</div>
|
|
56
115
|
<div>
|
|
57
116
|
<Select name={autoValuePath}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
<SelectItem value={"none"}>
|
|
76
|
-
<SelectItem value={"on_create"}>
|
|
77
|
-
<SelectItem value={"on_update"}>
|
|
117
|
+
disabled={disabled}
|
|
118
|
+
size={"large"}
|
|
119
|
+
fullWidth={true}
|
|
120
|
+
value={autoValueValue ?? ""}
|
|
121
|
+
onValueChange={(v) => setFieldValue(autoValuePath, v === "none" ? null : v)}
|
|
122
|
+
renderValue={(v) => {
|
|
123
|
+
switch (v) {
|
|
124
|
+
case "on_create":
|
|
125
|
+
return t("auto_on_create");
|
|
126
|
+
case "on_update":
|
|
127
|
+
return t("auto_on_update");
|
|
128
|
+
default:
|
|
129
|
+
return t("auto_none");
|
|
130
|
+
}
|
|
131
|
+
}}
|
|
132
|
+
error={Boolean(autoValueError)}
|
|
133
|
+
label={t("automatic_value")}>
|
|
134
|
+
<SelectItem value={"none"}> {t("auto_none")} </SelectItem>
|
|
135
|
+
<SelectItem value={"on_create"}> {t("auto_on_create")} </SelectItem>
|
|
136
|
+
<SelectItem value={"on_update"}> {t("auto_on_update")} </SelectItem>
|
|
78
137
|
</Select>
|
|
79
138
|
<FieldCaption error={Boolean(autoValueError)}>
|
|
80
|
-
{autoValueError ?? "
|
|
139
|
+
{autoValueError ?? t("auto_value_description")}
|
|
140
|
+
</FieldCaption>
|
|
141
|
+
</div>
|
|
142
|
+
<div>
|
|
143
|
+
<Select name={timezonePath}
|
|
144
|
+
disabled={disabled}
|
|
145
|
+
size={"large"}
|
|
146
|
+
fullWidth={true}
|
|
147
|
+
value={timezoneValue ?? "__local__"}
|
|
148
|
+
onValueChange={(v) => setFieldValue(timezonePath, v === "__local__" ? undefined : v)}
|
|
149
|
+
renderValue={(v) => {
|
|
150
|
+
if (!v || v === "__local__") return t("local_timezone");
|
|
151
|
+
const tz = TIMEZONES.find(t => t.value === v);
|
|
152
|
+
return tz?.label ?? v;
|
|
153
|
+
}}
|
|
154
|
+
error={Boolean(timezoneError)}
|
|
155
|
+
label={t("timezone_label")}>
|
|
156
|
+
<SelectItem value={"__local__"}> {t("local_timezone")} </SelectItem>
|
|
157
|
+
{TIMEZONES.map((tz) => (
|
|
158
|
+
<SelectItem key={tz.value} value={tz.value}>
|
|
159
|
+
{tz.label}
|
|
160
|
+
</SelectItem>
|
|
161
|
+
))}
|
|
162
|
+
</Select>
|
|
163
|
+
<FieldCaption error={Boolean(timezoneError)}>
|
|
164
|
+
{timezoneError ?? t("timezone_description")}
|
|
81
165
|
</FieldCaption>
|
|
82
166
|
</div>
|
|
83
167
|
|
|
@@ -85,7 +169,7 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
85
169
|
|
|
86
170
|
<div className={"col-span-12"}>
|
|
87
171
|
<ValidationPanel>
|
|
88
|
-
<GeneralPropertyValidation disabled={disabled}/>
|
|
172
|
+
<GeneralPropertyValidation disabled={disabled} />
|
|
89
173
|
</ValidationPanel>
|
|
90
174
|
</div>
|
|
91
175
|
</>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo } from "react";
|
|
2
2
|
import { getIn, useFormex } from "@firecms/formex";
|
|
3
|
-
import { EnumValueConfig, resolveEnumValues, useSnackbarController } from "@firecms/core";
|
|
3
|
+
import { EnumValueConfig, resolveEnumValues, useSnackbarController, useTranslation } from "@firecms/core";
|
|
4
4
|
import { Select, SelectItem } from "@firecms/ui";
|
|
5
5
|
import { EnumForm } from "../EnumForm";
|
|
6
6
|
import { StringPropertyValidation } from "./validation/StringPropertyValidation";
|
|
@@ -9,19 +9,21 @@ import { ValidationPanel } from "./validation/ValidationPanel";
|
|
|
9
9
|
import { PropertyWithId } from "../PropertyEditView";
|
|
10
10
|
|
|
11
11
|
export function EnumPropertyField({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
multiselect,
|
|
13
|
+
updateIds,
|
|
14
|
+
disabled,
|
|
15
|
+
showErrors,
|
|
16
|
+
allowDataInference,
|
|
17
|
+
getData,
|
|
18
|
+
propertyNamespace
|
|
19
|
+
}: {
|
|
19
20
|
multiselect: boolean;
|
|
20
21
|
updateIds: boolean;
|
|
21
22
|
disabled: boolean;
|
|
22
23
|
showErrors: boolean;
|
|
23
24
|
allowDataInference?: boolean;
|
|
24
25
|
getData?: () => Promise<object[]>;
|
|
26
|
+
propertyNamespace?: string;
|
|
25
27
|
}) {
|
|
26
28
|
|
|
27
29
|
const {
|
|
@@ -30,6 +32,7 @@ export function EnumPropertyField({
|
|
|
30
32
|
setFieldValue
|
|
31
33
|
} = useFormex<PropertyWithId>();
|
|
32
34
|
|
|
35
|
+
const { t } = useTranslation();
|
|
33
36
|
const snackbarContext = useSnackbarController();
|
|
34
37
|
|
|
35
38
|
const enumValuesPath = multiselect ? "of.enumValues" : "enumValues";
|
|
@@ -53,27 +56,32 @@ export function EnumPropertyField({
|
|
|
53
56
|
setFieldValue("defaultValue", undefined);
|
|
54
57
|
snackbarContext.open({
|
|
55
58
|
type: "warning",
|
|
56
|
-
message: "
|
|
59
|
+
message: t("default_value_was_cleared")
|
|
57
60
|
})
|
|
58
61
|
}
|
|
59
62
|
}
|
|
60
63
|
};
|
|
61
64
|
|
|
65
|
+
// Build full path including namespace for nested properties (e.g., "address.status")
|
|
66
|
+
const fullPropertyPath = values.id
|
|
67
|
+
? (propertyNamespace ? `${propertyNamespace}.${values.id}` : values.id)
|
|
68
|
+
: undefined;
|
|
69
|
+
|
|
62
70
|
return (
|
|
63
71
|
<>
|
|
64
72
|
<div className={"col-span-12"}>
|
|
65
73
|
<EnumForm enumValues={enumValues}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
updateIds={updateIds}
|
|
75
|
+
disabled={disabled}
|
|
76
|
+
allowDataInference={allowDataInference}
|
|
77
|
+
onError={(hasError) => {
|
|
78
|
+
setFieldError(enumValuesPath, hasError ? t("enum_missing_values") : undefined);
|
|
79
|
+
}}
|
|
80
|
+
getData={getData && fullPropertyPath
|
|
81
|
+
? () => getData()
|
|
82
|
+
.then(res => res.map(entry => getIn(entry, fullPropertyPath)).filter(Boolean))
|
|
83
|
+
: undefined}
|
|
84
|
+
onValuesChanged={onValuesChanged} />
|
|
77
85
|
</div>
|
|
78
86
|
|
|
79
87
|
<div className={"col-span-12"}>
|
|
@@ -81,9 +89,9 @@ export function EnumPropertyField({
|
|
|
81
89
|
<ValidationPanel>
|
|
82
90
|
{!multiselect &&
|
|
83
91
|
<StringPropertyValidation disabled={disabled}
|
|
84
|
-
|
|
92
|
+
showErrors={showErrors} />}
|
|
85
93
|
{multiselect &&
|
|
86
|
-
<ArrayPropertyValidation disabled={disabled}/>}
|
|
94
|
+
<ArrayPropertyValidation disabled={disabled} />}
|
|
87
95
|
</ValidationPanel>
|
|
88
96
|
|
|
89
97
|
</div>
|
|
@@ -98,13 +106,13 @@ export function EnumPropertyField({
|
|
|
98
106
|
setFieldValue("defaultValue", value);
|
|
99
107
|
}}
|
|
100
108
|
size={"large"}
|
|
101
|
-
label={"
|
|
109
|
+
label={t("default_value")}
|
|
102
110
|
value={defaultValue ?? ""}>
|
|
103
111
|
{enumValues
|
|
104
112
|
.filter((enumValue) => Boolean(enumValue?.id))
|
|
105
113
|
.map((enumValue) => (
|
|
106
114
|
<SelectItem key={enumValue.id}
|
|
107
|
-
|
|
115
|
+
value={enumValue.id?.toString()}>
|
|
108
116
|
{enumValue.label}
|
|
109
117
|
</SelectItem>
|
|
110
118
|
))}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { FieldCaption, MapProperty, Property, PropertyConfig,
|
|
2
|
+
import { FieldCaption, MapProperty, Property, PropertyConfig, useTranslation
|
|
3
|
+
} from "@firecms/core";
|
|
3
4
|
import { AddIcon, BooleanSwitchWithLabel, Button, Paper, Typography } from "@firecms/ui";
|
|
4
5
|
import { PropertyFormDialog } from "../PropertyEditView";
|
|
5
6
|
import { getIn, useFormex } from "@firecms/formex";
|
|
@@ -13,6 +14,7 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
13
14
|
propertyConfigs: Record<string, PropertyConfig>,
|
|
14
15
|
collectionEditable: boolean;
|
|
15
16
|
}) {
|
|
17
|
+
const { t } = useTranslation();
|
|
16
18
|
|
|
17
19
|
const {
|
|
18
20
|
values,
|
|
@@ -73,14 +75,12 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
73
75
|
<>
|
|
74
76
|
<div className={"col-span-12"}>
|
|
75
77
|
<div className="flex justify-between items-end my-4">
|
|
76
|
-
<Typography variant={"subtitle2"}>
|
|
78
|
+
<Typography variant={"subtitle2"}>{t("properties_in_this_group")}</Typography>
|
|
77
79
|
<Button
|
|
78
|
-
color="primary"
|
|
79
|
-
variant={"outlined"}
|
|
80
80
|
onClick={() => setPropertyDialogOpen(true)}
|
|
81
81
|
startIcon={<AddIcon/>}
|
|
82
82
|
>
|
|
83
|
-
|
|
83
|
+
{t("add_property_to", { name: values.name ?? t("properties_in_this_group") })}
|
|
84
84
|
</Button>
|
|
85
85
|
</div>
|
|
86
86
|
<Paper className="p-2 pl-8">
|
|
@@ -99,7 +99,7 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
99
99
|
{empty &&
|
|
100
100
|
<Typography variant={"label"}
|
|
101
101
|
className="h-full flex items-center justify-center p-4">
|
|
102
|
-
|
|
102
|
+
{t("add_first_property_to_group")}
|
|
103
103
|
</Typography>}
|
|
104
104
|
</Paper>
|
|
105
105
|
</div>
|
|
@@ -108,13 +108,12 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
108
108
|
<BooleanSwitchWithLabel
|
|
109
109
|
position={"start"}
|
|
110
110
|
size={"medium"}
|
|
111
|
-
label="
|
|
111
|
+
label={t("spread_children_as_columns")}
|
|
112
112
|
onValueChange={(v) => setFieldValue("spreadChildren", v)}
|
|
113
113
|
value={values.spreadChildren ?? false}
|
|
114
114
|
/>
|
|
115
115
|
<FieldCaption>
|
|
116
|
-
|
|
117
|
-
will only work for top level groups.
|
|
116
|
+
{t("spread_children_description")}
|
|
118
117
|
</FieldCaption>
|
|
119
118
|
</div>
|
|
120
119
|
|
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
2
|
import { StringPropertyValidation } from "./validation/StringPropertyValidation";
|
|
3
3
|
import { ValidationPanel } from "./validation/ValidationPanel";
|
|
4
4
|
import { Field, getIn, useFormex } from "@firecms/formex";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
BooleanSwitchWithLabel,
|
|
8
|
+
CloudUploadIcon,
|
|
9
|
+
DebouncedTextField,
|
|
10
|
+
ExpandablePanel,
|
|
11
|
+
SettingsIcon,
|
|
12
|
+
TextField,
|
|
13
|
+
Typography
|
|
14
|
+
} from "@firecms/ui";
|
|
15
|
+
import { useTranslation } from "@firecms/core";
|
|
7
16
|
|
|
8
17
|
export function MarkdownPropertyField({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
18
|
+
disabled,
|
|
19
|
+
showErrors
|
|
20
|
+
}: {
|
|
12
21
|
disabled: boolean;
|
|
13
22
|
showErrors: boolean;
|
|
14
23
|
}) {
|
|
@@ -18,20 +27,52 @@ export function MarkdownPropertyField({
|
|
|
18
27
|
setFieldValue
|
|
19
28
|
} = useFormex();
|
|
20
29
|
|
|
30
|
+
const { t } = useTranslation();
|
|
31
|
+
|
|
21
32
|
const baseStoragePath = "storage";
|
|
33
|
+
const baseMarkdownPath = "markdown";
|
|
22
34
|
|
|
23
35
|
const metadata = `${baseStoragePath}.metadata`;
|
|
24
36
|
const fileName = `${baseStoragePath}.fileName`;
|
|
25
37
|
const maxSize = `${baseStoragePath}.maxSize`;
|
|
26
38
|
const storagePath = `${baseStoragePath}.storagePath`;
|
|
27
39
|
|
|
40
|
+
// Markdown config paths
|
|
41
|
+
const htmlPath = `${baseMarkdownPath}.html`;
|
|
42
|
+
const transformPastedTextPath = `${baseMarkdownPath}.transformPastedText`;
|
|
43
|
+
|
|
28
44
|
const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
|
|
29
45
|
const storagePathValue = getIn(values, storagePath) ?? "/";
|
|
30
46
|
const maxSizeValue = getIn(values, maxSize);
|
|
31
47
|
|
|
48
|
+
// Markdown config values - check if markdown is an object or boolean
|
|
49
|
+
const markdownValue = getIn(values, "markdown");
|
|
50
|
+
const isMarkdownObject = typeof markdownValue === "object" && markdownValue !== null;
|
|
51
|
+
const htmlValue = isMarkdownObject ? (markdownValue.html ?? true) : true;
|
|
52
|
+
const transformPastedTextValue = isMarkdownObject ? (markdownValue.transformPastedText ?? false) : false;
|
|
53
|
+
|
|
32
54
|
const hasFilenameCallback = typeof fileNameValue === "function";
|
|
33
55
|
const hasStoragePathCallback = typeof storagePathValue === "function";
|
|
34
56
|
|
|
57
|
+
// Handler to update markdown config - converts from boolean to object if needed
|
|
58
|
+
const updateMarkdownConfig = useCallback((key: "html" | "transformPastedText", value: boolean) => {
|
|
59
|
+
const currentMarkdown = getIn(values, "markdown");
|
|
60
|
+
if (typeof currentMarkdown === "object" && currentMarkdown !== null) {
|
|
61
|
+
// Already an object, update the key
|
|
62
|
+
setFieldValue(`markdown.${key}`, value);
|
|
63
|
+
} else {
|
|
64
|
+
// Convert from boolean to object
|
|
65
|
+
const defaultConfig = {
|
|
66
|
+
html: true,
|
|
67
|
+
transformPastedText: false
|
|
68
|
+
};
|
|
69
|
+
setFieldValue("markdown", {
|
|
70
|
+
...defaultConfig,
|
|
71
|
+
[key]: value
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}, [values, setFieldValue]);
|
|
75
|
+
|
|
35
76
|
return (
|
|
36
77
|
<>
|
|
37
78
|
<div className={"col-span-12"}>
|
|
@@ -39,13 +80,13 @@ export function MarkdownPropertyField({
|
|
|
39
80
|
<ValidationPanel>
|
|
40
81
|
|
|
41
82
|
<StringPropertyValidation disabled={disabled}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
83
|
+
length={true}
|
|
84
|
+
lowercase={true}
|
|
85
|
+
max={true}
|
|
86
|
+
min={true}
|
|
87
|
+
trim={true}
|
|
88
|
+
uppercase={true}
|
|
89
|
+
showErrors={showErrors} />
|
|
49
90
|
|
|
50
91
|
</ValidationPanel>
|
|
51
92
|
|
|
@@ -55,10 +96,48 @@ export function MarkdownPropertyField({
|
|
|
55
96
|
<ExpandablePanel
|
|
56
97
|
title={
|
|
57
98
|
<div className="flex flex-row text-surface-500">
|
|
58
|
-
<
|
|
99
|
+
<SettingsIcon />
|
|
100
|
+
<Typography variant={"subtitle2"}
|
|
101
|
+
className="ml-4">
|
|
102
|
+
{t("paste_behavior")}
|
|
103
|
+
</Typography>
|
|
104
|
+
</div>
|
|
105
|
+
}>
|
|
106
|
+
|
|
107
|
+
<div className={"flex flex-col gap-2 p-4"}>
|
|
108
|
+
<BooleanSwitchWithLabel
|
|
109
|
+
size={"small"}
|
|
110
|
+
disabled={disabled}
|
|
111
|
+
value={!htmlValue}
|
|
112
|
+
onValueChange={(value) => updateMarkdownConfig("html", !value)}
|
|
113
|
+
label={t("strip_html_on_paste")}
|
|
114
|
+
/>
|
|
115
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
116
|
+
{t("strip_html_description")}
|
|
117
|
+
</Typography>
|
|
118
|
+
|
|
119
|
+
<BooleanSwitchWithLabel
|
|
120
|
+
size={"small"}
|
|
121
|
+
disabled={disabled}
|
|
122
|
+
value={transformPastedTextValue}
|
|
123
|
+
onValueChange={(value) => updateMarkdownConfig("transformPastedText", value)}
|
|
124
|
+
label={t("convert_pasted_to_markdown")}
|
|
125
|
+
/>
|
|
126
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
127
|
+
{t("convert_pasted_description")}
|
|
128
|
+
</Typography>
|
|
129
|
+
</div>
|
|
130
|
+
</ExpandablePanel>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div className={"col-span-12"}>
|
|
134
|
+
<ExpandablePanel
|
|
135
|
+
title={
|
|
136
|
+
<div className="flex flex-row text-surface-500">
|
|
137
|
+
<CloudUploadIcon />
|
|
59
138
|
<Typography variant={"subtitle2"}
|
|
60
|
-
|
|
61
|
-
|
|
139
|
+
className="ml-4">
|
|
140
|
+
{t("file_upload_config")}
|
|
62
141
|
</Typography>
|
|
63
142
|
</div>
|
|
64
143
|
}>
|
|
@@ -68,54 +147,50 @@ export function MarkdownPropertyField({
|
|
|
68
147
|
|
|
69
148
|
<div className={"col-span-12"}>
|
|
70
149
|
<Field name={fileName}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
150
|
+
as={DebouncedTextField}
|
|
151
|
+
label={t("file_name_label")}
|
|
152
|
+
size={"small"}
|
|
153
|
+
disabled={hasFilenameCallback || disabled}
|
|
154
|
+
value={hasFilenameCallback ? "-" : fileNameValue}
|
|
76
155
|
/>
|
|
77
156
|
</div>
|
|
78
157
|
<div className={"col-span-12"}>
|
|
79
158
|
<Field name={storagePath}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
159
|
+
as={DebouncedTextField}
|
|
160
|
+
label={t("storage_path_label")}
|
|
161
|
+
disabled={hasStoragePathCallback || disabled}
|
|
162
|
+
size={"small"}
|
|
163
|
+
value={hasStoragePathCallback ? "-" : storagePathValue}
|
|
85
164
|
/>
|
|
86
165
|
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
87
|
-
<p>
|
|
88
|
-
the file name
|
|
89
|
-
and storage path values:</p>
|
|
166
|
+
<p>{t("storage_placeholders_description")}</p>
|
|
90
167
|
<ul>
|
|
91
|
-
<li>{"
|
|
92
|
-
<li>{"
|
|
93
|
-
<li>{"
|
|
94
|
-
<li>{"
|
|
95
|
-
<li>{"
|
|
96
|
-
<li>{"
|
|
97
|
-
<li>{"
|
|
168
|
+
<li>{t("storage_placeholder_file")}</li>
|
|
169
|
+
<li>{t("storage_placeholder_file_name")}</li>
|
|
170
|
+
<li>{t("storage_placeholder_file_ext")}</li>
|
|
171
|
+
<li>{t("storage_placeholder_entity_id")}</li>
|
|
172
|
+
<li>{t("storage_placeholder_property_key")}</li>
|
|
173
|
+
<li>{t("storage_placeholder_path")}</li>
|
|
174
|
+
<li>{t("storage_placeholder_rand")}</li>
|
|
98
175
|
</ul>
|
|
99
176
|
</Typography>
|
|
100
177
|
|
|
101
178
|
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
102
|
-
|
|
103
|
-
(not
|
|
104
|
-
the path).
|
|
179
|
+
{t("markdown_url_note")}
|
|
105
180
|
</Typography>
|
|
106
181
|
</div>
|
|
107
182
|
|
|
108
183
|
<div className={"col-span-12"}>
|
|
109
184
|
<DebouncedTextField name={maxSize}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
185
|
+
type={"number"}
|
|
186
|
+
label={t("max_size_bytes")}
|
|
187
|
+
size={"small"}
|
|
188
|
+
value={maxSizeValue !== undefined && maxSizeValue !== null ? maxSizeValue.toString() : ""}
|
|
189
|
+
onChange={(e) => {
|
|
190
|
+
const value = e.target.value;
|
|
191
|
+
if (value === "") setFieldValue(maxSize, undefined);
|
|
192
|
+
else setFieldValue(maxSize, parseInt(value));
|
|
193
|
+
}}
|
|
119
194
|
/>
|
|
120
195
|
</div>
|
|
121
196
|
|
|
@@ -126,12 +201,12 @@ export function MarkdownPropertyField({
|
|
|
126
201
|
<div className={"col-span-12"}>
|
|
127
202
|
|
|
128
203
|
<TextField name={"defaultValue"}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
204
|
+
disabled={disabled}
|
|
205
|
+
onChange={(e: any) => {
|
|
206
|
+
setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
|
|
207
|
+
}}
|
|
208
|
+
label={t("default_value")}
|
|
209
|
+
value={getIn(values, "defaultValue") ?? ""} />
|
|
135
210
|
|
|
136
211
|
</div>
|
|
137
212
|
</>
|