diff --git a/packages/core/admin/admin/src/content-manager/components/InputJSON/FieldError.js b/packages/core/admin/admin/src/content-manager/components/InputJSON/FieldError.js
new file mode 100644
index 0000000000..f921d478e7
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/InputJSON/FieldError.js
@@ -0,0 +1,32 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from 'react-intl';
+import { P } from '@strapi/parts/Text';
+
+export const FieldError = ({ id, error, name }) => {
+ const { formatMessage } = useIntl();
+ const errorMessage = error ? formatMessage({ id: error, defaultMessage: error }) : '';
+
+ if (!error) {
+ return null;
+ }
+
+ return (
+
+ {errorMessage}
+
+ );
+};
+
+FieldError.defaultProps = {
+ id: undefined,
+ error: undefined,
+};
+
+FieldError.propTypes = {
+ error: PropTypes.string,
+ id: PropTypes.string,
+ name: PropTypes.string.isRequired,
+};
+
+export default FieldError;
diff --git a/packages/core/admin/admin/src/content-manager/components/InputJSON/Hint.js b/packages/core/admin/admin/src/content-manager/components/InputJSON/Hint.js
new file mode 100644
index 0000000000..693a3b1617
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/InputJSON/Hint.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from 'react-intl';
+import { P } from '@strapi/parts/Text';
+
+export const Hint = ({ id, error, name, description }) => {
+ const { formatMessage } = useIntl();
+ const hint = description
+ ? formatMessage(
+ { id: description.id, defaultMessage: description.defaultMessage },
+ { ...description.values }
+ )
+ : '';
+
+ if (!hint || error) {
+ return null;
+ }
+
+ return (
+
+ {hint}
+
+ );
+};
+
+Hint.defaultProps = {
+ id: undefined,
+ description: undefined,
+ error: undefined,
+};
+
+Hint.propTypes = {
+ description: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ values: PropTypes.object,
+ }),
+ error: PropTypes.string,
+ id: PropTypes.string,
+ name: PropTypes.string.isRequired,
+};
+
+export default Hint;
diff --git a/packages/core/admin/admin/src/content-manager/components/InputJSON/Label.js b/packages/core/admin/admin/src/content-manager/components/InputJSON/Label.js
new file mode 100644
index 0000000000..ca79857525
--- /dev/null
+++ b/packages/core/admin/admin/src/content-manager/components/InputJSON/Label.js
@@ -0,0 +1,51 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styled from 'styled-components';
+import { useIntl } from 'react-intl';
+import { Text } from '@strapi/parts/Text';
+import { Box } from '@strapi/parts/Box';
+import { Row } from '@strapi/parts/Row';
+
+const LabelAction = styled(Box)`
+ svg path {
+ fill: ${({ theme }) => theme.colors.neutral500};
+ }
+`;
+
+const Label = ({ id, intlLabel, labelAction, name }) => {
+ const { formatMessage } = useIntl();
+ const label = intlLabel?.id
+ ? formatMessage(
+ { id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
+ { ...intlLabel.values }
+ )
+ : name;
+
+ return (
+
+
+ {label}
+
+ {labelAction && {labelAction}}
+
+ );
+};
+
+Label.defaultProps = {
+ id: undefined,
+ intlLabel: undefined,
+ labelAction: undefined,
+};
+
+Label.propTypes = {
+ id: PropTypes.string,
+ intlLabel: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ values: PropTypes.object,
+ }),
+ labelAction: PropTypes.element,
+ name: PropTypes.string.isRequired,
+};
+
+export default Label;
diff --git a/packages/core/admin/admin/src/content-manager/components/InputJSON/components.js b/packages/core/admin/admin/src/content-manager/components/InputJSON/components.js
index 3bf43697e1..60e22d26f7 100644
--- a/packages/core/admin/admin/src/content-manager/components/InputJSON/components.js
+++ b/packages/core/admin/admin/src/content-manager/components/InputJSON/components.js
@@ -1,30 +1,314 @@
import styled from 'styled-components';
+import { Box } from '@strapi/parts/Box';
-const Wrapper = styled.div`
- position: relative;
- margin-bottom: 3px;
- line-height: 18px;
- cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'initial')};
-
- .CodeMirror {
- font-size: 13px !important;
- }
-
- > div {
- border-radius: 3px;
-
- > div:last-of-type {
- min-height: 315px;
- max-height: 635px;
- font-weight: 500;
- font-size: 1.3rem !important;
- }
- }
+/* eslint-disable */
+/* stylelint-disable */
+const EditorWrapper = styled.div`
+ cursor: ${({ disabled }) => (disabled ? 'not-allowed !important' : 'auto')};
+ /* BASICS */
.colored {
background-color: yellow;
color: black !important;
}
+
+ > div {
+ border-radius: 3px;
+ > div:last-of-type {
+ min-height: 253px;
+ max-height: 506px;
+ }
+ }
+
+ .CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-size: ${14 / 16}rem;
+ color: ${({ theme }) => theme.colors.neutral800};
+ direction: ltr;
+ }
+
+ /* PADDING */
+
+ .CodeMirror-scrollbar-filler,
+ .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+ }
+
+ /* GUTTER */
+
+ .CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+ }
+ .CodeMirror-linenumbers {
+ }
+ .CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ white-space: nowrap;
+ }
+
+ .CodeMirror-guttermarker {
+ color: black;
+ }
+ .CodeMirror-guttermarker-subtle {
+ color: #999;
+ }
+
+ /* CURSOR */
+
+ .CodeMirror-cursor {
+ border-left: 1px solid black;
+ border-right: none;
+ width: 0;
+ }
+ /* Shown when moving in bi-directional text */
+ .CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+ }
+ .cm-fat-cursor .CodeMirror-cursor {
+ width: auto;
+ border: 0 !important;
+ background: #7e7;
+ }
+ .cm-fat-cursor div.CodeMirror-cursors {
+ /* z-index: 1; */
+ }
+
+ .cm-fat-cursor-mark {
+ background-color: rgba(20, 255, 20, 0.5);
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ }
+ .cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ background-color: #7e7;
+ }
+
+ /* Can style cursor different in overwrite (non-insert) mode */
+ .CodeMirror-overwrite .CodeMirror-cursor {
+ }
+
+ .cm-tab {
+ display: inline-block;
+ text-decoration: inherit;
+ }
+
+ .CodeMirror-rulers {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: -50px;
+ bottom: 0;
+ overflow: hidden;
+ }
+ .CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ top: 0;
+ bottom: 0;
+ position: absolute;
+ }
+
+ /* DEFAULT THEME */
+
+ .CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+ }
+
+ .CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 50px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -50px;
+ margin-right: -50px;
+ padding-bottom: 50px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+ }
+ .CodeMirror-sizer {
+ position: relative;
+ border-right: 50px solid transparent;
+ }
+
+ /* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actual scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+ .CodeMirror-vscrollbar,
+ .CodeMirror-hscrollbar,
+ .CodeMirror-scrollbar-filler,
+ .CodeMirror-gutter-filler {
+ position: absolute;
+ /* z-index: 6; */
+ display: none;
+ outline: none;
+ }
+
+ .CodeMirror-vscrollbar {
+ right: 0;
+ top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+ }
+ .CodeMirror-hscrollbar {
+ bottom: 0;
+ left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+ }
+ .CodeMirror-scrollbar-filler {
+ right: 0;
+ bottom: 0;
+ }
+
+ .CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+ }
+ /* Reset some styles that the rest of the page might have set */
+ .CodeMirror pre.CodeMirror-line,
+ .CodeMirror pre.CodeMirror-line-like {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ /* z-index: 2; */
+ position: relative;
+ overflow: visible;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-font-variant-ligatures: contextual;
+ font-variant-ligatures: contextual;
+ }
+
+ .CodeMirror pre.CodeMirror-line-like {
+ z-index: 2;
+ }
+
+ .CodeMirror-wrap pre.CodeMirror-line,
+ .CodeMirror-wrap pre.CodeMirror-line-like {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+ }
+
+ .CodeMirror-linebackground {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ z-index: 0;
+ }
+
+ .CodeMirror-linewidget {
+ position: relative;
+ /* z-index: 2; */
+ padding: 0.1px; /* Force widget margins to stay inside of the container */
+ }
+
+ .CodeMirror-widget {
+ }
+
+ .CodeMirror-rtl pre {
+ direction: rtl;
+ }
+
+ .CodeMirror-code {
+ outline: none;
+ }
+
+ /* Force content-box sizing for the elements where we expect it */
+ .CodeMirror-scroll,
+ .CodeMirror-sizer,
+ .CodeMirror-gutter,
+ .CodeMirror-gutters,
+ .CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ }
+
+ .CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+ }
+
+ .CodeMirror-cursor {
+ position: absolute;
+ pointer-events: none;
+ }
+ .CodeMirror-measure pre {
+ position: static;
+ }
+
+ div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ + div {
+ z-index: 0 !important;
+ }
+ }
+
+ div.CodeMirror-dragcursors {
+ visibility: visible;
+ }
+
+ .CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+ }
+
+ .CodeMirror-selected {
+ background: ${({ theme }) => theme.colors.neutral200};
+ /* z-index: -10; */
+ }
+ .CodeMirror-crosshair {
+ cursor: crosshair;
+ }
+
+ /* Used to force a border model for a node */
+ .cm-force-border {
+ padding-right: 0.1px;
+ }
+
+ /* See issue #2901 */
+ .cm-tab-wrap-hack:after {
+ content: '';
+ }
+
+ /* Help users use markselection to safely style text background */
+ span.CodeMirror-selectedtext {
+ background: none;
+ }
+
+ .cm-s-solarized.CodeMirror {
+ box-shadow: none;
+ border-radius: ${({ theme }) => theme.borderRadius};
+ }
`;
-export default Wrapper;
+const StyledBox = styled(Box)`
+ border-radius: ${({ theme }) => theme.borderRadius};
+ border: 1px solid ${({ theme, error }) => (error ? theme.colors.danger600 : 'transparent')};
+`;
+
+export { EditorWrapper, StyledBox };
diff --git a/packages/core/admin/admin/src/content-manager/components/InputJSON/index.js b/packages/core/admin/admin/src/content-manager/components/InputJSON/index.js
index a3f849c2fd..b5053190fe 100644
--- a/packages/core/admin/admin/src/content-manager/components/InputJSON/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/InputJSON/index.js
@@ -7,10 +7,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import cm from 'codemirror';
-
-import { trimStart } from 'lodash';
+import trimStart from 'lodash/trimStart';
+import { Stack } from '@strapi/parts/Stack';
import jsonlint from './jsonlint';
-import Wrapper from './components';
+import { EditorWrapper, StyledBox } from './components';
+import Hint from './Hint';
+import Label from './Label';
+import FieldError from './FieldError';
const WAIT = 600;
const stringify = JSON.stringify;
@@ -60,7 +63,6 @@ class InputJSON extends React.Component {
fontSize: '13px',
});
this.codeMirror.on('change', this.handleChange);
- this.codeMirror.on('blur', this.handleBlur);
this.setSize();
this.setInitValue();
@@ -112,21 +114,6 @@ class InputJSON extends React.Component {
this.setState({ markedText });
};
- handleBlur = ({ target }) => {
- const { name, onBlur } = this.props;
-
- if (target === undefined) {
- // codemirror catches multiple events
- onBlur({
- target: {
- name,
- type: 'json',
- value: this.getValue(),
- },
- });
- }
- };
-
handleChange = (doc, change) => {
if (change.origin === 'setValue') {
return;
@@ -172,24 +159,61 @@ class InputJSON extends React.Component {
}
return (
-
-
-
+
+
+
+
+
+
+
+
+
+
);
}
}
InputJSON.defaultProps = {
+ description: null,
disabled: false,
- onBlur: () => {},
+ id: undefined,
+ error: undefined,
+ intlLabel: undefined,
+ labelAction: undefined,
onChange: () => {},
value: null,
};
InputJSON.propTypes = {
+ description: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ values: PropTypes.object,
+ }),
disabled: PropTypes.bool,
+ error: PropTypes.string,
+ id: PropTypes.string,
+ intlLabel: PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ defaultMessage: PropTypes.string.isRequired,
+ values: PropTypes.object,
+ }),
+ labelAction: PropTypes.element,
name: PropTypes.string.isRequired,
- onBlur: PropTypes.func,
onChange: PropTypes.func,
value: PropTypes.any,
};
diff --git a/packages/core/admin/admin/src/content-manager/components/Inputs/index.js b/packages/core/admin/admin/src/content-manager/components/Inputs/index.js
index 118ddaf0f5..18d663bee9 100644
--- a/packages/core/admin/admin/src/content-manager/components/Inputs/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/Inputs/index.js
@@ -10,7 +10,7 @@ import { useContentTypeLayout } from '../../hooks';
import { getFieldName } from '../../utils';
import Wysiwyg from '../Wysiwyg';
import GenericInput from './GenericInput';
-// import InputJSONWithErrors from '../InputJSONWithErrors';
+import InputJSON from '../InputJSON';
// import SelectWrapper from '../SelectWrapper';
// import WysiwygWithErrors from '../WysiwygWithErrors';
// import InputUID from '../InputUID';
@@ -31,7 +31,7 @@ function Inputs({
keys,
labelAction,
metadatas,
- onBlur,
+
onChange,
readableFields,
shouldNotRunValidations,
@@ -207,19 +207,15 @@ function Inputs({
// {...metadatas}
autoComplete="new-password"
intlLabel={{ id: label, defaultMessage: label }}
- // autoFocus={autoFocus}
description={description ? { id: description, defaultMessage: description } : null}
disabled={shouldDisableField}
error={errorId}
- // inputDescription={description}
labelAction={labelAction}
contentTypeUID={currentContentTypeLayout.uid}
customInputs={{
- // json: InputJSONWithErrors,
- // wysiwyg: WysiwygWithErrors,
// uid: InputUID,
// ...fields,
- json: () => TODO json
,
+ json: InputJSON,
media: () => TODO media
,
uid: () => TODO uid
,
// wysiwyg: () => TODO wysiwyg
,
@@ -228,7 +224,6 @@ function Inputs({
multiple={fieldSchema.multiple || false}
attribute={fieldSchema}
name={keys}
- onBlur={onBlur}
onChange={onChange}
options={options}
placeholder={placeholder ? { id: placeholder, defaultMessage: placeholder } : null}
@@ -244,7 +239,6 @@ function Inputs({
Inputs.defaultProps = {
formErrors: {},
labelAction: undefined,
- onBlur: null,
queryInfos: {},
value: null,
};
@@ -257,7 +251,6 @@ Inputs.propTypes = {
isCreatingEntry: PropTypes.bool.isRequired,
labelAction: PropTypes.element,
metadatas: PropTypes.object.isRequired,
- onBlur: PropTypes.func,
onChange: PropTypes.func.isRequired,
readableFields: PropTypes.array.isRequired,
shouldNotRunValidations: PropTypes.bool.isRequired,
diff --git a/packages/core/admin/admin/src/content-manager/components/Wysiwyg/index.js b/packages/core/admin/admin/src/content-manager/components/Wysiwyg/index.js
index 351aea13c7..e41dd788c4 100644
--- a/packages/core/admin/admin/src/content-manager/components/Wysiwyg/index.js
+++ b/packages/core/admin/admin/src/content-manager/components/Wysiwyg/index.js
@@ -125,7 +125,7 @@ const Wysiwyg = ({
return (
<>
-
+
{label}
{labelAction && {labelAction}}