@optiaxiom/proteus 0.1.0 → 0.1.1

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._1fm60vq;
2
- @layer optiaxiom._1fm60vq {
1
+ @layer optiaxiom._1qll070;
2
+ @layer optiaxiom._1qll070 {
3
3
  .ProteusChart__jmlqij1 {
4
4
  border-radius: 16px;
5
5
  }
@@ -1,5 +1,5 @@
1
- @layer optiaxiom._1fm60vq;
2
- @layer optiaxiom._1fm60vq {
1
+ @layer optiaxiom._1qll070;
2
+ @layer optiaxiom._1qll070 {
3
3
  .ProteusChartTooltipContent__1gsvq810 {
4
4
  min-width: 128px;
5
5
  }
@@ -1,13 +1,19 @@
1
- @layer optiaxiom._1fm60vq;
2
- @layer optiaxiom._1fm60vq {
1
+ @layer optiaxiom._1qll070;
2
+ @layer optiaxiom._1qll070 {
3
3
  .ProteusQuestionItem__uar8vn2 {
4
4
  background-color: var(--ax-colors-bg-page);
5
5
  border-color: var(--ax-colors-bg-page);
6
+ cursor: pointer;
6
7
  }
7
8
  .ProteusQuestionItem__uar8vn2:has(.ProteusQuestionItem__uar8vn1:checked) {
8
9
  background-color: var(--ax-colors-bg-accent-subtle);
9
10
  border-color: var(--ax-colors-bg-accent-light);
10
11
  }
12
+ .ProteusQuestionItem__uar8vn2:has(.ProteusQuestionItem__uar8vn1:disabled) {
13
+ cursor: default;
14
+ opacity: 0.5;
15
+ pointer-events: none;
16
+ }
11
17
  .ProteusQuestionItem__uar8vn2:has(.ProteusQuestionItem__uar8vn1:focus-visible) {
12
18
  outline: 2px solid var(--ax-colors-border-focus);
13
19
  outline-offset: 1px;
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { withIcon } from './withIcon.js';
4
+
5
+ const IconAngleRight = withIcon(
6
+ {
7
+ fill: "none",
8
+ name: "IconAngleRight"
9
+ },
10
+ /* @__PURE__ */ jsx(
11
+ "path",
12
+ {
13
+ d: "M7.5 13L12.5 8L7.5 3",
14
+ stroke: "currentColor",
15
+ strokeLinecap: "round",
16
+ strokeLinejoin: "round"
17
+ }
18
+ )
19
+ );
20
+
21
+ export { IconAngleRight };
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { withIcon } from './withIcon.js';
4
+
5
+ const IconPencil = withIcon(
6
+ {
7
+ name: "IconPencil",
8
+ viewBox: "0 0 640 640"
9
+ },
10
+ // <!--!Font Awesome Free v7.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2026 Fonticons, Inc.-->
11
+ /* @__PURE__ */ jsx(
12
+ "path",
13
+ {
14
+ d: "M100.4 417.2C104.5 402.6 112.2 389.3 123 378.5L304.2 197.3L338.1 163.4C354.7 180 389.4 214.7 442.1 267.4L476 301.3L442.1 335.2L260.9 516.4C250.2 527.1 236.8 534.9 222.2 539L94.4 574.6C86.1 576.9 77.1 574.6 71 568.4C64.9 562.2 62.6 553.3 64.9 545L100.4 417.2zM156 413.5C151.6 418.2 148.4 423.9 146.7 430.1L122.6 517L209.5 492.9C215.9 491.1 221.7 487.8 226.5 483.2L155.9 413.5zM510 267.4C493.4 250.8 458.7 216.1 406 163.4L372 129.5C398.5 103 413.4 88.1 416.9 84.6C430.4 71 448.8 63.4 468 63.4C487.2 63.4 505.6 71 519.1 84.6L554.8 120.3C568.4 133.9 576 152.3 576 171.4C576 190.5 568.4 209 554.8 222.5C551.3 226 536.4 240.9 509.9 267.4z",
15
+ fill: "currentColor"
16
+ }
17
+ )
18
+ );
19
+
20
+ export { IconPencil };
@@ -1,4 +1,4 @@
1
- import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-WpbWN23E.css';
1
+ import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-DWDlfc5R.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-sqWemcZm.css';
1
+ import './../assets/src/proteus-chart/ProteusChartTooltipContent.css.ts.vanilla-WrO00wrg.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']});
@@ -1,8 +1,9 @@
1
1
  "use client";
2
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
3
  import { Group, Button, Text } from '@optiaxiom/react';
4
- import { useState } from 'react';
4
+ import { useState, useRef, useEffect } from 'react';
5
5
  import { IconAngleLeft } from '../icons/IconAngleLeft.js';
6
+ import { IconAngleRight } from '../icons/IconAngleRight.js';
6
7
  import { useProteusDocumentContext } from '../proteus-document/ProteusDocumentContext.js';
7
8
  import { ProteusQuestionItem } from './ProteusQuestionItem.js';
8
9
 
@@ -12,6 +13,30 @@ function ProteusQuestion({ questions }) {
12
13
  );
13
14
  const [answers, setAnswers] = useState([]);
14
15
  const [currentIndex, setCurrentIndex] = useState(0);
16
+ const answer = answers[currentIndex];
17
+ const valid = Array.isArray(answer) && answer.length > 0 && answer.every(Boolean);
18
+ const questionRef = useRef(null);
19
+ const lastIndexRef = useRef(currentIndex);
20
+ useEffect(() => {
21
+ if (lastIndexRef.current !== currentIndex) {
22
+ questionRef.current?.animate(
23
+ [
24
+ {
25
+ opacity: 0,
26
+ translate: currentIndex > lastIndexRef.current ? "8px" : "-8px"
27
+ },
28
+ {
29
+ opacity: 1,
30
+ translate: "0px"
31
+ }
32
+ ],
33
+ {
34
+ duration: 150
35
+ }
36
+ );
37
+ lastIndexRef.current = currentIndex;
38
+ }
39
+ }, [currentIndex]);
15
40
  if (currentIndex >= questions.length) {
16
41
  return null;
17
42
  }
@@ -27,6 +52,40 @@ A: ${value === null ? "[Not specified]" : value}`
27
52
  /* @__PURE__ */ jsx(
28
53
  ProteusQuestionItem,
29
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
73
+ ] }),
74
+ /* @__PURE__ */ jsx(
75
+ Button,
76
+ {
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"
85
+ }
86
+ )
87
+ ] }),
88
+ choiceRef: questionRef,
30
89
  onValueChange: (value) => {
31
90
  answers[currentIndex] = value;
32
91
  setAnswers([...answers]);
@@ -38,25 +97,6 @@ A: ${value === null ? "[Not specified]" : value}`
38
97
  }
39
98
  ),
40
99
  /* @__PURE__ */ jsxs(Group, { gap: "8", justifyContent: "end", children: [
41
- questions.length > 1 && /* @__PURE__ */ jsxs(Fragment, { children: [
42
- /* @__PURE__ */ jsx(
43
- Button,
44
- {
45
- "aria-label": "Previous",
46
- disabled: currentIndex === 0,
47
- icon: /* @__PURE__ */ jsx(IconAngleLeft, {}),
48
- onClick: (event) => {
49
- event.preventDefault();
50
- setCurrentIndex((i) => i - 1);
51
- }
52
- }
53
- ),
54
- /* @__PURE__ */ jsxs(Text, { color: "fg.tertiary", children: [
55
- currentIndex + 1,
56
- " of ",
57
- questions.length
58
- ] })
59
- ] }),
60
100
  /* @__PURE__ */ jsx(
61
101
  Button,
62
102
  {
@@ -77,6 +117,7 @@ A: ${value === null ? "[Not specified]" : value}`
77
117
  Button,
78
118
  {
79
119
  appearance: "primary",
120
+ disabled: !valid,
80
121
  onClick: (event) => {
81
122
  event.preventDefault();
82
123
  if (isLast) {
@@ -1,8 +1,8 @@
1
- import './../assets/src/proteus-question/ProteusQuestionItem.css.ts.vanilla-Dns_NfIP.css';
1
+ import './../assets/src/proteus-question/ProteusQuestionItem.css.ts.vanilla-S1I3NGY4.css';
2
2
  import { recipe } from '@optiaxiom/react/css-runtime';
3
3
 
4
- var addon = recipe({base:[{display:'grid',fontWeight:'500',placeItems:'center',rounded:'lg',size:'md',transition:'colors'},'ProteusQuestionItem__uar8vn3']});
5
- var choice = recipe({base:[{border:'1',color:'fg.default',cursor:'pointer',flexDirection:'column',fontSize:'md',gap:'8',px:'16',py:'12',rounded:'lg',transition:'colors'},'ProteusQuestionItem__uar8vn2','ProteusQuestionItem__uar8vn0']});
4
+ var addon = recipe({base:[{display:'grid',fontWeight:'500',placeItems:'center',rounded:'lg',size:'md',transition:'colors'},'ProteusQuestionItem__uar8vn3'],variants:{cursor:{pointer:{cursor:'pointer'}}}});
5
+ var choice = recipe({base:[{border:'1',color:'fg.default',flexDirection:'column',fontSize:'md',gap:'8',px:'16',py:'12',rounded:'lg',transition:'colors'},'ProteusQuestionItem__uar8vn2','ProteusQuestionItem__uar8vn0'],variants:{cursor:{text:{cursor:'text'}}}});
6
6
  var choiceGroup = recipe({base:{flexDirection:'column',gap:'8'}});
7
7
  var input = recipe({base:'ProteusQuestionItem__uar8vn1'});
8
8
 
@@ -1,55 +1,117 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from 'react/jsx-runtime';
3
- import { createElement } from 'react';
3
+ import { useRef, createElement } from 'react';
4
4
  import { Group, Text, Box, Checkbox } from '@optiaxiom/react';
5
- import { VisuallyHidden } from '@optiaxiom/react/unstable';
5
+ import { VisuallyHidden, InlineInput } from '@optiaxiom/react/unstable';
6
+ import { IconPencil } from '../icons/IconPencil.js';
6
7
  import { choiceGroup, choice, input, addon } from './ProteusQuestionItem-css.js';
7
8
 
8
9
  function ProteusQuestionItem({
10
+ addonAfter,
11
+ choiceRef,
9
12
  onValueChange,
10
13
  options,
11
14
  question,
12
15
  type,
13
16
  value
14
17
  }) {
18
+ const otherInputRef = useRef(null);
19
+ const otherValue = value?.find((v) => !options.includes(v));
20
+ const otherChecked = otherValue !== void 0;
15
21
  return /* @__PURE__ */ jsxs(Group, { flexDirection: "column", gap: "16", children: [
16
- /* @__PURE__ */ jsx(Text, { fontWeight: "500", children: question }),
17
- /* @__PURE__ */ jsx(Group, { ...choiceGroup(), children: options.map((option, index) => {
18
- const checked = type === "single_select" ? value?.[0] === option : Array.isArray(value) && value.includes(option);
19
- return /* @__PURE__ */ createElement(Box, { asChild: true, ...choice(), key: option }, /* @__PURE__ */ jsxs("label", { children: [
22
+ /* @__PURE__ */ jsxs(Group, { children: [
23
+ /* @__PURE__ */ jsx(Text, { flex: "1", fontWeight: "500", children: question }),
24
+ addonAfter
25
+ ] }),
26
+ /* @__PURE__ */ jsxs(Group, { ref: choiceRef, ...choiceGroup(), children: [
27
+ options.map((option, index) => {
28
+ const checked = type === "single_select" ? value?.[0] === option : value?.includes(option);
29
+ const disabled = type === "single_select" && otherChecked && !!otherValue;
30
+ return /* @__PURE__ */ createElement(Box, { asChild: true, ...choice(), key: option }, /* @__PURE__ */ jsxs("label", { children: [
31
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(Box, { asChild: true, ...input(), children: /* @__PURE__ */ jsx(
32
+ "input",
33
+ {
34
+ checked,
35
+ disabled,
36
+ name: type === "single_select" ? "question-item" : void 0,
37
+ onChange: () => {
38
+ if (type === "single_select") {
39
+ onValueChange([option]);
40
+ } else {
41
+ const current = value ?? [];
42
+ onValueChange(
43
+ checked ? current.filter((v) => v !== option) : [...current, option]
44
+ );
45
+ }
46
+ },
47
+ type: type === "single_select" ? "radio" : "checkbox",
48
+ value: option
49
+ }
50
+ ) }) }),
51
+ /* @__PURE__ */ jsxs(Group, { gap: "12", children: [
52
+ /* @__PURE__ */ jsx(Box, { ...addon(), children: type === "single_select" ? index + 1 : /* @__PURE__ */ jsx(
53
+ Checkbox,
54
+ {
55
+ checked,
56
+ hidden: true,
57
+ pointerEvents: "none",
58
+ tabIndex: -1
59
+ }
60
+ ) }),
61
+ /* @__PURE__ */ jsx(Group, { flex: "1", flexDirection: "column", gap: "2", children: /* @__PURE__ */ jsx(Text, { children: option }) })
62
+ ] })
63
+ ] }));
64
+ }),
65
+ /* @__PURE__ */ createElement(Box, { asChild: true, ...choice({ cursor: "text" }), key: "other" }, /* @__PURE__ */ jsxs("label", { children: [
20
66
  /* @__PURE__ */ jsx(VisuallyHidden, { children: /* @__PURE__ */ jsx(Box, { asChild: true, ...input(), children: /* @__PURE__ */ jsx(
21
67
  "input",
22
68
  {
23
- checked,
69
+ checked: otherChecked,
24
70
  name: type === "single_select" ? "question-item" : void 0,
25
71
  onChange: () => {
26
72
  if (type === "single_select") {
27
- onValueChange([option]);
28
- } else {
29
- const current = Array.isArray(value) ? value : [];
30
- onValueChange(
31
- checked ? current.filter((v) => v !== option) : [...current, option]
32
- );
73
+ if (!otherValue) {
74
+ onValueChange([""]);
75
+ }
33
76
  }
77
+ otherInputRef.current?.focus();
34
78
  },
35
- type: type === "single_select" ? "radio" : "checkbox",
36
- value: option
79
+ type: "checkbox",
80
+ value: "other"
37
81
  }
38
82
  ) }) }),
39
83
  /* @__PURE__ */ jsxs(Group, { gap: "12", children: [
40
- /* @__PURE__ */ jsx(Box, { ...addon(), children: type === "single_select" ? index + 1 : /* @__PURE__ */ jsx(
84
+ /* @__PURE__ */ jsx(Box, { ...addon({ cursor: "pointer" }), children: type === "single_select" ? /* @__PURE__ */ jsx(IconPencil, {}) : /* @__PURE__ */ jsx(
41
85
  Checkbox,
42
86
  {
43
- checked,
87
+ checked: otherChecked,
44
88
  hidden: true,
45
89
  pointerEvents: "none",
46
90
  tabIndex: -1
47
91
  }
48
92
  ) }),
49
- /* @__PURE__ */ jsx(Group, { flex: "1", flexDirection: "column", gap: "2", children: /* @__PURE__ */ jsx(Text, { fontWeight: "500", children: option }) })
93
+ /* @__PURE__ */ jsx(Group, { flex: "1", flexDirection: "column", gap: "2", children: /* @__PURE__ */ jsx(
94
+ InlineInput,
95
+ {
96
+ label: "Something else",
97
+ onValueChange: (text) => {
98
+ if (type === "single_select") {
99
+ onValueChange([text]);
100
+ } else {
101
+ const current = value ?? [];
102
+ onValueChange([
103
+ ...current.filter((v) => options.includes(v)),
104
+ ...text ? [text] : []
105
+ ]);
106
+ }
107
+ },
108
+ ref: otherInputRef,
109
+ value: otherValue ?? ""
110
+ }
111
+ ) })
50
112
  ] })
51
- ] }));
52
- }) })
113
+ ] }))
114
+ ] })
53
115
  ] });
54
116
  }
55
117
  ProteusQuestionItem.displayName = "@optiaxiom/proteus/ProteusQuestionItem";
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "git+https://github.com/optimizely-axiom/optiaxiom.git"
8
8
  },
9
9
  "type": "module",
10
- "version": "0.1.0",
10
+ "version": "0.1.1",
11
11
  "files": [
12
12
  "dist/**",
13
13
  "LICENSE"
@@ -31,10 +31,10 @@
31
31
  "@cfworker/json-schema": "^4.1.1",
32
32
  "@radix-ui/react-context": "^1.1.2",
33
33
  "@radix-ui/react-use-controllable-state": "^1.2.2",
34
- "@tanstack/react-table": "^8.21.3",
34
+ "@tanstack/react-table": "^8.0.0",
35
35
  "jsonpointer": "^5.0.1",
36
36
  "recharts": "^3.7.0",
37
- "@optiaxiom/react": "1.9.18"
37
+ "@optiaxiom/react": "1.9.19"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@emotion/hash": "^0.9.2",