@optiaxiom/proteus 0.1.13 → 0.1.14

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.
@@ -1,5 +1,5 @@
1
- @layer optiaxiom._14psyja;
2
- @layer optiaxiom._14psyja {
1
+ @layer optiaxiom._13rw4yo;
2
+ @layer optiaxiom._13rw4yo {
3
3
  .ProteusChart__jmlqij1 {
4
4
  border-radius: 16px;
5
5
  }
@@ -1,5 +1,5 @@
1
- @layer optiaxiom._14psyja;
2
- @layer optiaxiom._14psyja {
1
+ @layer optiaxiom._13rw4yo;
2
+ @layer optiaxiom._13rw4yo {
3
3
  .ProteusChartTooltipContent__1gsvq810 {
4
4
  min-width: 128px;
5
5
  }
@@ -0,0 +1,36 @@
1
+ @layer optiaxiom._13rw4yo;
2
+ @layer optiaxiom._13rw4yo {
3
+ .ProteusQuestion__8f590p0 {
4
+ outline: none;
5
+ }
6
+ .ProteusQuestion__8f590p2 {
7
+ background-color: transparent;
8
+ border-color: var(--ax-colors-border-secondary);
9
+ border-inline-color: transparent;
10
+ border-inline-width: 6px;
11
+ cursor: pointer;
12
+ user-select: none;
13
+ }
14
+ .ProteusQuestion__8f590p2:focus-visible {
15
+ outline: 2px solid var(--ax-colors-border-focus);
16
+ outline-offset: 1px;
17
+ z-index: 1;
18
+ }
19
+ .ProteusQuestion__8f590p2[data-disabled] {
20
+ cursor: default;
21
+ opacity: 0.5;
22
+ pointer-events: none;
23
+ }
24
+ .ProteusQuestion__8f590p2:has(+ .ProteusQuestion__8f590p1:hover, + .ProteusQuestion__8f590p1[data-selected], + .ProteusQuestion__8f590p1 [contenteditable]:focus) {
25
+ border-bottom-color: transparent;
26
+ }
27
+ .ProteusQuestion__8f590p3 {
28
+ background-color: var(--ax-colors-bg-avatar-neutral);
29
+ }
30
+ @media (hover: hover) {
31
+ .ProteusQuestion__8f590p2:hover, .ProteusQuestion__8f590p2[data-selected], .ProteusQuestion__8f590p2:has([contenteditable]:focus) {
32
+ background-color: var(--ax-colors-bg-page);
33
+ border-color: var(--ax-colors-bg-page);
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { withIcon } from './withIcon.js';
4
+
5
+ const IconX = withIcon(
6
+ {
7
+ name: "IconX"
8
+ },
9
+ /* @__PURE__ */ jsx(
10
+ "path",
11
+ {
12
+ clipRule: "evenodd",
13
+ d: "M14.8669 3.77596C15.0444 3.59845 15.0444 3.31064 14.8669 3.13313C14.6894 2.95562 14.4016 2.95562 14.224 3.13313L10 7.35718L5.77596 3.13313C5.59845 2.95562 5.31064 2.95562 5.13313 3.13313C4.95562 3.31064 4.95562 3.59845 5.13313 3.77596L9.35718 8L5.13313 12.224C4.95562 12.4016 4.95562 12.6894 5.13313 12.8669C5.31064 13.0444 5.59845 13.0444 5.77596 12.8669L10 8.64282L14.224 12.8669C14.4016 13.0444 14.6894 13.0444 14.8669 12.8669C15.0444 12.6894 15.0444 12.4016 14.8669 12.224L10.6428 8L14.8669 3.77596Z",
14
+ fill: "currentColor",
15
+ fillRule: "evenodd"
16
+ }
17
+ )
18
+ );
19
+
20
+ export { IconX };
@@ -1,4 +1,4 @@
1
- import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-D33V0Us-.css';
1
+ import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-DMK3zTMn.css';
2
2
  import { recipe } from '@optiaxiom/react/css-runtime';
3
3
 
4
4
  var chart = recipe({base:[{border:'1',borderColor:'border.tertiary',fontSize:'sm',p:'16'},'ProteusChart__jmlqij1','ProteusChart__jmlqij0']});
@@ -1,4 +1,4 @@
1
- import './../assets/src/proteus-chart/ProteusChartTooltipContent.css.ts.vanilla-61AtcZEK.css';
1
+ import './../assets/src/proteus-chart/ProteusChartTooltipContent.css.ts.vanilla-BNYSJiKc.css';
2
2
  import { recipe } from '@optiaxiom/react/css-runtime';
3
3
 
4
4
  var tooltip = recipe({base:[{bg:'bg.default',border:'1',borderColor:'border.secondary',display:'grid',fontSize:'sm',gap:'6',px:'8',py:'10',rounded:'lg',shadow:'lg'},'ProteusChartTooltipContent__1gsvq810']});
@@ -0,0 +1,9 @@
1
+ import './../assets/src/proteus-question/ProteusQuestion.css.ts.vanilla-vsj__kKE.css';
2
+ import { recipe } from '@optiaxiom/react/css-runtime';
3
+
4
+ var addon = recipe({base:[{display:'grid',fontWeight:'500',placeItems:'center',rounded:'lg',size:'md',transition:'colors'},'ProteusQuestion__8f590p3'],variants:{cursor:{pointer:{cursor:'pointer'}}}});
5
+ var choice = recipe({base:[{color:'fg.default',flexDirection:'column',fontSize:'md',gap:'8',px:'10',py:'16',rounded:'lg',transition:'colors'},'ProteusQuestion__8f590p2','ProteusQuestion__8f590p1'],variants:{cursor:{pointer:{borderB:'1'},text:{cursor:'text'}}}});
6
+ var choiceGroup = recipe({base:{flexDirection:'column'}});
7
+ var question = recipe({base:[{flexDirection:'column',gap:'16',p:'4'},'ProteusQuestion__8f590p0']});
8
+
9
+ export { addon, choice, choiceGroup, question };
@@ -1,11 +1,16 @@
1
1
  "use client";
2
- import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { Group, Button, Text } from '@optiaxiom/react';
4
- import { useState, useRef, useEffect } from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { IconNorth } from '@optiaxiom/icons';
4
+ import { Group, Text, Button, Box, Checkbox } from '@optiaxiom/react';
5
+ import { InlineInput } from '@optiaxiom/react/unstable';
6
+ import * as RovingFocus from '@radix-ui/react-roving-focus';
7
+ import { useState, useCallback, useRef, useEffect } from 'react';
5
8
  import { IconAngleLeft } from '../icons/IconAngleLeft.js';
6
9
  import { IconAngleRight } from '../icons/IconAngleRight.js';
10
+ import { IconPencil } from '../icons/IconPencil.js';
11
+ import { IconX } from '../icons/IconX.js';
7
12
  import { useProteusDocumentContext } from '../proteus-document/ProteusDocumentContext.js';
8
- import { ProteusQuestionItem } from './ProteusQuestionItem.js';
13
+ import { question, choiceGroup, choice, addon } from './ProteusQuestion-css.js';
9
14
 
10
15
  function ProteusQuestion({ questions }) {
11
16
  const { onEvent } = useProteusDocumentContext(
@@ -13,8 +18,13 @@ function ProteusQuestion({ questions }) {
13
18
  );
14
19
  const [answers, setAnswers] = useState([]);
15
20
  const [currentIndex, setCurrentIndex] = useState(0);
16
- const answer = answers[currentIndex];
17
- const valid = Array.isArray(answer) && answer.length > 0 && answer.every(Boolean);
21
+ const value = answers[currentIndex];
22
+ const valid = Array.isArray(value) && value.length > 0 && value.every(Boolean);
23
+ const onDismiss = useCallback(() => {
24
+ void onEvent({
25
+ message: "[User declined to answer the question]"
26
+ });
27
+ }, [onEvent]);
18
28
  const questionRef = useRef(null);
19
29
  const lastIndexRef = useRef(currentIndex);
20
30
  useEffect(() => {
@@ -35,102 +45,319 @@ function ProteusQuestion({ questions }) {
35
45
  }
36
46
  );
37
47
  lastIndexRef.current = currentIndex;
48
+ const item = questionRef.current?.querySelector("[data-selected]") ?? questionRef.current?.querySelector("[tabindex]");
49
+ item?.focus();
38
50
  }
39
51
  }, [currentIndex]);
52
+ const otherInputRef = useRef(null);
53
+ const otherItemRef = useRef(null);
40
54
  if (currentIndex >= questions.length) {
41
55
  return null;
42
56
  }
43
- const current = questions[currentIndex];
57
+ const { options, question: question$1, type } = questions[currentIndex];
44
58
  const isLast = currentIndex === questions.length - 1;
59
+ const otherValue = value?.find((v) => !options.includes(v));
60
+ const otherChecked = otherValue !== void 0;
45
61
  const onComplete = () => onEvent({
46
62
  message: answers.map(
47
- (value, index) => `Q: ${questions[index].question}
48
- A: ${value || "[Not specified]"}`
63
+ (value2, index) => `Q: ${questions[index].question}
64
+ A: ${value2 || "[Not specified]"}`
49
65
  ).join("\n\n")
50
66
  });
51
- return /* @__PURE__ */ jsxs(Group, { flexDirection: "column", gap: "16", children: [
52
- /* @__PURE__ */ jsx(
53
- ProteusQuestionItem,
54
- {
55
- addonAfter: questions.length > 1 && /* @__PURE__ */ jsxs(Group, { gap: "6", children: [
56
- /* @__PURE__ */ jsx(
57
- Button,
58
- {
59
- "aria-label": "Previous",
60
- disabled: currentIndex === 0,
61
- icon: /* @__PURE__ */ jsx(IconAngleLeft, {}),
62
- onClick: (event) => {
63
- event.preventDefault();
64
- setCurrentIndex((i) => i - 1);
65
- },
66
- size: "sm"
67
- }
68
- ),
69
- /* @__PURE__ */ jsxs(Text, { color: "fg.tertiary", fontSize: "sm", children: [
70
- currentIndex + 1,
71
- " of ",
72
- questions.length
67
+ const onValueChange = (value2) => {
68
+ answers[currentIndex] = value2;
69
+ setAnswers([...answers]);
70
+ };
71
+ const onSkip = () => {
72
+ answers[currentIndex] = null;
73
+ setAnswers([...answers]);
74
+ if (isLast) {
75
+ void onComplete();
76
+ } else {
77
+ setCurrentIndex((i) => i + 1);
78
+ }
79
+ };
80
+ const onSubmit = () => {
81
+ if (isLast) {
82
+ void onComplete();
83
+ } else {
84
+ setCurrentIndex((i) => i + 1);
85
+ }
86
+ };
87
+ return /* @__PURE__ */ jsxs(
88
+ Group,
89
+ {
90
+ onKeyDown: (event) => {
91
+ if (event.key === "Escape") {
92
+ event.preventDefault();
93
+ if (valid) {
94
+ onValueChange(null);
95
+ } else {
96
+ onSkip();
97
+ }
98
+ }
99
+ if ((event.key === "ArrowLeft" || event.key === "ArrowRight") && !(event.target instanceof HTMLInputElement) && !event.target.isContentEditable) {
100
+ event.preventDefault();
101
+ if (event.key === "ArrowLeft" && currentIndex > 0) {
102
+ setCurrentIndex((i) => i - 1);
103
+ } else if (event.key === "ArrowRight" && !isLast) {
104
+ setCurrentIndex((i) => i + 1);
105
+ }
106
+ }
107
+ },
108
+ tabIndex: -1,
109
+ ...question(),
110
+ children: [
111
+ /* @__PURE__ */ jsxs(Group, { flexDirection: "column", gap: "16", children: [
112
+ /* @__PURE__ */ jsxs(Group, { pl: "16", children: [
113
+ /* @__PURE__ */ jsx(Text, { flex: "1", fontWeight: "500", children: question$1 }),
114
+ /* @__PURE__ */ jsxs(Group, { gap: "6", children: [
115
+ questions.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
116
+ /* @__PURE__ */ jsx(
117
+ Button,
118
+ {
119
+ appearance: "subtle",
120
+ "aria-label": "Previous",
121
+ disabled: currentIndex === 0,
122
+ icon: /* @__PURE__ */ jsx(IconAngleLeft, {}),
123
+ onClick: (event) => {
124
+ event.preventDefault();
125
+ setCurrentIndex((i) => i - 1);
126
+ },
127
+ size: "sm"
128
+ }
129
+ ),
130
+ /* @__PURE__ */ jsxs(Text, { color: "fg.tertiary", fontSize: "sm", children: [
131
+ currentIndex + 1,
132
+ " of ",
133
+ questions.length
134
+ ] }),
135
+ /* @__PURE__ */ jsx(
136
+ Button,
137
+ {
138
+ appearance: "subtle",
139
+ "aria-label": "Next",
140
+ disabled: isLast,
141
+ icon: /* @__PURE__ */ jsx(IconAngleRight, {}),
142
+ onClick: (event) => {
143
+ event.preventDefault();
144
+ setCurrentIndex((i) => i + 1);
145
+ },
146
+ size: "sm"
147
+ }
148
+ )
149
+ ] }),
150
+ /* @__PURE__ */ jsx(
151
+ Button,
152
+ {
153
+ appearance: "subtle",
154
+ "aria-label": "Dismiss",
155
+ icon: /* @__PURE__ */ jsx(IconX, {}),
156
+ onClick: (event) => {
157
+ event.preventDefault();
158
+ onDismiss();
159
+ },
160
+ size: "sm"
161
+ }
162
+ )
163
+ ] })
73
164
  ] }),
74
- /* @__PURE__ */ jsx(
75
- Button,
165
+ /* @__PURE__ */ jsx(RovingFocus.Root, { asChild: true, loop: true, orientation: "vertical", ref: questionRef, children: /* @__PURE__ */ jsxs(
166
+ Group,
76
167
  {
77
- "aria-label": "Next",
78
- disabled: isLast,
79
- icon: /* @__PURE__ */ jsx(IconAngleRight, {}),
80
- onClick: (event) => {
81
- event.preventDefault();
82
- setCurrentIndex((i) => i + 1);
83
- },
84
- size: "sm"
168
+ role: type === "single_select" ? "radiogroup" : "listbox",
169
+ ...choiceGroup(),
170
+ children: [
171
+ options.map((option, index) => {
172
+ const checked = type === "single_select" ? value?.[0] === option : value?.includes(option);
173
+ const disabled = type === "single_select" && otherChecked && !!otherValue;
174
+ return /* @__PURE__ */ jsx(RovingFocus.Item, { asChild: true, children: /* @__PURE__ */ jsx(
175
+ Box,
176
+ {
177
+ "aria-checked": type === "single_select" ? checked : void 0,
178
+ "data-disabled": disabled ? "" : void 0,
179
+ "data-selected": checked && type === "single_select" ? "" : void 0,
180
+ onClick: () => {
181
+ if (type === "single_select") {
182
+ onValueChange([option]);
183
+ onSubmit();
184
+ } else {
185
+ const current = value ?? [];
186
+ onValueChange(
187
+ checked ? current.filter((v) => v !== option) : [...current, option]
188
+ );
189
+ }
190
+ },
191
+ onFocus: () => {
192
+ if (type === "single_select") {
193
+ onValueChange([option]);
194
+ }
195
+ },
196
+ onKeyDown: (event) => {
197
+ if (type === "single_select" && event.key === "Enter") {
198
+ event.preventDefault();
199
+ onSubmit();
200
+ } else if (type === "multi_select" && (event.key === "Enter" || event.key === " ")) {
201
+ event.preventDefault();
202
+ const current = value ?? [];
203
+ onValueChange(
204
+ checked ? current.filter((v) => v !== option) : [...current, option]
205
+ );
206
+ }
207
+ },
208
+ role: type === "single_select" ? "radio" : "option",
209
+ tabIndex: 0,
210
+ ...choice({ cursor: "pointer" }),
211
+ children: /* @__PURE__ */ jsxs(Group, { gap: "12", children: [
212
+ /* @__PURE__ */ jsx(Box, { ...addon(), children: type === "single_select" ? index + 1 : /* @__PURE__ */ jsx(
213
+ Checkbox,
214
+ {
215
+ checked,
216
+ hidden: true,
217
+ pointerEvents: "none",
218
+ tabIndex: -1
219
+ }
220
+ ) }),
221
+ /* @__PURE__ */ jsx(Group, { flex: "1", flexDirection: "column", gap: "2", children: /* @__PURE__ */ jsx(Text, { children: option }) }),
222
+ type === "single_select" && checked && /* @__PURE__ */ jsx(Box, { asChild: true, color: "fg.tertiary", ml: "auto", children: /* @__PURE__ */ jsx(IconNorth, {}) })
223
+ ] })
224
+ }
225
+ ) }, option);
226
+ }),
227
+ /* @__PURE__ */ jsx(
228
+ Box,
229
+ {
230
+ "aria-label": "Something else",
231
+ "data-selected": otherChecked ? "" : void 0,
232
+ role: "group",
233
+ ...choice({ cursor: "text" }),
234
+ children: /* @__PURE__ */ jsxs(Group, { gap: "12", children: [
235
+ /* @__PURE__ */ jsx(Box, { ...addon({ cursor: "pointer" }), children: type === "single_select" ? /* @__PURE__ */ jsx(IconPencil, {}) : /* @__PURE__ */ jsx(
236
+ Checkbox,
237
+ {
238
+ checked: otherChecked,
239
+ hidden: true,
240
+ pointerEvents: "none",
241
+ tabIndex: -1
242
+ }
243
+ ) }),
244
+ /* @__PURE__ */ jsx(RovingFocus.Item, { asChild: true, children: /* @__PURE__ */ jsx(
245
+ Group,
246
+ {
247
+ flex: "1",
248
+ flexDirection: "column",
249
+ gap: "2",
250
+ onFocus: () => {
251
+ if (type === "single_select" && !otherValue) {
252
+ onValueChange([""]);
253
+ }
254
+ otherInputRef.current?.focus();
255
+ },
256
+ ref: otherItemRef,
257
+ children: /* @__PURE__ */ jsx(
258
+ InlineInput,
259
+ {
260
+ label: "Something else",
261
+ onKeyDown: (event) => {
262
+ if (event.key === "ArrowUp" || event.key === "ArrowDown") {
263
+ event.preventDefault();
264
+ otherItemRef.current?.focus();
265
+ otherItemRef.current?.dispatchEvent(
266
+ new KeyboardEvent("keydown", {
267
+ bubbles: true,
268
+ key: event.key
269
+ })
270
+ );
271
+ } else if (event.key === "Enter") {
272
+ onSubmit();
273
+ }
274
+ },
275
+ onValueChange: (text) => {
276
+ if (type === "single_select") {
277
+ onValueChange([text]);
278
+ } else {
279
+ const current = value ?? [];
280
+ onValueChange([
281
+ ...current.filter((v) => options.includes(v)),
282
+ ...text ? [text] : []
283
+ ]);
284
+ }
285
+ },
286
+ ref: otherInputRef,
287
+ value: otherValue ?? ""
288
+ }
289
+ )
290
+ }
291
+ ) }),
292
+ type === "single_select" && /* @__PURE__ */ jsx(
293
+ Button,
294
+ {
295
+ appearance: otherValue ? "primary" : "default",
296
+ "aria-label": otherValue && (isLast ? "Submit" : "Next"),
297
+ icon: otherValue && /* @__PURE__ */ jsx(IconNorth, {}),
298
+ ml: "auto",
299
+ onClick: (event) => {
300
+ event.preventDefault();
301
+ if (otherValue) {
302
+ onSubmit();
303
+ } else {
304
+ onSkip();
305
+ }
306
+ },
307
+ children: !otherValue && "Skip"
308
+ }
309
+ )
310
+ ] })
311
+ },
312
+ "other"
313
+ )
314
+ ]
85
315
  }
86
- )
316
+ ) })
87
317
  ] }),
88
- choiceRef: questionRef,
89
- onValueChange: (value) => {
90
- answers[currentIndex] = value;
91
- setAnswers([...answers]);
92
- },
93
- options: current.options,
94
- question: current.question,
95
- type: current.type,
96
- value: answers[currentIndex] ?? null
97
- }
98
- ),
99
- /* @__PURE__ */ jsxs(Group, { gap: "8", justifyContent: "end", children: [
100
- /* @__PURE__ */ jsx(
101
- Button,
102
- {
103
- onClick: (event) => {
104
- event.preventDefault();
105
- answers[currentIndex] = null;
106
- setAnswers([...answers]);
107
- if (isLast) {
108
- void onComplete();
109
- } else {
110
- setCurrentIndex((i) => i + 1);
111
- }
112
- },
113
- children: "Skip"
114
- }
115
- ),
116
- /* @__PURE__ */ jsx(
117
- Button,
118
- {
119
- appearance: "primary",
120
- disabled: !valid,
121
- onClick: (event) => {
122
- event.preventDefault();
123
- if (isLast) {
124
- void onComplete();
125
- } else {
126
- setCurrentIndex((i) => i + 1);
127
- }
128
- },
129
- children: isLast ? "Submit" : "Next"
130
- }
131
- )
132
- ] })
133
- ] });
318
+ type === "multi_select" && /* @__PURE__ */ jsxs(
319
+ Group,
320
+ {
321
+ borderColor: "border.secondary",
322
+ borderT: "1",
323
+ gap: "8",
324
+ pt: "16",
325
+ px: "16",
326
+ children: [
327
+ /* @__PURE__ */ jsxs(Text, { children: [
328
+ value?.length || 0,
329
+ " selected"
330
+ ] }),
331
+ (value?.length || 0) > 0 ? /* @__PURE__ */ jsx(
332
+ Button,
333
+ {
334
+ appearance: "primary",
335
+ "aria-label": isLast ? "Submit" : "Next",
336
+ disabled: !valid,
337
+ icon: /* @__PURE__ */ jsx(IconNorth, {}),
338
+ ml: "auto",
339
+ onClick: (event) => {
340
+ event.preventDefault();
341
+ onSubmit();
342
+ }
343
+ }
344
+ ) : /* @__PURE__ */ jsx(
345
+ Button,
346
+ {
347
+ ml: "auto",
348
+ onClick: (event) => {
349
+ event.preventDefault();
350
+ onSkip();
351
+ },
352
+ children: "Skip"
353
+ }
354
+ )
355
+ ]
356
+ }
357
+ )
358
+ ]
359
+ }
360
+ );
134
361
  }
135
362
  ProteusQuestion.displayName = "@optiaxiom/proteus/ProteusQuestion";
136
363
 
@@ -5802,6 +5802,17 @@ var definitions = {
5802
5802
  ],
5803
5803
  description: "Control the appearance of the input."
5804
5804
  },
5805
+ autoFocus: {
5806
+ anyOf: [
5807
+ {
5808
+ type: "boolean"
5809
+ },
5810
+ {
5811
+ $ref: "#/definitions/ProteusValue"
5812
+ }
5813
+ ],
5814
+ description: "Whether the input should be focused on mount."
5815
+ },
5805
5816
  backgroundImage: {
5806
5817
  $ref: "#/definitions/SprinkleProp_backgroundImage"
5807
5818
  },
@@ -5767,6 +5767,17 @@ var definitions = {
5767
5767
  ],
5768
5768
  description: "Control the appearance of the input."
5769
5769
  },
5770
+ autoFocus: {
5771
+ anyOf: [
5772
+ {
5773
+ type: "boolean"
5774
+ },
5775
+ {
5776
+ $ref: "#/definitions/ProteusValue"
5777
+ }
5778
+ ],
5779
+ description: "Whether the input should be focused on mount."
5780
+ },
5770
5781
  backgroundImage: {
5771
5782
  $ref: "#/definitions/SprinkleProp_backgroundImage"
5772
5783
  },