PromucFlow_constructor/app/client/src/components/propertyControls/IconSelectControl.tsx

206 lines
5.2 KiB
TypeScript
Raw Normal View History

import * as React from "react";
import styled, { createGlobalStyle } from "styled-components";
import { Alignment, Button, Classes, Menu, MenuItem } from "@blueprintjs/core";
import { IconName, IconNames } from "@blueprintjs/icons";
import { ItemListRenderer, ItemRenderer, Select } from "@blueprintjs/select";
import BaseControl, { ControlProps } from "./BaseControl";
import TooltipComponent from "components/ads/Tooltip";
const IconSelectContainerStyles = createGlobalStyle<{
targetWidth: number | undefined;
}>`
.bp3-select-popover {
width: ${({ targetWidth }) => targetWidth}px;
.bp3-input-group {
margin: 5px !important;
}
}
`;
const StyledButton = styled(Button)`
box-shadow: none !important;
border: none !important;
border-radius: 0;
background-color: #ffffff !important;
> span.bp3-icon-caret-down {
color: rgb(169, 167, 167);
}
`;
const StyledMenu = styled(Menu)`
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: minmax(50px, auto);
gap: 8px;
max-height: 170px !important;
padding-left: 5px !important;
padding-right: 5px !important;
&::-webkit-scrollbar {
width: 8px;
background-color: #eeeeee;
}
&::-webkit-scrollbar-thumb {
border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
background-color: #939090;
}
`;
const StyledMenuItem = styled(MenuItem)`
flex-direction: column;
align-items: center;
padding: 13px 5px;
&:active,
&:hover,
&.bp3-active {
background-color: #eeeeee !important;
}
> span.bp3-icon {
margin-right: 0;
color: #939090 !important;
}
> div {
width: 100%;
text-align: center;
color: #939090 !important;
}
`;
export interface IconSelectControlProps extends ControlProps {
propertyValue?: IconName;
}
export interface IconSelectControlState {
popoverTargetWidth: number | undefined;
}
const NONE = "(none)";
type IconType = IconName | typeof NONE;
const ICON_NAMES = Object.keys(IconNames).map<IconType>(
(name: string) => IconNames[name as keyof typeof IconNames],
);
ICON_NAMES.push(NONE);
const TypedSelect = Select.ofType<IconType>();
class IconSelectControl extends BaseControl<
IconSelectControlProps,
IconSelectControlState
> {
private iconSelectTargetRef: React.RefObject<HTMLButtonElement>;
private timer?: number;
constructor(props: IconSelectControlProps) {
super(props);
this.iconSelectTargetRef = React.createRef();
this.state = { popoverTargetWidth: 0 };
}
componentDidMount() {
this.timer = setTimeout(() => {
const iconSelectTargetElement = this.iconSelectTargetRef.current;
console.log(
`target width: => `,
iconSelectTargetElement?.getBoundingClientRect().width,
);
this.setState((prevState: IconSelectControlState) => {
return {
...prevState,
popoverTargetWidth: iconSelectTargetElement?.getBoundingClientRect()
.width,
};
});
}, 0);
}
componentWillUnmount() {
if (this.timer) {
clearTimeout(this.timer);
}
}
public render() {
const { propertyValue: iconName } = this.props;
const { popoverTargetWidth } = this.state;
return (
<>
<IconSelectContainerStyles targetWidth={popoverTargetWidth} />
<TypedSelect
className="icon-select-container"
itemListRenderer={this.renderMenu}
itemPredicate={this.filterIconName}
itemRenderer={this.renderIconItem}
items={ICON_NAMES}
noResults={<MenuItem disabled text="No results" />}
onItemSelect={this.handleIconChange}
popoverProps={{ minimal: true }}
>
<StyledButton
alignText={Alignment.LEFT}
className={Classes.TEXT_OVERFLOW_ELLIPSIS}
elementRef={this.iconSelectTargetRef}
fill
icon={iconName}
rightIcon="caret-down"
text={iconName || NONE}
/>
</TypedSelect>
</>
);
}
private renderMenu: ItemListRenderer<IconType> = ({
items,
itemsParentRef,
renderItem,
}) => {
const renderedItems = items.map(renderItem).filter((item) => item != null);
return <StyledMenu ulRef={itemsParentRef}>{renderedItems}</StyledMenu>;
};
private renderIconItem: ItemRenderer<IconName | typeof NONE> = (
icon,
{ handleClick, modifiers },
) => {
if (!modifiers.matchesPredicate) {
return null;
}
return (
<TooltipComponent content={icon}>
<StyledMenuItem
active={modifiers.active}
icon={icon === NONE ? undefined : icon}
key={icon}
onClick={handleClick}
text={icon === NONE ? NONE : undefined}
/>
</TooltipComponent>
);
};
private filterIconName = (
query: string,
iconName: IconName | typeof NONE,
) => {
if (iconName === NONE || query === "") {
return true;
}
return iconName.toLowerCase().indexOf(query.toLowerCase()) >= 0;
};
private handleIconChange = (icon: IconType) =>
this.updateProperty(
this.props.propertyName,
icon === NONE ? undefined : icon,
);
static getControlType() {
return "ICON_SELECT";
}
}
export default IconSelectControl;