Convert settings types

This commit is contained in:
Zack Rauen 2023-03-06 19:17:28 -05:00
parent 2976a4cf00
commit 0d57129de7
11 changed files with 212 additions and 296 deletions

View File

@ -1,27 +1,23 @@
import {React} from "modules";
export default class Checkbox extends React.Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
this.state = {checked: this.props.checked || false};
}
const {useState, useCallback} = React;
render() {
return <div className="checkbox-item">
<div className="checkbox-label label-JWQiNe da-label">{this.props.text}</div>
<div className="checkbox-wrapper checkbox-3kaeSU da-checkbox checkbox-3EVISJ da-checkbox" onClick={this.onClick}>
export default function Checkbox(props) {
const [checked, setChecked] = useState(props.checked);
const onClick = useCallback(() => {
props?.onChange(!checked);
setChecked(!checked);
}, [checked]);
return <div className="checkbox-item">
<div className="checkbox-label label-JWQiNe da-label">{props.text}</div>
<div className="checkbox-wrapper checkbox-3kaeSU da-checkbox checkbox-3EVISJ da-checkbox" onClick={onClick}>
<div className="checkbox-inner checkboxInner-3yjcPe da-checkboxInner">
<input className="checkbox checkboxElement-1qV33p da-checkboxElement" checked={this.state.checked} type="checkbox" />
<input className="checkbox checkboxElement-1qV33p da-checkboxElement" checked={checked} type="checkbox" />
<span></span>
</div>
<span></span>
</div>
</div>;
}
onClick() {
this.props.onChange(!this.state.checked);
this.setState({checked: !this.state.checked});
}
}

View File

@ -1,5 +1,8 @@
import {DiscordModules, React} from "modules";
const {useState, useCallback} = React;
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" />
@ -28,7 +31,6 @@ const resolveColor = (color, hex = true) => {
}
};
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])];
@ -52,56 +54,47 @@ 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));
}
export default function Color({value: initialValue, onChange, colors = defaultColors, defaultValue}) {
const [value, setValue] = useState(initialValue);
const change = useCallback((e) => {
onChange?.(resolveColor(e.target.value));
setValue(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">
<DiscordModules.Tooltip 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>
)}
</DiscordModules.Tooltip>
<DiscordModules.Tooltip 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>
)}
</DiscordModules.Tooltip>
</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>;
}
}
const intValue = resolveColor(value, false);
return <div className="bd-color-picker-container">
<div className="bd-color-picker-controls">
<DiscordModules.Tooltip text="Default" position="bottom">
{props => (
<div {...props} className="bd-color-picker-default" style={{backgroundColor: resolveColor(defaultValue)}} onClick={() => change({target: {value: defaultValue}})}>
{intValue === resolveColor(defaultValue, false)
? <Checkmark width="25" height="25" />
: null
}
</div>
)}
</DiscordModules.Tooltip>
<DiscordModules.Tooltip text="Custom Color" position="bottom">
{props => (
<div className="bd-color-picker-custom">
<Dropper color={getContrastColor(resolveColor(value, true))} />
<input {...props} style={{backgroundColor: resolveColor(value)}} type="color" className="bd-color-picker" value={resolveColor(value)} onChange={change} />
</div>
)}
</DiscordModules.Tooltip>
</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={() => change({target: {value: int}})}>
{intValue === int
? <Checkmark color={getContrastColor(resolveColor(value, true))} />
: null
}
</div>
))
}
</div>
</div>;
}

View File

