From ce3d9fb6f5babfe1f266b6cb1ff4657fb747a99d Mon Sep 17 00:00:00 2001 From: Alexander Polyankin <alexander.polyankin@metabase.com> Date: Mon, 20 Jun 2022 22:09:32 +0300 Subject: [PATCH] Cleanup icons and checkboxes in collections (#23413) --- .../collections/components/BaseTableItem.jsx | 2 +- .../components/ArchiveItem.styled.tsx | 7 -- .../src/metabase/components/ArchivedItem.jsx | 24 +++-- .../components/ArchivedItem.styled.tsx | 13 +++ .../src/metabase/components/EntityItem.jsx | 6 +- frontend/src/metabase/components/Swapper.jsx | 91 ------------------- .../components/Swapper/Swapper.styled.tsx | 22 +++++ .../core/components/Swapper/Swapper.tsx | 47 ++++++++++ .../metabase/core/components/Swapper/index.ts | 1 + 9 files changed, 101 insertions(+), 112 deletions(-) delete mode 100644 frontend/src/metabase/components/ArchiveItem.styled.tsx create mode 100644 frontend/src/metabase/components/ArchivedItem.styled.tsx delete mode 100644 frontend/src/metabase/components/Swapper.jsx create mode 100644 frontend/src/metabase/core/components/Swapper/Swapper.styled.tsx create mode 100644 frontend/src/metabase/core/components/Swapper/Swapper.tsx create mode 100644 frontend/src/metabase/core/components/Swapper/index.ts diff --git a/frontend/src/metabase/collections/components/BaseTableItem.jsx b/frontend/src/metabase/collections/components/BaseTableItem.jsx index 52fa9b3940f..e4c277ea68a 100644 --- a/frontend/src/metabase/collections/components/BaseTableItem.jsx +++ b/frontend/src/metabase/collections/components/BaseTableItem.jsx @@ -81,7 +81,7 @@ export function BaseTableItem({ const icon = { name: item.getIcon().name }; if (item.model === "card") { - icon.color = color("bg-dark"); + icon.color = color("text-light"); } // Table row can be wrapped with ItemDragSource, diff --git a/frontend/src/metabase/components/ArchiveItem.styled.tsx b/frontend/src/metabase/components/ArchiveItem.styled.tsx deleted file mode 100644 index 3793d997089..00000000000 --- a/frontend/src/metabase/components/ArchiveItem.styled.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import styled from "@emotion/styled"; -import IconWrapper from "metabase/components/IconWrapper"; - -export const IconContainer = styled(IconWrapper)` - padding: 0.5rem; - margin-right: 0.5rem; -`; diff --git a/frontend/src/metabase/components/ArchivedItem.jsx b/frontend/src/metabase/components/ArchivedItem.jsx index 773c23bea13..55fbba9ca96 100644 --- a/frontend/src/metabase/components/ArchivedItem.jsx +++ b/frontend/src/metabase/components/ArchivedItem.jsx @@ -6,11 +6,11 @@ import { t } from "ttag"; import CheckBox from "metabase/core/components/CheckBox"; import Icon from "metabase/components/Icon"; -import Swapper from "metabase/components/Swapper"; +import Swapper from "metabase/core/components/Swapper"; import Tooltip from "metabase/components/Tooltip"; import { color as c } from "metabase/lib/colors"; -import { IconContainer } from "./ArchiveItem.styled"; +import { ItemIcon, ItemIconContainer } from "./ArchivedItem.styled"; const ArchivedItem = ({ name, @@ -25,15 +25,19 @@ const ArchivedItem = ({ showSelect, }) => ( <div className="flex align-center p2 hover-parent hover--visibility border-bottom bg-light-hover"> - <IconContainer> - <Swapper - startSwapped={showSelect} - defaultElement={<Icon name={icon} color={color} />} - swappedElement={ + <Swapper + defaultElement={ + <ItemIconContainer> + <ItemIcon name={icon} color={color} /> + </ItemIconContainer> + } + swappedElement={ + <ItemIconContainer> <CheckBox checked={selected} onChange={onToggleSelected} /> - } - /> - </IconContainer> + </ItemIconContainer> + } + isSwapped={showSelect} + /> {name} {isAdmin && (onUnarchive || onDelete) && ( <span className="ml-auto mr2"> diff --git a/frontend/src/metabase/components/ArchivedItem.styled.tsx b/frontend/src/metabase/components/ArchivedItem.styled.tsx new file mode 100644 index 00000000000..8cab288bbb7 --- /dev/null +++ b/frontend/src/metabase/components/ArchivedItem.styled.tsx @@ -0,0 +1,13 @@ +import styled from "@emotion/styled"; +import IconWrapper from "metabase/components/IconWrapper"; +import Icon from "metabase/components/Icon"; + +export const ItemIcon = styled(Icon)` + display: block; +`; + +export const ItemIconContainer = styled(IconWrapper)` + padding: 0.5rem; + margin-right: 0.5rem; + line-height: 1; +`; diff --git a/frontend/src/metabase/components/EntityItem.jsx b/frontend/src/metabase/components/EntityItem.jsx index b79164ea8b1..7f841443b0e 100644 --- a/frontend/src/metabase/components/EntityItem.jsx +++ b/frontend/src/metabase/components/EntityItem.jsx @@ -4,7 +4,7 @@ import { t } from "ttag"; import cx from "classnames"; import EntityMenu from "metabase/components/EntityMenu"; -import Swapper from "metabase/components/Swapper"; +import Swapper from "metabase/core/components/Swapper"; import CheckBox from "metabase/core/components/CheckBox"; import Ellipsified from "metabase/core/components/Ellipsified"; import Icon from "metabase/components/Icon"; @@ -30,7 +30,7 @@ function EntityIconCheckBox({ onToggleSelected, ...props }) { - const iconSize = variant === "small" ? 12 : 18; + const iconSize = variant === "small" ? 12 : 16; const handleClick = e => { e.preventDefault(); onToggleSelected(); @@ -47,7 +47,6 @@ function EntityIconCheckBox({ > {selectable ? ( <Swapper - startSwapped={selected || showCheckbox} defaultElement={ <Icon name={icon.name} @@ -56,6 +55,7 @@ function EntityIconCheckBox({ /> } swappedElement={<CheckBox checked={selected} size={iconSize} />} + isSwapped={selected || showCheckbox} /> ) : ( <Icon diff --git a/frontend/src/metabase/components/Swapper.jsx b/frontend/src/metabase/components/Swapper.jsx deleted file mode 100644 index 698e7493c04..00000000000 --- a/frontend/src/metabase/components/Swapper.jsx +++ /dev/null @@ -1,91 +0,0 @@ -/* eslint-disable react/prop-types */ -import React from "react"; -import { Motion, spring } from "react-motion"; - -import { isReducedMotionPreferred } from "metabase/lib/dom"; - -class Swapper extends React.Component { - state = { - hovered: false, - }; - - _onMouseEnter() { - this.setState({ hovered: true }); - } - - _onMouseLeave() { - this.setState({ hovered: false }); - } - - render() { - const { defaultElement, swappedElement, startSwapped } = this.props; - const { hovered } = this.state; - - const preferReducedMotion = isReducedMotionPreferred(); - const springOpts = preferReducedMotion - ? { stiffness: 500 } - : { stiffness: 170 }; - - return ( - <span - onMouseEnter={() => this._onMouseEnter()} - onMouseLeave={() => this._onMouseLeave()} - className="block relative" - style={{ lineHeight: 1 }} - > - <Motion - defaultStyle={{ - scale: 1, - }} - style={{ - scale: - hovered || startSwapped - ? spring(0, springOpts) - : spring(1, springOpts), - }} - > - {({ scale }) => { - const snapScale = scale < 0.5 ? 0 : 1; - const _scale = preferReducedMotion ? snapScale : scale; - return ( - <span - style={{ - display: "block", - transform: `scale(${_scale})`, - }} - > - {defaultElement} - </span> - ); - }} - </Motion> - <Motion - defaultStyle={{ - scale: 0, - }} - style={{ - scale: - hovered || startSwapped - ? spring(1, springOpts) - : spring(0, springOpts), - }} - > - {({ scale }) => { - const snapScale = scale < 0.5 ? 0 : 1; - const _scale = preferReducedMotion ? snapScale : scale; - return ( - <span - className="absolute top left bottom right" - style={{ display: "block", transform: `scale(${_scale})` }} - > - {swappedElement} - </span> - ); - }} - </Motion> - </span> - ); - } -} - -export default Swapper; diff --git a/frontend/src/metabase/core/components/Swapper/Swapper.styled.tsx b/frontend/src/metabase/core/components/Swapper/Swapper.styled.tsx new file mode 100644 index 00000000000..4d43280bca0 --- /dev/null +++ b/frontend/src/metabase/core/components/Swapper/Swapper.styled.tsx @@ -0,0 +1,22 @@ +import styled from "@emotion/styled"; + +export interface SwapperElementProps { + isVisible: boolean; +} + +export const SwapperRoot = styled.div` + position: relative; +`; + +export const SwapperDefaultElement = styled.div<SwapperElementProps>` + transform: scale(${props => (props.isVisible ? 1 : 0)}); +`; + +export const SwapperLayeredElement = styled.div<SwapperElementProps>` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + transform: scale(${props => (props.isVisible ? 1 : 0)}); +`; diff --git a/frontend/src/metabase/core/components/Swapper/Swapper.tsx b/frontend/src/metabase/core/components/Swapper/Swapper.tsx new file mode 100644 index 00000000000..f811af4697a --- /dev/null +++ b/frontend/src/metabase/core/components/Swapper/Swapper.tsx @@ -0,0 +1,47 @@ +import React, { + forwardRef, + HTMLAttributes, + ReactNode, + Ref, + useCallback, + useState, +} from "react"; +import { + SwapperDefaultElement, + SwapperLayeredElement, + SwapperRoot, +} from "./Swapper.styled"; + +export interface SwapperProps extends HTMLAttributes<HTMLDivElement> { + defaultElement?: ReactNode; + swappedElement?: ReactNode; + isSwapped?: boolean; +} + +const Swapper = forwardRef(function Swapper( + { defaultElement, swappedElement, isSwapped = false, ...props }: SwapperProps, + ref: Ref<HTMLDivElement>, +) { + const [isHovered, setIsHovered] = useState(false); + const isSelected = isHovered || isSwapped; + const handleMouseEnter = useCallback(() => setIsHovered(true), []); + const handleMouseLeave = useCallback(() => setIsHovered(false), []); + + return ( + <SwapperRoot + {...props} + ref={ref} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} + > + <SwapperDefaultElement isVisible={!isSelected}> + {defaultElement} + </SwapperDefaultElement> + <SwapperLayeredElement isVisible={isSelected}> + {swappedElement} + </SwapperLayeredElement> + </SwapperRoot> + ); +}); + +export default Swapper; diff --git a/frontend/src/metabase/core/components/Swapper/index.ts b/frontend/src/metabase/core/components/Swapper/index.ts new file mode 100644 index 00000000000..b3226b152a3 --- /dev/null +++ b/frontend/src/metabase/core/components/Swapper/index.ts @@ -0,0 +1 @@ +export { default } from "./Swapper"; -- GitLab