@activecollab/components
Version:
ActiveCollab Components
816 lines (778 loc) • 39.8 kB
JavaScript
"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