@ -1,56 +1,46 @@
import {React} from "modules";
import Arrow from "../../icons/downarrow";
export default class Select extends React.Component {
constructor(props) {
super(props);
this.state = {open: false, value: this.props.hasOwnProperty("value") ? this.props.value : this.props.options[0].value};
this.dropdown = React.createRef();
this.onChange = this.onChange.bind(this);
this.showMenu = this.showMenu.bind(this);
this.hideMenu = this.hideMenu.bind(this);
}
const {useState, useCallback} = React;
showMenu(event) {
export default function Select({value: initialValue, options, style, onChange}) {
const [value, setValue] = useState(initialValue ?? options[0].value);
const change = useCallback((val) => {
onChange?.(val);
setValue(val);
}, []);
const hideMenu = useCallback(() => {
setOpen(false);
document.removeEventListener("click", hideMenu);
}, []);
const [open, setOpen] = useState(false);
const showMenu = useCallback((event) => {
event.preventDefault();
event.stopPropagation();
this.setState((state) => ({open: !state.open}), () => {
if (!this.state.open) return;
const next = !open;
setOpen(next);
if (!next) return;
document.addEventListener("click", hideMenu);
}, [open]);
document.addEventListener("click", this.hideMenu);
});
}
hideMenu() {
this.setState({open: false}, () => {
document.removeEventListener("click", this.hideMenu);
});
}
onChange(value) {
this.setState({value});
if (this.props.onChange) this.props.onChange(value);
}
get selected() {return this.props.options.find(o => o.value == this.state.value);}
get options() {
const selected = this.selected;
return <div className="bd-select-options">
{this.props.options.map(opt =>
<div className={`bd-select-option${selected.value == opt.value ? " selected" : ""}`} onClick={this.onChange.bind(this, opt.value)}>{opt.label}</div>
const selected = options.find(o => o.value == value);
const optionComponents = <div className="bd-select-options">
{options.map(opt =>
<div className={`bd-select-option${selected.value == opt.value ? " selected" : ""}`} onClick={() => change(opt.value)}>{opt.label}</div>
)}
</div>;
}
render() {
const style = this.props.style == "transparent" ? " bd-select-transparent" : "";
const isOpen = this.state.open ? " menu-open" : "";
return <div className={`bd-select${style}${isOpen}`} onClick={this.showMenu} ref={this.dropdown}>
<div className="bd-select-value">{this.selected.label}</div>
<Arrow className="bd-select-arrow" />
{this.state.open && this.options}
</div>;
}
const styleClass = style == "transparent" ? " bd-select-transparent" : "";
const isOpen = open ? " menu-open" : "";
return <div className={`bd-select${styleClass}${isOpen}`} onClick={showMenu}>
<div className="bd-select-value">{selected.label}</div>
<Arrow className="bd-select-arrow" />
{open && optionComponents}
</div>;
}

View File

@ -1,15 +1,13 @@
import {React} from "modules";
export default class SettingItem extends React.Component {
render() {
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.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>;
}
export default function SettingItem({id, name, note, inline, children}) {
return <div className={"bd-setting-item" + (inline ? " inline" : "")}>
<div className={"bd-setting-header"}>
<label htmlFor={id} className={"bd-setting-title"}>{name}</label>
{inline && children}
</div>
<div className={"bd-setting-note"}>{note}</div>
{!inline && children}
<div className={"bd-setting-divider"} />
</div>;
}

View File

@ -3,78 +3,50 @@ 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;
}
const {useState, useCallback, useEffect} = React;
componentDidMount() {
window.addEventListener("keydown", this.keyHandler);
}
componentWillUnmount() {
window.removeEventListener("keydown", this.keyHandler);
}
export default function Keybind({value: initialValue, onChange, max = 2, clearable = true}) {
const [state, setState] = useState({value: initialValue, isRecording: false, accum: []});
/**
*
* @param {KeyboardEvent} event
*/
keyHandler(event) {
if (!this.state.isRecording) return;
useEffect(() => {
window.addEventListener("keydown", keyHandler);
return () => window.removeEventListener("keydown", keyHandler);
});
const keyHandler = useCallback((event) => {
if (!state.isRecording) return;
event.stopImmediatePropagation();
event.stopPropagation();
event.preventDefault();
if (event.repeat || this.accum.includes(event.key)) return;
if (event.repeat || state.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));
state.accum.push(event.key);
if (state.accum.length == max) {
if (onChange) onChange(state.accum);
setState({value: state.accum.slice(0), isRecording: false, accum: []});
}
}
}, [state]);
/**
*
* @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});
}
const onClick = useCallback((e) => {
if (e.target?.className?.includes?.("bd-keybind-clear") || e.target?.closest(".bd-button")?.className?.includes("bd-keybind-clear")) return clearKeybind(e);
setState({...state, isRecording: !state.isRecording});
}, [state]);
/**
*
* @param {MouseEvent} event
*/
clearKeybind(event) {
const clearKeybind = useCallback((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});
}
if (onChange) onChange([]);
setState({...state, value: [], accum: []});
}, []);
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>;
}
const displayValue = state.isRecording ? "Recording..." : !state.value.length ? "N/A" : state.value.join(" + ");
return <div className={"bd-keybind-wrap" + (state.isRecording ? " recording" : "")} onClick={onClick}>
<input readOnly={true} type="text" className="bd-keybind-input" value={displayValue} />
<div className="bd-keybind-controls">
<button className={"bd-button bd-keybind-record" + (state.isRecording ? " bd-button-danger" : "")}><Keyboard size="24px" /></button>
{clearable && <button onClick={clearKeybind} className="bd-button bd-keybind-clear"><Close size="24px" /></button>}
</div>
</div>;
}

