implements readOnly on more fields, fixes bugs with relationship
This commit is contained in:
@@ -50,7 +50,7 @@ const Checkbox = (props) => {
|
||||
baseClass,
|
||||
showError && 'error',
|
||||
value && `${baseClass}--checked`,
|
||||
readOnly && 'read-only',
|
||||
readOnly && `${baseClass}--read-only`,
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
return (
|
||||
@@ -74,7 +74,7 @@ const Checkbox = (props) => {
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
onClick={readOnly ? undefined : () => {
|
||||
setValue(!value);
|
||||
if (typeof onChange === 'function') onChange(!value);
|
||||
}}
|
||||
|
||||
@@ -55,4 +55,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--read-only {
|
||||
&__input {
|
||||
background-color: lighten($color-gray, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,17 +61,27 @@
|
||||
}
|
||||
|
||||
.radio-group--read-only {
|
||||
.radio-input__label {
|
||||
color: $color-gray;
|
||||
}
|
||||
.radio-input {
|
||||
&__label {
|
||||
color: $color-gray;
|
||||
}
|
||||
|
||||
.radio-input--is-selected {
|
||||
.radio-input {
|
||||
&__styled-radio {
|
||||
&--is-selected {
|
||||
.radio-input__styled-radio {
|
||||
&:before {
|
||||
background-color: $color-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(&--is-selected) {
|
||||
&:hover {
|
||||
.radio-input__styled-radio {
|
||||
&:before {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class Relationship extends Component {
|
||||
lastLoadedPage: 1,
|
||||
options: [],
|
||||
errorLoading: false,
|
||||
loadedIDs: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,11 +42,15 @@ class Relationship extends Component {
|
||||
this.getNextOptions();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { search } = this.state;
|
||||
componentDidUpdate(_, prevState) {
|
||||
const { search, options } = this.state;
|
||||
if (search !== prevState.search) {
|
||||
this.getNextOptions({ clear: true });
|
||||
}
|
||||
|
||||
if (options !== prevState.options) {
|
||||
this.ensureValueHasOption();
|
||||
}
|
||||
}
|
||||
|
||||
getNextOptions = (params = {}) => {
|
||||
@@ -55,6 +60,8 @@ class Relationship extends Component {
|
||||
if (clear) {
|
||||
this.setState({
|
||||
options: [],
|
||||
loadedIDs: [],
|
||||
lastFullyLoadedRelation: -1,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,7 +70,7 @@ class Relationship extends Component {
|
||||
relations, lastFullyLoadedRelation, lastLoadedPage, search,
|
||||
} = this.state;
|
||||
|
||||
const relationsToSearch = relations.slice(lastFullyLoadedRelation + 1);
|
||||
const relationsToSearch = lastFullyLoadedRelation === -1 ? relations : relations.slice(lastFullyLoadedRelation + 1);
|
||||
|
||||
if (relationsToSearch.length > 0) {
|
||||
some(relationsToSearch, async (relation, callback) => {
|
||||
@@ -97,6 +104,9 @@ class Relationship extends Component {
|
||||
if (nextPage) {
|
||||
const { data, relation } = nextPage;
|
||||
this.addOptions(data, relation);
|
||||
this.setState({
|
||||
lastLoadedPage: lastLoadedPage + 1,
|
||||
});
|
||||
} else {
|
||||
const { data, relation } = lastPage;
|
||||
this.addOptions(data, relation);
|
||||
@@ -154,61 +164,116 @@ class Relationship extends Component {
|
||||
|
||||
addOptions = (data, relation) => {
|
||||
const { hasMultipleRelations } = this.props;
|
||||
const { lastLoadedPage, options } = this.state;
|
||||
const { options, loadedIDs } = this.state;
|
||||
const collection = collections.find((coll) => coll.slug === relation);
|
||||
|
||||
if (!hasMultipleRelations) {
|
||||
this.setState({
|
||||
options: [
|
||||
...options,
|
||||
...data.docs.map((doc) => ({
|
||||
label: doc[collection?.admin?.useAsTitle || 'id'],
|
||||
value: doc.id,
|
||||
})),
|
||||
],
|
||||
});
|
||||
} else {
|
||||
const allOptionGroups = [...options];
|
||||
const optionsToAddTo = allOptionGroups.find((optionGroup) => optionGroup.label === collection.labels.plural);
|
||||
const newlyLoadedIDs = [];
|
||||
|
||||
const newOptions = data.docs.map((doc) => ({
|
||||
label: doc[collection?.admin?.useAsTitle || 'id'],
|
||||
value: {
|
||||
relationTo: collection.slug,
|
||||
value: doc.id,
|
||||
},
|
||||
}));
|
||||
let newOptions = [];
|
||||
|
||||
if (!hasMultipleRelations) {
|
||||
newOptions = [
|
||||
...options,
|
||||
...data.docs.reduce((docs, doc) => {
|
||||
if (loadedIDs.indexOf(doc.id) === -1) {
|
||||
newlyLoadedIDs.push(doc.id);
|
||||
|
||||
return [
|
||||
...docs,
|
||||
{
|
||||
label: doc[collection?.admin?.useAsTitle || 'id'],
|
||||
value: doc.id,
|
||||
},
|
||||
];
|
||||
}
|
||||
return docs;
|
||||
}, []),
|
||||
];
|
||||
} else {
|
||||
newOptions = [...options];
|
||||
const optionsToAddTo = newOptions.find((optionGroup) => optionGroup.label === collection.labels.plural);
|
||||
|
||||
const newSubOptions = data.docs.reduce((docs, doc) => {
|
||||
if (loadedIDs.indexOf(doc.id) === -1) {
|
||||
newlyLoadedIDs.push(doc.id);
|
||||
|
||||
return [
|
||||
...docs,
|
||||
{
|
||||
label: doc[collection?.admin?.useAsTitle || 'id'],
|
||||
value: {
|
||||
relationTo: collection.slug,
|
||||
value: doc.id,
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return docs;
|
||||
}, []);
|
||||
|
||||
if (optionsToAddTo) {
|
||||
optionsToAddTo.options = [
|
||||
...optionsToAddTo.options,
|
||||
...newOptions,
|
||||
...newSubOptions,
|
||||
];
|
||||
} else {
|
||||
allOptionGroups.push({
|
||||
newOptions.push({
|
||||
label: collection.labels.plural,
|
||||
options: newOptions,
|
||||
options: newSubOptions,
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
options: [
|
||||
...allOptionGroups,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
lastLoadedPage: lastLoadedPage + 1,
|
||||
options: newOptions,
|
||||
loadedIDs: [
|
||||
...loadedIDs,
|
||||
...newlyLoadedIDs,
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
ensureValueHasOption = async () => {
|
||||
const { relationTo, hasMany, value } = this.props;
|
||||
const { options } = this.state;
|
||||
const locatedValue = this.findValueInOptions(options, value);
|
||||
|
||||
const hasMultipleRelations = Array.isArray(relationTo);
|
||||
|
||||
if (!locatedValue && value) {
|
||||
if (hasMany) {
|
||||
value.forEach((val) => {
|
||||
if (hasMultipleRelations) {
|
||||
this.addOptionByID(val.value, val.relationTo);
|
||||
} else {
|
||||
this.addOptionByID(val, relationTo);
|
||||
}
|
||||
});
|
||||
} else if (hasMultipleRelations) {
|
||||
this.addOptionByID(value.value, value.relationTo);
|
||||
} else {
|
||||
this.addOptionByID(value, relationTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addOptionByID = async (id, relation) => {
|
||||
const response = await fetch(`${serverURL}${api}/${relation}/${id}`);
|
||||
const data = await response.json();
|
||||
this.addOptions({ docs: [data] }, relation);
|
||||
}
|
||||
|
||||
handleInputChange = (search) => {
|
||||
this.setState({
|
||||
search,
|
||||
lastFullyLoadedRelation: -1,
|
||||
lastLoadedPage: 1,
|
||||
});
|
||||
const { search: existingSearch } = this.state;
|
||||
|
||||
if (search !== existingSearch) {
|
||||
this.setState({
|
||||
search,
|
||||
lastFullyLoadedRelation: -1,
|
||||
lastLoadedPage: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleMenuScrollToBottom = () => {
|
||||
@@ -240,10 +305,10 @@ class Relationship extends Component {
|
||||
baseClass,
|
||||
showError && 'error',
|
||||
errorLoading && 'error-loading',
|
||||
readOnly && 'read-only',
|
||||
readOnly && `${baseClass}--read-only`,
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
const valueToRender = this.findValueInOptions(options, value);
|
||||
const valueToRender = this.findValueInOptions(options, value) || value;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -264,6 +329,7 @@ class Relationship extends Component {
|
||||
/>
|
||||
{!errorLoading && (
|
||||
<ReactSelect
|
||||
isDisabled={readOnly}
|
||||
onInputChange={this.handleInputChange}
|
||||
onChange={!readOnly ? setValue : undefined}
|
||||
formatValue={this.formatSelectedValue}
|
||||
|
||||
@@ -11,3 +11,11 @@
|
||||
background-color: $color-red;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.relationship--read-only {
|
||||
div.react-select {
|
||||
div.rs__control {
|
||||
background: lighten($color-light-gray, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import { select } from '../../../../../fields/validations';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'select';
|
||||
|
||||
const findFullOption = (value, options) => {
|
||||
const matchedOption = options.find((option) => option?.value === value);
|
||||
|
||||
@@ -97,9 +99,9 @@ const Select = (props) => {
|
||||
|
||||
const classes = [
|
||||
'field-type',
|
||||
'select',
|
||||
baseClass,
|
||||
showError && 'error',
|
||||
readOnly && 'read-only',
|
||||
readOnly && `${baseClass}--read-only`,
|
||||
].filter(Boolean).join(' ');
|
||||
|
||||
const valueToRender = formatRenderValue(value, options);
|
||||
@@ -126,7 +128,7 @@ const Select = (props) => {
|
||||
value={valueToRender}
|
||||
formatValue={formatFormValue}
|
||||
showError={showError}
|
||||
disabled={readOnly}
|
||||
isDisabled={readOnly}
|
||||
options={options}
|
||||
isMulti={hasMany}
|
||||
/>
|
||||
|
||||
@@ -3,3 +3,11 @@
|
||||
.field-type.select {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.select--read-only {
|
||||
div.react-select {
|
||||
div.rs__control {
|
||||
background: lighten($color-light-gray, 5%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
background: $color-light-gray;
|
||||
color: $color-gray;
|
||||
background: lighten($color-light-gray, 5%);
|
||||
color: darken($color-gray, 5%);
|
||||
|
||||
&:hover {
|
||||
border-color: $color-light-gray;
|
||||
|
||||
Reference in New Issue
Block a user