@activecollab/components
Version:
ActiveCollab Components
209 lines (194 loc) • 11.1 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import React, { useState } from "react";
import styled from "styled-components";
import { IconButton } from "../../components/IconButton";
import { AttachmentIcon, CircleIcon, ConnectionIcon, DrawIcon, InsertLinkIcon, ProjectIcon, ProjectStarIcon, RoundedRectangleIcon, ShapesIcon, NoteIcon, TaskIcon, TextIcon, TriangleIcon } from "../../components/Icons";
import { Tooltip } from "../../components/Tooltip";
import { Caption1 } from "../../components/Typography";
/* ------------------------------------------------------------------ */
/* Floating toolbar surfaces */
/* ------------------------------------------------------------------ */
/* A floating, rounded toolbar surface (paper + shadow). */
export const ToolSurface = styled.div.withConfig({
displayName: "bottomDock__ToolSurface",
componentId: "sc-1xsxwg0-0"
})(["display:flex;align-items:center;gap:2px;padding:6px;border-radius:14px;background-color:var(--page-paper-main);box-shadow:0 6px 24px rgba(0,0,0,0.16);border:1px solid var(--border-primary);svg > [stroke],svg > g [stroke]{stroke:currentColor;}"]);
/* vertical separator used inside the toolbars */
export const verticalDivider = "\n .divider {\n width: 1px;\n height: 24px;\n background-color: var(--color-theme-400);\n margin: 0 6px;\n flex-shrink: 0;\n }\n";
export const StyledBottomDock = styled.div.withConfig({
displayName: "bottomDock__StyledBottomDock",
componentId: "sc-1xsxwg0-1"
})(["position:absolute;left:50%;bottom:18px;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:10px;max-width:calc(100% - 32px);"]);
/**
* The floating second toolbar row — a small pill above the dock that holds the
* members of one tool family (Circle / Rectangle / Triangle, or Task / Project).
*
* It is the same surface as the dock itself, so a family reads as an extension
* of the button that opened it rather than as a different kind of UI. Only one
* pill is ever up, because only one tool is ever armed.
*/
export const StyledToolPill = styled(ToolSurface).withConfig({
displayName: "bottomDock__StyledToolPill",
componentId: "sc-1xsxwg0-2"
})([""]);
/* Reserve two Caption1 lines (2 × 16px) so showing/hiding the hint never
shifts the dock; visibility keeps the reserved space when hidden. */
export const StyledHint = styled(Caption1).withConfig({
displayName: "bottomDock__StyledHint",
componentId: "sc-1xsxwg0-3"
})(["text-align:center;padding:0 16px;min-height:32px;&.hint-hidden{visibility:hidden;}"]);
/* Per-tool hint shown under the dock only while that tool is hovered. */
export const TOOL_HINTS = {
circle: "Click to drop a default circle, or draw one to size. Press O for a circle.",
roundedRectangle: "Click to drop a default rectangle, or draw one to size. Press R for a rectangle.",
triangle: "Click to drop a default triangle, or draw one to size. Press Y for a triangle.",
connector: "Drag from one element to another to connect them. Press C to connect.",
projectElements: "Put a task or a project on the board — pick which above, then draw its card.",
project: "Draw a box to place a project card, then pick the project it shows.",
task: "Draw a box to place a task card, then pick the task it shows.",
link: "Draw a box to place a link, or paste a URL anywhere. Press L for link.",
file: "Draw a box to place a file, or drop one on the board. Press F for file.",
sticky: "Click anywhere on the canvas to drop a sticky note. Press S for a sticky note.",
text: "Click anywhere on the canvas to start typing. Press T for text.",
draw: "Click and drag to draw freehand. Press D for the pen.",
shapes: "Pick a shape above, then draw it on the board. Press R for a rectangle."
};
/**
* The armed board tool. Link, File, Project, Task and the three shapes are
* wired — arming one turns the next canvas click into "drop the default-size
* element here" and a drag into "draw one this big"; everything else stays a
* mock, so the board is otherwise always in select mode.
*/
export const SHAPE_TOOLS = ["circle", "roundedRectangle", "triangle"];
export const isShapeTool = tool => SHAPE_TOOLS.includes(tool);
/**
* What a dock button arms the instant its pill opens.
*
* Opening a family and choosing inside it are two separate decisions, and the
* second one has a right answer often enough that asking for it every time is
* just a toll: a shape is usually a rectangle, and the thing people put on a
* board is usually a task. So the pill opens with that member already armed and
* the gesture can start immediately — picking another member is a correction,
* not a required step.
*/
export const DEFAULT_SHAPE_TOOL = "roundedRectangle";
export const DEFAULT_PROJECT_ELEMENT_TOOL = "task";
/* What a plain click drops — mirrors DEFAULT_SHAPE_SIZES in
new-activecollab-front, and is exactly what the hover preview promises. */
export const SHAPE_DEFAULT_SIZES = {
roundedRectangle: {
width: 100,
height: 60
},
circle: {
width: 80,
height: 80
},
triangle: {
width: 80,
height: 80
}
};
/* Cards are auto-height, so their hover preview uses the app's nominal wire
height (DEFAULT_LINK_CARD_SIZE / DEFAULT_FILE_CARD_SIZE). */
export const CARD_PREVIEW_HEIGHT = 240;
/** The kinds of card the draw gesture can place. */
export const ToolButton = _ref => {
let tip = _ref.tip,
active = _ref.active,
children = _ref.children,
onClick = _ref.onClick,
onMouseEnter = _ref.onMouseEnter,
onMouseLeave = _ref.onMouseLeave;
return /*#__PURE__*/React.createElement(Tooltip, {
title: tip
}, /*#__PURE__*/React.createElement(IconButton, {
variant: "text gray",
active: active,
onClick: onClick,
onMouseEnter: onMouseEnter,
onMouseLeave: onMouseLeave
}, children));
};
/* Bottom-centre tool dock: the family pill (when one is open), the main
toolbar, and the shared hint line. The dock is anchored by its BOTTOM edge,
so a pill appearing or disappearing grows the stack upwards and never moves
the toolbar itself. */
export const BottomDock = _ref2 => {
let activeTool = _ref2.activeTool,
onToolChange = _ref2.onToolChange;
const _useState = useState(null),
hoverHint = _useState[0],
setHoverHint = _useState[1];
const hoverProps = tool => ({
onMouseEnter: () => setHoverHint(TOOL_HINTS[tool]),
onMouseLeave: () => setHoverHint(null)
});
/* An armed tool keeps its hint up after the pointer leaves the dock — the
gesture happens on the canvas, so that is exactly when the instruction is
needed. */
const hint = hoverHint != null ? hoverHint : activeTool === "select" ? null : TOOL_HINTS[activeTool];
/* Which pill is up. Both are DERIVED from the armed tool rather than kept as
their own open/closed state: opening a pill always arms one of its members,
so "a member of this family is armed" and "this pill is showing" are the
same fact. Deriving it is what makes every way of leaving the family close
the pill for free — another dock tool, Esc on the canvas, or the gesture
finishing and disarming the tool. Two states could disagree; one cannot. */
const shapesOpen = isShapeTool(activeTool);
const projectElementsOpen = activeTool === "task" || activeTool === "project";
return /*#__PURE__*/React.createElement(StyledBottomDock, null, shapesOpen ? /*#__PURE__*/React.createElement(StyledToolPill, null, /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Circle",
active: activeTool === "circle",
onClick: () => onToolChange("circle")
}, hoverProps("circle")), /*#__PURE__*/React.createElement(CircleIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Rectangle",
active: activeTool === "roundedRectangle",
onClick: () => onToolChange("roundedRectangle")
}, hoverProps("roundedRectangle")), /*#__PURE__*/React.createElement(RoundedRectangleIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Triangle",
active: activeTool === "triangle",
onClick: () => onToolChange("triangle")
}, hoverProps("triangle")), /*#__PURE__*/React.createElement(TriangleIcon, null))) : null, projectElementsOpen ? /*#__PURE__*/React.createElement(StyledToolPill, null, /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Task",
active: activeTool === "task",
onClick: () => onToolChange("task")
}, hoverProps("task")), /*#__PURE__*/React.createElement(TaskIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Project",
active: activeTool === "project",
onClick: () => onToolChange("project")
}, hoverProps("project")), /*#__PURE__*/React.createElement(ProjectIcon, null))) : null, /*#__PURE__*/React.createElement(ToolSurface, null, /*#__PURE__*/React.createElement(Tooltip, {
title: "Sticky Note"
}, /*#__PURE__*/React.createElement(IconButton, _extends({
variant: "text gray",
onClick: () => onToolChange("select")
}, hoverProps("sticky")), /*#__PURE__*/React.createElement(NoteIcon, null))), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Text",
onClick: () => onToolChange("select")
}, hoverProps("text")), /*#__PURE__*/React.createElement(TextIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Draw",
onClick: () => onToolChange("select")
}, hoverProps("draw")), /*#__PURE__*/React.createElement(DrawIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Shape",
active: shapesOpen,
onClick: () => onToolChange(shapesOpen ? "select" : DEFAULT_SHAPE_TOOL)
}, hoverProps("shapes")), /*#__PURE__*/React.createElement(ShapesIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Connect",
onClick: () => onToolChange("select")
}, hoverProps("connector")), /*#__PURE__*/React.createElement(ConnectionIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Project Elements",
active: projectElementsOpen,
onClick: () => onToolChange(projectElementsOpen ? "select" : DEFAULT_PROJECT_ELEMENT_TOOL)
}, hoverProps("projectElements")), /*#__PURE__*/React.createElement(ProjectStarIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "Link",
active: activeTool === "link",
onClick: () => onToolChange(activeTool === "link" ? "select" : "link")
}, hoverProps("link")), /*#__PURE__*/React.createElement(InsertLinkIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
tip: "File",
active: activeTool === "file",
onClick: () => onToolChange(activeTool === "file" ? "select" : "file")
}, hoverProps("file")), /*#__PURE__*/React.createElement(AttachmentIcon, null))), /*#__PURE__*/React.createElement(StyledHint, {
color: "tertiary",
className: hint ? undefined : "hint-hidden"
}, hint != null ? hint : " "));
};
//# sourceMappingURL=bottomDock.js.map