fix: clean up dependencies and config schema (#704)

This commit is contained in:
Daniel Lautzenheiser 2023-04-17 10:35:35 -04:00 committed by GitHub
parent 778e6bac75
commit ecb1218646
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 304 additions and 179 deletions

View File

@ -156,8 +156,6 @@
"symbol-observable": "4.0.0", "symbol-observable": "4.0.0",
"unified": "10.1.2", "unified": "10.1.2",
"unist-util-visit": "4.1.2", "unist-util-visit": "4.1.2",
"uploadcare-widget": "3.21.0",
"uploadcare-widget-tab-effects": "1.6.0",
"url": "0.11.0", "url": "0.11.0",
"url-join": "5.0.0", "url-join": "5.0.0",
"uuid": "9.0.0", "uuid": "9.0.0",

View File

@ -1,5 +1,5 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Navigate, useParams } from 'react-router-dom'; import { Navigate, useParams, useSearchParams } from 'react-router-dom';
import { import {
selectCollection, selectCollection,
@ -16,6 +16,8 @@ interface CollectionRouteProps {
const CollectionRoute = ({ isSearchResults, isSingleSearchResult }: CollectionRouteProps) => { const CollectionRoute = ({ isSearchResults, isSingleSearchResult }: CollectionRouteProps) => {
const { name, searchTerm } = useParams(); const { name, searchTerm } = useParams();
const [searchParams] = useSearchParams();
const noRedirect = searchParams.has('noredirect');
const collectionSelector = useMemo(() => selectCollection(name), [name]); const collectionSelector = useMemo(() => selectCollection(name), [name]);
const collection = useAppSelector(collectionSelector); const collection = useAppSelector(collectionSelector);
@ -27,7 +29,12 @@ const CollectionRoute = ({ isSearchResults, isSingleSearchResult }: CollectionRo
return <Navigate to={defaultPath} />; return <Navigate to={defaultPath} />;
} }
if (collection && 'files' in collection && collection.files?.length === 1) { if (collection && 'files' in collection && collection.files?.length === 1 && !noRedirect) {
const href = window.location.href;
if (!href.includes('noredirect')) {
window.history.replaceState(null, document.title, `${href}?noredirect`);
console.log('REPLACE STATE', document.title, `${href}?noredirect`);
}
return <Navigate to={`/collections/${collection.name}/entries/${collection.files[0].name}`} />; return <Navigate to={`/collections/${collection.name}/entries/${collection.files[0].name}`} />;
} }

View File

@ -1,23 +1,31 @@
import React from 'react'; import React from 'react';
import type { FC } from 'react'; import classNames from '@staticcms/core/lib/util/classNames.util';
import type { FieldError } from '@staticcms/core/interface'; import type { FieldError } from '@staticcms/core/interface';
import type { FC } from 'react';
export interface ErrorMessageProps { export interface ErrorMessageProps {
errors: FieldError[]; errors: FieldError[];
className?: string;
} }
const ErrorMessage: FC<ErrorMessageProps> = ({ errors }) => { const ErrorMessage: FC<ErrorMessageProps> = ({ errors, className }) => {
return errors.length ? ( return errors.length ? (
<div <div
key="error" key="error"
data-testid="error" data-testid="error"
className="flex className={classNames(
`
flex
w-full w-full
text-xs text-xs
text-red-500 text-red-500
px-3 px-3
pt-1" pt-1
`,
className,
)}
> >
{errors[0].message} {errors[0].message}
</div> </div>

View File

@ -58,7 +58,6 @@ const Pill: FC<PillProps> = ({
` `
text-xs text-xs
font-medium font-medium
mr-2
px-3 px-3
py-1 py-1
rounded-lg rounded-lg

View File

@ -80,7 +80,7 @@ const EditorControl = ({
[field.name, fieldName, parentPath], [field.name, fieldName, parentPath],
); );
const [dirty, setDirty] = useState(!isEmpty(value)); const [dirty, setDirty] = useState(!isEmpty(widget.getValidValue(value, field as UnknownField)));
const fieldErrorsSelector = useMemo( const fieldErrorsSelector = useMemo(
() => selectFieldErrors(path, i18n, isMeta), () => selectFieldErrors(path, i18n, isMeta),
@ -114,10 +114,12 @@ const EditorControl = ({
const handleChangeDraftField = useCallback( const handleChangeDraftField = useCallback(
(value: ValueOrNestedValue) => { (value: ValueOrNestedValue) => {
setDirty(true); setDirty(
oldDirty => oldDirty || !isEmpty(widget.getValidValue(value, field as UnknownField)),
);
changeDraftField({ path, field, value, i18n, isMeta }); changeDraftField({ path, field, value, i18n, isMeta });
}, },
[changeDraftField, field, i18n, isMeta, path], [changeDraftField, field, i18n, isMeta, path, widget],
); );
const config = useMemo(() => configState.config, [configState.config]); const config = useMemo(() => configState.config, [configState.config]);

View File

@ -113,6 +113,9 @@ const viewFilters = {
{ {
type: 'string', type: 'string',
}, },
{
type: 'number',
},
], ],
}, },
}, },
@ -149,52 +152,42 @@ function getConfigSchema() {
type: 'object', type: 'object',
properties: { properties: {
name: { type: 'string', examples: ['test-repo'] }, name: { type: 'string', examples: ['test-repo'] },
repo: { type: 'string' },
branch: { type: 'string' },
api_root: { type: 'string' },
site_domain: { type: 'string' },
base_url: { type: 'string' },
auth_endpoint: { type: 'string' },
app_id: { type: 'string' },
auth_type: {
type: 'string',
examples: ['implicit', 'pkce'],
enum: ['implicit', 'public_pkcerepo'],
},
proxy_url: { type: 'string' },
large_media_url: { type: 'string' },
login: { type: 'boolean' },
identity_url: { type: 'string' },
gateway_url: { type: 'string' },
auth_scope: { auth_scope: {
type: 'string', type: 'string',
examples: ['repo', 'public_repo'], examples: ['repo', 'public_repo'],
enum: ['repo', 'public_repo'], enum: ['repo', 'public_repo'],
}, },
}, commit_messages: {
required: ['name'],
},
local_backend: {
oneOf: [
{ type: 'boolean' },
{
type: 'object', type: 'object',
properties: { properties: {
url: { type: 'string', examples: ['http://localhost:8081/api/v1'] }, create: { type: 'string' },
allowed_hosts: { update: { type: 'string' },
type: 'array', delete: { type: 'string' },
items: { type: 'string' }, uploadMedia: { type: 'string' },
}, deleteMedia: { type: 'string' },
}, },
additionalProperties: false, additionalProperties: false,
}, },
],
},
locale: { type: 'string', examples: ['en', 'fr', 'de'] },
i18n: i18nRoot,
site_url: { type: 'string', examples: ['https://example.com'] },
display_url: { type: 'string', examples: ['https://example.com'] },
logo_url: { type: 'string', examples: ['https://example.com/images/logo.svg'] },
media_folder: { type: 'string', examples: ['assets/uploads'] },
public_folder: { type: 'string', examples: ['/uploads'] },
media_folder_relative: { type: 'boolean' },
media_library: {
type: 'object',
properties: {
name: { type: 'string', examples: ['uploadcare'] },
config: { type: 'object' },
},
required: [],
},
slug: {
type: 'object',
properties: {
encoding: { type: 'string', enum: ['unicode', 'ascii'] },
clean_accents: { type: 'boolean' },
}, },
required: ['name'],
additionalProperties: false,
}, },
collections: { collections: {
type: 'array', type: 'array',
@ -204,34 +197,8 @@ function getConfigSchema() {
type: 'object', type: 'object',
properties: { properties: {
name: { type: 'string' }, name: { type: 'string' },
label: { type: 'string' },
label_singular: { type: 'string' },
description: { type: 'string' }, description: { type: 'string' },
folder: { type: 'string' }, icon: { type: 'string' },
files: {
type: 'array',
items: {
// ------- Each file: -------
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
label_singular: { type: 'string' },
description: { type: 'string' },
file: { type: 'string' },
editor: {
type: 'object',
properties: {
preview: { type: 'boolean' },
},
},
fields: fieldsConfig(),
},
required: ['name', 'label', 'file', 'fields'],
},
uniqueItemProperties: ['name'],
},
identifier_field: { type: 'string' },
summary: { type: 'string' }, summary: { type: 'string' },
summary_fields: { summary_fields: {
type: 'array', type: 'array',
@ -239,28 +206,17 @@ function getConfigSchema() {
type: 'string', type: 'string',
}, },
}, },
slug: { type: 'string' }, filter: {
path: { type: 'string' },
create: { type: 'boolean' },
publish: { type: 'boolean' },
hide: { type: 'boolean' },
editor: {
type: 'object', type: 'object',
properties: { properties: {
preview: { type: 'boolean' }, value: { type: 'string' },
field: { type: 'string' },
}, },
required: ['value', 'field'],
additionalProperties: false,
}, },
format: { type: 'string', enum: Object.keys(formatExtensions) }, label_singular: { type: 'string' },
extension: { type: 'string' }, label: { type: 'string' },
frontmatter_delimiter: {
type: ['string', 'array'],
minItems: 2,
maxItems: 2,
items: {
type: 'string',
},
},
fields: fieldsConfig(),
sortable_fields: { sortable_fields: {
type: 'object', type: 'object',
properties: { properties: {
@ -275,6 +231,7 @@ function getConfigSchema() {
}, },
}, },
required: ['field'], required: ['field'],
additionalProperties: false,
}, },
fields: { fields: {
type: 'array', type: 'array',
@ -284,9 +241,47 @@ function getConfigSchema() {
}, },
}, },
required: ['fields'], required: ['fields'],
additionalProperties: false,
}, },
view_filters: viewFilters, view_filters: viewFilters,
view_groups: viewGroups, view_groups: viewGroups,
i18n: i18nCollection,
hide: { type: 'boolean' },
editor: {
type: 'object',
properties: {
preview: { type: 'boolean' },
frame: { type: 'boolean' },
},
additionalProperties: false,
},
identifier_field: { type: 'string' },
path: { type: 'string' },
extension: { type: 'string' },
format: { type: 'string', enum: Object.keys(formatExtensions) },
frontmatter_delimiter: {
type: ['string', 'array'],
minItems: 2,
maxItems: 2,
items: {
type: 'string',
},
},
slug: { type: 'string' },
media_folder: { type: 'string' },
public_folder: { type: 'string' },
media_library: {
type: 'object',
properties: {
max_file_size: { type: 'number' },
folder_support: { type: 'boolean' },
},
additionalProperties: false,
},
folder: { type: 'string' },
fields: fieldsConfig(),
create: { type: 'boolean' },
delete: { type: 'boolean' },
nested: { nested: {
type: 'object', type: 'object',
properties: { properties: {
@ -299,11 +294,49 @@ function getConfigSchema() {
index_file: { type: 'string' }, index_file: { type: 'string' },
}, },
required: ['index_file'], required: ['index_file'],
additionalProperties: false,
}, },
}, },
required: ['depth'], required: ['depth'],
additionalProperties: false,
},
files: {
type: 'array',
items: {
// ------- Each file: -------
type: 'object',
properties: {
name: { type: 'string' },
label: { type: 'string' },
file: { type: 'string' },
fields: fieldsConfig(),
label_singular: { type: 'string' },
description: { type: 'string' },
media_folder: { type: 'string' },
public_folder: { type: 'string' },
media_library: {
type: 'object',
properties: {
max_file_size: { type: 'number' },
folder_support: { type: 'boolean' },
},
additionalProperties: false,
}, },
i18n: i18nCollection, i18n: i18nCollection,
editor: {
type: 'object',
properties: {
preview: { type: 'boolean' },
frame: { type: 'boolean' },
},
additionalProperties: false,
},
},
required: ['name', 'label', 'file', 'fields'],
additionalProperties: false,
},
uniqueItemProperties: ['name'],
},
}, },
required: ['name', 'label'], required: ['name', 'label'],
oneOf: [{ required: ['files'] }, { required: ['folder', 'fields'] }], oneOf: [{ required: ['files'] }, { required: ['folder', 'fields'] }],
@ -328,15 +361,62 @@ function getConfigSchema() {
}, },
uniqueItemProperties: ['name'], uniqueItemProperties: ['name'],
}, },
locale: { type: 'string', examples: ['en', 'fr', 'de'] },
site_id: { type: 'string' },
site_url: { type: 'string', examples: ['https://example.com'] },
display_url: { type: 'string', examples: ['https://example.com'] },
base_url: { type: 'string' },
logo_url: { type: 'string', examples: ['https://example.com/images/logo.svg'] },
media_folder: { type: 'string', examples: ['assets/uploads'] },
public_folder: { type: 'string', examples: ['/uploads'] },
media_folder_relative: { type: 'boolean' },
media_library: {
type: 'object',
properties: {
max_file_size: { type: 'number' },
folder_support: { type: 'boolean' },
},
additionalProperties: false,
},
load_config_file: { type: 'boolean' },
slug: {
type: 'object',
properties: {
encoding: { type: 'string', enum: ['unicode', 'ascii'] },
clean_accents: { type: 'boolean' },
sanitize_replacement: { type: 'string' },
},
additionalProperties: false,
},
i18n: i18nRoot,
local_backend: {
oneOf: [
{ type: 'boolean' },
{
type: 'object',
properties: {
url: { type: 'string', examples: ['http://localhost:8081/api/v1'] },
allowed_hosts: {
type: 'array',
items: { type: 'string' },
},
},
additionalProperties: false,
},
],
},
editor: { editor: {
type: 'object', type: 'object',
properties: { properties: {
preview: { type: 'boolean' }, preview: { type: 'boolean' },
frame: { type: 'boolean' },
}, },
additionalProperties: false,
}, },
search: { type: 'string' },
}, },
required: ['backend', 'collections'], required: ['backend', 'collections', 'media_folder'],
anyOf: [{ required: ['media_folder'] }, { required: ['media_library'] }], additionalProperties: false,
}; };
} }

