mirror of
https://github.com/filecoin-project/slate.git
synced 2024-11-23 14:07:20 +03:00
added list editor dropdown with reordering
This commit is contained in:
parent
6d592d1945
commit
700fa8f4f6
@ -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>
|
||||
);
|
||||
}
|
||||
|
227
components/system/components/ListEditor.js
Normal file
227
components/system/components/ListEditor.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user