View File

@ -1,18 +1,14 @@
import {React} from "modules";
export default class Number extends React.Component {
constructor(props) {
super(props);
this.state = {value: this.props.value};
this.onChange = this.onChange.bind(this);
}
const {useState, useCallback} = React;
onChange(e) {
this.setState({value: e.target.value});
if (this.props.onChange) this.props.onChange(e.target.value);
}
render() {
return <input onChange={this.onChange} type="number" className="bd-number-input" min={this.props.min} max={this.props.max} step={this.props.step} value={this.state.value} />;
}
export default function Number({value: initialValue, min, max, step, onChange}) {
const [value, setValue] = useState(initialValue);
const change = useCallback((e) => {
onChange?.(e.target.value);
setValue(e.target.value);
}, []);
return <input onChange={change} type="number" className="bd-number-input" min={min} max={max} step={step} value={value} />;
}

View File

@ -2,25 +2,22 @@ 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);
}
const {useState, useCallback} = React;
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;
export default function Radio({name, value, options, onChange}) {
const [index, setIndex] = useState(options.findIndex(o => o.value === value));
const change = useCallback((e) => {
const newIndex = parseInt(e.target.value);
const newValue = options[newIndex].value;
onChange?.(newValue);
setIndex(newIndex);
}, [index, options]);
function renderOption(opt, i) {
const isSelected = index === i;
return <label className={"bd-radio-option" + (isSelected ? " bd-radio-selected" : "")}>
<input onChange={this.onChange} type="radio" name={this.props.name} checked={isSelected} value={index} />
<input onChange={change} type="radio" name={name} checked={isSelected} value={i} />
{/* <span className="bd-radio-button"></span> */}
<RadioIcon className="bd-radio-icon" size="24" checked={isSelected} />
<div className="bd-radio-label-wrap">
@ -30,15 +27,5 @@ export default class Radio extends React.Component {
</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> */
return <div className="bd-radio-group">{options.map(renderOption)}</div>;
}

View File

@ -1,22 +1,20 @@
import {React} from "modules";
import SearchIcon from "../../icons/search";
export default class Search extends React.Component {
constructor(props) {
super(props);
this.state = {value: this.props.value};
this.onChange = this.onChange.bind(this);
}
const {useState, useCallback} = React;
onChange(e) {
this.setState({value: e.target.value});
if (this.props.onChange) this.props.onChange(e);
}
render() {
return <div className={"bd-search-wrapper" + (this.props.className ? ` ${this.props.className}` : "")}>
<input onChange={this.onChange} onKeyDown={this.props.onKeyDown} type="text" className="bd-search" placeholder={this.props.placeholder} maxLength="50" value={this.state.value} />
<SearchIcon />
</div>;
}
export default function Search({onChange, className, onKeyDown, placeholder}) {
const [value, setValue] = useState("");
const change = useCallback((e) => {
onChange?.(e);
setValue(e.target.value);
}, []);
return <div className={"bd-search-wrapper" + (className ? ` ${className}` : "")}>
<input onChange={change} onKeyDown={onKeyDown} type="text" className="bd-search" placeholder={placeholder} maxLength="50" value={value} />
<SearchIcon />
</div>;
}

View File

@ -1,21 +1,16 @@
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);
}
const {useState, useCallback} = React;
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>;
}
export default function Slider({value: initialValue, min, max, step, onChange}) {
const [value, setValue] = useState(initialValue);
const change = useCallback((e) => {
onChange?.(e.target.value);
setValue(e.target.value);
}, []);
return <div className="bd-slider-wrap">
<div className="bd-slider-label">{value}</div><input onChange={change} type="range" className="bd-slider-input" min={min} max={max} step={step} value={value} style={{backgroundSize: (value - min) * 100 / (max - min) + "% 100%"}} />
</div>;
}

