Add common setting types
This commit is contained in:
parent
87652dab8e
commit
70194a7114
|
@ -70,5 +70,31 @@ export default [
|
|||
{type: "switch", id: "inspectElement", value: false, enableWith: "devTools"},
|
||||
{type: "switch", id: "devToolsWarning", value: false, enableWith: "devTools"},
|
||||
]
|
||||
}
|
||||
},
|
||||
// {
|
||||
// type: "category",
|
||||
// id: "debug",
|
||||
// name: "Debug",
|
||||
// collapsible: true,
|
||||
// shown: true,
|
||||
// settings: [
|
||||
// {name: "Text test", note: "Just testing it", type: "text", id: "texttest", value: ""},
|
||||
// {name: "Slider test", note: "Just testing it", type: "slider", id: "slidertest", value: 30, min: 20, max: 50, step: 10},
|
||||
// {
|
||||
// name: "Radio test",
|
||||
// note: "Just testing it",
|
||||
// type: "radio",
|
||||
// id: "radiotest",
|
||||
// value: "test",
|
||||
// options: [
|
||||
// {name: "First", value: 30, description: "little hint"},
|
||||
// {name: "IDK", value: "test", description: "who cares"},
|
||||
// {name: "Something", value: 666, description: "something else"},
|
||||
// {name: "Last", value: "last", description: "nothing more to add"}
|
||||
// ]
|
||||
// },
|
||||
// {name: "Keybind test", note: "Just testing it", type: "keybind", id: "keybindtest", value: ["Control", "H"]},
|
||||
// {name: "Color test", note: "Just testing it", type: "color", id: "colortest", value: "#ff0000", defaultValue: "#ffffff"},
|
||||
// ]
|
||||
// }
|
||||
];
|
|
@ -111,11 +111,16 @@ export default new class SettingsManager {
|
|||
for (const setting in this.state[id][category]) {
|
||||
if (previousState[category][setting] == undefined) continue;
|
||||
const settingObj = this.getSetting(id, category, setting);
|
||||
if (settingObj.type == "switch") this.state[id][category][setting] = previousState[category][setting];
|
||||
if (settingObj.type == "number") this.state[id][category][setting] = previousState[category][setting];
|
||||
if (settingObj.type == "dropdown") {
|
||||
const exists = settingObj.options.some(o => o.value == previousState[category][setting]);
|
||||
if (exists) this.state[id][category][setting] = previousState[category][setting];
|
||||
switch (settingObj.type) {
|
||||
case "radio":
|
||||
case "dropdown": {
|
||||
const exists = settingObj.options.some(o => o.value == previousState[category][setting]);
|
||||
if (exists) this.state[id][category][setting] = previousState[category][setting];
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this.state[id][category][setting] = previousState[category][setting];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,10 @@
|
|||
font-weight: 400;
|
||||
}
|
||||
|
||||
.bd-setting-item:not(.inline) .bd-setting-note {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bd-setting-divider {
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
.bd-color-picker-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bd-color-picker-controls {
|
||||
padding-left: 1px;
|
||||
padding-top: 2px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bd-color-picker-default {
|
||||
cursor: pointer;
|
||||
width: 72px;
|
||||
height: 54px;
|
||||
border-radius: 4px;
|
||||
margin-right: 9px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.bd-color-picker-custom {
|
||||
position: relative;
|
||||
display: inline-table;
|
||||
}
|
||||
|
||||
.bd-color-picker-custom svg {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.bd-color-picker {
|
||||
outline: none;
|
||||
width: 70px;
|
||||
border: none;
|
||||
height: 54px;
|
||||
margin-top: 1px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bd-color-picker::-webkit-color-swatch {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.bd-color-picker-swatch {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
margin-left: 5px !important;
|
||||
max-width: 340px;
|
||||
}
|
||||
|
||||
.bd-color-picker-swatch-item {
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
margin: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
.bd-keybind-wrap {
|
||||
position: relative;
|
||||
min-width: 250px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
background-color: hsla(0, calc(var(--saturation-factor, 1)*0%), 0%, .1);
|
||||
border: 1px solid hsla(0, calc(var(--saturation-factor, 1)*0%), 0%, .3);
|
||||
padding: 10px;
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bd-keybind-wrap input {
|
||||
outline: none;
|
||||
border: none;
|
||||
pointer-events: none;
|
||||
color: var(--text-normal);
|
||||
background: none;
|
||||
font-size: 16px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.bd-keybind-wrap.recording {
|
||||
border-color: hsla(359, calc(var(--saturation-factor, 1)*82.6%), 59.4%, .3);
|
||||
}
|
||||
|
||||
.bd-keybind-wrap.recording {
|
||||
box-shadow: 0 0 6px hsla(359, calc(var(--saturation-factor, 1)*82.6%), 59.4%, .3);
|
||||
}
|
||||
|
||||
.bd-keybind-controls {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 3px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bd-keybind-clear {
|
||||
background: none!important;
|
||||
opacity: 0.5;
|
||||
padding-right: 4px!important;
|
||||
}
|
||||
|
||||
.bd-keybind-clear:hover {
|
||||
background: none;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bd-keybind-clear svg {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
.bd-radio-group {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.bd-radio-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-color: var(--background-secondary);
|
||||
border-radius: 3px;
|
||||
color: var(--interactive-normal);
|
||||
}
|
||||
|
||||
.bd-radio-option:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.bd-radio-option.bd-radio-selected {
|
||||
background-color: var(--background-modifier-selected);
|
||||
color: var(--interactive-active);
|
||||
}
|
||||
|
||||
.bd-radio-option input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.bd-radio-icon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.bd-radio-label-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.bd-radio-label {
|
||||
font-family: var(--font-primary);
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.bd-radio-description {
|
||||
font-family: var(--font-primary);
|
||||
font-size: 14px;
|
||||
line-height: 18px;
|
||||
font-weight: 400;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
.bd-slider-wrap {
|
||||
display: flex;
|
||||
color: var(--text-normal);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bd-slider-label {
|
||||
background: var(--brand-experiment);
|
||||
font-weight: 700;
|
||||
padding: 5px;
|
||||
margin-right: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.bd-slider-input {
|
||||
/* -webkit-appearance: none; */
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
appearance: none;
|
||||
min-width: 350px;
|
||||
border-radius: 5px;
|
||||
background: hsl(217,calc(var(--saturation-factor, 1)*7.6%),33.5%);
|
||||
outline: none;
|
||||
transition: opacity .2s;
|
||||
background-image: linear-gradient(var(--brand-experiment), var(--brand-experiment));
|
||||
background-size: 70% 100%;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
|
||||
.bd-slider-input::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
width: 10px;
|
||||
height: 24px;
|
||||
top: 50%;
|
||||
border-radius: 3px;
|
||||
background-color: hsl(0,calc(var(--saturation-factor, 1)*0%),100%);
|
||||
border: 1px solid hsl(210,calc(var(--saturation-factor, 1)*2.9%),86.7%);
|
||||
-webkit-box-shadow: 0 3px 1px 0 hsla(0,calc(var(--saturation-factor, 1)*0%),0%,.05),0 2px 2px 0 hsla(0,calc(var(--saturation-factor, 1)*0%),0%,.1),0 3px 3px 0 hsla(0,calc(var(--saturation-factor, 1)*0%),0%,.05);
|
||||
box-shadow: 0 3px 1px 0 hsla(0,calc(var(--saturation-factor, 1)*0%),0%,.05),0 2px 2px 0 hsla(0,calc(var(--saturation-factor, 1)*0%),0%,.1),0 3px 3px 0 hsla(0,calc(var(--saturation-factor, 1)*0%),0%,.05);
|
||||
cursor: ew-resize;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
.bd-text-input {
|
||||
min-width: 250px;
|
||||
font-size: 16px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
color: var(--text-normal);
|
||||
background-color: var(--input-background);
|
||||
border: none;
|
||||
padding: 10px;
|
||||
height: 40px;
|
||||
}
|
|
@ -2,7 +2,8 @@ import {React} from "modules";
|
|||
|
||||
export default class CloseButton extends React.Component {
|
||||
render() {
|
||||
return <svg viewBox="0 0 12 12" style={{width: "18px", height: "18px"}}>
|
||||
const size = this.props.size || "18px";
|
||||
return <svg viewBox="0 0 12 12" style={{width: size, height: size}}>
|
||||
<g className="background" fill="none" fillRule="evenodd">
|
||||
<path d="M0 0h12v12H0" />
|
||||
<path className="fill" fill="#dcddde" d="M9.5 3.205L8.795 2.5 6 5.295 3.205 2.5l-.705.705L5.295 6 2.5 8.795l.705.705L6 6.705 8.795 9.5l.705-.705L6.705 6" />
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Keyboard extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "24px";
|
||||
return <svg className={this.props.className} viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}}>
|
||||
<path d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z" />
|
||||
<path fill="none" d="M0 0h24v24H0zm0 0h24v24H0z" />
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Radio extends React.Component {
|
||||
render() {
|
||||
const size = this.props.size || "24px";
|
||||
return <svg className={this.props.className} viewBox="0 0 24 24" fill="#FFFFFF" style={{width: size, height: size}}>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
{this.props.checked && <path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zm0-5C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />}
|
||||
{!this.props.checked && <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />}
|
||||
</svg>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
import {React, WebpackModules} from "modules";
|
||||
|
||||
const TooltipWrapper = WebpackModules.getByPrototypes("renderTooltip");
|
||||
|
||||
const Checkmark = React.memo((props) => (
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" {...props}>
|
||||
<path fillRule="evenodd" clipRule="evenodd" fill={props.color ?? "#fff"} d="M8.99991 16.17L4.82991 12L3.40991 13.41L8.99991 19L20.9999 7.00003L19.5899 5.59003L8.99991 16.17Z" />
|
||||
</svg>
|
||||
));
|
||||
|
||||
const Dropper = React.memo((props) => (
|
||||
<svg width="14" height="14" viewBox="0 0 16 16" {...props}>
|
||||
<g fill="none">
|
||||
<path d="M-4-4h24v24H-4z"/>
|
||||
<path fill={props.color ?? "#fff"} d="M14.994 1.006C13.858-.257 11.904-.3 10.72.89L8.637 2.975l-.696-.697-1.387 1.388 5.557 5.557 1.387-1.388-.697-.697 1.964-1.964c1.13-1.13 1.3-2.985.23-4.168zm-13.25 10.25c-.225.224-.408.48-.55.764L.02 14.37l1.39 1.39 2.35-1.174c.283-.14.54-.33.765-.55l4.808-4.808-2.776-2.776-4.813 4.803z" />
|
||||
</g>
|
||||
</svg>
|
||||
));
|
||||
|
||||
const defaultColors = [1752220, 3066993, 3447003, 10181046, 15277667, 15844367, 15105570, 15158332, 9807270, 6323595, 1146986, 2067276, 2123412, 7419530, 11342935, 12745742, 11027200, 10038562, 9936031, 5533306];
|
||||
|
||||
const resolveColor = (color, hex = true) => {
|
||||
switch (typeof color) {
|
||||
case (hex && "number"): return `#${color.toString(16)}`;
|
||||
case (!hex && "string"): return Number.parseInt(color.replace("#", ""), 16);
|
||||
case (!hex && "number"): return color;
|
||||
case (hex && "string"): return color;
|
||||
|
||||
default: return color;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const getRGB = (color) => {
|
||||
let result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color);
|
||||
if (result) return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
|
||||
|
||||
result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*,\s*([0-9]+(?:\.[0-9]+)?)%\s*\)/.exec(color);
|
||||
if (result) return [parseFloat(result[1]) * 2.55, parseFloat(result[2]) * 2.55, parseFloat(result[3]) * 2.55];
|
||||
|
||||
result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color);
|
||||
if (result) return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
|
||||
|
||||
result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color);
|
||||
if (result) return [parseInt(result[1] + result[1], 16), parseInt(result[2] + result[2], 16), parseInt(result[3] + result[3], 16)];
|
||||
};
|
||||
|
||||
const luma = (color) => {
|
||||
const rgb = (typeof(color) === "string") ? getRGB(color) : color;
|
||||
return (0.2126 * rgb[0]) + (0.7152 * rgb[1]) + (0.0722 * rgb[2]); // SMPTE C, Rec. 709 weightings
|
||||
};
|
||||
|
||||
const getContrastColor = (color) => {
|
||||
return (luma(color) >= 165) ? "#000" : "#fff";
|
||||
};
|
||||
|
||||
export default class Color extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {value: this.props.value};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
this.setState({value: e.target.value});
|
||||
if (this.props.onChange) this.props.onChange(resolveColor(e.target.value));
|
||||
}
|
||||
|
||||
render() {
|
||||
const intValue = resolveColor(this.state.value, false);
|
||||
const {colors = defaultColors, defaultValue} = this.props;
|
||||
|
||||
|
||||
return <div className="bd-color-picker-container">
|
||||
<div className="bd-color-picker-controls">
|
||||
<TooltipWrapper text="Default" position="bottom">
|
||||
{props => (
|
||||
<div {...props} className="bd-color-picker-default" style={{backgroundColor: resolveColor(defaultValue)}} onClick={() => this.onChange({target: {value: defaultValue}})}>
|
||||
{intValue === resolveColor(defaultValue, false)
|
||||
? <Checkmark width="25" height="25" />
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
</TooltipWrapper>
|
||||
<TooltipWrapper text="Custom Color" position="bottom">
|
||||
{props => (
|
||||
<div className="bd-color-picker-custom">
|
||||
<Dropper color={getContrastColor(resolveColor(this.state.value, true))} />
|
||||
<input {...props} style={{backgroundColor: resolveColor(this.state.value)}} type="color" className="bd-color-picker" value={resolveColor(this.state.value)} onChange={this.onChange} />
|
||||
</div>
|
||||
)}
|
||||
</TooltipWrapper>
|
||||
</div>
|
||||
<div className="bd-color-picker-swatch">
|
||||
{
|
||||
colors.map((int, index) => (
|
||||
<div key={index} className="bd-color-picker-swatch-item" style={{backgroundColor: resolveColor(int)}} onClick={() => this.onChange({target: {value: int}})}>
|
||||
{intValue === int
|
||||
? <Checkmark color={getContrastColor(resolveColor(this.state.value, true))} />
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -2,12 +2,13 @@ import {React} from "modules";
|
|||
|
||||
export default class SettingItem extends React.Component {
|
||||
render() {
|
||||
return <div className={"bd-setting-item"}>
|
||||
return <div className={"bd-setting-item" + (this.props.inline ? " inline" : "")}>
|
||||
<div className={"bd-setting-header"}>
|
||||
<label htmlFor={this.props.id} className={"bd-setting-title"}>{this.props.name}</label>
|
||||
{this.props.children}
|
||||
{this.props.inline && this.props.children}
|
||||
</div>
|
||||
<div className={"bd-setting-note"}>{this.props.note}</div>
|
||||
{!this.props.inline && this.props.children}
|
||||
<div className={"bd-setting-divider"} />
|
||||
</div>;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import {React} from "modules";
|
||||
|
||||
import Keyboard from "../../icons/keyboard";
|
||||
import Close from "../../icons/close";
|
||||
|
||||
export default class Keybind extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {value: this.props.value, isRecording: false};
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.keyHandler = this.keyHandler.bind(this);
|
||||
this.clearKeybind = this.clearKeybind.bind(this);
|
||||
this.accum = [];
|
||||
this.max = this.props.max ?? 2;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("keydown", this.keyHandler);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("keydown", this.keyHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {KeyboardEvent} event
|
||||
*/
|
||||
keyHandler(event) {
|
||||
if (!this.state.isRecording) return;
|
||||
event.stopImmediatePropagation();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
if (event.repeat || this.accum.includes(event.key)) return;
|
||||
|
||||
this.accum.push(event.key);
|
||||
if (this.accum.length == this.max) {
|
||||
if (this.props.onChange) this.props.onChange(this.accum);
|
||||
this.setState({value: this.accum.slice(0), isRecording: false}, () => this.accum.splice(0, this.accum.length));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MouseEvent} e
|
||||
*/
|
||||
onClick(e) {
|
||||
if (e.target?.className?.includes?.("bd-keybind-clear") || e.target?.closest(".bd-button")?.className?.includes("bd-keybind-clear")) return this.clearKeybind(e);
|
||||
this.setState({isRecording: !this.state.isRecording});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
clearKeybind(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.accum.splice(0, this.accum.length);
|
||||
if (this.props.onChange) this.props.onChange(this.accum);
|
||||
this.setState({value: this.accum, isRecording: false});
|
||||
}
|
||||
|
||||
display() {
|
||||
if (this.state.isRecording) return "Recording...";
|
||||
if (!this.state.value.length) return "N/A";
|
||||
return this.state.value.join(" + ");
|
||||
}
|
||||
|
||||
render() {
|
||||
const {clearable = true} = this.props;
|
||||
return <div className={"bd-keybind-wrap" + (this.state.isRecording ? " recording" : "")} onClick={this.onClick}>
|
||||
<input readOnly={true} type="text" className="bd-keybind-input" value={this.display()} />
|
||||
<div className="bd-keybind-controls">
|
||||
<button className={"bd-button bd-keybind-record" + (this.state.isRecording ? " bd-button-danger" : "")}><Keyboard size="24px" /></button>
|
||||
{clearable && <button onClick={this.clearKeybind} className="bd-button bd-keybind-clear"><Close size="24px" /></button>}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import {React} from "modules";
|
||||
|
||||
import RadioIcon from "../../icons/radio";
|
||||
|
||||
export default class Radio extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {value: this.props.options.findIndex(o => o.value === this.props.value)};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
this.renderOption = this.renderOption.bind(this);
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
const index = parseInt(e.target.value);
|
||||
const newValue = this.props.options[index].value;
|
||||
this.setState({value: index});
|
||||
if (this.props.onChange) this.props.onChange(newValue);
|
||||
}
|
||||
|
||||
renderOption(opt, index) {
|
||||
const isSelected = this.state.value === index;
|
||||
return <label className={"bd-radio-option" + (isSelected ? " bd-radio-selected" : "")}>
|
||||
<input onChange={this.onChange} type="radio" name={this.props.name} checked={isSelected} value={index} />
|
||||
{/* <span className="bd-radio-button"></span> */}
|
||||
<RadioIcon className="bd-radio-icon" size="24" checked={isSelected} />
|
||||
<div className="bd-radio-label-wrap">
|
||||
<div className="bd-radio-label">{opt.name}</div>
|
||||
<div className="bd-radio-description">{opt.desc || opt.description}</div>
|
||||
</div>
|
||||
</label>;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="bd-radio-group">
|
||||
{this.props.options.map(this.renderOption)}
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
/* <label class="container">
|
||||
<input type="radio" name="test" checked="checked">
|
||||
<span class="checkmark"></span>
|
||||
<div class="test">One<div class="desc">Description</div></div>
|
||||
</label> */
|
|
@ -0,0 +1,21 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Slider extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {value: this.props.value};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
this.setState({value: e.target.value});
|
||||
// e.target.style.backgroundSize = (e.target.value - this.props.min) * 100 / (this.props.max - this.props.min) + "% 100%";
|
||||
if (this.props.onChange) this.props.onChange(e.target.value);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="bd-slider-wrap">
|
||||
<div className="bd-slider-label">{this.state.value}</div><input onChange={this.onChange} type="range" className="bd-slider-input" min={this.props.min} max={this.props.max} step={this.props.step} value={this.state.value} style={{backgroundSize: (this.state.value - this.props.min) * 100 / (this.props.max - this.props.min) + "% 100%"}} />
|
||||
</div>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import {React} from "modules";
|
||||
|
||||
export default class Textbox extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {value: this.props.value};
|
||||
this.onChange = this.onChange.bind(this);
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
this.setState({value: e.target.value});
|
||||
if (this.props.onChange) this.props.onChange(e.target.value);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <input onChange={this.onChange} onKeyDown={this.props.onKeyDown} type="text" className="bd-text-input" placeholder={this.props.placeholder} maxLength={this.props.maxLength} value={this.state.value} />;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,11 @@ import Switch from "./components/switch";
|
|||
import Dropdown from "./components/dropdown";
|
||||
import Number from "./components/number";
|
||||
import Item from "./components/item";
|
||||
import Textbox from "./components/textbox";
|
||||
import Slider from "./components/slider";
|
||||
import Radio from "./components/radio";
|
||||
import Keybind from "./components/keybind";
|
||||
import Color from "./components/color";
|
||||
|
||||
const baseClassName = "bd-settings-group";
|
||||
|
||||
|
@ -64,8 +69,13 @@ export default class Group extends React.Component {
|
|||
if (setting.type == "dropdown") component = <Dropdown disabled={setting.disabled} id={setting.id} options={setting.options} value={setting.value} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "number") component = <Number disabled={setting.disabled} id={setting.id} min={setting.min} max={setting.max} step={setting.step} value={setting.value} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "switch") component = <Switch disabled={setting.disabled} id={setting.id} checked={setting.value} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "text") component = <Textbox disabled={setting.disabled} id={setting.id} value={setting.value} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "slider") component = <Slider disabled={setting.disabled} id={setting.id} min={setting.min} max={setting.max} step={setting.step} value={setting.value} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "radio") component = <Radio disabled={setting.disabled} id={setting.id} name={setting.id} options={setting.options} value={setting.value} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "keybind") component = <Keybind disabled={setting.disabled} id={setting.id} value={setting.value} max={setting.max} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (setting.type == "color") component = <Color disabled={setting.disabled} id={setting.id} value={setting.value} defaultValue={setting.defaultValue} colors={setting.colors} onChange={this.onChange.bind(this, setting.id)} />;
|
||||
if (!component) return null;
|
||||
return <Item id={setting.id} key={setting.id} name={setting.name} note={setting.note}>{component}</Item>;
|
||||
return <Item id={setting.id} inline={setting.type !== "radio"} key={setting.id} name={setting.name} note={setting.note}>{component}</Item>;
|
||||
})}
|
||||
</div>
|
||||
{this.props.showDivider && <Divider />}
|
||||
|
|
Loading…
Reference in New Issue