Fix request tasks for selects

This commit is contained in:
soupette 2019-07-18 14:07:22 +02:00
parent 771b8ab9b5
commit c74cbd9126
16 changed files with 22 additions and 1063 deletions

View File

@ -39,7 +39,7 @@ function InputDate(props) {
'form-control',
styles.inputDate,
!props.deactivateErrorHighlight && props.error && 'is-invalid',
!isEmpty(props.className) && props.className,
!isEmpty(props.className) && props.className
),
disabled: props.disabled,
id: props.name,

View File

@ -59,7 +59,7 @@ function Row({ goTo, isBulkable, row, headers }) {
return (
<>
{isBulkable && (
<td key="i">
<td key="i" onClick={e => e.stopPropagation()}>
<CustomInputCheckbox
name={row.id}
onChange={onChangeBulk}

View File

@ -37,12 +37,11 @@ function Input({ type, ...rest }) {
const style = { width: '210px', paddingTop: '4px' };
const styles =
type === 'boolean' ? { minWidth: '100px', maxWidth: '200px' } : style;
const wrapperStyle = type == 'boolean' ? { marginRight: '20px' } : {};
return (
<InputWrapperDate type={type || 'text'} style={wrapperStyle}>
<Component {...rest} style={styles} />
<Component {...rest} style={styles} autoComplete="off" />
</InputWrapperDate>
);
}

View File

@ -13,13 +13,7 @@ import 'codemirror/addon/lint/javascript-lint';
import 'codemirror/addon/edit/closebrackets';
import 'codemirror/addon/selection/mark-selection';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/liquibyte.css';
import 'codemirror/theme/xq-dark.css';
import 'codemirror/theme/3024-day.css';
import 'codemirror/theme/3024-night.css';
import 'codemirror/theme/blackboard.css';
import 'codemirror/theme/monokai.css';
import 'codemirror/theme/cobalt.css';
import { isEmpty, isObject, trimStart } from 'lodash';
import jsonlint from './jsonlint';
@ -28,16 +22,7 @@ import styles from './styles.scss';
const WAIT = 600;
const stringify = JSON.stringify;
const parse = JSON.parse;
const DEFAULT_THEME = 'monokai';
const THEMES = [
'blackboard',
'cobalt',
'monokai',
'3024-day',
'3024-night',
'liquibyte',
'xq-dark',
];
const DEFAULT_THEME = '3024-night';
class InputJSON extends React.Component {
constructor(props) {
@ -94,8 +79,6 @@ class InputJSON extends React.Component {
setSize = () => this.codeMirror.setSize('100%', 'auto');
setTheme = theme => this.codeMirror.setOption('theme', theme);
getContentAtLine = line => this.codeMirror.getLine(line);
getEditorOption = opt => this.codeMirror.getOption(opt);
@ -104,7 +87,6 @@ class InputJSON extends React.Component {
markSelection = ({ message }) => {
let line = parseInt(message.split(':')[0].split('line ')[1], 10) - 1;
let content = this.getContentAtLine(line);
if (content === '{') {
@ -196,17 +178,6 @@ class InputJSON extends React.Component {
id={this.props.name}
defaultValue=""
/>
<select
className={styles.select}
onChange={({ target }) => this.setTheme(target.value)}
defaultValue={DEFAULT_THEME}
>
{THEMES.sort().map(theme => (
<option key={theme} value={theme}>
{theme}
</option>
))}
</select>
</div>
);
}

View File

@ -49,6 +49,7 @@ function ListItem({
}, [preview]);
const opacity = isDragging ? 0.2 : 1;
return (
<Li ref={node => drag(drop(node))} style={{ opacity }}>
<Relation mainField={mainField} onRemove={onRemove} data={data} to={to} />

View File

@ -1,151 +0,0 @@
/**
*
* SortableItem
*
*/
/* eslint-disable react/no-find-dom-node */
import React from 'react';
import { findDOMNode } from 'react-dom';
import { DragSource, DropTarget } from 'react-dnd';
import { getEmptyImage } from 'react-dnd-html5-backend';
import PropTypes from 'prop-types';
import { flow, get } from 'lodash';
import cn from 'classnames';
import ItemTypes from '../../utils/ItemTypes';
import SelectManyDraggedItem from '../SelectManyDraggedItem';
import styles from './styles.scss';
const sortableItemSource = {
beginDrag: props => {
return {
id: get(props, ['item', 'value', 'id' ]) || get(props, ['item', 'value', '_id'], ''),
index: props.index,
data: props.item,
};
},
endDrag: props => {
props.moveAttrEnd();
return {};
},
};
const sortableItemTarget = {
hover: (props, monitor, component) => {
const dragIndex = monitor.getItem().index;
const hoverIndex = props.index;
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return;
}
// Determine rectangle on screen
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect();
// Get vertical middle
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
// Determine mouse position
const clientOffset = monitor.getClientOffset();
// Get pixels to the top
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
// Only perform the move when the mouse has crossed half of the items height
// When dragging downwards, only move when the cursor is below 50%
// When dragging upwards, only move when the cursor is above 50%
// Dragging downwards
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
// Dragging upwards
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
// Time to actually perform the action
props.moveAttr(dragIndex, hoverIndex, props.keys);
// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
monitor.getItem().index = hoverIndex;
},
};
class SortableItem extends React.Component {
componentDidMount() {
// Use empty image as a drag preview so browsers don't draw it
// and we can draw whatever we want on the custom drag layer instead.
this.props.connectDragPreview(getEmptyImage(), {
// IE fallback: specify that we'd rather screenshot the node
// when it already knows it's being dragged so we can hide it with CSS.
// Removginv the fallabck makes it handle variable height elements
// captureDraggingState: true,
});
}
render() {
const {
connectDragSource,
connectDropTarget,
index,
item,
isDragging,
isDraggingSibling,
onClick,
onRemove,
} = this.props;
const opacity = isDragging ? 0.2 : 1;
return (
connectDragSource(
connectDropTarget(
<li
className={cn(styles.sortableListItem, !isDraggingSibling && styles.sortableListItemHover)}
style={{ opacity }}
>
<SelectManyDraggedItem index={index} item={item} onClick={onClick} onRemove={onRemove} />
</li>
),
)
);
}
}
const withDropTarget = DropTarget(ItemTypes.SORTABLEITEM, sortableItemTarget, connect => ({
connectDropTarget: connect.dropTarget(),
}));
const withDragSource = DragSource(ItemTypes.SORTABLEITEM, sortableItemSource, (connect, monitor) => ({
connectDragPreview: connect.dragPreview(),
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}));
SortableItem.defaultProps = {
isDraggingSibling: false,
};
SortableItem.propTypes = {
connectDragPreview: PropTypes.func.isRequired,
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
isDraggingSibling: PropTypes.bool,
item: PropTypes.object.isRequired,
onClick: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
};
export default flow([withDropTarget, withDragSource])(SortableItem);

View File

@ -1,49 +0,0 @@
/**
*
* SortableList
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import cn from 'classnames';
// import { SortableContainer } from 'react-sortable-hoc';
import SortableItem from './SortableItem';
// CSS.
import styles from './styles.scss';
const SortableList = ({ items, isDraggingSibling, keys, moveAttr, moveAttrEnd, name, onClick, onRemove }) => {
return (
<div className={cn(styles.sortableList)}>
<ul id={`sortableListOf${name}`}>
{items.map((item, index) => (
<SortableItem
isDraggingSibling={isDraggingSibling}
key={item.value.id || item.value._id || `item-${index}`}
keys={keys}
index={index}
item={item}
moveAttr={moveAttr}
moveAttrEnd={moveAttrEnd}
onRemove={onRemove}
onClick={onClick}
/>
))}
</ul>
{items.length > 4 && <div className={styles.sortableListLong} />}
</div>
);
};
SortableList.propTypes = {
isDraggingSibling: PropTypes.bool.isRequired,
items: PropTypes.array.isRequired,
keys: PropTypes.string.isRequired,
moveAttr: PropTypes.func.isRequired,
moveAttrEnd: PropTypes.func.isRequired,
name: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired,
};
export default SortableList;

View File

@ -1,267 +0,0 @@
/**
*
* SelectMany
*
*/
/* eslint-disable */
import React from 'react';
import Select from 'react-select';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import {
cloneDeep,
includes,
isArray,
isNull,
isUndefined,
get,
findIndex,
isEmpty,
} from 'lodash';
// Utils.
import { request, templateObject } from 'strapi-helper-plugin';
// Component.
import SortableList from './SortableList';
// CSS.
import styles from './styles.scss';
class SelectMany extends React.PureComponent {
state = {
isLoading: true,
options: [],
start: 0,
};
componentDidMount() {
this.getOptions('');
}
componentDidUpdate(prevProps, prevState) {
if (isEmpty(prevProps.record) && !isEmpty(this.props.record)) {
const values = (
get(this.props.record, this.props.relation.alias) || []
).map(el => el.id || el._id);
const options = this.state.options.filter(el => {
return !values.includes(el.value.id || el.value._id);
});
this.state.options = options;
}
if (prevState.start !== this.state.start) {
this.getOptions('');
}
}
getOptions = query => {
const params = {
_limit: 20,
_start: this.state.start,
source: this.props.relation.plugin || 'content-manager',
};
// Set `query` parameter if necessary
if (query) {
delete params._limit;
delete params._skip;
params[`${this.props.relation.displayedAttribute}_contains`] = query;
}
// Request URL
const requestUrl = `/content-manager/explorer/${this.props.relation.model ||
this.props.relation.collection}`;
// Call our request helper (see 'utils/request')
return request(requestUrl, {
method: 'GET',
params,
})
.then(response => {
/* eslint-disable indent */
const options = isArray(response)
? response.map(item => ({
value: item,
label: templateObject(
{ mainField: this.props.relation.displayedAttribute },
item
).mainField,
}))
: [
{
value: response,
label: response[this.props.relation.displayedAttribute],
},
];
/* eslint-enable indent */
const newOptions = cloneDeep(this.state.options);
options.map(option => {
// Don't add the values when searching
if (
findIndex(newOptions, o => o.value.id === option.value.id) === -1
) {
return newOptions.push(option);
}
});
return this.setState({
options: newOptions,
isLoading: false,
});
})
.catch(() => {
strapi.notification.error(
'content-manager.notification.error.relationship.fetch'
);
});
};
handleInputChange = value => {
const clonedOptions = this.state.options;
const filteredValues = clonedOptions.filter(data =>
includes(data.label, value)
);
if (filteredValues.length === 0) {
return this.getOptions(value);
}
};
handleChange = value => {
// Remove new added value from available option;
this.state.options = this.state.options.filter(
el =>
!((el.value._id || el.value.id) === (value.value.id || value.value._id))
);
this.props.onAddRelationalItem({
key: this.props.relation.alias,
value: value.value,
});
};
handleBottomScroll = () => {
this.setState(prevState => {
return {
start: prevState.start + 1,
};
});
};
handleRemove = index => {
const values = get(this.props.record, this.props.relation.alias);
// Add removed value from available option;
const toAdd = {
value: values[index],
label: templateObject(
{ mainField: this.props.relation.displayedAttribute },
values[index]
).mainField,
};
this.setState(prevState => ({
options: prevState.options.concat([toAdd]),
}));
this.props.onRemoveRelationItem({
key: this.props.relation.alias,
index,
});
};
// Redirect to the edit page
handleClick = (item = {}) => {
this.props.onRedirect({
model: this.props.relation.collection || this.props.relation.model,
id: item.value.id || item.value._id,
source: this.props.relation.plugin,
});
};
render() {
const description = this.props.relation.description ? (
<p>{this.props.relation.description}</p>
) : (
''
);
const value = get(this.props.record, this.props.relation.alias) || [];
/* eslint-disable jsx-a11y/label-has-for */
return (
<div
className={`form-group ${styles.selectMany} ${value.length > 4 &&
styles.selectManyUpdate}`}
>
<label htmlFor={this.props.relation.alias}>
{this.props.relation.alias} <span>({value.length})</span>
</label>
{description}
<Select
className={`${styles.select}`}
id={this.props.relation.alias}
isLoading={this.state.isLoading}
onChange={this.handleChange}
onInputChange={this.handleInputChange}
onMenuScrollToBottom={this.handleBottomScroll}
options={this.state.options}
placeholder={
<FormattedMessage id="content-manager.containers.Edit.addAnItem" />
}
/>
<SortableList
items={
/* eslint-disable indent */
isNull(value) || isUndefined(value) || value.size === 0
? null
: value.map(item => {
if (item) {
return {
value: get(item, 'value') || item,
label:
get(item, 'label') ||
templateObject(
{ mainField: this.props.relation.displayedAttribute },
item
).mainField ||
item.id,
};
}
})
}
/* eslint-enable indent */
isDraggingSibling={this.props.isDraggingSibling}
keys={this.props.relation.alias}
moveAttr={this.props.moveAttr}
moveAttrEnd={this.props.moveAttrEnd}
name={this.props.relation.alias}
onRemove={this.handleRemove}
distance={1}
onClick={this.handleClick}
/>
</div>
);
/* eslint-disable jsx-a11y/label-has-for */
}
}
SelectMany.defaultProps = {
isDraggingSibling: false,
moveAttr: () => {},
moveAttrEnd: () => {},
};
SelectMany.propTypes = {
isDraggingSibling: PropTypes.bool,
moveAttr: PropTypes.func,
moveAttrEnd: PropTypes.func,
onAddRelationalItem: PropTypes.func.isRequired,
onRedirect: PropTypes.func.isRequired,
onRemoveRelationItem: PropTypes.func.isRequired,
record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired,
relation: PropTypes.object.isRequired,
};
export default SelectMany;

View File

@ -1,188 +0,0 @@
.selectMany { /* stylelint-disable */
padding-bottom: 20px;
margin-bottom: 0px;
label{
font-size: 1.3rem;
font-weight: 500;
text-transform: capitalize;
margin-top: 3px;
> span {
font-weight: 400;
font-size: 1.2rem;
}
}
label + div{
margin: 3px 0 26px;
&:focus{
outline: none;
}
>div{
box-shadow: none !important;
border-color: #E3E9F3 !important;
>span:last-of-type{
span{
border-color: #B3B5B9 transparent transparent;
}
}
}
}
}
.selectManyUpdate{
padding-bottom: 15px !important;
}
.select{
margin-bottom: 0px !important;
}
.sortableList {
overflow: hidden;
max-height: 116px;
margin-bottom: -9px;
> ul {
margin: 4px -20px 0;
padding: 0 20px !important;
list-style: none !important;
overflow: auto;
max-height: 110px;
}
}
.sortableListLong {
position: relative;
display: inline-block;
width: 100%;
height: 0px;
&:after {
position: absolute;
top: -15px;
left: -5px;
content: '';
display: inline-block;
width: calc(100% + 10px);
height: 1px;
margin-bottom: -25px;
box-shadow: 0px -2px 4px 0px rgba(227, 233, 243, .5);
}
}
.sortableListItemHover {
&:hover {
cursor: pointer;
}
}
.sortableListItem {
display: flex;
flex-wrap: nowrap;
align-content: center;
justify-content: space-between;
height: 27px;
background-color: transparent !important;
&:active{
.dragHandle{
// cursor: pointer;
> span {
background: #AED4FB;
}
}
}
.dragHandle{
outline: none;
text-decoration: none;
margin-top: -1px;
> span {
vertical-align: middle;
position: relative;
display: inline-block;
width: 6px;
height: 1px;
padding: 0px !important;
background: #B3B5B9;
overflow: visible !important;
transition: background .25s ease-out;
&:before, &:after{
content: '';
display: inline-block;
width: 6px;
height: 1px;
background: inherit;
}
&:before{
position: absolute;
top: -2px;
left: 0;
}
&:after{
position: absolute;
bottom: -2px;
left: 0;
}
}
}
> div {
width: 90%;
span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&:first-of-type{
display: flex;
align-items: center;
transition: color .25s ease-out;
&:hover{
.dragHandle{
> span {
background: #007EFF;
}
}
color: #007EFF;
}
span {
&:last-of-type{
padding-left: 10px;
}
}
}
&:last-of-type{
display: inline-block;
height: 100%;
padding-right: 0px;
line-height: 27px;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
img{
display: inline-block;
height: 14px;
}
}
}
}

View File

@ -1,52 +0,0 @@
/**
*
* Content
*/
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import IconRemove from '../../assets/images/icon_remove.svg';
import styles from '../SelectMany/styles.scss';
function Content({ index, item, onClick, onRemove }) {
return (
<React.Fragment>
<div>
<div className={styles.dragHandle}>
<span />
</div>
<FormattedMessage id="content-manager.containers.Edit.clickToJump">
{title => (
<span onClick={() => onClick(item)} title={title}>
{item.label}
</span>
)}
</FormattedMessage>
</div>
<div className={styles.selectManyDraggedItemActions}>
<img
src={IconRemove}
alt="Remove Icon"
onClick={() => onRemove(index)}
/>
</div>
</React.Fragment>
);
}
Content.defaultProps = {
index: 0,
onClick: () => {},
onRemove: () => {},
};
Content.propTypes = {
index: PropTypes.number,
item: PropTypes.object.isRequired,
onClick: PropTypes.func,
onRemove: PropTypes.func,
};
export default Content;

View File

@ -1,38 +0,0 @@
/**
*
* SelectManyDraggedItem
*/
import React from 'react';
import PropTypes from 'prop-types';
import styles from '../SelectMany/styles.scss';
import Content from './Content';
function SelectManyDraggedItem(props) {
if (props.withLiWrapper) {
return (
<li className={styles.sortableListItem} style={{ padding: '0 2px' }}>
<Content {...props} />
</li>
);
}
return <Content {...props} />;
}
SelectManyDraggedItem.defaultProps = {
index: 0,
onClick: () => {},
onRemove: () => {},
withLiWrapper: false,
};
SelectManyDraggedItem.propTypes = {
index: PropTypes.number,
item: PropTypes.object.isRequired,
onClick: PropTypes.func,
onRemove: PropTypes.func,
withLiWrapper: PropTypes.bool,
};
export default SelectManyDraggedItem;

View File

@ -1,228 +0,0 @@
/**
*
* SelectOne
*
*/
import React from 'react';
import Select from 'react-select';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';
import {
cloneDeep,
map,
includes,
isArray,
isNull,
isUndefined,
isFunction,
get,
findIndex,
} from 'lodash';
import { request, templateObject } from 'strapi-helper-plugin';
import styles from './styles.scss';
class SelectOne extends React.Component {
// eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
isLoading: true,
options: [],
toSkip: 0,
};
}
componentDidMount() {
this.getOptions('');
}
componentDidUpdate(prevProps, prevState) {
if (prevState.toSkip !== this.state.toSkip) {
this.getOptions('');
}
}
getOptions = query => {
const params = {
_limit: 20,
_start: this.state.toSkip,
source: this.props.relation.plugin || 'content-manager',
};
// Set `query` parameter if necessary
if (query) {
delete params._limit;
delete params._start;
params[`${this.props.relation.displayedAttribute}_contains`] = query;
}
// Request URL
const requestUrlSuffix =
query && get(this.props.record, [this.props.relation.alias])
? get(this.props.record, [this.props.relation.alias])
: '';
const requestUrl = `/content-manager/explorer/${this.props.relation.model ||
this.props.relation.collection}/${requestUrlSuffix}`;
// Call our request helper (see 'utils/request')
return request(requestUrl, {
method: 'GET',
params,
})
.then(response => {
const options = isArray(response)
? map(response, item => ({
value: item,
label: templateObject(
{ mainField: this.props.relation.displayedAttribute },
item,
).mainField,
}))
: [
{
value: response,
label: templateObject(
{ mainField: this.props.relation.displayedAttribute },
response,
).mainField,
},
];
const newOptions = cloneDeep(this.state.options);
options.map(option => {
// Don't add the values when searching
if (
findIndex(newOptions, o => o.value.id === option.value.id) === -1
) {
return newOptions.push(option);
}
});
return this.setState({
options: newOptions,
isLoading: false,
});
})
.catch(() => {
strapi.notification.error(
'content-manager.notification.error.relationship.fetch',
);
});
};
handleChange = value => {
const target = {
name: `record.${this.props.relation.alias}`,
value,
type: 'select',
};
this.props.setRecordAttribute({ target });
};
handleBottomScroll = () => {
this.setState(prevState => {
return {
toSkip: prevState.toSkip + 1,
};
});
};
// Redirect to the edit page
handleClick = (item = {}) => {
this.props.onRedirect({
model: this.props.relation.collection || this.props.relation.model,
id: item.value.id || item.value._id,
source: this.props.relation.plugin,
});
};
handleInputChange = value => {
const clonedOptions = this.state.options;
const filteredValues = clonedOptions.filter(data =>
includes(data.label, value),
);
if (filteredValues.length === 0) {
return this.getOptions(value);
}
};
render() {
const description = this.props.relation.description ? (
<p>{this.props.relation.description}</p>
) : (
''
);
const value = get(this.props.record, this.props.relation.alias);
const excludeModel = ['role', 'permission', 'file'].includes(
this.props.relation.model || this.props.relation.collection,
); // Temporary.
const entryLink =
isNull(value) || isUndefined(value) || excludeModel ? (
''
) : (
<FormattedMessage id="content-manager.containers.Edit.clickToJump">
{title => (
<a onClick={() => this.handleClick({ value })} title={title}>
<FormattedMessage id="content-manager.containers.Edit.seeDetails" />
</a>
)}
</FormattedMessage>
);
/* eslint-disable jsx-a11y/label-has-for */
return (
<div className={`form-group ${styles.selectOne}`}>
<nav className={styles.headline}>
<label htmlFor={this.props.relation.alias}>
{this.props.relation.alias}
</label>
{entryLink}
</nav>
{description}
<Select
onChange={this.handleChange}
options={this.state.options}
id={this.props.relation.alias}
isLoading={this.state.isLoading}
onMenuScrollToBottom={this.handleBottomScroll}
onInputChange={this.handleInputChange}
onSelectResetsInput={false}
simpleValue
value={
isNull(value) || isUndefined(value)
? null
: {
value: isFunction(value.toJS) ? value.toJS() : value,
label:
templateObject(
{ mainField: this.props.relation.displayedAttribute },
isFunction(value.toJS) ? value.toJS() : value,
).mainField ||
(isFunction(value.toJS)
? get(value.toJS(), 'id')
: get(value, 'id')),
}
}
/>
</div>
);
/* eslint-disable jsx-a11y/label-has-for */
}
}
SelectOne.propTypes = {
onRedirect: PropTypes.func.isRequired,
record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired,
relation: PropTypes.object.isRequired,
setRecordAttribute: PropTypes.func.isRequired,
};
export default SelectOne;

View File

@ -1,48 +0,0 @@
.selectOne { /* stylelint-disable */
position: relative;
label{
font-size: 1.3rem;
font-weight: 500;
text-transform: capitalize;
margin-top: 3px;
}
nav + div{
height: 34px;
margin: 3px 0 26px;
>div{
box-shadow: none !important;
border-color: #E3E9F3 !important;
>span:first-of-type{
>div:first-of-type{
color: #9EA7B8;
}
}
>span:last-of-type{
span{
border-color: #B3B5B9 transparent transparent;
}
}
}
}
}
.headline{
display: flex;
justify-content: space-between;
a{
color: #007EFF !important;
font-size: 1.3rem;
padding-top: 3px;
&:hover{
text-decoration: underline !important;
cursor: pointer;
}
}
}

View File

@ -41,9 +41,9 @@ function SelectWrapper({
const startRef = useRef();
startRef.current = state._start;
ref.current = async (uid, params = state) => {
ref.current = async (signal, params = state) => {
try {
const requestUrl = `/${pluginId}/explorer/${uid}`;
const requestUrl = `/${pluginId}/explorer/${targetModel}`;
if (isEmpty(params._q)) {
delete params._q;
@ -52,6 +52,7 @@ function SelectWrapper({
const data = await request(requestUrl, {
method: 'GET',
params: params,
signal,
});
const formattedData = data.map(obj => {
return { value: obj, label: obj[mainField] };
@ -84,10 +85,14 @@ function SelectWrapper({
};
useEffect(() => {
ref.current(targetModel);
const abortController = new AbortController();
const { signal } = abortController;
ref.current(signal);
return () => {};
}, [ref, targetModel, state._start, state._q]);
return () => {
abortController.abort();
};
}, [ref]);
const onInputChange = inputValue => {
setState(prevState => {
@ -98,11 +103,15 @@ function SelectWrapper({
return { ...prevState, _q: inputValue };
});
ref.current();
return inputValue;
};
const onMenuScrollToBottom = () => {
setState(prevState => ({ ...prevState, _start: prevState._start + 1 }));
ref.current();
};
const isSingle = [
'oneWay',

View File

@ -32,9 +32,9 @@ function settingViewModelReducer(state = initialState, action) {
);
case GET_DATA_SUCCEEDED:
return state
.update('initialData', () => fromJS(action.layout))
.update('initialData', () => fromJS(action.layout || {}))
.update('isLoading', () => false)
.update('modifiedData', () => fromJS(action.layout));
.update('modifiedData', () => fromJS(action.layout || {}));
case MOVE_FIELD_LIST:
return state
.updateIn(['modifiedData', 'layouts', 'list'], list => {

View File

@ -18,7 +18,7 @@ export function getDataSucceeded(generalSettings, groups, models) {
type: GET_DATA_SUCCEEDED,
generalSettings,
groups,
models,
models: models.filter(model => model.isDisplayed === true),
};
}