View File

@ -121,8 +121,9 @@ export interface FieldsErrors {
[field: string]: FieldError[]; [field: string]: FieldError[];
} }
export type FieldGetValidValueMethod<T = unknown> = ( export type FieldGetValidValueMethod<T = unknown, F extends BaseField = UnknownField> = (
value: T | undefined | null, value: T | undefined | null,
field: F,
) => T | undefined | null; ) => T | undefined | null;
export type FieldGetDefaultMethod<T = unknown, F extends BaseField = UnknownField> = ( export type FieldGetDefaultMethod<T = unknown, F extends BaseField = UnknownField> = (
@ -363,7 +364,7 @@ export interface Widget<T = unknown, F extends BaseField = UnknownField> {
control: ComponentType<WidgetControlProps<T, F>>; control: ComponentType<WidgetControlProps<T, F>>;
preview?: WidgetPreviewComponent<T, F>; preview?: WidgetPreviewComponent<T, F>;
validator: FieldValidationMethod<T, F>; validator: FieldValidationMethod<T, F>;
getValidValue: FieldGetValidValueMethod<T>; getValidValue: FieldGetValidValueMethod<T, F>;
getDefaultValue?: FieldGetDefaultMethod<T, F>; getDefaultValue?: FieldGetDefaultMethod<T, F>;
schema?: PropertiesSchema<unknown>; schema?: PropertiesSchema<unknown>;
} }

View File

@ -34,7 +34,7 @@ export default function useBreadcrumbs(
const crumbs: Breadcrumb[] = [ const crumbs: Breadcrumb[] = [
{ {
name: collection.label, name: collection.label,
to: `/collections/${collection.name}`, to: `/collections/${collection.name}?noredirect`,
}, },
]; ];

View File

@ -83,7 +83,7 @@ export async function validate(
widget: Widget<any, any>, widget: Widget<any, any>,
t: t, t: t,
): Promise<FieldError[]> { ): Promise<FieldError[]> {
const validValue = widget.getValidValue(value); const validValue = widget.getValidValue(value, field);
const errors: FieldError[] = []; const errors: FieldError[] = [];
const validations: FieldValidationMethod<ValueOrNestedValue>[] = [ const validations: FieldValidationMethod<ValueOrNestedValue>[] = [
validatePresence, validatePresence,

View File

@ -8,6 +8,7 @@ import {
ENTRIES_SUCCESS, ENTRIES_SUCCESS,
ENTRY_DELETE_SUCCESS, ENTRY_DELETE_SUCCESS,
ENTRY_FAILURE, ENTRY_FAILURE,
ENTRY_PERSIST_SUCCESS,
ENTRY_REQUEST, ENTRY_REQUEST,
ENTRY_SUCCESS, ENTRY_SUCCESS,
FILTER_ENTRIES_FAILURE, FILTER_ENTRIES_FAILURE,
@ -551,6 +552,19 @@ function entries(
}; };
} }
case ENTRY_PERSIST_SUCCESS: {
const payload = action.payload;
const { collectionName } = payload;
const pages = { ...state.pages };
delete pages[collectionName];
return {
...state,
pages,
};
}
default: default:
return state; return state;
} }

View File

@ -1,33 +0,0 @@
declare module 'uploadcare-widget-tab-effects';
declare module 'uploadcare-widget' {
interface UploadcareFileGroupInfo {
cdnUrl: string;
name: string;
isImage: boolean;
}
const Uploadcare: {
loadFileGroup: (groupId: string | undefined) => {
done: (callback: (group: UploadcareFileGroupInfo) => void) => void;
};
fileFrom: (uploadedOrUrl: 'uploaded' | 'url', url: string) => Promise<UploadcareFileGroupInfo>;
registerTab: (tab: string, tabContent: unknown) => void;
openDialog: (
files:
| UploadcareFileGroupInfo
| UploadcareFileGroupInfo[]
| Promise<UploadcareFileGroupInfo | UploadcareFileGroupInfo[]>,
settings: Record<string, unknown>,
) => {
done: (
callback: (values: {
promise: () => Promise<UploadcareFileGroupInfo>;
files: () => Promise<UploadcareFileGroupInfo>[];
}) => void,
) => void;
};
};
export default Uploadcare;
}

View File

@ -279,7 +279,7 @@ const CodeControl: FC<WidgetControlProps<string | { [key: string]: string }, Cod
{field.hint} {field.hint}
</Hint> </Hint>
) : null} ) : null}
<ErrorMessage errors={errors} /> <ErrorMessage errors={errors} className="pt-2 pb-3" />
</div> </div>
</div> </div>
); );

