Skip to content

Commit

Permalink
Merge branch 'feature/ant5' of https://github.com/simularium/simulari…
Browse files Browse the repository at this point in the history
…um-website into feature/ant5-styling
  • Loading branch information
interim17 committed Jan 15, 2025
2 parents c9c58b1 + 108f7cc commit 69cd8ef
Show file tree
Hide file tree
Showing 28 changed files with 878 additions and 253 deletions.
16 changes: 9 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/components/AgentTree/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { map, filter, isEmpty } from "lodash";

import {
ChangeAgentsRenderingStateAction,
ApplyUserColorAction,
SetRecentColorsAction,
SetVisibleAction,
AgentRenderingCheckboxMap,
} from "../../state/selection/types";
import { ColorChange } from "../../constants/interfaces";
import SharedCheckbox from "../SharedCheckbox";
import AgentTreeSubmenu from "../AgentTreeSubmenu";
import TreeNode from "../TreeNode";
Expand Down Expand Up @@ -47,7 +47,7 @@ interface AgentTreeProps {
payloadForSelectNone: AgentRenderingCheckboxMap;
isSharedCheckboxIndeterminate: boolean;
recentColors: string[];
applyUserColor: ActionCreator<ApplyUserColorAction>;
applyUserColor: (colorChange: ColorChange) => void;
setRecentColors: ActionCreator<SetRecentColorsAction>;
}
const CHECKBOX_SPAN_NO = 2;
Expand Down
33 changes: 16 additions & 17 deletions src/components/ColorPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import { useDebounce } from "use-debounce";
import { AGENT_COLORS } from "../../containers/ViewerPanel/constants";
import { ColorChange } from "../../constants/interfaces";
import { COLORPICKER_POPOVER_OFFSET } from "../../constants";
import {
ApplyUserColorAction,
SetRecentColorsAction,
} from "../../state/selection/types";
import { SetRecentColorsAction } from "../../state/selection/types";

import styles from "./style.css";

Expand All @@ -21,7 +18,7 @@ interface ColorPickerProps {
agentName: string;
tags: string[];
recentColors: string[];
applyUserColor: ActionCreator<ApplyUserColorAction>;
applyUserColor: (colorChange: ColorChange) => void;
setRecentColors: ActionCreator<SetRecentColorsAction>;
}