View File

@ -1,32 +1,27 @@
import {React} from "modules";
export default class Switch extends React.Component {
constructor(props) {
super(props);
this.state = {checked: this.props.checked};
this.onChange = this.onChange.bind(this);
}
const {useState, useCallback} = React;
onChange() {
if (this.props.disabled) return;
this.props.onChange(!this.state.checked);
this.setState({checked: !this.state.checked});
}
render() {
const enabledClass = this.props.disabled ? " bd-switch-disabled" : "";
const checkedClass = this.state.checked ? " bd-switch-checked" : "";
return <div className={`bd-switch` + enabledClass + checkedClass}>
<input id={this.props.id} type="checkbox" disabled={this.props.disabled} checked={this.state.checked} onChange={this.onChange} />
<div className="bd-switch-body">
<svg className="bd-switch-slider" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet">
<rect className="bd-switch-handle" fill="white" x="4" y="0" height="20" width="20" rx="10"></rect>
<svg className="bd-switch-symbol" viewBox="0 0 20 20" fill="none">
<path></path>
<path></path>
</svg>
export default function Switch({id, checked: initialValue, disabled, onChange}) {
const [checked, setChecked] = useState(initialValue);
const change = useCallback(() => {
onChange?.(!checked);
setChecked(!checked);
}, [checked]);
const enabledClass = disabled ? " bd-switch-disabled" : "";
const checkedClass = checked ? " bd-switch-checked" : "";
return <div className={`bd-switch` + enabledClass + checkedClass}>
<input id={id} type="checkbox" disabled={disabled} checked={checked} onChange={change} />
<div className="bd-switch-body">
<svg className="bd-switch-slider" viewBox="0 0 28 20" preserveAspectRatio="xMinYMid meet">
<rect className="bd-switch-handle" fill="white" x="4" y="0" height="20" width="20" rx="10"></rect>
<svg className="bd-switch-symbol" viewBox="0 0 20 20" fill="none">
<path></path>
<path></path>
</svg>
</div>
</div>;
}
</svg>
</div>
</div>;
}

View File

@ -1,18 +1,14 @@
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);
}
const {useState, useCallback} = React;
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} />;
}
export default function Textbox({value: initialValue, maxLength, placeholder, onKeyDown, onChange}) {
const [value, setValue] = useState(initialValue);
const change = useCallback((e) => {
onChange?.(e.target.value);
setValue(e.target.value);
}, []);
return <input onChange={change} onKeyDown={onKeyDown} type="text" className="bd-text-input" placeholder={placeholder} maxLength={maxLength} value={value} />;
}