From 4e5a619b36f12270a38c716d24d5a1686c7acb5e Mon Sep 17 00:00:00 2001 From: Martina Date: Mon, 27 Jul 2020 16:56:03 -0700 Subject: [PATCH] slider component --- components/system/SystemPage.js | 1 + components/system/components/Input.js | 1 - components/system/components/Slider.js | 242 +++++++++++++ components/system/index.js | 3 +- components/system/modules/FilecoinSettings.js | 15 +- pages/system/sliders.js | 337 ++++++++++++++++++ 6 files changed, 587 insertions(+), 12 deletions(-) create mode 100644 components/system/components/Slider.js create mode 100644 pages/system/sliders.js diff --git a/components/system/SystemPage.js b/components/system/SystemPage.js index c11ceb8e..69ae521a 100644 --- a/components/system/SystemPage.js +++ b/components/system/SystemPage.js @@ -247,6 +247,7 @@ export default class SystemPage extends React.Component { + diff --git a/components/system/components/Input.js b/components/system/components/Input.js index ab354088..dbe60e31 100644 --- a/components/system/components/Input.js +++ b/components/system/components/Input.js @@ -149,7 +149,6 @@ export class Input extends React.Component { }; render() { - console.log(this._unit); return (
{ + 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 ( +
+ +
+
+
{ + this._bar = c; + }} + /> +
+ +
+ +
+ + {this.props.bubble ? ( +
{this.props.value}
+ ) : null} +
+ +
+ {this.props.inputBox ? ( + + ) : null} +
+
+ ); + } +} diff --git a/components/system/index.js b/components/system/index.js index b904c62e..a00db1df 100644 --- a/components/system/index.js +++ b/components/system/index.js @@ -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, diff --git a/components/system/modules/FilecoinSettings.js b/components/system/modules/FilecoinSettings.js index 638c8991..10ddcd6e 100644 --- a/components/system/modules/FilecoinSettings.js +++ b/components/system/modules/FilecoinSettings.js @@ -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" diff --git a/pages/system/sliders.js b/pages/system/sliders.js new file mode 100644 index 00000000..d37beff7 --- /dev/null +++ b/pages/system/sliders.js @@ -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 ( + + + Sliders + +
+
+ + The Slider component is used to select from a range of numeric values. + +
+
+
+ Imports +
+
+ Import React and the Slider Component. +
+
+ + {`import * as React from 'react'; +import { Slider } from 'slate-react-system';`} + +
+
+ Slider +
+
+ +
+ +
+ + Declare the Slider component, specifying a{" "} + min,{" "} + max, and{" "} + step. Declaring{" "} + discrete true will yield a slider + that snaps to each step. + +
+
+ + {`class ExampleOne extends React.Component { + state = { + one: 0, + two: 0 + } + + _handleChange = e => this.setState( + { [e.target.name]: e.target.value } + ); + + render() { + return( +
+ + +
+ ) + } +}`} +
+
+
+
+ Slider with display +
+
+ +
+ +
+ + You can declare the Slider component with{" "} + inputBox or{" "} + bubble to include a display of the + value. Values can be entered in the input box and the input box can be + styled with inputStyle. + +
+
+ + {`class ExampleTwo extends React.Component { + state = { + three: 9500, + four: 0 + } + + _handleChange = e => this.setState( + { [e.target.name]: e.target.value } + ); + + render() { + return( +
+ + +
+ ) + } +}`} +
+
+
+
+ Accepted React Properties +
+
+ + + onChange + + ), + b: "function", + c: "null", + d: "Function called upon an onChange event", + }, + { + id: 2, + a: ( + + value + + ), + 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).", + }, + ], + }} + /> + +
+ ); + } +}