mirror of
https://github.com/strapi/strapi.git
synced 2025-09-21 14:31:16 +00:00
commit
1cb6275a16
@ -115,30 +115,6 @@
|
||||
"component": "blog.test-como",
|
||||
"required": false,
|
||||
"min": 2
|
||||
},
|
||||
"temp_one_to_one": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::temp.temp",
|
||||
"inversedBy": "one_to_one"
|
||||
},
|
||||
"temp": {
|
||||
"type": "relation",
|
||||
"relation": "manyToOne",
|
||||
"target": "api::temp.temp",
|
||||
"inversedBy": "one_to_many"
|
||||
},
|
||||
"temps": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "api::temp.temp",
|
||||
"mappedBy": "many_to_one"
|
||||
},
|
||||
"many_to_many_temp": {
|
||||
"type": "relation",
|
||||
"relation": "manyToMany",
|
||||
"target": "api::temp.temp",
|
||||
"inversedBy": "many_to_many"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@
|
||||
"description": ""
|
||||
},
|
||||
"options": {
|
||||
"draftAndPublish": true
|
||||
"draftAndPublish": false
|
||||
},
|
||||
"pluginOptions": {},
|
||||
"attributes": {
|
||||
@ -17,7 +17,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"long": {
|
||||
"type": "string"
|
||||
"type": "text"
|
||||
},
|
||||
"email": {
|
||||
"type": "email"
|
||||
@ -28,7 +28,7 @@
|
||||
"password": {
|
||||
"type": "password"
|
||||
},
|
||||
"number": {
|
||||
"number_int": {
|
||||
"type": "integer"
|
||||
},
|
||||
"enum": {
|
||||
@ -77,39 +77,33 @@
|
||||
"uid": {
|
||||
"type": "uid"
|
||||
},
|
||||
"one_way": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::address.address"
|
||||
"number_big": {
|
||||
"type": "biginteger"
|
||||
},
|
||||
"one_to_one": {
|
||||
"type": "relation",
|
||||
"relation": "oneToOne",
|
||||
"target": "api::address.address",
|
||||
"inversedBy": "temp_one_to_one"
|
||||
"number_dec": {
|
||||
"type": "decimal"
|
||||
},
|
||||
"one_to_many": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "api::address.address",
|
||||
"mappedBy": "temp"
|
||||
"number_float": {
|
||||
"type": "float"
|
||||
},
|
||||
"many_to_one": {
|
||||
"type": "relation",
|
||||
"relation": "manyToOne",
|
||||
"target": "api::address.address",
|
||||
"inversedBy": "temps"
|
||||
"enum_req_def": {
|
||||
"type": "enumeration",
|
||||
"enum": [
|
||||
"un",
|
||||
"deux",
|
||||
"trois"
|
||||
],
|
||||
"required": true,
|
||||
"default": "un"
|
||||
},
|
||||
"many_to_many": {
|
||||
"type": "relation",
|
||||
"relation": "manyToMany",
|
||||
"target": "api::address.address",
|
||||
"inversedBy": "many_to_many_temp"
|
||||
},
|
||||
"many_way": {
|
||||
"type": "relation",
|
||||
"relation": "oneToMany",
|
||||
"target": "api::address.address"
|
||||
"enum_req": {
|
||||
"type": "enumeration",
|
||||
"enum": [
|
||||
"un",
|
||||
"deux",
|
||||
"trois"
|
||||
],
|
||||
"required": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ const EditViewDataManagerProvider = ({
|
||||
const errorsInForm = Object.keys(formErrors);
|
||||
|
||||
// TODO check if working with DZ, components...
|
||||
// TODO use querySelector querySelectorAll('[data-strapi-field-error]')
|
||||
if (errorsInForm.length > 0) {
|
||||
const firstError = errorsInForm[0];
|
||||
const el = document.getElementById(firstError);
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { get, isArray, isObject } from 'lodash';
|
||||
import get from 'lodash/get';
|
||||
import isArray from 'lodash/isArray';
|
||||
import isObject from 'lodash/isObject';
|
||||
|
||||
/* eslint-disable indent */
|
||||
|
||||
@ -23,13 +25,23 @@ const cleanData = (retrievedData, currentSchema, componentsSchema) => {
|
||||
}
|
||||
|
||||
break;
|
||||
case 'date':
|
||||
cleanedData =
|
||||
value && value._isAMomentObject === true ? value.format('YYYY-MM-DD') : value;
|
||||
break;
|
||||
case 'datetime':
|
||||
cleanedData = value && value._isAMomentObject === true ? value.toISOString() : value;
|
||||
// TODO
|
||||
// case 'date':
|
||||
// cleanedData =
|
||||
// value && value._isAMomentObject === true ? value.format('YYYY-MM-DD') : value;
|
||||
// break;
|
||||
// case 'datetime':
|
||||
// cleanedData = value && value._isAMomentObject === true ? value.toISOString() : value;
|
||||
// break;
|
||||
case 'time': {
|
||||
cleanedData = value;
|
||||
|
||||
if (value && value.split(':').length < 3) {
|
||||
cleanedData = `${value}:00`;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'media':
|
||||
if (getOtherInfos(schema, [current, 'multiple']) === true) {
|
||||
cleanedData = value ? value.filter(file => !(file instanceof File)) : null;
|
||||
|
@ -1,16 +1,26 @@
|
||||
/**
|
||||
*
|
||||
* Input
|
||||
* GenericInput
|
||||
* This is a temp file move it to the helper plugin when ready
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { ToggleInput } from '@strapi/parts/ToggleInput';
|
||||
import { TextInput } from '@strapi/parts/TextInput';
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useIntl } from 'react-intl';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { formatISO } from 'date-fns';
|
||||
import { DatePicker } from '@strapi/parts/DatePicker';
|
||||
import { NumberInput } from '@strapi/parts/NumberInput';
|
||||
import { Select, Option } from '@strapi/parts/Select';
|
||||
import { Textarea } from '@strapi/parts/Textarea';
|
||||
import { TextInput } from '@strapi/parts/TextInput';
|
||||
import { TimePicker } from '@strapi/parts/TimePicker';
|
||||
import { ToggleInput } from '@strapi/parts/ToggleInput';
|
||||
import Hide from '@strapi/icons/Hide';
|
||||
import Show from '@strapi/icons/Show';
|
||||
|
||||
const Input = ({
|
||||
const GenericInput = ({
|
||||
autoComplete,
|
||||
customInputs,
|
||||
description,
|
||||
disabled,
|
||||
@ -19,12 +29,15 @@ const Input = ({
|
||||
error,
|
||||
name,
|
||||
onChange,
|
||||
options,
|
||||
placeholder,
|
||||
step,
|
||||
type,
|
||||
value,
|
||||
...rest
|
||||
}) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
|
||||
const CustomInput = customInputs ? customInputs[type] : null;
|
||||
|
||||
@ -46,10 +59,12 @@ const Input = ({
|
||||
);
|
||||
}
|
||||
|
||||
const label = formatMessage(
|
||||
{ id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
|
||||
{ ...intlLabel.values }
|
||||
);
|
||||
const label = intlLabel.id
|
||||
? formatMessage(
|
||||
{ id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
|
||||
{ ...intlLabel.values }
|
||||
)
|
||||
: name;
|
||||
|
||||
const hint = description
|
||||
? formatMessage(
|
||||
@ -58,30 +73,6 @@ const Input = ({
|
||||
)
|
||||
: '';
|
||||
|
||||
if (type === 'bool') {
|
||||
return (
|
||||
<ToggleInput
|
||||
checked={value || false}
|
||||
disabled={disabled}
|
||||
hint={hint}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
name={name}
|
||||
offLabel={formatMessage({
|
||||
id: 'app.components.ToggleCheckbox.off-label',
|
||||
defaultMessage: 'Off',
|
||||
})}
|
||||
onLabel={formatMessage({
|
||||
id: 'app.components.ToggleCheckbox.on-label',
|
||||
defaultMessage: 'On',
|
||||
})}
|
||||
onChange={e => {
|
||||
onChange({ target: { name, value: e.target.checked } });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const formattedPlaceholder = placeholder
|
||||
? formatMessage(
|
||||
{ id: placeholder.id, defaultMessage: placeholder.defaultMessage },
|
||||
@ -91,34 +82,228 @@ const Input = ({
|
||||
|
||||
const errorMessage = error ? formatMessage({ id: error, defaultMessage: error }) : '';
|
||||
|
||||
return (
|
||||
<TextInput
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
placeholder={formattedPlaceholder}
|
||||
type={type}
|
||||
value={value || ''}
|
||||
/>
|
||||
);
|
||||
switch (type) {
|
||||
case 'bool': {
|
||||
return (
|
||||
<ToggleInput
|
||||
checked={value || false}
|
||||
disabled={disabled}
|
||||
hint={hint}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
name={name}
|
||||
offLabel={formatMessage({
|
||||
id: 'app.components.ToggleCheckbox.off-label',
|
||||
defaultMessage: 'Off',
|
||||
})}
|
||||
onLabel={formatMessage({
|
||||
id: 'app.components.ToggleCheckbox.on-label',
|
||||
defaultMessage: 'On',
|
||||
})}
|
||||
onChange={e => {
|
||||
onChange({ target: { name, value: e.target.checked } });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'date': {
|
||||
return (
|
||||
<DatePicker
|
||||
clearLabel={formatMessage({ id: 'clearLabel', defaultMessage: 'Clear' })}
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={date => {
|
||||
const formattedDate = formatISO(cloneDeep(date), { representation: 'date' });
|
||||
|
||||
onChange({ target: { name, value: formattedDate, type } });
|
||||
}}
|
||||
onClear={() => onChange({ target: { name, value: '', type } })}
|
||||
placeholder={formattedPlaceholder}
|
||||
selectedDate={value ? new Date(value) : null}
|
||||
selectedDateLabel={formattedDate => `Date picker, current is ${formattedDate}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'number': {
|
||||
return (
|
||||
<NumberInput
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onValueChange={value => {
|
||||
onChange({ target: { name, value, type } });
|
||||
}}
|
||||
placeholder={formattedPlaceholder}
|
||||
step={step}
|
||||
value={value || undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'email':
|
||||
case 'text':
|
||||
case 'string': {
|
||||
return (
|
||||
<TextInput
|
||||
autoComplete={autoComplete}
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
placeholder={formattedPlaceholder}
|
||||
type={type}
|
||||
value={value || ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'password': {
|
||||
return (
|
||||
<TextInput
|
||||
autoComplete={autoComplete}
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
endAction={
|
||||
<button
|
||||
aria-label={formatMessage({
|
||||
id: 'Auth.form.password.show-password',
|
||||
defaultMessage: 'Show password',
|
||||
})}
|
||||
onClick={() => {
|
||||
setShowPassword(prev => !prev);
|
||||
}}
|
||||
style={{
|
||||
border: 'none',
|
||||
padding: 0,
|
||||
background: 'transparent',
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{showPassword ? <Show /> : <Hide />}
|
||||
</button>
|
||||
}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
placeholder={formattedPlaceholder}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={value || ''}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'select': {
|
||||
return (
|
||||
<Select
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={value => {
|
||||
onChange({ target: { name, value: value === '' ? null : value, type: 'select' } });
|
||||
}}
|
||||
placeholder={formattedPlaceholder}
|
||||
value={value || ''}
|
||||
>
|
||||
{options.map(({ metadatas: { intlLabel, disabled, hidden }, key, value }) => {
|
||||
return (
|
||||
<Option key={key} value={value} disabled={disabled} hidden={hidden}>
|
||||
{formatMessage(intlLabel)}
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
case 'textarea': {
|
||||
return (
|
||||
<Textarea
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={onChange}
|
||||
placeholder={formattedPlaceholder}
|
||||
type={type}
|
||||
value={value || ''}
|
||||
>
|
||||
{value}
|
||||
</Textarea>
|
||||
);
|
||||
}
|
||||
case 'time': {
|
||||
let time = value;
|
||||
|
||||
// The backend send a value which has the following format: '00:45:00.000'
|
||||
// or the time picker only supports hours & minutes so we need to mutate the value
|
||||
if (value && value.split(':').length > 2) {
|
||||
time = time.split(':');
|
||||
time.pop();
|
||||
time = time.join(':');
|
||||
}
|
||||
|
||||
return (
|
||||
<TimePicker
|
||||
clearLabel={formatMessage({ id: 'clearLabel', defaultMessage: 'Clear' })}
|
||||
disabled={disabled}
|
||||
error={errorMessage}
|
||||
label={label}
|
||||
labelAction={labelAction}
|
||||
id={name}
|
||||
hint={hint}
|
||||
name={name}
|
||||
onChange={time => {
|
||||
onChange({ target: { name, value: `${time}`, type } });
|
||||
}}
|
||||
onClear={() => {
|
||||
onChange({ target: { name, value: null, type } });
|
||||
}}
|
||||
placeholder={formattedPlaceholder}
|
||||
step={step}
|
||||
value={time}
|
||||
/>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return <div>{type} is not supported</div>;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Input.defaultProps = {
|
||||
GenericInput.defaultProps = {
|
||||
autoComplete: undefined,
|
||||
customInputs: null,
|
||||
description: null,
|
||||
disabled: false,
|
||||
error: '',
|
||||
labelAction: undefined,
|
||||
placeholder: null,
|
||||
options: [],
|
||||
step: 1,
|
||||
value: '',
|
||||
};
|
||||
|
||||
Input.propTypes = {
|
||||
GenericInput.propTypes = {
|
||||
autoComplete: PropTypes.string,
|
||||
customInputs: PropTypes.object,
|
||||
description: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
@ -135,13 +320,28 @@ Input.propTypes = {
|
||||
labelAction: PropTypes.element,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
options: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
metadatas: PropTypes.shape({
|
||||
intlLabel: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
hidden: PropTypes.bool,
|
||||
}).isRequired,
|
||||
key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||
}).isRequired
|
||||
),
|
||||
placeholder: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
step: PropTypes.number,
|
||||
type: PropTypes.string.isRequired,
|
||||
value: PropTypes.any,
|
||||
};
|
||||
|
||||
export default Input;
|
||||
export default GenericInput;
|
||||
|
@ -166,7 +166,7 @@ function Inputs({
|
||||
isRequired,
|
||||
]);
|
||||
|
||||
const { label, description, visible } = metadatas;
|
||||
const { label, description, placeholder, visible } = metadatas;
|
||||
|
||||
if (visible === false) {
|
||||
return null;
|
||||
@ -218,8 +218,10 @@ function Inputs({
|
||||
// wysiwyg: WysiwygWithErrors,
|
||||
// uid: InputUID,
|
||||
// ...fields,
|
||||
json: () => <div>TODO json</div>,
|
||||
media: () => <div>TODO media</div>,
|
||||
uid: () => <div>TODO uid</div>,
|
||||
wysiwyg: () => <div>TODO wysiwyg</div>,
|
||||
}}
|
||||
multiple={fieldSchema.multiple || false}
|
||||
attribute={fieldSchema}
|
||||
@ -227,9 +229,10 @@ function Inputs({
|
||||
onBlur={onBlur}
|
||||
onChange={onChange}
|
||||
options={options}
|
||||
placeholder={placeholder ? { id: placeholder, defaultMessage: placeholder } : null}
|
||||
step={step}
|
||||
type={inputType}
|
||||
validations={validations}
|
||||
// validations={validations}
|
||||
value={inputValue}
|
||||
withDefaultValue={false}
|
||||
/>
|
||||
|
@ -1,19 +1,32 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const generateOptions = (options, isRequired = false) => [
|
||||
<FormattedMessage id="components.InputSelect.option.placeholder" key="__enum_option_null">
|
||||
{msg => (
|
||||
<option disabled={isRequired} hidden={isRequired} value="">
|
||||
{msg}
|
||||
</option>
|
||||
)}
|
||||
</FormattedMessage>,
|
||||
...options.map(v => (
|
||||
<option key={v} value={v}>
|
||||
{v}
|
||||
</option>
|
||||
)),
|
||||
];
|
||||
const generateOptions = (options, isRequired = false) => {
|
||||
return [
|
||||
{
|
||||
metadatas: {
|
||||
intlLabel: {
|
||||
id: 'components.InputSelect.option.placeholder',
|
||||
defaultMessage: 'Choose here',
|
||||
},
|
||||
disabled: isRequired,
|
||||
hidden: isRequired,
|
||||
},
|
||||
key: '__enum_option_null',
|
||||
value: '',
|
||||
},
|
||||
...options.map(option => {
|
||||
return {
|
||||
metadatas: {
|
||||
intlLabel: {
|
||||
id: option,
|
||||
defaultMessage: option,
|
||||
},
|
||||
hidden: false,
|
||||
disabled: false,
|
||||
},
|
||||
key: option,
|
||||
value: option,
|
||||
};
|
||||
}),
|
||||
];
|
||||
};
|
||||
|
||||
export default generateOptions;
|
||||
|
@ -156,7 +156,7 @@ const EditView = ({
|
||||
{grid.map(
|
||||
({ fieldSchema, labelAction, metadatas, name, size }) => {
|
||||
return (
|
||||
<GridItem col={size} key={name}>
|
||||
<GridItem col={size} key={name} s={12} xs={12}>
|
||||
<Inputs
|
||||
fieldSchema={fieldSchema}
|
||||
keys={name}
|
||||
|
@ -7,7 +7,7 @@ import { TimePicker } from '@strapi/parts/TimePicker';
|
||||
import { Select, Option } from '@strapi/parts/Select';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { formatISO } from 'date-fns';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
const Inputs = ({ label, onChange, options, type, value }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
|
@ -110,6 +110,7 @@ const CMEditViewLocalePicker = ({
|
||||
<Option
|
||||
value={value.value}
|
||||
disabled
|
||||
hidden
|
||||
startIcon={hasDraftAndPublishEnabled ? <Bullet status={currentLocaleStatus} /> : null}
|
||||
>
|
||||
{value.label}
|
||||
|
Loading…
x
Reference in New Issue
Block a user