From dc9e623b757c5a7beac8be1d36c19cb071ad7be3 Mon Sep 17 00:00:00 2001 From: trevor-anderson Date: Sat, 18 Mar 2023 06:21:31 -0400 Subject: [PATCH] feat: add 'ItemDetailsGroup', rm nestability of 'ItemDetails' --- src/layouts/CoreItemView/CoreItemView.tsx | 12 +-- src/layouts/CoreItemView/ItemDetails.tsx | 61 ++++------- src/layouts/CoreItemView/ItemDetailsGroup.tsx | 102 ++++++++++++++++++ src/layouts/CoreItemView/ItemDetailsLabel.tsx | 8 +- src/layouts/CoreItemView/index.ts | 7 +- 5 files changed, 135 insertions(+), 55 deletions(-) create mode 100644 src/layouts/CoreItemView/ItemDetailsGroup.tsx diff --git a/src/layouts/CoreItemView/CoreItemView.tsx b/src/layouts/CoreItemView/CoreItemView.tsx index 5292aeb0..34917b7a 100644 --- a/src/layouts/CoreItemView/CoreItemView.tsx +++ b/src/layouts/CoreItemView/CoreItemView.tsx @@ -23,13 +23,9 @@ export const CoreItemView = ({ ); -const StyledCoreContentViewLayout = styled(CoreContentViewLayout)(({ theme }) => ({ +const StyledCoreContentViewLayout = styled(CoreContentViewLayout)({ "& .core-content-view-children-container": { - overflowY: "auto !important", - // child of scroll container: - "& > div": { - alignSelf: "center", - width: theme.variables.isMobilePageLayout ? "100%" : "calc(100% - 2rem)" // <-- -2rem helps view to always look centered, with OR without scrollbar - } + overflowX: "hidden", + overflowY: "auto" } -})); +}); diff --git a/src/layouts/CoreItemView/ItemDetails.tsx b/src/layouts/CoreItemView/ItemDetails.tsx index 51b62896..dd84d133 100644 --- a/src/layouts/CoreItemView/ItemDetails.tsx +++ b/src/layouts/CoreItemView/ItemDetails.tsx @@ -3,6 +3,8 @@ import Box from "@mui/material/Box"; import Text from "@mui/material/Typography"; import { ItemDetailsLabel } from "./ItemDetailsLabel"; +// TODO Create a separate ItemDetailsGroup component + /** * Displays one or more item properties. * - Can be nested for grouping related properties together. @@ -25,14 +27,7 @@ export const ItemDetails = ({ children, emptyFallback = --, ...containerProps // any remaining props are passed to the containing div -}: { - header?: React.ReactNode; - label?: string; - labelVariant?: React.ComponentProps["variant"]; - labelIcon?: React.ComponentProps["icon"]; - children?: React.ReactNode; - emptyFallback?: React.ReactNode; -} & React.ComponentProps) => ( +}: ItemDetailsProps) => ( {(header || label) && (
@@ -52,8 +47,7 @@ export const ItemDetails = ({ ); const ItemDetailsContainer = styled(Box)(({ theme }) => ({ - border: `2px solid ${theme.palette.divider}`, - borderRadius: "0.35rem", + maxWidth: "100%", "& *": { overflow: "hidden", @@ -63,17 +57,17 @@ const ItemDetailsContainer = styled(Box)(({ theme }) => ({ // HEADER: "& > .item-details-header": { + height: "auto", width: "100%", - padding: "1rem", + padding: 0, display: "flex", flexDirection: "row", alignItems: "center", - borderWidth: "0 0 2px 0", - borderStyle: "solid", - borderColor: theme.palette.divider, - "& .MuiTypography-root": { - color: theme.palette.text.primary + "& > .item-details-label": { + color: theme.palette.text.primary, + marginTop: 0, + opacity: "0.7" }, "& > svg:first-of-type": { @@ -84,34 +78,17 @@ const ItemDetailsContainer = styled(Box)(({ theme }) => ({ // CONTENT: "& > .item-details-content": { - padding: "1.25rem", display: "flex", flexDirection: "column", gap: "2rem" - }, - - /* NESTED ItemDetails: - - may be placed within HEADER or CONTENT - - no borders - - no padding - - label opacity set to 0.7, no margin-top - - header height set to auto - */ - "& > .item-details-header, .item-details-content": { - "& div.item-details-container": { - border: "none", - "& > div.item-details-header": { - height: "auto", - padding: "0", - border: "none", - "& > .item-details-label": { - marginTop: 0, - opacity: "0.7" - } - }, - "& > div.item-details-content": { - padding: "0" - } - } } })); + +export type ItemDetailsProps = { + header?: React.ReactNode; + label?: string; + labelVariant?: React.ComponentProps["variant"]; + labelIcon?: React.ComponentProps["icon"]; + children?: React.ReactNode; + emptyFallback?: React.ReactNode; +} & React.ComponentProps; diff --git a/src/layouts/CoreItemView/ItemDetailsGroup.tsx b/src/layouts/CoreItemView/ItemDetailsGroup.tsx new file mode 100644 index 00000000..84f6175b --- /dev/null +++ b/src/layouts/CoreItemView/ItemDetailsGroup.tsx @@ -0,0 +1,102 @@ +import { styled, alpha } from "@mui/material/styles"; +import Box from "@mui/material/Box"; +import Paper from "@mui/material/Paper"; +import Text from "@mui/material/Typography"; +import { ItemDetailsLabel } from "./ItemDetailsLabel"; +import type { ItemDetailsProps } from "./ItemDetails"; + +/** + * Displays one or more ItemDetails components as a group. + */ +export const ItemDetailsGroup = ({ + header, + label, + labelVariant, + labelIcon, + children, + emptyFallback = --, + ...containerProps // any remaining props are passed to the containing div +}: ItemDetailsProps) => ( + + {(header || label) && ( + + {label && ( + <> + {labelIcon} + {label} + + )} + {header} + + )} +
+ {typeof children === "string" ? {children || "--"} : children ?? emptyFallback} +
+
+); + +const ItemDetailsGroupContainer = styled(Box)(({ theme }) => ({ + ...(theme.palette.mode === "light" && { + border: `2px solid ${theme.palette.divider}` + }), + borderRadius: "0.35rem", + overflow: "hidden", + + "& *": { + textOverflow: "ellipsis" + }, + + // HEADER: + + "& > .item-details-group-header": { + width: "100%", + padding: "1rem", + overflow: "hidden", + display: "flex", + flexDirection: "row", + alignItems: "center", + borderWidth: "0 0 1px 0", + borderStyle: "solid", + borderColor: alpha(theme.palette.divider, 0.05), + borderRadius: "0.35rem 0.35rem 0 0", + + "& > .MuiTypography-root": { + color: theme.palette.text.primary + }, + + "& > svg:first-of-type": { + marginRight: "0.75rem" + } + }, + + // CONTENT: + + "& > .item-details-group-content": { + position: "relative", + zIndex: 1, + padding: "1.25rem", + borderRadius: "0 0 0.35rem 0.35rem", + display: "flex", + flexDirection: "column", + gap: "1.5rem 2rem", + + "&::before": { + content: '""', + position: "absolute", + zIndex: 2, + pointerEvents: "none", + top: 0, + left: "-10px", + display: "block", + height: "calc(100% + 20px)", + width: "110%", + boxShadow: + theme.palette.mode === "dark" + ? "inset 0 1px 5px 1px rgba(0,0,0,0.2)" + : "inset 0 1px 5px 0 rgba(0,0,0,0.05)" + } + } +})); diff --git a/src/layouts/CoreItemView/ItemDetailsLabel.tsx b/src/layouts/CoreItemView/ItemDetailsLabel.tsx index 14d8714d..5298c587 100644 --- a/src/layouts/CoreItemView/ItemDetailsLabel.tsx +++ b/src/layouts/CoreItemView/ItemDetailsLabel.tsx @@ -4,6 +4,7 @@ export const ItemDetailsLabel = ({ icon, variant = "h6", style = {}, + sx = {}, children, ...props }: { icon?: React.ReactNode } & React.ComponentProps) => ( @@ -16,13 +17,16 @@ export const ItemDetailsLabel = ({ margin: "0.25rem 0", fontSize: "0.9rem", lineHeight: "1rem", - fontWeight: "200", textTransform: "uppercase", - overflowX: "hidden", + overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis", ...style }} + sx={({ palette }) => ({ + fontWeight: palette.mode === "dark" ? 200 : "normal", + ...(sx as any) + })} {...props} > {children} diff --git a/src/layouts/CoreItemView/index.ts b/src/layouts/CoreItemView/index.ts index 275d856b..e3576739 100644 --- a/src/layouts/CoreItemView/index.ts +++ b/src/layouts/CoreItemView/index.ts @@ -1,3 +1,4 @@ -export { CoreItemView } from "./CoreItemView"; -export { ItemDetails } from "./ItemDetails"; -export { ItemDetailsLabel } from "./ItemDetailsLabel"; +export * from "./CoreItemView"; +export * from "./ItemDetails"; +export * from "./ItemDetailsGroup"; +export * from "./ItemDetailsLabel";