@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.
- 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/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/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
|
@@ -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
|