Grabbed dndbeyond's source code
``` ~/go/bin/sourcemapper -output ddb -jsurl https://media.dndbeyond.com/character-app/static/js/main.90aa78c5.js ```
This commit is contained in:
@@ -0,0 +1,242 @@
|
||||
import React from "react";
|
||||
|
||||
export interface ButtonProps {
|
||||
onClick?: ((evt: React.MouseEvent) => void) | (() => void);
|
||||
clsNames?: Array<string>;
|
||||
className: string;
|
||||
disabled?: boolean;
|
||||
styledDisabled: boolean;
|
||||
block?: boolean;
|
||||
size?: string;
|
||||
style?: string;
|
||||
active?: boolean;
|
||||
stopPropagation: boolean;
|
||||
enableConfirm: boolean;
|
||||
confirmText: string;
|
||||
confirmDuration: number;
|
||||
confirmSuccessDuration: number;
|
||||
isInteractive: boolean;
|
||||
role: string;
|
||||
}
|
||||
interface ButtonState {
|
||||
isConfirming: boolean;
|
||||
isConfirmed: boolean;
|
||||
currentDuration: number;
|
||||
confirmText: string;
|
||||
}
|
||||
export class Button extends React.PureComponent<ButtonProps, ButtonState> {
|
||||
static defaultProps = {
|
||||
className: "",
|
||||
styledDisabled: false,
|
||||
stopPropagation: true,
|
||||
enableConfirm: false,
|
||||
confirmText: "Confirm",
|
||||
confirmDuration: 3000,
|
||||
confirmSuccessDuration: 1000,
|
||||
isInteractive: true,
|
||||
role: "button",
|
||||
};
|
||||
|
||||
confirmIntervalId: number;
|
||||
confirmSuccessTimeoutId: number;
|
||||
|
||||
constructor(props: ButtonProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isConfirming: false,
|
||||
isConfirmed: false,
|
||||
currentDuration: 0,
|
||||
confirmText: "",
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.confirmSuccessTimeoutId);
|
||||
clearInterval(this.confirmIntervalId);
|
||||
}
|
||||
|
||||
handleConfirmClick = (evt: React.MouseEvent<HTMLButtonElement>): void => {
|
||||
const { isConfirming, isConfirmed } = this.state;
|
||||
const {
|
||||
confirmText,
|
||||
stopPropagation,
|
||||
confirmDuration,
|
||||
confirmSuccessDuration,
|
||||
onClick,
|
||||
} = this.props;
|
||||
|
||||
if (stopPropagation) {
|
||||
evt.stopPropagation();
|
||||
evt.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
if (isConfirmed) {
|
||||
this.setState({
|
||||
isConfirmed: false,
|
||||
});
|
||||
clearTimeout(this.confirmSuccessTimeoutId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isConfirming) {
|
||||
this.setState({
|
||||
isConfirming: false,
|
||||
currentDuration: 0,
|
||||
isConfirmed: true,
|
||||
});
|
||||
if (onClick) {
|
||||
onClick(evt);
|
||||
}
|
||||
clearInterval(this.confirmIntervalId);
|
||||
this.confirmSuccessTimeoutId = window.setTimeout(() => {
|
||||
this.setState({
|
||||
isConfirmed: false,
|
||||
});
|
||||
}, confirmSuccessDuration);
|
||||
} else {
|
||||
this.setState({
|
||||
isConfirming: true,
|
||||
confirmText: `${confirmText} (${Math.round(confirmDuration / 1000)})`,
|
||||
currentDuration: 1000,
|
||||
});
|
||||
|
||||
this.confirmIntervalId = window.setInterval(() => {
|
||||
if (this.state.currentDuration >= confirmDuration) {
|
||||
clearInterval(this.confirmIntervalId);
|
||||
this.setState({
|
||||
isConfirming: false,
|
||||
currentDuration: 0,
|
||||
});
|
||||
} else {
|
||||
this.setState((prevState) => ({
|
||||
confirmText: `${confirmText} (${Math.round(
|
||||
(confirmDuration - prevState.currentDuration) / 1000
|
||||
)})`,
|
||||
currentDuration: prevState.currentDuration + 1000,
|
||||
}));
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = (evt: React.MouseEvent<HTMLButtonElement>): void => {
|
||||
const { onClick, stopPropagation, isInteractive } = this.props;
|
||||
|
||||
if (onClick && isInteractive) {
|
||||
if (stopPropagation) {
|
||||
evt.stopPropagation();
|
||||
evt.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
onClick(evt);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isConfirming, isConfirmed, confirmText } = this.state;
|
||||
const {
|
||||
children,
|
||||
className,
|
||||
clsNames,
|
||||
disabled,
|
||||
styledDisabled,
|
||||
size,
|
||||
block,
|
||||
active,
|
||||
style,
|
||||
enableConfirm,
|
||||
role,
|
||||
confirmSuccessDuration, // to be ignored on props spread
|
||||
confirmDuration, // to be ignored on props spread
|
||||
confirmText: _confirmText, // to be ignored on props spread
|
||||
isInteractive, // to be ignored on props spread
|
||||
stopPropagation, // to be ignored on props spread
|
||||
...restProps
|
||||
} = this.props;
|
||||
|
||||
let buttonBase: string = "character-button";
|
||||
let btnClassNames: Array<string> = [
|
||||
className,
|
||||
"ct-button",
|
||||
buttonBase,
|
||||
"ddbc-button",
|
||||
];
|
||||
|
||||
let buttonSizeBase: string = buttonBase;
|
||||
if (block) {
|
||||
buttonSizeBase += "-block";
|
||||
}
|
||||
|
||||
switch (size) {
|
||||
case "oversized":
|
||||
case "large":
|
||||
case "medium":
|
||||
case "small":
|
||||
btnClassNames.push(`${buttonSizeBase}-${size}`);
|
||||
break;
|
||||
default:
|
||||
btnClassNames.push(`${buttonSizeBase}`);
|
||||
}
|
||||
|
||||
let buttonStyleBase: string = buttonBase;
|
||||
|
||||
switch (style) {
|
||||
case "dark":
|
||||
btnClassNames.push(
|
||||
active ? `${buttonStyleBase}-dark-active` : `${buttonStyleBase}-dark`
|
||||
);
|
||||
break;
|
||||
case "modal":
|
||||
case "modal-cancel":
|
||||
btnClassNames.push(`${buttonStyleBase}-${style}`);
|
||||
break;
|
||||
case "outline":
|
||||
btnClassNames.push(`${buttonStyleBase}-outline`);
|
||||
break;
|
||||
default:
|
||||
if (active) {
|
||||
btnClassNames.push(`${buttonStyleBase}-active`);
|
||||
}
|
||||
}
|
||||
|
||||
if (styledDisabled && disabled) {
|
||||
btnClassNames.push(`${buttonBase}--disabled`);
|
||||
}
|
||||
|
||||
if (clsNames) {
|
||||
btnClassNames = [...btnClassNames, ...clsNames];
|
||||
}
|
||||
|
||||
if (enableConfirm) {
|
||||
btnClassNames.push("ct-button--confirm");
|
||||
|
||||
if (isConfirming) {
|
||||
btnClassNames.push("ct-button--is-confirming");
|
||||
}
|
||||
if (isConfirmed) {
|
||||
btnClassNames.push("ct-button--is-confirmed");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
{...restProps}
|
||||
disabled={!styledDisabled && disabled}
|
||||
onClick={enableConfirm ? this.handleConfirmClick : this.handleClick}
|
||||
className={btnClassNames.join(" ")}
|
||||
role={role}
|
||||
>
|
||||
<span className="ct-button__content">{children}</span>
|
||||
{enableConfirm && (
|
||||
<React.Fragment>
|
||||
<span className="ct-button__confirming">{confirmText}</span>
|
||||
<span className="ct-button__confirmed" />
|
||||
</React.Fragment>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Button;
|
||||
@@ -0,0 +1,39 @@
|
||||
import React from "react";
|
||||
|
||||
import Button, { ButtonProps } from "../Button";
|
||||
|
||||
//Duplicated from CharacterToolsClient
|
||||
// ToolsClient implements as a ThemeButton wrapper
|
||||
// eventually RemoveButton usage should be replaced with this one with specialized style overrides
|
||||
|
||||
export interface RemoveButtonProps extends Partial<ButtonProps> {}
|
||||
const RemoveButton: React.FunctionComponent<RemoveButtonProps> = ({
|
||||
size = "small",
|
||||
style = "outline",
|
||||
stopPropagation = true,
|
||||
children,
|
||||
className,
|
||||
...restProps
|
||||
}) => {
|
||||
let classNames: Array<string> = [
|
||||
"ddbc-remove-button",
|
||||
"character-button-remove",
|
||||
];
|
||||
if (className) {
|
||||
classNames.push(className);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...restProps}
|
||||
size={size}
|
||||
style={style}
|
||||
stopPropagation={stopPropagation}
|
||||
className={classNames.join(" ")}
|
||||
>
|
||||
{children ? children : "Remove"}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default RemoveButton;
|
||||
@@ -0,0 +1,4 @@
|
||||
import RemoveButton from "./RemoveButton";
|
||||
|
||||
export default RemoveButton;
|
||||
export { RemoveButton };
|
||||
@@ -0,0 +1,244 @@
|
||||
import React from "react";
|
||||
|
||||
import {
|
||||
ApiAdapterPromise,
|
||||
ApiAdapterUtils,
|
||||
ApiResponse,
|
||||
} from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
interface Props<T extends any = any, TData extends any = any> {
|
||||
loadOptions?: () => ApiAdapterPromise<ApiResponse<Array<TData>>>;
|
||||
clsNames: Array<string>;
|
||||
id?: any; //TODO type?
|
||||
options: Array<any>; //TODO type Options
|
||||
disabled: boolean;
|
||||
value: React.ReactText | null;
|
||||
placeholder: string;
|
||||
onChange?: (value: string) => void;
|
||||
onChangePromise?: (
|
||||
newValue: string,
|
||||
oldValue: string,
|
||||
accept: () => void,
|
||||
reject: () => void
|
||||
) => void;
|
||||
parseLoadedOptions?: (data: Array<TData>) => Array<T>;
|
||||
resetAfterChoice: boolean;
|
||||
initialOptionRemoved: boolean;
|
||||
preventClickPropagating: boolean;
|
||||
isReadonly?: boolean;
|
||||
className: string;
|
||||
}
|
||||
interface State {
|
||||
isLazy: boolean;
|
||||
lazyOptions: Array<any>;
|
||||
value: React.ReactText;
|
||||
resetAfterChoiceValues: Array<string>;
|
||||
}
|
||||
export default class Select extends React.Component<Props, State> {
|
||||
static defaultProps = {
|
||||
clsNames: [],
|
||||
className: "",
|
||||
disabled: false,
|
||||
resetAfterChoice: false,
|
||||
initialOptionRemoved: false,
|
||||
placeholder: "-- Choose an Option --",
|
||||
options: [],
|
||||
preventClickPropagating: false,
|
||||
};
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isLazy: !!props.loadOptions,
|
||||
lazyOptions: [],
|
||||
value: props.value === null ? "" : props.value,
|
||||
resetAfterChoiceValues: this.getResetAfterValueChoices(props),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate(
|
||||
prevProps: Readonly<Props>,
|
||||
prevState: Readonly<State>,
|
||||
snapshot?: any
|
||||
): void {
|
||||
const { value } = this.props;
|
||||
|
||||
if (value !== prevProps.value) {
|
||||
this.setState({
|
||||
value: value === null ? "" : value,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { isLazy } = this.state;
|
||||
const { loadOptions, parseLoadedOptions } = this.props;
|
||||
|
||||
if (isLazy && loadOptions && parseLoadedOptions) {
|
||||
loadOptions().then((response) => {
|
||||
const data = ApiAdapterUtils.getResponseData(response);
|
||||
if (data === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
lazyOptions: parseLoadedOptions(data),
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getResetAfterValueChoices = (props: Props): Array<string> => {
|
||||
const { options, resetAfterChoice } = props;
|
||||
|
||||
if (resetAfterChoice) {
|
||||
return options.reduce((acc, option) => {
|
||||
if (option.options) {
|
||||
acc.push(...option.options.map((opt) => "" + opt.value));
|
||||
} else {
|
||||
acc.push("" + option.value);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return options.reduce((acc, option) => {
|
||||
if (option.options && option.resetAfterChoice) {
|
||||
acc.push(...option.options.map((opt) => "" + opt.value));
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
handleChange = (evt: React.ChangeEvent<HTMLSelectElement>): void => {
|
||||
const { value, resetAfterChoiceValues } = this.state;
|
||||
const { onChangePromise, onChange, resetAfterChoice } = this.props;
|
||||
|
||||
const newValue: string = evt.target.value;
|
||||
|
||||
if (onChangePromise) {
|
||||
onChangePromise(
|
||||
newValue,
|
||||
String(value),
|
||||
() => {
|
||||
this.setState({
|
||||
value: newValue,
|
||||
});
|
||||
|
||||
if (onChange) {
|
||||
onChange(newValue);
|
||||
}
|
||||
|
||||
if (resetAfterChoice) {
|
||||
evt.target.selectedIndex = 0;
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
);
|
||||
} else {
|
||||
if (resetAfterChoice || resetAfterChoiceValues.includes(newValue)) {
|
||||
evt.target.selectedIndex = 0;
|
||||
} else {
|
||||
this.setState({
|
||||
value: newValue,
|
||||
});
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange(newValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleClick = (evt: React.MouseEvent): void => {
|
||||
const { preventClickPropagating } = this.props;
|
||||
|
||||
if (preventClickPropagating) {
|
||||
evt.stopPropagation();
|
||||
evt.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
};
|
||||
|
||||
renderOption = (option: any): React.ReactNode => {
|
||||
return (
|
||||
<option
|
||||
key={option.value}
|
||||
value={String(option.value)}
|
||||
disabled={option.disabled}
|
||||
>
|
||||
{option.label}
|
||||
</option>
|
||||
);
|
||||
};
|
||||
|
||||
renderOptGroup = (
|
||||
label: string,
|
||||
options: Array<any>,
|
||||
resetAfterChoice: boolean
|
||||
) => {
|
||||
if (!options.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<optgroup label={label} key={label}>
|
||||
{options.map((option) => this.renderOption(option))}
|
||||
</optgroup>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { isLazy, lazyOptions, value } = this.state;
|
||||
const {
|
||||
options,
|
||||
placeholder,
|
||||
initialOptionRemoved,
|
||||
id,
|
||||
disabled,
|
||||
clsNames,
|
||||
isReadonly,
|
||||
className,
|
||||
} = this.props;
|
||||
|
||||
let displayOptions: Array<any> = options;
|
||||
if (isLazy) {
|
||||
displayOptions = lazyOptions;
|
||||
}
|
||||
|
||||
const conClassNames: Array<string> = [
|
||||
"ddbc-select",
|
||||
...clsNames,
|
||||
className,
|
||||
];
|
||||
|
||||
return (
|
||||
<select
|
||||
id={id}
|
||||
className={conClassNames.join(" ")}
|
||||
value={value}
|
||||
onChange={this.handleChange}
|
||||
onClick={this.handleClick}
|
||||
disabled={disabled || isReadonly}
|
||||
>
|
||||
<option
|
||||
value=""
|
||||
disabled={initialOptionRemoved}
|
||||
hidden={initialOptionRemoved}
|
||||
>
|
||||
{placeholder}
|
||||
</option>
|
||||
{displayOptions.map((option) => {
|
||||
if (option.options) {
|
||||
return this.renderOptGroup(
|
||||
option.optGroupLabel,
|
||||
option.options,
|
||||
option.resetAfterChoice
|
||||
);
|
||||
} else {
|
||||
return this.renderOption(option);
|
||||
}
|
||||
})}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import Select from "./Select";
|
||||
|
||||
export default Select;
|
||||
export { Select };
|
||||
Reference in New Issue
Block a user