View File

@ -11,6 +11,15 @@ const CodeWidget = (): WidgetParam<string | { [key: string]: string }, CodeField
previewComponent, previewComponent,
options: { options: {
schema, schema,
getValidValue: (value, field) => {
if (!value || typeof value === 'string') {
return value;
}
const codeKey = field.keys ? field.keys.code : 'code';
return value[codeKey];
},
getDefaultValue: ( getDefaultValue: (
defaultValue: string | { [key: string]: string } | null | undefined, defaultValue: string | { [key: string]: string } | null | undefined,
field: CodeField, field: CodeField,

View File

@ -1,14 +1,15 @@
export default { export default {
properties: { properties: {
default: {
oneOf: [{ type: 'string' }, { type: 'object' }],
},
default_language: { type: 'string' }, default_language: { type: 'string' },
allow_language_selection: { type: 'boolean' }, allow_language_selection: { type: 'boolean' },
output_code_only: { type: 'boolean' },
keys: { keys: {
type: 'object', type: 'object',
properties: { code: { type: 'string' }, lang: { type: 'string' } }, properties: { code: { type: 'string' }, lang: { type: 'string' } },
}, },
default: { output_code_only: { type: 'boolean' },
oneOf: [{ type: 'string' }, { type: 'object' }], code_mirror_config: { type: 'object' },
},
}, },
}; };

View File

@ -1,5 +1,7 @@
export default { export default {
properties: { properties: {
default: { type: 'string' }, default: { type: 'string' },
allow_input: { type: 'boolean' },
enable_alpha: { type: 'boolean' },
}, },
}; };

View File

@ -1,9 +1,9 @@
export default { export default {
properties: { properties: {
default: { type: 'string' },
format: { type: 'string' }, format: { type: 'string' },
date_format: { oneOf: [{ type: 'string' }, { type: 'boolean' }] }, date_format: { oneOf: [{ type: 'string' }, { type: 'boolean' }] },
time_format: { oneOf: [{ type: 'string' }, { type: 'boolean' }] }, time_format: { oneOf: [{ type: 'string' }, { type: 'boolean' }] },
picker_utc: { type: 'boolean' }, picker_utc: { type: 'boolean' },
default: { type: 'string' },
}, },
}; };

View File

@ -12,5 +12,17 @@ export default {
}, },
], ],
}, },
media_folder: { type: 'string' },
public_folder: { type: 'string' },
choose_url: { type: 'boolean' },
multiple: { type: 'boolean' },
media_library: {
type: 'object',
properties: {
max_file_size: { type: 'number' },
folder_support: { type: 'boolean' },
},
additionalProperties: false,
},
}, },
}; };

