@activecollab/components 2.0.417 → 2.0.419
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/Portfolio.js +61 -0
- package/dist/cjs/components/Icons/collection/Portfolio.js.map +1 -0
- package/dist/cjs/components/Icons/collection/PortfolioPlus.js +62 -0
- package/dist/cjs/components/Icons/collection/PortfolioPlus.js.map +1 -0
- package/dist/cjs/components/Icons/collection/Target.js +65 -0
- package/dist/cjs/components/Icons/collection/Target.js.map +1 -0
- package/dist/cjs/components/Icons/collection/index.js +21 -0
- package/dist/cjs/components/Icons/collection/index.js.map +1 -1
- package/dist/cjs/presentation/shared/MessageDialog.js +62 -107
- package/dist/cjs/presentation/shared/MessageDialog.js.map +1 -1
- package/dist/cjs/presentation/shared/MessageFeedback.js +284 -0
- package/dist/cjs/presentation/shared/MessageFeedback.js.map +1 -0
- package/dist/cjs/presentation/shared/PortfolioOptionsMenu.js +64 -0
- package/dist/cjs/presentation/shared/PortfolioOptionsMenu.js.map +1 -0
- package/dist/cjs/presentation/shared/ProjectOptionsMenu.js +367 -0
- package/dist/cjs/presentation/shared/ProjectOptionsMenu.js.map +1 -0
- package/dist/cjs/presentation/shared/dashboardWidgets.js +726 -0
- package/dist/cjs/presentation/shared/dashboardWidgets.js.map +1 -0
- package/dist/cjs/presentation/shared/headers.js +25 -360
- package/dist/cjs/presentation/shared/headers.js.map +1 -1
- package/dist/cjs/presentation/shared/index.js +130 -0
- package/dist/cjs/presentation/shared/index.js.map +1 -1
- package/dist/esm/components/Icons/collection/Portfolio.d.ts +32 -0
- package/dist/esm/components/Icons/collection/Portfolio.d.ts.map +1 -0
- package/dist/esm/components/Icons/collection/Portfolio.js +54 -0
- package/dist/esm/components/Icons/collection/Portfolio.js.map +1 -0
- package/dist/esm/components/Icons/collection/PortfolioPlus.d.ts +32 -0
- package/dist/esm/components/Icons/collection/PortfolioPlus.d.ts.map +1 -0
- package/dist/esm/components/Icons/collection/PortfolioPlus.js +55 -0
- package/dist/esm/components/Icons/collection/PortfolioPlus.js.map +1 -0
- package/dist/esm/components/Icons/collection/Target.d.ts +32 -0
- package/dist/esm/components/Icons/collection/Target.d.ts.map +1 -0
- package/dist/esm/components/Icons/collection/Target.js +58 -0
- package/dist/esm/components/Icons/collection/Target.js.map +1 -0
- package/dist/esm/components/Icons/collection/index.d.ts +3 -0
- package/dist/esm/components/Icons/collection/index.d.ts.map +1 -1
- package/dist/esm/components/Icons/collection/index.js +3 -0
- package/dist/esm/components/Icons/collection/index.js.map +1 -1
- package/dist/esm/presentation/shared/MessageDialog.d.ts +8 -3
- package/dist/esm/presentation/shared/MessageDialog.d.ts.map +1 -1
- package/dist/esm/presentation/shared/MessageDialog.js +64 -98
- package/dist/esm/presentation/shared/MessageDialog.js.map +1 -1
- package/dist/esm/presentation/shared/MessageFeedback.d.ts +54 -0
- package/dist/esm/presentation/shared/MessageFeedback.d.ts.map +1 -0
- package/dist/esm/presentation/shared/MessageFeedback.js +252 -0
- package/dist/esm/presentation/shared/MessageFeedback.js.map +1 -0
- package/dist/esm/presentation/shared/PortfolioOptionsMenu.d.ts +12 -0
- package/dist/esm/presentation/shared/PortfolioOptionsMenu.d.ts.map +1 -0
- package/dist/esm/presentation/shared/PortfolioOptionsMenu.js +46 -0
- package/dist/esm/presentation/shared/PortfolioOptionsMenu.js.map +1 -0
- package/dist/esm/presentation/shared/ProjectOptionsMenu.d.ts +31 -0
- package/dist/esm/presentation/shared/ProjectOptionsMenu.d.ts.map +1 -0
- package/dist/esm/presentation/shared/ProjectOptionsMenu.js +311 -0
- package/dist/esm/presentation/shared/ProjectOptionsMenu.js.map +1 -0
- package/dist/esm/presentation/shared/dashboardWidgets.d.ts +203 -0
- package/dist/esm/presentation/shared/dashboardWidgets.d.ts.map +1 -0
- package/dist/esm/presentation/shared/dashboardWidgets.js +665 -0
- package/dist/esm/presentation/shared/dashboardWidgets.js.map +1 -0
- package/dist/esm/presentation/shared/headers.d.ts +0 -11
- package/dist/esm/presentation/shared/headers.d.ts.map +1 -1
- package/dist/esm/presentation/shared/headers.js +23 -315
- package/dist/esm/presentation/shared/headers.js.map +1 -1
- package/dist/esm/presentation/shared/index.d.ts +8 -0
- package/dist/esm/presentation/shared/index.d.ts.map +1 -1
- package/dist/esm/presentation/shared/index.js +4 -0
- package/dist/esm/presentation/shared/index.js.map +1 -1
- package/dist/index.js +666 -508
- 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,252 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import styled from "styled-components";
|
|
3
|
+
import { screenBelow } from "../../components/BreakPoints";
|
|
4
|
+
import { Button } from "../../components/Button";
|
|
5
|
+
import { Dialog } from "../../components/Dialog";
|
|
6
|
+
import { IconButton } from "../../components/IconButton";
|
|
7
|
+
import { ThumbUpOutlineIcon } from "../../components/Icons";
|
|
8
|
+
import { Label } from "../../components/Label";
|
|
9
|
+
import { Textarea } from "../../components/Textarea";
|
|
10
|
+
import { Tooltip } from "../../components/Tooltip";
|
|
11
|
+
import { Body2 } from "../../components/Typography";
|
|
12
|
+
import { Typography } from "../../components/Typography/Typography";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* MessageFeedback — the reusable "Was this helpful?" flow, as a Presentation
|
|
16
|
+
* mock.
|
|
17
|
+
*
|
|
18
|
+
* It is built only from design-system parts and owns the whole flow, so any
|
|
19
|
+
* content surface (the message dialog, a help article, a settings panel) can
|
|
20
|
+
* drop it in and get the same behaviour:
|
|
21
|
+
*
|
|
22
|
+
* 1. Prompt — an inline "Was this helpful?" row with a thumb up / down.
|
|
23
|
+
* 2. Details — a centred dialog: option chips for the chosen sentiment,
|
|
24
|
+
* plus an optional free-text comment. Submit or Cancel.
|
|
25
|
+
* 3. Thank you — a short confirmation, then the prompt shows a quiet
|
|
26
|
+
* "Thanks for your feedback!".
|
|
27
|
+
*
|
|
28
|
+
* The vote is recorded the moment a thumb is pressed (`onVote`), so a user who
|
|
29
|
+
* cancels the details step still leaves a valid vote — the same contract the
|
|
30
|
+
* trial Feedback flow uses. The details are reported on Submit (`onSubmit`).
|
|
31
|
+
*
|
|
32
|
+
* In the product this lives in application code; the presentation ships this
|
|
33
|
+
* demo copy so the stories need no app imports.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/* ---- public types & default option catalogs ------------------------ */
|
|
37
|
+
|
|
38
|
+
/* The options rate the one piece of content, not the whole product. Each
|
|
39
|
+
negative option maps to an action a writer can take. Override per surface
|
|
40
|
+
through the component props. */
|
|
41
|
+
export const POSITIVE_FEEDBACK_OPTIONS = [{
|
|
42
|
+
id: "clear",
|
|
43
|
+
label: "Clear and easy to understand"
|
|
44
|
+
}, {
|
|
45
|
+
id: "answered",
|
|
46
|
+
label: "Answered my question"
|
|
47
|
+
}, {
|
|
48
|
+
id: "right-detail",
|
|
49
|
+
label: "Right amount of detail"
|
|
50
|
+
}, {
|
|
51
|
+
id: "helped-use",
|
|
52
|
+
label: "Helped me use the feature"
|
|
53
|
+
}, {
|
|
54
|
+
id: "good-examples",
|
|
55
|
+
label: "Good examples or screenshots"
|
|
56
|
+
}, {
|
|
57
|
+
id: "other",
|
|
58
|
+
label: "Other"
|
|
59
|
+
}];
|
|
60
|
+
export const NEGATIVE_FEEDBACK_OPTIONS = [{
|
|
61
|
+
id: "not-answered",
|
|
62
|
+
label: "Did not answer my question"
|
|
63
|
+
}, {
|
|
64
|
+
id: "confusing",
|
|
65
|
+
label: "Confusing or hard to follow"
|
|
66
|
+
}, {
|
|
67
|
+
id: "too-long",
|
|
68
|
+
label: "Too long"
|
|
69
|
+
}, {
|
|
70
|
+
id: "not-enough-detail",
|
|
71
|
+
label: "Not enough detail"
|
|
72
|
+
}, {
|
|
73
|
+
id: "out-of-date",
|
|
74
|
+
label: "Out of date or incorrect"
|
|
75
|
+
}, {
|
|
76
|
+
id: "wrong-plan-role",
|
|
77
|
+
label: "Wrong for my plan or role"
|
|
78
|
+
}, {
|
|
79
|
+
id: "not-found",
|
|
80
|
+
label: "I could not find what I needed"
|
|
81
|
+
}, {
|
|
82
|
+
id: "broken",
|
|
83
|
+
label: "Something is broken"
|
|
84
|
+
}, {
|
|
85
|
+
id: "other",
|
|
86
|
+
label: "Other"
|
|
87
|
+
}];
|
|
88
|
+
|
|
89
|
+
/* ---- the inline prompt row ----------------------------------------- */
|
|
90
|
+
|
|
91
|
+
/* Down-vote reuses the up icon, rotated (the DS set only ships
|
|
92
|
+
ThumbUpOutlineIcon). */
|
|
93
|
+
const StyledPrompt = styled.div.withConfig({
|
|
94
|
+
displayName: "MessageFeedback__StyledPrompt",
|
|
95
|
+
componentId: "sc-1i96o75-0"
|
|
96
|
+
})(["display:flex;align-items:center;justify-content:center;gap:8px;padding:8px 24px 28px;", "{padding:4px 24px 24px;}.fb-question{margin-right:4px;}.fb-btn svg{width:18px;height:18px;}.fb-down svg{transform:rotate(180deg);}"], screenBelow.sm);
|
|
97
|
+
|
|
98
|
+
/* ---- the details dialog -------------------------------------------- */
|
|
99
|
+
|
|
100
|
+
const StyledForm = styled.div.withConfig({
|
|
101
|
+
displayName: "MessageFeedback__StyledForm",
|
|
102
|
+
componentId: "sc-1i96o75-1"
|
|
103
|
+
})(["display:flex;flex-direction:column;gap:20px;.fb-field{display:flex;flex-direction:column;gap:8px;}.fb-chips{display:flex;flex-wrap:wrap;gap:8px;}.c-textarea{width:100%;box-sizing:border-box;}"]);
|
|
104
|
+
|
|
105
|
+
/* The option chip — a pill that toggles to the theme primary tint when
|
|
106
|
+
selected. Translated from the trial Feedback chip into styled-components
|
|
107
|
+
(runtime tw- classes do not work in the presentation storybook). The label
|
|
108
|
+
text is a DS Body2 so it carries the correct font. */
|
|
109
|
+
const StyledChip = styled.button.withConfig({
|
|
110
|
+
displayName: "MessageFeedback__StyledChip",
|
|
111
|
+
componentId: "sc-1i96o75-2"
|
|
112
|
+
})(["display:inline-flex;align-items:center;box-sizing:border-box;margin:0;padding:3px 8px 4px;border:1px solid ", ";border-radius:4px;background-color:", ";cursor:pointer;transition:border-color 120ms ease,background-color 120ms ease;&:hover{border-color:var(--color-primary);}"], _ref => {
|
|
113
|
+
let $selected = _ref.$selected;
|
|
114
|
+
return $selected ? "var(--color-primary)" : "var(--color-theme-500)";
|
|
115
|
+
}, _ref2 => {
|
|
116
|
+
let $selected = _ref2.$selected;
|
|
117
|
+
return $selected ? "var(--color-primary-300)" : "transparent";
|
|
118
|
+
});
|
|
119
|
+
const FeedbackChip = _ref3 => {
|
|
120
|
+
let option = _ref3.option,
|
|
121
|
+
selected = _ref3.selected,
|
|
122
|
+
onToggle = _ref3.onToggle;
|
|
123
|
+
return /*#__PURE__*/React.createElement(StyledChip, {
|
|
124
|
+
type: "button",
|
|
125
|
+
$selected: selected,
|
|
126
|
+
"aria-pressed": selected,
|
|
127
|
+
onClick: () => onToggle(option.id)
|
|
128
|
+
}, /*#__PURE__*/React.createElement(Typography, {
|
|
129
|
+
variant: "Body 2",
|
|
130
|
+
as: "span"
|
|
131
|
+
}, option.label));
|
|
132
|
+
};
|
|
133
|
+
const StyledActions = styled.div.withConfig({
|
|
134
|
+
displayName: "MessageFeedback__StyledActions",
|
|
135
|
+
componentId: "sc-1i96o75-3"
|
|
136
|
+
})(["display:flex;gap:12px;"]);
|
|
137
|
+
|
|
138
|
+
/* ---- the component ------------------------------------------------- */
|
|
139
|
+
|
|
140
|
+
export const MessageFeedback = _ref4 => {
|
|
141
|
+
let _ref4$question = _ref4.question,
|
|
142
|
+
question = _ref4$question === void 0 ? "Was this helpful?" : _ref4$question,
|
|
143
|
+
_ref4$positiveTitle = _ref4.positiveTitle,
|
|
144
|
+
positiveTitle = _ref4$positiveTitle === void 0 ? "What was good?" : _ref4$positiveTitle,
|
|
145
|
+
_ref4$negativeTitle = _ref4.negativeTitle,
|
|
146
|
+
negativeTitle = _ref4$negativeTitle === void 0 ? "What went wrong?" : _ref4$negativeTitle,
|
|
147
|
+
_ref4$positiveOptions = _ref4.positiveOptions,
|
|
148
|
+
positiveOptions = _ref4$positiveOptions === void 0 ? POSITIVE_FEEDBACK_OPTIONS : _ref4$positiveOptions,
|
|
149
|
+
_ref4$negativeOptions = _ref4.negativeOptions,
|
|
150
|
+
negativeOptions = _ref4$negativeOptions === void 0 ? NEGATIVE_FEEDBACK_OPTIONS : _ref4$negativeOptions,
|
|
151
|
+
onVote = _ref4.onVote,
|
|
152
|
+
onSubmit = _ref4.onSubmit;
|
|
153
|
+
const _useState = useState(null),
|
|
154
|
+
sentiment = _useState[0],
|
|
155
|
+
setSentiment = _useState[1];
|
|
156
|
+
const _useState2 = useState("closed"),
|
|
157
|
+
stage = _useState2[0],
|
|
158
|
+
setStage = _useState2[1];
|
|
159
|
+
const _useState3 = useState([]),
|
|
160
|
+
selected = _useState3[0],
|
|
161
|
+
setSelected = _useState3[1];
|
|
162
|
+
const _useState4 = useState(""),
|
|
163
|
+
comment = _useState4[0],
|
|
164
|
+
setComment = _useState4[1];
|
|
165
|
+
const _useState5 = useState(false),
|
|
166
|
+
done = _useState5[0],
|
|
167
|
+
setDone = _useState5[1];
|
|
168
|
+
const vote = useCallback(value => {
|
|
169
|
+
setSentiment(value);
|
|
170
|
+
setSelected([]);
|
|
171
|
+
setComment("");
|
|
172
|
+
onVote == null || onVote(value);
|
|
173
|
+
setStage("details");
|
|
174
|
+
}, [onVote]);
|
|
175
|
+
const toggleOption = useCallback(id => {
|
|
176
|
+
setSelected(current => current.includes(id) ? current.filter(one => one !== id) : [...current, id]);
|
|
177
|
+
}, []);
|
|
178
|
+
const submit = useCallback(() => {
|
|
179
|
+
if (sentiment) {
|
|
180
|
+
onSubmit == null || onSubmit({
|
|
181
|
+
sentiment,
|
|
182
|
+
options: selected,
|
|
183
|
+
comment: comment.trim()
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
setStage("closed");
|
|
187
|
+
setDone(true);
|
|
188
|
+
}, [sentiment, selected, comment, onSubmit]);
|
|
189
|
+
|
|
190
|
+
/* Cancel keeps the vote (already sent on the thumb press) and closes. */
|
|
191
|
+
const finish = useCallback(() => {
|
|
192
|
+
setStage("closed");
|
|
193
|
+
setDone(true);
|
|
194
|
+
}, []);
|
|
195
|
+
const isPositive = sentiment === "up";
|
|
196
|
+
const options = isPositive ? positiveOptions : negativeOptions;
|
|
197
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(StyledPrompt, null, done ? /*#__PURE__*/React.createElement(Body2, {
|
|
198
|
+
color: "secondary"
|
|
199
|
+
}, "Thank you for the feedback!") : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Body2, {
|
|
200
|
+
color: "secondary",
|
|
201
|
+
className: "fb-question"
|
|
202
|
+
}, question), /*#__PURE__*/React.createElement(Tooltip, {
|
|
203
|
+
title: "Yes, helpful"
|
|
204
|
+
}, /*#__PURE__*/React.createElement(IconButton, {
|
|
205
|
+
type: "button",
|
|
206
|
+
variant: "text gray",
|
|
207
|
+
className: "fb-btn",
|
|
208
|
+
"aria-label": "Yes, helpful",
|
|
209
|
+
onClick: () => vote("up")
|
|
210
|
+
}, /*#__PURE__*/React.createElement(ThumbUpOutlineIcon, null))), /*#__PURE__*/React.createElement(Tooltip, {
|
|
211
|
+
title: "No, not helpful"
|
|
212
|
+
}, /*#__PURE__*/React.createElement(IconButton, {
|
|
213
|
+
type: "button",
|
|
214
|
+
variant: "text gray",
|
|
215
|
+
className: "fb-btn fb-down",
|
|
216
|
+
"aria-label": "No, not helpful",
|
|
217
|
+
onClick: () => vote("down")
|
|
218
|
+
}, /*#__PURE__*/React.createElement(ThumbUpOutlineIcon, null))))), /*#__PURE__*/React.createElement(Dialog, {
|
|
219
|
+
open: stage !== "closed",
|
|
220
|
+
onClose: finish,
|
|
221
|
+
enableBackgroundClick: true
|
|
222
|
+
}, stage === "details" ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Dialog.Title, null, isPositive ? positiveTitle : negativeTitle), /*#__PURE__*/React.createElement(Dialog.ContentDivider, null), /*#__PURE__*/React.createElement(Dialog.Content, null, /*#__PURE__*/React.createElement(StyledForm, null, /*#__PURE__*/React.createElement("div", {
|
|
223
|
+
className: "fb-field"
|
|
224
|
+
}, /*#__PURE__*/React.createElement(Label, null, "Select all that apply"), /*#__PURE__*/React.createElement("div", {
|
|
225
|
+
className: "fb-chips"
|
|
226
|
+
}, options.map(option => /*#__PURE__*/React.createElement(FeedbackChip, {
|
|
227
|
+
key: option.id,
|
|
228
|
+
option: option,
|
|
229
|
+
selected: selected.includes(option.id),
|
|
230
|
+
onToggle: toggleOption
|
|
231
|
+
})))), /*#__PURE__*/React.createElement("div", {
|
|
232
|
+
className: "fb-field"
|
|
233
|
+
}, /*#__PURE__*/React.createElement(Label, {
|
|
234
|
+
htmlFor: "fb-comment"
|
|
235
|
+
}, "Tell us more (optional)"), /*#__PURE__*/React.createElement(Textarea, {
|
|
236
|
+
id: "fb-comment",
|
|
237
|
+
value: comment,
|
|
238
|
+
placeholder: isPositive ? "What made this useful?" : "What would have helped?",
|
|
239
|
+
onChange: event => setComment(event.target.value)
|
|
240
|
+
})))), /*#__PURE__*/React.createElement(Dialog.ContentDivider, null), /*#__PURE__*/React.createElement(Dialog.Actions, null, /*#__PURE__*/React.createElement(StyledActions, null, /*#__PURE__*/React.createElement(Button, {
|
|
241
|
+
variant: "primary",
|
|
242
|
+
size: "small",
|
|
243
|
+
type: "button",
|
|
244
|
+
onClick: submit
|
|
245
|
+
}, "Submit"), /*#__PURE__*/React.createElement(Button, {
|
|
246
|
+
variant: "secondary",
|
|
247
|
+
size: "small",
|
|
248
|
+
type: "button",
|
|
249
|
+
onClick: finish
|
|
250
|
+
}, "Cancel")))) : null));
|
|
251
|
+
};
|
|
252
|
+
//# sourceMappingURL=MessageFeedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageFeedback.js","names":["React","useCallback","useState","styled","screenBelow","Button","Dialog","IconButton","ThumbUpOutlineIcon","Label","Textarea","Tooltip","Body2","Typography","POSITIVE_FEEDBACK_OPTIONS","id","label","NEGATIVE_FEEDBACK_OPTIONS","StyledPrompt","div","withConfig","displayName","componentId","sm","StyledForm","StyledChip","button","_ref","$selected","_ref2","FeedbackChip","_ref3","option","selected","onToggle","createElement","type","onClick","variant","as","StyledActions","MessageFeedback","_ref4","_ref4$question","question","_ref4$positiveTitle","positiveTitle","_ref4$negativeTitle","negativeTitle","_ref4$positiveOptions","positiveOptions","_ref4$negativeOptions","negativeOptions","onVote","onSubmit","_useState","sentiment","setSentiment","_useState2","stage","setStage","_useState3","setSelected","_useState4","comment","setComment","_useState5","done","setDone","vote","value","toggleOption","current","includes","filter","one","submit","options","trim","finish","isPositive","Fragment","color","className","title","open","onClose","enableBackgroundClick","Title","ContentDivider","Content","map","key","htmlFor","placeholder","onChange","event","target","Actions","size"],"sources":["../../../../src/presentation/shared/MessageFeedback.tsx"],"sourcesContent":["import React, { ReactElement, ReactNode, useCallback, useState } from \"react\";\n\nimport styled from \"styled-components\";\n\nimport { screenBelow } from \"../../components/BreakPoints\";\nimport { Button } from \"../../components/Button\";\nimport { Dialog } from \"../../components/Dialog\";\nimport { IconButton } from \"../../components/IconButton\";\nimport { ThumbUpOutlineIcon } from \"../../components/Icons\";\nimport { Label } from \"../../components/Label\";\nimport { Textarea } from \"../../components/Textarea\";\nimport { Tooltip } from \"../../components/Tooltip\";\nimport { Body2 } from \"../../components/Typography\";\nimport { Typography } from \"../../components/Typography/Typography\";\n\n/**\n * MessageFeedback — the reusable \"Was this helpful?\" flow, as a Presentation\n * mock.\n *\n * It is built only from design-system parts and owns the whole flow, so any\n * content surface (the message dialog, a help article, a settings panel) can\n * drop it in and get the same behaviour:\n *\n * 1. Prompt — an inline \"Was this helpful?\" row with a thumb up / down.\n * 2. Details — a centred dialog: option chips for the chosen sentiment,\n * plus an optional free-text comment. Submit or Cancel.\n * 3. Thank you — a short confirmation, then the prompt shows a quiet\n * \"Thanks for your feedback!\".\n *\n * The vote is recorded the moment a thumb is pressed (`onVote`), so a user who\n * cancels the details step still leaves a valid vote — the same contract the\n * trial Feedback flow uses. The details are reported on Submit (`onSubmit`).\n *\n * In the product this lives in application code; the presentation ships this\n * demo copy so the stories need no app imports.\n */\n\n/* ---- public types & default option catalogs ------------------------ */\n\nexport type FeedbackSentiment = \"up\" | \"down\";\n\nexport interface FeedbackOption {\n /** Stable id sent with the feedback record; not shown to the user. */\n id: string;\n /** The chip label the user sees. */\n label: string;\n}\n\nexport interface MessageFeedbackResult {\n sentiment: FeedbackSentiment;\n /** The ids of the chosen option chips. */\n options: string[];\n /** The free-text comment, trimmed; \"\" when left blank. */\n comment: string;\n}\n\n/* The options rate the one piece of content, not the whole product. Each\n negative option maps to an action a writer can take. Override per surface\n through the component props. */\nexport const POSITIVE_FEEDBACK_OPTIONS: FeedbackOption[] = [\n { id: \"clear\", label: \"Clear and easy to understand\" },\n { id: \"answered\", label: \"Answered my question\" },\n { id: \"right-detail\", label: \"Right amount of detail\" },\n { id: \"helped-use\", label: \"Helped me use the feature\" },\n { id: \"good-examples\", label: \"Good examples or screenshots\" },\n { id: \"other\", label: \"Other\" },\n];\n\nexport const NEGATIVE_FEEDBACK_OPTIONS: FeedbackOption[] = [\n { id: \"not-answered\", label: \"Did not answer my question\" },\n { id: \"confusing\", label: \"Confusing or hard to follow\" },\n { id: \"too-long\", label: \"Too long\" },\n { id: \"not-enough-detail\", label: \"Not enough detail\" },\n { id: \"out-of-date\", label: \"Out of date or incorrect\" },\n { id: \"wrong-plan-role\", label: \"Wrong for my plan or role\" },\n { id: \"not-found\", label: \"I could not find what I needed\" },\n { id: \"broken\", label: \"Something is broken\" },\n { id: \"other\", label: \"Other\" },\n];\n\n/* ---- the inline prompt row ----------------------------------------- */\n\n/* Down-vote reuses the up icon, rotated (the DS set only ships\n ThumbUpOutlineIcon). */\nconst StyledPrompt = styled.div`\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 8px 24px 28px;\n\n ${screenBelow.sm} {\n padding: 4px 24px 24px;\n }\n\n .fb-question {\n margin-right: 4px;\n }\n\n .fb-btn svg {\n width: 18px;\n height: 18px;\n }\n\n .fb-down svg {\n transform: rotate(180deg);\n }\n`;\n\n/* ---- the details dialog -------------------------------------------- */\n\nconst StyledForm = styled.div`\n display: flex;\n flex-direction: column;\n gap: 20px;\n\n .fb-field {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .fb-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n }\n\n /* DS Textarea is a fixed 360px; let it fill the dialog width instead. */\n .c-textarea {\n width: 100%;\n box-sizing: border-box;\n }\n`;\n\n/* The option chip — a pill that toggles to the theme primary tint when\n selected. Translated from the trial Feedback chip into styled-components\n (runtime tw- classes do not work in the presentation storybook). The label\n text is a DS Body2 so it carries the correct font. */\nconst StyledChip = styled.button<{ $selected: boolean }>`\n display: inline-flex;\n align-items: center;\n box-sizing: border-box;\n margin: 0;\n padding: 3px 8px 4px;\n border: 1px solid\n ${({ $selected }) =>\n $selected ? \"var(--color-primary)\" : \"var(--color-theme-500)\"};\n border-radius: 4px;\n background-color: ${({ $selected }) =>\n $selected ? \"var(--color-primary-300)\" : \"transparent\"};\n cursor: pointer;\n transition: border-color 120ms ease, background-color 120ms ease;\n\n &:hover {\n border-color: var(--color-primary);\n }\n`;\n\nconst FeedbackChip = ({\n option,\n selected,\n onToggle,\n}: {\n option: FeedbackOption;\n selected: boolean;\n onToggle: (id: string) => void;\n}): ReactElement => (\n <StyledChip\n type=\"button\"\n $selected={selected}\n aria-pressed={selected}\n onClick={() => onToggle(option.id)}\n >\n <Typography variant=\"Body 2\" as=\"span\">\n {option.label}\n </Typography>\n </StyledChip>\n);\n\nconst StyledActions = styled.div`\n display: flex;\n gap: 12px;\n`;\n\n/* ---- the component ------------------------------------------------- */\n\ntype DialogStage = \"closed\" | \"details\";\n\nexport interface MessageFeedbackProps {\n /** The inline prompt copy. */\n question?: ReactNode;\n /** Details-dialog title for a thumb up / down. */\n positiveTitle?: string;\n negativeTitle?: string;\n /** Option catalogs; default to the content-feedback sets above. */\n positiveOptions?: FeedbackOption[];\n negativeOptions?: FeedbackOption[];\n /** Fired the moment a thumb is pressed (the vote itself). */\n onVote?: (sentiment: FeedbackSentiment) => void;\n /** Fired on Submit, with the chosen options and comment. */\n onSubmit?: (result: MessageFeedbackResult) => void;\n}\n\nexport const MessageFeedback = ({\n question = \"Was this helpful?\",\n positiveTitle = \"What was good?\",\n negativeTitle = \"What went wrong?\",\n positiveOptions = POSITIVE_FEEDBACK_OPTIONS,\n negativeOptions = NEGATIVE_FEEDBACK_OPTIONS,\n onVote,\n onSubmit,\n}: MessageFeedbackProps): ReactElement => {\n const [sentiment, setSentiment] = useState<FeedbackSentiment | null>(null);\n const [stage, setStage] = useState<DialogStage>(\"closed\");\n const [selected, setSelected] = useState<string[]>([]);\n const [comment, setComment] = useState(\"\");\n const [done, setDone] = useState(false);\n\n const vote = useCallback(\n (value: FeedbackSentiment) => {\n setSentiment(value);\n setSelected([]);\n setComment(\"\");\n onVote?.(value);\n setStage(\"details\");\n },\n [onVote]\n );\n\n const toggleOption = useCallback((id: string) => {\n setSelected((current) =>\n current.includes(id)\n ? current.filter((one) => one !== id)\n : [...current, id]\n );\n }, []);\n\n const submit = useCallback(() => {\n if (sentiment) {\n onSubmit?.({ sentiment, options: selected, comment: comment.trim() });\n }\n setStage(\"closed\");\n setDone(true);\n }, [sentiment, selected, comment, onSubmit]);\n\n /* Cancel keeps the vote (already sent on the thumb press) and closes. */\n const finish = useCallback(() => {\n setStage(\"closed\");\n setDone(true);\n }, []);\n\n const isPositive = sentiment === \"up\";\n const options = isPositive ? positiveOptions : negativeOptions;\n\n return (\n <>\n <StyledPrompt>\n {done ? (\n <Body2 color=\"secondary\">Thank you for the feedback!</Body2>\n ) : (\n <>\n <Body2 color=\"secondary\" className=\"fb-question\">\n {question}\n </Body2>\n <Tooltip title=\"Yes, helpful\">\n <IconButton\n type=\"button\"\n variant=\"text gray\"\n className=\"fb-btn\"\n aria-label=\"Yes, helpful\"\n onClick={() => vote(\"up\")}\n >\n <ThumbUpOutlineIcon />\n </IconButton>\n </Tooltip>\n <Tooltip title=\"No, not helpful\">\n <IconButton\n type=\"button\"\n variant=\"text gray\"\n className=\"fb-btn fb-down\"\n aria-label=\"No, not helpful\"\n onClick={() => vote(\"down\")}\n >\n <ThumbUpOutlineIcon />\n </IconButton>\n </Tooltip>\n </>\n )}\n </StyledPrompt>\n\n <Dialog open={stage !== \"closed\"} onClose={finish} enableBackgroundClick>\n {stage === \"details\" ? (\n <>\n <Dialog.Title>\n {isPositive ? positiveTitle : negativeTitle}\n </Dialog.Title>\n <Dialog.ContentDivider />\n <Dialog.Content>\n <StyledForm>\n <div className=\"fb-field\">\n <Label>Select all that apply</Label>\n <div className=\"fb-chips\">\n {options.map((option) => (\n <FeedbackChip\n key={option.id}\n option={option}\n selected={selected.includes(option.id)}\n onToggle={toggleOption}\n />\n ))}\n </div>\n </div>\n <div className=\"fb-field\">\n <Label htmlFor=\"fb-comment\">Tell us more (optional)</Label>\n <Textarea\n id=\"fb-comment\"\n value={comment}\n placeholder={\n isPositive\n ? \"What made this useful?\"\n : \"What would have helped?\"\n }\n onChange={(event) => setComment(event.target.value)}\n />\n </div>\n </StyledForm>\n </Dialog.Content>\n <Dialog.ContentDivider />\n <Dialog.Actions>\n <StyledActions>\n <Button\n variant=\"primary\"\n size=\"small\"\n type=\"button\"\n onClick={submit}\n >\n Submit\n </Button>\n <Button\n variant=\"secondary\"\n size=\"small\"\n type=\"button\"\n onClick={finish}\n >\n Cancel\n </Button>\n </StyledActions>\n </Dialog.Actions>\n </>\n ) : null}\n </Dialog>\n </>\n );\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
export interface PortfolioOptionsMenuProps {
|
|
3
|
+
/** Class on the TreeDots trigger (parent controls its hover visibility). */
|
|
4
|
+
targetClassName?: string;
|
|
5
|
+
/** IconButton size of the trigger (omit for the default/regular size). */
|
|
6
|
+
size?: "small" | "big";
|
|
7
|
+
}
|
|
8
|
+
export declare const PortfolioOptionsMenu: {
|
|
9
|
+
({ targetClassName, size, }: PortfolioOptionsMenuProps): ReactElement;
|
|
10
|
+
displayName: string;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=PortfolioOptionsMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortfolioOptionsMenu.d.ts","sourceRoot":"","sources":["../../../../src/presentation/shared/PortfolioOptionsMenu.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,YAAY,EAAyB,MAAM,OAAO,CAAC;AA4BnE,MAAM,WAAW,yBAAyB;IACxC,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CACxB;AAED,eAAO,MAAM,oBAAoB;iCAG9B,yBAAyB,GAAG,YAAY;;CA8C1C,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import styled from "styled-components";
|
|
3
|
+
import { IconButton } from "../../components/IconButton";
|
|
4
|
+
import { ArchiveIcon, EditIcon, TrashIcon, TreeDotsIcon } from "../../components/Icons";
|
|
5
|
+
import { List, ListItem, ListSeparator } from "../../components/List";
|
|
6
|
+
import { Menu } from "../../components/Menu";
|
|
7
|
+
|
|
8
|
+
/* Portfolio three-dots menu — Edit / Archive / Delete, per the Portfolios
|
|
9
|
+
spec (notebook 1998, note 01). Shared by the Portfolios list rows and the
|
|
10
|
+
portfolio dashboard header. Clicks are intentionally no-ops. */
|
|
11
|
+
const StyledPortfolioMenu = styled.div.withConfig({
|
|
12
|
+
displayName: "PortfolioOptionsMenu__StyledPortfolioMenu",
|
|
13
|
+
componentId: "sc-i253fh-0"
|
|
14
|
+
})([".danger{color:var(--red-alert);}.danger svg{fill:var(--red-alert);}"]);
|
|
15
|
+
export const PortfolioOptionsMenu = _ref => {
|
|
16
|
+
let targetClassName = _ref.targetClassName,
|
|
17
|
+
size = _ref.size;
|
|
18
|
+
const _useState = useState(false),
|
|
19
|
+
open = _useState[0],
|
|
20
|
+
setOpen = _useState[1];
|
|
21
|
+
const handleOpen = useCallback(() => setOpen(true), []);
|
|
22
|
+
const handleClose = useCallback(() => setOpen(false), []);
|
|
23
|
+
const triggerClassName = [targetClassName, open ? "open" : ""].filter(Boolean).join(" ") || undefined;
|
|
24
|
+
return /*#__PURE__*/React.createElement(Menu, {
|
|
25
|
+
open: open,
|
|
26
|
+
onOpen: handleOpen,
|
|
27
|
+
onClose: handleClose,
|
|
28
|
+
position: "bottom-end",
|
|
29
|
+
target: /*#__PURE__*/React.createElement(IconButton, {
|
|
30
|
+
type: "button",
|
|
31
|
+
variant: "text gray",
|
|
32
|
+
size: size,
|
|
33
|
+
className: triggerClassName,
|
|
34
|
+
active: open
|
|
35
|
+
}, /*#__PURE__*/React.createElement(TreeDotsIcon, null))
|
|
36
|
+
}, /*#__PURE__*/React.createElement(StyledPortfolioMenu, null, /*#__PURE__*/React.createElement(List, {
|
|
37
|
+
style: {
|
|
38
|
+
padding: "8px 0"
|
|
39
|
+
},
|
|
40
|
+
tabIndex: -1
|
|
41
|
+
}, /*#__PURE__*/React.createElement(ListItem, null, /*#__PURE__*/React.createElement(EditIcon, null), "Edit"), /*#__PURE__*/React.createElement(ListItem, null, /*#__PURE__*/React.createElement(ArchiveIcon, null), "Archive"), /*#__PURE__*/React.createElement(ListSeparator, null), /*#__PURE__*/React.createElement(ListItem, {
|
|
42
|
+
className: "danger"
|
|
43
|
+
}, /*#__PURE__*/React.createElement(TrashIcon, null), "Delete"))));
|
|
44
|
+
};
|
|
45
|
+
PortfolioOptionsMenu.displayName = "PortfolioOptionsMenu";
|
|
46
|
+
//# sourceMappingURL=PortfolioOptionsMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PortfolioOptionsMenu.js","names":["React","useCallback","useState","styled","IconButton","ArchiveIcon","EditIcon","TrashIcon","TreeDotsIcon","List","ListItem","ListSeparator","Menu","StyledPortfolioMenu","div","withConfig","displayName","componentId","PortfolioOptionsMenu","_ref","targetClassName","size","_useState","open","setOpen","handleOpen","handleClose","triggerClassName","filter","Boolean","join","undefined","createElement","onOpen","onClose","position","target","type","variant","className","active","style","padding","tabIndex"],"sources":["../../../../src/presentation/shared/PortfolioOptionsMenu.tsx"],"sourcesContent":["import React, { ReactElement, useCallback, useState } from \"react\";\n\nimport styled from \"styled-components\";\n\nimport { IconButton } from \"../../components/IconButton\";\nimport {\n ArchiveIcon,\n EditIcon,\n TrashIcon,\n TreeDotsIcon,\n} from \"../../components/Icons\";\nimport { List, ListItem, ListSeparator } from \"../../components/List\";\nimport { Menu } from \"../../components/Menu\";\n\n/* Portfolio three-dots menu — Edit / Archive / Delete, per the Portfolios\n spec (notebook 1998, note 01). Shared by the Portfolios list rows and the\n portfolio dashboard header. Clicks are intentionally no-ops. */\nconst StyledPortfolioMenu = styled.div`\n /* Delete is a destructive action → red text + icon */\n .danger {\n color: var(--red-alert);\n }\n\n .danger svg {\n fill: var(--red-alert);\n }\n`;\n\nexport interface PortfolioOptionsMenuProps {\n /** Class on the TreeDots trigger (parent controls its hover visibility). */\n targetClassName?: string;\n /** IconButton size of the trigger (omit for the default/regular size). */\n size?: \"small\" | \"big\";\n}\n\nexport const PortfolioOptionsMenu = ({\n targetClassName,\n size,\n}: PortfolioOptionsMenuProps): ReactElement => {\n const [open, setOpen] = useState(false);\n const handleOpen = useCallback(() => setOpen(true), []);\n const handleClose = useCallback(() => setOpen(false), []);\n\n const triggerClassName =\n [targetClassName, open ? \"open\" : \"\"].filter(Boolean).join(\" \") ||\n undefined;\n\n return (\n <Menu\n open={open}\n onOpen={handleOpen}\n onClose={handleClose}\n position=\"bottom-end\"\n target={\n <IconButton\n type=\"button\"\n variant=\"text gray\"\n size={size}\n className={triggerClassName}\n active={open}\n >\n <TreeDotsIcon />\n </IconButton>\n }\n >\n <StyledPortfolioMenu>\n <List style={{ padding: \"8px 0\" }} tabIndex={-1}>\n <ListItem>\n <EditIcon />\n Edit\n </ListItem>\n <ListItem>\n <ArchiveIcon />\n Archive\n </ListItem>\n <ListSeparator />\n <ListItem className=\"danger\">\n <TrashIcon />\n Delete\n </ListItem>\n </List>\n </StyledPortfolioMenu>\n </Menu>\n );\n};\n\nPortfolioOptionsMenu.displayName = \"PortfolioOptionsMenu\";\n"],"mappings":"AAAA,OAAOA,KAAK,IAAkBC,WAAW,EAAEC,QAAQ,QAAQ,OAAO;AAElE,OAAOC,MAAM,MAAM,mBAAmB;AAEtC,SAASC,UAAU,QAAQ,6BAA6B;AACxD,SACEC,WAAW,EACXC,QAAQ,EACRC,SAAS,EACTC,YAAY,QACP,wBAAwB;AAC/B,SAASC,IAAI,EAAEC,QAAQ,EAAEC,aAAa,QAAQ,uBAAuB;AACrE,SAASC,IAAI,QAAQ,uBAAuB;;AAE5C;AACA;AACA;AACA,MAAMC,mBAAmB,GAAGV,MAAM,CAACW,GAAG,CAAAC,UAAA;EAAAC,WAAA;EAAAC,WAAA;AAAA,2EASrC;AASD,OAAO,MAAMC,oBAAoB,GAAGC,IAAA,IAGW;EAAA,IAF7CC,eAAe,GAAAD,IAAA,CAAfC,eAAe;IACfC,IAAI,GAAAF,IAAA,CAAJE,IAAI;EAEJ,MAAAC,SAAA,GAAwBpB,QAAQ,CAAC,KAAK,CAAC;IAAhCqB,IAAI,GAAAD,SAAA;IAAEE,OAAO,GAAAF,SAAA;EACpB,MAAMG,UAAU,GAAGxB,WAAW,CAAC,MAAMuB,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;EACvD,MAAME,WAAW,GAAGzB,WAAW,CAAC,MAAMuB,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;EAEzD,MAAMG,gBAAgB,GACpB,CAACP,eAAe,EAAEG,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,CAACK,MAAM,CAACC,OAAO,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC,IAC/DC,SAAS;EAEX,oBACE/B,KAAA,CAAAgC,aAAA,CAACpB,IAAI;IACHW,IAAI,EAAEA,IAAK;IACXU,MAAM,EAAER,UAAW;IACnBS,OAAO,EAAER,WAAY;IACrBS,QAAQ,EAAC,YAAY;IACrBC,MAAM,eACJpC,KAAA,CAAAgC,aAAA,CAAC5B,UAAU;MACTiC,IAAI,EAAC,QAAQ;MACbC,OAAO,EAAC,WAAW;MACnBjB,IAAI,EAAEA,IAAK;MACXkB,SAAS,EAAEZ,gBAAiB;MAC5Ba,MAAM,EAAEjB;IAAK,gBAEbvB,KAAA,CAAAgC,aAAA,CAACxB,YAAY,MAAE,CACL;EACb,gBAEDR,KAAA,CAAAgC,aAAA,CAACnB,mBAAmB,qBAClBb,KAAA,CAAAgC,aAAA,CAACvB,IAAI;IAACgC,KAAK,EAAE;MAAEC,OAAO,EAAE;IAAQ,CAAE;IAACC,QAAQ,EAAE,CAAC;EAAE,gBAC9C3C,KAAA,CAAAgC,aAAA,CAACtB,QAAQ,qBACPV,KAAA,CAAAgC,aAAA,CAAC1B,QAAQ,MAAE,CAAC,QAEJ,CAAC,eACXN,KAAA,CAAAgC,aAAA,CAACtB,QAAQ,qBACPV,KAAA,CAAAgC,aAAA,CAAC3B,WAAW,MAAE,CAAC,WAEP,CAAC,eACXL,KAAA,CAAAgC,aAAA,CAACrB,aAAa,MAAE,CAAC,eACjBX,KAAA,CAAAgC,aAAA,CAACtB,QAAQ;IAAC6B,SAAS,EAAC;EAAQ,gBAC1BvC,KAAA,CAAAgC,aAAA,CAACzB,SAAS,MAAE,CAAC,UAEL,CACN,CACa,CACjB,CAAC;AAEX,CAAC;AAEDW,oBAAoB,CAACF,WAAW,GAAG,sBAAsB","ignoreList":[]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
import { Repository } from "./ConnectRepositoryDialog";
|
|
3
|
+
export interface ProjectOptionsMenuProps {
|
|
4
|
+
/** When the `source` module is loaded, show the Repositories section. */
|
|
5
|
+
sourceModuleLoaded?: boolean;
|
|
6
|
+
/** Repositories already connected to the project when the menu first mounts. */
|
|
7
|
+
initialConnectedRepositories?: Repository[];
|
|
8
|
+
/**
|
|
9
|
+
* Whether the current user can manage the project's repositories. Gates the
|
|
10
|
+
* Webhook Logs entry (a manage-only action) — see note 09, Permissions.
|
|
11
|
+
*/
|
|
12
|
+
canManageProjectRepositories?: boolean;
|
|
13
|
+
/** Extra class on the TreeDots trigger (e.g. for hover-reveal in a listing). */
|
|
14
|
+
className?: string;
|
|
15
|
+
/** IconButton size of the trigger (omit for the default/regular size). */
|
|
16
|
+
size?: "small" | "big";
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The project "..." options menu. When the `source` module is loaded it gains
|
|
20
|
+
* a **Repositories** item that, instead of flying out to the side, **drills in
|
|
21
|
+
* within the same menu** (the pattern used across the app — see `Filter`): the
|
|
22
|
+
* index slides out to the left and the Repositories view (a back-arrow header,
|
|
23
|
+
* the connected repositories sorted by name, and a **Connect Repository**
|
|
24
|
+
* action) slides in. Connecting adds a repository to the list; clicking one
|
|
25
|
+
* would, in production, open it in a new tab (surfaced here via a dialog).
|
|
26
|
+
*/
|
|
27
|
+
export declare const ProjectOptionsMenu: {
|
|
28
|
+
({ sourceModuleLoaded, initialConnectedRepositories, canManageProjectRepositories, className, size, }: ProjectOptionsMenuProps): ReactElement;
|
|
29
|
+
displayName: string;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=ProjectOptionsMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectOptionsMenu.d.ts","sourceRoot":"","sources":["../../../../src/presentation/shared/ProjectOptionsMenu.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAe,YAAY,EAAyB,MAAM,OAAO,CAAC;AAIhF,OAAO,EAGL,UAAU,EACX,MAAM,2BAA2B,CAAC;AAqLnC,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gFAAgF;IAChF,4BAA4B,CAAC,EAAE,UAAU,EAAE,CAAC;IAC5C;;;OAGG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;2GAM5B,uBAAuB,GAAG,YAAY;;CAiTxC,CAAC"}
|