From 9dd03566b1c836832fbdeca5099e79fad664e72b Mon Sep 17 00:00:00 2001 From: Anton Kulyk <kuliks.anton@gmail.com> Date: Wed, 21 Sep 2022 18:07:40 +0100 Subject: [PATCH] Extract `DraggableSidebarLink` component (#25457) * Export `SidebarLinkProps` * Expose `SidebarLink's` left & right containers That'd allow styling left and right elements from external components * Extract `DraggableSidebarLink` component Based on sortable bookmarks code * Use `DraggableSidebarLink` in `BookmarkList` * Fix dragged item styles --- .../BookmarkList/BookmarkList.styled.tsx | 41 +---------------- .../BookmarkList/BookmarkList.tsx | 7 +-- .../DraggableSidebarLink.styled.tsx | 45 +++++++++++++++++++ .../SidebarItems/DraggableSidebarLink.tsx | 21 +++++++++ .../SidebarItems/SidebarItems.styled.tsx | 3 ++ .../MainNavbar/SidebarItems/SidebarLink.tsx | 19 ++++++-- .../MainNavbar/SidebarItems/index.ts | 1 + .../sortable.css | 3 +- 8 files changed, 90 insertions(+), 50 deletions(-) create mode 100644 frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx create mode 100644 frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx rename frontend/src/metabase/nav/containers/MainNavbar/{MainNavbarContainer/BookmarkList => SidebarItems}/sortable.css (67%) diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx index 4f76a194448..cf891ff2512 100644 --- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx +++ b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.styled.tsx @@ -1,28 +1,11 @@ import styled from "@emotion/styled"; -import { css } from "@emotion/react"; import { color } from "metabase/lib/colors"; -import Icon from "metabase/components/Icon"; +import { DraggableSidebarLink } from "../../SidebarItems"; -import { SidebarLink } from "../../SidebarItems"; - -type SidebarBookmarkItem = { - isSorting: boolean; -}; - -export const DragIcon = styled(Icon)` - left: 2px; - opacity: 0; - position: absolute; - top: 50%; - transform: translateY(-50%); - cursor: grab; -`; - -export const SidebarBookmarkItem = styled(SidebarLink)<SidebarBookmarkItem>` +export const SidebarBookmarkItem = styled(DraggableSidebarLink)` padding-left: 0.75rem; - position: relative; &:hover { button { @@ -33,10 +16,6 @@ export const SidebarBookmarkItem = styled(SidebarLink)<SidebarBookmarkItem>` outline: none; } } - - > svg { - opacity: 0.3; - } } button { @@ -50,20 +29,4 @@ export const SidebarBookmarkItem = styled(SidebarLink)<SidebarBookmarkItem>` outline: none; } } - - ${props => - props.isSorting && - css` - &:hover { - background: white; - - svg { - color: ${color("brand-light")} !important; - } - - button { - opacity: 0; - } - } - `} `; diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx index 1f4f9dc23ee..3da21b7d89a 100644 --- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx +++ b/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/BookmarkList.tsx @@ -2,8 +2,6 @@ import React, { useCallback, useEffect, useState } from "react"; import { t } from "ttag"; import { connect } from "react-redux"; -import "./sortable.css"; - import { SortableContainer, SortableElement, @@ -20,7 +18,7 @@ import * as Urls from "metabase/lib/urls"; import { SelectedItem } from "../../types"; import { SidebarHeading } from "../../MainNavbar.styled"; -import { DragIcon, SidebarBookmarkItem } from "./BookmarkList.styled"; +import { SidebarBookmarkItem } from "./BookmarkList.styled"; const mapDispatchToProps = { onDeleteBookmark: ({ item_id, type }: Bookmark) => @@ -91,10 +89,9 @@ const BookmarkItem = ({ url={url} icon={icon} isSelected={isSelected} - isSorting={isSorting} + isDragging={isSorting} hasDefaultIconStyle={!isIrregularCollection} onClick={onSelect} - left={<DragIcon name="grabber2" size={12} />} right={ <button onClick={onRemove}> <Tooltip tooltip={t`Remove bookmark`} placement="bottom"> diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx new file mode 100644 index 00000000000..50f3b0bee2c --- /dev/null +++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.styled.tsx @@ -0,0 +1,45 @@ +import styled from "@emotion/styled"; +import { css } from "@emotion/react"; + +import Icon from "metabase/components/Icon"; + +import { color } from "metabase/lib/colors"; + +import SidebarLink from "./SidebarLink"; + +export const DragIcon = styled(Icon)` + left: 2px; + opacity: 0; + position: absolute; + top: 50%; + transform: translateY(-50%); + cursor: grab; +`; + +// Notice that dragged item styles are defined in sortable.css file +// This is a limitation of react-sortable-hoc library +export const StyledSidebarLink = styled(SidebarLink)<{ isDragging: boolean }>` + position: relative; + + &:hover { + ${DragIcon} { + opacity: 0.3; + } + } + + ${props => + props.isDragging && + css` + &:hover { + background: ${color("bg-white")}; + + ${SidebarLink.Icon}, ${DragIcon} { + color: ${color("brand-light")} !important; + } + + ${SidebarLink.RightElement} { + opacity: 0; + } + } + `} +`; diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx new file mode 100644 index 00000000000..42106f8d322 --- /dev/null +++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/DraggableSidebarLink.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +import { SidebarLinkProps } from "./SidebarLink"; +import { DragIcon, StyledSidebarLink } from "./DraggableSidebarLink.styled"; + +import "./sortable.css"; + +interface Props extends Omit<SidebarLinkProps, "left"> { + isDragging: boolean; +} + +function DraggableSidebarLink(props: Props) { + return ( + <StyledSidebarLink + {...props} + left={<DragIcon name="grabber2" size={12} />} + /> + ); +} + +export default DraggableSidebarLink; diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx index 3eec95ba5fd..0153ba7e7da 100644 --- a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx +++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarItems.styled.tsx @@ -127,3 +127,6 @@ export function NameContainer({ children: itemName }: { children: string }) { } return <TreeNode.NameContainer>{itemName}</TreeNode.NameContainer>; } + +export const LeftElementContainer = styled.div``; +export const RightElementContainer = styled.div``; diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx index dacbbef2e91..af0a7e38e41 100644 --- a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx +++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/SidebarLink.tsx @@ -11,9 +11,11 @@ import { NodeRoot, SidebarIcon, FullWidthButton, + LeftElementContainer, + RightElementContainer, } from "./SidebarItems.styled"; -interface Props { +interface SidebarLinkProps { children: string; url?: string; icon?: string | IconProps | React.ReactElement; @@ -51,7 +53,7 @@ function SidebarLink({ left = null, right = null, ...props -}: Props) { +}: SidebarLinkProps) { const renderIcon = useCallback(() => { if (!icon) { return null; @@ -83,16 +85,25 @@ function SidebarLink({ onMouseDown={disableImageDragging} {...props} > - {React.isValidElement(left) && left} + {React.isValidElement(left) && ( + <LeftElementContainer>{left}</LeftElementContainer> + )} <Content> {icon && renderIcon()} <NameContainer>{children}</NameContainer> </Content> - {React.isValidElement(right) && right} + {React.isValidElement(right) && ( + <RightElementContainer>{right}</RightElementContainer> + )} </NodeRoot> ); } +export type { SidebarLinkProps }; + export default Object.assign(SidebarLink, { NameContainers: [ItemName, TreeNode.NameContainer], + Icon: SidebarIcon, + LeftElement: LeftElementContainer, + RightElement: RightElementContainer, }); diff --git a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts index f97e8e7a976..8ce9b417f03 100644 --- a/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts +++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/index.ts @@ -1,3 +1,4 @@ +export { default as DraggableSidebarLink } from "./DraggableSidebarLink"; export { default as SidebarCollectionLink } from "./SidebarCollectionLink"; export { default as SidebarDataAppLink } from "./SidebarDataAppLink"; export { default as SidebarLink } from "./SidebarLink"; diff --git a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/sortable.css b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/sortable.css similarity index 67% rename from frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/sortable.css rename to frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/sortable.css index 68bb7d49aaf..181c075c2fb 100644 --- a/frontend/src/metabase/nav/containers/MainNavbar/MainNavbarContainer/BookmarkList/sortable.css +++ b/frontend/src/metabase/nav/containers/MainNavbar/SidebarItems/sortable.css @@ -1,9 +1,8 @@ .react-sortable-hoc-helper.sorting { - background: white; border-bottom: 2px solid var(--color-brand); } -.react-sortable-hoc-helper.sorting > svg { +.react-sortable-hoc-helper.sorting .Icon-grabber2 { color: var(--color-brand); opacity: 0.5; } -- GitLab