View File

@ -12,5 +12,17 @@ export default {
}, },
], ],
}, },
media_folder: { type: 'string' },
public_folder: { type: 'string' },
choose_url: { type: 'boolean' },
multiple: { type: 'boolean' },
media_library: {
type: 'object',
properties: {
max_file_size: { type: 'number' },
folder_support: { type: 'boolean' },
},
additionalProperties: false,
},
}, },
}; };

View File

@ -142,7 +142,7 @@ const ListFieldWrapper: FC<ListFieldWrapperProps> = ({
{hint} {hint}
</Hint> </Hint>
) : null} ) : null}
<ErrorMessage errors={errors} /> <ErrorMessage errors={errors} className="pb-3" />
</div> </div>
</div> </div>
); );

View File

@ -1,11 +1,27 @@
export default { export default {
properties: { properties: {
default: {
oneOf: [
{ type: 'boolean' },
{ type: 'string' },
{ type: 'number' },
{
type: 'array',
minItems: 1,
items: { oneOf: [{ type: 'boolean' }, { type: 'string' }, { type: 'number' }] },
},
],
},
allow_add: { type: 'boolean' }, allow_add: { type: 'boolean' },
collapsed: { type: 'boolean' }, collapsed: { type: 'boolean' },
summary: { type: 'string' }, summary: { type: 'string' },
label_singular: { type: 'string' }, label_singular: { type: 'string' },
i18n: { type: 'boolean' }, fields: { type: 'object' },
min: { type: 'number' },
max: { type: 'number' }, max: { type: 'number' },
min: { type: 'number' },
i18n: { type: 'boolean' },
add_to_top: { type: 'boolean' },
types: { type: 'object' },
type_key: { type: 'string' },
}, },
}; };

