mirror of
https://github.com/filecoin-project/slate.git
synced 2024-09-20 18:58:59 +03:00
slider component
This commit is contained in:
parent
e65b00579f
commit
4e5a619b36
@ -247,6 +247,7 @@ export default class SystemPage extends React.Component {
|
||||
<SidebarLink url={url} href="/system/checkboxes" title="Checkboxes" />
|
||||
<SidebarLink url={url} href="/system/radios" title="Radios" />
|
||||
<SidebarLink url={url} href="/system/toggles" title="Toggles" />
|
||||
<SidebarLink url={url} href="/system/sliders" title="Sliders" />
|
||||
<SidebarLink url={url} href="/system/inputs" title="Inputs" />
|
||||
<SidebarLink url={url} href="/system/dropdowns" title="Dropdowns" />
|
||||
<SidebarLink url={url} href="/system/datepicker" title="Datepicker" />
|
||||
|
@ -149,7 +149,6 @@ export class Input extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log(this._unit);
|
||||
return (
|
||||
<div
|
||||
css={
|
||||
|
242
components/system/components/Slider.js
Normal file
242
components/system/components/Slider.js
Normal file
@ -0,0 +1,242 @@
|
||||
import * as React from "react";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import { css } from "@emotion/react";
|
||||
import { DescriptionGroup } from "~/components/system/components/fragments/DescriptionGroup";
|
||||
import { Input } from "~/components/system/components/Input";
|
||||
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
const STYLES_BAR_CONTAINER = css`
|
||||
height: 48px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const STYLES_SLIDER_BAR = css`
|
||||
width: calc(100% - 24px);
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background-color: ${Constants.system.gray};
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 12px;
|
||||
box-sizing: border-box;
|
||||
`;
|
||||
|
||||
const STYLES_ACTIVE_SLIDER_BAR = css`
|
||||
height: 4px;
|
||||
border-radius: 2px 0px 0px 2px;
|
||||
background-color: ${Constants.system.brand};
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 12px;
|
||||
`;
|
||||
|
||||
const STYLES_BUBBLE = css`
|
||||
font-family: ${Constants.font.text};
|
||||
font-size: 0.9em;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background-color: ${Constants.system.gray};
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
top: -2.5em;
|
||||
transform: translateX(calc(-50% + 12px));
|
||||
cursor: default;
|
||||
|
||||
::selection {
|
||||
color: none;
|
||||
background: none;
|
||||
}
|
||||
`;
|
||||
|
||||
const STYLES_SLIDER_HANDLE = css`
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background-color: ${Constants.system.brand};
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
cursor: pointer;
|
||||
|
||||
:hover {
|
||||
box-shadow: 0 0 0 4px rgba(0, 72, 255, 0.1);
|
||||
}
|
||||
|
||||
:active {
|
||||
box-shadow: 0 0 0 8px rgba(0, 72, 255, 0.2);
|
||||
}
|
||||
`;
|
||||
|
||||
export class Slider extends React.Component {
|
||||
_bar;
|
||||
|
||||
static defaultProps = {
|
||||
step: 1,
|
||||
min: 0,
|
||||
max: 100,
|
||||
};
|
||||
|
||||
state = {
|
||||
value: 0,
|
||||
width: 0,
|
||||
input: this.props.value,
|
||||
};
|
||||
|
||||
componentDidMount = () => {
|
||||
this.setState(
|
||||
{
|
||||
decimals: this.getDecimals(),
|
||||
value:
|
||||
((this.props.value - this.props.min) * this._bar.offsetWidth) /
|
||||
(this.props.max - this.props.min),
|
||||
},
|
||||
this.updateDimensions
|
||||
);
|
||||
window.addEventListener("resize", this.updateDimensions);
|
||||
};
|
||||
|
||||
componentWillUnmount = () => {
|
||||
window.removeEventListener("resize", this.updateDimensions);
|
||||
};
|
||||
|
||||
updateDimensions = () => {
|
||||
let conversion = (this.props.max - this.props.min) / this._bar.offsetWidth;
|
||||
this.setState({
|
||||
width: this._bar.offsetWidth,
|
||||
conversion,
|
||||
step: this.props.step / conversion,
|
||||
});
|
||||
};
|
||||
|
||||
/* Gets how many decimal places the step has. To help deal with javascript rounding errors. */
|
||||
getDecimals = () => {
|
||||
let step = this.props.step.toString();
|
||||
let place = step.indexOf(".");
|
||||
if (place === -1) {
|
||||
return 0;
|
||||
}
|
||||
return step.length - step.indexOf(".") - 1;
|
||||
};
|
||||
|
||||
/* Converts it to increments of this.props.step while accounting for javascript rounding errors */
|
||||
formatNum = (num) => {
|
||||
return (Math.round(num / this.props.step) * this.props.step).toFixed(
|
||||
this.state.decimals
|
||||
);
|
||||
};
|
||||
|
||||
/* Converts from px width to return value */
|
||||
toValue = (px) => {
|
||||
return px * this.state.conversion + this.props.min;
|
||||
};
|
||||
|
||||
/* Converts from return value to px width */
|
||||
toPx = (value) => {
|
||||
return (value - this.props.min) / this.state.conversion;
|
||||
};
|
||||
|
||||
/* NOTE (Martina): make sure you only query this.state.value and ui.deltaX once and save the value. Querying
|
||||
it twice in one function call will give two different values since it changes so rapidly */
|
||||
_handleDrag = (e, ui) => {
|
||||
let px = this.state.value + ui.deltaX;
|
||||
let value = this.formatNum(this.toValue(px));
|
||||
this.props.onChange({
|
||||
target: { name: this.props.name, value },
|
||||
});
|
||||
this.setState({ value: px, input: value });
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
let val = e.target.value;
|
||||
if (isNaN(e.target.value)) {
|
||||
val = this.props.min;
|
||||
}
|
||||
let value = Math.min(Math.max(val, this.props.min), this.props.max);
|
||||
value = this.formatNum(value);
|
||||
this.props.onChange({
|
||||
target: { name: this.props.name, value },
|
||||
});
|
||||
let px = this.toPx(value);
|
||||
this.setState({ value: px, input: e.target.value });
|
||||
};
|
||||
|
||||
_handleBlur = (e) => {
|
||||
this.setState({ input: this.props.value });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<DescriptionGroup
|
||||
full={this.props.full}
|
||||
tooltip={this.props.tooltip}
|
||||
label={this.props.label}
|
||||
description={this.props.description}
|
||||
/>
|
||||
<div
|
||||
css={STYLES_BAR_CONTAINER}
|
||||
style={{
|
||||
marginTop: this.props.bubble ? "32px" : "0px",
|
||||
...this.props.containerStyle,
|
||||
}}
|
||||
>
|
||||
<div style={{ height: "24px", position: "relative" }}>
|
||||
<div
|
||||
css={STYLES_SLIDER_BAR}
|
||||
ref={(c) => {
|
||||
this._bar = c;
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
css={STYLES_ACTIVE_SLIDER_BAR}
|
||||
style={{
|
||||
width: `${this.state.value}px`,
|
||||
}}
|
||||
/>
|
||||
<Draggable
|
||||
axis="x"
|
||||
position={{ x: this.state.value, y: 0 }}
|
||||
bounds={{ left: 0, right: this.state.width }}
|
||||
grid={
|
||||
this.props.discrete ? [this.state.step, this.state.step] : null
|
||||
}
|
||||
onDrag={this._handleDrag}
|
||||
handle="strong"
|
||||
>
|
||||
<div
|
||||
style={{ position: "relative", width: "24px", height: "24px" }}
|
||||
>
|
||||
<strong>
|
||||
<div css={STYLES_SLIDER_HANDLE} />
|
||||
</strong>
|
||||
{this.props.bubble ? (
|
||||
<div css={STYLES_BUBBLE}>{this.props.value}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Draggable>
|
||||
</div>
|
||||
{this.props.inputBox ? (
|
||||
<Input
|
||||
value={this.state.input}
|
||||
onChange={this._handleChange}
|
||||
onBlur={this._handleBlur}
|
||||
pattern="^[\.\d-]*$"
|
||||
style={{
|
||||
width: "80px",
|
||||
paddingLeft: "8px",
|
||||
paddingRight: "8px",
|
||||
textOverflow: "clip",
|
||||
marginLeft: "16px",
|
||||
...this.props.inputStyle,
|
||||
}}
|
||||
containerStyle={{ minWidth: "80px" }}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ import {
|
||||
LoaderProgress,
|
||||
LoaderSpinner,
|
||||
} from "~/components/system/components/Loaders";
|
||||
|
||||
import { Slider } from "~/components/system/components/Slider";
|
||||
import {
|
||||
SelectCountryMenu,
|
||||
SelectMenu,
|
||||
@ -115,6 +115,7 @@ export {
|
||||
RadioGroup,
|
||||
SelectCountryMenu,
|
||||
SelectMenu,
|
||||
Slider,
|
||||
StatUpload,
|
||||
StatDownload,
|
||||
TabGroup,
|
||||
|
@ -273,9 +273,8 @@ export class FilecoinSettings extends React.Component {
|
||||
description="How long you would like your files stored for, before the deal must be renewed or the file will be removed from the Filecoin network."
|
||||
tooltip="Duration in epochs (~25 seconds)."
|
||||
name="settings_cold_default_duration"
|
||||
type="number"
|
||||
unit="epochs"
|
||||
pattern="[0-9]"
|
||||
pattern="^\d*$"
|
||||
value={this.state.settings_cold_default_duration}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
@ -288,9 +287,8 @@ export class FilecoinSettings extends React.Component {
|
||||
tooltip="A higher replication factor means your files are more secure against loss, but also costs more."
|
||||
name="settings_cold_default_replication_factor"
|
||||
value={this.state.settings_cold_default_replication_factor}
|
||||
type="number"
|
||||
unit="miners"
|
||||
pattern="[0-9]"
|
||||
pattern="^\d*$"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
|
||||
@ -301,8 +299,7 @@ export class FilecoinSettings extends React.Component {
|
||||
description="The maximum price in Filecoin you're willing to pay to store 1 GB for 1 Epoch (~25 seconds)."
|
||||
tooltip="Slate will always try to find you the best price, regardless of how high you set this."
|
||||
name="settings_cold_default_max_price"
|
||||
type="number"
|
||||
pattern="[0-9]"
|
||||
pattern="^\d*$"
|
||||
value={this.state.settings_cold_default_max_price}
|
||||
unit="FIL/GB/epoch"
|
||||
onChange={this._handleChange}
|
||||
@ -335,8 +332,7 @@ export class FilecoinSettings extends React.Component {
|
||||
description="How long before a deal expires should it auto renew."
|
||||
tooltip="Placeholder."
|
||||
name="settings_cold_default_auto_renew_threshold"
|
||||
type="number"
|
||||
pattern="[0-9]"
|
||||
pattern="^\d*$"
|
||||
value={
|
||||
this.state.settings_cold_default_auto_renew_threshold
|
||||
}
|
||||
@ -399,8 +395,7 @@ export class FilecoinSettings extends React.Component {
|
||||
full
|
||||
containerStyle={{ marginTop: 24 }}
|
||||
label="Add timeout"
|
||||
type="number"
|
||||
pattern="\d"
|
||||
pattern="^\d*$"
|
||||
description="How many seconds Slate will search for a file in hot storage before executing an unfreeze retrieval deal to get it from cold storage."
|
||||
tooltip="Placeholder."
|
||||
name="settings_hot_ipfs_add_timeout"
|
||||
|
337
pages/system/sliders.js
Normal file
337
pages/system/sliders.js
Normal file
@ -0,0 +1,337 @@
|
||||
import * as React from "react";
|
||||
import * as System from "~/components/system";
|
||||
import * as Constants from "~/common/constants";
|
||||
|
||||
import Group from "~/components/system/Group";
|
||||
import SystemPage from "~/components/system/SystemPage";
|
||||
import ViewSourceLink from "~/components/system/ViewSourceLink";
|
||||
import CodeBlock from "~/components/system/CodeBlock";
|
||||
|
||||
export default class SystemPageSliders extends React.Component {
|
||||
state = {
|
||||
one: 0,
|
||||
two: 0,
|
||||
three: 9500,
|
||||
four: 0,
|
||||
};
|
||||
|
||||
_handleChange = (e) => {
|
||||
this.setState({ [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SystemPage
|
||||
title="SDS: Sliders"
|
||||
description="..."
|
||||
url="https://slate.host/system/sliders"
|
||||
>
|
||||
<System.H1>
|
||||
Sliders <ViewSourceLink file="system/sliders.js" />
|
||||
</System.H1>
|
||||
<br />
|
||||
<br />
|
||||
<System.P>
|
||||
The Slider component is used to select from a range of numeric values.
|
||||
</System.P>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<System.H2>Imports</System.H2>
|
||||
<hr />
|
||||
<br />
|
||||
<System.P>Import React and the Slider Component.</System.P>
|
||||
<br />
|
||||
<br />
|
||||
<CodeBlock>
|
||||
{`import * as React from 'react';
|
||||
import { Slider } from 'slate-react-system';`}
|
||||
</CodeBlock>
|
||||
<br />
|
||||
<br />
|
||||
<System.H2>Slider</System.H2>
|
||||
<hr />
|
||||
<br />
|
||||
<System.Slider
|
||||
discrete
|
||||
label="Discrete Slider"
|
||||
value={this.state.one}
|
||||
name="one"
|
||||
min={0}
|
||||
max={100}
|
||||
step={20}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<br />
|
||||
<System.Slider
|
||||
label="Continuous Slider"
|
||||
value={this.state.two}
|
||||
name="two"
|
||||
min={0}
|
||||
max={100}
|
||||
step={20}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<br />
|
||||
<System.P>
|
||||
Declare the Slider component, specifying a{" "}
|
||||
<System.CodeText>min</System.CodeText>,{" "}
|
||||
<System.CodeText>max</System.CodeText>, and{" "}
|
||||
<System.CodeText>step</System.CodeText>. Declaring{" "}
|
||||
<System.CodeText>discrete</System.CodeText> true will yield a slider
|
||||
that snaps to each step.
|
||||
</System.P>
|
||||
<br />
|
||||
<br />
|
||||
<CodeBlock>
|
||||
{`class ExampleOne extends React.Component {
|
||||
state = {
|
||||
one: 0,
|
||||
two: 0
|
||||
}
|
||||
|
||||
_handleChange = e => this.setState(
|
||||
{ [e.target.name]: e.target.value }
|
||||
);
|
||||
|
||||
render() {
|
||||
return(
|
||||
<div>
|
||||
<System.Slider
|
||||
discrete
|
||||
label="Discrete Slider"
|
||||
value={this.state.one}
|
||||
name="one"
|
||||
min={0}
|
||||
max={100}
|
||||
step={20}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<System.Slider
|
||||
label="Continuous Slider"
|
||||
value={this.state.two}
|
||||
name="two"
|
||||
min={0}
|
||||
max={100}
|
||||
step={20}
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<System.H2>Slider with display</System.H2>
|
||||
<hr />
|
||||
<br />
|
||||
<System.Slider
|
||||
inputBox
|
||||
label="Slider with Input Box"
|
||||
min={9000}
|
||||
max={10000}
|
||||
step={100}
|
||||
value={this.state.three}
|
||||
name="three"
|
||||
onChange={this._handleChange}
|
||||
inputStyle={{ width: "60px" }}
|
||||
/>
|
||||
<br />
|
||||
<System.Slider
|
||||
bubble
|
||||
label="Slider with Display Bubble"
|
||||
min={-10}
|
||||
max={10}
|
||||
step={0.5}
|
||||
value={this.state.four}
|
||||
name="four"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
<br />
|
||||
<System.P>
|
||||
You can declare the Slider component with{" "}
|
||||
<System.CodeText>inputBox</System.CodeText> or{" "}
|
||||
<System.CodeText>bubble</System.CodeText> to include a display of the
|
||||
value. Values can be entered in the input box and the input box can be
|
||||
styled with <System.CodeText>inputStyle</System.CodeText>.
|
||||
</System.P>
|
||||
<br />
|
||||
<br />
|
||||
<CodeBlock>
|
||||
{`class ExampleTwo extends React.Component {
|
||||
state = {
|
||||
three: 9500,
|
||||
four: 0
|
||||
}
|
||||
|
||||
_handleChange = e => this.setState(
|
||||
{ [e.target.name]: e.target.value }
|
||||
);
|
||||
|
||||
render() {
|
||||
return(
|
||||
<div>
|
||||
<System.Slider
|
||||
inputBox
|
||||
label="Slider with Input Box"
|
||||
min={9000}
|
||||
max={10000}
|
||||
step={100}
|
||||
value={this.state.one}
|
||||
name="three"
|
||||
onChange={this._handleChange}
|
||||
inputStyle={{ width: "60px" }}
|
||||
/>
|
||||
<System.Slider
|
||||
bubble
|
||||
label="Slider with Display Bubble"
|
||||
min={-10}
|
||||
max={10}
|
||||
step={0.5}
|
||||
value={this.state.four}
|
||||
name="four"
|
||||
onChange={this._handleChange}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}`}
|
||||
</CodeBlock>
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
<System.H2>Accepted React Properties</System.H2>
|
||||
<hr />
|
||||
<br />
|
||||
<Group title="Toggles">
|
||||
<System.Table
|
||||
data={{
|
||||
columns: [
|
||||
{ key: "a", name: "Name", width: "128px" },
|
||||
{ key: "b", name: "Type", width: "88px", type: "OBJECT_TYPE" },
|
||||
{ key: "c", name: "Default", width: "88px" },
|
||||
{ key: "d", name: "Description", width: "100%" },
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
id: 1,
|
||||
a: (
|
||||
<span style={{ fontFamily: Constants.font.semiBold }}>
|
||||
onChange
|
||||
</span>
|
||||
),
|
||||
b: "function",
|
||||
c: "null",
|
||||
d: "Function called upon an onChange event",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
a: (
|
||||
<span style={{ fontFamily: Constants.font.semiBold }}>
|
||||
value
|
||||
</span>
|
||||
),
|
||||
b: "number",
|
||||
c: "null",
|
||||
d:
|
||||
"The value that the slider takes. Can be used to assign default values as well.",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
a: "name",
|
||||
b: "string",
|
||||
c: "null",
|
||||
d: "Slider name.",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
a: "label",
|
||||
b: "string",
|
||||
c: "null",
|
||||
d: "Label text.",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
a: "description",
|
||||
b: "string",
|
||||
c: "null",
|
||||
d: "Description text.",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
a: "tooltip",
|
||||
b: "string",
|
||||
c: "null",
|
||||
d: "Tooltip text.",
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
a: "min",
|
||||
b: "number",
|
||||
c: "0",
|
||||
d: "Lower end of range",
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
a: "max",
|
||||
b: "number",
|
||||
c: "100",
|
||||
d: "Higher end of range",
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
a: "step",
|
||||
b: "number",
|
||||
c: "1",
|
||||
d: "Increments in which values can be selected.",
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
a: "discrete",
|
||||
b: "boolean",
|
||||
c: "false",
|
||||
d:
|
||||
"If true, slider will snap to steps specified. Otherwise, slider is continuous",
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
a: "bubble",
|
||||
b: "boolean",
|
||||
c: "false",
|
||||
d:
|
||||
"If true, a bubble floating above the handle will be displayed with the selected value.",
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
a: "inputBox",
|
||||
b: "boolean",
|
||||
c: "false",
|
||||
d:
|
||||
"If true, an input box will be displayed with the selected value. The value can be edited using the input box.",
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
a: "inputStyle",
|
||||
b: "Object",
|
||||
c: "null",
|
||||
d:
|
||||
"Style applied to the input box (useful for specifying width, etc).",
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
a: "containerStyle",
|
||||
b: "Object",
|
||||
c: "null",
|
||||
d:
|
||||
"Style applied to the container holding the slider and input box (useful for specifying margin or height, etc).",
|
||||
},
|
||||
],
|
||||
}}
|
||||
/>
|
||||
</Group>
|
||||
</SystemPage>
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user