@activecollab/components 2.0.407 → 2.0.409
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.
- package/dist/cjs/components/Icons/collection/Project.js +98 -0
- package/dist/cjs/components/Icons/collection/Project.js.map +1 -0
- package/dist/cjs/components/Icons/collection/Task.js +61 -0
- package/dist/cjs/components/Icons/collection/Task.js.map +1 -0
- package/dist/cjs/components/Icons/collection/index.js +14 -0
- package/dist/cjs/components/Icons/collection/index.js.map +1 -1
- package/dist/cjs/presentation/shared/WebhookLogsDialog.js +170 -46
- package/dist/cjs/presentation/shared/WebhookLogsDialog.js.map +1 -1
- package/dist/cjs/presentation/stackedCard/content/NoteCard.js +7 -3
- package/dist/cjs/presentation/stackedCard/content/NoteCard.js.map +1 -1
- package/dist/cjs/presentation/stackedCard/content/ProjectCard.js +136 -0
- package/dist/cjs/presentation/stackedCard/content/ProjectCard.js.map +1 -0
- package/dist/cjs/presentation/stackedCard/content/TaskCard.js +183 -56
- package/dist/cjs/presentation/stackedCard/content/TaskCard.js.map +1 -1
- package/dist/cjs/presentation/stackedCard/content/index.js +11 -0
- package/dist/cjs/presentation/stackedCard/content/index.js.map +1 -1
- package/dist/cjs/presentation/stackedCard/content/shared.js +66 -19
- package/dist/cjs/presentation/stackedCard/content/shared.js.map +1 -1
- package/dist/cjs/presentation/whiteboard/bottomDock.js +95 -32
- package/dist/cjs/presentation/whiteboard/bottomDock.js.map +1 -1
- package/dist/cjs/presentation/whiteboard/canvasElements.js +196 -8
- package/dist/cjs/presentation/whiteboard/canvasElements.js.map +1 -1
- package/dist/cjs/presentation/whiteboard/inspector.js +149 -47
- package/dist/cjs/presentation/whiteboard/inspector.js.map +1 -1
- package/dist/cjs/presentation/whiteboard/outline.js +91 -18
- package/dist/cjs/presentation/whiteboard/outline.js.map +1 -1
- package/dist/cjs/presentation/whiteboard/projectElements.js +883 -0
- package/dist/cjs/presentation/whiteboard/projectElements.js.map +1 -0
- package/dist/esm/components/Icons/collection/Project.d.ts +36 -0
- package/dist/esm/components/Icons/collection/Project.d.ts.map +1 -0
- package/dist/esm/components/Icons/collection/Project.js +91 -0
- package/dist/esm/components/Icons/collection/Project.js.map +1 -0
- package/dist/esm/components/Icons/collection/Task.d.ts +32 -0
- package/dist/esm/components/Icons/collection/Task.d.ts.map +1 -0
- package/dist/esm/components/Icons/collection/Task.js +54 -0
- package/dist/esm/components/Icons/collection/Task.js.map +1 -0
- package/dist/esm/components/Icons/collection/index.d.ts +2 -0
- package/dist/esm/components/Icons/collection/index.d.ts.map +1 -1
- package/dist/esm/components/Icons/collection/index.js +2 -0
- package/dist/esm/components/Icons/collection/index.js.map +1 -1
- package/dist/esm/presentation/shared/WebhookLogsDialog.d.ts +1 -3
- package/dist/esm/presentation/shared/WebhookLogsDialog.d.ts.map +1 -1
- package/dist/esm/presentation/shared/WebhookLogsDialog.js +164 -46
- package/dist/esm/presentation/shared/WebhookLogsDialog.js.map +1 -1
- package/dist/esm/presentation/stackedCard/content/NoteCard.d.ts +8 -1
- package/dist/esm/presentation/stackedCard/content/NoteCard.d.ts.map +1 -1
- package/dist/esm/presentation/stackedCard/content/NoteCard.js +8 -4
- package/dist/esm/presentation/stackedCard/content/NoteCard.js.map +1 -1
- package/dist/esm/presentation/stackedCard/content/ProjectCard.d.ts +64 -0
- package/dist/esm/presentation/stackedCard/content/ProjectCard.d.ts.map +1 -0
- package/dist/esm/presentation/stackedCard/content/ProjectCard.js +128 -0
- package/dist/esm/presentation/stackedCard/content/ProjectCard.js.map +1 -0
- package/dist/esm/presentation/stackedCard/content/TaskCard.d.ts +64 -2
- package/dist/esm/presentation/stackedCard/content/TaskCard.d.ts.map +1 -1
- package/dist/esm/presentation/stackedCard/content/TaskCard.js +181 -58
- package/dist/esm/presentation/stackedCard/content/TaskCard.js.map +1 -1
- package/dist/esm/presentation/stackedCard/content/index.d.ts +1 -0
- package/dist/esm/presentation/stackedCard/content/index.d.ts.map +1 -1
- package/dist/esm/presentation/stackedCard/content/index.js +1 -0
- package/dist/esm/presentation/stackedCard/content/index.js.map +1 -1
- package/dist/esm/presentation/stackedCard/content/shared.d.ts +30 -0
- package/dist/esm/presentation/stackedCard/content/shared.d.ts.map +1 -1
- package/dist/esm/presentation/stackedCard/content/shared.js +66 -19
- package/dist/esm/presentation/stackedCard/content/shared.js.map +1 -1
- package/dist/esm/presentation/whiteboard/bottomDock.d.ts +30 -7
- package/dist/esm/presentation/whiteboard/bottomDock.d.ts.map +1 -1
- package/dist/esm/presentation/whiteboard/bottomDock.js +79 -32
- package/dist/esm/presentation/whiteboard/bottomDock.js.map +1 -1
- package/dist/esm/presentation/whiteboard/canvasElements.d.ts +4 -1
- package/dist/esm/presentation/whiteboard/canvasElements.d.ts.map +1 -1
- package/dist/esm/presentation/whiteboard/canvasElements.js +158 -7
- package/dist/esm/presentation/whiteboard/canvasElements.js.map +1 -1
- package/dist/esm/presentation/whiteboard/inspector.d.ts +35 -1
- package/dist/esm/presentation/whiteboard/inspector.d.ts.map +1 -1
- package/dist/esm/presentation/whiteboard/inspector.js +170 -68
- package/dist/esm/presentation/whiteboard/inspector.js.map +1 -1
- package/dist/esm/presentation/whiteboard/outline.d.ts +23 -3
- package/dist/esm/presentation/whiteboard/outline.d.ts.map +1 -1
- package/dist/esm/presentation/whiteboard/outline.js +89 -18
- package/dist/esm/presentation/whiteboard/outline.js.map +1 -1
- package/dist/esm/presentation/whiteboard/projectElements.d.ts +267 -0
- package/dist/esm/presentation/whiteboard/projectElements.d.ts.map +1 -0
- package/dist/esm/presentation/whiteboard/projectElements.js +824 -0
- package/dist/esm/presentation/whiteboard/projectElements.js.map +1 -0
- package/dist/index.js +629 -490
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
|
|
|
2
2
|
import React, { useState } from "react";
|
|
3
3
|
import styled from "styled-components";
|
|
4
4
|
import { IconButton } from "../../components/IconButton";
|
|
5
|
-
import { AttachmentIcon, CircleIcon, ConnectionIcon, DrawIcon, InsertLinkIcon, RoundedRectangleIcon, ShapesIcon, NoteIcon, TextIcon, TriangleIcon } from "../../components/Icons";
|
|
5
|
+
import { AttachmentIcon, CircleIcon, ConnectionIcon, DrawIcon, InsertLinkIcon, ProjectIcon, ProjectStarIcon, RoundedRectangleIcon, ShapesIcon, NoteIcon, TaskIcon, TextIcon, TriangleIcon } from "../../components/Icons";
|
|
6
6
|
import { Tooltip } from "../../components/Tooltip";
|
|
7
7
|
import { Caption1 } from "../../components/Typography";
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ import { Caption1 } from "../../components/Typography";
|
|
|
14
14
|
export const ToolSurface = styled.div.withConfig({
|
|
15
15
|
displayName: "bottomDock__ToolSurface",
|
|
16
16
|
componentId: "sc-1xsxwg0-0"
|
|
17
|
-
})(["display:flex;align-items:center;gap:2px;padding:6px;border-radius:14px;background-color:var(--page-paper-main);box-shadow:0 6px 24px rgba(0,0,0,0.16);border:1px solid var(--border-primary);"]);
|
|
17
|
+
})(["display:flex;align-items:center;gap:2px;padding:6px;border-radius:14px;background-color:var(--page-paper-main);box-shadow:0 6px 24px rgba(0,0,0,0.16);border:1px solid var(--border-primary);svg > [stroke],svg > g [stroke]{stroke:currentColor;}"]);
|
|
18
18
|
|
|
19
19
|
/* vertical separator used inside the toolbars */
|
|
20
20
|
export const verticalDivider = "\n .divider {\n width: 1px;\n height: 24px;\n background-color: var(--color-theme-400);\n margin: 0 6px;\n flex-shrink: 0;\n }\n";
|
|
@@ -23,9 +23,16 @@ export const StyledBottomDock = styled.div.withConfig({
|
|
|
23
23
|
componentId: "sc-1xsxwg0-1"
|
|
24
24
|
})(["position:absolute;left:50%;bottom:18px;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:10px;max-width:calc(100% - 32px);"]);
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The floating second toolbar row — a small pill above the dock that holds the
|
|
28
|
+
* members of one tool family (Circle / Rectangle / Triangle, or Task / Project).
|
|
29
|
+
*
|
|
30
|
+
* It is the same surface as the dock itself, so a family reads as an extension
|
|
31
|
+
* of the button that opened it rather than as a different kind of UI. Only one
|
|
32
|
+
* pill is ever up, because only one tool is ever armed.
|
|
33
|
+
*/
|
|
34
|
+
export const StyledToolPill = styled(ToolSurface).withConfig({
|
|
35
|
+
displayName: "bottomDock__StyledToolPill",
|
|
29
36
|
componentId: "sc-1xsxwg0-2"
|
|
30
37
|
})([""]);
|
|
31
38
|
|
|
@@ -42,24 +49,40 @@ export const TOOL_HINTS = {
|
|
|
42
49
|
roundedRectangle: "Click to drop a default rectangle, or draw one to size. Press R for a rectangle.",
|
|
43
50
|
triangle: "Click to drop a default triangle, or draw one to size. Press Y for a triangle.",
|
|
44
51
|
connector: "Drag from one element to another to connect them. Press C to connect.",
|
|
52
|
+
projectElements: "Put a task or a project on the board — pick which above, then draw its card.",
|
|
53
|
+
project: "Draw a box to place a project card, then pick the project it shows.",
|
|
54
|
+
task: "Draw a box to place a task card, then pick the task it shows.",
|
|
45
55
|
link: "Draw a box to place a link, or paste a URL anywhere. Press L for link.",
|
|
46
56
|
file: "Draw a box to place a file, or drop one on the board. Press F for file.",
|
|
47
57
|
sticky: "Click anywhere on the canvas to drop a sticky note. Press S for a sticky note.",
|
|
48
58
|
text: "Click anywhere on the canvas to start typing. Press T for text.",
|
|
49
59
|
draw: "Click and drag to draw freehand. Press D for the pen.",
|
|
50
|
-
shapes: "
|
|
60
|
+
shapes: "Pick a shape above, then draw it on the board. Press R for a rectangle."
|
|
51
61
|
};
|
|
52
62
|
|
|
53
63
|
/**
|
|
54
|
-
* The armed board tool. Link, File and the three shapes are
|
|
55
|
-
* turns the next canvas click into "drop the default-size
|
|
56
|
-
* drag into "draw one this big"; everything else stays a
|
|
57
|
-
* otherwise always in select mode.
|
|
64
|
+
* The armed board tool. Link, File, Project, Task and the three shapes are
|
|
65
|
+
* wired — arming one turns the next canvas click into "drop the default-size
|
|
66
|
+
* element here" and a drag into "draw one this big"; everything else stays a
|
|
67
|
+
* mock, so the board is otherwise always in select mode.
|
|
58
68
|
*/
|
|
59
69
|
|
|
60
70
|
export const SHAPE_TOOLS = ["circle", "roundedRectangle", "triangle"];
|
|
61
71
|
export const isShapeTool = tool => SHAPE_TOOLS.includes(tool);
|
|
62
72
|
|
|
73
|
+
/**
|
|
74
|
+
* What a dock button arms the instant its pill opens.
|
|
75
|
+
*
|
|
76
|
+
* Opening a family and choosing inside it are two separate decisions, and the
|
|
77
|
+
* second one has a right answer often enough that asking for it every time is
|
|
78
|
+
* just a toll: a shape is usually a rectangle, and the thing people put on a
|
|
79
|
+
* board is usually a task. So the pill opens with that member already armed and
|
|
80
|
+
* the gesture can start immediately — picking another member is a correction,
|
|
81
|
+
* not a required step.
|
|
82
|
+
*/
|
|
83
|
+
export const DEFAULT_SHAPE_TOOL = "roundedRectangle";
|
|
84
|
+
export const DEFAULT_PROJECT_ELEMENT_TOOL = "task";
|
|
85
|
+
|
|
63
86
|
/* What a plain click drops — mirrors DEFAULT_SHAPE_SIZES in
|
|
64
87
|
new-activecollab-front, and is exactly what the hover preview promises. */
|
|
65
88
|
export const SHAPE_DEFAULT_SIZES = {
|
|
@@ -100,7 +123,10 @@ export const ToolButton = _ref => {
|
|
|
100
123
|
onMouseLeave: onMouseLeave
|
|
101
124
|
}, children));
|
|
102
125
|
};
|
|
103
|
-
/* Bottom-centre tool dock
|
|
126
|
+
/* Bottom-centre tool dock: the family pill (when one is open), the main
|
|
127
|
+
toolbar, and the shared hint line. The dock is anchored by its BOTTOM edge,
|
|
128
|
+
so a pill appearing or disappearing grows the stack upwards and never moves
|
|
129
|
+
the toolbar itself. */
|
|
104
130
|
export const BottomDock = _ref2 => {
|
|
105
131
|
let activeTool = _ref2.activeTool,
|
|
106
132
|
onToolChange = _ref2.onToolChange;
|
|
@@ -116,37 +142,58 @@ export const BottomDock = _ref2 => {
|
|
|
116
142
|
gesture happens on the canvas, so that is exactly when the instruction is
|
|
117
143
|
needed. */
|
|
118
144
|
const hint = hoverHint != null ? hoverHint : activeTool === "select" ? null : TOOL_HINTS[activeTool];
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
145
|
+
|
|
146
|
+
/* Which pill is up. Both are DERIVED from the armed tool rather than kept as
|
|
147
|
+
their own open/closed state: opening a pill always arms one of its members,
|
|
148
|
+
so "a member of this family is armed" and "this pill is showing" are the
|
|
149
|
+
same fact. Deriving it is what makes every way of leaving the family close
|
|
150
|
+
the pill for free — another dock tool, Esc on the canvas, or the gesture
|
|
151
|
+
finishing and disarming the tool. Two states could disagree; one cannot. */
|
|
152
|
+
const shapesOpen = isShapeTool(activeTool);
|
|
153
|
+
const projectElementsOpen = activeTool === "task" || activeTool === "project";
|
|
154
|
+
return /*#__PURE__*/React.createElement(StyledBottomDock, null, shapesOpen ? /*#__PURE__*/React.createElement(StyledToolPill, null, /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
155
|
+
tip: "Circle",
|
|
123
156
|
active: activeTool === "circle",
|
|
124
|
-
onClick: () => onToolChange(
|
|
125
|
-
}, hoverProps("circle")), /*#__PURE__*/React.createElement(CircleIcon, null))
|
|
126
|
-
|
|
127
|
-
}, /*#__PURE__*/React.createElement(IconButton, _extends({
|
|
128
|
-
variant: "text gray",
|
|
157
|
+
onClick: () => onToolChange("circle")
|
|
158
|
+
}, hoverProps("circle")), /*#__PURE__*/React.createElement(CircleIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
159
|
+
tip: "Rectangle",
|
|
129
160
|
active: activeTool === "roundedRectangle",
|
|
130
|
-
onClick: () => onToolChange(
|
|
131
|
-
}, hoverProps("roundedRectangle")), /*#__PURE__*/React.createElement(RoundedRectangleIcon, null))
|
|
132
|
-
|
|
133
|
-
}, /*#__PURE__*/React.createElement(IconButton, _extends({
|
|
134
|
-
variant: "text gray",
|
|
161
|
+
onClick: () => onToolChange("roundedRectangle")
|
|
162
|
+
}, hoverProps("roundedRectangle")), /*#__PURE__*/React.createElement(RoundedRectangleIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
163
|
+
tip: "Triangle",
|
|
135
164
|
active: activeTool === "triangle",
|
|
136
|
-
onClick: () => onToolChange(
|
|
137
|
-
}, hoverProps("triangle")), /*#__PURE__*/React.createElement(TriangleIcon, null)))
|
|
165
|
+
onClick: () => onToolChange("triangle")
|
|
166
|
+
}, hoverProps("triangle")), /*#__PURE__*/React.createElement(TriangleIcon, null))) : null, projectElementsOpen ? /*#__PURE__*/React.createElement(StyledToolPill, null, /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
167
|
+
tip: "Task",
|
|
168
|
+
active: activeTool === "task",
|
|
169
|
+
onClick: () => onToolChange("task")
|
|
170
|
+
}, hoverProps("task")), /*#__PURE__*/React.createElement(TaskIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
171
|
+
tip: "Project",
|
|
172
|
+
active: activeTool === "project",
|
|
173
|
+
onClick: () => onToolChange("project")
|
|
174
|
+
}, hoverProps("project")), /*#__PURE__*/React.createElement(ProjectIcon, null))) : null, /*#__PURE__*/React.createElement(ToolSurface, null, /*#__PURE__*/React.createElement(Tooltip, {
|
|
138
175
|
title: "Sticky Note"
|
|
139
176
|
}, /*#__PURE__*/React.createElement(IconButton, _extends({
|
|
140
|
-
variant: "text gray"
|
|
177
|
+
variant: "text gray",
|
|
178
|
+
onClick: () => onToolChange("select")
|
|
141
179
|
}, hoverProps("sticky")), /*#__PURE__*/React.createElement(NoteIcon, null))), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
142
|
-
tip: "Text"
|
|
180
|
+
tip: "Text",
|
|
181
|
+
onClick: () => onToolChange("select")
|
|
143
182
|
}, hoverProps("text")), /*#__PURE__*/React.createElement(TextIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
144
|
-
tip: "Draw"
|
|
183
|
+
tip: "Draw",
|
|
184
|
+
onClick: () => onToolChange("select")
|
|
145
185
|
}, hoverProps("draw")), /*#__PURE__*/React.createElement(DrawIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
146
|
-
tip: "Shape"
|
|
186
|
+
tip: "Shape",
|
|
187
|
+
active: shapesOpen,
|
|
188
|
+
onClick: () => onToolChange(shapesOpen ? "select" : DEFAULT_SHAPE_TOOL)
|
|
147
189
|
}, hoverProps("shapes")), /*#__PURE__*/React.createElement(ShapesIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
148
|
-
tip: "Connect"
|
|
190
|
+
tip: "Connect",
|
|
191
|
+
onClick: () => onToolChange("select")
|
|
149
192
|
}, hoverProps("connector")), /*#__PURE__*/React.createElement(ConnectionIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
193
|
+
tip: "Project Elements",
|
|
194
|
+
active: projectElementsOpen,
|
|
195
|
+
onClick: () => onToolChange(projectElementsOpen ? "select" : DEFAULT_PROJECT_ELEMENT_TOOL)
|
|
196
|
+
}, hoverProps("projectElements")), /*#__PURE__*/React.createElement(ProjectStarIcon, null)), /*#__PURE__*/React.createElement(ToolButton, _extends({
|
|
150
197
|
tip: "Link",
|
|
151
198
|
active: activeTool === "link",
|
|
152
199
|
onClick: () => onToolChange(activeTool === "link" ? "select" : "link")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottomDock.js","names":["React","useState","styled","IconButton","AttachmentIcon","CircleIcon","ConnectionIcon","DrawIcon","InsertLinkIcon","RoundedRectangleIcon","ShapesIcon","NoteIcon","TextIcon","TriangleIcon","Tooltip","Caption1","ToolSurface","div","withConfig","displayName","componentId","verticalDivider","StyledBottomDock","StyledShapePicker","StyledHint","TOOL_HINTS","circle","roundedRectangle","triangle","connector","link","file","sticky","text","draw","shapes","SHAPE_TOOLS","isShapeTool","tool","includes","SHAPE_DEFAULT_SIZES","width","height","CARD_PREVIEW_HEIGHT","ToolButton","_ref","tip","active","children","onClick","onMouseEnter","onMouseLeave","createElement","title","variant","BottomDock","_ref2","activeTool","onToolChange","_useState","hoverHint","setHoverHint","hoverProps","hint","_extends","color","className","undefined"],"sources":["../../../../src/presentation/whiteboard/bottomDock.tsx"],"sourcesContent":["import React, { ReactElement, ReactNode, useState } from \"react\";\n\nimport styled from \"styled-components\";\n\nimport { IconButton } from \"../../components/IconButton\";\nimport {\n AttachmentIcon,\n CircleIcon,\n ConnectionIcon,\n DrawIcon,\n InsertLinkIcon,\n RoundedRectangleIcon,\n ShapesIcon,\n NoteIcon,\n TextIcon,\n TriangleIcon,\n} from \"../../components/Icons\";\nimport { Tooltip } from \"../../components/Tooltip\";\nimport { Caption1 } from \"../../components/Typography\";\n\n/* ------------------------------------------------------------------ */\n/* Floating toolbar surfaces */\n/* ------------------------------------------------------------------ */\n\n/* A floating, rounded toolbar surface (paper + shadow). */\nexport const ToolSurface = styled.div`\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 6px;\n border-radius: 14px;\n background-color: var(--page-paper-main);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.16);\n border: 1px solid var(--border-primary);\n`;\n\n/* vertical separator used inside the toolbars */\nexport const verticalDivider = `\n .divider {\n width: 1px;\n height: 24px;\n background-color: var(--color-theme-400);\n margin: 0 6px;\n flex-shrink: 0;\n }\n`;\n\nexport const StyledBottomDock = styled.div`\n position: absolute;\n left: 50%;\n bottom: 18px;\n transform: translateX(-50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n max-width: calc(100% - 32px);\n`;\n\n/* shape sub-toolbar: Circle / Rectangle / Triangle */\nexport const StyledShapePicker = styled(ToolSurface)``;\n\n/* Reserve two Caption1 lines (2 × 16px) so showing/hiding the hint never\n shifts the dock; visibility keeps the reserved space when hidden. */\nexport const StyledHint = styled(Caption1)`\n text-align: center;\n padding: 0 16px;\n min-height: 32px;\n\n &.hint-hidden {\n visibility: hidden;\n }\n`;\n\n/* Per-tool hint shown under the dock only while that tool is hovered. */\nexport const TOOL_HINTS = {\n circle:\n \"Click to drop a default circle, or draw one to size. Press O for a circle.\",\n roundedRectangle:\n \"Click to drop a default rectangle, or draw one to size. Press R for a rectangle.\",\n triangle:\n \"Click to drop a default triangle, or draw one to size. Press Y for a triangle.\",\n connector:\n \"Drag from one element to another to connect them. Press C to connect.\",\n link: \"Draw a box to place a link, or paste a URL anywhere. Press L for link.\",\n file: \"Draw a box to place a file, or drop one on the board. Press F for file.\",\n sticky:\n \"Click anywhere on the canvas to drop a sticky note. Press S for a sticky note.\",\n text: \"Click anywhere on the canvas to start typing. Press T for text.\",\n draw: \"Click and drag to draw freehand. Press D for the pen.\",\n shapes:\n \"Start drawing the shape on canvas to see the result. Press R to draw a rectangle.\",\n} as const;\n\nexport type ToolHintKey = keyof typeof TOOL_HINTS;\n\nexport type ShapeTool = \"circle\" | \"roundedRectangle\" | \"triangle\";\n\n/**\n * The armed board tool. Link, File and the three shapes are wired — arming one\n * turns the next canvas click into \"drop the default-size element here\" and a\n * drag into \"draw one this big\"; everything else stays a mock, so the board is\n * otherwise always in select mode.\n */\nexport type BoardTool = \"select\" | \"link\" | \"file\" | ShapeTool;\n\nexport const SHAPE_TOOLS: readonly ShapeTool[] = [\n \"circle\",\n \"roundedRectangle\",\n \"triangle\",\n];\n\nexport const isShapeTool = (tool: BoardTool): tool is ShapeTool =>\n (SHAPE_TOOLS as readonly string[]).includes(tool);\n\n/* What a plain click drops — mirrors DEFAULT_SHAPE_SIZES in\n new-activecollab-front, and is exactly what the hover preview promises. */\nexport const SHAPE_DEFAULT_SIZES: Record<\n ShapeTool,\n { width: number; height: number }\n> = {\n roundedRectangle: { width: 100, height: 60 },\n circle: { width: 80, height: 80 },\n triangle: { width: 80, height: 80 },\n};\n\n/* Cards are auto-height, so their hover preview uses the app's nominal wire\n height (DEFAULT_LINK_CARD_SIZE / DEFAULT_FILE_CARD_SIZE). */\nexport const CARD_PREVIEW_HEIGHT = 240;\n\n/** The kinds of card the draw gesture can place. */\nexport type DrawableTool = Exclude<BoardTool, \"select\">;\n\nexport interface ToolButtonProps {\n tip: string;\n active?: boolean;\n children: ReactNode;\n onClick?: () => void;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}\n\nexport const ToolButton = ({\n tip,\n active,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n}: ToolButtonProps): ReactElement => (\n <Tooltip title={tip}>\n <IconButton\n variant=\"text gray\"\n active={active}\n onClick={onClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {children}\n </IconButton>\n </Tooltip>\n);\n\nexport interface BottomDockProps {\n activeTool: BoardTool;\n onToolChange: (tool: BoardTool) => void;\n}\n\n/* Bottom-centre tool dock (shape picker + main toolbar + hint). */\nexport const BottomDock = ({\n activeTool,\n onToolChange,\n}: BottomDockProps): ReactElement => {\n const [hoverHint, setHoverHint] = useState<string | null>(null);\n\n const hoverProps = (tool: ToolHintKey): Record<string, () => void> => ({\n onMouseEnter: () => setHoverHint(TOOL_HINTS[tool]),\n onMouseLeave: () => setHoverHint(null),\n });\n\n /* An armed tool keeps its hint up after the pointer leaves the dock — the\n gesture happens on the canvas, so that is exactly when the instruction is\n needed. */\n const hint =\n hoverHint ?? (activeTool === \"select\" ? null : TOOL_HINTS[activeTool]);\n\n return (\n <StyledBottomDock>\n <StyledShapePicker>\n <Tooltip title=\"Circle\">\n <IconButton\n variant=\"text gray\"\n active={activeTool === \"circle\"}\n onClick={() =>\n onToolChange(activeTool === \"circle\" ? \"select\" : \"circle\")\n }\n {...hoverProps(\"circle\")}\n >\n <CircleIcon />\n </IconButton>\n </Tooltip>\n <Tooltip title=\"Rectangle\">\n <IconButton\n variant=\"text gray\"\n active={activeTool === \"roundedRectangle\"}\n onClick={() =>\n onToolChange(\n activeTool === \"roundedRectangle\"\n ? \"select\"\n : \"roundedRectangle\"\n )\n }\n {...hoverProps(\"roundedRectangle\")}\n >\n <RoundedRectangleIcon />\n </IconButton>\n </Tooltip>\n <Tooltip title=\"Triangle\">\n <IconButton\n variant=\"text gray\"\n active={activeTool === \"triangle\"}\n onClick={() =>\n onToolChange(activeTool === \"triangle\" ? \"select\" : \"triangle\")\n }\n {...hoverProps(\"triangle\")}\n >\n <TriangleIcon />\n </IconButton>\n </Tooltip>\n </StyledShapePicker>\n\n <ToolSurface>\n <Tooltip title=\"Sticky Note\">\n <IconButton variant=\"text gray\" {...hoverProps(\"sticky\")}>\n <NoteIcon />\n </IconButton>\n </Tooltip>\n <ToolButton tip=\"Text\" {...hoverProps(\"text\")}>\n <TextIcon />\n </ToolButton>\n <ToolButton tip=\"Draw\" {...hoverProps(\"draw\")}>\n <DrawIcon />\n </ToolButton>\n <ToolButton tip=\"Shape\" {...hoverProps(\"shapes\")}>\n <ShapesIcon />\n </ToolButton>\n <ToolButton tip=\"Connect\" {...hoverProps(\"connector\")}>\n <ConnectionIcon />\n </ToolButton>\n <ToolButton\n tip=\"Link\"\n active={activeTool === \"link\"}\n onClick={() =>\n onToolChange(activeTool === \"link\" ? \"select\" : \"link\")\n }\n {...hoverProps(\"link\")}\n >\n <InsertLinkIcon />\n </ToolButton>\n <ToolButton\n tip=\"File\"\n active={activeTool === \"file\"}\n onClick={() =>\n onToolChange(activeTool === \"file\" ? \"select\" : \"file\")\n }\n {...hoverProps(\"file\")}\n >\n <AttachmentIcon />\n </ToolButton>\n </ToolSurface>\n\n <StyledHint color=\"tertiary\" className={hint ? undefined : \"hint-hidden\"}>\n {hint ?? \" \"}\n </StyledHint>\n </StyledBottomDock>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"bottomDock.js","names":["React","useState","styled","IconButton","AttachmentIcon","CircleIcon","ConnectionIcon","DrawIcon","InsertLinkIcon","ProjectIcon","ProjectStarIcon","RoundedRectangleIcon","ShapesIcon","NoteIcon","TaskIcon","TextIcon","TriangleIcon","Tooltip","Caption1","ToolSurface","div","withConfig","displayName","componentId","verticalDivider","StyledBottomDock","StyledToolPill","StyledHint","TOOL_HINTS","circle","roundedRectangle","triangle","connector","projectElements","project","task","link","file","sticky","text","draw","shapes","SHAPE_TOOLS","isShapeTool","tool","includes","DEFAULT_SHAPE_TOOL","DEFAULT_PROJECT_ELEMENT_TOOL","SHAPE_DEFAULT_SIZES","width","height","CARD_PREVIEW_HEIGHT","ToolButton","_ref","tip","active","children","onClick","onMouseEnter","onMouseLeave","createElement","title","variant","BottomDock","_ref2","activeTool","onToolChange","_useState","hoverHint","setHoverHint","hoverProps","hint","shapesOpen","projectElementsOpen","_extends","color","className","undefined"],"sources":["../../../../src/presentation/whiteboard/bottomDock.tsx"],"sourcesContent":["import React, { ReactElement, ReactNode, useState } from \"react\";\n\nimport styled from \"styled-components\";\n\nimport { IconButton } from \"../../components/IconButton\";\nimport {\n AttachmentIcon,\n CircleIcon,\n ConnectionIcon,\n DrawIcon,\n InsertLinkIcon,\n ProjectIcon,\n ProjectStarIcon,\n RoundedRectangleIcon,\n ShapesIcon,\n NoteIcon,\n TaskIcon,\n TextIcon,\n TriangleIcon,\n} from \"../../components/Icons\";\nimport { Tooltip } from \"../../components/Tooltip\";\nimport { Caption1 } from \"../../components/Typography\";\n\n/* ------------------------------------------------------------------ */\n/* Floating toolbar surfaces */\n/* ------------------------------------------------------------------ */\n\n/* A floating, rounded toolbar surface (paper + shadow). */\nexport const ToolSurface = styled.div`\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 6px;\n border-radius: 14px;\n background-color: var(--page-paper-main);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.16);\n border: 1px solid var(--border-primary);\n\n /* A DS button colours its icon by declaring svg { fill: currentColor }, so\n every FILLED glyph follows the button's active and hover states for free.\n Three glyphs here are not filled: RoundedRectangleIcon and TriangleIcon are\n outlines with a stroke of var(--color-theme-600) baked into the path, and\n ProjectIcon draws its ruler as a stroked rect. Nothing about the button\n could ever reach them — an armed rectangle stayed exactly as grey as an\n unarmed one. This extends the same currentColor contract to a declared\n stroke.\n\n Scoped to direct children and to shapes inside a <g>, NOT to every\n descendant: ProjectIcon cuts its ruler with a <mask> whose <line> is\n stroked BLACK on purpose. In a mask only luminance matters, so recolouring\n that line would turn a clean diagonal cut into a half-transparent smear. */\n svg > [stroke],\n svg > g [stroke] {\n stroke: currentColor;\n }\n`;\n\n/* vertical separator used inside the toolbars */\nexport const verticalDivider = `\n .divider {\n width: 1px;\n height: 24px;\n background-color: var(--color-theme-400);\n margin: 0 6px;\n flex-shrink: 0;\n }\n`;\n\nexport const StyledBottomDock = styled.div`\n position: absolute;\n left: 50%;\n bottom: 18px;\n transform: translateX(-50%);\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n max-width: calc(100% - 32px);\n`;\n\n/**\n * The floating second toolbar row — a small pill above the dock that holds the\n * members of one tool family (Circle / Rectangle / Triangle, or Task / Project).\n *\n * It is the same surface as the dock itself, so a family reads as an extension\n * of the button that opened it rather than as a different kind of UI. Only one\n * pill is ever up, because only one tool is ever armed.\n */\nexport const StyledToolPill = styled(ToolSurface)``;\n\n/* Reserve two Caption1 lines (2 × 16px) so showing/hiding the hint never\n shifts the dock; visibility keeps the reserved space when hidden. */\nexport const StyledHint = styled(Caption1)`\n text-align: center;\n padding: 0 16px;\n min-height: 32px;\n\n &.hint-hidden {\n visibility: hidden;\n }\n`;\n\n/* Per-tool hint shown under the dock only while that tool is hovered. */\nexport const TOOL_HINTS = {\n circle:\n \"Click to drop a default circle, or draw one to size. Press O for a circle.\",\n roundedRectangle:\n \"Click to drop a default rectangle, or draw one to size. Press R for a rectangle.\",\n triangle:\n \"Click to drop a default triangle, or draw one to size. Press Y for a triangle.\",\n connector:\n \"Drag from one element to another to connect them. Press C to connect.\",\n projectElements:\n \"Put a task or a project on the board — pick which above, then draw its card.\",\n project:\n \"Draw a box to place a project card, then pick the project it shows.\",\n task: \"Draw a box to place a task card, then pick the task it shows.\",\n link: \"Draw a box to place a link, or paste a URL anywhere. Press L for link.\",\n file: \"Draw a box to place a file, or drop one on the board. Press F for file.\",\n sticky:\n \"Click anywhere on the canvas to drop a sticky note. Press S for a sticky note.\",\n text: \"Click anywhere on the canvas to start typing. Press T for text.\",\n draw: \"Click and drag to draw freehand. Press D for the pen.\",\n shapes:\n \"Pick a shape above, then draw it on the board. Press R for a rectangle.\",\n} as const;\n\nexport type ToolHintKey = keyof typeof TOOL_HINTS;\n\nexport type ShapeTool = \"circle\" | \"roundedRectangle\" | \"triangle\";\n\n/**\n * The armed board tool. Link, File, Project, Task and the three shapes are\n * wired — arming one turns the next canvas click into \"drop the default-size\n * element here\" and a drag into \"draw one this big\"; everything else stays a\n * mock, so the board is otherwise always in select mode.\n */\nexport type BoardTool =\n | \"select\"\n | \"link\"\n | \"file\"\n | \"project\"\n | \"task\"\n | ShapeTool;\n\nexport const SHAPE_TOOLS: readonly ShapeTool[] = [\n \"circle\",\n \"roundedRectangle\",\n \"triangle\",\n];\n\nexport const isShapeTool = (tool: BoardTool): tool is ShapeTool =>\n (SHAPE_TOOLS as readonly string[]).includes(tool);\n\n/**\n * What a dock button arms the instant its pill opens.\n *\n * Opening a family and choosing inside it are two separate decisions, and the\n * second one has a right answer often enough that asking for it every time is\n * just a toll: a shape is usually a rectangle, and the thing people put on a\n * board is usually a task. So the pill opens with that member already armed and\n * the gesture can start immediately — picking another member is a correction,\n * not a required step.\n */\nexport const DEFAULT_SHAPE_TOOL: ShapeTool = \"roundedRectangle\";\nexport const DEFAULT_PROJECT_ELEMENT_TOOL: BoardTool = \"task\";\n\n/* What a plain click drops — mirrors DEFAULT_SHAPE_SIZES in\n new-activecollab-front, and is exactly what the hover preview promises. */\nexport const SHAPE_DEFAULT_SIZES: Record<\n ShapeTool,\n { width: number; height: number }\n> = {\n roundedRectangle: { width: 100, height: 60 },\n circle: { width: 80, height: 80 },\n triangle: { width: 80, height: 80 },\n};\n\n/* Cards are auto-height, so their hover preview uses the app's nominal wire\n height (DEFAULT_LINK_CARD_SIZE / DEFAULT_FILE_CARD_SIZE). */\nexport const CARD_PREVIEW_HEIGHT = 240;\n\n/** The kinds of card the draw gesture can place. */\nexport type DrawableTool = Exclude<BoardTool, \"select\">;\n\nexport interface ToolButtonProps {\n tip: string;\n active?: boolean;\n children: ReactNode;\n onClick?: () => void;\n onMouseEnter?: () => void;\n onMouseLeave?: () => void;\n}\n\nexport const ToolButton = ({\n tip,\n active,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n}: ToolButtonProps): ReactElement => (\n <Tooltip title={tip}>\n <IconButton\n variant=\"text gray\"\n active={active}\n onClick={onClick}\n onMouseEnter={onMouseEnter}\n onMouseLeave={onMouseLeave}\n >\n {children}\n </IconButton>\n </Tooltip>\n);\n\nexport interface BottomDockProps {\n activeTool: BoardTool;\n onToolChange: (tool: BoardTool) => void;\n}\n\n/* Bottom-centre tool dock: the family pill (when one is open), the main\n toolbar, and the shared hint line. The dock is anchored by its BOTTOM edge,\n so a pill appearing or disappearing grows the stack upwards and never moves\n the toolbar itself. */\nexport const BottomDock = ({\n activeTool,\n onToolChange,\n}: BottomDockProps): ReactElement => {\n const [hoverHint, setHoverHint] = useState<string | null>(null);\n\n const hoverProps = (tool: ToolHintKey): Record<string, () => void> => ({\n onMouseEnter: () => setHoverHint(TOOL_HINTS[tool]),\n onMouseLeave: () => setHoverHint(null),\n });\n\n /* An armed tool keeps its hint up after the pointer leaves the dock — the\n gesture happens on the canvas, so that is exactly when the instruction is\n needed. */\n const hint =\n hoverHint ?? (activeTool === \"select\" ? null : TOOL_HINTS[activeTool]);\n\n /* Which pill is up. Both are DERIVED from the armed tool rather than kept as\n their own open/closed state: opening a pill always arms one of its members,\n so \"a member of this family is armed\" and \"this pill is showing\" are the\n same fact. Deriving it is what makes every way of leaving the family close\n the pill for free — another dock tool, Esc on the canvas, or the gesture\n finishing and disarming the tool. Two states could disagree; one cannot. */\n const shapesOpen = isShapeTool(activeTool);\n const projectElementsOpen = activeTool === \"task\" || activeTool === \"project\";\n\n return (\n <StyledBottomDock>\n {/* Shapes pill — hidden until the Shape dock button opens it. Inside a\n pill a click is a plain choice, never a toggle off: the dock button is\n what closes the family, so clicking the armed shape again must not\n make the row that is being used disappear. */}\n {shapesOpen ? (\n <StyledToolPill>\n <ToolButton\n tip=\"Circle\"\n active={activeTool === \"circle\"}\n onClick={() => onToolChange(\"circle\")}\n {...hoverProps(\"circle\")}\n >\n <CircleIcon />\n </ToolButton>\n <ToolButton\n tip=\"Rectangle\"\n active={activeTool === \"roundedRectangle\"}\n onClick={() => onToolChange(\"roundedRectangle\")}\n {...hoverProps(\"roundedRectangle\")}\n >\n <RoundedRectangleIcon />\n </ToolButton>\n <ToolButton\n tip=\"Triangle\"\n active={activeTool === \"triangle\"}\n onClick={() => onToolChange(\"triangle\")}\n {...hoverProps(\"triangle\")}\n >\n <TriangleIcon />\n </ToolButton>\n </StyledToolPill>\n ) : null}\n\n {/* Project Elements pill — the same surface, the same button size and\n spacing, in the same slot above the dock. Task comes first because it\n is what the button arms by default. */}\n {projectElementsOpen ? (\n <StyledToolPill>\n <ToolButton\n tip=\"Task\"\n active={activeTool === \"task\"}\n onClick={() => onToolChange(\"task\")}\n {...hoverProps(\"task\")}\n >\n <TaskIcon />\n </ToolButton>\n <ToolButton\n tip=\"Project\"\n active={activeTool === \"project\"}\n onClick={() => onToolChange(\"project\")}\n {...hoverProps(\"project\")}\n >\n <ProjectIcon />\n </ToolButton>\n </StyledToolPill>\n ) : null}\n\n <ToolSurface>\n {/* The tools that are still mocks arm nothing — but they do DISARM, so\n reaching for one closes whatever pill is up and ends the gesture the\n board was waiting for. A dead button that also leaves the previous\n tool armed would be the confusing half of \"not implemented yet\". */}\n <Tooltip title=\"Sticky Note\">\n <IconButton\n variant=\"text gray\"\n onClick={() => onToolChange(\"select\")}\n {...hoverProps(\"sticky\")}\n >\n <NoteIcon />\n </IconButton>\n </Tooltip>\n <ToolButton\n tip=\"Text\"\n onClick={() => onToolChange(\"select\")}\n {...hoverProps(\"text\")}\n >\n <TextIcon />\n </ToolButton>\n <ToolButton\n tip=\"Draw\"\n onClick={() => onToolChange(\"select\")}\n {...hoverProps(\"draw\")}\n >\n <DrawIcon />\n </ToolButton>\n {/* Opening the family and arming its default member are one click: the\n pill appears with Rectangle already armed, so the next thing the\n pointer does can be the drawing itself. */}\n <ToolButton\n tip=\"Shape\"\n active={shapesOpen}\n onClick={() =>\n onToolChange(shapesOpen ? \"select\" : DEFAULT_SHAPE_TOOL)\n }\n {...hoverProps(\"shapes\")}\n >\n <ShapesIcon />\n </ToolButton>\n <ToolButton\n tip=\"Connect\"\n onClick={() => onToolChange(\"select\")}\n {...hoverProps(\"connector\")}\n >\n <ConnectionIcon />\n </ToolButton>\n <ToolButton\n tip=\"Project Elements\"\n active={projectElementsOpen}\n onClick={() =>\n onToolChange(\n projectElementsOpen ? \"select\" : DEFAULT_PROJECT_ELEMENT_TOOL\n )\n }\n {...hoverProps(\"projectElements\")}\n >\n <ProjectStarIcon />\n </ToolButton>\n <ToolButton\n tip=\"Link\"\n active={activeTool === \"link\"}\n onClick={() =>\n onToolChange(activeTool === \"link\" ? \"select\" : \"link\")\n }\n {...hoverProps(\"link\")}\n >\n <InsertLinkIcon />\n </ToolButton>\n <ToolButton\n tip=\"File\"\n active={activeTool === \"file\"}\n onClick={() =>\n onToolChange(activeTool === \"file\" ? \"select\" : \"file\")\n }\n {...hoverProps(\"file\")}\n >\n <AttachmentIcon />\n </ToolButton>\n </ToolSurface>\n\n <StyledHint color=\"tertiary\" className={hint ? undefined : \"hint-hidden\"}>\n {hint ?? \" \"}\n </StyledHint>\n </StyledBottomDock>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -3,6 +3,7 @@ import { BoardTool } from "./bottomDock";
|
|
|
3
3
|
import { CanvasElementDef, GridSize, ShapeElementState, StickyTextStyle } from "./elements";
|
|
4
4
|
import { FileElementDef } from "./fileElement";
|
|
5
5
|
import { LinkElementDef } from "./linkElement";
|
|
6
|
+
import { ProjectElementDef } from "./projectElements";
|
|
6
7
|
/**
|
|
7
8
|
* A card is full of its own controls — the URL field, the display toggle, the
|
|
8
9
|
* retry button, the toolbar. A mousedown on any of them selects the element but
|
|
@@ -47,6 +48,8 @@ export interface CanvasElementsProps {
|
|
|
47
48
|
setLinks: React.Dispatch<React.SetStateAction<LinkElementDef[]>>;
|
|
48
49
|
files: FileElementDef[];
|
|
49
50
|
setFiles: React.Dispatch<React.SetStateAction<FileElementDef[]>>;
|
|
51
|
+
projectElements: ProjectElementDef[];
|
|
52
|
+
setProjectElements: React.Dispatch<React.SetStateAction<ProjectElementDef[]>>;
|
|
50
53
|
createdShapes: CanvasElementDef[];
|
|
51
54
|
setCreatedShapes: React.Dispatch<React.SetStateAction<CanvasElementDef[]>>;
|
|
52
55
|
sizes: Record<string, {
|
|
@@ -57,5 +60,5 @@ export interface CanvasElementsProps {
|
|
|
57
60
|
locks: Record<string, boolean>;
|
|
58
61
|
setLocks: React.Dispatch<React.SetStateAction<Record<string, boolean>>>;
|
|
59
62
|
}
|
|
60
|
-
export declare const CanvasElements: ({ gridSize, snapToGrid, activeTool, onToolUsed, selectedId, onSelectedIdChange: setSelectedId, positions, setPositions, shapes, setShapes, stickyStyle, setStickyStyle, links, setLinks, files, setFiles, createdShapes, setCreatedShapes, sizes, rotations, locks, setLocks, }: CanvasElementsProps) => ReactElement;
|
|
63
|
+
export declare const CanvasElements: ({ gridSize, snapToGrid, activeTool, onToolUsed, selectedId, onSelectedIdChange: setSelectedId, positions, setPositions, shapes, setShapes, stickyStyle, setStickyStyle, links, setLinks, files, setFiles, projectElements, setProjectElements, createdShapes, setCreatedShapes, sizes, rotations, locks, setLocks, }: CanvasElementsProps) => ReactElement;
|
|
61
64
|
//# sourceMappingURL=canvasElements.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvasElements.d.ts","sourceRoot":"","sources":["../../../../src/presentation/whiteboard/canvasElements.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,YAAY,EAKb,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,SAAS,EAMV,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,gBAAgB,EAChB,QAAQ,EAGR,iBAAiB,EAEjB,eAAe,EAShB,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,cAAc,EAIf,MAAM,eAAe,CAAC;AACvB,OAAO,EAIL,cAAc,EAMf,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"canvasElements.d.ts","sourceRoot":"","sources":["../../../../src/presentation/whiteboard/canvasElements.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,YAAY,EAKb,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,SAAS,EAMV,MAAM,cAAc,CAAC;AACtB,OAAO,EAEL,gBAAgB,EAChB,QAAQ,EAGR,iBAAiB,EAEjB,eAAe,EAShB,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,cAAc,EAIf,MAAM,eAAe,CAAC;AACvB,OAAO,EAIL,cAAc,EAMf,MAAM,eAAe,CAAC;AACvB,OAAO,EAKL,iBAAiB,EASlB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,+CAA+C,CAAC;AAE7E;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,UAAU,EAAE,MAAM,IAAI,CAAC;IAKvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;IAKxE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,YAAY,EAAE,KAAK,CAAC,QAAQ,CAC1B,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAC/D,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC1C,SAAS,EAAE,KAAK,CAAC,QAAQ,CACvB,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CACxD,CAAC;IACF,WAAW,EAAE,eAAe,CAAC;IAC7B,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC,CAAC;IAGtE,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IACjE,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;IAGjE,eAAe,EAAE,iBAAiB,EAAE,CAAC;IACrC,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC;IAG9E,aAAa,EAAE,gBAAgB,EAAE,CAAC;IAClC,gBAAgB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC;IAE3E,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGlC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;CACzE;AAUD,eAAO,MAAM,cAAc,GAAI,sTAyB5B,mBAAmB,KAAG,YA6lCxB,CAAC"}
|
|
@@ -4,6 +4,7 @@ import { CARD_PREVIEW_HEIGHT, SHAPE_DEFAULT_SIZES, isShapeTool } from "./bottomD
|
|
|
4
4
|
import { CANVAS_ELEMENTS, PostitFormattingToolbar, ResizeHandles, ShapeFormattingToolbar, StyledCanvasElement, StyledCanvasLayer, StyledSticky, defaultShapeState, gridLevel, renderShapeBody, snapValue, stickyGradient } from "./elements";
|
|
5
5
|
import { FILE_DEFAULT_WIDTH, FileElement, binaryFrom, describeFile, revokeThumbnail } from "./fileElement";
|
|
6
6
|
import { CARD_DRAW_THRESHOLD, LINK_DEFAULT_WIDTH, LinkElement, StyledLinkDraft, StyledShapePreview, clampCardWidth, isValidLinkUrl, shapePreviewGlyph } from "./linkElement";
|
|
7
|
+
import { PLACEHOLDER_MIN_HEIGHT, PLACEHOLDER_MIN_WIDTH, ProjectElementNode, ProjectElementPicker, RESOLVE_MS, StyledProjectDraft, StyledProjectElementBox, isProjectTool, projectCardWidth } from "./projectElements";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* A card is full of its own controls — the URL field, the display toggle, the
|
|
@@ -36,6 +37,7 @@ export const DRAG_THROUGH_SELECTOR = "[data-drag-through]";
|
|
|
36
37
|
Link elements are created here too — by drawing a box with the Link tool
|
|
37
38
|
armed, or by pasting a URL onto the canvas. */
|
|
38
39
|
export const CanvasElements = _ref => {
|
|
40
|
+
var _projectElements$find;
|
|
39
41
|
let gridSize = _ref.gridSize,
|
|
40
42
|
snapToGrid = _ref.snapToGrid,
|
|
41
43
|
activeTool = _ref.activeTool,
|
|
@@ -52,6 +54,8 @@ export const CanvasElements = _ref => {
|
|
|
52
54
|
setLinks = _ref.setLinks,
|
|
53
55
|
files = _ref.files,
|
|
54
56
|
setFiles = _ref.setFiles,
|
|
57
|
+
projectElements = _ref.projectElements,
|
|
58
|
+
setProjectElements = _ref.setProjectElements,
|
|
55
59
|
createdShapes = _ref.createdShapes,
|
|
56
60
|
setCreatedShapes = _ref.setCreatedShapes,
|
|
57
61
|
sizes = _ref.sizes,
|
|
@@ -172,6 +176,23 @@ export const CanvasElements = _ref => {
|
|
|
172
176
|
window.addEventListener("mousemove", handleDragMove);
|
|
173
177
|
window.addEventListener("mouseup", handleDragEnd);
|
|
174
178
|
}, [positions, handleDragMove, handleDragEnd, setSelectedId]);
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The press a limited card gets instead of the one every other element gets.
|
|
182
|
+
*
|
|
183
|
+
* It selects — the Inspector still has something to say about it — and stops
|
|
184
|
+
* there: no drag is armed, because a viewer who cannot see what a card points
|
|
185
|
+
* at has no business moving it for the people who can. Gating at the call
|
|
186
|
+
* site rather than inside `handleElementMouseDown` leaves the normal drag
|
|
187
|
+
* path exactly as it was.
|
|
188
|
+
*/
|
|
189
|
+
const handleLimitedMouseDown = useCallback((event, id) => {
|
|
190
|
+
if (event.button !== 0 || activeToolRef.current !== "select") {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
event.stopPropagation();
|
|
194
|
+
setSelectedId(id);
|
|
195
|
+
}, [setSelectedId]);
|
|
175
196
|
const updateShape = useCallback((id, partial) => setShapes(current => _extends({}, current, {
|
|
176
197
|
[id]: _extends({}, current[id], partial)
|
|
177
198
|
})), [setShapes]);
|
|
@@ -379,6 +400,80 @@ export const CanvasElements = _ref => {
|
|
|
379
400
|
}) : link));
|
|
380
401
|
}, [setLinks]);
|
|
381
402
|
|
|
403
|
+
/* ---- project / task elements --------------------------------------- */
|
|
404
|
+
|
|
405
|
+
const projectCounterRef = useRef(0);
|
|
406
|
+
/* The placeholder the centred picker is currently answering for. This one
|
|
407
|
+
stays local: it is a half-finished GESTURE, not board content, so nothing
|
|
408
|
+
outside the canvas has any use for it. The elements themselves are lifted
|
|
409
|
+
to the scene (see props) exactly like links and files, because the
|
|
410
|
+
Inspector reads and edits them. */
|
|
411
|
+
const _useState5 = useState(null),
|
|
412
|
+
pickerFor = _useState5[0],
|
|
413
|
+
setPickerFor = _useState5[1];
|
|
414
|
+
/* Pending "resolving" flashes, so unmounting never leaves a timer behind
|
|
415
|
+
that sets state on a component that is gone. */
|
|
416
|
+
const resolveTimersRef = useRef([]);
|
|
417
|
+
useEffect(() => () => resolveTimersRef.current.forEach(timer => window.clearTimeout(timer)), []);
|
|
418
|
+
|
|
419
|
+
/* Create a placeholder and open its picker. The drawn box is what the card
|
|
420
|
+
lands in; the picker supplies what the card actually says. */
|
|
421
|
+
const addProjectElement = useCallback((point, size, kind) => {
|
|
422
|
+
projectCounterRef.current += 1;
|
|
423
|
+
const id = kind + "-element-" + projectCounterRef.current;
|
|
424
|
+
const step = snapStepRef.current;
|
|
425
|
+
setPositions(current => _extends({}, current, {
|
|
426
|
+
[id]: {
|
|
427
|
+
x: snapValue(point.x, step),
|
|
428
|
+
y: snapValue(point.y, step)
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
setProjectElements(current => [...current, {
|
|
432
|
+
id,
|
|
433
|
+
kind,
|
|
434
|
+
width: size.width,
|
|
435
|
+
height: size.height,
|
|
436
|
+
status: "placeholder",
|
|
437
|
+
project: null,
|
|
438
|
+
task: null
|
|
439
|
+
}]);
|
|
440
|
+
setSelectedId(id);
|
|
441
|
+
setPickerFor(id);
|
|
442
|
+
}, [setPositions, setProjectElements, setSelectedId]);
|
|
443
|
+
|
|
444
|
+
/* The picker answered. The card keeps the placeholder's top-left and flashes
|
|
445
|
+
a skeleton while the "backend" fills the details in — the same two-phase
|
|
446
|
+
honesty the file card's upload / preparing pair has. */
|
|
447
|
+
const resolveProjectElement = useCallback((id, project, task) => {
|
|
448
|
+
setPickerFor(null);
|
|
449
|
+
setProjectElements(current => current.map(entry => entry.id === id ? _extends({}, entry, {
|
|
450
|
+
status: "resolving",
|
|
451
|
+
project,
|
|
452
|
+
task
|
|
453
|
+
}) : entry));
|
|
454
|
+
const timer = window.setTimeout(() => {
|
|
455
|
+
setProjectElements(current => current.map(entry => entry.id === id ? _extends({}, entry, {
|
|
456
|
+
status: "resolved",
|
|
457
|
+
/* The card takes its natural width HERE rather than the
|
|
458
|
+
renderer deriving it, so from this moment on there is a
|
|
459
|
+
single stored width for the canvas, the drag and the
|
|
460
|
+
Inspector's Width field to agree on. */
|
|
461
|
+
width: projectCardWidth(entry.kind)
|
|
462
|
+
}) : entry));
|
|
463
|
+
}, RESOLVE_MS);
|
|
464
|
+
resolveTimersRef.current.push(timer);
|
|
465
|
+
}, [setProjectElements]);
|
|
466
|
+
|
|
467
|
+
/* Esc, or a click on the overlay. The placeholder was only ever the first
|
|
468
|
+
half of the gesture, so cancelling throws it away rather than leaving a
|
|
469
|
+
blank card on the board. */
|
|
470
|
+
const cancelProjectElement = useCallback(id => {
|
|
471
|
+
setPickerFor(null);
|
|
472
|
+
setProjectElements(current => current.filter(entry => entry.id !== id));
|
|
473
|
+
setSelectedId(selected => selected === id ? null : selected);
|
|
474
|
+
}, [setProjectElements, setSelectedId]);
|
|
475
|
+
const pickerElement = (_projectElements$find = projectElements.find(entry => entry.id === pickerFor)) != null ? _projectElements$find : null;
|
|
476
|
+
|
|
382
477
|
/* ---- draw-to-place ------------------------------------------------ */
|
|
383
478
|
|
|
384
479
|
const drawRef = useRef(null);
|
|
@@ -433,13 +528,21 @@ export const CanvasElements = _ref => {
|
|
|
433
528
|
width,
|
|
434
529
|
height
|
|
435
530
|
} : SHAPE_DEFAULT_SIZES[origin.tool]);
|
|
531
|
+
} else if (isProjectTool(origin.tool)) {
|
|
532
|
+
/* Both dimensions matter here (a project card is not auto-height the
|
|
533
|
+
way a link is), and a card smaller than the minimum could not hold
|
|
534
|
+
its own contents — so the drawn box is clamped, not honoured. */
|
|
535
|
+
addProjectElement(at, {
|
|
536
|
+
width: Math.max(PLACEHOLDER_MIN_WIDTH, Math.round(width)),
|
|
537
|
+
height: Math.max(PLACEHOLDER_MIN_HEIGHT, Math.round(height))
|
|
538
|
+
}, origin.tool);
|
|
436
539
|
} else if (origin.tool === "file") {
|
|
437
540
|
addFile(at, drawn ? width : FILE_DEFAULT_WIDTH, null);
|
|
438
541
|
} else {
|
|
439
542
|
addLink(at, drawn ? width : LINK_DEFAULT_WIDTH, null);
|
|
440
543
|
}
|
|
441
544
|
onToolUsed();
|
|
442
|
-
}, [addFile, addLink, addShape, canvasPoint, handleDrawMove, onToolUsed]);
|
|
545
|
+
}, [addFile, addLink, addProjectElement, addShape, canvasPoint, handleDrawMove, onToolUsed]);
|
|
443
546
|
const handleLayerMouseDown = useCallback(event => {
|
|
444
547
|
const tool = activeToolRef.current;
|
|
445
548
|
if (event.button !== 0 || tool === "select") {
|
|
@@ -467,9 +570,9 @@ export const CanvasElements = _ref => {
|
|
|
467
570
|
|
|
468
571
|
/* Where the armed tool's hover preview sits — cleared while a draw gesture
|
|
469
572
|
runs (the rubber band takes over) and whenever the tool disarms. */
|
|
470
|
-
const
|
|
471
|
-
hoverPoint =
|
|
472
|
-
setHoverPoint =
|
|
573
|
+
const _useState6 = useState(null),
|
|
574
|
+
hoverPoint = _useState6[0],
|
|
575
|
+
setHoverPoint = _useState6[1];
|
|
473
576
|
useEffect(() => {
|
|
474
577
|
if (activeTool === "select") {
|
|
475
578
|
setHoverPoint(null);
|
|
@@ -691,7 +794,33 @@ export const CanvasElements = _ref => {
|
|
|
691
794
|
onCancel: () => detachFile(entry.id),
|
|
692
795
|
onDelete: () => deleteFile(entry.id)
|
|
693
796
|
}));
|
|
694
|
-
}),
|
|
797
|
+
}), projectElements.map(entry => {
|
|
798
|
+
var _positions$entry$id$x2, _positions$entry$id3, _positions$entry$id$y2, _positions$entry$id4;
|
|
799
|
+
const selected = entry.id === selectedId;
|
|
800
|
+
return /*#__PURE__*/React.createElement(StyledProjectElementBox, {
|
|
801
|
+
key: entry.id,
|
|
802
|
+
className: [selected ? "selected" : "", entry.limited ? "limited" : "",
|
|
803
|
+
/* A resolved card is a StackedCard, and the shell draws its own
|
|
804
|
+
ring — so the box stands down, exactly as it does for the link
|
|
805
|
+
and file cards. The placeholder, the resolving flash and the
|
|
806
|
+
limited card are not StackedCards and keep the box's ring. */
|
|
807
|
+
entry.status === "resolved" && !entry.limited ? "is-card" : ""].filter(Boolean).join(" "),
|
|
808
|
+
style: {
|
|
809
|
+
left: (_positions$entry$id$x2 = (_positions$entry$id3 = positions[entry.id]) == null ? void 0 : _positions$entry$id3.x) != null ? _positions$entry$id$x2 : 0,
|
|
810
|
+
top: (_positions$entry$id$y2 = (_positions$entry$id4 = positions[entry.id]) == null ? void 0 : _positions$entry$id4.y) != null ? _positions$entry$id$y2 : 0,
|
|
811
|
+
width: entry.width,
|
|
812
|
+
/* Only the placeholder box is measured in both directions; the
|
|
813
|
+
resolved card is auto-height like every other card here. */
|
|
814
|
+
height: entry.status === "resolved" ? undefined : entry.height
|
|
815
|
+
},
|
|
816
|
+
onMouseDown: event => entry.limited ? handleLimitedMouseDown(event, entry.id) : handleElementMouseDown(event, entry.id),
|
|
817
|
+
onClickCapture: handleElementClickCapture,
|
|
818
|
+
onClick: event => event.stopPropagation()
|
|
819
|
+
}, /*#__PURE__*/React.createElement(ProjectElementNode, {
|
|
820
|
+
element: entry,
|
|
821
|
+
selected: selected
|
|
822
|
+
}));
|
|
823
|
+
}), draft && !isShapeTool(draft.tool) && !isProjectTool(draft.tool) ? /*#__PURE__*/React.createElement(StyledLinkDraft, {
|
|
695
824
|
style: {
|
|
696
825
|
left: draft.x,
|
|
697
826
|
top: draft.y,
|
|
@@ -700,7 +829,14 @@ export const CanvasElements = _ref => {
|
|
|
700
829
|
}
|
|
701
830
|
}, /*#__PURE__*/React.createElement("span", {
|
|
702
831
|
className: "draft-width"
|
|
703
|
-
}, "W", " ", draft.width >= CARD_DRAW_THRESHOLD ? clampCardWidth(draft.width) : draft.tool === "file" ? FILE_DEFAULT_WIDTH : LINK_DEFAULT_WIDTH, " ", "\xB7 height follows content")) : null, draft &&
|
|
832
|
+
}, "W", " ", draft.width >= CARD_DRAW_THRESHOLD ? clampCardWidth(draft.width) : draft.tool === "file" ? FILE_DEFAULT_WIDTH : LINK_DEFAULT_WIDTH, " ", "\xB7 height follows content")) : null, draft && isProjectTool(draft.tool) ? /*#__PURE__*/React.createElement(StyledProjectDraft, {
|
|
833
|
+
style: {
|
|
834
|
+
left: draft.x,
|
|
835
|
+
top: draft.y,
|
|
836
|
+
width: Math.max(draft.width, PLACEHOLDER_MIN_WIDTH),
|
|
837
|
+
height: Math.max(draft.height, PLACEHOLDER_MIN_HEIGHT)
|
|
838
|
+
}
|
|
839
|
+
}) : null, draft && isShapeTool(draft.tool) ? /*#__PURE__*/React.createElement(StyledShapePreview, {
|
|
704
840
|
style: {
|
|
705
841
|
left: draft.x,
|
|
706
842
|
top: draft.y,
|
|
@@ -722,6 +858,16 @@ export const CanvasElements = _ref => {
|
|
|
722
858
|
}, size)
|
|
723
859
|
}, shapePreviewGlyph(activeTool, size.width, size.height));
|
|
724
860
|
}
|
|
861
|
+
if (isProjectTool(activeTool)) {
|
|
862
|
+
return /*#__PURE__*/React.createElement(StyledProjectDraft, {
|
|
863
|
+
style: {
|
|
864
|
+
left: at.x,
|
|
865
|
+
top: at.y,
|
|
866
|
+
width: PLACEHOLDER_MIN_WIDTH,
|
|
867
|
+
height: PLACEHOLDER_MIN_HEIGHT
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
725
871
|
const width = activeTool === "file" ? FILE_DEFAULT_WIDTH : LINK_DEFAULT_WIDTH;
|
|
726
872
|
return /*#__PURE__*/React.createElement(StyledShapePreview, {
|
|
727
873
|
style: {
|
|
@@ -741,6 +887,11 @@ export const CanvasElements = _ref => {
|
|
|
741
887
|
rx: 8,
|
|
742
888
|
ry: 8
|
|
743
889
|
})));
|
|
744
|
-
})() : null
|
|
890
|
+
})() : null, pickerElement ? /*#__PURE__*/React.createElement(ProjectElementPicker, {
|
|
891
|
+
kind: pickerElement.kind,
|
|
892
|
+
onSelectProject: project => resolveProjectElement(pickerElement.id, project, null),
|
|
893
|
+
onSelectTask: (project, task) => resolveProjectElement(pickerElement.id, project, task),
|
|
894
|
+
onCancel: () => cancelProjectElement(pickerElement.id)
|
|
895
|
+
}) : null);
|
|
745
896
|
};
|
|
746
897
|
//# sourceMappingURL=canvasElements.js.map
|