@activecollab/components 2.0.408 → 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.
Files changed (83) hide show
  1. package/dist/cjs/components/Icons/collection/Project.js +98 -0
  2. package/dist/cjs/components/Icons/collection/Project.js.map +1 -0
  3. package/dist/cjs/components/Icons/collection/Task.js +61 -0
  4. package/dist/cjs/components/Icons/collection/Task.js.map +1 -0
  5. package/dist/cjs/components/Icons/collection/index.js +14 -0
  6. package/dist/cjs/components/Icons/collection/index.js.map +1 -1
  7. package/dist/cjs/presentation/stackedCard/content/NoteCard.js +7 -3
  8. package/dist/cjs/presentation/stackedCard/content/NoteCard.js.map +1 -1
  9. package/dist/cjs/presentation/stackedCard/content/ProjectCard.js +136 -0
  10. package/dist/cjs/presentation/stackedCard/content/ProjectCard.js.map +1 -0
  11. package/dist/cjs/presentation/stackedCard/content/TaskCard.js +183 -56
  12. package/dist/cjs/presentation/stackedCard/content/TaskCard.js.map +1 -1
  13. package/dist/cjs/presentation/stackedCard/content/index.js +11 -0
  14. package/dist/cjs/presentation/stackedCard/content/index.js.map +1 -1
  15. package/dist/cjs/presentation/stackedCard/content/shared.js +66 -19
  16. package/dist/cjs/presentation/stackedCard/content/shared.js.map +1 -1
  17. package/dist/cjs/presentation/whiteboard/bottomDock.js +95 -32
  18. package/dist/cjs/presentation/whiteboard/bottomDock.js.map +1 -1
  19. package/dist/cjs/presentation/whiteboard/canvasElements.js +196 -8
  20. package/dist/cjs/presentation/whiteboard/canvasElements.js.map +1 -1
  21. package/dist/cjs/presentation/whiteboard/inspector.js +149 -47
  22. package/dist/cjs/presentation/whiteboard/inspector.js.map +1 -1
  23. package/dist/cjs/presentation/whiteboard/outline.js +91 -18
  24. package/dist/cjs/presentation/whiteboard/outline.js.map +1 -1
  25. package/dist/cjs/presentation/whiteboard/projectElements.js +883 -0
  26. package/dist/cjs/presentation/whiteboard/projectElements.js.map +1 -0
  27. package/dist/esm/components/Icons/collection/Project.d.ts +36 -0
  28. package/dist/esm/components/Icons/collection/Project.d.ts.map +1 -0
  29. package/dist/esm/components/Icons/collection/Project.js +91 -0
  30. package/dist/esm/components/Icons/collection/Project.js.map +1 -0
  31. package/dist/esm/components/Icons/collection/Task.d.ts +32 -0
  32. package/dist/esm/components/Icons/collection/Task.d.ts.map +1 -0
  33. package/dist/esm/components/Icons/collection/Task.js +54 -0
  34. package/dist/esm/components/Icons/collection/Task.js.map +1 -0
  35. package/dist/esm/components/Icons/collection/index.d.ts +2 -0
  36. package/dist/esm/components/Icons/collection/index.d.ts.map +1 -1
  37. package/dist/esm/components/Icons/collection/index.js +2 -0
  38. package/dist/esm/components/Icons/collection/index.js.map +1 -1
  39. package/dist/esm/presentation/stackedCard/content/NoteCard.d.ts +8 -1
  40. package/dist/esm/presentation/stackedCard/content/NoteCard.d.ts.map +1 -1
  41. package/dist/esm/presentation/stackedCard/content/NoteCard.js +8 -4
  42. package/dist/esm/presentation/stackedCard/content/NoteCard.js.map +1 -1
  43. package/dist/esm/presentation/stackedCard/content/ProjectCard.d.ts +64 -0
  44. package/dist/esm/presentation/stackedCard/content/ProjectCard.d.ts.map +1 -0
  45. package/dist/esm/presentation/stackedCard/content/ProjectCard.js +128 -0
  46. package/dist/esm/presentation/stackedCard/content/ProjectCard.js.map +1 -0
  47. package/dist/esm/presentation/stackedCard/content/TaskCard.d.ts +64 -2
  48. package/dist/esm/presentation/stackedCard/content/TaskCard.d.ts.map +1 -1
  49. package/dist/esm/presentation/stackedCard/content/TaskCard.js +181 -58
  50. package/dist/esm/presentation/stackedCard/content/TaskCard.js.map +1 -1
  51. package/dist/esm/presentation/stackedCard/content/index.d.ts +1 -0
  52. package/dist/esm/presentation/stackedCard/content/index.d.ts.map +1 -1
  53. package/dist/esm/presentation/stackedCard/content/index.js +1 -0
  54. package/dist/esm/presentation/stackedCard/content/index.js.map +1 -1
  55. package/dist/esm/presentation/stackedCard/content/shared.d.ts +30 -0
  56. package/dist/esm/presentation/stackedCard/content/shared.d.ts.map +1 -1
  57. package/dist/esm/presentation/stackedCard/content/shared.js +66 -19
  58. package/dist/esm/presentation/stackedCard/content/shared.js.map +1 -1
  59. package/dist/esm/presentation/whiteboard/bottomDock.d.ts +30 -7
  60. package/dist/esm/presentation/whiteboard/bottomDock.d.ts.map +1 -1
  61. package/dist/esm/presentation/whiteboard/bottomDock.js +79 -32
  62. package/dist/esm/presentation/whiteboard/bottomDock.js.map +1 -1
  63. package/dist/esm/presentation/whiteboard/canvasElements.d.ts +4 -1
  64. package/dist/esm/presentation/whiteboard/canvasElements.d.ts.map +1 -1
  65. package/dist/esm/presentation/whiteboard/canvasElements.js +158 -7
  66. package/dist/esm/presentation/whiteboard/canvasElements.js.map +1 -1
  67. package/dist/esm/presentation/whiteboard/inspector.d.ts +35 -1
  68. package/dist/esm/presentation/whiteboard/inspector.d.ts.map +1 -1
  69. package/dist/esm/presentation/whiteboard/inspector.js +170 -68
  70. package/dist/esm/presentation/whiteboard/inspector.js.map +1 -1
  71. package/dist/esm/presentation/whiteboard/outline.d.ts +23 -3
  72. package/dist/esm/presentation/whiteboard/outline.d.ts.map +1 -1
  73. package/dist/esm/presentation/whiteboard/outline.js +89 -18
  74. package/dist/esm/presentation/whiteboard/outline.js.map +1 -1
  75. package/dist/esm/presentation/whiteboard/projectElements.d.ts +267 -0
  76. package/dist/esm/presentation/whiteboard/projectElements.d.ts.map +1 -0
  77. package/dist/esm/presentation/whiteboard/projectElements.js +824 -0
  78. package/dist/esm/presentation/whiteboard/projectElements.js.map +1 -0
  79. package/dist/index.js +629 -490
  80. package/dist/index.js.map +1 -1
  81. package/dist/index.min.js +1 -1
  82. package/dist/index.min.js.map +1 -1
  83. package/package.json +1 -1
