Browse Source

Drag to reorder sections

update-deps
Alex Mikhalev 7 years ago
parent
commit
116c803ec7
  1. 74
      app/components/ProgramSequenceView.tsx
  2. 67
      app/styles/DurationView.scss
  3. 23
      app/styles/ProgramSequenceView.scss
  4. 4
      app/styles/SectionChooser.scss
  5. 2
      package.json
  6. 29
      yarn.lock

74
app/components/ProgramSequenceView.tsx

@ -1,7 +1,9 @@
import classNames = require("classnames"); import classNames = require("classnames");
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import * as React from "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 { DurationView, SectionChooser } from "@app/components/index";
import { Duration } from "@common/Duration"; import { Duration } from "@common/Duration";
@ -9,6 +11,8 @@ import { ProgramItem, Section } from "@common/sprinklersRpc";
import "@app/styles/ProgramSequenceView"; import "@app/styles/ProgramSequenceView";
const Handle = SortableHandle(() => <Icon name="bars"/>);
@observer @observer
class ProgramSequenceItem extends React.Component<{ class ProgramSequenceItem extends React.Component<{
sequenceItem: ProgramItem, sections: Section[], onChange?: (newItem: ProgramItem) => void, sequenceItem: ProgramItem, sections: Section[], onChange?: (newItem: ProgramItem) => void,
@ -21,17 +25,15 @@ class ProgramSequenceItem extends React.Component<{
if (editing) { if (editing) {
return ( return (
<Form.Group inline> <Form.Group>
<SectionChooser <SectionChooser
label="Section" label="Section"
inline
sections={sections} sections={sections}
value={section} value={section}
onChange={this.onSectionChange} onChange={this.onSectionChange}
/> />
<DurationView <DurationView
label="Duration" label="Duration"
inline
duration={duration} duration={duration}
onDurationChange={this.onDurationChange} onDurationChange={this.onDurationChange}
/> />
@ -48,11 +50,12 @@ class ProgramSequenceItem extends React.Component<{
} }
render() { render() {
const editing = this.props.onChange != null;
return ( return (
<List.Item> <li className="programSequence-item ui form">
<List.Icon name="caret right"/> {editing ? <Handle /> : <List.Icon name="caret right"/>}
<List.Content>{this.renderContent()}</List.Content> <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 @observer
export default class ProgramSequenceView extends React.Component<{ class ProgramSequenceView extends React.Component<{
sequence: ProgramItem[], sections: Section[], editing?: boolean, sequence: ProgramItem[], sections: Section[], editing?: boolean,
}> { }> {
render() { render() {
const { sequence, sections } = this.props; const { sequence, sections } = this.props;
const editing = this.props.editing || false; const editing = this.props.editing || false;
const className = classNames("programSequence", { editing }); const className = classNames("programSequence", { editing });
const sequenceItems = sequence.map((item, index) => { const onChange = editing ? this.changeItem : undefined;
const onChange = editing ? (newItem: ProgramItem) => this.changeItem(newItem, index) : undefined; return (
return <ProgramSequenceItem sequenceItem={item} sections={sections} key={index} onChange={onChange}/>; <ProgramSequenceList
}); className={className}
return <List className={className}>{sequenceItems}</List>; 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; 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;

67
app/styles/DurationView.scss

@ -1,49 +1,42 @@
$durationInput-spacing: 1.0em; $durationInput-spacing: 1.0em;
$durationInput-inputWidth: 4em;
$durationInput-labelWidth: 2.5em;
.durationInputs { .field .durationInputs {
display: flex; display: flex; // max-width: 100%;
// max-width: 100%;
justify-content: start; justify-content: start;
flex-wrap: wrap; flex-wrap: wrap;
margin: -$durationInput-spacing / 2; margin: -$durationInput-spacing / 2;
&.inline {
min-width: 20em;
.ui.input.durationInput {
>input {
width: 0 !important;
}
}
}
}
$durationInput-inputWidth: 4em;
$durationInput-labelWidth: 2.5em;
.ui.form .field .ui.input.durationInput {
padding: $durationInput-spacing / 2;
max-width: 100%;
width: auto; width: auto;
flex-basis: auto;
flex-shrink: 0;
flex-grow: 1;
> input { .ui.input.durationInput>input {
min-width: $durationInput-inputWidth; width: 0 !important;
width: auto;
flex-basis: $durationInput-inputWidth;
flex-grow: 1;
flex-shrink: 0;
} }
> .label { .ui.input.durationInput {
min-width: $durationInput-labelWidth; padding: $durationInput-spacing / 2;
width: $durationInput-labelWidth; max-width: 100%;
flex: $durationInput-labelWidth; width: auto !important;
flex-grow: 0; flex-basis: auto;
flex-shrink: 0; flex-shrink: 0;
text-align: center; 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;
}
} }
} }

23
app/styles/ProgramSequenceView.scss

@ -1,3 +1,22 @@
.programSequence.editing .item .content { .programSequence {
width: 20em; &.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;
}
}

4
app/styles/SectionChooser.scss

@ -1,5 +1,7 @@
.sectionChooser { .sectionChooser {
.ui.selection.dropdown { .ui.selection.dropdown {
min-width: 14em; min-width: 10em;
max-width: 100%;
width: 14em !important;
} }
} }

2
package.json

@ -72,6 +72,7 @@
"@types/react-dom": "16.0.6", "@types/react-dom": "16.0.6",
"@types/react-hot-loader": "^4.1.0", "@types/react-hot-loader": "^4.1.0",
"@types/react-router-dom": "^4.3.0", "@types/react-router-dom": "^4.3.0",
"@types/react-sortable-hoc": "^0.6.3",
"@types/webpack-env": "^1.13.6", "@types/webpack-env": "^1.13.6",
"@types/ws": "^5.1.2", "@types/ws": "^5.1.2",
"async": "^2.6.1", "async": "^2.6.1",
@ -105,6 +106,7 @@
"react-hot-loader": "^4.3.3", "react-hot-loader": "^4.3.3",
"react-router": "^4.3.1", "react-router": "^4.3.1",
"react-router-dom": "^4.3.1", "react-router-dom": "^4.3.1",
"react-sortable-hoc": "^0.8.3",
"sass-loader": "^7.0.3", "sass-loader": "^7.0.3",
"semantic-ui-css": "^2.3.3", "semantic-ui-css": "^2.3.3",
"semantic-ui-react": "^0.82.1", "semantic-ui-react": "^0.82.1",

29
yarn.lock

@ -144,6 +144,12 @@
"@types/history" "*" "@types/history" "*"
"@types/react" "*" "@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": "@types/react@*", "@types/react@16.4.7":
version "16.4.7" version "16.4.7"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.4.7.tgz#f33f6d759a7e1833befa15224d68942d178a5a3f" 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" esutils "^2.0.2"
js-tokens "^3.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: backo2@1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" 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" version "1.2.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" 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" version "2.5.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
@ -5624,7 +5637,7 @@ promise@^8.0.1:
dependencies: dependencies:
asap "~2.0.3" 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" version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies: dependencies:
@ -5858,6 +5871,14 @@ react-router@^4.3.1:
prop-types "^15.6.1" prop-types "^15.6.1"
warning "^4.0.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: react@16.4.1:
version "16.4.1" version "16.4.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32" resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32"
@ -5954,6 +5975,10 @@ regenerate@^1.2.1:
version "1.4.0" version "1.4.0"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" 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: regenerator-runtime@^0.12.0:
version "0.12.0" version "0.12.0"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.0.tgz#8052ac952d85b10f3425192cd0c53f45cf65c6cb" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.0.tgz#8052ac952d85b10f3425192cd0c53f45cf65c6cb"

Loading…
Cancel
Save