Expand All @@ -36,7 +33,7 @@ const ColorPicker = ({
}: ColorPickerProps): JSX.Element => {
const [currentColor, setCurrentColor] = useState(initialColor);
const [debouncedColor] = useDebounce(currentColor, 250);
const isInitialRender = useRef(true);
const isUserInteraction = useRef(false);
const [isColorPickerVisible, setColorPickerVisible] = useState(false);
const [lastSelectedColor, setLastSelectedColor] = useState(initialColor);

Expand All @@ -55,21 +52,25 @@ const ColorPicker = ({
color: newColor.toLowerCase(),
};
applyUserColor(colorChange);
updateRecentColors(newColor.toLowerCase());
};

useEffect(() => {
if (isInitialRender.current) {
isInitialRender.current = false;
} else {
if (isUserInteraction.current) {
handleColorChange(debouncedColor);
updateRecentColors(debouncedColor);
isUserInteraction.current = false;
}
}, [debouncedColor]);

useEffect(() => {
setCurrentColor(initialColor);
}, [initialColor]);

const onColorUpdate = (newColor: string) => {
isUserInteraction.current = true;
setCurrentColor(newColor);
};

const updateRecentColors = (color: string) => {
if (recentColors.includes(color)) {
return;
Expand All @@ -83,15 +84,15 @@ const ColorPicker = ({

const renderColorPickerComponent = () => (
<div className={styles.colorPicker}>
<HexColorPicker color={currentColor} onChange={setCurrentColor} />
<HexColorPicker color={currentColor} onChange={onColorUpdate} />
<div className={styles.selectionDisplay}>
<div className={styles.colorSelections}>
<div className={styles.selection}>
<div
className={styles.largeSwatch}
style={{ backgroundColor: lastSelectedColor }}
onClick={() => {
setCurrentColor(lastSelectedColor);
onColorUpdate(lastSelectedColor);
}}
></div>
<label>
Expand All @@ -112,9 +113,7 @@ const ColorPicker = ({
<HexColorInput
className={styles.hexInput}
color={currentColor}
onChange={(color) =>
setCurrentColor(color.toLowerCase())
}
onChange={(color) => onColorUpdate(color.toLowerCase())}
/>
<label>Hex</label>
</div>
Expand All @@ -136,7 +135,7 @@ const ColorPicker = ({
key={color}
className={styles.swatch}
style={{ background: color }}
onClick={() => setCurrentColor(color)}
onClick={() => onColorUpdate(color)}
/>
</Tooltip>
))}
Expand All @@ -148,7 +147,7 @@ const ColorPicker = ({
key={color}
className={styles.swatch}
style={{ background: color }}
onClick={() => setCurrentColor(color)}
onClick={() => onColorUpdate(color)}
/>
))}
</div>
Expand Down
106 changes: 103 additions & 3 deletions src/components/CustomDropdown/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import React, { ReactNode } from "react";
import React, {
KeyboardEventHandler,
ReactNode,
useEffect,
useRef,
useState,
} from "react";
import { Dropdown, DropDownProps, MenuProps } from "antd";
import { ButtonClass } from "../../constants/interfaces";
import { ButtonClass, DropdownState } from "../../constants/interfaces";
import { DROPDOWN_HOVER_DELAY } from "../../constants";
import NavButton from "../NavButton";

import styles from "./style.css";
Expand All @@ -22,19 +29,112 @@ const CustomDropdown: React.FC<CustomDropdownProps> = ({
placement,
disabled,
}) => {
const [dropdownState, setDropdownState] = useState<DropdownState>(
DropdownState.CLOSED
);

const triggerRef = useRef<HTMLElement | null>(null);
const dropdownRef = useRef<HTMLDivElement | null>(null);
const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);

/**
* Prevents the menu wrapper from capturing focus,
* this prevents losing focus to the body
* when "Escape" is pressed.
*/
useEffect(() => {
const element = dropdownRef.current;
if (element) {
const menuElement = element.querySelector(
".ant-dropdown-menu"
) as HTMLElement;
if (menuElement) {
if (dropdownState === DropdownState.FORCED_OPEN) {
menuElement.setAttribute("tabIndex", "-1");
} else if (dropdownState === DropdownState.CLOSED) {
menuElement.setAttribute("tabIndex", "0");
}
}
}
}, [dropdownRef, dropdownState]);

/**
* Manually handling keydown and hover behavior because
* our focus management overrides the defaults of the antd components.
*/
const openTriggers = new Set(["Enter", " ", "ArrowDown"]);

const handleKeyDown: KeyboardEventHandler<any> = (event) => {
if (event.key === "Escape") {
event.preventDefault();
setDropdownState(DropdownState.CLOSED);
triggerRef.current?.focus();
}
if (
openTriggers.has(event.key) &&
dropdownState !== DropdownState.FORCED_OPEN
) {
event.preventDefault();
if (closeTimeoutRef.current) {
clearTimeout(closeTimeoutRef.current);
}
setDropdownState(DropdownState.FORCED_OPEN); // Opened by keyboard
}
};

const handleMouseEnter = () => {
if (dropdownState === DropdownState.CLOSED) {
setDropdownState(DropdownState.OPEN);
}
if (closeTimeoutRef.current) {
clearTimeout(closeTimeoutRef.current);
}
};

const handleMouseLeaveWithDelay = () => {
if (dropdownState !== DropdownState.FORCED_OPEN) {
closeTimeoutRef.current = setTimeout(() => {
setDropdownState(DropdownState.CLOSED);
}, DROPDOWN_HOVER_DELAY);
}
};

const buttonClickHandler = () => {
setDropdownState(
dropdownState === DropdownState.CLOSED
? DropdownState.OPEN
: DropdownState.CLOSED
);
};

return (
<Dropdown
menu={{ items, theme: "dark", className: styles.menu }}
placement={placement}
disabled={disabled}
trigger={["click"]}
open={dropdownState !== DropdownState.CLOSED}
dropdownRender={(menu) => (
<div className={styles.menuWrapper}>{menu}</div>
<div
ref={dropdownRef}
tabIndex={-1}
onKeyDown={handleKeyDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeaveWithDelay}
>
{menu}
</div>
)}
>
<NavButton
ref={triggerRef as React.RefObject<HTMLButtonElement>}
titleText={titleText}
icon={icon}
buttonType={buttonType}
onClick={buttonClickHandler}
onKeyDown={handleKeyDown}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeaveWithDelay}
/>
</Dropdown>
);
Expand Down
62 changes: 34 additions & 28 deletions src/components/NavButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { ReactNode } from "react";
import React, { ReactNode, forwardRef } from "react";
import { Button, ButtonProps } from "antd";
import classNames from "classnames";

import styles from "./style.css";
import { ButtonClass } from "../../constants/interfaces";

Expand All @@ -13,32 +12,39 @@ export interface NavButtonProps extends ButtonProps {
isDisabled?: boolean;
}

const NavButton: React.FC<NavButtonProps> = ({
className,
titleText,
buttonType = ButtonClass.Action,
icon,
onClick,
isDisabled,
...props
}) => {
// NavButtons default to action button styling, provide secondary or primary to override
const buttonClassNames = classNames(
className,
styles.navButton,
styles[buttonType],
{ [styles.disabled]: isDisabled }
);
const NavButton = forwardRef<HTMLButtonElement, NavButtonProps>(
(
{
className,
titleText,
buttonType = ButtonClass.Action,
icon,
onClick,
isDisabled,
...props
},
ref
) => {
const buttonClassNames = classNames(
className,
styles.navButton,
styles[buttonType],
{ [styles.disabled]: isDisabled }
);

return (
<Button
{...props}
ref={ref}
className={buttonClassNames}
onClick={!isDisabled ? onClick : undefined}
>
{titleText} {icon}
</Button>
);
}
);

return (
<Button
{...props}
className={buttonClassNames}
onClick={!isDisabled ? onClick : undefined}
>
{titleText} {icon}
</Button>
);
};
NavButton.displayName = "NavButton";

export default NavButton;
4 changes: 2 additions & 2 deletions src/components/ViewportButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const ViewportButton: React.FC<ViewportButtonProps> = ({
const getTooltip = () =>
!disabled || tooltipWhenDisabled ? tooltipText : "";

const getClickHandler = () => (!disabled ? onClick : undefined);
const onClickProp = !disabled ? onClick : undefined;

const buttonClassNames = classNames([
className,
Expand All @@ -79,7 +79,7 @@ const ViewportButton: React.FC<ViewportButtonProps> = ({
<Button
{...props}
className={buttonClassNames}
onClick={getClickHandler()}
onClick={onClickProp}
loading={loading}
icon={getIcon()}
/>
Expand Down
1 change: 1 addition & 0 deletions src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,4 @@ export const MAX_CONVERSION_FILE_SIZE = 2e8; // 200 MB
export const CONTROLS_MIN_WIDTH = 650;
export const CONTROLS_MIN_HEIGHT = 320;
export const SCALE_BAR_MIN_WIDTH = 550;
export const DROPDOWN_HOVER_DELAY = 300;
Loading

0 comments on commit 69cd8ef

Please sign in to comment.