UNPKG

@activecollab/components

Version:

ActiveCollab Components

816 lines (778 loc) 39.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.withOpacity = exports.stopClick = exports.stickyGradient = exports.snapValue = exports.shapeBorder = exports.renderShapeBody = exports.gridLevel = exports.defaultShapeState = exports.WHEEL_SIZE = exports.WHEEL_RADIUS = exports.ToolbarTextControls = exports.ToolbarColorSwatch = exports.ToolbarActions = exports.THICKNESS_OPTIONS = exports.StyledToolbarLink = exports.StyledToolbarButton = exports.StyledSwatchRow = exports.StyledSticky = exports.StyledHandles = exports.StyledFormattingToolbar = exports.StyledFontSizeValue = exports.StyledColorWheel = exports.StyledColorPopover = exports.StyledCanvasLayer = exports.StyledCanvasElement = exports.ShapeTypeMenu = exports.ShapeFormattingToolbar = exports.SUBMENU_SHADOW = exports.STYLE_OPTIONS = exports.STICKY_FILL = exports.SHAPE_TYPE_OPTIONS = exports.SHAPE_TYPE_ICON = exports.SELECTION_BLUE = exports.ResizeHandles = exports.RESIZE_DIRECTIONS = exports.RADIUS_OPTIONS = exports.PostitFormattingToolbar = exports.OPACITY_OPTIONS = exports.INITIAL_SHAPES = exports.INITIAL_POSITIONS = exports.GRID_LEVELS = exports.FONT_SIZE_OPTIONS = exports.ELEMENT_TEXT = exports.ELEMENT_STROKE = exports.ELEMENT_FILL = exports.ELEMENT_BODIES = exports.ColorWheel = exports.ColorSwatchRow = exports.COLOR_PALETTE = exports.COLOR_OPTIONS = exports.CANVAS_ELEMENTS = exports.BORDER_STYLE_OPTIONS = exports.AlignmentMenu = exports.ALIGN_OPTIONS = void 0; var _react = _interopRequireWildcard(require("react")); var _styledComponents = _interopRequireDefault(require("styled-components")); var _glyphs = require("./glyphs"); var _Dot = require("../../components/Dot"); var _IconButton = require("../../components/IconButton"); var _Icons = require("../../components/Icons"); var _List = require("../../components/List"); var _Menu = require("../../components/Menu"); var _Tooltip = require("../../components/Tooltip"); var _colors = require("../../utils/colors"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } /* ------------------------------------------------------------------ */ /* Canvas elements (1:1 with new-activecollab-front */ /* src/components/Whiteboards/ — constants.js, ShapeElement.jsx, */ /* PostitElement.jsx, ResizableWrapper.jsx, ResizeHandles.jsx, */ /* FormattingToolbarContainer.jsx + the two formatting toolbars) */ /* ------------------------------------------------------------------ */ /* element colors are canvas DATA (app constants), not theme — hardcoded */ var ELEMENT_FILL = exports.ELEMENT_FILL = "#ffffff"; var ELEMENT_STROKE = exports.ELEMENT_STROKE = "#374151"; var ELEMENT_TEXT = exports.ELEMENT_TEXT = "#1f2937"; var STICKY_FILL = exports.STICKY_FILL = "#fbbf24"; var SELECTION_BLUE = exports.SELECTION_BLUE = "#3b82f6"; /* COLOR_PALETTE from toolbar/colorPalette.js (submenu swatch rows) */ var COLOR_PALETTE = exports.COLOR_PALETTE = ["#ffffff", "#3b82f6", "#ef4444", "#10b981", "#f59e0b", "#8b5cf6", "#f97316", "#06b6d4", "#84cc16", "#ec4899", "#6b7280"]; /* Discrete option sets for the right Inspector — mirror the app's fixed font-size ladder and the border-thickness steps offered in the toolbar. */ var FONT_SIZE_OPTIONS = exports.FONT_SIZE_OPTIONS = [10, 13, 16, 20, 24, 30, 36, 48, 72, 96, 144, 216, 288]; var THICKNESS_OPTIONS = exports.THICKNESS_OPTIONS = [1, 2, 3, 4, 5]; var OPACITY_OPTIONS = exports.OPACITY_OPTIONS = [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]; /* Corner radii scaled to the board: shapes are drawn at ~100–300px, so the jump from a crisp corner to a pill reads across 0–64px. */ var RADIUS_OPTIONS = exports.RADIUS_OPTIONS = [0, 4, 8, 12, 16, 24, 32, 48, 64]; /* Element bodies are HTML, as the API returns them (the `body` field). The Inspector renders them read-only; when a body is empty the field is omitted. Mock the three cases across the canvas: a short body (shape-rounded, the default selection), a long one that scrolls (shape-circle), and none at all (shape-triangle / sticky). */ var ELEMENT_BODIES = exports.ELEMENT_BODIES = { "shape-rounded": "<p>Marks the <strong>Kickoff</strong> phase.</p>", "shape-circle": "<p>The <strong>discovery</strong> loop for this initiative.</p><p>We collect signals from support, sales and analytics, then cluster them into themes before committing to a direction.</p><ul><li>Owner: research</li><li>Inputs: interviews, tickets, funnel data</li><li>Output: a prioritised opportunity list</li></ul><p>See the <em>retro notes</em> and last quarter's readout for the full context and prior decisions.</p>" }; /* Named-colour options for the Inspector fill/stroke pickers, built from the design system's shared data colours (utils/colors). Shaped for the DS Select (id + name + color + textColor) so it renders the standard dot-and-radio menu used by the label dialogs. */ var COLOR_OPTIONS = exports.COLOR_OPTIONS = Object.values(_colors.colors).map(function (entry) { return { id: entry.color, name: entry.name, color: entry.color, textColor: entry.color }; }); /* per-shape editable state (the app bakes opacity into rgba via hexToRgba/parseOpacity; the mock keeps opacity separate and rebuilds the color when rendering) */ var defaultShapeState = exports.defaultShapeState = function defaultShapeState(type) { return { type, fill: ELEMENT_FILL, fillOpacity: 100, stroke: ELEMENT_STROKE, strokeOpacity: 100, strokeWidth: 2, strokeDash: undefined, borderRadius: type === "roundedRectangle" ? 12 : 0, fontSize: 16, bold: false, italic: false, underline: false, strikethrough: false, textColor: ELEMENT_TEXT, align: "left" }; }; /* swatches are 6-digit hex, the color wheel returns hsl(...) */ var withOpacity = exports.withOpacity = function withOpacity(color, opacityPct) { if (opacityPct >= 100) { return color; } var alpha = opacityPct / 100; if (color.startsWith("#")) { var r = parseInt(color.slice(1, 3), 16); var g = parseInt(color.slice(3, 5), 16); var b = parseInt(color.slice(5, 7), 16); return "rgba(".concat(r, ", ").concat(g, ", ").concat(b, ", ").concat(alpha, ")"); } if (color.startsWith("hsl(")) { return color.replace("hsl(", "hsla(").replace(")", ", ".concat(alpha, ")")); } return color; }; /* getShapeBorder from ShapeElement.jsx */ var shapeBorder = exports.shapeBorder = function shapeBorder(state) { if (state.strokeWidth === 0) { return "none"; } var lineStyle = state.strokeDash === "5,5" ? "dashed" : state.strokeDash === "2,2" ? "dotted" : "solid"; return "".concat(state.strokeWidth, "px ").concat(lineStyle, " ").concat(withOpacity(state.stroke, state.strokeOpacity)); }; /* PostitElement gradient: color → same color at ~87% alpha */ var stickyGradient = exports.stickyGradient = function stickyGradient(color) { var opacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100; var main = withOpacity(color, opacity); var faded = withOpacity(color, Math.max(0, opacity - 13)); return "linear-gradient(135deg, ".concat(main, " 0%, ").concat(faded, " 100%)"); }; /* Grid config, aligned to the 8px design-system grid. dotSpacing drives the dot layer; snapStep drives snapping — decoupled, so each level snaps finer than its dots (Small: 16px dots / 8px snap; Large: 32px dots / 16px snap, i.e. dots and half-dots). */ var GRID_LEVELS = exports.GRID_LEVELS = [{ value: "off", label: "Off", dotSpacing: null, snapStep: null }, { value: "small", label: "Small", dotSpacing: 16, snapStep: 8 }, { value: "large", label: "Large", dotSpacing: 32, snapStep: 16 }]; var gridLevel = exports.gridLevel = function gridLevel(value) { var _GRID_LEVELS$find; return (_GRID_LEVELS$find = GRID_LEVELS.find(function (level) { return level.value === value; })) !== null && _GRID_LEVELS$find !== void 0 ? _GRID_LEVELS$find : GRID_LEVELS[0]; }; /* default sizes from DEFAULT_SHAPE_SIZES / DEFAULT_STICKY_SIZE; positions are board-space pixels (top-left) so dragging can snap to the pixel grid */ var CANVAS_ELEMENTS = exports.CANVAS_ELEMENTS = [{ id: "shape-circle", kind: "shape", initialType: "circle", x: 320, y: 180, width: 80, height: 80 }, { id: "shape-rounded", kind: "shape", initialType: "roundedRectangle", x: 560, y: 300, width: 100, height: 60 }, { id: "shape-triangle", kind: "shape", initialType: "triangle", x: 820, y: 170, width: 80, height: 80 }, { id: "sticky", kind: "sticky", x: 430, y: 430, width: 120, height: 120 }]; var INITIAL_SHAPES = exports.INITIAL_SHAPES = CANVAS_ELEMENTS.reduce(function (acc, element) { if (element.kind === "shape" && element.initialType) { acc[element.id] = defaultShapeState(element.initialType); } return acc; }, {}); var INITIAL_POSITIONS = exports.INITIAL_POSITIONS = CANVAS_ELEMENTS.reduce(function (acc, element) { acc[element.id] = { x: element.x, y: element.y }; return acc; }, {}); var snapValue = exports.snapValue = function snapValue(value, step) { return step ? Math.round(value / step) * step : value; }; /* deselect happens on canvas-background click (layer under the bars) */ var StyledCanvasLayer = exports.StyledCanvasLayer = _styledComponents.default.div.withConfig({ displayName: "elements__StyledCanvasLayer", componentId: "sc-ohnju4-0" })(["position:absolute;inset:0;&.drawing{cursor:crosshair;}"]); /* ResizableWrapper: selected = 2px blue-500 ring @50%, hover = 4px blue-400 ring @70% (Tailwind rings are box-shadows) */ var StyledCanvasElement = exports.StyledCanvasElement = _styledComponents.default.div.withConfig({ displayName: "elements__StyledCanvasElement", componentId: "sc-ohnju4-1" })(["position:absolute;cursor:move;&:hover:not(.selected){box-shadow:0 0 0 4px rgba(96,165,250,0.7);}&.selected{box-shadow:0 0 0 2px rgba(59,130,246,0.5);}&.is-card{cursor:grab;height:auto;&:hover:not(.selected),&.selected{box-shadow:none;}}.shape-fill{width:100%;height:100%;box-sizing:border-box;}"]); /* PostitElement: 12px padding, shadow-lg, 1deg tilt that straightens on hover, diagonal gradient to ~87% alpha, Comic Sans */ var StyledSticky = exports.StyledSticky = _styledComponents.default.div.withConfig({ displayName: "elements__StyledSticky", componentId: "sc-ohnju4-2" })(["width:100%;height:100%;box-sizing:border-box;padding:12px;box-shadow:0 10px 15px -3px rgba(0,0,0,0.1),0 4px 6px -4px rgba(0,0,0,0.1);transform:rotate(1deg);transition:transform 200ms;font-family:\"Comic Sans MS\",cursive,system-ui,sans-serif;line-height:1.4;word-break:break-word;white-space:pre-wrap;", ":hover &{transform:rotate(0deg);}"], StyledCanvasElement); /* ResizeHandles: 8 12px blue squares w/ 2px white border on the box, plus the 24px rotate handle outside the bottom-left corner */ var StyledHandles = exports.StyledHandles = _styledComponents.default.div.withConfig({ displayName: "elements__StyledHandles", componentId: "sc-ohnju4-3" })([".rh{position:absolute;width:12px;height:12px;box-sizing:border-box;background-color:", ";border:2px solid #ffffff;border-radius:2px;z-index:10;&:hover{background-color:#2563eb;}}.rh-nw{top:-6px;left:-6px;cursor:nw-resize;}.rh-n{top:-6px;left:50%;margin-left:-6px;cursor:n-resize;}.rh-ne{top:-6px;right:-6px;cursor:ne-resize;}.rh-w{top:50%;left:-6px;margin-top:-6px;cursor:w-resize;}.rh-e{top:50%;right:-6px;margin-top:-6px;cursor:e-resize;}.rh-sw{bottom:-6px;left:-6px;cursor:sw-resize;}.rh-s{bottom:-6px;left:50%;margin-left:-6px;cursor:s-resize;}.rh-se{bottom:-6px;right:-6px;cursor:se-resize;}.rotate-handle{position:absolute;bottom:-35px;left:-35px;width:24px;height:24px;box-sizing:border-box;display:flex;align-items:center;justify-content:center;background-color:#ffffff;border:2px solid ", ";border-radius:50%;cursor:grab;z-index:11;svg{width:16px;height:16px;}}"], SELECTION_BLUE, SELECTION_BLUE); var RESIZE_DIRECTIONS = exports.RESIZE_DIRECTIONS = ["nw", "n", "ne", "w", "e", "sw", "s", "se"]; var ResizeHandles = exports.ResizeHandles = function ResizeHandles() { return /*#__PURE__*/_react.default.createElement(StyledHandles, { onMouseDown: function onMouseDown(event) { return event.stopPropagation(); } }, RESIZE_DIRECTIONS.map(function (direction) { return /*#__PURE__*/_react.default.createElement("span", { key: direction, className: "rh rh-".concat(direction) }); }), /*#__PURE__*/_react.default.createElement("span", { className: "rotate-handle" }, /*#__PURE__*/_react.default.createElement(_Icons.ArrowRefreshIcon, { fill: SELECTION_BLUE }))); }; /* FormattingToolbarContainer: Paper paper-2 (bg + 8px radius + shadow, no border), 8px padding, 4px gap, centered 10px above the element */ var StyledFormattingToolbar = exports.StyledFormattingToolbar = _styledComponents.default.div.withConfig({ displayName: "elements__StyledFormattingToolbar", componentId: "sc-ohnju4-4" })(["position:absolute;bottom:calc(100% + 10px);left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:4px;padding:8px;border-radius:8px;background-color:var(--page-paper-main);box-shadow:var(--shadow-tertiary);white-space:nowrap;cursor:auto;z-index:20;.tb-group{position:relative;display:flex;align-items:center;gap:4px;padding-right:8px;margin-right:8px;border-right:1px solid var(--border-primary);}.tb-rel{position:relative;display:inline-flex;}.divider{width:1px;height:20px;background-color:var(--border-primary);margin:0 4px;flex-shrink:0;}"]); /* Button variant="option" (idle) / "tertiary" (active), h-8 px-2 gap-1 */ var StyledToolbarButton = exports.StyledToolbarButton = _styledComponents.default.button.withConfig({ displayName: "elements__StyledToolbarButton", componentId: "sc-ohnju4-5" })(["display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;box-sizing:border-box;padding:0 8px;border:0;border-radius:8px;background-color:transparent;color:var(--color-secondary);font-family:-apple-system,BlinkMacSystemFont,\"Roboto\",\"Helvetica Neue\",Arial,sans-serif;font-size:14px;cursor:pointer;&:hover{background-color:var(--color-theme-200);}&.active{background-color:var(--color-theme-300);}&.icon-only{width:32px;padding:0;}&:disabled{opacity:0.45;cursor:default;&:hover{background-color:transparent;}}svg{fill:currentColor;flex-shrink:0;}svg.i16{width:16px;height:16px;}svg.i12{width:12px;height:12px;}"]); /* Same chrome as a toolbar button, but an <a> — used where the action is a real navigation (Visit). `withComponent` keeps the styles and swaps the element, so the props type becomes the anchor's. */ var StyledToolbarLink = exports.StyledToolbarLink = (0, _styledComponents.default)(StyledToolbarButton.withComponent("a")).withConfig({ displayName: "elements__StyledToolbarLink", componentId: "sc-ohnju4-6" })(["text-decoration:none;"]); /* font-mono min-w-[24px] text-xs readout between the stepper buttons */ var StyledFontSizeValue = exports.StyledFontSizeValue = _styledComponents.default.span.withConfig({ displayName: "elements__StyledFontSizeValue", componentId: "sc-ohnju4-7" })(["min-width:24px;text-align:center;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;color:var(--color-secondary);"]); var stopClick = exports.stopClick = function stopClick(event) { return event.stopPropagation(); }; /* Shared drop shadow for the colour swatch popover / colour wheel that the compact toolbar swatch trigger opens. */ var SUBMENU_SHADOW = exports.SUBMENU_SHADOW = "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1)"; /* ColorWheelPopover: beside the submenu (SHAPE_POPOVER_CLASS) or below a toolbar trigger (sticky default class) */ var StyledColorPopover = exports.StyledColorPopover = _styledComponents.default.div.withConfig({ displayName: "elements__StyledColorPopover", componentId: "sc-ohnju4-8" })(["position:absolute;box-sizing:border-box;padding:12px;background-color:var(--page-paper-main);border:1px solid var(--border-primary);border-radius:8px;box-shadow:", ";z-index:70;cursor:auto;&.beside{top:0;left:100%;margin-left:8px;}&.below{top:40px;left:0;}"], SUBMENU_SHADOW); /* ImprovedColorWheel: hue = angle from east (atan2), saturation = distance, lightness 50 — the canvas wheel replicated with CSS gradients */ var WHEEL_SIZE = exports.WHEEL_SIZE = 200; var WHEEL_RADIUS = exports.WHEEL_RADIUS = WHEEL_SIZE / 2 - 5; var StyledColorWheel = exports.StyledColorWheel = _styledComponents.default.button.withConfig({ displayName: "elements__StyledColorWheel", componentId: "sc-ohnju4-9" })(["display:block;width:", "px;height:", "px;padding:0;border:0;border-radius:50%;cursor:crosshair;box-shadow:", ";background:radial-gradient( circle closest-side,#ffffff,rgba(255,255,255,0) ),conic-gradient( from 90deg,#ff0000,#ffff00,#00ff00,#00ffff,#0000ff,#ff00ff,#ff0000 );"], WHEEL_SIZE, WHEEL_SIZE, SUBMENU_SHADOW); var ColorWheel = exports.ColorWheel = function ColorWheel(_ref) { var onColorChange = _ref.onColorChange; return /*#__PURE__*/_react.default.createElement(StyledColorWheel, { onClick: function onClick(event) { var rect = event.currentTarget.getBoundingClientRect(); var dx = event.clientX - rect.left - WHEEL_SIZE / 2; var dy = event.clientY - rect.top - WHEEL_SIZE / 2; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > WHEEL_RADIUS) { return; } var angle = Math.atan2(dy, dx); if (angle < 0) { angle += 2 * Math.PI; } var hue = Math.round(angle * 180 / Math.PI); var saturation = Math.round(Math.min(100, distance / WHEEL_RADIUS * 100)); onColorChange("hsl(".concat(hue, ", ").concat(saturation, "%, 50%)")); } }); }; /* ColorSwatchRow.jsx: 32px swatches + a "+" that opens the color wheel */ var StyledSwatchRow = exports.StyledSwatchRow = _styledComponents.default.div.withConfig({ displayName: "elements__StyledSwatchRow", componentId: "sc-ohnju4-10" })(["display:flex;flex-wrap:wrap;gap:8px;.swatch{width:32px;height:32px;box-sizing:border-box;padding:0;border:2px solid var(--border-primary);border-radius:4px;cursor:pointer;&:hover{border-color:var(--color-theme-500);}&.selected{box-shadow:0 0 0 2px ", ";}}.swatch-add{display:flex;align-items:center;justify-content:center;background-color:var(--color-theme-200);color:var(--color-secondary);}"], SELECTION_BLUE); var ColorSwatchRow = exports.ColorSwatchRow = function ColorSwatchRow(_ref2) { var selectedColor = _ref2.selectedColor, includeTransparent = _ref2.includeTransparent, onSelect = _ref2.onSelect, onAddClick = _ref2.onAddClick; return /*#__PURE__*/_react.default.createElement(StyledSwatchRow, null, includeTransparent ? /*#__PURE__*/_react.default.createElement("button", { type: "button", className: selectedColor === "transparent" ? "swatch swatch-add selected" : "swatch swatch-add", "aria-label": "Transparent", onClick: function onClick() { return onSelect("transparent"); } }, /*#__PURE__*/_react.default.createElement(_Icons.CircleSlashIcon, { width: 20, height: 20, fill: "currentColor" })) : null, COLOR_PALETTE.map(function (color) { return /*#__PURE__*/_react.default.createElement("button", { key: color, type: "button", className: selectedColor === color ? "swatch selected" : "swatch", style: { backgroundColor: color }, onClick: function onClick() { return onSelect(color); } }); }), /*#__PURE__*/_react.default.createElement("button", { type: "button", className: "swatch swatch-add", onClick: onAddClick }, /*#__PURE__*/_react.default.createElement(_glyphs.PlusGlyph, null))); }; var SHAPE_TYPE_ICON = exports.SHAPE_TYPE_ICON = { rectangle: /*#__PURE__*/_react.default.createElement(_glyphs.SquareGlyph, null), roundedRectangle: /*#__PURE__*/_react.default.createElement(_Icons.RoundedRectangleIcon, { className: "i16" }), circle: /*#__PURE__*/_react.default.createElement(_Icons.CircleIcon, { className: "i16" }), triangle: /*#__PURE__*/_react.default.createElement(_Icons.TriangleIcon, { className: "i16" }) }; /* The four shape types, in the order the shape-type menu lists them. */ var SHAPE_TYPE_OPTIONS = exports.SHAPE_TYPE_OPTIONS = [{ type: "rectangle", label: "Rectangle" }, { type: "roundedRectangle", label: "Rounded Rectangle" }, { type: "circle", label: "Circle" }, { type: "triangle", label: "Triangle" }]; /* Stroke pattern options — now only the Inspector's Stroke group renders these (the shape floating toolbar no longer edits stroke). Mirrors the app's STROKE_PATTERN_OPTIONS dash enum: solid | dashed | dotted. */ var BORDER_STYLE_OPTIONS = exports.BORDER_STYLE_OPTIONS = [{ value: undefined, label: "Solid" }, { value: "5,5", label: "Dashed" }, { value: "2,2", label: "Dotted" }]; /* ── Shared floating-toolbar controls ─────────────────────────────────────── The floating toolbars were flattened this session to match the app: compact, collapsed controls (single-button dropdowns) with uniform 4px spacing and a single hairline separator only before Lock / Delete. These mirror the shared controls in the app's Inspector/Controls (ShapeTypeMenu, AlignmentMenu, ColorPicker compact, ToolbarActions). */ /* ToolbarActions — the trailing block shared by EVERY floating toolbar: separator → Lock → Delete, in the standard control colour (not red). Mirrors app `toolbar/ToolbarActions.jsx`. */ var ToolbarActions = exports.ToolbarActions = function ToolbarActions(_ref3) { var locked = _ref3.locked, onToggleLock = _ref3.onToggleLock, onDelete = _ref3.onDelete; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", { className: "divider" }), /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, { title: locked ? "Unlock" : "Lock" }, /*#__PURE__*/_react.default.createElement(StyledToolbarButton, { className: locked ? "icon-only active" : "icon-only", "aria-pressed": locked, onClick: onToggleLock }, /*#__PURE__*/_react.default.createElement(_Icons.LockIcon, { className: "i16" }))), /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, { title: "Delete" }, /*#__PURE__*/_react.default.createElement(StyledToolbarButton, { className: "icon-only", onClick: onDelete }, /*#__PURE__*/_react.default.createElement(_Icons.TrashIcon, { className: "i16" })))); }; /* ShapeTypeMenu — collapsed shape-type picker: one icon button showing the current shape, opening a dropdown of the four types. Modeled on the app's ShapeTypeMenu (DS Menu + List). */ var ShapeTypeMenu = exports.ShapeTypeMenu = function ShapeTypeMenu(_ref4) { var value = _ref4.value, onChange = _ref4.onChange; var _useState = (0, _react.useState)(false), _useState2 = _slicedToArray(_useState, 2), open = _useState2[0], setOpen = _useState2[1]; return /*#__PURE__*/_react.default.createElement(_Menu.Menu, { open: open, onOpen: function onOpen() { return setOpen(true); }, onClose: function onClose() { return setOpen(false); }, position: "bottom-start", target: /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, { type: "button", variant: "text gray", active: open, "aria-label": "Shape" }, SHAPE_TYPE_ICON[value]) }, /*#__PURE__*/_react.default.createElement(_List.List, { style: { padding: "8px 0" }, tabIndex: -1 }, SHAPE_TYPE_OPTIONS.map(function (option) { return /*#__PURE__*/_react.default.createElement(_List.ListItem, { key: option.type, "aria-label": option.label, onClick: function onClick() { onChange(option.type); setOpen(false); } }, SHAPE_TYPE_ICON[option.type], option.label); }))); }; /* AlignmentMenu — collapsed alignment picker (single button + dropdown), the compact-toolbar counterpart to the Inspector's expanded four-button row. Mirrors the app's AlignmentMenu. */ var AlignmentMenu = exports.AlignmentMenu = function AlignmentMenu(_ref5) { var _ALIGN_OPTIONS$find; var value = _ref5.value, onChange = _ref5.onChange, disabled = _ref5.disabled; var _useState3 = (0, _react.useState)(false), _useState4 = _slicedToArray(_useState3, 2), open = _useState4[0], setOpen = _useState4[1]; var selected = (_ALIGN_OPTIONS$find = ALIGN_OPTIONS.find(function (option) { return option.value === value; })) !== null && _ALIGN_OPTIONS$find !== void 0 ? _ALIGN_OPTIONS$find : ALIGN_OPTIONS[0]; return /*#__PURE__*/_react.default.createElement(_Menu.Menu, { open: open, onOpen: function onOpen() { return setOpen(true); }, onClose: function onClose() { return setOpen(false); }, position: "bottom-start", target: /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, { type: "button", variant: "text gray", active: open, disabled: disabled, "aria-label": "Alignment" }, selected.icon) }, /*#__PURE__*/_react.default.createElement(_List.List, { style: { padding: "8px 0" }, tabIndex: -1 }, ALIGN_OPTIONS.map(function (option) { return /*#__PURE__*/_react.default.createElement(_List.ListItem, { key: option.value, "aria-label": option.label, onClick: function onClick() { onChange(option.value); setOpen(false); } }, option.icon, option.label); }))); }; /* Compact swatch-only colour trigger: a single dot (or a CircleSlash chip when transparent) that opens a swatch popover. The Inspector's ColorPickerField keeps the swatch + name; the floating toolbars use this swatch-only variant. `includeTransparent` is passed through to the swatch row — shape fill opts in, sticky fill and text colour do not. */ var ToolbarColorSwatch = exports.ToolbarColorSwatch = function ToolbarColorSwatch(_ref6) { var value = _ref6.value, includeTransparent = _ref6.includeTransparent, label = _ref6.label, onChange = _ref6.onChange; var _useState5 = (0, _react.useState)(false), _useState6 = _slicedToArray(_useState5, 2), open = _useState6[0], setOpen = _useState6[1]; var _useState7 = (0, _react.useState)(false), _useState8 = _slicedToArray(_useState7, 2), wheelOpen = _useState8[0], setWheelOpen = _useState8[1]; var isTransparent = value === "transparent"; return /*#__PURE__*/_react.default.createElement("span", { className: "tb-rel" }, /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, { title: label }, /*#__PURE__*/_react.default.createElement(StyledToolbarButton, { className: open ? "icon-only active" : "icon-only", "aria-label": label, onClick: function onClick() { setOpen(function (current) { return !current; }); setWheelOpen(false); } }, isTransparent ? /*#__PURE__*/_react.default.createElement(_Icons.CircleSlashIcon, { width: 16, height: 16, fill: "currentColor" }) : /*#__PURE__*/_react.default.createElement(_Dot.Dot, { color: value, size: 16 }))), open ? /*#__PURE__*/_react.default.createElement(StyledColorPopover, { className: "below" }, /*#__PURE__*/_react.default.createElement(ColorSwatchRow, { selectedColor: value, includeTransparent: includeTransparent, onSelect: function onSelect(color) { onChange(color); setOpen(false); }, onAddClick: function onAddClick() { return setWheelOpen(function (current) { return !current; }); } }), wheelOpen ? /*#__PURE__*/_react.default.createElement(StyledColorPopover, { className: "beside" }, /*#__PURE__*/_react.default.createElement(ColorWheel, { onColorChange: function onColorChange(color) { onChange(color); setOpen(false); } })) : null) : null); }; /* The text controls shared by the shape and sticky toolbars: font-size stepper, B / I / U / S, and the collapsed alignment menu. Same set the app's sticky and shape toolbars share (font-size picker, TextStyleButtons, AlignmentMenu). Kept generic so both element shapes can drive it. */ var ToolbarTextControls = exports.ToolbarTextControls = function ToolbarTextControls(_ref7) { var fontSize = _ref7.fontSize, styleFlags = _ref7.styleFlags, align = _ref7.align, onFontSize = _ref7.onFontSize, onToggleStyle = _ref7.onToggleStyle, onAlign = _ref7.onAlign; return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, { title: "Decrease font size" }, /*#__PURE__*/_react.default.createElement(StyledToolbarButton, { className: "icon-only", "aria-label": "Decrease font size", onClick: function onClick() { return onFontSize(Math.max(8, fontSize - 2)); } }, /*#__PURE__*/_react.default.createElement(_glyphs.MinusGlyph, { className: "i16" }))), /*#__PURE__*/_react.default.createElement(StyledFontSizeValue, null, fontSize), /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, { title: "Increase font size" }, /*#__PURE__*/_react.default.createElement(StyledToolbarButton, { className: "icon-only", "aria-label": "Increase font size", onClick: function onClick() { return onFontSize(Math.min(96, fontSize + 2)); } }, /*#__PURE__*/_react.default.createElement(_glyphs.PlusGlyph, { className: "i16" }))), STYLE_OPTIONS.map(function (option) { return /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, { key: option.key, title: option.label }, /*#__PURE__*/_react.default.createElement(StyledToolbarButton, { className: styleFlags[option.key] ? "icon-only active" : "icon-only", "aria-label": option.label, "aria-pressed": styleFlags[option.key], onClick: function onClick() { return onToggleStyle(option.key); } }, option.icon)); }), /*#__PURE__*/_react.default.createElement(AlignmentMenu, { value: align, onChange: onAlign })); }; /* ShapeFormattingToolbar — flat layout (was submenu-based): shape-type menu → fill colour → the same text controls as the sticky toolbar → Lock / Delete. Stroke is no longer edited here — it lives only in the Inspector's Stroke group now. Mirrors the app's ShapeFormattingToolbar. */ var ShapeFormattingToolbar = exports.ShapeFormattingToolbar = function ShapeFormattingToolbar(_ref8) { var _element$align; var element = _ref8.element, onUpdate = _ref8.onUpdate, onDelete = _ref8.onDelete, locked = _ref8.locked, onToggleLock = _ref8.onToggleLock; return /*#__PURE__*/_react.default.createElement(StyledFormattingToolbar, { onClick: stopClick, onMouseDown: stopClick }, /*#__PURE__*/_react.default.createElement(ShapeTypeMenu, { value: element.type, onChange: function onChange(type) { return onUpdate({ type }); } }), /*#__PURE__*/_react.default.createElement(ToolbarColorSwatch, { value: element.fill, includeTransparent: true, label: "Fill color", onChange: function onChange(fill) { return onUpdate({ fill }); } }), /*#__PURE__*/_react.default.createElement(ToolbarTextControls, { fontSize: element.fontSize, styleFlags: element, align: (_element$align = element.align) !== null && _element$align !== void 0 ? _element$align : "left", onFontSize: function onFontSize(fontSize) { return onUpdate({ fontSize }); }, onToggleStyle: function onToggleStyle(key) { return onUpdate({ [key]: !element[key] }); }, onAlign: function onAlign(align) { return onUpdate({ align }); } }), /*#__PURE__*/_react.default.createElement(ToolbarActions, { locked: locked, onToggleLock: onToggleLock, onDelete: onDelete })); }; /* PostitFormattingToolbar — flat layout: note (fill) colour → the shared text controls (font-size, B/I/U/S, alignment menu) → Lock / Delete. The note's colour is its whole point, so its fill swatch does NOT offer Transparent. Mirrors the app's PostitFormattingToolbar. */ var PostitFormattingToolbar = exports.PostitFormattingToolbar = function PostitFormattingToolbar(_ref9) { var value = _ref9.value, _onChange = _ref9.onChange, onDelete = _ref9.onDelete, locked = _ref9.locked, onToggleLock = _ref9.onToggleLock; return /*#__PURE__*/_react.default.createElement(StyledFormattingToolbar, { onClick: stopClick, onMouseDown: stopClick }, /*#__PURE__*/_react.default.createElement(ToolbarColorSwatch, { value: value.color, label: "Note color", onChange: function onChange(color) { return _onChange(_objectSpread(_objectSpread({}, value), {}, { color })); } }), /*#__PURE__*/_react.default.createElement(ToolbarTextControls, { fontSize: value.fontSize, styleFlags: value, align: value.align, onFontSize: function onFontSize(fontSize) { return _onChange(_objectSpread(_objectSpread({}, value), {}, { fontSize })); }, onToggleStyle: function onToggleStyle(key) { return _onChange(_objectSpread(_objectSpread({}, value), {}, { [key]: !value[key] })); }, onAlign: function onAlign(align) { return _onChange(_objectSpread(_objectSpread({}, value), {}, { align })); } }), /*#__PURE__*/_react.default.createElement(ToolbarActions, { locked: locked, onToggleLock: onToggleLock, onDelete: onDelete })); }; /* shape body per current (editable) type */ var renderShapeBody = exports.renderShapeBody = function renderShapeBody(def, state) { var width = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : def.width; var height = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : def.height; return state.type === "triangle" ? /*#__PURE__*/ /* TriangleElement: SVG polygon, apex top-center */ _react.default.createElement("svg", { width: "100%", height: "100%", style: { display: "block", overflow: "visible" } }, /*#__PURE__*/_react.default.createElement("polygon", { points: "".concat(width / 2, ",0 ").concat(width, ",").concat(height, " 0,").concat(height), fill: withOpacity(state.fill, state.fillOpacity), stroke: withOpacity(state.stroke, state.strokeOpacity), strokeWidth: state.strokeWidth, strokeDasharray: state.strokeDash })) : /*#__PURE__*/_react.default.createElement("div", { className: "shape-fill", style: { backgroundColor: withOpacity(state.fill, state.fillOpacity), border: shapeBorder(state), borderRadius: state.type === "circle" ? "50%" : state.borderRadius } }); }; var ALIGN_OPTIONS = exports.ALIGN_OPTIONS = [{ value: "left", label: "Align Left", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextAlignLeftIcon, null) }, { value: "center", label: "Align Center", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextAlignCenterIcon, null) }, { value: "right", label: "Align Right", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextAlignRightIcon, null) }, { value: "justify", label: "Justify", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextAlignJustifyIcon, null) }]; var STYLE_OPTIONS = exports.STYLE_OPTIONS = [{ key: "bold", label: "Bold", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextStyleBoldIcon, null) }, { key: "italic", label: "Italic", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextStyleItalicIcon, null) }, { key: "underline", label: "Underline", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextStyleUnderlineIcon, null) }, { key: "strikethrough", label: "Strikethrough", icon: /*#__PURE__*/_react.default.createElement(_Icons.TextStyleStrikeIcon, null) }]; //# sourceMappingURL=elements.js.map