@activecollab/components 2.0.398 → 2.0.400

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/cjs/presentation/whiteboard/bottomDock.js +194 -0
  2. package/dist/cjs/presentation/whiteboard/bottomDock.js.map +1 -0
  3. package/dist/cjs/presentation/whiteboard/canvasElements.js +877 -0
  4. package/dist/cjs/presentation/whiteboard/canvasElements.js.map +1 -0
  5. package/dist/cjs/presentation/whiteboard/elements.js +816 -0
  6. package/dist/cjs/presentation/whiteboard/elements.js.map +1 -0
  7. package/dist/cjs/presentation/whiteboard/fileElement.js +457 -0
  8. package/dist/cjs/presentation/whiteboard/fileElement.js.map +1 -0
  9. package/dist/cjs/presentation/whiteboard/glyphs.js +67 -0
  10. package/dist/cjs/presentation/whiteboard/glyphs.js.map +1 -0
  11. package/dist/cjs/presentation/whiteboard/inspector.js +884 -0
  12. package/dist/cjs/presentation/whiteboard/inspector.js.map +1 -0
  13. package/dist/cjs/presentation/whiteboard/linkElement.js +357 -0
  14. package/dist/cjs/presentation/whiteboard/linkElement.js.map +1 -0
  15. package/dist/cjs/presentation/whiteboard/outline.js +966 -0
  16. package/dist/cjs/presentation/whiteboard/outline.js.map +1 -0
  17. package/dist/cjs/presentation/whiteboard/peopleMenu.js +516 -0
  18. package/dist/cjs/presentation/whiteboard/peopleMenu.js.map +1 -0
  19. package/dist/cjs/presentation/whiteboard/settingsMenu.js +177 -0
  20. package/dist/cjs/presentation/whiteboard/settingsMenu.js.map +1 -0
  21. package/dist/esm/presentation/whiteboard/bottomDock.d.ts +51 -0
  22. package/dist/esm/presentation/whiteboard/bottomDock.d.ts.map +1 -0
  23. package/dist/esm/presentation/whiteboard/bottomDock.js +162 -0
  24. package/dist/esm/presentation/whiteboard/bottomDock.js.map +1 -0
  25. package/dist/esm/presentation/whiteboard/canvasElements.d.ts +61 -0
  26. package/dist/esm/presentation/whiteboard/canvasElements.d.ts.map +1 -0
  27. package/dist/esm/presentation/whiteboard/canvasElements.js +746 -0
  28. package/dist/esm/presentation/whiteboard/canvasElements.js.map +1 -0
  29. package/dist/esm/presentation/whiteboard/elements.d.ts +180 -0
  30. package/dist/esm/presentation/whiteboard/elements.d.ts.map +1 -0
  31. package/dist/esm/presentation/whiteboard/elements.js +736 -0
  32. package/dist/esm/presentation/whiteboard/elements.js.map +1 -0
  33. package/dist/esm/presentation/whiteboard/fileElement.d.ts +113 -0
  34. package/dist/esm/presentation/whiteboard/fileElement.d.ts.map +1 -0
  35. package/dist/esm/presentation/whiteboard/fileElement.js +430 -0
  36. package/dist/esm/presentation/whiteboard/fileElement.js.map +1 -0
  37. package/dist/esm/presentation/whiteboard/glyphs.d.ts +32 -0
  38. package/dist/esm/presentation/whiteboard/glyphs.d.ts.map +1 -0
  39. package/dist/esm/presentation/whiteboard/glyphs.js +50 -0
  40. package/dist/esm/presentation/whiteboard/glyphs.js.map +1 -0
  41. package/dist/esm/presentation/whiteboard/inspector.d.ts +100 -0
  42. package/dist/esm/presentation/whiteboard/inspector.d.ts.map +1 -0
  43. package/dist/esm/presentation/whiteboard/inspector.js +753 -0
  44. package/dist/esm/presentation/whiteboard/inspector.js.map +1 -0
  45. package/dist/esm/presentation/whiteboard/linkElement.d.ts +97 -0
  46. package/dist/esm/presentation/whiteboard/linkElement.d.ts.map +1 -0
  47. package/dist/esm/presentation/whiteboard/linkElement.js +327 -0
  48. package/dist/esm/presentation/whiteboard/linkElement.js.map +1 -0
  49. package/dist/esm/presentation/whiteboard/outline.d.ts +89 -0
  50. package/dist/esm/presentation/whiteboard/outline.d.ts.map +1 -0
  51. package/dist/esm/presentation/whiteboard/outline.js +870 -0
  52. package/dist/esm/presentation/whiteboard/outline.js.map +1 -0
  53. package/dist/esm/presentation/whiteboard/peopleMenu.d.ts +44 -0
  54. package/dist/esm/presentation/whiteboard/peopleMenu.d.ts.map +1 -0
  55. package/dist/esm/presentation/whiteboard/peopleMenu.js +440 -0
  56. package/dist/esm/presentation/whiteboard/peopleMenu.js.map +1 -0
  57. package/dist/esm/presentation/whiteboard/settingsMenu.d.ts +13 -0
  58. package/dist/esm/presentation/whiteboard/settingsMenu.d.ts.map +1 -0
  59. package/dist/esm/presentation/whiteboard/settingsMenu.js +155 -0
  60. package/dist/esm/presentation/whiteboard/settingsMenu.js.map +1 -0
  61. package/package.json +1 -1
