Drag to reorder sections
This commit is contained in:
parent
1eff9b4267
commit
116c803ec7
@ -1,7 +1,9 @@
|
||||
import classNames = require("classnames");
|
||||
import { observer } from "mobx-react";
|
||||
import * as React from "react";
|
||||
import { Form, List } from "semantic-ui-react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import { SortableContainer, SortableElement, SortableHandle, SortEnd, arrayMove } from "react-sortable-hoc";
|
||||
import { Form, Icon, List } from "semantic-ui-react";
|
||||
|
||||
import { DurationView, SectionChooser } from "@app/components/index";
|
||||
import { Duration } from "@common/Duration";
|
||||
@ -9,6 +11,8 @@ import { ProgramItem, Section } from "@common/sprinklersRpc";
|
||||
|
||||
import "@app/styles/ProgramSequenceView";
|
||||
|
||||
const Handle = SortableHandle(() => <Icon name="bars"/>);
|
||||
|
||||
@observer
|
||||
class ProgramSequenceItem extends React.Component<{
|
||||
sequenceItem: ProgramItem, sections: Section[], onChange?: (newItem: ProgramItem) => void,
|
||||
@ -21,17 +25,15 @@ class ProgramSequenceItem extends React.Component<{
|
||||
|
||||
if (editing) {
|
||||
return (
|
||||
<Form.Group inline>
|
||||
<Form.Group>
|
||||
<SectionChooser
|
||||
label="Section"
|
||||
inline
|
||||
sections={sections}
|
||||
value={section}
|
||||
onChange={this.onSectionChange}
|
||||
/>
|
||||
<DurationView
|
||||
label="Duration"
|
||||
inline
|
||||
duration={duration}
|
||||
onDurationChange={this.onDurationChange}
|
||||
/>
|
||||
@ -48,11 +50,12 @@ class ProgramSequenceItem extends React.Component<{
|
||||
}
|
||||
|
||||
render() {
|
||||
const editing = this.props.onChange != null;
|
||||
return (
|
||||
<List.Item>
|
||||
<List.Icon name="caret right"/>
|
||||
<li className="programSequence-item ui form">
|
||||
{editing ? <Handle /> : <List.Icon name="caret right"/>}
|
||||
<List.Content>{this.renderContent()}</List.Content>
|
||||
</List.Item>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@ -75,22 +78,65 @@ class ProgramSequenceItem extends React.Component<{
|
||||
}
|
||||
}
|
||||
|
||||
const ProgramSequenceItemD = SortableElement(ProgramSequenceItem);
|
||||
|
||||
type ItemChangeHandler = (newItem: ProgramItem, index: number) => void;
|
||||
|
||||
const ProgramSequenceList = SortableContainer(observer(({ className, list, sections, onChange }: {
|
||||
className: string,
|
||||
list: ProgramItem[],
|
||||
sections: Section[],
|
||||
onChange?: ItemChangeHandler,
|
||||
}) => {
|
||||
const listItems = list.map((item, index) => {
|
||||
const onChangeHandler = onChange ? (newItem: ProgramItem) => onChange(newItem, index) : undefined;
|
||||
const key = `item-${index}`;
|
||||
return (
|
||||
<ProgramSequenceItemD
|
||||
sequenceItem={item}
|
||||
sections={sections}
|
||||
key={key}
|
||||
index={index}
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
);
|
||||
});
|
||||
return <ul className={className}>{listItems}</ul>;
|
||||
}), { withRef: true });
|
||||
|
||||
@observer
|
||||
export default class ProgramSequenceView extends React.Component<{
|
||||
class ProgramSequenceView extends React.Component<{
|
||||
sequence: ProgramItem[], sections: Section[], editing?: boolean,
|
||||
}> {
|
||||
render() {
|
||||
const { sequence, sections } = this.props;
|
||||
const editing = this.props.editing || false;
|
||||
const className = classNames("programSequence", { editing });
|
||||
const sequenceItems = sequence.map((item, index) => {
|
||||
const onChange = editing ? (newItem: ProgramItem) => this.changeItem(newItem, index) : undefined;
|
||||
return <ProgramSequenceItem sequenceItem={item} sections={sections} key={index} onChange={onChange}/>;
|
||||
});
|
||||
return <List className={className}>{sequenceItems}</List>;
|
||||
const onChange = editing ? this.changeItem : undefined;
|
||||
return (
|
||||
<ProgramSequenceList
|
||||
className={className}
|
||||
useDragHandle
|
||||
list={sequence}
|
||||
sections={sections}
|
||||
onChange={onChange}
|
||||
onSortEnd={this.onSortEnd}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private changeItem = (newItem: ProgramItem, index: number) => {
|
||||
private changeItem: ItemChangeHandler = (newItem, index) => {
|
||||
this.props.sequence[index] = newItem;
|
||||
}
|
||||
|
||||
private onSortEnd = ({oldIndex, newIndex}: SortEnd) => {
|
||||
const { sequence: array } = this.props;
|
||||
if (newIndex >= array.length) {
|
||||
return;
|
||||
}
|
||||
array.splice(newIndex, 0, array.splice(oldIndex, 1)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
const ProgramSequenceViewD = SortableContainer(ProgramSequenceView);
|
||||
export default ProgramSequenceViewD;
|
||||
|
@ -1,49 +1,42 @@
|
||||
$durationInput-spacing: 1.0em;
|
||||
$durationInput-inputWidth: 4em;
|
||||
$durationInput-labelWidth: 2.5em;
|
||||
|
||||
.durationInputs {
|
||||
display: flex;
|
||||
// max-width: 100%;
|
||||
.field .durationInputs {
|
||||
display: flex; // max-width: 100%;
|
||||
justify-content: start;
|
||||
flex-wrap: wrap;
|
||||
margin: -$durationInput-spacing / 2;
|
||||
|
||||
&.inline {
|
||||
min-width: 20em;
|
||||
width: auto;
|
||||
|
||||
.ui.input.durationInput {
|
||||
>input {
|
||||
width: 0 !important;
|
||||
}
|
||||
.ui.input.durationInput>input {
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
.ui.input.durationInput {
|
||||
padding: $durationInput-spacing / 2;
|
||||
max-width: 100%;
|
||||
width: auto !important;
|
||||
flex-basis: auto;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
|
||||
>input {
|
||||
min-width: $durationInput-inputWidth;
|
||||
width: auto;
|
||||
flex-basis: $durationInput-inputWidth;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
>.label {
|
||||
min-width: $durationInput-labelWidth;
|
||||
width: $durationInput-labelWidth;
|
||||
flex: $durationInput-labelWidth;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$durationInput-inputWidth: 4em;
|
||||
$durationInput-labelWidth: 2.5em;
|
||||
|
||||
.ui.form .field .ui.input.durationInput {
|
||||
padding: $durationInput-spacing / 2;
|
||||
|
||||
max-width: 100%;
|
||||
width: auto;
|
||||
flex-basis: auto;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
|
||||
> input {
|
||||
min-width: $durationInput-inputWidth;
|
||||
width: auto;
|
||||
flex-basis: $durationInput-inputWidth;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
> .label {
|
||||
min-width: $durationInput-labelWidth;
|
||||
width: $durationInput-labelWidth;
|
||||
flex: $durationInput-labelWidth;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,22 @@
|
||||
.programSequence.editing .item .content {
|
||||
width: 20em;
|
||||
.programSequence {
|
||||
&.editing .programSequence-item .content {
|
||||
// width: 20em;
|
||||
}
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.programSequence-item {
|
||||
list-style-type: none;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
margin-bottom: .5em;
|
||||
i.icon {
|
||||
margin: .5rem;
|
||||
}
|
||||
.header {
|
||||
font-weight: bold;
|
||||
}
|
||||
.durationInputs {
|
||||
width: 20em;
|
||||
}
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
.sectionChooser {
|
||||
.ui.selection.dropdown {
|
||||
min-width: 14em;
|
||||
min-width: 10em;
|
||||
max-width: 100%;
|
||||
width: 14em !important;
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +72,7 @@
|
||||
"@types/react-dom": "16.0.6",
|
||||
"@types/react-hot-loader": "^4.1.0",
|
||||
"@types/react-router-dom": "^4.3.0",
|
||||
"@types/react-sortable-hoc": "^0.6.3",
|
||||
"@types/webpack-env": "^1.13.6",
|
||||
"@types/ws": "^5.1.2",
|
||||
"async": "^2.6.1",
|
||||
@ -105,6 +106,7 @@
|
||||
"react-hot-loader": "^4.3.3",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-sortable-hoc": "^0.8.3",
|
||||
"sass-loader": "^7.0.3",
|
||||
"semantic-ui-css": "^2.3.3",
|
||||
"semantic-ui-react": "^0.82.1",
|
||||
|
29
yarn.lock
29
yarn.lock
@ -144,6 +144,12 @@
|
||||
"@types/history" "*"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-sortable-hoc@^0.6.3":
|
||||
version "0.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-sortable-hoc/-/react-sortable-hoc-0.6.3.tgz#b9d034ab2728691ef3270ede5f4b4fd1cead52bd"
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@16.4.7":
|
||||
version "16.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.7.tgz#f33f6d759a7e1833befa15224d68942d178a5a3f"
|
||||
@ -641,6 +647,13 @@ babel-code-frame@6.26.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
|
||||
esutils "^2.0.2"
|
||||
js-tokens "^3.0.2"
|
||||
|
||||
babel-runtime@^6.11.6:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
dependencies:
|
||||
core-js "^2.4.0"
|
||||
regenerator-runtime "^0.11.0"
|
||||
|
||||
backo2@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
|
||||
@ -1442,7 +1455,7 @@ core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
|
||||
core-js@^2.5.7:
|
||||
core-js@^2.4.0, core-js@^2.5.7:
|
||||
version "2.5.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
|
||||
|
||||
@ -5624,7 +5637,7 @@ promise@^8.0.1:
|
||||
dependencies:
|
||||
asap "~2.0.3"
|
||||
|
||||
prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
|
||||
prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||
dependencies:
|
||||
@ -5858,6 +5871,14 @@ react-router@^4.3.1:
|
||||
prop-types "^15.6.1"
|
||||
warning "^4.0.1"
|
||||
|
||||
react-sortable-hoc@^0.8.3:
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-0.8.3.tgz#8537e8ab8d6bad6829885755a0f847817ed78648"
|
||||
dependencies:
|
||||
babel-runtime "^6.11.6"
|
||||
invariant "^2.2.1"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react@16.4.1:
|
||||
version "16.4.1"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32"
|
||||
@ -5954,6 +5975,10 @@ regenerate@^1.2.1:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
|
||||
|
||||
regenerator-runtime@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
|
||||
|
||||
regenerator-runtime@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.0.tgz#8052ac952d85b10f3425192cd0c53f45cf65c6cb"
|
||||
|
Loading…
x
Reference in New Issue
Block a user