diff --git a/frontend/src/metabase/query_builder/components/notebook/NotebookCell.jsx b/frontend/src/metabase/query_builder/components/notebook/NotebookCell.jsx deleted file mode 100644 index 682e655559b1e4243d4cf6eb0a8237ec6142274a..0000000000000000000000000000000000000000 --- a/frontend/src/metabase/query_builder/components/notebook/NotebookCell.jsx +++ /dev/null @@ -1,139 +0,0 @@ -/* eslint-disable react/prop-types */ -import { isValidElement } from "react"; - -import styled from "@emotion/styled"; -import { css } from "@emotion/react"; - -import { Icon } from "metabase/core/components/Icon"; - -import { alpha } from "metabase/lib/colors"; - -const CONTAINER_PADDING = "10px"; - -const _NotebookCell = styled.div` - display: flex; - align-items: center; - flex-wrap: wrap; - border-radius: 8px; - background-color: ${props => alpha(props.color, 0.1)}; - padding: ${props => props.padding || "14px"}; - color: ${props => props.color}; -`; - -export const NotebookCell = Object.assign(_NotebookCell, { - displayName: "NotebookCell", - CONTAINER_PADDING, -}); - -const NotebookCellItemContainer = styled.div` - display: flex; - align-items: center; - font-weight: bold; - color: ${props => (props.inactive ? props.color : "white")}; - border-radius: 6px; - margin-right: 4px; - - border: 2px solid transparent; - border-color: ${props => - props.inactive ? alpha(props.color, 0.25) : "transparent"}; - - &:hover { - border-color: ${props => props.inactive && alpha(props.color, 0.8)}; - } - - transition: border 300ms linear; - - .Icon-close { - opacity: 0.6; - } -`; - -const NotebookCellItemContentContainer = styled.div` - display: flex; - align-items: center; - padding: ${CONTAINER_PADDING}; - background-color: ${props => (props.inactive ? "transparent" : props.color)}; - - &:hover { - background-color: ${props => !props.inactive && alpha(props.color, 0.8)}; - } - - ${props => - !!props.border && - css` - border-${props.border}: 1px solid ${alpha("white", 0.25)}; - `} - - ${props => - props.roundedCorners.includes("left") && - css` - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; - `} - - ${props => - props.roundedCorners.includes("right") && - css` - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; - `} - - transition: background 300ms linear; -`; - -export function NotebookCellItem(props) { - const { - inactive, - color, - containerStyle, - right, - rightContainerStyle, - children, - readOnly, - ...restProps - } = props; - - const hasRightSide = isValidElement(right) && !readOnly; - const mainContentRoundedCorners = ["left"]; - if (!hasRightSide) { - mainContentRoundedCorners.push("right"); - } - return ( - <NotebookCellItemContainer - inactive={inactive} - color={color} - {...restProps} - data-testid={props["data-testid"] ?? "notebook-cell-item"} - > - <NotebookCellItemContentContainer - inactive={inactive} - color={color} - roundedCorners={mainContentRoundedCorners} - style={containerStyle} - > - {children} - </NotebookCellItemContentContainer> - {hasRightSide && ( - <NotebookCellItemContentContainer - inactive={inactive} - color={color} - border="left" - roundedCorners={["right"]} - style={rightContainerStyle} - > - {right} - </NotebookCellItemContentContainer> - )} - </NotebookCellItemContainer> - ); -} - -NotebookCellItem.displayName = "NotebookCellItem"; - -export const NotebookCellAdd = ({ initialAddText, ...props }) => ( - <NotebookCellItem {...props} inactive={!!initialAddText}> - {initialAddText || <Icon name="add" className="text-white" />} - </NotebookCellItem> -); - -NotebookCellAdd.displayName = "NotebookCellAdd"; diff --git a/frontend/src/metabase/query_builder/components/notebook/NotebookCell/NotebookCell.styled.tsx b/frontend/src/metabase/query_builder/components/notebook/NotebookCell/NotebookCell.styled.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5321c9c02395a7c94ac5c176b6ffe979ac48b4d1 --- /dev/null +++ b/frontend/src/metabase/query_builder/components/notebook/NotebookCell/NotebookCell.styled.tsx @@ -0,0 +1,81 @@ +import styled from "@emotion/styled"; +import { css } from "@emotion/react"; +import { alpha } from "metabase/lib/colors"; + +export type BorderSide = "top" | "right" | "bottom" | "left"; + +export const CONTAINER_PADDING = "10px"; + +export const NotebookCell = styled.div<{ color: string; padding?: string }>` + display: flex; + align-items: center; + flex-wrap: wrap; + border-radius: 8px; + background-color: ${props => alpha(props.color, 0.1)}; + padding: ${props => props.padding || "14px"}; + color: ${props => props.color}; +`; + +export const NotebookCellItemContainer = styled.div<{ + color: string; + inactive?: boolean; +}>` + display: flex; + align-items: center; + font-weight: bold; + color: ${props => (props.inactive ? props.color : "white")}; + border-radius: 6px; + margin-right: 4px; + + border: 2px solid transparent; + border-color: ${props => + props.inactive ? alpha(props.color, 0.25) : "transparent"}; + + &:hover { + border-color: ${props => props.inactive && alpha(props.color, 0.8)}; + } + + transition: border 300ms linear; + + .Icon-close { + opacity: 0.6; + } +`; + +export const NotebookCellItemContentContainer = styled.div<{ + color: string; + inactive?: boolean; + border?: BorderSide; + roundedCorners: BorderSide[]; +}>` + display: flex; + align-items: center; + padding: ${CONTAINER_PADDING}; + background-color: ${props => (props.inactive ? "transparent" : props.color)}; + + &:hover { + background-color: ${props => !props.inactive && alpha(props.color, 0.8)}; + } + + ${props => + !!props.border && + css` + border-${props.border}: 1px solid ${alpha("white", 0.25)}; + `} + + ${props => + props.roundedCorners.includes("left") && + css` + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + `} + + ${props => + props.roundedCorners.includes("right") && + css` + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + `} + + transition: background 300ms linear; +`; diff --git a/frontend/src/metabase/query_builder/components/notebook/NotebookCell/NotebookCell.tsx b/frontend/src/metabase/query_builder/components/notebook/NotebookCell/NotebookCell.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aba6f5ee660b0d00aed1d78af1075072ec6a1418 --- /dev/null +++ b/frontend/src/metabase/query_builder/components/notebook/NotebookCell/NotebookCell.tsx @@ -0,0 +1,93 @@ +import { forwardRef, isValidElement } from "react"; +import { Icon } from "metabase/core/components/Icon"; +import { + NotebookCell as _NotebookCell, + NotebookCellItemContainer, + NotebookCellItemContentContainer, + CONTAINER_PADDING, + BorderSide, +} from "./NotebookCell.styled"; + +export const NotebookCell = Object.assign(_NotebookCell, { + displayName: "NotebookCell", + CONTAINER_PADDING, +}); + +interface NotebookCellItemProps { + color: string; + inactive?: boolean; + readOnly?: boolean; + right?: React.ReactNode; + containerStyle?: React.CSSProperties; + rightContainerStyle?: React.CSSProperties; + children?: React.ReactNode; + onClick?: React.MouseEventHandler; + "data-testid"?: string; + ref?: React.Ref<HTMLDivElement>; +} + +export const NotebookCellItem = forwardRef< + HTMLDivElement, + NotebookCellItemProps +>(function NotebookCellItem( + { + inactive, + color, + containerStyle, + right, + rightContainerStyle, + children, + readOnly, + ...restProps + }, + ref, +) { + const hasRightSide = isValidElement(right) && !readOnly; + const mainContentRoundedCorners: BorderSide[] = ["left"]; + if (!hasRightSide) { + mainContentRoundedCorners.push("right"); + } + return ( + <NotebookCellItemContainer + inactive={inactive} + color={color} + {...restProps} + data-testid={restProps["data-testid"] ?? "notebook-cell-item"} + ref={ref} + > + <NotebookCellItemContentContainer + inactive={inactive} + color={color} + roundedCorners={mainContentRoundedCorners} + style={containerStyle} + > + {children} + </NotebookCellItemContentContainer> + {hasRightSide && ( + <NotebookCellItemContentContainer + inactive={inactive} + color={color} + border="left" + roundedCorners={["right"]} + style={rightContainerStyle} + > + {right} + </NotebookCellItemContentContainer> + )} + </NotebookCellItemContainer> + ); +}); + +interface NotebookCellAddProps extends NotebookCellItemProps { + initialAddText?: React.ReactNode; +} + +export const NotebookCellAdd = forwardRef<HTMLDivElement, NotebookCellAddProps>( + function NotebookCellAdd({ initialAddText, ...props }, ref) { + return ( + <NotebookCellItem {...props} inactive={!!initialAddText} ref={ref}> + {initialAddText || <Icon name="add" className="text-white" />} + </NotebookCellItem> + ); + }, +); diff --git a/frontend/src/metabase/query_builder/components/notebook/NotebookCell/index.ts b/frontend/src/metabase/query_builder/components/notebook/NotebookCell/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a7c9f790bd3b50eb151b65c1959ce12a0c979ee --- /dev/null +++ b/frontend/src/metabase/query_builder/components/notebook/NotebookCell/index.ts @@ -0,0 +1 @@ +export * from "./NotebookCell";