@@ -0,0 +1,753 @@
1
+ import _extends from "@babel/runtime/helpers/esm/extends";
2
+ import React, { useCallback, useState } from "react";
3
+ import styled from "styled-components";
4
+ import { BORDER_STYLE_OPTIONS, CANVAS_ELEMENTS, COLOR_OPTIONS, ELEMENT_BODIES, FONT_SIZE_OPTIONS, OPACITY_OPTIONS, RADIUS_OPTIONS, SHAPE_TYPE_ICON, THICKNESS_OPTIONS, ALIGN_OPTIONS, STYLE_OPTIONS } from "./elements";
5
+ import { formatFileSize } from "./fileElement";
6
+ import { OUTLINE_MAX_WIDTH, OUTLINE_MIN_WIDTH, OUTLINE_WIDTH, StyledChooseRow, StyledPanelChooseSlot } from "./outline";
7
+ import { Checkbox } from "../../components/Checkbox";
8
+ import { ChooseV2 } from "../../components/ChooseV2";
9
+ import { Dot } from "../../components/Dot";
10
+ import { EditableText } from "../../components/EditableText";
11
+ import { IconButton } from "../../components/IconButton";
12
+ import { AttachmentIcon, CircleSlashIcon, InsertLinkIcon, NoteIcon } from "../../components/Icons";
13
+ import { Input, InputAdornment, InputNumber } from "../../components/Input";
14
+ import { Label } from "../../components/Label";
15
+ import { Select } from "../../components/Select";
16
+ import { SelectTrigger } from "../../components/SelectTrigger";
17
+ import { Tooltip } from "../../components/Tooltip";
18
+ import { Body2, Caption1 } from "../../components/Typography";
19
+
20
+ /* ------------------------------------------------------------------ */
21
+ /* Right Inspector — selected-element properties panel */
22
+ /* ------------------------------------------------------------------ */
23
+
24
+ /* Which right-docked panel is open. Only "inspector" is built here; "comments"
25
+ is reserved for the next iteration. */
26
+
27
+ /* ── Inspector capability traits ──────────────────────────────────────────
28
+ The Inspector is TRAIT-DRIVEN: a selected element declares which property
29
+ groups it supports, and only those groups render. This is the "interface
30
+ with implementation traits" model — the interface is the full set of groups,
31
+ and each element kind overrides the flags it does not support (a sticky has
32
+ no stroke, and its height is auto because the note grows with its content).
33
+ New element kinds slot in by returning their own trait set. */
34
+
35
+ export const elementTraits = def => def.kind === "sticky" ? {
36
+ geometry: true,
37
+ fill: true,
38
+ fillTransparent: false,
39
+ stroke: false,
40
+ text: true,
41
+ autoHeight: true
42
+ } : {
43
+ geometry: true,
44
+ fill: true,
45
+ fillTransparent: true,
46
+ stroke: true,
47
+ text: true
48
+ };
49
+ export const TYPE_LABEL = {
50
+ rectangle: "Rectangle",
51
+ roundedRectangle: "Rounded Rectangle",
52
+ circle: "Circle",
53
+ triangle: "Triangle"
54
+ };
55
+ export const sameColor = (a, b) => (a != null ? a : "").toLowerCase() === b.toLowerCase();
56
+
57
+ /* Right dock — mirrors StyledOutlineSidebar but docked on the RIGHT: border on
58
+ the left edge, resizer on the left edge. Same 260–780 width band. */
59
+ export const StyledInspectorSidebar = styled.aside.withConfig({
60
+ displayName: "inspector__StyledInspectorSidebar",
61
+ componentId: "sc-do334k-0"
62
+ })(["flex-shrink:0;position:relative;box-sizing:border-box;display:flex;flex-direction:column;border-left:1px solid var(--selectable);background-color:var(--page-paper-main);font-family:-apple-system,BlinkMacSystemFont,\"Roboto\",\"Helvetica Neue\",Arial,sans-serif;"]);
63
+
64
+ /* Grab strip on the LEFT edge, because the panel is docked right — dragging it
65
+ left widens. Same token pair as the outline resizer: selectable grey at
66
+ rest, the hover token while grabbed. */
67
+ export const StyledInspectorResizer = styled.div.withConfig({
68
+ displayName: "inspector__StyledInspectorResizer",
69
+ componentId: "sc-do334k-1"
70
+ })(["position:absolute;top:0;bottom:0;left:-2px;width:5px;cursor:col-resize;z-index:2;&::after{content:\"\";position:absolute;top:0;bottom:0;left:2px;width:1px;background-color:var(--selectable-active);opacity:0;transition:opacity 150ms ease;}&:hover::after,&.resizing::after{opacity:1;}"]);
71
+ export const StyledInspectorBody = styled.div.withConfig({
72
+ displayName: "inspector__StyledInspectorBody",
73
+ componentId: "sc-do334k-2"
74
+ })(["flex:1;overflow-y:auto;display:flex;flex-direction:column;"]);
75
+
76
+ /* Wraps the property groups. The Properties/Comments switch above does the
77
+ separating, so the first group drops its top hairline — no divider directly
78
+ under the header, matching the left sidebar. Inner groups keep separators. */
79
+ export const StyledPropGroups = styled.div.withConfig({
80
+ displayName: "inspector__StyledPropGroups",
81
+ componentId: "sc-do334k-3"
82
+ })(["display:flex;flex-direction:column;> div:first-child{border-top:none;}"]);
83
+ export const StyledInspectorHeader = styled.div.withConfig({
84
+ displayName: "inspector__StyledInspectorHeader",
85
+ componentId: "sc-do334k-4"
86
+ })(["display:flex;align-items:center;gap:8px;height:56px;padding:0 16px;box-sizing:border-box;flex-shrink:0;.inspector-type-icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;flex-shrink:0;color:var(--color-theme-700);}.inspector-type-icon svg{width:20px;height:20px;}.inspector-name{flex:1;min-width:0;}"]);
87
+
88
+ /* Empty state — same muted treatment the outline uses for unnamed rows. */
89
+ export const StyledInspectorEmpty = styled.div.withConfig({
90
+ displayName: "inspector__StyledInspectorEmpty",
91
+ componentId: "sc-do334k-5"
92
+ })(["flex:1;display:flex;align-items:center;justify-content:center;padding:24px;text-align:center;color:var(--color-theme-600);"]);
93
+
94
+ /* Non-collapsible section: bold title, no chevron / drag handle, separated from
95
+ the section above by a hairline. */
96
+ export const StyledPropGroup = styled.div.withConfig({
97
+ displayName: "inspector__StyledPropGroup",
98
+ componentId: "sc-do334k-6"
99
+ })(["display:flex;flex-direction:column;gap:12px;padding:16px;border-top:1px solid var(--border-primary);.group-title{color:var(--color-theme-800);}"]);
100
+ export const StyledPropRow = styled.div.withConfig({
101
+ displayName: "inspector__StyledPropRow",
102
+ componentId: "sc-do334k-7"
103
+ })(["display:flex;flex-direction:column;gap:4px;"]);
104
+ export const StyledFieldGrid = styled.div.withConfig({
105
+ displayName: "inspector__StyledFieldGrid",
106
+ componentId: "sc-do334k-8"
107
+ })(["display:grid;grid-template-columns:1fr 1fr;gap:12px;"]);
108
+
109
+ /* InputNumber's wrapper is a fixed 360px and SelectTrigger a fixed 300px — both
110
+ overflow the narrow dock, so force every inspector control to fill its cell. */
111
+ export const StyledControl = styled.div.withConfig({
112
+ displayName: "inspector__StyledControl",
113
+ componentId: "sc-do334k-9"
114
+ })(["width:100%;&.disabled{opacity:0.4;pointer-events:none;}.c-input-wrapper{width:100%;}.wb-inspector-trigger{width:100%;}.wb-color-value{display:flex;align-items:center;gap:8px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.wb-color-value .wb-transparent-swatch{flex:none;color:var(--color-theme-500);}"]);
115
+
116
+ /* Locked checkbox row — the DS Checkbox renders only the box, so it is paired
117
+ with a Label the way the design system expects. */
118
+ export const StyledCheckboxRow = styled.div.withConfig({
119
+ displayName: "inspector__StyledCheckboxRow",
120
+ componentId: "sc-do334k-10"
121
+ })(["display:flex;align-items:center;gap:8px;"]);
122
+
123
+ /* The element body is HTML (the API's `body` field). The Inspector renders it
124
+ formatted and read-only for now — editing comes later. Raw text needs an
125
+ explicit font-family (no global reset), and the child tags get light prose
126
+ styling. */
127
+ export const StyledBody = styled.div.withConfig({
128
+ displayName: "inspector__StyledBody",
129
+ componentId: "sc-do334k-11"
130
+ })(["width:100%;max-height:140px;overflow-y:auto;font-family:-apple-system,BlinkMacSystemFont,\"Roboto\",\"Helvetica Neue\",Arial,sans-serif;font-size:13px;line-height:1.5;color:var(--color-theme-800);p{margin:0 0 8px;}p:last-child{margin-bottom:0;}ul,ol{margin:0 0 8px;padding-left:18px;}li{margin:2px 0;}strong{font-weight:600;}em{font-style:italic;}a{color:var(--color-primary);}"]);
131
+
132
+ /* Read-only "Auto" field for an auto-sized dimension (a sticky's height). */
133
+ export const StyledAutoField = styled.div.withConfig({
134
+ displayName: "inspector__StyledAutoField",
135
+ componentId: "sc-do334k-12"
136
+ })(["height:32px;display:flex;align-items:center;padding:0 8px;box-sizing:border-box;border:1px solid var(--color-theme-400);border-radius:var(--ac-br-8);background-color:var(--color-theme-200);color:var(--color-theme-600);font-size:13px;"]);
137
+ export const StyledSegmented = styled.div.withConfig({
138
+ displayName: "inspector__StyledSegmented",
139
+ componentId: "sc-do334k-13"
140
+ })(["display:flex;gap:4px;svg{display:block;width:16px;height:16px;}"]);
141
+ export const NumberField = _ref => {
142
+ let label = _ref.label,
143
+ value = _ref.value,
144
+ min = _ref.min,
145
+ unit = _ref.unit,
146
+ onCommit = _ref.onCommit;
147
+ return /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
148
+ size: "small",
149
+ weight: "regular"
150
+ }, label), /*#__PURE__*/React.createElement(StyledControl, null, /*#__PURE__*/React.createElement(InputNumber, {
151
+ className: "wb-inspector-num",
152
+ value: value,
153
+ decimalLength: 0,
154
+ min: min,
155
+ update: true,
156
+ endAdornment: unit ? /*#__PURE__*/React.createElement(InputAdornment, {
157
+ disablePointerEvents: true
158
+ }, /*#__PURE__*/React.createElement(Caption1, {
159
+ color: "secondary"
160
+ }, unit)) : undefined,
161
+ onChange: next => {
162
+ if (next !== undefined && !Number.isNaN(next)) {
163
+ onCommit(Math.round(next));
164
+ }
165
+ }
166
+ })));
167
+ };
168
+ export const AutoField = _ref2 => {
169
+ let label = _ref2.label;
170
+ return /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
171
+ size: "small",
172
+ weight: "regular"
173
+ }, label), /*#__PURE__*/React.createElement(StyledAutoField, null, "Auto"));
174
+ };
175
+ export const OptionSelect = _ref3 => {
176
+ let options = _ref3.options,
177
+ value = _ref3.value,
178
+ triggerLabel = _ref3.triggerLabel,
179
+ onChange = _ref3.onChange;
180
+ return /*#__PURE__*/React.createElement(StyledControl, null, /*#__PURE__*/React.createElement(Select, {
181
+ type: "single",
182
+ disableSearch: true,
183
+ forceCloseMenu: true,
184
+ options: options,
185
+ selected: value,
186
+ onChange: next => onChange(Array.isArray(next) ? next[0] : next),
187
+ target: /*#__PURE__*/React.createElement(SelectTrigger, {
188
+ className: "wb-inspector-trigger"
189
+ }, triggerLabel)
190
+ }));
191
+ };
192
+ export const TRANSPARENT_OPTION = {
193
+ id: "transparent",
194
+ name: "Transparent",
195
+ color: "transparent",
196
+ textColor: "transparent"
197
+ };
198
+
199
+ /* The name shown on the color picker's trigger, resolved from the current hex. */
200
+ export const colorName = hex => {
201
+ if (!hex || sameColor(hex, "transparent")) {
202
+ return "Transparent";
203
+ }
204
+ const match = COLOR_OPTIONS.find(option => sameColor(hex, option.color));
205
+ return match ? match.name : "Custom";
206
+ };
207
+ /* The regular design-system named-colour picker (the one in the label dialogs):
208
+ a Select whose trigger is a Dot + colour name, and whose menu is the standard
209
+ dot-and-radio list. Custom colours are deferred. */
210
+ export const ColorPickerField = _ref4 => {
211
+ let value = _ref4.value,
212
+ disabled = _ref4.disabled,
213
+ includeTransparent = _ref4.includeTransparent,
214
+ onSelect = _ref4.onSelect;
215
+ const options = includeTransparent ? [TRANSPARENT_OPTION, ...COLOR_OPTIONS] : COLOR_OPTIONS;
216
+ const isTransparent = !value || sameColor(value, "transparent");
217
+ return /*#__PURE__*/React.createElement(StyledControl, {
218
+ className: disabled ? "disabled" : undefined
219
+ }, /*#__PURE__*/React.createElement(Select, {
220
+ type: "single",
221
+ disableSearch: true,
222
+ forceCloseMenu: true,
223
+ options: options,
224
+ selected: isTransparent ? "transparent" : value,
225
+ onChange: next => onSelect(String(Array.isArray(next) ? next[0] : next)),
226
+ target: /*#__PURE__*/React.createElement(SelectTrigger, {
227
+ className: "wb-inspector-trigger"
228
+ }, /*#__PURE__*/React.createElement("span", {
229
+ className: "wb-color-value"
230
+ }, isTransparent ? /*#__PURE__*/React.createElement(CircleSlashIcon, {
231
+ width: 14,
232
+ height: 14,
233
+ fill: "currentColor",
234
+ className: "wb-transparent-swatch"
235
+ }) : /*#__PURE__*/React.createElement(Dot, {
236
+ color: value,
237
+ size: 14
238
+ }), colorName(value)))
239
+ }));
240
+ };
241
+ export const RightInspector = _ref5 => {
242
+ var _sizes$def$id, _positions$def$id, _rotations$def$id, _names$def$id, _shape$align, _BORDER_STYLE_OPTIONS, _BORDER_STYLE_OPTIONS2;
243
+ let selectedId = _ref5.selectedId,
244
+ positions = _ref5.positions,
245
+ setPositions = _ref5.setPositions,
246
+ sizes = _ref5.sizes,
247
+ setSizes = _ref5.setSizes,
248
+ rotations = _ref5.rotations,
249
+ setRotations = _ref5.setRotations,
250
+ shapes = _ref5.shapes,
251
+ setShapes = _ref5.setShapes,
252
+ stickyStyle = _ref5.stickyStyle,
253
+ setStickyStyle = _ref5.setStickyStyle,
254
+ links = _ref5.links,
255
+ setLinks = _ref5.setLinks,
256
+ files = _ref5.files,
257
+ setFiles = _ref5.setFiles,
258
+ createdShapes = _ref5.createdShapes,
259
+ locks = _ref5.locks,
260
+ setLocks = _ref5.setLocks;
261
+ const _useState = useState(OUTLINE_WIDTH),
262
+ width = _useState[0],
263
+ setWidth = _useState[1];
264
+ const _useState2 = useState(false),
265
+ resizing = _useState2[0],
266
+ setResizing = _useState2[1];
267
+ /* Mock only — element display names are not persisted anywhere; they default
268
+ to the type label until renamed here. */
269
+ const _useState3 = useState({}),
270
+ names = _useState3[0],
271
+ setNames = _useState3[1];
272
+ /* Which inspector tab is showing: the property groups we design, or the
273
+ upcoming comments stream (details TBD). */
274
+ const _useState4 = useState("properties"),
275
+ tab = _useState4[0],
276
+ setTab = _useState4[1];
277
+
278
+ /* Docked right, so the grab strip is on the LEFT edge and dragging left
279
+ widens: the delta is subtracted rather than added (see LeftSidebar). */
280
+ const startResize = useCallback(event => {
281
+ var _event$currentTarget$, _event$currentTarget$2;
282
+ event.preventDefault();
283
+ const startX = event.clientX;
284
+ const startWidth = (_event$currentTarget$ = (_event$currentTarget$2 = event.currentTarget.parentElement) == null ? void 0 : _event$currentTarget$2.offsetWidth) != null ? _event$currentTarget$ : 0;
285
+ const previousUserSelect = document.body.style.userSelect;
286
+ setResizing(true);
287
+ document.body.style.userSelect = "none";
288
+ document.body.style.cursor = "col-resize";
289
+ const handleMove = moveEvent => {
290
+ const next = startWidth - (moveEvent.clientX - startX);
291
+ setWidth(Math.min(OUTLINE_MAX_WIDTH, Math.max(OUTLINE_MIN_WIDTH, next)));
292
+ };
293
+ const handleUp = () => {
294
+ setResizing(false);
295
+ document.body.style.userSelect = previousUserSelect;
296
+ document.body.style.cursor = "";
297
+ window.removeEventListener("mousemove", handleMove);
298
+ window.removeEventListener("mouseup", handleUp);
299
+ };
300
+ window.addEventListener("mousemove", handleMove);
301
+ window.addEventListener("mouseup", handleUp);
302
+ }, []);
303
+ const def = selectedId ? [...CANVAS_ELEMENTS, ...createdShapes].find(element => element.id === selectedId) : undefined;
304
+ const linkDef = selectedId ? links.find(link => link.id === selectedId) : undefined;
305
+ const fileDef = selectedId ? files.find(file => file.id === selectedId) : undefined;
306
+ const shell = content => /*#__PURE__*/React.createElement(StyledInspectorSidebar, {
307
+ style: {
308
+ width
309
+ }
310
+ }, /*#__PURE__*/React.createElement(StyledInspectorResizer, {
311
+ className: resizing ? "resizing" : "",
312
+ onMouseDown: startResize,
313
+ role: "separator",
314
+ "aria-orientation": "vertical",
315
+ "aria-label": "Resize inspector"
316
+ }), content);
317
+
318
+ /* Description — inline, multi-line editable text, shown first for every
319
+ element kind. */
320
+ /* Read-only formatted body (HTML from the API's `body` field). No label; when
321
+ the body is empty the field is not rendered at all. No edit yet. */
322
+ const renderBody = id => {
323
+ var _ELEMENT_BODIES$id;
324
+ const html = (_ELEMENT_BODIES$id = ELEMENT_BODIES[id]) != null ? _ELEMENT_BODIES$id : "";
325
+ if (!html) {
326
+ return null;
327
+ }
328
+
329
+ // eslint-disable-next-line react/no-danger
330
+ return /*#__PURE__*/React.createElement(StyledBody, {
331
+ dangerouslySetInnerHTML: {
332
+ __html: html
333
+ }
334
+ });
335
+ };
336
+
337
+ /* The Locked checkbox — shown for every element kind, toggling a design lock
338
+ flag keyed by element id. */
339
+ const renderLocked = id => {
340
+ var _locks$id;
341
+ return /*#__PURE__*/React.createElement(StyledCheckboxRow, null, /*#__PURE__*/React.createElement(Checkbox, {
342
+ id: "wb-lock-" + id,
343
+ checked: (_locks$id = locks[id]) != null ? _locks$id : false,
344
+ onChange: () => setLocks(current => {
345
+ var _current$id;
346
+ return _extends({}, current, {
347
+ [id]: !((_current$id = current[id]) != null ? _current$id : false)
348
+ });
349
+ })
350
+ }), /*#__PURE__*/React.createElement(Label, {
351
+ htmlFor: "wb-lock-" + id,
352
+ size: "small",
353
+ weight: "regular"
354
+ }, "Locked"));
355
+ };
356
+
357
+ /* Geometry for a card (link / file): X/Y from the shared positions map, an
358
+ editable width and an auto height (cards are content-sized). Cards cannot
359
+ be rotated, so there is no Rotation field. */
360
+ const renderCardGeometry = (id, cardWidth, onWidth) => {
361
+ var _positions$id;
362
+ const cardPosition = (_positions$id = positions[id]) != null ? _positions$id : {
363
+ x: 0,
364
+ y: 0
365
+ };
366
+ return /*#__PURE__*/React.createElement(StyledPropGroup, null, /*#__PURE__*/React.createElement(Body2, {
367
+ weight: "bold",
368
+ className: "group-title"
369
+ }, "Geometry"), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(NumberField, {
370
+ label: "X",
371
+ unit: "px",
372
+ value: cardPosition.x,
373
+ onCommit: next => setPositions(current => {
374
+ var _current$id2;
375
+ return _extends({}, current, {
376
+ [id]: _extends({}, (_current$id2 = current[id]) != null ? _current$id2 : {
377
+ x: 0,
378
+ y: 0
379
+ }, {
380
+ x: next
381
+ })
382
+ });
383
+ })
384
+ }), /*#__PURE__*/React.createElement(NumberField, {
385
+ label: "Y",
386
+ unit: "px",
387
+ value: cardPosition.y,
388
+ onCommit: next => setPositions(current => {
389
+ var _current$id3;
390
+ return _extends({}, current, {
391
+ [id]: _extends({}, (_current$id3 = current[id]) != null ? _current$id3 : {
392
+ x: 0,
393
+ y: 0
394
+ }, {
395
+ y: next
396
+ })
397
+ });
398
+ })
399
+ })), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(NumberField, {
400
+ label: "Width",
401
+ unit: "px",
402
+ value: cardWidth,
403
+ min: 1,
404
+ onCommit: onWidth
405
+ }), /*#__PURE__*/React.createElement(AutoField, {
406
+ label: "Height"
407
+ })));
408
+ };
409
+
410
+ /* Header + panel switch + body, shared by every element kind — only the
411
+ groups inside differ. */
412
+ const renderInspectorBody = (icon, headerName, id, groups) => /*#__PURE__*/React.createElement(StyledInspectorBody, null, /*#__PURE__*/React.createElement(StyledInspectorHeader, null, /*#__PURE__*/React.createElement("span", {
413
+ className: "inspector-type-icon"
414
+ }, icon), /*#__PURE__*/React.createElement("span", {
415
+ className: "inspector-name"
416
+ }, /*#__PURE__*/React.createElement(EditableText, {
417
+ value: headerName,
418
+ variant: "Header 3",
419
+ weight: "bold",
420
+ onSave: event => setNames(current => _extends({}, current, {
421
+ [id]: event.target.value
422
+ })),
423
+ inputProps: {
424
+ maxLength: 120
425
+ }
426
+ }))), /*#__PURE__*/React.createElement(StyledChooseRow, null, /*#__PURE__*/React.createElement(StyledPanelChooseSlot, null, /*#__PURE__*/React.createElement(ChooseV2, {
427
+ required: true,
428
+ options: [{
429
+ id: "properties",
430
+ name: "Properties"
431
+ }, {
432
+ id: "comments",
433
+ name: "Comments"
434
+ }],
435
+ selected: [tab],
436
+ onChange: selected => setTab(selected[0])
437
+ }))), tab === "comments" ? /*#__PURE__*/React.createElement(StyledInspectorEmpty, null, /*#__PURE__*/React.createElement(Body2, null, "The comments stream is coming soon.")) : /*#__PURE__*/React.createElement(StyledPropGroups, null, groups));
438
+
439
+ /* Nothing selected. */
440
+ if (!def && !linkDef && !fileDef) {
441
+ return shell(/*#__PURE__*/React.createElement(StyledInspectorEmpty, null, /*#__PURE__*/React.createElement(Body2, null, "Select an element to see its properties.")));
442
+ }
443
+
444
+ /* Link card — its URL plus the shared Locked flag. */
445
+ if (linkDef) {
446
+ var _names$linkDef$id, _linkDef$url;
447
+ return shell(renderInspectorBody(/*#__PURE__*/React.createElement(InsertLinkIcon, null), (_names$linkDef$id = names[linkDef.id]) != null ? _names$linkDef$id : linkDef.url || "Link", linkDef.id, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledPropGroup, null, renderBody(linkDef.id), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
448
+ size: "small",
449
+ weight: "regular"
450
+ }, "URL"), /*#__PURE__*/React.createElement(StyledControl, null, /*#__PURE__*/React.createElement(Input, {
451
+ value: (_linkDef$url = linkDef.url) != null ? _linkDef$url : "",
452
+ placeholder: "https://\u2026",
453
+ onChange: event => setLinks(current => current.map(link => link.id === linkDef.id ? _extends({}, link, {
454
+ url: event.target.value
455
+ }) : link))
456
+ }))), renderLocked(linkDef.id)), renderCardGeometry(linkDef.id, linkDef.width, width => setLinks(current => current.map(link => link.id === linkDef.id ? _extends({}, link, {
457
+ width
458
+ }) : link))))));
459
+ }
460
+
461
+ /* File card — read-only size plus the shared Locked flag. */
462
+ if (fileDef) {
463
+ var _names$fileDef$id;
464
+ return shell(renderInspectorBody(/*#__PURE__*/React.createElement(AttachmentIcon, null), (_names$fileDef$id = names[fileDef.id]) != null ? _names$fileDef$id : fileDef.file ? fileDef.file.base + "." + fileDef.file.extension : "File", fileDef.id, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledPropGroup, null, renderBody(fileDef.id), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
465
+ size: "small",
466
+ weight: "regular"
467
+ }, "Size"), /*#__PURE__*/React.createElement(StyledAutoField, null, fileDef.file ? formatFileSize(fileDef.file.size) : "—")), renderLocked(fileDef.id)), renderCardGeometry(fileDef.id, fileDef.width, width => setFiles(current => current.map(file => file.id === fileDef.id ? _extends({}, file, {
468
+ width
469
+ }) : file))))));
470
+ }
471
+
472
+ /* Shape / sticky from here on. */
473
+ if (!def) {
474
+ return shell(/*#__PURE__*/React.createElement(StyledInspectorEmpty, null, /*#__PURE__*/React.createElement(Body2, null, "Select an element to see its properties.")));
475
+ }
476
+ const traits = elementTraits(def);
477
+ const shape = def.kind === "shape" ? shapes[def.id] : null;
478
+ const size = (_sizes$def$id = sizes[def.id]) != null ? _sizes$def$id : {
479
+ width: def.width,
480
+ height: def.height
481
+ };
482
+ const position = (_positions$def$id = positions[def.id]) != null ? _positions$def$id : {
483
+ x: def.x,
484
+ y: def.y
485
+ };
486
+ const rotation = (_rotations$def$id = rotations[def.id]) != null ? _rotations$def$id : 0;
487
+ const typeLabel = shape ? TYPE_LABEL[shape.type] : "Sticky note";
488
+ const name = (_names$def$id = names[def.id]) != null ? _names$def$id : typeLabel;
489
+ const updateShape = partial => setShapes(current => _extends({}, current, {
490
+ [def.id]: _extends({}, current[def.id], partial)
491
+ }));
492
+
493
+ /* Fill is unified across kinds: a shape writes shape.fill, a sticky its note
494
+ colour. A transparent colour means no fill. */
495
+ const currentFill = shape ? shape.fill : stickyStyle.color;
496
+ const currentFillOpacity = shape ? shape.fillOpacity : stickyStyle.fillOpacity;
497
+ const setFill = color => {
498
+ if (shape) {
499
+ updateShape({
500
+ fill: color
501
+ });
502
+ } else {
503
+ setStickyStyle(_extends({}, stickyStyle, {
504
+ color
505
+ }));
506
+ }
507
+ };
508
+ const setFillOpacity = next => {
509
+ if (shape) {
510
+ updateShape({
511
+ fillOpacity: next
512
+ });
513
+ } else {
514
+ setStickyStyle(_extends({}, stickyStyle, {
515
+ fillOpacity: next
516
+ }));
517
+ }
518
+ };
519
+ const currentFontSize = shape ? shape.fontSize : stickyStyle.fontSize;
520
+ const currentAlign = shape ? (_shape$align = shape.align) != null ? _shape$align : "left" : stickyStyle.align;
521
+ const setFontSize = next => {
522
+ if (shape) {
523
+ updateShape({
524
+ fontSize: next
525
+ });
526
+ } else {
527
+ setStickyStyle(_extends({}, stickyStyle, {
528
+ fontSize: next
529
+ }));
530
+ }
531
+ };
532
+ const setAlign = next => {
533
+ if (shape) {
534
+ updateShape({
535
+ align: next
536
+ });
537
+ } else {
538
+ setStickyStyle(_extends({}, stickyStyle, {
539
+ align: next
540
+ }));
541
+ }
542
+ };
543
+ const styleFlags = shape != null ? shape : stickyStyle;
544
+ const toggleStyle = key => {
545
+ if (shape) {
546
+ updateShape({
547
+ [key]: !shape[key]
548
+ });
549
+ } else {
550
+ setStickyStyle(_extends({}, stickyStyle, {
551
+ [key]: !stickyStyle[key]
552
+ }));
553
+ }
554
+ };
555
+ return shell(renderInspectorBody(shape ? SHAPE_TYPE_ICON[shape.type] : /*#__PURE__*/React.createElement(NoteIcon, null), name, def.id, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledPropGroup, null, renderBody(def.id), shape ? /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
556
+ size: "small",
557
+ weight: "regular"
558
+ }, "Shape"), /*#__PURE__*/React.createElement(OptionSelect, {
559
+ options: ["rectangle", "roundedRectangle", "circle", "triangle"].map(shapeType => ({
560
+ id: shapeType,
561
+ name: TYPE_LABEL[shapeType]
562
+ })),
563
+ value: shape.type,
564
+ triggerLabel: TYPE_LABEL[shape.type],
565
+ onChange: next => updateShape({
566
+ type: next
567
+ })
568
+ })) : null, renderLocked(def.id)), traits.geometry ? /*#__PURE__*/React.createElement(StyledPropGroup, null, /*#__PURE__*/React.createElement(Body2, {
569
+ weight: "bold",
570
+ className: "group-title"
571
+ }, "Geometry"), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(NumberField, {
572
+ label: "X",
573
+ unit: "px",
574
+ value: position.x,
575
+ onCommit: next => setPositions(current => _extends({}, current, {
576
+ [def.id]: _extends({}, current[def.id], {
577
+ x: next
578
+ })
579
+ }))
580
+ }), /*#__PURE__*/React.createElement(NumberField, {
581
+ label: "Y",
582
+ unit: "px",
583
+ value: position.y,
584
+ onCommit: next => setPositions(current => _extends({}, current, {
585
+ [def.id]: _extends({}, current[def.id], {
586
+ y: next
587
+ })
588
+ }))
589
+ })), /*#__PURE__*/React.createElement(StyledFieldGrid, null, traits.autoWidth ? /*#__PURE__*/React.createElement(AutoField, {
590
+ label: "Width"
591
+ }) : /*#__PURE__*/React.createElement(NumberField, {
592
+ label: "Width",
593
+ unit: "px",
594
+ value: size.width,
595
+ min: 1,
596
+ onCommit: next => setSizes(current => {
597
+ var _current$def$id$heigh, _current$def$id;
598
+ return _extends({}, current, {
599
+ [def.id]: {
600
+ width: next,
601
+ height: (_current$def$id$heigh = (_current$def$id = current[def.id]) == null ? void 0 : _current$def$id.height) != null ? _current$def$id$heigh : def.height
602
+ }
603
+ });
604
+ })
605
+ }), traits.autoHeight ? /*#__PURE__*/React.createElement(AutoField, {
606
+ label: "Height"
607
+ }) : /*#__PURE__*/React.createElement(NumberField, {
608
+ label: "Height",
609
+ unit: "px",
610
+ value: size.height,
611
+ min: 1,
612
+ onCommit: next => setSizes(current => {
613
+ var _current$def$id$width, _current$def$id2;
614
+ return _extends({}, current, {
615
+ [def.id]: {
616
+ width: (_current$def$id$width = (_current$def$id2 = current[def.id]) == null ? void 0 : _current$def$id2.width) != null ? _current$def$id$width : def.width,
617
+ height: next
618
+ }
619
+ });
620
+ })
621
+ })), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(NumberField, {
622
+ label: "Rotation",
623
+ unit: "\xB0",
624
+ value: rotation,
625
+ onCommit: next => setRotations(current => _extends({}, current, {
626
+ [def.id]: next
627
+ }))
628
+ }))) : null, traits.fill ? /*#__PURE__*/React.createElement(StyledPropGroup, null, /*#__PURE__*/React.createElement(Body2, {
629
+ weight: "bold",
630
+ className: "group-title"
631
+ }, "Fill"), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
632
+ size: "small",
633
+ weight: "regular"
634
+ }, "Color"), /*#__PURE__*/React.createElement(ColorPickerField, {
635
+ includeTransparent: traits.fillTransparent,
636
+ value: currentFill,
637
+ onSelect: setFill
638
+ })), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
639
+ size: "small",
640
+ weight: "regular"
641
+ }, "Opacity"), /*#__PURE__*/React.createElement(OptionSelect, {
642
+ options: OPACITY_OPTIONS.map(opacity => ({
643
+ id: opacity,
644
+ name: opacity + "%"
645
+ })),
646
+ value: currentFillOpacity,
647
+ triggerLabel: currentFillOpacity + "%",
648
+ onChange: next => setFillOpacity(Number(next))
649
+ })))) : null, traits.stroke && shape ? /*#__PURE__*/React.createElement(StyledPropGroup, null, /*#__PURE__*/React.createElement(Body2, {
650
+ weight: "bold",
651
+ className: "group-title"
652
+ }, "Stroke"), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
653
+ size: "small",
654
+ weight: "regular"
655
+ }, "Pattern"), /*#__PURE__*/React.createElement(StyledSegmented, null, /*#__PURE__*/React.createElement(ChooseV2, {
656
+ required: true,
657
+ options: BORDER_STYLE_OPTIONS.map(option => ({
658
+ id: option.label,
659
+ name: option.label
660
+ })),
661
+ selected: [(_BORDER_STYLE_OPTIONS = (_BORDER_STYLE_OPTIONS2 = BORDER_STYLE_OPTIONS.find(option => option.value === shape.strokeDash)) == null ? void 0 : _BORDER_STYLE_OPTIONS2.label) != null ? _BORDER_STYLE_OPTIONS : "Solid"],
662
+ onChange: selected => {
663
+ var _BORDER_STYLE_OPTIONS3;
664
+ return updateShape({
665
+ strokeDash: (_BORDER_STYLE_OPTIONS3 = BORDER_STYLE_OPTIONS.find(option => option.label === selected[0])) == null ? void 0 : _BORDER_STYLE_OPTIONS3.value
666
+ });
667
+ }
668
+ }))), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
669
+ size: "small",
670
+ weight: "regular"
671
+ }, "Thickness"), /*#__PURE__*/React.createElement(OptionSelect, {
672
+ options: THICKNESS_OPTIONS.map(thickness => ({
673
+ id: thickness,
674
+ name: thickness + " pt"
675
+ })),
676
+ value: shape.strokeWidth,
677
+ triggerLabel: shape.strokeWidth + " pt",
678
+ onChange: next => updateShape({
679
+ strokeWidth: Number(next)
680
+ })
681
+ })), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
682
+ size: "small",
683
+ weight: "regular"
684
+ }, "Color"), /*#__PURE__*/React.createElement(ColorPickerField, {
685
+ value: shape.stroke,
686
+ onSelect: color => updateShape({
687
+ stroke: color
688
+ })
689
+ }))), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
690
+ size: "small",
691
+ weight: "regular"
692
+ }, "Radius"), /*#__PURE__*/React.createElement(OptionSelect, {
693
+ options: RADIUS_OPTIONS.map(radius => ({
694
+ id: radius,
695
+ name: radius + " px"
696
+ })),
697
+ value: shape.borderRadius,
698
+ triggerLabel: shape.borderRadius + " px",
699
+ onChange: next => updateShape({
700
+ borderRadius: Number(next)
701
+ })
702
+ })), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
703
+ size: "small",
704
+ weight: "regular"
705
+ }, "Opacity"), /*#__PURE__*/React.createElement(OptionSelect, {
706
+ options: OPACITY_OPTIONS.map(opacity => ({
707
+ id: opacity,
708
+ name: opacity + "%"
709
+ })),
710
+ value: shape.strokeOpacity,
711
+ triggerLabel: shape.strokeOpacity + "%",
712
+ onChange: next => updateShape({
713
+ strokeOpacity: Number(next)
714
+ })
715
+ })))) : null, traits.text ? /*#__PURE__*/React.createElement(StyledPropGroup, null, /*#__PURE__*/React.createElement(Body2, {
716
+ weight: "bold",
717
+ className: "group-title"
718
+ }, "Text"), /*#__PURE__*/React.createElement(StyledFieldGrid, null, /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
719
+ size: "small",
720
+ weight: "regular"
721
+ }, "Style"), /*#__PURE__*/React.createElement(StyledSegmented, null, STYLE_OPTIONS.map(option => /*#__PURE__*/React.createElement(Tooltip, {
722
+ key: option.key,
723
+ title: option.label
724
+ }, /*#__PURE__*/React.createElement(IconButton, {
725
+ variant: "text gray",
726
+ active: styleFlags[option.key],
727
+ "aria-label": option.label,
728
+ onClick: () => toggleStyle(option.key)
729
+ }, option.icon))))), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
730
+ size: "small",
731
+ weight: "regular"
732
+ }, "Size"), /*#__PURE__*/React.createElement(OptionSelect, {
733
+ options: FONT_SIZE_OPTIONS.map(fontSize => ({
734
+ id: fontSize,
735
+ name: fontSize + " pt"
736
+ })),
737
+ value: currentFontSize,
738
+ triggerLabel: currentFontSize + " pt",
739
+ onChange: next => setFontSize(Number(next))
740
+ }))), /*#__PURE__*/React.createElement(StyledPropRow, null, /*#__PURE__*/React.createElement(Label, {
741
+ size: "small",
742
+ weight: "regular"
743
+ }, "Alignment"), /*#__PURE__*/React.createElement(StyledSegmented, null, ALIGN_OPTIONS.map(option => /*#__PURE__*/React.createElement(Tooltip, {
744
+ key: option.value,
745
+ title: option.label
746
+ }, /*#__PURE__*/React.createElement(IconButton, {
747
+ variant: "text gray",
748
+ active: currentAlign === option.value,
749
+ "aria-label": option.label,
750
+ onClick: () => setAlign(option.value)
751
+ }, option.icon)))))) : null)));
752
+ };
753
+ //# sourceMappingURL=inspector.js.map