@activecollab/components
Version:
ActiveCollab Components
191 lines (172 loc) • 11.4 kB
JavaScript
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