UNPKG

@activecollab/components

Version:

ActiveCollab Components

191 lines (172 loc) 11.4 kB
import styled, { css } from "styled-components"; import { STACKED_CARD_CONTROL_CLASS, STACKED_CARD_FIXED_ATTR, STACKED_CARD_RADIUS, STACKED_CARD_ROLE_PADDING, STACKED_CARD_WIDTH } from "./constants"; import { BoxSizingStyle } from "../BoxSizingStyle"; /** * Theming hooks. * * Every colour a StackedCard part needs is read through a `--sc-*` custom * property with a built-in fallback, so the component looks right standing on * its own and a host surface can still retint it by declaring the variable on * any ancestor. No rgba/hex is baked into a rule; data colours (a swatch value, * a priority colour) are always passed in by the caller. */ const SELECTION_RING = "var(--sc-selection-ring, var(--color-primary-500))"; const SCRIM = "var(--sc-scrim, rgba(0, 0, 0, 0.55))"; const SCRIM_STRONG = "var(--sc-scrim-strong, rgba(0, 0, 0, 0.72))"; const SCRIM_FG = "var(--sc-scrim-fg, #ffffff)"; /** Selector for anything carrying the control class. */ const CONTROL = "." + STACKED_CARD_CONTROL_CLASS; /** * The one reveal rule the whole subsystem shares: controls are hidden until the * card (or the cover) is hovered OR holds focus. The focus path is what keeps * them reachable by keyboard instead of mouse-only. */ const revealControls = css(["", "{opacity:0;transition:opacity 120ms ease;}&:hover ", ",&:focus-within ", "{opacity:1;}"], CONTROL, CONTROL, CONTROL); /* ------------------------------------------------------------------ */ /* Stack — the vertical column. A card is a Stack of rows. */ /* ------------------------------------------------------------------ */ export const StyledStackedCardStack = styled.div.withConfig({ displayName: "Styles__StyledStackedCardStack", componentId: "sc-vnqm9x-0" })(["", " position:relative;display:flex;flex-direction:column;"], BoxSizingStyle); StyledStackedCardStack.displayName = "StyledStackedCardStack"; /* ------------------------------------------------------------------ */ /* Row — the only layout primitive, differentiated by `role`. */ /* ------------------------------------------------------------------ */ export const StyledStackedCardRow = styled.div.withConfig({ displayName: "Styles__StyledStackedCardRow", componentId: "sc-vnqm9x-1" })(["", " position:relative;display:flex;flex-direction:", ";align-items:", ";gap:", ";padding:", ";", " ", ""], BoxSizingStyle, _ref => { let $direction = _ref.$direction; return $direction; }, _ref2 => { let $direction = _ref2.$direction; return $direction === "column" ? "stretch" : "center"; }, _ref3 => { let $direction = _ref3.$direction; return $direction === "column" ? "4px" : "8px"; }, _ref4 => { let $role = _ref4.$role, $bleed = _ref4.$bleed; return $bleed ? "0" : STACKED_CARD_ROLE_PADDING[$role]; }, _ref5 => { let $background = _ref5.$background; return $background && css(["background:", ";"], $background); }, _ref6 => { let $direction = _ref6.$direction; return $direction === "row" && css(["> *{flex:1 1 0;min-width:0;}> [", "]{flex:0 0 auto;}"], STACKED_CARD_FIXED_ATTR); }); StyledStackedCardRow.displayName = "StyledStackedCardRow"; /* ------------------------------------------------------------------ */ /* Cover — a bleed row that holds media. */ /* ------------------------------------------------------------------ */ /** * The media box reserves its height with `aspect-ratio` so the image (or the * skeleton standing in for it) never reflows the card when it arrives. */ export const StyledStackedCardCoverMedia = styled.div.withConfig({ displayName: "Styles__StyledStackedCardCoverMedia", componentId: "sc-vnqm9x-2" })(["position:relative;width:100%;aspect-ratio:", ";overflow:hidden;", ""], _ref7 => { let $aspectRatio = _ref7.$aspectRatio; return $aspectRatio; }, revealControls); StyledStackedCardCoverMedia.displayName = "StyledStackedCardCoverMedia"; /* The fill (an <img>, an inline <svg> wrapper, or a solid-colour box) is laid over the reserved box and made to cover it. */ export const StyledStackedCardCoverFill = styled.div.withConfig({ displayName: "Styles__StyledStackedCardCoverFill", componentId: "sc-vnqm9x-3" })(["position:absolute;inset:0;> *{width:100%;height:100%;}> img{display:block;object-fit:cover;}> img,> svg{-webkit-user-drag:none;user-select:none;pointer-events:none;}"]); StyledStackedCardCoverFill.displayName = "StyledStackedCardCoverFill"; export const StyledStackedCardCoverCenter = styled.div.withConfig({ displayName: "Styles__StyledStackedCardCoverCenter", componentId: "sc-vnqm9x-4" })(["position:absolute;inset:0;display:flex;align-items:center;justify-content:center;"]); StyledStackedCardCoverCenter.displayName = "StyledStackedCardCoverCenter"; export const StyledStackedCardCoverTopRight = styled.div.withConfig({ displayName: "Styles__StyledStackedCardCoverTopRight", componentId: "sc-vnqm9x-5" })(["position:absolute;top:8px;right:8px;display:flex;gap:6px;"]); StyledStackedCardCoverTopRight.displayName = "StyledStackedCardCoverTopRight"; /** * Collapsed cover — the media box gives way to a thin strip. The strip IS the * expand affordance: the whole bar is a button and a chevron hint is revealed * on hover or focus. It reserves no height of its own; the caller's * `collapsedStrip` (an ~8px colour bar, a favicon + domain row) sets how tall * it reads. */ export const StyledStackedCardCollapsedStrip = styled.button.withConfig({ displayName: "Styles__StyledStackedCardCollapsedStrip", componentId: "sc-vnqm9x-6" })(["", " position:relative;display:flex;align-items:center;width:100%;min-height:16px;padding:0;border:0;background:transparent;cursor:pointer;text-align:left;", "{opacity:0;transition:opacity 120ms ease;}&:hover ", ",&:focus-visible ", "{opacity:1;}&:focus-visible{outline:2px solid ", ";outline-offset:-2px;}"], BoxSizingStyle, CONTROL, CONTROL, CONTROL, SELECTION_RING); StyledStackedCardCollapsedStrip.displayName = "StyledStackedCardCollapsedStrip"; /* The expand chevron sits at the right, vertically centred, so it reads the same whether the strip is a hairline colour bar or a taller favicon row. */ export const StyledStackedCardStripExpand = styled.span.withConfig({ displayName: "Styles__StyledStackedCardStripExpand", componentId: "sc-vnqm9x-7" })(["position:absolute;top:50%;right:8px;transform:translateY(-50%);display:inline-flex;width:16px;height:16px;color:var(--color-theme-600);svg{width:14px;height:14px;}"]); StyledStackedCardStripExpand.displayName = "StyledStackedCardStripExpand"; /* ------------------------------------------------------------------ */ /* Cover chrome — scrim-backed controls that sit over media */ /* ------------------------------------------------------------------ */ /** Centered media control (a play button). The caller drops the glyph inside. */ export const StyledStackedCardPlayBadge = styled.span.withConfig({ displayName: "Styles__StyledStackedCardPlayBadge", componentId: "sc-vnqm9x-8" })(["display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;border-radius:12px;background:", ";color:", ";svg{fill:currentColor;width:20px;height:20px;margin-left:2px;}"], SCRIM, SCRIM_FG); StyledStackedCardPlayBadge.displayName = "StyledStackedCardPlayBadge"; /** Top-right cover-management button (collapse, remove …). */ export const StyledStackedCardCoverButton = styled.button.withConfig({ displayName: "Styles__StyledStackedCardCoverButton", componentId: "sc-vnqm9x-9" })(["", " display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:0;border-radius:8px;cursor:pointer;color:", ";background:", ";svg{width:16px;height:16px;display:block;}&:hover{background:", ";}"], BoxSizingStyle, SCRIM_FG, SCRIM, SCRIM_STRONG); StyledStackedCardCoverButton.displayName = "StyledStackedCardCoverButton"; /* ------------------------------------------------------------------ */ /* Resize (opt-in): handle on hover, the card only reports deltas */ /* ------------------------------------------------------------------ */ /* The grip is a focusable separator, so it needs a focus ring of its own — a keyboard user has no pointer to tell them where they are. */ export const StyledStackedCardResizeHandle = styled.span.withConfig({ displayName: "Styles__StyledStackedCardResizeHandle", componentId: "sc-vnqm9x-10" })(["position:absolute;right:2px;bottom:2px;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:4px;cursor:", ";color:var(--color-theme-600);z-index:2;&:hover{color:var(--color-theme-900);}&:focus-visible{outline:2px solid ", ";outline-offset:1px;color:var(--color-theme-900);}svg{display:block;}"], _ref8 => { let $axis = _ref8.$axis; return $axis === "width" ? "ew-resize" : "nwse-resize"; }, SELECTION_RING); StyledStackedCardResizeHandle.displayName = "StyledStackedCardResizeHandle"; /* ------------------------------------------------------------------ */ /* The shell */ /* ------------------------------------------------------------------ */ const hoverStyle = css(["box-shadow:0 4px 12px rgba(0,0,0,0.12);border-color:var(--color-theme-500);"]); export const StyledStackedCard = styled.div.withConfig({ displayName: "Styles__StyledStackedCard", componentId: "sc-vnqm9x-11" })(["", " position:relative;display:flex;flex-direction:column;width:", "px;border-radius:", "px;background-color:var(--page-paper-main);border:1px solid var(--border-primary);box-shadow:0 1px 3px rgba(0,0,0,0.08);overflow:hidden;transition:box-shadow 150ms ease,border-color 150ms ease;", " &:hover{", "}&:focus-visible{outline:none;", " box-shadow:0 0 0 2px ", ";}", " ", " ", " ", " ", " ", " ", " ", ""], BoxSizingStyle, STACKED_CARD_WIDTH, STACKED_CARD_RADIUS, revealControls, hoverStyle, hoverStyle, SELECTION_RING, _ref9 => { let $forceHover = _ref9.$forceHover; return $forceHover && css(["", " && ", "{opacity:1;}"], hoverStyle, CONTROL); }, _ref0 => { let $priorityColor = _ref0.$priorityColor; return $priorityColor && css(["&::before{content:\"\";position:absolute;top:0;bottom:0;left:0;width:4px;background:", ";z-index:2;}"], $priorityColor); }, _ref1 => { let $selected = _ref1.$selected; return $selected && css(["&&{border-color:", ";box-shadow:0 0 0 2px ", ";}"], SELECTION_RING, SELECTION_RING); }, _ref10 => { let $disabled = _ref10.$disabled; return $disabled && css(["opacity:0.5;pointer-events:none;"]); }, _ref11 => { let $pending = _ref11.$pending; return $pending && css(["opacity:0.6;pointer-events:none;cursor:progress;"]); }, _ref12 => { let $isDragSource = _ref12.$isDragSource; return $isDragSource && css(["background:transparent;border:1px dashed var(--color-theme-500);box-shadow:none;&:hover{box-shadow:none;border-color:var(--color-theme-500);}> *{visibility:hidden;}"]); }, _ref13 => { let $isDragPreview = _ref13.$isDragPreview; return $isDragPreview && css(["box-shadow:0 12px 28px rgba(0,0,0,0.24);cursor:grabbing;"]); }, _ref14 => { let $entered = _ref14.$entered; return $entered && css(["&&{border-color:", ";box-shadow:inset 0 0 0 2px ", ",0 0 0 2px ", ";}"], SELECTION_RING, SELECTION_RING, SELECTION_RING); }); StyledStackedCard.displayName = "StyledStackedCard"; //# sourceMappingURL=Styles.js.map