@@ -0,0 +1,883 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.taskDisplayName = exports.projectElementTitle = exports.projectClientLabel = exports.projectCardWidth = exports.isProjectTool = exports.groupTasks = exports.groupProjects = exports.TaskElementCard = exports.TASK_CARD_WIDTH = exports.StyledResolving = exports.StyledProjectElementBox = exports.StyledProjectDraft = exports.StyledPlaceholder = exports.StyledLimitedCard = exports.SEEDED_PROJECT_ELEMENT_POSITIONS = exports.SEEDED_PROJECT_ELEMENTS = exports.RESOLVE_MS = exports.ProjectElementPicker = exports.ProjectElementNode = exports.ProjectElementCard = exports.PROJECT_CARD_WIDTH = exports.PLACEHOLDER_MIN_WIDTH = exports.PLACEHOLDER_MIN_HEIGHT = exports.PICKER_PROJECT_LABELS = exports.PICKER_PROJECTS = exports.PICKER_PEOPLE = exports.PICKER_LABELS = exports.LimitedElementCard = exports.LIMITED_ACCESS_MESSAGE = exports.LIMITED_ACCESS_LABEL = exports.INTERNAL_CLIENT_LABEL = void 0;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _styledComponents = _interopRequireDefault(require("styled-components"));
9
+ var _elements = require("./elements");
10
+ var _IconButton = require("../../components/IconButton");
11
+ var _Icons = require("../../components/Icons");
12
+ var _Input = require("../../components/Input");
13
+ var _Loaders = require("../../components/Loaders");
14
+ var _Modal = require("../../components/Modal");
15
+ var _Paper = require("../../components/Paper");
16
+ var _RadioButton = require("../../components/RadioButton");
17
+ var _StackedCard = require("../../components/StackedCard");
18
+ var _Tooltip = require("../../components/Tooltip");
19
+ var _Typography = require("../../components/Typography");
20
+ var _shared = require("../shared");
21
+ var _content = require("../stackedCard/content");
22
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
23
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
24
+ function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
25
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
26
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
27
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
28
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
29
+ function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
30
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
31
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
32
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
33
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
34
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
35
+ /* ------------------------------------------------------------------ */
36
+ /* Project elements — placeholder → picker → resolved card */
37
+ /* ------------------------------------------------------------------ */
38
+
39
+ /**
40
+ * The board's third "draw a box, then say what goes in it" element, after the
41
+ * link and the file card. The gesture ladder is deliberately the same one those
42
+ * two already established, because it is the only one the dock teaches:
43
+ *
44
+ * armed the Project Elements dock button opens a submenu (Project /
45
+ * Task); picking one arms the tool and puts the canvas in
46
+ * crosshair mode.
47
+ * drawn mouse down → drag → up leaves a dashed PLACEHOLDER at the drawn
48
+ * box (clamped to a card-sized minimum), exactly where the real
49
+ * card will sit.
50
+ * picking the release opens the picker — a centred popover replicating the
51
+ * app's `MenuSelector/ProjectList` (and, for tasks, the two-step
52
+ * `ProjectTaskMenuSelector`: projects, then that project's tasks).
53
+ * resolving a short skeleton flash in the placeholder's own box, standing in
54
+ * for "the board is fetching this record's details".
55
+ * resolved the placeholder becomes the real card, anchored at the
56
+ * placeholder's top-left and grown to the card's natural size.
57
+ *
58
+ * Escape or a click on the overlay cancels: the picker closes and the
59
+ * placeholder is thrown away, so a cancelled gesture leaves no debris.
60
+ */
61
+
62
+ /* A drawn box smaller than this reads as "I just want a card here", so the
63
+ placeholder takes the minimum rather than the (unusable) drawn size. */
64
+ var PLACEHOLDER_MIN_WIDTH = exports.PLACEHOLDER_MIN_WIDTH = 260;
65
+ var PLACEHOLDER_MIN_HEIGHT = exports.PLACEHOLDER_MIN_HEIGHT = 120;
66
+
67
+ /* The natural width each resolved card grows to. Heights follow the content,
68
+ like every other card on this board. */
69
+ var PROJECT_CARD_WIDTH = exports.PROJECT_CARD_WIDTH = 300;
70
+ var TASK_CARD_WIDTH = exports.TASK_CARD_WIDTH = 260;
71
+
72
+ /* How long the "details are being resolved from the backend" flash lasts. */
73
+ var RESOLVE_MS = exports.RESOLVE_MS = 480;
74
+ /** True for the two board tools this module owns. */
75
+ var isProjectTool = exports.isProjectTool = function isProjectTool(tool) {
76
+ return tool === "project" || tool === "task";
77
+ };
78
+
79
+ /* ------------------------------------------------------------------ */
80
+ /* Mock data */
81
+ /* ------------------------------------------------------------------ */
82
+
83
+ /**
84
+ * The people and labels the board carries are exactly what the shared content
85
+ * cards read, so the picker's types ARE the cards' types — one shape, so a card
86
+ * cannot be fed something it does not understand.
87
+ */
88
+
89
+ /**
90
+ * A project label. The app renders it as a FILLED pill whose background is the
91
+ * label's own colour and whose text is a darker shade of it
92
+ * (`ProjectLabel.jsx`: `EntityLabel` with `backgroundColor: label.color` and
93
+ * `color: label.darker_text_color`), which is the design system's `Chip` — not
94
+ * the colour-text `Tag` the column cards use. Both colours are data. The shape
95
+ * is the shared `ProjectCard`'s own.
96
+ */
97
+
98
+ /**
99
+ * What an internal project's client line says. The word belongs to the card (it
100
+ * is the card that has to decide what to print for a project with no client), so
101
+ * the board re-exports the card's own constant rather than keeping a second copy
102
+ * that could drift.
103
+ */
104
+ var INTERNAL_CLIENT_LABEL = exports.INTERNAL_CLIENT_LABEL = _content.INTERNAL_CLIENT_LABEL;
105
+
106
+ /* One rule for both the card's client line and the picker's group header, so a
107
+ project can never be filed under one name and labelled with another. */
108
+ var projectClientLabel = exports.projectClientLabel = function projectClientLabel(project) {
109
+ var _project$client;
110
+ return (_project$client = project.client) !== null && _project$client !== void 0 ? _project$client : INTERNAL_CLIENT_LABEL;
111
+ };
112
+ /* Avatar colours are DATA, not theme — they stand in for real user avatars. */
113
+ var PICKER_PEOPLE = exports.PICKER_PEOPLE = {
114
+ jasmina: {
115
+ name: "Jasmina Sekulić",
116
+ initials: "JS",
117
+ color: _shared.AVATAR_COLORS.purple
118
+ },
119
+ marko: {
120
+ name: "Marko Antić",
121
+ initials: "MA",
122
+ color: _shared.AVATAR_COLORS.teal
123
+ },
124
+ dina: {
125
+ name: "Dina Kovač",
126
+ initials: "DK",
127
+ color: _shared.AVATAR_COLORS.orange
128
+ },
129
+ elena: {
130
+ name: "Elena Petrović",
131
+ initials: "EP",
132
+ color: _shared.AVATAR_COLORS.yellow
133
+ },
134
+ tomas: {
135
+ name: "Tomas Slavik",
136
+ initials: "TS",
137
+ color: _shared.AVATAR_COLORS.blue
138
+ }
139
+ };
140
+
141
+ /* Label colours are data too: they are the label's own colour in the app. */
142
+ var PICKER_LABELS = exports.PICKER_LABELS = {
143
+ assigned: {
144
+ text: "ASSIGNED",
145
+ color: "#E08B8B"
146
+ },
147
+ blocked: {
148
+ text: "BLOCKED",
149
+ color: "#D9534F"
150
+ },
151
+ inProgress: {
152
+ text: "IN PROGRESS",
153
+ color: "#9BA0A8"
154
+ },
155
+ review: {
156
+ text: "REVIEW",
157
+ color: "#D4A017"
158
+ }
159
+ };
160
+
161
+ /* Project labels carry a background AND a darker text colour, the pair the app
162
+ stores as `color` / `darker_text_color`. Uppercase because that is how the
163
+ app saves a new project label (ProjectLabel.jsx upper-cases the name). */
164
+ var PICKER_PROJECT_LABELS = exports.PICKER_PROJECT_LABELS = {
165
+ client: {
166
+ text: "CLIENT WORK",
167
+ color: "#FBBB75",
168
+ textColor: "#6B4416"
169
+ },
170
+ internal: {
171
+ text: "INTERNAL",
172
+ color: "#B7D8B0",
173
+ textColor: "#2F5B28"
174
+ },
175
+ urgent: {
176
+ text: "URGENT",
177
+ color: "#F3A7A7",
178
+ textColor: "#7A2222"
179
+ },
180
+ research: {
181
+ text: "RESEARCH",
182
+ color: "#B8CDEB",
183
+ textColor: "#274974"
184
+ }
185
+ };
186
+
187
+ /* A compact constructor so every mocked task stays one readable row. */
188
+ var mockTask = function mockTask(number, name, taskList) {
189
+ var extra = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
190
+ return _objectSpread({
191
+ id: "task-".concat(number),
192
+ number,
193
+ name,
194
+ taskList,
195
+ assignees: [PICKER_PEOPLE.elena],
196
+ date: "16. Jul"
197
+ }, extra);
198
+ };
199
+ var PICKER_PROJECTS = exports.PICKER_PROJECTS = [{
200
+ id: "p-ama",
201
+ name: "AMA Webinar",
202
+ client: "ActiveCollab Inc.",
203
+ description: "Quarterly live session with the community. Collect questions up front, keep the run of show to forty minutes, and publish the recap the same week.",
204
+ lead: PICKER_PEOPLE.jasmina,
205
+ label: PICKER_PROJECT_LABELS.client,
206
+ tasks: [mockTask(184, "Draft the run of show", "Preparation", {
207
+ subtasks: 3
208
+ }), mockTask(185, "Collect questions from the community", "Preparation", {
209
+ label: PICKER_LABELS.assigned,
210
+ comments: 4
211
+ }), mockTask(186, "Record the intro segment", "Production", {
212
+ assignees: [PICKER_PEOPLE.marko]
213
+ }), mockTask(187, "Publish the recap post", "Follow-up", {
214
+ label: PICKER_LABELS.review,
215
+ date: "24. Jul"
216
+ })]
217
+ }, {
218
+ id: "p-care",
219
+ /* Internal project WITH a description and a leader, but no label. */
220
+ name: "Customer Care Team",
221
+ client: null,
222
+ description: "Where the support team plans its week. Macros, escalation paths and the weekly churn review all live here.",
223
+ lead: PICKER_PEOPLE.dina,
224
+ tasks: [mockTask(201, "Rewrite the macro replies", "Inbox", {
225
+ label: PICKER_LABELS.inProgress,
226
+ comments: 2
227
+ }), mockTask(202, "Weekly churn review", "Reporting", {
228
+ subtasks: 5
229
+ }), mockTask(203, "Escalation path for billing", "Inbox", {
230
+ assignees: [PICKER_PEOPLE.tomas]
231
+ }), mockTask(204, "Onboarding call script", "Reporting", {
232
+ date: "2. Aug"
233
+ })]
234
+ }, {
235
+ id: "p-backlog",
236
+ /* No description and NO leader — the meta row is the label alone, pushed
237
+ to the right of an otherwise empty row. */
238
+ name: "Design Backlog",
239
+ client: null,
240
+ label: PICKER_PROJECT_LABELS.internal,
241
+ tasks: [mockTask(311, "Whiteboard element inspector", "Ideas", {
242
+ label: PICKER_LABELS.assigned,
243
+ subtasks: 2,
244
+ comments: 3
245
+ }), mockTask(312, "Empty states for the board list", "Ideas"), mockTask(313, "Dark theme audit of the dock", "Ready", {
246
+ label: PICKER_LABELS.blocked,
247
+ assignees: [PICKER_PEOPLE.jasmina, PICKER_PEOPLE.marko]
248
+ }), mockTask(314, "Icon set for board elements", "Ready", {
249
+ date: "9. Aug"
250
+ })]
251
+ }, {
252
+ id: "p-design-team",
253
+ /* Leader only: no description, no label. */
254
+ name: "Design Team",
255
+ client: null,
256
+ lead: PICKER_PEOPLE.elena,
257
+ tasks: [mockTask(402, "Design system spring cleanup", "This Sprint", {
258
+ subtasks: 8
259
+ }), mockTask(403, "Retro board template", "This Sprint", {
260
+ label: PICKER_LABELS.inProgress,
261
+ assignees: [PICKER_PEOPLE.dina]
262
+ }), mockTask(404, "Handoff checklist", "Next Sprint", {
263
+ comments: 1
264
+ }), mockTask(405, "Component audit: cards", "Next Sprint", {
265
+ date: "30. Jul"
266
+ })]
267
+ }, {
268
+ id: "p-foo",
269
+ /* The bare minimum a project card can be: name and client line only. No
270
+ description, no leader, no label — so no meta row at all. */
271
+ name: "Foo Kanban",
272
+ client: null,
273
+ tasks: [mockTask(77, "Set up the columns", "Task List", {
274
+ subtasks: 1
275
+ }), mockTask(78, "Import the old cards", "Task List", {
276
+ label: PICKER_LABELS.blocked
277
+ }), mockTask(79, "Invite the team", "Task List 2", {
278
+ assignees: [PICKER_PEOPLE.marko]
279
+ }), mockTask(80, "Archive stale swimlanes", "Task List 2", {
280
+ date: "5. Aug"
281
+ })]
282
+ }, {
283
+ id: "p-marketing",
284
+ name: "Marketing Site",
285
+ client: "ActiveCollab Inc.",
286
+ description: "Pricing, case studies and the homepage. Copy passes go through content, performance budgets through engineering.",
287
+ lead: PICKER_PEOPLE.marko,
288
+ label: PICKER_PROJECT_LABELS.urgent,
289
+ tasks: [mockTask(510, "Pricing page copy pass", "Content", {
290
+ label: PICKER_LABELS.review,
291
+ comments: 6
292
+ }), mockTask(511, "Case study: Bright Lane", "Content", {
293
+ subtasks: 4
294
+ }), mockTask(512, "Lighthouse budget for the homepage", "Engineering", {
295
+ assignees: [PICKER_PEOPLE.tomas]
296
+ }), mockTask(513, "Cookie banner rework", "Engineering", {
297
+ date: "11. Aug"
298
+ })]
299
+ }, {
300
+ id: "p-research",
301
+ name: "Product Research",
302
+ client: null,
303
+ description: "Interviews, synthesis and concept tests for the board work. Findings are shared with design at the end of each round.",
304
+ lead: PICKER_PEOPLE.jasmina,
305
+ label: PICKER_PROJECT_LABELS.research,
306
+ tasks: [mockTask(620, "Interview script for board users", "Discovery", {
307
+ subtasks: 3
308
+ }), mockTask(621, "Synthesis of the June calls", "Discovery", {
309
+ label: PICKER_LABELS.inProgress,
310
+ comments: 2
311
+ }), mockTask(622, "Concept test: project cards", "Validation", {
312
+ assignees: [PICKER_PEOPLE.elena, PICKER_PEOPLE.dina]
313
+ }), mockTask(623, "Share findings with design", "Validation", {
314
+ date: "19. Aug"
315
+ })]
316
+ }, {
317
+ id: "p-brand",
318
+ name: "Brand Refresh",
319
+ client: "Bright Lane Studio",
320
+ description: "Identity work: logo lockups, a typography scale and the photography direction, collected into a brand book.",
321
+ lead: PICKER_PEOPLE.dina,
322
+ label: PICKER_PROJECT_LABELS.client,
323
+ tasks: [mockTask(701, "Logo lockup variations", "Identity", {
324
+ subtasks: 6
325
+ }), mockTask(702, "Typography scale", "Identity", {
326
+ label: PICKER_LABELS.assigned
327
+ }), mockTask(703, "Photography direction", "Guidelines", {
328
+ comments: 3
329
+ }), mockTask(704, "Brand book layout", "Guidelines", {
330
+ date: "28. Jul"
331
+ })]
332
+ }, {
333
+ id: "p-portal",
334
+ /* Leader AND label, but no description. */
335
+ name: "Client Portal",
336
+ client: "Bright Lane Studio",
337
+ lead: PICKER_PEOPLE.tomas,
338
+ label: PICKER_PROJECT_LABELS.urgent,
339
+ tasks: [mockTask(810, "Invoice list pagination", "Backend", {
340
+ label: PICKER_LABELS.blocked,
341
+ subtasks: 2
342
+ }), mockTask(811, "Single sign-on for clients", "Backend", {
343
+ comments: 5
344
+ }), mockTask(812, "Dashboard tiles", "Frontend", {
345
+ assignees: [PICKER_PEOPLE.jasmina]
346
+ }), mockTask(813, "Mobile navigation", "Frontend", {
347
+ date: "3. Sep"
348
+ })]
349
+ }];
350
+
351
+ /* The picker groups projects by client and tasks by task list, exactly the way
352
+ `ProjectList` / `TaskList` group them in the app. Order is the data's own
353
+ order — the first project of a client decides where its group sits. Internal
354
+ projects group under the same word their card shows, so the header and the
355
+ card cannot say different things about the same project. */
356
+ var groupProjects = exports.groupProjects = function groupProjects(projects) {
357
+ return projects.reduce(function (groups, project) {
358
+ var client = projectClientLabel(project);
359
+ var group = groups.find(function (entry) {
360
+ return entry.client === client;
361
+ });
362
+ if (group) {
363
+ group.projects.push(project);
364
+ return groups;
365
+ }
366
+ return [...groups, {
367
+ client,
368
+ projects: [project]
369
+ }];
370
+ }, []);
371
+ };
372
+ var groupTasks = exports.groupTasks = function groupTasks(tasks) {
373
+ return tasks.reduce(function (groups, task) {
374
+ var group = groups.find(function (entry) {
375
+ return entry.taskList === task.taskList;
376
+ });
377
+ if (group) {
378
+ group.tasks.push(task);
379
+ return groups;
380
+ }
381
+ return [...groups, {
382
+ taskList: task.taskList,
383
+ tasks: [task]
384
+ }];
385
+ }, []);
386
+ };
387
+ var matchesQuery = function matchesQuery(value, query) {
388
+ return value.toLowerCase().includes(query.trim().toLowerCase());
389
+ };
390
+
391
+ /** `#123: Name` — `utils/taskDisplayName`, the app's own task display name. */
392
+ var taskDisplayName = exports.taskDisplayName = function taskDisplayName(task) {
393
+ return "#".concat(task.number, ": ").concat(task.name);
394
+ };
395
+
396
+ /* ------------------------------------------------------------------ */
397
+ /* Limited access */
398
+ /* ------------------------------------------------------------------ */
399
+
400
+ /**
401
+ * A board is shared with people who do not all see the same projects. When a
402
+ * card points at a task or project the current viewer cannot open, the board
403
+ * cannot just drop it: the element is part of everyone else's layout, and a
404
+ * hole would be a worse lie than a blank. So the card stays and says exactly
405
+ * one thing — something is here, and it is not yours to see.
406
+ *
407
+ * This follows the app's existing limited-access language (Workload):
408
+ * - the wording is "Limited Access", the phrase `EntityPlaceholder`,
409
+ * `WorkloadCalendarEvent`, Expenses and Timelist all already use;
410
+ * - nothing else is rendered. Workload's own limited cell
411
+ * (`.workload_null_project_cell`) shows an aggregate and no name, and the
412
+ * shape it is fed is literally `{ hoursOccupied, leftOffset }` — a quantity
413
+ * and a position, never an identity;
414
+ * - the surface is muted and hatched, the treatment the app reserves for
415
+ * "this area is not live" (weekend / day-off cells, `--muted-surface`);
416
+ * - no affordance suggests opening: Workload points the link at "#", disables
417
+ * the name tooltip so the title cannot leak, and marks blocked interactions
418
+ * `cursor: not-allowed`.
419
+ */
420
+ var LIMITED_ACCESS_LABEL = exports.LIMITED_ACCESS_LABEL = "Limited Access";
421
+
422
+ /* The one sentence a limited card is allowed to say, in the app's own voice.
423
+ It states the viewer's situation as a fact and stops — Workload's secondary
424
+ line is "with your limited access" (WorkloadProjectContainer.js), and its
425
+ status sentences read "Work scheduled for a day off." and "task doesn't have
426
+ an estimate": second person, sentence case, contraction, no apology, and
427
+ never a word about why the thing is on the screen. */
428
+ var LIMITED_ACCESS_MESSAGE = exports.LIMITED_ACCESS_MESSAGE = "You don't have access to this item.";
429
+
430
+ /* ------------------------------------------------------------------ */
431
+ /* The element on the board */
432
+ /* ------------------------------------------------------------------ */
433
+
434
+ /** A project / task element on the board. Position lives in `positions`. */
435
+
436
+ /** The natural width a resolved card of this kind grows to. */
437
+ var projectCardWidth = exports.projectCardWidth = function projectCardWidth(kind) {
438
+ return kind === "project" ? PROJECT_CARD_WIDTH : TASK_CARD_WIDTH;
439
+ };
440
+
441
+ /**
442
+ * What the card is OF — the task's or the project's own name.
443
+ *
444
+ * These cards are a view of a record that lives in the project, so the board
445
+ * does not get to name them: the title is the entity's, and it is read-only
446
+ * wherever it appears. A placeholder has no entity yet, so it says what it is
447
+ * still waiting for.
448
+ */
449
+ var projectElementTitle = exports.projectElementTitle = function projectElementTitle(element) {
450
+ if (element.limited) {
451
+ return LIMITED_ACCESS_LABEL;
452
+ }
453
+ if (element.kind === "project") {
454
+ var _element$project$name, _element$project;
455
+ return (_element$project$name = (_element$project = element.project) === null || _element$project === void 0 ? void 0 : _element$project.name) !== null && _element$project$name !== void 0 ? _element$project$name : "Project";
456
+ }
457
+ return element.task ? taskDisplayName(element.task) : "Task";
458
+ };
459
+
460
+ /* ------------------------------------------------------------------ */
461
+ /* Seeded board state */
462
+ /* ------------------------------------------------------------------ */
463
+
464
+ /**
465
+ * Two cards the board starts with, both limited.
466
+ *
467
+ * They are seeded rather than reachable through the picker on purpose: limited
468
+ * access is a property of the VIEWER, and you cannot pick a card you are not
469
+ * allowed to see. Someone else placed these; this session is simply the one
470
+ * that cannot open them. Seeding is also the whole demonstration — no "view as"
471
+ * switch is needed to show what the state looks like.
472
+ *
473
+ * Both are `resolved` with `project` and `task` left null: the record was never
474
+ * withheld pending a fetch, it is withheld full stop.
475
+ */
476
+
477
+ var SEEDED = [{
478
+ element: {
479
+ id: "limited-task",
480
+ kind: "task",
481
+ width: TASK_CARD_WIDTH,
482
+ height: PLACEHOLDER_MIN_HEIGHT,
483
+ status: "resolved",
484
+ limited: true,
485
+ project: null,
486
+ task: null
487
+ },
488
+ x: 120,
489
+ y: 200
490
+ }, {
491
+ element: {
492
+ id: "limited-project",
493
+ kind: "project",
494
+ width: PROJECT_CARD_WIDTH,
495
+ height: PLACEHOLDER_MIN_HEIGHT,
496
+ status: "resolved",
497
+ limited: true,
498
+ project: null,
499
+ task: null
500
+ },
501
+ x: 120,
502
+ y: 380
503
+ }];
504
+
505
+ /* Derived from one list, the way INITIAL_POSITIONS is derived from
506
+ CANVAS_ELEMENTS — a seed's position is written once, not twice. */
507
+ var SEEDED_PROJECT_ELEMENTS = exports.SEEDED_PROJECT_ELEMENTS = SEEDED.map(function (seed) {
508
+ return seed.element;
509
+ });
510
+ var SEEDED_PROJECT_ELEMENT_POSITIONS = exports.SEEDED_PROJECT_ELEMENT_POSITIONS = SEEDED.reduce(function (acc, seed) {
511
+ acc[seed.element.id] = {
512
+ x: seed.x,
513
+ y: seed.y
514
+ };
515
+ return acc;
516
+ }, {});
517
+
518
+ /* The board wrapper. It is the canvas element box, only rounded — the shared
519
+ box is square because shapes are, and a square selection ring around a
520
+ rounded card reads as a second, wrong border. The transition is what makes
521
+ the placeholder GROW into its card instead of being replaced by one. */
522
+ var StyledProjectElementBox = exports.StyledProjectElementBox = (0, _styledComponents.default)(_elements.StyledCanvasElement).withConfig({
523
+ displayName: "projectElements__StyledProjectElementBox",
524
+ componentId: "sc-1cayxi5-0"
525
+ })(["border-radius:8px;transition:width 160ms ease;&.limited{cursor:not-allowed;}"]);
526
+
527
+ /* The rubber band and the armed tool's hover preview — the same blue dashed
528
+ box the link tool draws, so the gesture reads as one board convention. */
529
+ var StyledProjectDraft = exports.StyledProjectDraft = _styledComponents.default.div.withConfig({
530
+ displayName: "projectElements__StyledProjectDraft",
531
+ componentId: "sc-1cayxi5-1"
532
+ })(["position:absolute;box-sizing:border-box;border:1.5px dashed ", ";border-radius:8px;background-color:rgba(59,130,246,0.08);pointer-events:none;"], _elements.SELECTION_BLUE);
533
+
534
+ /* The placeholder that waits for the picker's answer. Card-like on purpose:
535
+ rounded, dashed, tinted — a card-shaped hole, not a selection marquee. */
536
+ var StyledPlaceholder = exports.StyledPlaceholder = _styledComponents.default.div.withConfig({
537
+ displayName: "projectElements__StyledPlaceholder",
538
+ componentId: "sc-1cayxi5-2"
539
+ })(["box-sizing:border-box;width:100%;height:100%;display:flex;align-items:center;justify-content:center;padding:12px;border:1.5px dashed var(--border-primary);border-radius:8px;background-color:var(--color-theme-200);user-select:none;"]);
540
+
541
+ /* The resolving flash: the placeholder's box, with the card's rows sketched in
542
+ so the swap to the real card does not read as a different element. */
543
+ var StyledResolving = exports.StyledResolving = (0, _styledComponents.default)(StyledPlaceholder).withConfig({
544
+ displayName: "projectElements__StyledResolving",
545
+ componentId: "sc-1cayxi5-3"
546
+ })(["flex-direction:column;align-items:stretch;justify-content:center;gap:10px;.c-loader{height:12px;}"]);
547
+
548
+ /**
549
+ * The limited-access face — the card-shaped translation of Workload's black
550
+ * `.workload_null_project_cell`.
551
+ *
552
+ * The app hardcodes that cell `#000000` on `#ffffff` text, which works there
553
+ * because it always sits on light paper. A canvas is not always light: on the
554
+ * dark board a black slab would sink into the background instead of reading as
555
+ * something withheld. So the same idea is expressed with the design system's
556
+ * own "not live" tokens — `--muted-surface` / `--muted-surface-text` are the
557
+ * pair the app itself substitutes for muted surfaces in dark themes, and they
558
+ * are a legible, deliberately flat combination in every theme.
559
+ *
560
+ * The -45° hatch is copied stroke for stroke from the app's unavailable
561
+ * surfaces (weekend and day-off cells): `transparent 5px, <line> 5px, <line>
562
+ * 5.7px`, with `--stripe-pattern` as the line — the token workload.less uses
563
+ * for exactly this in dark themes.
564
+ */
565
+ var StyledLimitedCard = exports.StyledLimitedCard = _styledComponents.default.div.withConfig({
566
+ displayName: "projectElements__StyledLimitedCard",
567
+ componentId: "sc-1cayxi5-4"
568
+ })(["box-sizing:border-box;width:100%;height:100%;min-height:96px;display:flex;align-items:center;justify-content:center;padding:12px;border:1px solid var(--border-primary);border-radius:8px;background-color:var(--muted-surface);background-image:repeating-linear-gradient( -45deg,transparent,transparent 5px,var(--stripe-pattern) 5px,var(--stripe-pattern) 5.7px );color:var(--muted-surface-text);cursor:not-allowed;user-select:none;.limited-label{color:var(--muted-surface-text);}"]);
569
+
570
+ /**
571
+ * Both resolved faces are the design system's `StackedCard` — the same shell the
572
+ * board's link and file cards already stand on — filled with the shared typed
573
+ * content cards from `../stackedCard/content`. The board owns only the width and
574
+ * the selection state; everything inside the card is the card's business.
575
+ *
576
+ * Identical to `StyledLinkCard`: the shell's reference 260px width gives way to
577
+ * the box the board drew, and the height stays auto.
578
+ */
579
+ var StyledEntityCard = (0, _styledComponents.default)(_StackedCard.StackedCard).withConfig({
580
+ displayName: "projectElements__StyledEntityCard",
581
+ componentId: "sc-1cayxi5-5"
582
+ })(["&&{width:100%;}user-select:none;"]);
583
+ /**
584
+ * The task face — the shared `TaskCard`, with the project line switched on.
585
+ *
586
+ * The board writes no anatomy of its own: the title clamp, the info line (labels
587
+ * as colour text, the counter signifiers and their 2px dot separators) and the
588
+ * date / assignees row all live in the content card, where the column-view
589
+ * baseline they preserve is documented. The one thing the board asks for is the
590
+ * project line above the title: on a canvas a task card has lost the column that
591
+ * used to say which project it came from.
592
+ *
593
+ * The title is the app's own display name (`#123: Name`, `taskDisplayName`), so
594
+ * the card's separate task-number prefix stays off.
595
+ */
596
+ var TaskElementCard = exports.TaskElementCard = function TaskElementCard(_ref) {
597
+ var _task$subtasks, _task$comments, _task$date;
598
+ var project = _ref.project,
599
+ task = _ref.task,
600
+ selected = _ref.selected;
601
+ return /*#__PURE__*/_react.default.createElement(StyledEntityCard, {
602
+ selected: selected
603
+ }, /*#__PURE__*/_react.default.createElement(_content.TaskCard, {
604
+ title: taskDisplayName(task),
605
+ showProjectName: true,
606
+ projectName: project.name,
607
+ labels: task.label ? [task.label] : [],
608
+ subtaskCount: (_task$subtasks = task.subtasks) !== null && _task$subtasks !== void 0 ? _task$subtasks : 0,
609
+ commentCount: (_task$comments = task.comments) !== null && _task$comments !== void 0 ? _task$comments : 0,
610
+ dateLabel: (_task$date = task.date) !== null && _task$date !== void 0 ? _task$date : null,
611
+ assigned: task.assignees.length > 0,
612
+ assignees: task.assignees
613
+ }));
614
+ };
615
+
616
+ /**
617
+ * The project face — the shared `ProjectCard`.
618
+ *
619
+ * The whiteboard's face spec and the content type's are the SAME spec, so it is
620
+ * implemented once, in the card: name and client, an optional description
621
+ * excerpt, and one row carrying the leader on the left and the label on the
622
+ * right. No progress indicator, and no row at all when the project has neither a
623
+ * leader nor a label.
624
+ */
625
+ var ProjectElementCard = exports.ProjectElementCard = function ProjectElementCard(_ref2) {
626
+ var project = _ref2.project,
627
+ selected = _ref2.selected;
628
+ return /*#__PURE__*/_react.default.createElement(StyledEntityCard, {
629
+ selected: selected
630
+ }, /*#__PURE__*/_react.default.createElement(_content.ProjectCard, {
631
+ name: project.name,
632
+ client: project.client,
633
+ description: project.description,
634
+ leader: project.lead,
635
+ label: project.label
636
+ }));
637
+ };
638
+
639
+ /**
640
+ * The limited-access card. Deliberately the shortest component in this file:
641
+ * one phrase, no icon, no entity data of any kind.
642
+ *
643
+ * No icon because the app uses none — Expenses and Timelist both render the
644
+ * bare `Caption1 weight="bold"` "Limited Access", and an icon here would be a
645
+ * second signal competing with the hatch. Even the KIND (task vs project) is
646
+ * withheld: the viewer is told that a card exists, not what sort of thing it
647
+ * points at.
648
+ *
649
+ * The tooltip is allowed to exist because it carries the viewer's situation,
650
+ * not the entity's identity — the opposite of the tooltip Workload disables on
651
+ * a limited event, which would have leaked the name.
652
+ */
653
+ var LimitedElementCard = exports.LimitedElementCard = function LimitedElementCard() {
654
+ return /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
655
+ title: LIMITED_ACCESS_MESSAGE
656
+ }, /*#__PURE__*/_react.default.createElement(StyledLimitedCard, {
657
+ "data-testid": "wb-limited-card"
658
+ }, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
659
+ weight: "bold",
660
+ className: "limited-label"
661
+ }, LIMITED_ACCESS_LABEL)));
662
+ };
663
+
664
+ /** One board element, in whichever of its three states it currently is. */
665
+ var ProjectElementNode = exports.ProjectElementNode = function ProjectElementNode(_ref3) {
666
+ var element = _ref3.element,
667
+ selected = _ref3.selected;
668
+ /* Checked before anything else: a limited element must never be able to fall
669
+ through to a branch that renders entity data. */
670
+ if (element.limited) {
671
+ return /*#__PURE__*/_react.default.createElement(LimitedElementCard, null);
672
+ }
673
+ if (element.status === "placeholder") {
674
+ return /*#__PURE__*/_react.default.createElement(StyledPlaceholder, null, /*#__PURE__*/_react.default.createElement(_Typography.Caption1, {
675
+ color: "tertiary"
676
+ }, element.kind === "project" ? "Pick a project…" : "Pick a task…"));
677
+ }
678
+ if (element.status === "resolving") {
679
+ return /*#__PURE__*/_react.default.createElement(StyledResolving, null, /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
680
+ style: {
681
+ width: "80%"
682
+ }
683
+ }), /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
684
+ style: {
685
+ width: "55%"
686
+ }
687
+ }), /*#__PURE__*/_react.default.createElement(_Loaders.SkeletonLoader, {
688
+ style: {
689
+ width: "40%"
690
+ }
691
+ }));
692
+ }
693
+ if (element.kind === "project" && element.project) {
694
+ return /*#__PURE__*/_react.default.createElement(ProjectElementCard, {
695
+ project: element.project,
696
+ selected: selected
697
+ });
698
+ }
699
+ if (element.project && element.task) {
700
+ return /*#__PURE__*/_react.default.createElement(TaskElementCard, {
701
+ project: element.project,
702
+ task: element.task,
703
+ selected: selected
704
+ });
705
+ }
706
+ return /*#__PURE__*/_react.default.createElement(StyledPlaceholder, null);
707
+ };
708
+
709
+ /* ------------------------------------------------------------------ */
710
+ /* The picker */
711
+ /* ------------------------------------------------------------------ */
712
+
713
+ /* The popover, centred over the board's own overlay. It is deliberately NOT a
714
+ DS `Dialog`: the app's picker has no title and no action buttons — you search,
715
+ you pick, it closes. `Modal` is used only for what a dialog and a popover do
716
+ share: the portal, the overlay, the focus lock and the Esc handler. */
717
+ var StyledPickerPanel = (0, _styledComponents.default)(_Paper.Paper).withConfig({
718
+ displayName: "projectElements__StyledPickerPanel",
719
+ componentId: "sc-1cayxi5-6"
720
+ })(["position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;flex-direction:column;width:470px;max-width:calc(100vw - 32px);max-height:70vh;overflow:hidden;&&{border-radius:12px;border:1px solid var(--border-primary);box-shadow:0 18px 48px rgba(0,0,0,0.24);}"]);
721
+
722
+ /* Search + adornments, mirroring `Select`'s own form row: the back arrow is a
723
+ start adornment (TaskList), the group-by sort a end adornment (ProjectList). */
724
+ var StyledPickerHeader = _styledComponents.default.div.withConfig({
725
+ displayName: "projectElements__StyledPickerHeader",
726
+ componentId: "sc-1cayxi5-7"
727
+ })(["display:flex;align-items:center;gap:8px;flex-shrink:0;padding:12px 12px 8px;.picker-search{width:100%;border-radius:8px;}.picker-search:focus-within{border-color:var(--color-primary);outline:1px solid var(--color-primary);}"]);
728
+ var StyledPickerList = _styledComponents.default.div.withConfig({
729
+ displayName: "projectElements__StyledPickerList",
730
+ componentId: "sc-1cayxi5-8"
731
+ })(["flex:1 1 auto;min-height:0;overflow-y:auto;padding:4px 0 12px;.picker-group-header{box-sizing:border-box;height:36px;display:flex;align-items:center;padding:0 20px;margin-top:4px;}.picker-empty{padding:24px 20px;text-align:center;}"]);
732
+
733
+ /* A generously spaced row: the name on the left, an empty radio on the right —
734
+ the shape `Select` gives a single-choice option, at picker scale. */
735
+ var StyledPickerRow = _styledComponents.default.div.withConfig({
736
+ displayName: "projectElements__StyledPickerRow",
737
+ componentId: "sc-1cayxi5-9"
738
+ })(["box-sizing:border-box;height:56px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:0 20px;cursor:pointer;outline:none;&:hover,&:focus-visible{background-color:var(--color-theme-200);}.row-name{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;}.c-radio-btn{flex-shrink:0;}"]);
739
+ var PickerRow = function PickerRow(_ref4) {
740
+ var id = _ref4.id,
741
+ name = _ref4.name,
742
+ onSelect = _ref4.onSelect;
743
+ return /*#__PURE__*/_react.default.createElement(StyledPickerRow, {
744
+ role: "button",
745
+ tabIndex: 0,
746
+ onClick: onSelect,
747
+ onKeyDown: function onKeyDown(event) {
748
+ if (event.key === "Enter" || event.key === " ") {
749
+ event.preventDefault();
750
+ onSelect();
751
+ }
752
+ }
753
+ }, /*#__PURE__*/_react.default.createElement("span", {
754
+ className: "row-name"
755
+ }, /*#__PURE__*/_react.default.createElement(_Typography.Body2, null, name)), /*#__PURE__*/_react.default.createElement(_RadioButton.RadioButton, {
756
+ id: "wb-picker-".concat(id),
757
+ tabIndex: -1
758
+ }));
759
+ };
760
+ /**
761
+ * One picker, two flows — the same split the app makes between
762
+ * `MenuSelector/ProjectList` (one step) and `ProjectTaskMenuSelector` (project,
763
+ * then task). The task flow deliberately drops the app's "Directly on project"
764
+ * default option: a task card has to be a task.
765
+ */
766
+ var ProjectElementPicker = exports.ProjectElementPicker = function ProjectElementPicker(_ref5) {
767
+ var kind = _ref5.kind,
768
+ onSelectProject = _ref5.onSelectProject,
769
+ onSelectTask = _ref5.onSelectTask,
770
+ onCancel = _ref5.onCancel;
771
+ var _useState = (0, _react.useState)(null),
772
+ _useState2 = _slicedToArray(_useState, 2),
773
+ project = _useState2[0],
774
+ setProject = _useState2[1];
775
+ var _useState3 = (0, _react.useState)(""),
776
+ _useState4 = _slicedToArray(_useState3, 2),
777
+ query = _useState4[0],
778
+ setQuery = _useState4[1];
779
+ /* The picker opens on a mouseUP — the end of the draw gesture — so the click
780
+ that closes that gesture arrives when the overlay is already on screen. A
781
+ dismissal therefore has to be a WHOLE click on the overlay: press, then
782
+ release. Without this the placeholder could be cancelled by the very
783
+ gesture that created it. */
784
+ var pressedInsideRef = (0, _react.useRef)(false);
785
+ var onTaskStep = kind === "task" && project !== null;
786
+ var projectGroups = (0, _react.useMemo)(function () {
787
+ return groupProjects(PICKER_PROJECTS.filter(function (entry) {
788
+ return matchesQuery(entry.name, query);
789
+ }));
790
+ }, [query]);
791
+ var taskGroups = (0, _react.useMemo)(function () {
792
+ return project ? groupTasks(project.tasks.filter(function (task) {
793
+ return matchesQuery(taskDisplayName(task), query);
794
+ })) : [];
795
+ }, [project, query]);
796
+ var handleProject = function handleProject(picked) {
797
+ if (kind === "project") {
798
+ onSelectProject(picked);
799
+ return;
800
+ }
801
+ setProject(picked);
802
+ setQuery("");
803
+ };
804
+ var handleBack = function handleBack() {
805
+ setProject(null);
806
+ setQuery("");
807
+ };
808
+ var isEmpty = onTaskStep ? taskGroups.length === 0 : projectGroups.length === 0;
809
+ return /*#__PURE__*/_react.default.createElement(_Modal.Modal, {
810
+ open: true,
811
+ onClose: onCancel,
812
+ disableBackgroundClick: true,
813
+ onMouseDown: function onMouseDown() {
814
+ pressedInsideRef.current = true;
815
+ },
816
+ onClickOutside: function onClickOutside() {
817
+ if (pressedInsideRef.current) {
818
+ onCancel();
819
+ }
820
+ }
821
+ }, /*#__PURE__*/_react.default.createElement(StyledPickerPanel, {
822
+ type: "paper-2"
823
+ }, /*#__PURE__*/_react.default.createElement(StyledPickerHeader, null, onTaskStep ? /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
824
+ title: "Back to projects"
825
+ }, /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, {
826
+ type: "button",
827
+ variant: "text gray",
828
+ onClick: handleBack,
829
+ "aria-label": "Back to projects"
830
+ }, /*#__PURE__*/_react.default.createElement(_Icons.ArrowLeftIcon, null))) : null, /*#__PURE__*/_react.default.createElement(_Input.Input, {
831
+ className: "picker-search",
832
+ autoFocus: true,
833
+ value: query,
834
+ placeholder: onTaskStep ? "Search a task" : "Search a project",
835
+ onChange: function onChange(event) {
836
+ return setQuery(event.target.value);
837
+ }
838
+ }), onTaskStep ? null : /*#__PURE__*/_react.default.createElement(_Tooltip.Tooltip, {
839
+ title: "Group by"
840
+ }, /*#__PURE__*/_react.default.createElement(_IconButton.IconButton, {
841
+ type: "button",
842
+ variant: "text gray"
843
+ }, /*#__PURE__*/_react.default.createElement(_Icons.SortGeneralIcon, null)))), /*#__PURE__*/_react.default.createElement(StyledPickerList, null, isEmpty ? /*#__PURE__*/_react.default.createElement("div", {
844
+ className: "picker-empty"
845
+ }, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
846
+ color: "tertiary"
847
+ }, onTaskStep ? "No Result" : "No Projects")) : null, onTaskStep && project ? taskGroups.map(function (group) {
848
+ return /*#__PURE__*/_react.default.createElement("div", {
849
+ key: group.taskList
850
+ }, /*#__PURE__*/_react.default.createElement("div", {
851
+ className: "picker-group-header"
852
+ }, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
853
+ weight: "bold"
854
+ }, group.taskList)), group.tasks.map(function (task) {
855
+ return /*#__PURE__*/_react.default.createElement(PickerRow, {
856
+ key: task.id,
857
+ id: task.id,
858
+ name: taskDisplayName(task),
859
+ onSelect: function onSelect() {
860
+ return onSelectTask(project, task);
861
+ }
862
+ });
863
+ }));
864
+ }) : null, onTaskStep ? null : projectGroups.map(function (group) {
865
+ return /*#__PURE__*/_react.default.createElement("div", {
866
+ key: group.client
867
+ }, /*#__PURE__*/_react.default.createElement("div", {
868
+ className: "picker-group-header"
869
+ }, /*#__PURE__*/_react.default.createElement(_Typography.Body2, {
870
+ weight: "bold"
871
+ }, group.client)), group.projects.map(function (entry) {
872
+ return /*#__PURE__*/_react.default.createElement(PickerRow, {
873
+ key: entry.id,
874
+ id: entry.id,
875
+ name: entry.name,
876
+ onSelect: function onSelect() {
877
+ return handleProject(entry);
878
+ }
879
+ });
880
+ }));
881
+ }))));
882
+ };
883
+ //# sourceMappingURL=projectElements.js.map