added list editor dropdown with reordering

This commit is contained in:
Martina 2020-07-03 19:59:28 -07:00
parent 6d592d1945
commit 700fa8f4f6
7 changed files with 300 additions and 23 deletions

View File

@ -34,6 +34,7 @@ const STYLES_INPUT_CONTAINER = css`
const STYLES_INPUT = css`
${INPUT_STYLES}
padding: 0 24px 0 24px;
text-overflow: ellipsis;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15),
inset 0 0 0 1px ${Constants.system.darkGray};
@ -89,12 +90,13 @@ export class Input extends React.Component {
};
_handleKeyUp = (e) => {
if (e.which === 13 && this.props.onSubmit) {
if ((e.which === 13 || e.keyCode === 13) && this.props.onSubmit) {
this.props.onSubmit(e);
return;
}
this.props.onKeyUp(e);
if (this.props.onKeyUp) {
this.props.onKeyUp(e);
}
};
_handleChange = (e) => {
@ -137,6 +139,7 @@ export class Input extends React.Component {
type={this.props.type}
placeholder={this.props.placeholder}
onChange={this._handleChange}
onKeyUp={this._handleKeyUp}
autoComplete="off"
readOnly={this.props.readOnly}
style={{
@ -146,6 +149,8 @@ export class Input extends React.Component {
INPUT_COLOR_MAP[this.props.validation]
}`
: null,
paddingRight:
this.props.copyable || this.props.search ? "32px" : "24px",
}}
/>
{this.props.copyable ? (
@ -155,6 +160,13 @@ export class Input extends React.Component {
onClick={this._handleCopy}
/>
) : null}
{this.props.search ? (
<SVG.Search
height="16px"
css={STYLES_COPY_AND_PASTE}
onClick={this.props.onSubmit}
/>
) : null}
</div>
);
}

View File

@ -0,0 +1,227 @@
import * as React from "react";
import * as Constants from "~/common/constants";
import * as SVG from "~/components/system/svg";
import { css, keyframes } from "@emotion/react";
import Draggable from "react-draggable";
import { Input } from "~/components/system/components/Input";
import { ButtonPrimary } from "~/components/system/components/Buttons";
const ITEM_HEIGHT = 30;
const expand = keyframes`
0% {
max-height: 0px;
overflow: scroll;
}
100% {
max-height: 400px;
overflow: scroll;
}
`;
const INPUT = css`
display: inline-block;
width: calc(100% - 80px);
`;
const BUTTON = css`
display: inline-block;
text-align: right;
`;
const STYLES_DELETE = css`
height: 18px;
margin-top: 1px;
cursor: pointer;
color: ${Constants.system.darkGray};
justify-self: end;
:hover {
color: ${Constants.system.brand};
}
`;
const STYLES_REORDER = css`
height: 14px;
margin-top: 1px;
cursor: grab;
color: ${Constants.system.darkGray};
justify-self: start;
:hover {
color: ${Constants.system.brand};
}
`;
const STYLES_LIST = css`
max-width: 480px;
min-width: 188px;
width: 100%;
max-height: 400px;
overflow-y: scroll;
background-color: ${Constants.system.white};
border: 1px solid ${Constants.system.darkGray};
padding: 4px;
border-radius: 4px;
animation: ${expand} 500ms ease-out 1;
position: absolute;
`;
const STYLES_LIST_ITEM = css`
font-size: 14px;
height: 30px;
display: grid;
align-items: center;
grid-template-columns: 2fr 30fr 1fr;
border-radius: 4px;
padding: 0 5px;
background-color: ${Constants.system.white};
border: 1px solid ${Constants.system.gray};
transition: 200ms ease all;
`;
export class ListEditor extends React.Component {
state = {
expand: false,
selected: this.props.selected,
reordering: null,
deltaY: 0,
search: "",
};
_handleDelete = (i) => {
let selected = this.state.selected;
selected.splice(i, 1);
this.setState({ selected });
};
_handleAdd = () => {
if (this.state.search.length) {
let selected = this.state.selected;
selected.splice(0, 0, this.state.search);
this.setState({ selected, search: "" });
}
};
_handleDrag = (e, ui) => {
this.setState({
deltaY: this.state.deltaY + ui.deltaY,
});
};
_handleStop = () => {
let selected = this.state.selected;
let index = this.state.reordering;
let newIndex = index + this.state.deltaY / ITEM_HEIGHT;
let item = selected.splice(index, 1)[0];
selected.splice(newIndex, 0, item);
this.setState({ reordering: null, selected, deltaY: 0 });
};
_handleChange = (e) => {
this.setState({ search: e.target.value });
};
render() {
if (!this.state.expand) {
return (
<div>
<div
css={INPUT}
onFocus={() => {
this.setState({ expand: true });
}}
>
<Input
name={this.props.name}
value={this.state.selected}
readOnly
tooltip={this.props.tooltip}
label={this.props.label}
description={this.props.description}
/>
</div>
<ButtonPrimary
css={BUTTON}
onClick={() => {
this.setState({ expand: true });
}}
>
Edit
</ButtonPrimary>
</div>
);
}
let selected = this.state.selected.map((item, i) => (
<Draggable
axis="y"
grid={[ITEM_HEIGHT, ITEM_HEIGHT]}
position={{
x: 0,
y:
this.state.reordering === null
? 0
: this.state.reordering > i &&
this.state.reordering + this.state.deltaY / ITEM_HEIGHT <= i
? ITEM_HEIGHT
: this.state.reordering < i &&
this.state.reordering + this.state.deltaY / ITEM_HEIGHT >= i
? -ITEM_HEIGHT
: 0,
}}
bounds={{
top: -i * ITEM_HEIGHT,
left: 0,
right: 0,
bottom: (this.state.selected.length - i - 1) * ITEM_HEIGHT,
}}
onStart={() => this.setState({ reordering: i })}
onDrag={this._handleDrag}
onStop={this._handleStop}
key={item}
>
<div
className="no-cursor"
css={STYLES_LIST_ITEM}
style={{
backgroundColor:
this.state.reordering === i
? Constants.system.gray
: Constants.system.white,
}}
>
<SVG.Reorder className="cursor" css={STYLES_REORDER} />
<div>{item}</div>
<SVG.X css={STYLES_DELETE} onClick={() => this._handleDelete(i)} />
</div>
</Draggable>
));
return (
<div>
<div css={INPUT}>
<Input
value={this.state.search}
search
tooltip={this.props.tooltip}
label={this.props.label}
placeholder={this.props.placeholder}
description={this.props.description}
onChange={this._handleChange}
onSubmit={this._handleAdd}
/>
</div>
<ButtonPrimary
css={BUTTON}
onClick={() => {
this.setState({ expand: false });
}}
>
Save
</ButtonPrimary>
<div css={STYLES_LIST}>{selected}</div>
</div>
);
}
}

View File

@ -36,6 +36,7 @@ export class Textarea extends React.Component {
onChange={this.props.onChange}
name={this.props.name}
value={this.props.value}
readOnly={this.props.readOnly}
/>
);
}

View File

@ -15,6 +15,7 @@ import { CodeBlock } from "~/components/system/components/CodeBlock";
import { CodeTextarea } from "~/components/system/components/CodeTextarea";
import { DatePicker } from "~/components/system/components/DatePicker";
import { Input } from "~/components/system/components/Input";
import { ListEditor } from "~/components/system/components/ListEditor";
import { Notification } from "~/components/system/components/Notification";
import { PopoverNavigation } from "~/components/system/components/PopoverNavigation";
import { RadioGroup } from "~/components/system/components/RadioGroup";
@ -54,6 +55,7 @@ export {
CodeTextarea,
DatePicker,
Input,
ListEditor,
Notification,
PopoverNavigation,
RadioGroup,

View File

@ -198,7 +198,7 @@ export const BandwidthUp = (props) => {
export const CheckCircle = (props) => {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="none"
stroke="currentColor"
@ -206,16 +206,16 @@ export const CheckCircle = (props) => {
strokeLinejoin="round"
strokeWidth="1.5"
>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z"/>
<path d="M16.6666 8.5L10.25 14.9167L7.33331 12"/>
<path d="M12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22Z" />
<path d="M16.6666 8.5L10.25 14.9167L7.33331 12" />
</g>
</svg>
)
}
);
};
export const InfoCircle = (props) => {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="none"
stroke="currentColor"
@ -228,12 +228,12 @@ export const InfoCircle = (props) => {
<line x1="12" y1="8" x2="12.01" y2="8"></line>
</g>
</svg>
)
}
);
};
export const AlertCircle = (props) => {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="none"
stroke="currentColor"
@ -246,12 +246,12 @@ export const AlertCircle = (props) => {
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</g>
</svg>
)
}
);
};
export const XCircle = (props) => {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="none"
stroke="currentColor"
@ -264,12 +264,12 @@ export const XCircle = (props) => {
<line x1="9" y1="9" x2="15" y2="15"></line>
</g>
</svg>
)
}
);
};
export const X = (props) => {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<svg viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="none"
stroke="currentColor"
@ -281,6 +281,41 @@ export const X = (props) => {
<line x1="6" y1="6" x2="18" y2="18"></line>
</g>
</svg>
)
}
);
};
export const Search = (props) => {
return (
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
>
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</g>
</svg>
);
};
export const Reorder = (props) => {
return (
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" {...props}>
<g
fill="currentColor"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="1.5"
>
<path d="M2 10C2.55228 10 3 9.55228 3 9C3 8.44772 2.55228 8 2 8C1.44772 8 1 8.44772 1 9C1 9.55228 1.44772 10 2 10Z" />
<path d="M2 3C2.55228 3 3 2.55228 3 2C3 1.44772 2.55228 1 2 1C1.44772 1 1 1.44772 1 2C1 2.55228 1.44772 3 2 3Z" />
<path d="M9 10C9.55228 10 10 9.55228 10 9C10 8.44772 9.55228 8 9 8C8.44772 8 8 8.44772 8 9C8 9.55228 8.44772 10 9 10Z" />
<path d="M9 3C9.55228 3 10 2.55228 10 2C10 1.44772 9.55228 1 9 1C8.44772 1 8 1.44772 8 2C8 2.55228 8.44772 3 9 3Z" />
</g>
</svg>
);
};

View File

@ -34,6 +34,7 @@
"next": "^9.4.4",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-draggable": "^4.4.3",
"react-tippy": "^1.3.4",
"three": "^0.108.0",
"uuid": "^8.0.0",

View File

@ -15,9 +15,9 @@ export default class SystemPageInputs extends React.Component {
exampleFive: '',
=======
twelve: "Replace me friend.",
thirteen: "",
thirteen: ["t0001", "t0002", "t0003", "t0004"],
fourteen: "",
fifteen: "aaaaa-bbbbb-ccccc-ddddd-eeee",
fifteen: "aaaaa-bbbbb-ccccc-ddddd",
sixteen: "",
seventeen: `Example text`,
>>>>>>> added country dropdown and refactored dropdown options
@ -414,7 +414,6 @@ class ExampleError extends React.Component {
}}
/>
</Group>
<System.DatePicker label="Birthday" />
</SystemPage>
);
}