291 lines
8.7 KiB
JavaScript
Raw Normal View History

2018-07-24 23:35:59 -04:00
import React from 'react';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { Async as AsyncSelect } from 'react-select';
import { find, isEmpty, last, debounce, get, uniqBy, sortBy } from 'lodash';
import { List, Map, fromJS } from 'immutable';
import { reactSelectStyles } from 'netlify-cms-ui-default';
import { stringTemplate } from 'netlify-cms-lib-widgets';
import { FixedSizeList } from 'react-window';
const Option = ({ index, style, data }) => <div style={style}>{data.options[index]}</div>;
const MenuList = props => {
if (props.isLoading || props.options.length <= 0 || !Array.isArray(props.children)) {
return props.children;
}
const rows = props.children;
const itemSize = 30;
return (
<FixedSizeList
style={{ width: '100%' }}
width={300}
height={Math.min(300, rows.length * itemSize + itemSize / 3)}
itemCount={rows.length}
itemSize={itemSize}
itemData={{ options: rows }}
>
{Option}
</FixedSizeList>
);
};
function optionToString(option) {
return option && option.value ? option.value : '';
}
2018-07-25 05:34:19 -04:00
function convertToOption(raw) {
if (typeof raw === 'string') {
return { label: raw, value: raw };
2018-07-25 05:34:19 -04:00
}
return Map.isMap(raw) ? raw.toJS() : raw;
}
2018-07-25 05:34:19 -04:00
function getSelectedOptions(value) {
const selectedOptions = List.isList(value) ? value.toJS() : value;
if (!selectedOptions || !Array.isArray(selectedOptions)) {
return null;
}
return selectedOptions;
}
function uniqOptions(initial, current) {
return uniqBy(initial.concat(current), o => o.value);
}
function getSelectedValue({ value, options, isMultiple }) {
if (isMultiple) {
const selectedOptions = getSelectedOptions(value);
if (selectedOptions === null) {
return null;
}
2018-07-25 05:34:19 -04:00
const selected = selectedOptions
.map(i => options.find(o => o.value === (i.value || i)))
.filter(Boolean)
.map(convertToOption);
return selected;
} else {
return find(options, ['value', value]) || null;
2018-07-25 05:34:19 -04:00
}
}
2018-07-24 23:35:59 -04:00
export default class RelationControl extends React.Component {
didInitialSearch = false;
mounted = false;
state = {
initialOptions: [],
};
static propTypes = {
onChange: PropTypes.func.isRequired,
forID: PropTypes.string.isRequired,
value: PropTypes.node,
field: ImmutablePropTypes.map,
fetchID: PropTypes.string,
query: PropTypes.func.isRequired,
queryHits: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
WIP - Global UI (#785) * update top bar and collections sidebar UI * update collection entries UI * improve global layout * merge search page into collection page * enable new entry button * search fixup * wip -initial editor update * update editor scrolling and markdown toolbar position * wip * editor toolbar progress * editor toolbar wip * finished basic editor toolbar * add standalone toggle component * improve markdown toolbar spacing * add user avatar placeholder * finish markdown toggle styling * refactor icon setup, add new icons * add new icons to markdown editor toolbar * remove extra app container * add markdown active mark style * relation and text widget styling * widget design updates, basic list/object design update * widget style updates, image widget improvements * refactor widget directory, fix file removal * widget focus styles * finish editor widget focus styles * migrate media library modal to react-modal * wip - migrate editor component form to modal * wip - move editor component form to modal * wip - embed plugin forms in the editor * inline shortcode forms working * disable react hot loading, its breaking things * improve shortcode form styles * make shortcode form collapsible, improve styling * add close functionality to shortcode blocks * improve base media library styling * fix shortcode label * migrate unstyled workflow to new UI * wip - reorganizing everything * more work moving everything * finish more moving and eliminating stuff * restructure, remove react-toolbox * wip - removing old stuff, more restructure * finish restructure * wip - css arch * switch back to test repo * update react-datetime to ^2.11.0 * remove leftover react-toolbox button * more restructuring clean-up * fix UI component directory case * wip -css editor control style * wip - consolidate widget styles * wip - use a single control renderer * fixed object values breaking * wip - editor control active styles * pass control wrapper to widgets * ensure branch name is trimmed * wip - improve widget authoring support * import Map to Widget component * refactor toolbar buttons * wip - more widget active styles * break out editor toggle component * add local scroll sync back * update editor toggle icons * limit editor control pane content width * fix editor control spacing * migrate markdown toolbar stickiness to css * fix markdown toolbar border radius * temporarily use test backend * stop markdown toolbar from going to bottom * restore disabled markdown toolbar buttons for raw * test markdown widget without focus styles * more widget updates * remove card visuals from editor * disable dragging editor split off screen * use editorControl component for shortcode fields * make header site link configurable * add configurable collection descriptions * temporarily add example assets * add basic list view * remove outdated css mixins * add and implement search icon * activate quick add menu * visualize usable space in editor view * fix entry close, other improvements * wip - editorial workflow updates * some dropshadow and other CSS tweaks * workflow ui updates * add worfklow card buttons * fix workflow card button handlers * some dropshadow and other CSS tweaks * make workflow board wider * center workflow and collection views * add basic responsiveness * a bunch of fun UI fixes! a BUNCH! (#875) * give `.nc-entryEditor-toolbar-mainSection` left and right child divs * a bunch of fun UI fixes! a BUNCH! * remove obscure --buttonShadow * revert to test repo * fix not found page styling * allow workflow publishing from any column * disallow publishing from all columns, with feedback * fix new entry button * fix markdown state persisting across entries * enable simple workflow save and new from editor * update slug in address bar when saving new entry * wip - workflow updates, deletion working * add status change functionality to editor * wip - improving status change from editor * editor toolbar back button improvements, loading improvements, cleanup * progress on the media library UI cleanup * remove font smothing css * a quick fix for these buttons * tweaks * progress on media library modal— broken FYI * fix media library functionality, finish migrating footer * remove media library footer files * remove leftover css import * fix media library * editor publishing functionality complete (unstyled) * remove leftover loader var from media library * wip - editor publishing styles * add status dropdown styling * editor toolbar style updates * editor toolbar state improvements * progress on the media library UI cleanup, style improvements * finish editorial workflow editor styling * finish media library styling * fix config * add what-input to optimize focus styling * fix button * fix navigation blocking for simple workflow * improve simple workflow publishing * add avatar dropdown to editor top bar * style github and test-repo auth pages * add git gateway auth page styles * improve editor error styling
2017-12-07 12:37:10 -05:00
classNameWrapper: PropTypes.string.isRequired,
setActiveStyle: PropTypes.func.isRequired,
setInactiveStyle: PropTypes.func.isRequired,
};
shouldComponentUpdate(nextProps) {
return (
this.props.value !== nextProps.value ||
this.props.hasActiveStyle !== nextProps.hasActiveStyle ||
this.props.queryHits !== nextProps.queryHits
);
}
async componentDidMount() {
this.mounted = true;
// if the field has a previous value perform an initial search based on the value field
// this is required since each search is limited by optionsLength so the selected value
// might not show up on the search
const { forID, field, value, query } = this.props;
const collection = field.get('collection');
const file = field.get('file');
const initialSearchValues = this.isMultiple() ? getSelectedOptions(value) : [value];
if (initialSearchValues && initialSearchValues.length > 0) {
const allOptions = await Promise.all(
initialSearchValues.map((v, index) => {
return query(forID, collection, [field.get('valueField')], v, file, 1).then(
({ payload }) => {
const hits = payload.response?.hits || [];
const options = this.parseHitOptions(hits);
return { options, index };
},
);
}),
);
const initialOptions = [].concat(
...sortBy(allOptions, ({ index }) => index).map(({ options }) => options),
);
this.mounted && this.setState({ initialOptions });
}
}
componentWillUnmount() {
this.mounted = false;
}
componentDidUpdate(prevProps) {
/**
* Load extra post data into the store after first query.
*/
if (this.didInitialSearch) return;
const { value, field, forID, queryHits, onChange } = this.props;
if (queryHits !== prevProps.queryHits && queryHits.get(forID)) {
this.didInitialSearch = true;
const valueField = field.get('valueField');
const hits = queryHits.get(forID);
if (value) {
const listValue = List.isList(value) ? value : List([value]);
listValue.forEach(val => {
const hit = hits.find(hit => this.parseNestedFields(hit, valueField) === val);
if (hit) {
onChange(value, {
[field.get('name')]: {
[field.get('collection')]: { [val]: hit.data },
},
});
}
});
}
}
}
handleChange = selectedOption => {
const { onChange, field } = this.props;
let value;
let metadata;
if (Array.isArray(selectedOption)) {
this.setState({ initialOptions: selectedOption });
value = selectedOption.map(optionToString);
metadata =
(!isEmpty(selectedOption) && {
[field.get('name')]: {
[field.get('collection')]: {
[last(value)]: last(selectedOption).data,
},
},
}) ||
{};
onChange(fromJS(value), metadata);
} else {
this.setState({ initialOptions: [selectedOption] });
value = optionToString(selectedOption);
metadata = selectedOption && {
[field.get('name')]: {
[field.get('collection')]: { [value]: selectedOption.data },
},
};
onChange(value, metadata);
}
};
parseNestedFields = (hit, field) => {
const templateVars = stringTemplate.extractTemplateVars(field);
// return non template fields as is
if (templateVars.length <= 0) {
return get(hit.data, field);
}
const data = stringTemplate.addFileTemplateFields(hit.path, fromJS(hit.data));
const value = stringTemplate.compileStringTemplate(field, null, hit.slug, data);
return value;
};
isMultiple() {
return this.props.field.get('multiple', false);
}
parseHitOptions = hits => {
const { field } = this.props;
const valueField = field.get('valueField');
const displayField = field.get('displayFields') || List([field.get('valueField')]);
const options = hits.reduce((acc, hit) => {
const valuesPaths = stringTemplate.expandPath({ data: hit.data, path: valueField });
for (let i = 0; i < valuesPaths.length; i++) {
const label = displayField
.toJS()
.map(key => {
const displayPaths = stringTemplate.expandPath({ data: hit.data, path: key });
return this.parseNestedFields(hit, displayPaths[i] || displayPaths[0]);
})
.join(' ');
const value = this.parseNestedFields(hit, valuesPaths[i]);
acc.push({ data: hit.data, value, label });
}
return acc;
}, []);
return options;
};
loadOptions = debounce((term, callback) => {
const { field, query, forID } = this.props;
const collection = field.get('collection');
const searchFields = field.get('searchFields');
const optionsLength = field.get('optionsLength') || 20;
const searchFieldsArray = List.isList(searchFields) ? searchFields.toJS() : [searchFields];
const file = field.get('file');
query(forID, collection, searchFieldsArray, term, file, optionsLength).then(({ payload }) => {
const hits = payload.response?.hits || [];
const options = this.parseHitOptions(hits);
const uniq = uniqOptions(this.state.initialOptions, options);
callback(uniq);
});
}, 500);
render() {
WIP - Global UI (#785) * update top bar and collections sidebar UI * update collection entries UI * improve global layout * merge search page into collection page * enable new entry button * search fixup * wip -initial editor update * update editor scrolling and markdown toolbar position * wip * editor toolbar progress * editor toolbar wip * finished basic editor toolbar * add standalone toggle component * improve markdown toolbar spacing * add user avatar placeholder * finish markdown toggle styling * refactor icon setup, add new icons * add new icons to markdown editor toolbar * remove extra app container * add markdown active mark style * relation and text widget styling * widget design updates, basic list/object design update * widget style updates, image widget improvements * refactor widget directory, fix file removal * widget focus styles * finish editor widget focus styles * migrate media library modal to react-modal * wip - migrate editor component form to modal * wip - move editor component form to modal * wip - embed plugin forms in the editor * inline shortcode forms working * disable react hot loading, its breaking things * improve shortcode form styles * make shortcode form collapsible, improve styling * add close functionality to shortcode blocks * improve base media library styling * fix shortcode label * migrate unstyled workflow to new UI * wip - reorganizing everything * more work moving everything * finish more moving and eliminating stuff * restructure, remove react-toolbox * wip - removing old stuff, more restructure * finish restructure * wip - css arch * switch back to test repo * update react-datetime to ^2.11.0 * remove leftover react-toolbox button * more restructuring clean-up * fix UI component directory case * wip -css editor control style * wip - consolidate widget styles * wip - use a single control renderer * fixed object values breaking * wip - editor control active styles * pass control wrapper to widgets * ensure branch name is trimmed * wip - improve widget authoring support * import Map to Widget component * refactor toolbar buttons * wip - more widget active styles * break out editor toggle component * add local scroll sync back * update editor toggle icons * limit editor control pane content width * fix editor control spacing * migrate markdown toolbar stickiness to css * fix markdown toolbar border radius * temporarily use test backend * stop markdown toolbar from going to bottom * restore disabled markdown toolbar buttons for raw * test markdown widget without focus styles * more widget updates * remove card visuals from editor * disable dragging editor split off screen * use editorControl component for shortcode fields * make header site link configurable * add configurable collection descriptions * temporarily add example assets * add basic list view * remove outdated css mixins * add and implement search icon * activate quick add menu * visualize usable space in editor view * fix entry close, other improvements * wip - editorial workflow updates * some dropshadow and other CSS tweaks * workflow ui updates * add worfklow card buttons * fix workflow card button handlers * some dropshadow and other CSS tweaks * make workflow board wider * center workflow and collection views * add basic responsiveness * a bunch of fun UI fixes! a BUNCH! (#875) * give `.nc-entryEditor-toolbar-mainSection` left and right child divs * a bunch of fun UI fixes! a BUNCH! * remove obscure --buttonShadow * revert to test repo * fix not found page styling * allow workflow publishing from any column * disallow publishing from all columns, with feedback * fix new entry button * fix markdown state persisting across entries * enable simple workflow save and new from editor * update slug in address bar when saving new entry * wip - workflow updates, deletion working * add status change functionality to editor * wip - improving status change from editor * editor toolbar back button improvements, loading improvements, cleanup * progress on the media library UI cleanup * remove font smothing css * a quick fix for these buttons * tweaks * progress on media library modal— broken FYI * fix media library functionality, finish migrating footer * remove media library footer files * remove leftover css import * fix media library * editor publishing functionality complete (unstyled) * remove leftover loader var from media library * wip - editor publishing styles * add status dropdown styling * editor toolbar style updates * editor toolbar state improvements * progress on the media library UI cleanup, style improvements * finish editorial workflow editor styling * finish media library styling * fix config * add what-input to optimize focus styling * fix button * fix navigation blocking for simple workflow * improve simple workflow publishing * add avatar dropdown to editor top bar * style github and test-repo auth pages * add git gateway auth page styles * improve editor error styling
2017-12-07 12:37:10 -05:00
const {
value,
field,
WIP - Global UI (#785) * update top bar and collections sidebar UI * update collection entries UI * improve global layout * merge search page into collection page * enable new entry button * search fixup * wip -initial editor update * update editor scrolling and markdown toolbar position * wip * editor toolbar progress * editor toolbar wip * finished basic editor toolbar * add standalone toggle component * improve markdown toolbar spacing * add user avatar placeholder * finish markdown toggle styling * refactor icon setup, add new icons * add new icons to markdown editor toolbar * remove extra app container * add markdown active mark style * relation and text widget styling * widget design updates, basic list/object design update * widget style updates, image widget improvements * refactor widget directory, fix file removal * widget focus styles * finish editor widget focus styles * migrate media library modal to react-modal * wip - migrate editor component form to modal * wip - move editor component form to modal * wip - embed plugin forms in the editor * inline shortcode forms working * disable react hot loading, its breaking things * improve shortcode form styles * make shortcode form collapsible, improve styling * add close functionality to shortcode blocks * improve base media library styling * fix shortcode label * migrate unstyled workflow to new UI * wip - reorganizing everything * more work moving everything * finish more moving and eliminating stuff * restructure, remove react-toolbox * wip - removing old stuff, more restructure * finish restructure * wip - css arch * switch back to test repo * update react-datetime to ^2.11.0 * remove leftover react-toolbox button * more restructuring clean-up * fix UI component directory case * wip -css editor control style * wip - consolidate widget styles * wip - use a single control renderer * fixed object values breaking * wip - editor control active styles * pass control wrapper to widgets * ensure branch name is trimmed * wip - improve widget authoring support * import Map to Widget component * refactor toolbar buttons * wip - more widget active styles * break out editor toggle component * add local scroll sync back * update editor toggle icons * limit editor control pane content width * fix editor control spacing * migrate markdown toolbar stickiness to css * fix markdown toolbar border radius * temporarily use test backend * stop markdown toolbar from going to bottom * restore disabled markdown toolbar buttons for raw * test markdown widget without focus styles * more widget updates * remove card visuals from editor * disable dragging editor split off screen * use editorControl component for shortcode fields * make header site link configurable * add configurable collection descriptions * temporarily add example assets * add basic list view * remove outdated css mixins * add and implement search icon * activate quick add menu * visualize usable space in editor view * fix entry close, other improvements * wip - editorial workflow updates * some dropshadow and other CSS tweaks * workflow ui updates * add worfklow card buttons * fix workflow card button handlers * some dropshadow and other CSS tweaks * make workflow board wider * center workflow and collection views * add basic responsiveness * a bunch of fun UI fixes! a BUNCH! (#875) * give `.nc-entryEditor-toolbar-mainSection` left and right child divs * a bunch of fun UI fixes! a BUNCH! * remove obscure --buttonShadow * revert to test repo * fix not found page styling * allow workflow publishing from any column * disallow publishing from all columns, with feedback * fix new entry button * fix markdown state persisting across entries * enable simple workflow save and new from editor * update slug in address bar when saving new entry * wip - workflow updates, deletion working * add status change functionality to editor * wip - improving status change from editor * editor toolbar back button improvements, loading improvements, cleanup * progress on the media library UI cleanup * remove font smothing css * a quick fix for these buttons * tweaks * progress on media library modal— broken FYI * fix media library functionality, finish migrating footer * remove media library footer files * remove leftover css import * fix media library * editor publishing functionality complete (unstyled) * remove leftover loader var from media library * wip - editor publishing styles * add status dropdown styling * editor toolbar style updates * editor toolbar state improvements * progress on the media library UI cleanup, style improvements * finish editorial workflow editor styling * finish media library styling * fix config * add what-input to optimize focus styling * fix button * fix navigation blocking for simple workflow * improve simple workflow publishing * add avatar dropdown to editor top bar * style github and test-repo auth pages * add git gateway auth page styles * improve editor error styling
2017-12-07 12:37:10 -05:00
forID,
classNameWrapper,
setActiveStyle,
setInactiveStyle,
queryHits,
WIP - Global UI (#785) * update top bar and collections sidebar UI * update collection entries UI * improve global layout * merge search page into collection page * enable new entry button * search fixup * wip -initial editor update * update editor scrolling and markdown toolbar position * wip * editor toolbar progress * editor toolbar wip * finished basic editor toolbar * add standalone toggle component * improve markdown toolbar spacing * add user avatar placeholder * finish markdown toggle styling * refactor icon setup, add new icons * add new icons to markdown editor toolbar * remove extra app container * add markdown active mark style * relation and text widget styling * widget design updates, basic list/object design update * widget style updates, image widget improvements * refactor widget directory, fix file removal * widget focus styles * finish editor widget focus styles * migrate media library modal to react-modal * wip - migrate editor component form to modal * wip - move editor component form to modal * wip - embed plugin forms in the editor * inline shortcode forms working * disable react hot loading, its breaking things * improve shortcode form styles * make shortcode form collapsible, improve styling * add close functionality to shortcode blocks * improve base media library styling * fix shortcode label * migrate unstyled workflow to new UI * wip - reorganizing everything * more work moving everything * finish more moving and eliminating stuff * restructure, remove react-toolbox * wip - removing old stuff, more restructure * finish restructure * wip - css arch * switch back to test repo * update react-datetime to ^2.11.0 * remove leftover react-toolbox button * more restructuring clean-up * fix UI component directory case * wip -css editor control style * wip - consolidate widget styles * wip - use a single control renderer * fixed object values breaking * wip - editor control active styles * pass control wrapper to widgets * ensure branch name is trimmed * wip - improve widget authoring support * import Map to Widget component * refactor toolbar buttons * wip - more widget active styles * break out editor toggle component * add local scroll sync back * update editor toggle icons * limit editor control pane content width * fix editor control spacing * migrate markdown toolbar stickiness to css * fix markdown toolbar border radius * temporarily use test backend * stop markdown toolbar from going to bottom * restore disabled markdown toolbar buttons for raw * test markdown widget without focus styles * more widget updates * remove card visuals from editor * disable dragging editor split off screen * use editorControl component for shortcode fields * make header site link configurable * add configurable collection descriptions * temporarily add example assets * add basic list view * remove outdated css mixins * add and implement search icon * activate quick add menu * visualize usable space in editor view * fix entry close, other improvements * wip - editorial workflow updates * some dropshadow and other CSS tweaks * workflow ui updates * add worfklow card buttons * fix workflow card button handlers * some dropshadow and other CSS tweaks * make workflow board wider * center workflow and collection views * add basic responsiveness * a bunch of fun UI fixes! a BUNCH! (#875) * give `.nc-entryEditor-toolbar-mainSection` left and right child divs * a bunch of fun UI fixes! a BUNCH! * remove obscure --buttonShadow * revert to test repo * fix not found page styling * allow workflow publishing from any column * disallow publishing from all columns, with feedback * fix new entry button * fix markdown state persisting across entries * enable simple workflow save and new from editor * update slug in address bar when saving new entry * wip - workflow updates, deletion working * add status change functionality to editor * wip - improving status change from editor * editor toolbar back button improvements, loading improvements, cleanup * progress on the media library UI cleanup * remove font smothing css * a quick fix for these buttons * tweaks * progress on media library modal— broken FYI * fix media library functionality, finish migrating footer * remove media library footer files * remove leftover css import * fix media library * editor publishing functionality complete (unstyled) * remove leftover loader var from media library * wip - editor publishing styles * add status dropdown styling * editor toolbar style updates * editor toolbar state improvements * progress on the media library UI cleanup, style improvements * finish editorial workflow editor styling * finish media library styling * fix config * add what-input to optimize focus styling * fix button * fix navigation blocking for simple workflow * improve simple workflow publishing * add avatar dropdown to editor top bar * style github and test-repo auth pages * add git gateway auth page styles * improve editor error styling
2017-12-07 12:37:10 -05:00
} = this.props;
const isMultiple = this.isMultiple();
const isClearable = !field.get('required', true) || isMultiple;
const hits = queryHits.get(forID, []);
const queryOptions = this.parseHitOptions(hits);
const options = uniqOptions(this.state.initialOptions, queryOptions);
const selectedValue = getSelectedValue({
options,
value,
isMultiple,
});
return (
<AsyncSelect
components={{ MenuList }}
value={selectedValue}
inputId={forID}
cacheOptions
defaultOptions
loadOptions={this.loadOptions}
onChange={this.handleChange}
className={classNameWrapper}
onFocus={setActiveStyle}
onBlur={setInactiveStyle}
styles={reactSelectStyles}
isMulti={isMultiple}
isClearable={isClearable}
placeholder=""
/>
);
}
}