View File

@ -1,5 +1,17 @@
export default { export default {
properties: { properties: {
default: { type: 'string' }, default: { type: 'string' },
media_folder: { type: 'string' },
public_folder: { type: 'string' },
choose_url: { type: 'boolean' },
multiple: { type: 'boolean' },
media_library: {
type: 'object',
properties: {
max_file_size: { type: 'number' },
folder_support: { type: 'boolean' },
},
additionalProperties: false,
},
}, },
}; };

View File

@ -134,7 +134,7 @@ const ObjectFieldWrapper: FC<ObjectFieldWrapperProps> = ({
{hint} {hint}
</Hint> </Hint>
) : null} ) : null}
<ErrorMessage errors={errors} /> <ErrorMessage errors={errors} className="pl-4 pt-2 pb-3" />
</div> </div>
); );
}; };

View File

@ -1,6 +1,9 @@
export default { export default {
properties: { properties: {
default: { type: 'object' },
collapsed: { type: 'boolean' }, collapsed: { type: 'boolean' },
summary: { type: 'string' },
i18n: { type: 'boolean' }, i18n: { type: 'boolean' },
fields: { type: 'object' },
}, },
}; };

View File

@ -322,7 +322,7 @@ const RelationControl: FC<WidgetControlProps<string | string[], RelationField>>
label={ label={
<> <>
{Array.isArray(selectedValue) && selectedValue.length > 0 ? ( {Array.isArray(selectedValue) && selectedValue.length > 0 ? (
<div className="flex flex-wrap gap-0.5 w-full pr-4 p-2"> <div className="flex flex-wrap gap-2 w-full p-2 pr-0 max-w-fit">
{selectedValue.map(selectValue => { {selectedValue.map(selectValue => {
const option = uniqueOptionsByValue[selectValue]; const option = uniqueOptionsByValue[selectValue];
return ( return (

View File

@ -1,14 +1,5 @@
export default { export default {
properties: { properties: {
collection: { type: 'string' },
value_field: { type: 'string' },
search_fields: { type: 'array', minItems: 1, items: { type: 'string' } },
file: { type: 'string' },
multiple: { type: 'boolean' },
min: { type: 'integer' },
max: { type: 'integer' },
display_fields: { type: 'array', minItems: 1, items: { type: 'string' } },
options_length: { type: 'integer' },
default: { default: {
oneOf: [ oneOf: [
{ type: 'string' }, { type: 'string' },
@ -20,6 +11,15 @@ export default {
}, },
], ],
}, },
collection: { type: 'string' },
value_field: { type: 'string' },
search_fields: { type: 'array', minItems: 1, items: { type: 'string' } },
file: { type: 'string' },
display_fields: { type: 'array', minItems: 1, items: { type: 'string' } },
multiple: { type: 'boolean' },
min: { type: 'integer' },
max: { type: 'integer' },
options_length: { type: 'integer' },
}, },
oneOf: [ oneOf: [
{ {

View File

@ -126,7 +126,7 @@ const SelectControl: FC<WidgetControlProps<string | number | (string | number)[]
<Select <Select
label={ label={
Array.isArray(stringValue) ? ( Array.isArray(stringValue) ? (
<div className="flex wrap gap-0.5"> <div className="flex wrap gap-2 max-w-fit">
{stringValue.map(selectValue => { {stringValue.map(selectValue => {
const label = optionsByValue[selectValue]?.label ?? selectValue; const label = optionsByValue[selectValue]?.label ?? selectValue;
return ( return (

View File

@ -1,8 +1,5 @@
export default { export default {
properties: { properties: {
multiple: { type: 'boolean' },
min: { type: 'integer' },
max: { type: 'integer' },
default: { default: {
oneOf: [ oneOf: [
{ type: 'string' }, { type: 'string' },
@ -32,6 +29,9 @@ export default {
], ],
}, },
}, },
multiple: { type: 'boolean' },
min: { type: 'integer' },
max: { type: 'integer' },
}, },
required: ['options'], required: ['options'],
}; };

View File

@ -8470,7 +8470,7 @@ escape-goat@^3.0.0:
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c" resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c"
integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw== integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==
escape-html@1.0.3, escape-html@^1.0.3, escape-html@~1.0.3: escape-html@1.0.3, escape-html@~1.0.3:
version "1.0.3" version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
@ -11905,11 +11905,6 @@ jotai@^1.7.2:
resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.13.1.tgz#20cc46454cbb39096b12fddfa635b873b3668236" resolved "https://registry.yarnpkg.com/jotai/-/jotai-1.13.1.tgz#20cc46454cbb39096b12fddfa635b873b3668236"
integrity sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw== integrity sha512-RUmH1S4vLsG3V6fbGlKzGJnLrDcC/HNb5gH2AeA9DzuJknoVxSGvvg8OBB7lke+gDc4oXmdVsaKn/xDUhWZ0vw==
jquery@^3.6.0:
version "3.6.4"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.4.tgz#ba065c188142100be4833699852bf7c24dc0252f"
integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==
js-base64@3.7.5: js-base64@3.7.5:
version "3.7.5" version "3.7.5"
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca" resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca"
@ -18605,19 +18600,6 @@ update-browserslist-db@^1.0.10:
escalade "^3.1.1" escalade "^3.1.1"
picocolors "^1.0.0" picocolors "^1.0.0"
uploadcare-widget-tab-effects@1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/uploadcare-widget-tab-effects/-/uploadcare-widget-tab-effects-1.6.0.tgz#6de4664b0b2fa47100b5a3e0afc1d41b35b8f429"
integrity sha512-RoWxeZnk41qm8Mo19R8Zvshsuqetc4y+gFtxA89x+noLCSFItuviGZJBf7rcX3ocwn+aFNKbXp44dlaVDoYRIA==
uploadcare-widget@3.21.0:
version "3.21.0"
resolved "https://registry.yarnpkg.com/uploadcare-widget/-/uploadcare-widget-3.21.0.tgz#db67e7f97d257c3ab8da65328121502f8ab6b1f8"
integrity sha512-9C/WLwK3Anx+76rDzg/pe1vBv41j4HapgxfbyVLS+3oolB3bd9NvURAkvfyEif8QUnt7MwK2Ubw3f7UP/a3v9w==
dependencies:
escape-html "^1.0.3"
jquery "^3.6.0"
uri-js@^4.2.2: uri-js@^4.2.2:
version "4.4.1" version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"