@firecms/collection_editor 3.0.1 → 3.1.0-canary.1df3b2c
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 +9418 -5587
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +9413 -5582
- package/dist/index.umd.js.map +1 -1
- 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 +33 -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 +20 -0
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +3 -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 +11 -11
- package/src/ConfigControllerProvider.tsx +81 -47
- package/src/api/generateCollectionApi.ts +119 -0
- package/src/api/index.ts +1 -0
- package/src/index.ts +28 -1
- 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/EditorCollectionActionStart.tsx +1 -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 +1 -1
- package/src/ui/PropertyAddColumnComponent.tsx +1 -1
- package/src/ui/collection_editor/AICollectionGeneratorPopover.tsx +225 -0
- package/src/ui/collection_editor/AIModifiedPathsContext.tsx +88 -0
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +209 -257
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +226 -167
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +130 -67
- package/src/ui/collection_editor/CollectionJsonImportDialog.tsx +171 -0
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +190 -91
- package/src/ui/collection_editor/DisplaySettingsForm.tsx +333 -0
- package/src/ui/collection_editor/EntityActionsEditTab.tsx +106 -96
- package/src/ui/collection_editor/EntityActionsSelectDialog.tsx +6 -7
- package/src/ui/collection_editor/EntityCustomViewsSelectDialog.tsx +1 -3
- package/src/ui/collection_editor/EnumForm.tsx +147 -100
- package/src/ui/collection_editor/ExtendSettingsForm.tsx +93 -0
- package/src/ui/collection_editor/GeneralSettingsForm.tsx +335 -0
- package/src/ui/collection_editor/GetCodeDialog.tsx +57 -36
- package/src/ui/collection_editor/KanbanConfigSection.tsx +207 -0
- package/src/ui/collection_editor/LayoutModeSwitch.tsx +22 -41
- package/src/ui/collection_editor/PropertyEditView.tsx +205 -141
- 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 +171 -162
- package/src/ui/collection_editor/UnsavedChangesDialog.tsx +0 -2
- package/src/ui/collection_editor/ViewModeSwitch.tsx +41 -0
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +1 -0
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +117 -35
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +28 -21
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +0 -2
- package/src/ui/collection_editor/properties/MarkdownPropertyField.tsx +115 -39
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +1 -1
- package/src/ui/collection_editor/properties/conditions/ConditionsEditor.tsx +861 -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/ValidationPanel.tsx +1 -1
- package/src/useCollectionEditorPlugin.tsx +32 -17
- package/src/utils/validateCollectionJson.ts +380 -0
|
@@ -5,6 +5,59 @@ 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
|
}) {
|
|
@@ -24,28 +77,32 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
24
77
|
const autoValueValue: string | undefined = getIn(values, autoValuePath);
|
|
25
78
|
const autoValueError: string | undefined = getIn(touched, autoValuePath) && getIn(errors, autoValuePath);
|
|
26
79
|
|
|
80
|
+
const timezonePath = "timezone";
|
|
81
|
+
const timezoneValue: string | undefined = getIn(values, timezonePath);
|
|
82
|
+
const timezoneError: string | undefined = getIn(touched, timezonePath) && getIn(errors, timezonePath);
|
|
83
|
+
|
|
27
84
|
return (
|
|
28
85
|
<>
|
|
29
86
|
<div className={"flex flex-col col-span-12 gap-2"}>
|
|
30
87
|
<div>
|
|
31
88
|
<Select name={modePath}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
89
|
+
value={modeValue ?? "date"}
|
|
90
|
+
error={Boolean(modeError)}
|
|
91
|
+
size={"large"}
|
|
92
|
+
onValueChange={(v) => setFieldValue(modePath, v)}
|
|
93
|
+
label={"Mode"}
|
|
94
|
+
fullWidth={true}
|
|
95
|
+
renderValue={(v) => {
|
|
96
|
+
switch (v) {
|
|
97
|
+
case "date_time":
|
|
98
|
+
return "Date/Time";
|
|
99
|
+
case "date":
|
|
100
|
+
return "Date";
|
|
101
|
+
default:
|
|
102
|
+
return "";
|
|
103
|
+
}
|
|
104
|
+
}}
|
|
105
|
+
disabled={disabled}>
|
|
49
106
|
<SelectItem value={"date_time"}> Date/Time </SelectItem>
|
|
50
107
|
<SelectItem value={"date"}> Date </SelectItem>
|
|
51
108
|
</Select>
|
|
@@ -55,23 +112,23 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
55
112
|
</div>
|
|
56
113
|
<div>
|
|
57
114
|
<Select name={autoValuePath}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
115
|
+
disabled={disabled}
|
|
116
|
+
size={"large"}
|
|
117
|
+
fullWidth={true}
|
|
118
|
+
value={autoValueValue ?? ""}
|
|
119
|
+
onValueChange={(v) => setFieldValue(autoValuePath, v === "none" ? null : v)}
|
|
120
|
+
renderValue={(v) => {
|
|
121
|
+
switch (v) {
|
|
122
|
+
case "on_create":
|
|
123
|
+
return "On create";
|
|
124
|
+
case "on_update":
|
|
125
|
+
return "On any update";
|
|
126
|
+
default:
|
|
127
|
+
return "None";
|
|
128
|
+
}
|
|
129
|
+
}}
|
|
130
|
+
error={Boolean(autoValueError)}
|
|
131
|
+
label={"Automatic value"}>
|
|
75
132
|
<SelectItem value={"none"}> None </SelectItem>
|
|
76
133
|
<SelectItem value={"on_create"}> On create </SelectItem>
|
|
77
134
|
<SelectItem value={"on_update"}> On any update </SelectItem>
|
|
@@ -80,12 +137,37 @@ export function DateTimePropertyField({ disabled }: {
|
|
|
80
137
|
{autoValueError ?? "Update this field automatically when creating or updating the entity"}
|
|
81
138
|
</FieldCaption>
|
|
82
139
|
</div>
|
|
140
|
+
<div>
|
|
141
|
+
<Select name={timezonePath}
|
|
142
|
+
disabled={disabled}
|
|
143
|
+
size={"large"}
|
|
144
|
+
fullWidth={true}
|
|
145
|
+
value={timezoneValue ?? "__local__"}
|
|
146
|
+
onValueChange={(v) => setFieldValue(timezonePath, v === "__local__" ? undefined : v)}
|
|
147
|
+
renderValue={(v) => {
|
|
148
|
+
if (!v || v === "__local__") return "Local timezone";
|
|
149
|
+
const tz = TIMEZONES.find(t => t.value === v);
|
|
150
|
+
return tz?.label ?? v;
|
|
151
|
+
}}
|
|
152
|
+
error={Boolean(timezoneError)}
|
|
153
|
+
label={"Timezone"}>
|
|
154
|
+
<SelectItem value={"__local__"}> Local timezone </SelectItem>
|
|
155
|
+
{TIMEZONES.map((tz) => (
|
|
156
|
+
<SelectItem key={tz.value} value={tz.value}>
|
|
157
|
+
{tz.label}
|
|
158
|
+
</SelectItem>
|
|
159
|
+
))}
|
|
160
|
+
</Select>
|
|
161
|
+
<FieldCaption error={Boolean(timezoneError)}>
|
|
162
|
+
{timezoneError ?? "Timezone for displaying and inputting dates. Values are always stored in UTC."}
|
|
163
|
+
</FieldCaption>
|
|
164
|
+
</div>
|
|
83
165
|
|
|
84
166
|
</div>
|
|
85
167
|
|
|
86
168
|
<div className={"col-span-12"}>
|
|
87
169
|
<ValidationPanel>
|
|
88
|
-
<GeneralPropertyValidation disabled={disabled}/>
|
|
170
|
+
<GeneralPropertyValidation disabled={disabled} />
|
|
89
171
|
</ValidationPanel>
|
|
90
172
|
</div>
|
|
91
173
|
</>
|
|
@@ -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 {
|
|
@@ -59,21 +61,26 @@ export function EnumPropertyField({
|
|
|
59
61
|
}
|
|
60
62
|
};
|
|
61
63
|
|
|
64
|
+
// Build full path including namespace for nested properties (e.g., "address.status")
|
|
65
|
+
const fullPropertyPath = values.id
|
|
66
|
+
? (propertyNamespace ? `${propertyNamespace}.${values.id}` : values.id)
|
|
67
|
+
: undefined;
|
|
68
|
+
|
|
62
69
|
return (
|
|
63
70
|
<>
|
|
64
71
|
<div className={"col-span-12"}>
|
|
65
72
|
<EnumForm enumValues={enumValues}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
updateIds={updateIds}
|
|
74
|
+
disabled={disabled}
|
|
75
|
+
allowDataInference={allowDataInference}
|
|
76
|
+
onError={(hasError) => {
|
|
77
|
+
setFieldError(enumValuesPath, hasError ? "This enum property is missing some values" : undefined);
|
|
78
|
+
}}
|
|
79
|
+
getData={getData && fullPropertyPath
|
|
80
|
+
? () => getData()
|
|
81
|
+
.then(res => res.map(entry => getIn(entry, fullPropertyPath)).filter(Boolean))
|
|
82
|
+
: undefined}
|
|
83
|
+
onValuesChanged={onValuesChanged} />
|
|
77
84
|
</div>
|
|
78
85
|
|
|
79
86
|
<div className={"col-span-12"}>
|
|
@@ -81,9 +88,9 @@ export function EnumPropertyField({
|
|
|
81
88
|
<ValidationPanel>
|
|
82
89
|
{!multiselect &&
|
|
83
90
|
<StringPropertyValidation disabled={disabled}
|
|
84
|
-
|
|
91
|
+
showErrors={showErrors} />}
|
|
85
92
|
{multiselect &&
|
|
86
|
-
<ArrayPropertyValidation disabled={disabled}/>}
|
|
93
|
+
<ArrayPropertyValidation disabled={disabled} />}
|
|
87
94
|
</ValidationPanel>
|
|
88
95
|
|
|
89
96
|
</div>
|
|
@@ -104,7 +111,7 @@ export function EnumPropertyField({
|
|
|
104
111
|
.filter((enumValue) => Boolean(enumValue?.id))
|
|
105
112
|
.map((enumValue) => (
|
|
106
113
|
<SelectItem key={enumValue.id}
|
|
107
|
-
|
|
114
|
+
value={enumValue.id?.toString()}>
|
|
108
115
|
{enumValue.label}
|
|
109
116
|
</SelectItem>
|
|
110
117
|
))}
|
|
@@ -75,8 +75,6 @@ export function MapPropertyField({ disabled, getData, allowDataInference, proper
|
|
|
75
75
|
<div className="flex justify-between items-end my-4">
|
|
76
76
|
<Typography variant={"subtitle2"}>Properties in this group</Typography>
|
|
77
77
|
<Button
|
|
78
|
-
color="primary"
|
|
79
|
-
variant={"outlined"}
|
|
80
78
|
onClick={() => setPropertyDialogOpen(true)}
|
|
81
79
|
startIcon={<AddIcon/>}
|
|
82
80
|
>
|
|
@@ -1,14 +1,22 @@
|
|
|
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";
|
|
7
15
|
|
|
8
16
|
export function MarkdownPropertyField({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
disabled,
|
|
18
|
+
showErrors
|
|
19
|
+
}: {
|
|
12
20
|
disabled: boolean;
|
|
13
21
|
showErrors: boolean;
|
|
14
22
|
}) {
|
|
@@ -19,19 +27,49 @@ export function MarkdownPropertyField({
|
|
|
19
27
|
} = useFormex();
|
|
20
28
|
|
|
21
29
|
const baseStoragePath = "storage";
|
|
30
|
+
const baseMarkdownPath = "markdown";
|
|
22
31
|
|
|
23
32
|
const metadata = `${baseStoragePath}.metadata`;
|
|
24
33
|
const fileName = `${baseStoragePath}.fileName`;
|
|
25
34
|
const maxSize = `${baseStoragePath}.maxSize`;
|
|
26
35
|
const storagePath = `${baseStoragePath}.storagePath`;
|
|
27
36
|
|
|
37
|
+
// Markdown config paths
|
|
38
|
+
const htmlPath = `${baseMarkdownPath}.html`;
|
|
39
|
+
const transformPastedTextPath = `${baseMarkdownPath}.transformPastedText`;
|
|
40
|
+
|
|
28
41
|
const fileNameValue = getIn(values, fileName) ?? "{rand}_{file}";
|
|
29
42
|
const storagePathValue = getIn(values, storagePath) ?? "/";
|
|
30
43
|
const maxSizeValue = getIn(values, maxSize);
|
|
31
44
|
|
|
45
|
+
// Markdown config values - check if markdown is an object or boolean
|
|
46
|
+
const markdownValue = getIn(values, "markdown");
|
|
47
|
+
const isMarkdownObject = typeof markdownValue === "object" && markdownValue !== null;
|
|
48
|
+
const htmlValue = isMarkdownObject ? (markdownValue.html ?? true) : true;
|
|
49
|
+
const transformPastedTextValue = isMarkdownObject ? (markdownValue.transformPastedText ?? false) : false;
|
|
50
|
+
|
|
32
51
|
const hasFilenameCallback = typeof fileNameValue === "function";
|
|
33
52
|
const hasStoragePathCallback = typeof storagePathValue === "function";
|
|
34
53
|
|
|
54
|
+
// Handler to update markdown config - converts from boolean to object if needed
|
|
55
|
+
const updateMarkdownConfig = useCallback((key: "html" | "transformPastedText", value: boolean) => {
|
|
56
|
+
const currentMarkdown = getIn(values, "markdown");
|
|
57
|
+
if (typeof currentMarkdown === "object" && currentMarkdown !== null) {
|
|
58
|
+
// Already an object, update the key
|
|
59
|
+
setFieldValue(`markdown.${key}`, value);
|
|
60
|
+
} else {
|
|
61
|
+
// Convert from boolean to object
|
|
62
|
+
const defaultConfig = {
|
|
63
|
+
html: true,
|
|
64
|
+
transformPastedText: false
|
|
65
|
+
};
|
|
66
|
+
setFieldValue("markdown", {
|
|
67
|
+
...defaultConfig,
|
|
68
|
+
[key]: value
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}, [values, setFieldValue]);
|
|
72
|
+
|
|
35
73
|
return (
|
|
36
74
|
<>
|
|
37
75
|
<div className={"col-span-12"}>
|
|
@@ -39,13 +77,13 @@ export function MarkdownPropertyField({
|
|
|
39
77
|
<ValidationPanel>
|
|
40
78
|
|
|
41
79
|
<StringPropertyValidation disabled={disabled}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
80
|
+
length={true}
|
|
81
|
+
lowercase={true}
|
|
82
|
+
max={true}
|
|
83
|
+
min={true}
|
|
84
|
+
trim={true}
|
|
85
|
+
uppercase={true}
|
|
86
|
+
showErrors={showErrors} />
|
|
49
87
|
|
|
50
88
|
</ValidationPanel>
|
|
51
89
|
|
|
@@ -55,9 +93,47 @@ export function MarkdownPropertyField({
|
|
|
55
93
|
<ExpandablePanel
|
|
56
94
|
title={
|
|
57
95
|
<div className="flex flex-row text-surface-500">
|
|
58
|
-
<
|
|
96
|
+
<SettingsIcon />
|
|
97
|
+
<Typography variant={"subtitle2"}
|
|
98
|
+
className="ml-4">
|
|
99
|
+
Paste behavior
|
|
100
|
+
</Typography>
|
|
101
|
+
</div>
|
|
102
|
+
}>
|
|
103
|
+
|
|
104
|
+
<div className={"flex flex-col gap-2 p-4"}>
|
|
105
|
+
<BooleanSwitchWithLabel
|
|
106
|
+
size={"small"}
|
|
107
|
+
disabled={disabled}
|
|
108
|
+
value={!htmlValue}
|
|
109
|
+
onValueChange={(value) => updateMarkdownConfig("html", !value)}
|
|
110
|
+
label={"Strip HTML on paste"}
|
|
111
|
+
/>
|
|
112
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
113
|
+
Remove HTML tags and inline styles when pasting content from external sources
|
|
114
|
+
</Typography>
|
|
115
|
+
|
|
116
|
+
<BooleanSwitchWithLabel
|
|
117
|
+
size={"small"}
|
|
118
|
+
disabled={disabled}
|
|
119
|
+
value={transformPastedTextValue}
|
|
120
|
+
onValueChange={(value) => updateMarkdownConfig("transformPastedText", value)}
|
|
121
|
+
label={"Convert pasted text to markdown"}
|
|
122
|
+
/>
|
|
123
|
+
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
124
|
+
Convert rich text (from Google Docs, Word, etc.) to clean markdown format
|
|
125
|
+
</Typography>
|
|
126
|
+
</div>
|
|
127
|
+
</ExpandablePanel>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div className={"col-span-12"}>
|
|
131
|
+
<ExpandablePanel
|
|
132
|
+
title={
|
|
133
|
+
<div className="flex flex-row text-surface-500">
|
|
134
|
+
<CloudUploadIcon />
|
|
59
135
|
<Typography variant={"subtitle2"}
|
|
60
|
-
|
|
136
|
+
className="ml-4">
|
|
61
137
|
File upload config
|
|
62
138
|
</Typography>
|
|
63
139
|
</div>
|
|
@@ -68,20 +144,20 @@ export function MarkdownPropertyField({
|
|
|
68
144
|
|
|
69
145
|
<div className={"col-span-12"}>
|
|
70
146
|
<Field name={fileName}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
147
|
+
as={DebouncedTextField}
|
|
148
|
+
label={"File name"}
|
|
149
|
+
size={"small"}
|
|
150
|
+
disabled={hasFilenameCallback || disabled}
|
|
151
|
+
value={hasFilenameCallback ? "-" : fileNameValue}
|
|
76
152
|
/>
|
|
77
153
|
</div>
|
|
78
154
|
<div className={"col-span-12"}>
|
|
79
155
|
<Field name={storagePath}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
156
|
+
as={DebouncedTextField}
|
|
157
|
+
label={"Storage path"}
|
|
158
|
+
disabled={hasStoragePathCallback || disabled}
|
|
159
|
+
size={"small"}
|
|
160
|
+
value={hasStoragePathCallback ? "-" : storagePathValue}
|
|
85
161
|
/>
|
|
86
162
|
<Typography variant={"caption"} className={"ml-3.5 mt-1 mb-2"}>
|
|
87
163
|
<p>You can use the following placeholders in
|
|
@@ -107,15 +183,15 @@ export function MarkdownPropertyField({
|
|
|
107
183
|
|
|
108
184
|
<div className={"col-span-12"}>
|
|
109
185
|
<DebouncedTextField name={maxSize}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
186
|
+
type={"number"}
|
|
187
|
+
label={"Max size (in bytes)"}
|
|
188
|
+
size={"small"}
|
|
189
|
+
value={maxSizeValue !== undefined && maxSizeValue !== null ? maxSizeValue.toString() : ""}
|
|
190
|
+
onChange={(e) => {
|
|
191
|
+
const value = e.target.value;
|
|
192
|
+
if (value === "") setFieldValue(maxSize, undefined);
|
|
193
|
+
else setFieldValue(maxSize, parseInt(value));
|
|
194
|
+
}}
|
|
119
195
|
/>
|
|
120
196
|
</div>
|
|
121
197
|
|
|
@@ -126,12 +202,12 @@ export function MarkdownPropertyField({
|
|
|
126
202
|
<div className={"col-span-12"}>
|
|
127
203
|
|
|
128
204
|
<TextField name={"defaultValue"}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
205
|
+
disabled={disabled}
|
|
206
|
+
onChange={(e: any) => {
|
|
207
|
+
setFieldValue("defaultValue", e.target.value === "" ? undefined : e.target.value);
|
|
208
|
+
}}
|
|
209
|
+
label={"Default value"}
|
|
210
|
+
value={getIn(values, "defaultValue") ?? ""} />
|
|
135
211
|
|
|
136
212
|
</div>
|
|
137
213
|
</>
|
|
@@ -88,7 +88,7 @@ export function StoragePropertyField({
|
|
|
88
88
|
|
|
89
89
|
<ExpandablePanel
|
|
90
90
|
title={
|
|
91
|
-
<div className="flex flex-row text-surface-500">
|
|
91
|
+
<div className="flex flex-row text-surface-500 text-text-secondary dark:text-text-secondary-dark">
|
|
92
92
|
<CloudUploadIcon/>
|
|
93
93
|
<Typography variant={"subtitle2"}
|
|
94
94
|
className="ml-4">
|