@firecms/collection_editor 3.0.0-alpha.17 → 3.0.0-alpha.19
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/package.json +4 -3
- package/src/ConfigControllerProvider.tsx +177 -0
- package/src/components/EditorCollectionAction.tsx +95 -0
- package/src/components/HomePageEditorCollectionAction.tsx +81 -0
- package/src/components/NewCollectionCard.tsx +45 -0
- package/src/components/RootCollectionSuggestions.tsx +53 -0
- package/src/components/collection_editor/CollectionDetailsForm.tsx +312 -0
- package/src/components/collection_editor/CollectionEditorDialog.tsx +640 -0
- package/src/components/collection_editor/CollectionEditorWelcomeView.tsx +212 -0
- package/src/components/collection_editor/CollectionPropertiesEditorForm.tsx +450 -0
- package/src/components/collection_editor/CollectionYupValidation.tsx +6 -0
- package/src/components/collection_editor/EntityCustomViewsSelectDialog.tsx +29 -0
- package/src/components/collection_editor/EnumForm.tsx +354 -0
- package/src/components/collection_editor/PropertyEditView.tsx +535 -0
- package/src/components/collection_editor/PropertyFieldPreview.tsx +205 -0
- package/src/components/collection_editor/PropertySelectItem.tsx +31 -0
- package/src/components/collection_editor/PropertyTree.tsx +228 -0
- package/src/components/collection_editor/SelectIcons.tsx +72 -0
- package/src/components/collection_editor/SubcollectionsEditTab.tsx +239 -0
- package/src/components/collection_editor/UnsavedChangesDialog.tsx +47 -0
- package/src/components/collection_editor/import/CollectionEditorImportDataPreview.tsx +37 -0
- package/src/components/collection_editor/import/CollectionEditorImportMapping.tsx +236 -0
- package/src/components/collection_editor/import/clean_import_data.ts +53 -0
- package/src/components/collection_editor/properties/BlockPropertyField.tsx +131 -0
- package/src/components/collection_editor/properties/BooleanPropertyField.tsx +36 -0
- package/src/components/collection_editor/properties/CommonPropertyFields.tsx +112 -0
- package/src/components/collection_editor/properties/DateTimePropertyField.tsx +86 -0
- package/src/components/collection_editor/properties/EnumPropertyField.tsx +116 -0
- package/src/components/collection_editor/properties/FieldHelperView.tsx +13 -0
- package/src/components/collection_editor/properties/KeyValuePropertyField.tsx +20 -0
- package/src/components/collection_editor/properties/MapPropertyField.tsx +154 -0
- package/src/components/collection_editor/properties/NumberPropertyField.tsx +38 -0
- package/src/components/collection_editor/properties/ReferencePropertyField.tsx +184 -0
- package/src/components/collection_editor/properties/RepeatPropertyField.tsx +115 -0
- package/src/components/collection_editor/properties/StoragePropertyField.tsx +194 -0
- package/src/components/collection_editor/properties/StringPropertyField.tsx +85 -0
- package/src/components/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +36 -0
- package/src/components/collection_editor/properties/validation/ArrayPropertyValidation.tsx +50 -0
- package/src/components/collection_editor/properties/validation/GeneralPropertyValidation.tsx +49 -0
- package/src/components/collection_editor/properties/validation/NumberPropertyValidation.tsx +99 -0
- package/src/components/collection_editor/properties/validation/StringPropertyValidation.tsx +131 -0
- package/src/components/collection_editor/properties/validation/ValidationPanel.tsx +28 -0
- package/src/components/collection_editor/templates/blog_template.ts +115 -0
- package/src/components/collection_editor/templates/products_template.ts +89 -0
- package/src/components/collection_editor/templates/users_template.ts +34 -0
- package/src/components/collection_editor/util.ts +21 -0
- package/src/components/collection_editor/utils/supported_fields.tsx +28 -0
- package/src/components/collection_editor/utils/update_property_for_widget.ts +258 -0
- package/src/components/collection_editor/utils/useTraceUpdate.tsx +23 -0
- package/src/index.ts +31 -0
- package/src/types/collection_editor_controller.tsx +31 -0
- package/src/types/collection_inference.ts +3 -0
- package/src/types/config_controller.tsx +30 -0
- package/src/types/config_permissions.ts +20 -0
- package/src/types/persisted_collection.ts +7 -0
- package/src/useCollectionEditorController.tsx +9 -0
- package/src/useCollectionEditorPlugin.tsx +103 -0
- package/src/useCollectionsConfigController.tsx +9 -0
- package/src/utils/arrays.ts +3 -0
- package/src/utils/entities.ts +38 -0
- package/src/utils/icons.ts +17 -0
- package/src/utils/join_collections.ts +144 -0
- package/src/utils/synonyms.ts +1952 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cardClickableMixin,
|
|
3
|
+
cardMixin,
|
|
4
|
+
cardSelectedMixin,
|
|
5
|
+
cn,
|
|
6
|
+
ErrorBoundary,
|
|
7
|
+
FieldConfigBadge,
|
|
8
|
+
FunctionsIcon,
|
|
9
|
+
getFieldConfig,
|
|
10
|
+
isPropertyBuilder,
|
|
11
|
+
Paper,
|
|
12
|
+
Property,
|
|
13
|
+
PropertyOrBuilder,
|
|
14
|
+
RemoveCircleIcon,
|
|
15
|
+
Typography,
|
|
16
|
+
useFireCMSContext
|
|
17
|
+
} from "@firecms/core";
|
|
18
|
+
|
|
19
|
+
import { editableProperty } from "../../utils/entities";
|
|
20
|
+
|
|
21
|
+
export function PropertyFieldPreview({
|
|
22
|
+
property,
|
|
23
|
+
onClick,
|
|
24
|
+
hasError,
|
|
25
|
+
includeName,
|
|
26
|
+
includeEditButton,
|
|
27
|
+
selected
|
|
28
|
+
}: {
|
|
29
|
+
property: Property,
|
|
30
|
+
hasError?: boolean,
|
|
31
|
+
selected?: boolean,
|
|
32
|
+
includeName?: boolean,
|
|
33
|
+
includeEditButton?: boolean;
|
|
34
|
+
onClick?: () => void
|
|
35
|
+
}) {
|
|
36
|
+
|
|
37
|
+
const { fields } = useFireCMSContext();
|
|
38
|
+
|
|
39
|
+
const fieldConfig = getFieldConfig(property, fields);
|
|
40
|
+
const disabled = !editableProperty(property);
|
|
41
|
+
|
|
42
|
+
const borderColorClass = hasError
|
|
43
|
+
? "border-red-500"
|
|
44
|
+
: (selected ? "border-blue-500" : "border-transparent");
|
|
45
|
+
|
|
46
|
+
return <ErrorBoundary>
|
|
47
|
+
<div
|
|
48
|
+
onClick={onClick}
|
|
49
|
+
className="flex flex-row w-full cursor-pointer">
|
|
50
|
+
<div className={"m-4"}>
|
|
51
|
+
<FieldConfigBadge fieldConfig={fieldConfig}/>
|
|
52
|
+
</div>
|
|
53
|
+
<Paper
|
|
54
|
+
className={cn(
|
|
55
|
+
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
56
|
+
cardMixin,
|
|
57
|
+
onClick ? cardClickableMixin : "",
|
|
58
|
+
selected ? cardSelectedMixin : "",
|
|
59
|
+
"flex-grow p-4 border transition-colors duration-200",
|
|
60
|
+
borderColorClass
|
|
61
|
+
)}
|
|
62
|
+
>
|
|
63
|
+
|
|
64
|
+
<div className="w-full flex flex-col">
|
|
65
|
+
|
|
66
|
+
{includeName &&
|
|
67
|
+
<ErrorBoundary>
|
|
68
|
+
<Typography variant="body1"
|
|
69
|
+
component="span"
|
|
70
|
+
className="flex-grow pr-2">
|
|
71
|
+
{property.name
|
|
72
|
+
? property.name
|
|
73
|
+
: "\u00a0"
|
|
74
|
+
}
|
|
75
|
+
</Typography>
|
|
76
|
+
</ErrorBoundary>}
|
|
77
|
+
|
|
78
|
+
<div className="flex flex-row items-center">
|
|
79
|
+
<ErrorBoundary>
|
|
80
|
+
<Typography className="flex-grow pr-2"
|
|
81
|
+
variant={includeName ? "body2" : "subtitle1"}
|
|
82
|
+
component="span"
|
|
83
|
+
color="secondary">
|
|
84
|
+
{fieldConfig?.name}
|
|
85
|
+
</Typography>
|
|
86
|
+
</ErrorBoundary>
|
|
87
|
+
<ErrorBoundary>
|
|
88
|
+
<Typography variant="body2"
|
|
89
|
+
component="span"
|
|
90
|
+
color="disabled">
|
|
91
|
+
{property.dataType}
|
|
92
|
+
</Typography>
|
|
93
|
+
</ErrorBoundary>
|
|
94
|
+
|
|
95
|
+
{disabled && <div
|
|
96
|
+
className="text-xs h-3 ml-0.5">
|
|
97
|
+
<RemoveCircleIcon color={"disabled"}/>
|
|
98
|
+
</div>}
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{includeEditButton && <Typography variant={"button"}>
|
|
103
|
+
EDIT
|
|
104
|
+
</Typography>}
|
|
105
|
+
|
|
106
|
+
</Paper>
|
|
107
|
+
</div>
|
|
108
|
+
</ErrorBoundary>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function NonEditablePropertyPreview({
|
|
112
|
+
name,
|
|
113
|
+
selected,
|
|
114
|
+
onClick,
|
|
115
|
+
property
|
|
116
|
+
}: {
|
|
117
|
+
name: string,
|
|
118
|
+
selected: boolean,
|
|
119
|
+
onClick?: () => void,
|
|
120
|
+
property?: PropertyOrBuilder
|
|
121
|
+
}) {
|
|
122
|
+
|
|
123
|
+
const { fields } = useFireCMSContext();
|
|
124
|
+
|
|
125
|
+
const fieldConfig = !isPropertyBuilder(property) && property ? getFieldConfig(property, fields) : undefined;
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div
|
|
129
|
+
onClick={onClick}
|
|
130
|
+
className="flex flex-row w-full cursor-pointer">
|
|
131
|
+
<div className={"relative m-4"}>
|
|
132
|
+
{fieldConfig && <FieldConfigBadge fieldConfig={fieldConfig}/>}
|
|
133
|
+
{!fieldConfig && <div
|
|
134
|
+
className={"h-8 w-8 p-1 rounded-full shadow text-white bg-gray-500"}>
|
|
135
|
+
<FunctionsIcon color={"inherit"} size={"medium"}/>
|
|
136
|
+
</div>}
|
|
137
|
+
<RemoveCircleIcon color={"disabled"} size={"small"} className={"absolute -right-2 -top-2"}/>
|
|
138
|
+
</div>
|
|
139
|
+
<Paper
|
|
140
|
+
className={cn(
|
|
141
|
+
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
142
|
+
cardMixin,
|
|
143
|
+
onClick ? cardClickableMixin : "",
|
|
144
|
+
selected ? cardSelectedMixin : "",
|
|
145
|
+
"flex-grow p-4 border transition-colors duration-200",
|
|
146
|
+
selected ? "border-blue-500" : "border-transparent")}
|
|
147
|
+
>
|
|
148
|
+
|
|
149
|
+
<div className="w-full flex flex-col">
|
|
150
|
+
<Typography variant="body1"
|
|
151
|
+
component="span"
|
|
152
|
+
className="flex-grow pr-2">
|
|
153
|
+
{property?.name
|
|
154
|
+
? property.name
|
|
155
|
+
: name
|
|
156
|
+
}
|
|
157
|
+
</Typography>
|
|
158
|
+
|
|
159
|
+
<div className="flex flex-row items-center">
|
|
160
|
+
{fieldConfig && <Typography className="flex-grow pr-2"
|
|
161
|
+
variant={"body2"}
|
|
162
|
+
component="span"
|
|
163
|
+
color="secondary">
|
|
164
|
+
{fieldConfig?.name}
|
|
165
|
+
</Typography>}
|
|
166
|
+
|
|
167
|
+
{property && !isPropertyBuilder(property) && <ErrorBoundary>
|
|
168
|
+
<Typography variant="body2"
|
|
169
|
+
component="span"
|
|
170
|
+
color="disabled">
|
|
171
|
+
{property.dataType}
|
|
172
|
+
</Typography>
|
|
173
|
+
</ErrorBoundary>}
|
|
174
|
+
|
|
175
|
+
{property && isPropertyBuilder(property) && <ErrorBoundary>
|
|
176
|
+
<Typography variant="body2"
|
|
177
|
+
component="span"
|
|
178
|
+
color="disabled">
|
|
179
|
+
This property is defined as a property builder in code
|
|
180
|
+
</Typography>
|
|
181
|
+
</ErrorBoundary>}
|
|
182
|
+
|
|
183
|
+
{!property && <ErrorBoundary>
|
|
184
|
+
<Typography variant="body2"
|
|
185
|
+
component="span"
|
|
186
|
+
color="disabled">
|
|
187
|
+
This field is defined as an additional field in code
|
|
188
|
+
</Typography>
|
|
189
|
+
</ErrorBoundary>}
|
|
190
|
+
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
{/*<div className="flex flex-row text-xs">*/}
|
|
194
|
+
{/* <Typography className="flex-grow pr-2"*/}
|
|
195
|
+
{/* variant="body2"*/}
|
|
196
|
+
{/* component="span"*/}
|
|
197
|
+
{/* color="secondary">*/}
|
|
198
|
+
{/* This field can only be edited in code*/}
|
|
199
|
+
{/* </Typography>*/}
|
|
200
|
+
{/*</div>*/}
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
</Paper>
|
|
204
|
+
</div>)
|
|
205
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { cn, FieldConfig, FieldConfigBadge, SelectItem, Typography } from "@firecms/core";
|
|
2
|
+
|
|
3
|
+
export interface PropertySelectItemProps {
|
|
4
|
+
value: string;
|
|
5
|
+
optionDisabled: boolean;
|
|
6
|
+
fieldConfig: FieldConfig;
|
|
7
|
+
existing: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function PropertySelectItem({ value, optionDisabled, fieldConfig, existing }: PropertySelectItemProps) {
|
|
11
|
+
return <SelectItem value={value}
|
|
12
|
+
disabled={optionDisabled}
|
|
13
|
+
className={"flex flex-row items-center"}>
|
|
14
|
+
<div
|
|
15
|
+
className={cn(
|
|
16
|
+
"flex flex-row items-center text-base min-h-[52px]",
|
|
17
|
+
optionDisabled ? "w-full" : "")}>
|
|
18
|
+
<div className={"mr-8"}>
|
|
19
|
+
<FieldConfigBadge fieldConfig={fieldConfig}/>
|
|
20
|
+
</div>
|
|
21
|
+
<div>
|
|
22
|
+
<div>{fieldConfig.name}</div>
|
|
23
|
+
<Typography variant={"caption"}
|
|
24
|
+
color={"disabled"}
|
|
25
|
+
className={"max-w-sm"}>
|
|
26
|
+
{existing && optionDisabled ? "You can only switch to widgets that use the same data type" : fieldConfig.description}
|
|
27
|
+
</Typography>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</SelectItem>
|
|
31
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AdditionalFieldDelegate,
|
|
3
|
+
AutoAwesomeIcon,
|
|
4
|
+
CMSType,
|
|
5
|
+
defaultBorderMixin,
|
|
6
|
+
DragHandleIcon,
|
|
7
|
+
ErrorBoundary,
|
|
8
|
+
IconButton, isPropertyBuilder,
|
|
9
|
+
PropertiesOrBuilders, PropertyOrBuilder,
|
|
10
|
+
RemoveIcon,
|
|
11
|
+
Tooltip
|
|
12
|
+
} from "@firecms/core";
|
|
13
|
+
import { NonEditablePropertyPreview, PropertyFieldPreview } from "./PropertyFieldPreview";
|
|
14
|
+
import { DragDropContext, Draggable, DraggableProvided, Droppable } from "@hello-pangea/dnd";
|
|
15
|
+
import { getFullId, idToPropertiesPath } from "./util";
|
|
16
|
+
import { getIn } from "formik";
|
|
17
|
+
import { editableProperty } from "../../utils/entities";
|
|
18
|
+
import { useCallback } from "react";
|
|
19
|
+
|
|
20
|
+
export function PropertyTree<M extends {
|
|
21
|
+
[Key: string]: CMSType
|
|
22
|
+
}>({
|
|
23
|
+
namespace,
|
|
24
|
+
selectedPropertyKey,
|
|
25
|
+
onPropertyClick,
|
|
26
|
+
properties,
|
|
27
|
+
propertiesOrder: propertiesOrderProp,
|
|
28
|
+
additionalFields,
|
|
29
|
+
errors,
|
|
30
|
+
onPropertyMove,
|
|
31
|
+
onPropertyRemove,
|
|
32
|
+
className,
|
|
33
|
+
inferredPropertyKeys
|
|
34
|
+
}: {
|
|
35
|
+
namespace?: string;
|
|
36
|
+
selectedPropertyKey?: string;
|
|
37
|
+
onPropertyClick?: (propertyKey: string, namespace?: string) => void;
|
|
38
|
+
properties: PropertiesOrBuilders<M>;
|
|
39
|
+
propertiesOrder?: string[];
|
|
40
|
+
additionalFields?: AdditionalFieldDelegate<M>[];
|
|
41
|
+
errors: Record<string, any>;
|
|
42
|
+
onPropertyMove?: (propertiesOrder: string[], namespace?: string) => void;
|
|
43
|
+
onPropertyRemove?: (propertyKey: string, namespace?: string) => void;
|
|
44
|
+
className?: string;
|
|
45
|
+
inferredPropertyKeys?: string[];
|
|
46
|
+
}) {
|
|
47
|
+
|
|
48
|
+
const propertiesOrder = propertiesOrderProp ?? Object.keys(properties);
|
|
49
|
+
|
|
50
|
+
const onDragEnd = useCallback((result: any) => {
|
|
51
|
+
// dropped outside the list
|
|
52
|
+
if (!result.destination) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const startIndex = result.source.index;
|
|
56
|
+
const endIndex = result.destination.index;
|
|
57
|
+
|
|
58
|
+
const newPropertiesOrder = Array.from(propertiesOrder);
|
|
59
|
+
const [removed] = newPropertiesOrder.splice(startIndex, 1);
|
|
60
|
+
newPropertiesOrder.splice(endIndex, 0, removed);
|
|
61
|
+
if (onPropertyMove)
|
|
62
|
+
onPropertyMove(newPropertiesOrder, namespace);
|
|
63
|
+
}, [namespace, onPropertyMove, propertiesOrder])
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<>
|
|
67
|
+
|
|
68
|
+
<DragDropContext onDragEnd={onDragEnd}>
|
|
69
|
+
<Droppable droppableId={`droppable_${namespace}`}>
|
|
70
|
+
{(droppableProvided, droppableSnapshot) => (
|
|
71
|
+
<div
|
|
72
|
+
{...droppableProvided.droppableProps}
|
|
73
|
+
ref={droppableProvided.innerRef}
|
|
74
|
+
className={className}>
|
|
75
|
+
{propertiesOrder && propertiesOrder
|
|
76
|
+
// .filter((propertyKey) => Boolean(properties[propertyKey]))
|
|
77
|
+
.map((propertyKey: string, index: number) => {
|
|
78
|
+
const property = properties[propertyKey] as PropertyOrBuilder;
|
|
79
|
+
const additionalField = additionalFields?.find(field => field.id === propertyKey);
|
|
80
|
+
|
|
81
|
+
if (!property && !additionalField) {
|
|
82
|
+
console.warn(`Property ${propertyKey} not found in properties or additionalFields`);
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return (
|
|
86
|
+
<Draggable
|
|
87
|
+
key={`array_field_${namespace}_${propertyKey}}`}
|
|
88
|
+
draggableId={`array_field_${namespace}_${propertyKey}}`}
|
|
89
|
+
index={index}>
|
|
90
|
+
{(provided, snapshot) => {
|
|
91
|
+
return (
|
|
92
|
+
<ErrorBoundary>
|
|
93
|
+
<PropertyTreeEntry
|
|
94
|
+
propertyKey={propertyKey as string}
|
|
95
|
+
propertyOrBuilder={property}
|
|
96
|
+
additionalField={additionalField}
|
|
97
|
+
provided={provided}
|
|
98
|
+
errors={errors}
|
|
99
|
+
namespace={namespace}
|
|
100
|
+
inferredPropertyKeys={inferredPropertyKeys}
|
|
101
|
+
onPropertyMove={onPropertyMove}
|
|
102
|
+
onPropertyRemove={onPropertyRemove}
|
|
103
|
+
onPropertyClick={snapshot.isDragging ? undefined : onPropertyClick}
|
|
104
|
+
selectedPropertyKey={selectedPropertyKey}
|
|
105
|
+
/>
|
|
106
|
+
</ErrorBoundary>
|
|
107
|
+
);
|
|
108
|
+
}}
|
|
109
|
+
</Draggable>);
|
|
110
|
+
}).filter(Boolean)}
|
|
111
|
+
|
|
112
|
+
{droppableProvided.placeholder}
|
|
113
|
+
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
</Droppable>
|
|
117
|
+
</DragDropContext>
|
|
118
|
+
|
|
119
|
+
</>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function PropertyTreeEntry({
|
|
124
|
+
propertyKey,
|
|
125
|
+
namespace,
|
|
126
|
+
propertyOrBuilder,
|
|
127
|
+
additionalField,
|
|
128
|
+
provided,
|
|
129
|
+
selectedPropertyKey,
|
|
130
|
+
errors,
|
|
131
|
+
onPropertyClick,
|
|
132
|
+
onPropertyMove,
|
|
133
|
+
onPropertyRemove,
|
|
134
|
+
inferredPropertyKeys
|
|
135
|
+
}: {
|
|
136
|
+
propertyKey: string;
|
|
137
|
+
namespace?: string;
|
|
138
|
+
propertyOrBuilder: PropertyOrBuilder;
|
|
139
|
+
additionalField?: AdditionalFieldDelegate<any>;
|
|
140
|
+
selectedPropertyKey?: string;
|
|
141
|
+
provided: DraggableProvided;
|
|
142
|
+
errors: Record<string, any>;
|
|
143
|
+
onPropertyClick?: (propertyKey: string, namespace?: string) => void;
|
|
144
|
+
onPropertyMove?: (propertiesOrder: string[], namespace?: string) => void;
|
|
145
|
+
onPropertyRemove?: (propertyKey: string, namespace?: string) => void;
|
|
146
|
+
inferredPropertyKeys?: string[];
|
|
147
|
+
}) {
|
|
148
|
+
|
|
149
|
+
const isPropertyInferred = inferredPropertyKeys?.includes(namespace ? `${namespace}.${propertyKey}` : propertyKey);
|
|
150
|
+
|
|
151
|
+
const fullId = getFullId(propertyKey, namespace);
|
|
152
|
+
let subtree;
|
|
153
|
+
if (typeof propertyOrBuilder === "object") {
|
|
154
|
+
const property = propertyOrBuilder;
|
|
155
|
+
if (property.dataType === "map" && property.properties) {
|
|
156
|
+
subtree = <PropertyTree
|
|
157
|
+
selectedPropertyKey={selectedPropertyKey}
|
|
158
|
+
namespace={fullId}
|
|
159
|
+
properties={property.properties}
|
|
160
|
+
propertiesOrder={property.propertiesOrder}
|
|
161
|
+
errors={errors}
|
|
162
|
+
onPropertyClick={onPropertyClick}
|
|
163
|
+
onPropertyMove={onPropertyMove}
|
|
164
|
+
onPropertyRemove={onPropertyRemove}/>
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const hasError = fullId ? getIn(errors, idToPropertiesPath(fullId)) : false;
|
|
169
|
+
const selected = selectedPropertyKey === fullId;
|
|
170
|
+
const editable = propertyOrBuilder && editableProperty(propertyOrBuilder);
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<div
|
|
174
|
+
ref={provided.innerRef}
|
|
175
|
+
{...provided.draggableProps}
|
|
176
|
+
{...provided.dragHandleProps}
|
|
177
|
+
className="relative -ml-8"
|
|
178
|
+
>
|
|
179
|
+
{subtree && <div
|
|
180
|
+
className={"absolute border-l " + defaultBorderMixin}
|
|
181
|
+
style={{
|
|
182
|
+
left: "32px",
|
|
183
|
+
top: "64px",
|
|
184
|
+
bottom: "16px"
|
|
185
|
+
}}/>}
|
|
186
|
+
|
|
187
|
+
{!isPropertyBuilder(propertyOrBuilder) && !additionalField && editable
|
|
188
|
+
? <PropertyFieldPreview
|
|
189
|
+
property={propertyOrBuilder}
|
|
190
|
+
onClick={onPropertyClick ? () => onPropertyClick(propertyKey, namespace) : undefined}
|
|
191
|
+
includeName={true}
|
|
192
|
+
selected={selected}
|
|
193
|
+
hasError={hasError}/>
|
|
194
|
+
: <NonEditablePropertyPreview name={propertyKey}
|
|
195
|
+
property={propertyOrBuilder}
|
|
196
|
+
onClick={onPropertyClick ? () => onPropertyClick(propertyKey, namespace) : undefined}
|
|
197
|
+
selected={selected}/>}
|
|
198
|
+
|
|
199
|
+
<div className="absolute top-2 right-2 flex flex-row ">
|
|
200
|
+
|
|
201
|
+
{isPropertyInferred && <Tooltip title={"Inferred property"}>
|
|
202
|
+
<AutoAwesomeIcon size="small" className={"p-2"}/>
|
|
203
|
+
</Tooltip>}
|
|
204
|
+
|
|
205
|
+
{onPropertyRemove && <Tooltip title={"Remove"}>
|
|
206
|
+
<IconButton size="small"
|
|
207
|
+
color="inherit"
|
|
208
|
+
onClick={() => onPropertyRemove(propertyKey, namespace)}>
|
|
209
|
+
<RemoveIcon size={"small"}/>
|
|
210
|
+
</IconButton>
|
|
211
|
+
</Tooltip>}
|
|
212
|
+
|
|
213
|
+
{onPropertyMove && <Tooltip title={"Move"}>
|
|
214
|
+
<IconButton
|
|
215
|
+
component={"span"}
|
|
216
|
+
size="small"
|
|
217
|
+
>
|
|
218
|
+
<DragHandleIcon size={"small"}/>
|
|
219
|
+
</IconButton>
|
|
220
|
+
</Tooltip>}
|
|
221
|
+
</div>
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
{subtree && <div className={"ml-16"}>{subtree}</div>}
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import synonyms from "../../utils/synonyms";
|
|
3
|
+
import { iconsSearch } from "../../utils/icons";
|
|
4
|
+
import { coolIconKeys, debounce, Icon, IconButton, iconKeys, SearchBar, Tooltip } from "@firecms/core";
|
|
5
|
+
|
|
6
|
+
const UPDATE_SEARCH_INDEX_WAIT_MS = 220;
|
|
7
|
+
|
|
8
|
+
if (process.env.NODE_ENV !== "production") {
|
|
9
|
+
Object.keys(synonyms).forEach((icon: string) => {
|
|
10
|
+
if (!iconKeys.includes(icon)) {
|
|
11
|
+
console.warn(`The icon ${icon} no longer exists. Remove it from \`synonyms\``);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface SearchIconsProps {
|
|
17
|
+
selectedIcon?: string;
|
|
18
|
+
onIconSelected: (icon: string) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function SearchIcons({ selectedIcon = "", onIconSelected }: SearchIconsProps) {
|
|
22
|
+
const [keys, setKeys] = React.useState<string[] | null>(null);
|
|
23
|
+
const [query, setQuery] = React.useState<string>("");
|
|
24
|
+
|
|
25
|
+
const updateSearchResults = React.useMemo(() =>
|
|
26
|
+
debounce((value: string) => {
|
|
27
|
+
if (!value || value === "") {
|
|
28
|
+
setKeys(null);
|
|
29
|
+
} else {
|
|
30
|
+
const searchResult = iconsSearch.search(value);
|
|
31
|
+
setKeys(searchResult.map((e:any) => e.key));
|
|
32
|
+
}
|
|
33
|
+
}, UPDATE_SEARCH_INDEX_WAIT_MS), []
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
updateSearchResults(query);
|
|
38
|
+
return () => {
|
|
39
|
+
updateSearchResults.clear();
|
|
40
|
+
};
|
|
41
|
+
}, [query, updateSearchResults]);
|
|
42
|
+
|
|
43
|
+
const icons = keys === null ? coolIconKeys : keys;
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<>
|
|
47
|
+
<SearchBar
|
|
48
|
+
autoFocus
|
|
49
|
+
className={"w-full sticky top-0 z-10"}
|
|
50
|
+
onTextSearch={(value?: string) => setQuery(value ?? "")}
|
|
51
|
+
placeholder="Search for more icons…"
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
<div className={"flex max-w-full flex-wrap mt-4"}>
|
|
55
|
+
{icons.map((icon: string) => {
|
|
56
|
+
return (
|
|
57
|
+
<Tooltip title={icon} key={icon}>
|
|
58
|
+
<IconButton
|
|
59
|
+
shape={"square"}
|
|
60
|
+
toggled={selectedIcon === icon}
|
|
61
|
+
onClick={() => onIconSelected(icon)}
|
|
62
|
+
className="box-content m-1"
|
|
63
|
+
>
|
|
64
|
+
<Icon iconKey={icon} size={24} />
|
|
65
|
+
</IconButton>
|
|
66
|
+
</Tooltip>
|
|
67
|
+
);
|
|
68
|
+
})}
|
|
69
|
+
</div>
|
|
70
|
+
</>
|
|
71
|
+
);
|
|
72
|
+
}
|