@firecms/collection_editor 3.0.0-beta.2-pre.2 → 3.0.0-beta.2-pre.4
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/index.es.js +2532 -2386
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/types/collection_editor_controller.d.ts +3 -2
- package/dist/types/config_controller.d.ts +3 -3
- package/dist/ui/collection_editor/CollectionEditorDialog.d.ts +3 -5
- package/dist/ui/collection_editor/CollectionEditorWelcomeView.d.ts +2 -2
- package/dist/ui/collection_editor/CollectionPropertiesEditorForm.d.ts +1 -2
- package/dist/ui/collection_editor/EnumForm.d.ts +1 -2
- package/dist/ui/collection_editor/PropertyEditView.d.ts +5 -5
- package/dist/ui/collection_editor/PropertyTree.d.ts +14 -13
- package/dist/ui/collection_editor/SwitchControl.d.ts +8 -0
- package/dist/ui/collection_editor/properties/CommonPropertyFields.d.ts +0 -1
- package/dist/ui/collection_editor/util.d.ts +1 -0
- package/package.json +6 -5
- package/src/ConfigControllerProvider.tsx +24 -22
- package/src/types/collection_editor_controller.tsx +4 -3
- package/src/types/config_controller.tsx +3 -3
- package/src/ui/CollectionViewHeaderAction.tsx +1 -1
- package/src/ui/EditorCollectionAction.tsx +3 -3
- package/src/ui/HomePageEditorCollectionAction.tsx +2 -2
- package/src/ui/MissingReferenceWidget.tsx +2 -1
- package/src/ui/NewCollectionButton.tsx +3 -3
- package/src/ui/NewCollectionCard.tsx +2 -1
- package/src/ui/PropertyAddColumnComponent.tsx +1 -1
- package/src/ui/RootCollectionSuggestions.tsx +3 -2
- package/src/ui/collection_editor/CollectionDetailsForm.tsx +2 -2
- package/src/ui/collection_editor/CollectionEditorDialog.tsx +420 -374
- package/src/ui/collection_editor/CollectionEditorWelcomeView.tsx +19 -12
- package/src/ui/collection_editor/CollectionPropertiesEditorForm.tsx +26 -18
- package/src/ui/collection_editor/EnumForm.tsx +118 -114
- package/src/ui/collection_editor/GetCodeDialog.tsx +1 -1
- package/src/ui/collection_editor/PropertyEditView.tsx +199 -142
- package/src/ui/collection_editor/PropertyFieldPreview.tsx +5 -1
- package/src/ui/collection_editor/PropertyTree.tsx +132 -113
- package/src/ui/collection_editor/SubcollectionsEditTab.tsx +18 -11
- package/src/ui/collection_editor/SwitchControl.tsx +39 -0
- package/src/ui/collection_editor/import/CollectionEditorImportMapping.tsx +10 -2
- package/src/ui/collection_editor/properties/BlockPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/BooleanPropertyField.tsx +13 -9
- package/src/ui/collection_editor/properties/CommonPropertyFields.tsx +11 -37
- package/src/ui/collection_editor/properties/DateTimePropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/EnumPropertyField.tsx +3 -6
- package/src/ui/collection_editor/properties/MapPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/NumberPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/ReferencePropertyField.tsx +11 -14
- package/src/ui/collection_editor/properties/RepeatPropertyField.tsx +10 -9
- package/src/ui/collection_editor/properties/StoragePropertyField.tsx +15 -9
- package/src/ui/collection_editor/properties/StringPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/UrlPropertyField.tsx +2 -2
- package/src/ui/collection_editor/properties/advanced/AdvancedPropertyValidation.tsx +27 -18
- package/src/ui/collection_editor/properties/validation/ArrayPropertyValidation.tsx +2 -2
- package/src/ui/collection_editor/properties/validation/GeneralPropertyValidation.tsx +27 -16
- package/src/ui/collection_editor/properties/validation/NumberPropertyValidation.tsx +33 -18
- package/src/ui/collection_editor/properties/validation/StringPropertyValidation.tsx +99 -80
- package/src/ui/collection_editor/util.ts +7 -0
- package/src/ui/collection_editor/utils/strings.ts +2 -1
- package/src/useCollectionEditorPlugin.tsx +1 -16
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import React, { useDeferredValue, useEffect, useRef, useState } from "react";
|
|
2
2
|
import equal from "react-fast-compare"
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { Formex, FormexController, getIn, useCreateFormex } from "@firecms/formex";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_FIELD_CONFIGS,
|
|
7
7
|
DeleteConfirmationDialog,
|
|
8
|
-
PropertyConfigBadge,
|
|
9
8
|
FieldConfigId,
|
|
10
9
|
getFieldConfig,
|
|
11
10
|
getFieldId,
|
|
12
11
|
isPropertyBuilder,
|
|
12
|
+
isValidRegExp,
|
|
13
13
|
mergeDeep,
|
|
14
14
|
Property,
|
|
15
15
|
PropertyConfig,
|
|
16
|
+
PropertyConfigBadge,
|
|
16
17
|
} from "@firecms/core";
|
|
17
18
|
import {
|
|
18
19
|
Button,
|
|
@@ -68,40 +69,42 @@ export type PropertyFormProps = {
|
|
|
68
69
|
onPropertyChanged?: (params: OnPropertyChangedParams) => void;
|
|
69
70
|
onPropertyChangedImmediate?: boolean;
|
|
70
71
|
onDelete?: (id?: string, namespace?: string) => void;
|
|
71
|
-
onError?: (id: string, namespace?: string, error?:
|
|
72
|
-
initialErrors?:
|
|
73
|
-
forceShowErrors?: boolean;
|
|
72
|
+
onError?: (id: string, namespace?: string, error?: Record<string, any>) => void;
|
|
73
|
+
initialErrors?: Record<string, any>;
|
|
74
74
|
existingPropertyKeys?: string[];
|
|
75
|
+
forceShowErrors?: boolean;
|
|
75
76
|
allowDataInference: boolean;
|
|
76
77
|
getData?: () => Promise<object[]>;
|
|
77
|
-
|
|
78
|
+
getController?: (formex: FormexController<PropertyWithId>) => void;
|
|
78
79
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
79
80
|
collectionEditable: boolean;
|
|
80
81
|
};
|
|
81
82
|
|
|
82
83
|
export const PropertyForm = React.memo(
|
|
83
|
-
function PropertyForm({
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
84
|
+
function PropertyForm(props: PropertyFormProps) {
|
|
85
|
+
|
|
86
|
+
const {
|
|
87
|
+
includeIdAndName = true,
|
|
88
|
+
autoOpenTypeSelect,
|
|
89
|
+
existingProperty,
|
|
90
|
+
autoUpdateId,
|
|
91
|
+
inArray,
|
|
92
|
+
propertyKey,
|
|
93
|
+
existingPropertyKeys,
|
|
94
|
+
propertyNamespace,
|
|
95
|
+
property,
|
|
96
|
+
onPropertyChanged,
|
|
97
|
+
onPropertyChangedImmediate = true,
|
|
98
|
+
onDelete,
|
|
99
|
+
onError,
|
|
100
|
+
initialErrors,
|
|
101
|
+
forceShowErrors,
|
|
102
|
+
allowDataInference,
|
|
103
|
+
getController,
|
|
104
|
+
getData,
|
|
105
|
+
propertyConfigs,
|
|
106
|
+
collectionEditable
|
|
107
|
+
} = props;
|
|
105
108
|
|
|
106
109
|
const initialValue: PropertyWithId = {
|
|
107
110
|
id: "",
|
|
@@ -130,13 +133,14 @@ export const PropertyForm = React.memo(
|
|
|
130
133
|
onPropertyChanged?.(params);
|
|
131
134
|
};
|
|
132
135
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
initialErrors={initialErrors}
|
|
136
|
-
initialValues={property
|
|
136
|
+
const formexController = useCreateFormex<PropertyWithId>({
|
|
137
|
+
initialValues: property
|
|
137
138
|
? { id: propertyKey, ...property } as PropertyWithId
|
|
138
|
-
: initialValue
|
|
139
|
-
|
|
139
|
+
: initialValue,
|
|
140
|
+
initialErrors,
|
|
141
|
+
validateOnChange: true,
|
|
142
|
+
validateOnInitialRender: true,
|
|
143
|
+
onSubmit: (newPropertyWithId, controller) => {
|
|
140
144
|
console.debug("onSubmit", newPropertyWithId);
|
|
141
145
|
const {
|
|
142
146
|
id,
|
|
@@ -147,54 +151,80 @@ export const PropertyForm = React.memo(
|
|
|
147
151
|
property: { ...property, editable: property.editable ?? true }
|
|
148
152
|
});
|
|
149
153
|
if (!existingProperty)
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
154
|
+
controller.resetForm({ values: initialValue });
|
|
155
|
+
},
|
|
156
|
+
validation: (values) => {
|
|
157
|
+
const errors: Record<string, any> = {};
|
|
158
|
+
if (includeIdAndName) {
|
|
159
|
+
if (!values.name) {
|
|
160
|
+
errors.name = "Required";
|
|
161
|
+
} else {
|
|
162
|
+
const nameError = validateName(values.name);
|
|
163
|
+
if (nameError)
|
|
164
|
+
errors.name = nameError;
|
|
165
|
+
}
|
|
166
|
+
if (!values.id) {
|
|
167
|
+
errors.id = "Required";
|
|
168
|
+
} else {
|
|
169
|
+
const idError = validateId(values.id, existingPropertyKeys);
|
|
170
|
+
if (idError)
|
|
171
|
+
errors.id = idError;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (values.dataType === "string") {
|
|
176
|
+
if (values.validation?.matches && !isValidRegExp(values.validation?.matches.toString())) {
|
|
177
|
+
errors.validation = {
|
|
178
|
+
matches: "Invalid regular expression"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (values.dataType === "reference" && !values.path) {
|
|
183
|
+
errors.path = "You must specify a target collection for the field";
|
|
184
|
+
}
|
|
185
|
+
if (values.propertyConfig === "repeat") {
|
|
186
|
+
if (!(values as any).of) {
|
|
187
|
+
errors.of = "You need to specify a repeat field";
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (values.propertyConfig === "block") {
|
|
191
|
+
if (!(values as any).oneOf) {
|
|
192
|
+
errors.oneOf = "You need to specify the properties of this block";
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return errors;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
getController?.(formexController);
|
|
201
|
+
}, [formexController, getController]);
|
|
202
|
+
|
|
203
|
+
return <Formex value={formexController}>
|
|
204
|
+
<PropertyEditFormFields
|
|
205
|
+
onPropertyChanged={onPropertyChangedImmediate
|
|
206
|
+
? doOnPropertyChanged
|
|
207
|
+
: undefined}
|
|
208
|
+
onDelete={onDelete}
|
|
209
|
+
includeIdAndTitle={includeIdAndName}
|
|
210
|
+
propertyNamespace={propertyNamespace}
|
|
211
|
+
onError={onError}
|
|
212
|
+
showErrors={forceShowErrors || formexController.submitCount > 0}
|
|
213
|
+
existing={existingProperty}
|
|
214
|
+
autoUpdateId={autoUpdateId}
|
|
215
|
+
inArray={inArray}
|
|
216
|
+
autoOpenTypeSelect={autoOpenTypeSelect}
|
|
217
|
+
disabled={disabled}
|
|
218
|
+
getData={getData}
|
|
219
|
+
allowDataInference={allowDataInference}
|
|
220
|
+
propertyConfigs={propertyConfigs}
|
|
221
|
+
collectionEditable={collectionEditable}
|
|
222
|
+
{...formexController}/>
|
|
223
|
+
</Formex>;
|
|
196
224
|
}, (a, b) =>
|
|
197
225
|
a.getData === b.getData &&
|
|
226
|
+
a.propertyKey === b.propertyKey &&
|
|
227
|
+
a.propertyNamespace === b.propertyNamespace &&
|
|
198
228
|
a.includeIdAndName === b.includeIdAndName &&
|
|
199
229
|
a.autoOpenTypeSelect === b.autoOpenTypeSelect &&
|
|
200
230
|
a.autoUpdateId === b.autoUpdateId &&
|
|
@@ -214,9 +244,9 @@ export function PropertyFormDialog({
|
|
|
214
244
|
onOkClicked?: () => void;
|
|
215
245
|
onCancel?: () => void;
|
|
216
246
|
}) {
|
|
217
|
-
const
|
|
218
|
-
const
|
|
219
|
-
|
|
247
|
+
const formexRef = useRef<FormexController<PropertyWithId>>();
|
|
248
|
+
const getController = (helpers: FormexController<PropertyWithId>) => {
|
|
249
|
+
formexRef.current = helpers;
|
|
220
250
|
};
|
|
221
251
|
|
|
222
252
|
return <Dialog
|
|
@@ -224,64 +254,68 @@ export function PropertyFormDialog({
|
|
|
224
254
|
maxWidth={"xl"}
|
|
225
255
|
fullWidth={true}
|
|
226
256
|
>
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
257
|
+
<form noValidate={true}
|
|
258
|
+
autoComplete={"off"}
|
|
259
|
+
onSubmit={(e) => {
|
|
260
|
+
e.preventDefault();
|
|
261
|
+
e.stopPropagation();
|
|
262
|
+
formexRef.current?.handleSubmit(e)
|
|
263
|
+
}}>
|
|
264
|
+
<DialogContent>
|
|
265
|
+
<PropertyForm {...formProps}
|
|
266
|
+
onPropertyChanged={(params) => {
|
|
267
|
+
onPropertyChanged?.(params);
|
|
268
|
+
onOkClicked?.();
|
|
269
|
+
}}
|
|
270
|
+
collectionEditable={collectionEditable}
|
|
271
|
+
onPropertyChangedImmediate={false}
|
|
272
|
+
getController={getController}
|
|
273
|
+
getData={getData}
|
|
274
|
+
/>
|
|
275
|
+
</DialogContent>
|
|
276
|
+
|
|
277
|
+
<DialogActions>
|
|
278
|
+
|
|
279
|
+
{onCancel && <Button
|
|
280
|
+
variant={"text"}
|
|
281
|
+
onClick={() => {
|
|
282
|
+
onCancel();
|
|
283
|
+
formexRef.current?.resetForm();
|
|
284
|
+
}}>
|
|
285
|
+
Cancel
|
|
286
|
+
</Button>}
|
|
287
|
+
|
|
288
|
+
<Button variant="outlined"
|
|
289
|
+
type={"submit"}
|
|
290
|
+
color="primary">
|
|
291
|
+
Ok
|
|
292
|
+
</Button>
|
|
293
|
+
</DialogActions>
|
|
294
|
+
</form>
|
|
258
295
|
</Dialog>;
|
|
259
296
|
|
|
260
297
|
}
|
|
261
298
|
|
|
262
|
-
function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
propertyConfigs,
|
|
283
|
-
collectionEditable
|
|
284
|
-
}: {
|
|
299
|
+
function PropertyEditFormFields({
|
|
300
|
+
values,
|
|
301
|
+
errors,
|
|
302
|
+
setValues,
|
|
303
|
+
existing,
|
|
304
|
+
autoUpdateId = false,
|
|
305
|
+
autoOpenTypeSelect,
|
|
306
|
+
includeIdAndTitle,
|
|
307
|
+
onPropertyChanged,
|
|
308
|
+
onDelete,
|
|
309
|
+
propertyNamespace,
|
|
310
|
+
onError,
|
|
311
|
+
showErrors,
|
|
312
|
+
disabled,
|
|
313
|
+
inArray,
|
|
314
|
+
getData,
|
|
315
|
+
allowDataInference,
|
|
316
|
+
propertyConfigs,
|
|
317
|
+
collectionEditable
|
|
318
|
+
}: {
|
|
285
319
|
includeIdAndTitle?: boolean;
|
|
286
320
|
existing: boolean;
|
|
287
321
|
autoUpdateId?: boolean;
|
|
@@ -289,16 +323,15 @@ function PropertyEditView({
|
|
|
289
323
|
propertyNamespace?: string;
|
|
290
324
|
onPropertyChanged?: (params: OnPropertyChangedParams) => void;
|
|
291
325
|
onDelete?: (id?: string, namespace?: string) => void;
|
|
292
|
-
onError?: (id: string, namespace?: string, error?:
|
|
326
|
+
onError?: (id: string, namespace?: string, error?: Record<string, any>) => void;
|
|
293
327
|
showErrors: boolean;
|
|
294
328
|
inArray: boolean;
|
|
295
329
|
disabled: boolean;
|
|
296
|
-
existingPropertyKeys?: string[];
|
|
297
330
|
getData?: () => Promise<object[]>;
|
|
298
331
|
allowDataInference: boolean;
|
|
299
332
|
propertyConfigs: Record<string, PropertyConfig>;
|
|
300
333
|
collectionEditable: boolean;
|
|
301
|
-
} &
|
|
334
|
+
} & FormexController<PropertyWithId>) {
|
|
302
335
|
|
|
303
336
|
const [selectOpen, setSelectOpen] = useState(autoOpenTypeSelect);
|
|
304
337
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
@@ -337,7 +370,7 @@ function PropertyEditView({
|
|
|
337
370
|
}, [deferredValues, includeIdAndTitle, onPropertyChanged, propertyNamespace]);
|
|
338
371
|
|
|
339
372
|
useEffect(() => {
|
|
340
|
-
if (values?.id && onError
|
|
373
|
+
if (values?.id && onError) {
|
|
341
374
|
onError(values?.id, propertyNamespace, errors);
|
|
342
375
|
}
|
|
343
376
|
}, [errors, onError, propertyNamespace, values?.id]);
|
|
@@ -530,7 +563,6 @@ function PropertyEditView({
|
|
|
530
563
|
<CommonPropertyFields showErrors={showErrors}
|
|
531
564
|
disabledId={existing}
|
|
532
565
|
isNewProperty={!existing}
|
|
533
|
-
existingPropertyKeys={existingPropertyKeys}
|
|
534
566
|
disabled={disabled}
|
|
535
567
|
autoUpdateId={autoUpdateId}
|
|
536
568
|
ref={nameFieldRef}/>}
|
|
@@ -556,3 +588,28 @@ function PropertyEditView({
|
|
|
556
588
|
</>
|
|
557
589
|
);
|
|
558
590
|
}
|
|
591
|
+
|
|
592
|
+
const idRegEx = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
593
|
+
|
|
594
|
+
function validateId(value?: string, existingPropertyKeys?: string[]) {
|
|
595
|
+
|
|
596
|
+
let error;
|
|
597
|
+
if (!value) {
|
|
598
|
+
error = "You must specify an id for the field";
|
|
599
|
+
}
|
|
600
|
+
if (value && !value.match(idRegEx)) {
|
|
601
|
+
error = "The id can only contain letters, numbers and underscores (_), and not start with a number";
|
|
602
|
+
}
|
|
603
|
+
if (value && existingPropertyKeys && existingPropertyKeys.includes(value)) {
|
|
604
|
+
error = "There is another field with this ID already";
|
|
605
|
+
}
|
|
606
|
+
return error;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function validateName(value: string) {
|
|
610
|
+
let error;
|
|
611
|
+
if (!value) {
|
|
612
|
+
error = "You must specify a title for the field";
|
|
613
|
+
}
|
|
614
|
+
return error;
|
|
615
|
+
}
|
|
@@ -42,9 +42,12 @@ export function PropertyFieldPreview({
|
|
|
42
42
|
const disabled = !editableProperty(property);
|
|
43
43
|
|
|
44
44
|
const borderColorClass = hasError
|
|
45
|
-
? "border-red-500"
|
|
45
|
+
? "border-red-500 dark:border-red-500 border-opacity-100 dark:border-opacity-100 ring-0 dark:ring-0"
|
|
46
46
|
: (selected ? "border-primary" : "border-transparent");
|
|
47
47
|
|
|
48
|
+
if(hasError)
|
|
49
|
+
console.log("PropertyFieldPreview", property)
|
|
50
|
+
|
|
48
51
|
return <ErrorBoundary>
|
|
49
52
|
<div
|
|
50
53
|
onClick={onClick}
|
|
@@ -54,6 +57,7 @@ export function PropertyFieldPreview({
|
|
|
54
57
|
</div>
|
|
55
58
|
<Paper
|
|
56
59
|
className={cn(
|
|
60
|
+
"border",
|
|
57
61
|
"pl-2 w-full flex flex-row gap-4 items-center",
|
|
58
62
|
cardMixin,
|
|
59
63
|
onClick ? cardClickableMixin : "",
|