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,41 @@
|
||||
import React from "react";
|
||||
|
||||
import { ThemeButton } from "../ThemeButton";
|
||||
|
||||
//Eventually remove and use character-components/Button/RemoveButton with theme override when needed
|
||||
|
||||
interface Props {
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
styledDisabled?: boolean;
|
||||
block?: boolean;
|
||||
size?: string;
|
||||
style?: string;
|
||||
active?: boolean;
|
||||
stopPropagation?: boolean;
|
||||
enableConfirm?: boolean;
|
||||
isInteractive?: boolean;
|
||||
}
|
||||
export default class RemoveButton extends React.PureComponent<Props, {}> {
|
||||
static defaultProps = {
|
||||
size: "small",
|
||||
style: "outline",
|
||||
stopPropagation: true,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { className, children, ...restProps } = this.props;
|
||||
|
||||
let classNames: Array<string> = ["ct-remove-button"];
|
||||
if (className) {
|
||||
classNames.push(className);
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeButton {...restProps} className={classNames.join(" ")}>
|
||||
{children ? children : "Delete"}
|
||||
</ThemeButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import RemoveButton from "./RemoveButton";
|
||||
|
||||
export default RemoveButton;
|
||||
export { RemoveButton };
|
||||
@@ -0,0 +1,52 @@
|
||||
import React from "react";
|
||||
|
||||
import { Button } from "@dndbeyond/character-components/es";
|
||||
|
||||
interface Props {
|
||||
onClick?: ((evt: React.MouseEvent<HTMLButtonElement>) => void) | (() => void);
|
||||
className: string;
|
||||
disabled?: boolean;
|
||||
styledDisabled?: boolean;
|
||||
block?: boolean;
|
||||
size?: string;
|
||||
style?: string;
|
||||
active?: boolean;
|
||||
stopPropagation?: boolean;
|
||||
enableConfirm?: boolean;
|
||||
confirmText?: string;
|
||||
confirmDuration?: number;
|
||||
isInteractive?: boolean;
|
||||
}
|
||||
export default class ThemeButton extends React.PureComponent<Props, {}> {
|
||||
static defaultProps = {
|
||||
className: "",
|
||||
style: "filled",
|
||||
stopPropagation: true,
|
||||
isInteractive: true,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { style, className, isInteractive, ...restProps } = this.props;
|
||||
|
||||
let classNames: Array<string> = ["ct-theme-button"];
|
||||
if (className) {
|
||||
classNames.push(className);
|
||||
}
|
||||
if (style) {
|
||||
classNames.push(`ct-theme-button--${style}`);
|
||||
} else {
|
||||
classNames.push("ct-theme-button--filled");
|
||||
}
|
||||
if (isInteractive) {
|
||||
classNames.push("ct-theme-button--interactive");
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...restProps}
|
||||
className={classNames.join(" ")}
|
||||
isInteractive={isInteractive}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import ThemeButton from "./ThemeButton";
|
||||
|
||||
export default ThemeButton;
|
||||
export { ThemeButton };
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import List from "@mui/material/List";
|
||||
import ListSubheader from "@mui/material/ListSubheader";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Popover from "@mui/material/Popover";
|
||||
import React from "react";
|
||||
|
||||
import { GroupedMenuOption } from "@dndbeyond/character-rules-engine/es";
|
||||
|
||||
import { ThemeButton } from "..";
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode;
|
||||
groupedOptions: Array<GroupedMenuOption>;
|
||||
className?: string;
|
||||
size?: string;
|
||||
buttonStyle?: string;
|
||||
showSingleOption?: boolean;
|
||||
onSelect: (containerDefinitionKey: string) => void;
|
||||
containerEl?: HTMLElement;
|
||||
}
|
||||
|
||||
export const ThemeButtonWithMenu: React.FC<Props> = ({
|
||||
className,
|
||||
children,
|
||||
groupedOptions,
|
||||
size = "small",
|
||||
buttonStyle = "filled",
|
||||
onSelect,
|
||||
showSingleOption = false,
|
||||
containerEl,
|
||||
}) => {
|
||||
const singleOption =
|
||||
groupedOptions.length === 1 &&
|
||||
groupedOptions[0].options?.length === 1 &&
|
||||
!showSingleOption;
|
||||
|
||||
const classNames = [className, "ct-theme-button-with-menu"];
|
||||
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
|
||||
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
if (!singleOption) {
|
||||
setAnchorEl(event.currentTarget);
|
||||
} else if (singleOption) {
|
||||
onSelect(groupedOptions[0].options[0]?.value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = (value?: string) => {
|
||||
if (value) {
|
||||
onSelect(value);
|
||||
}
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<span className={classNames.join(" ")}>
|
||||
<ThemeButton
|
||||
size={size}
|
||||
style={buttonStyle}
|
||||
onClick={(evt: React.MouseEvent<HTMLButtonElement>) => {
|
||||
handleClick(evt);
|
||||
}}
|
||||
data-testid={`theme-button-with-menu-${children?.toString()}`
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, "-")}
|
||||
>
|
||||
{children}{" "}
|
||||
{!singleOption && (
|
||||
<KeyboardArrowDownIcon sx={{ width: 8, height: 8 }} />
|
||||
)}
|
||||
</ThemeButton>
|
||||
{!singleOption && (
|
||||
<Popover
|
||||
anchorEl={anchorEl}
|
||||
container={containerEl}
|
||||
keepMounted
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={(evt: any) => {
|
||||
evt.stopPropagation();
|
||||
evt.nativeEvent?.stopImmediatePropagation();
|
||||
|
||||
handleClose();
|
||||
}}
|
||||
>
|
||||
<List
|
||||
sx={{ bgcolor: "background.default" }}
|
||||
onClick={(evt) => {
|
||||
evt.nativeEvent?.stopImmediatePropagation();
|
||||
evt.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{groupedOptions.map((groupedOption) => (
|
||||
<React.Fragment key={groupedOption.label}>
|
||||
<ListSubheader
|
||||
sx={{
|
||||
lineHeight: "32px",
|
||||
textTransform: "uppercase",
|
||||
fontWeight: 700,
|
||||
fontSize: "12px",
|
||||
bgcolor: "background.default",
|
||||
}}
|
||||
data-testid={`theme-button-with-menu-subheader-${groupedOption.label}`
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, "-")}
|
||||
>
|
||||
{groupedOption.label}
|
||||
</ListSubheader>
|
||||
{groupedOption.options.map((option) => {
|
||||
return (
|
||||
<MenuItem
|
||||
sx={{ bgcolor: "background.default" }}
|
||||
key={option.value}
|
||||
onClick={(evt) => {
|
||||
evt.stopPropagation();
|
||||
evt.nativeEvent?.stopImmediatePropagation();
|
||||
handleClose(option.value);
|
||||
}}
|
||||
data-testid={`theme-button-with-menu-item-${option.label}`
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, "-")}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</List>
|
||||
</Popover>
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeButtonWithMenu;
|
||||
@@ -0,0 +1,4 @@
|
||||
import { ThemeButtonWithMenu } from "./ThemeButtonWithMenu";
|
||||
|
||||
export default ThemeButtonWithMenu;
|
||||
export { ThemeButtonWithMenu };
|
||||
Reference in New Issue
Block a user