@optiaxiom/proteus 0.1.16-next.0 → 0.1.16

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.ndq0nz;
2
- @layer optiaxiom.ndq0nz {
1
+ @layer optiaxiom._1g6ippr;
2
+ @layer optiaxiom._1g6ippr {
3
3
  .ProteusChart__jmlqij1 {
4
4
  border-radius: 16px;
5
5
  }
@@ -1,5 +1,5 @@
1
- @layer optiaxiom.ndq0nz;
2
- @layer optiaxiom.ndq0nz {
1
+ @layer optiaxiom._1g6ippr;
2
+ @layer optiaxiom._1g6ippr {
3
3
  .ProteusChartTooltipContent__1gsvq810 {
4
4
  min-width: 128px;
5
5
  }
@@ -0,0 +1,72 @@
1
+ @layer optiaxiom._1g6ippr;
2
+ @layer optiaxiom._1g6ippr {
3
+ .ProteusImageCarousel__1t6qej70 {
4
+ outline: none;
5
+ user-select: none;
6
+ }
7
+ .ProteusImageCarousel__1t6qej71 {
8
+ aspect-ratio: 16 / 9;
9
+ grid-template-columns: auto 1fr auto;
10
+ grid-template-rows: auto 1fr auto;
11
+ position: relative;
12
+ }
13
+ .ProteusImageCarousel__1t6qej72 {
14
+ grid-area: 1 / 1 / -1 / -1;
15
+ }
16
+ .ProteusImageCarousel__1t6qej73 {
17
+ backface-visibility: hidden;
18
+ display: flex;
19
+ touch-action: pan-y pinch-zoom;
20
+ }
21
+ .ProteusImageCarousel__1t6qej74 {
22
+ flex: 0 0 100%;
23
+ height: 100%;
24
+ min-width: 0;
25
+ }
26
+ .ProteusImageCarousel__1t6qej75 {
27
+ align-self: center;
28
+ grid-row: 2;
29
+ opacity: 0.7;
30
+ z-index: 1;
31
+ }
32
+ .ProteusImageCarousel__1t6qej75::before {
33
+ content: "";
34
+ inset-block: 0;
35
+ padding-left: 32px;
36
+ padding-right: 16px;
37
+ position: absolute;
38
+ width: 32px;
39
+ }
40
+ .ProteusImageCarousel__1t6qej75:hover {
41
+ opacity: 1;
42
+ }
43
+ .ProteusImageCarousel__1t6qej76 {
44
+ grid-column: 1;
45
+ }
46
+ .ProteusImageCarousel__1t6qej77 {
47
+ grid-column: 3;
48
+ }
49
+ .ProteusImageCarousel__1t6qej78 {
50
+ position: relative;
51
+ }
52
+ .ProteusImageCarousel__1t6qej78::after {
53
+ content: "";
54
+ border-radius: inherit;
55
+ box-shadow: inset 0 0 0 2px var(--ax-colors-border-focus);
56
+ inset: 0;
57
+ opacity: 0;
58
+ outline: 2px solid var(--ax-colors-bg-default);
59
+ outline-offset: -4px;
60
+ position: absolute;
61
+ transition: opacity var(--ax-duration-sm);
62
+ }
63
+ .ProteusImageCarousel__1t6qej79 {
64
+ opacity: 0.7;
65
+ }
66
+ .ProteusImageCarousel__1t6qej7a {
67
+ opacity: 1;
68
+ }
69
+ .ProteusImageCarousel__1t6qej7a::after {
70
+ opacity: 1;
71
+ }
72
+ }
@@ -1,5 +1,5 @@
1
- @layer optiaxiom.ndq0nz;
2
- @layer optiaxiom.ndq0nz {
1
+ @layer optiaxiom._1g6ippr;
2
+ @layer optiaxiom._1g6ippr {
3
3
  .ProteusQuestion__8f590p0 {
4
4
  outline: none;
5
5
  }
package/dist/esm/index.js CHANGED
@@ -6,6 +6,7 @@ export { ProteusDocumentRenderer } from './proteus-document/ProteusDocumentRende
6
6
  export { ProteusDocumentShell } from './proteus-document/ProteusDocumentShell.js';
7
7
  export { safeParseDocument } from './proteus-document/schemas.js';
8
8
  export { ProteusImage } from './proteus-image/ProteusImage.js';
9
+ export { ProteusImageCarousel } from './proteus-image-carousel/ProteusImageCarousel.js';
9
10
  export { ProteusInput } from './proteus-input/ProteusInput.js';
10
11
  export { ProteusMap } from './proteus-map/ProteusMap.js';
11
12
  export { ProteusSelect } from './proteus-select/ProteusSelect.js';
@@ -1,4 +1,4 @@
1
- import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-Ct8ajpEs.css';
1
+ import './../assets/src/proteus-chart/ProteusChart.css.ts.vanilla-CK0mlfvQ.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-CV4s3EJV.css';
1
+ import './../assets/src/proteus-chart/ProteusChartTooltipContent.css.ts.vanilla-VEKfizml.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']});
@@ -28,6 +28,24 @@ function ProteusDocumentShell({
28
28
  setValid(formRef.current.checkValidity());
29
29
  }
30
30
  }, []);
31
+ useEffect(() => {
32
+ if (!element.blocking || !formRef.current)
33
+ return;
34
+ const walker = document.createTreeWalker(
35
+ formRef.current,
36
+ NodeFilter.SHOW_ELEMENT,
37
+ {
38
+ acceptNode: (node) => {
39
+ if (node instanceof HTMLInputElement && node.type === "hidden")
40
+ return NodeFilter.FILTER_SKIP;
41
+ if (node.hidden)
42
+ return NodeFilter.FILTER_SKIP;
43
+ return node.tabIndex >= 0 ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
44
+ }
45
+ }
46
+ );
47
+ walker.nextNode()?.focus();
48
+ }, [element.blocking]);
31
49
  const [open, setOpen] = useControllableState({
32
50
  defaultProp: defaultOpen,
33
51
  onChange: onOpenChange,
@@ -77,7 +95,7 @@ function ProteusDocumentShell({
77
95
  borderColor: "border.tertiary",
78
96
  onOpenChange: setOpen,
79
97
  open,
80
- p: "16",
98
+ p: "20",
81
99
  rounded: "xl",
82
100
  children: [
83
101
  element.appName && /* @__PURE__ */ jsx(Trigger, { py: "0", ...collapsible ? { chevronPosition: "end" } : {}, children: /* @__PURE__ */ jsxs(Group, { fontSize: "sm", gap: "8", children: [
@@ -11,6 +11,7 @@ import { useProteusDocumentContext } from '../proteus-document/ProteusDocumentCo
11
11
  import { useProteusDocumentPathContext } from '../proteus-document/ProteusDocumentPathContext.js';
12
12
  import { resolveProteusProp } from '../proteus-document/resolveProteusProp.js';
13
13
  import { safeParseElement } from '../proteus-document/schemas.js';
14
+ import { ProteusImageCarousel } from '../proteus-image-carousel/ProteusImageCarousel.js';
14
15
  import { ProteusImage } from '../proteus-image/ProteusImage.js';
15
16
  import { ProteusInput } from '../proteus-input/ProteusInput.js';
16
17
  import { ProteusMap } from '../proteus-map/ProteusMap.js';
@@ -72,7 +73,7 @@ const ProteusElement = ({
72
73
  case "CardHeader":
73
74
  return /* @__PURE__ */ jsx(CardHeader, { ...resolve(element) });
74
75
  case "CardLink":
75
- return /* @__PURE__ */ jsx(CardLink, { ...resolve(element) });
76
+ return /* @__PURE__ */ jsx(CardLink, { target: "_blank", ...resolve(element) });
76
77
  case "Chart":
77
78
  return /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
78
79
  ProteusChart,
@@ -97,10 +98,17 @@ const ProteusElement = ({
97
98
  return /* @__PURE__ */ jsx(Box, { asChild: true, ...resolve(element), children: /* @__PURE__ */ jsx(IconCalendar, {}) });
98
99
  case "Image":
99
100
  return /* @__PURE__ */ jsx(ProteusImage, { ...resolve(element) });
101
+ case "ImageCarousel":
102
+ return /* @__PURE__ */ jsx(
103
+ ProteusImageCarousel,
104
+ {
105
+ ...resolve(element)
106
+ }
107
+ );
100
108
  case "Input":
101
109
  return /* @__PURE__ */ jsx(ProteusInput, { ...resolve(element) });
102
110
  case "Link":
103
- return /* @__PURE__ */ jsx(Link, { ...resolve(element) });
111
+ return /* @__PURE__ */ jsx(Link, { target: "_blank", ...resolve(element) });
104
112
  case "Map":
105
113
  return /* @__PURE__ */ jsx(
106
114
  ProteusMap,
@@ -0,0 +1,12 @@
1
+ import './../assets/src/proteus-image-carousel/ProteusImageCarousel.css.ts.vanilla-DV3O-VwO.css';
2
+ import { recipe } from '@optiaxiom/react/css-runtime';
3
+
4
+ var carousel = recipe({base:[{flexDirection:'column',gap:'12'},'ProteusImageCarousel__1t6qej70']});
5
+ var content = recipe({base:[{display:'grid',overflow:'hidden',rounded:'lg'},'ProteusImageCarousel__1t6qej71']});
6
+ var navButton = recipe({base:[{bg:'bg.default',display:'grid',mx:'16',placeItems:'center',rounded:'full',size:'md',transition:'opacity'},'ProteusImageCarousel__1t6qej75'],variants:{side:{left:[{mr:'32'},'ProteusImageCarousel__1t6qej76'],right:[{ml:'32'},'ProteusImageCarousel__1t6qej77']}}});
7
+ var slide = recipe({base:[{objectFit:'cover',size:'full'},'ProteusImageCarousel__1t6qej74']});
8
+ var slideContainer = recipe({base:[{size:'full'},'ProteusImageCarousel__1t6qej73']});
9
+ var thumbnail = recipe({base:[{cursor:'pointer',flex:'none',objectFit:'cover',overflow:'hidden',rounded:'lg',size:'80',transition:'opacity'},'ProteusImageCarousel__1t6qej78'],variants:{selected:{false:'ProteusImageCarousel__1t6qej79',true:'ProteusImageCarousel__1t6qej7a'}}});
10
+ var viewport = recipe({base:[{overflow:'hidden'},'ProteusImageCarousel__1t6qej72']});
11
+
12
+ export { carousel, content, navButton, slide, slideContainer, thumbnail, viewport };
@@ -0,0 +1,164 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { Group, Box, Text, Menu, MenuTrigger, MenuContent, Button } from '@optiaxiom/react';
4
+ import useEmblaCarousel from 'embla-carousel-react';
5
+ import { useState, useCallback, useEffect } from 'react';
6
+ import { IconAngleLeft } from '../icons/IconAngleLeft.js';
7
+ import { IconAngleRight } from '../icons/IconAngleRight.js';
8
+ import { downloadFile } from '../proteus-image/downloadFile.js';
9
+ import { ProteusImage } from '../proteus-image/ProteusImage.js';
10
+ import { carousel, content, viewport, slideContainer, slide, navButton, thumbnail } from './ProteusImageCarousel-css.js';
11
+
12
+ function ProteusImageCarousel({
13
+ downloadAll,
14
+ images,
15
+ title
16
+ }) {
17
+ const [emblaMainRef, emblaMainApi] = useEmblaCarousel({ loop: false });
18
+ const [emblaThumbsRef, emblaThumbsApi] = useEmblaCarousel({
19
+ containScroll: "keepSnaps",
20
+ dragFree: true
21
+ });
22
+ const [selectedIndex, setSelectedIndex] = useState(0);
23
+ const [canScrollPrev, setCanScrollPrev] = useState(false);
24
+ const [canScrollNext, setCanScrollNext] = useState(false);
25
+ const onSelect = useCallback(() => {
26
+ if (!emblaMainApi || !emblaThumbsApi)
27
+ return;
28
+ setSelectedIndex(emblaMainApi.selectedScrollSnap());
29
+ emblaThumbsApi.scrollTo(emblaMainApi.selectedScrollSnap());
30
+ setCanScrollPrev(emblaMainApi.canScrollPrev());
31
+ setCanScrollNext(emblaMainApi.canScrollNext());
32
+ }, [emblaMainApi, emblaThumbsApi]);
33
+ useEffect(() => {
34
+ if (!emblaMainApi)
35
+ return;
36
+ onSelect();
37
+ emblaMainApi.on("select", onSelect).on("reInit", onSelect);
38
+ }, [emblaMainApi, onSelect]);
39
+ if (!images.length) {
40
+ return null;
41
+ }
42
+ return /* @__PURE__ */ jsxs(
43
+ Group,
44
+ {
45
+ "aria-label": title ?? "Image carousel",
46
+ "aria-roledescription": "carousel",
47
+ onKeyDownCapture: (event) => {
48
+ if (event.target instanceof HTMLInputElement)
49
+ return;
50
+ if (event.key === "ArrowLeft") {
51
+ event.preventDefault();
52
+ emblaMainApi?.scrollPrev();
53
+ } else if (event.key === "ArrowRight") {
54
+ event.preventDefault();
55
+ emblaMainApi?.scrollNext();
56
+ }
57
+ },
58
+ role: "region",
59
+ tabIndex: 0,
60
+ ...carousel(),
61
+ children: [
62
+ /* @__PURE__ */ jsxs(Box, { ...content(), children: [
63
+ /* @__PURE__ */ jsx(Box, { ...viewport(), ref: emblaMainRef, children: /* @__PURE__ */ jsx(Group, { ...slideContainer(), children: images.map((image, index) => /* @__PURE__ */ jsx(
64
+ ProteusImage,
65
+ {
66
+ alt: image.alt,
67
+ src: image.src,
68
+ ...slide()
69
+ },
70
+ index
71
+ )) }) }),
72
+ canScrollPrev && /* @__PURE__ */ jsx(
73
+ Box,
74
+ {
75
+ "aria-label": "Previous image",
76
+ asChild: true,
77
+ onClick: () => emblaMainApi?.scrollPrev(),
78
+ ...navButton({ side: "left" }),
79
+ children: /* @__PURE__ */ jsx("button", { children: /* @__PURE__ */ jsx(IconAngleLeft, {}) })
80
+ }
81
+ ),
82
+ canScrollNext && /* @__PURE__ */ jsx(
83
+ Box,
84
+ {
85
+ "aria-label": "Next image",
86
+ asChild: true,
87
+ onClick: () => emblaMainApi?.scrollNext(),
88
+ ...navButton({ side: "right" }),
89
+ children: /* @__PURE__ */ jsx("button", { children: /* @__PURE__ */ jsx(IconAngleRight, {}) })
90
+ }
91
+ )
92
+ ] }),
93
+ images.length > 1 && /* @__PURE__ */ jsx(Box, { overflow: "hidden", ref: emblaThumbsRef, children: /* @__PURE__ */ jsx(Group, { gap: "12", children: images.map((image, index) => /* @__PURE__ */ jsx(
94
+ Box,
95
+ {
96
+ onClick: () => emblaMainApi?.scrollTo(index),
97
+ ...thumbnail({
98
+ selected: index === selectedIndex
99
+ }),
100
+ children: /* @__PURE__ */ jsx(Box, { asChild: true, objectFit: "cover", size: "full", children: /* @__PURE__ */ jsx("img", { alt: image.alt, src: image.thumb ?? image.src }) })
101
+ },
102
+ index
103
+ )) }) }),
104
+ /* @__PURE__ */ jsxs(Group, { mt: "4", children: [
105
+ images.length > 1 && /* @__PURE__ */ jsxs(
106
+ Text,
107
+ {
108
+ bg: "bg.secondary",
109
+ color: "fg.default",
110
+ fontSize: "sm",
111
+ fontWeight: "500",
112
+ px: "8",
113
+ py: "2",
114
+ rounded: "full",
115
+ children: [
116
+ selectedIndex + 1,
117
+ " / ",
118
+ images.length
119
+ ]
120
+ }
121
+ ),
122
+ images.length > 1 ? /* @__PURE__ */ jsxs(
123
+ Menu,
124
+ {
125
+ options: [
126
+ {
127
+ execute: () => downloadFile(images[selectedIndex].src),
128
+ label: "Download this image"
129
+ },
130
+ {
131
+ execute: () => {
132
+ if (downloadAll) {
133
+ window.open(downloadAll, "_blank");
134
+ } else {
135
+ for (const image of images) {
136
+ void downloadFile(image.src);
137
+ }
138
+ }
139
+ },
140
+ label: "Download all images"
141
+ }
142
+ ],
143
+ children: [
144
+ /* @__PURE__ */ jsx(MenuTrigger, { appearance: "primary", ml: "auto", children: "Download" }),
145
+ /* @__PURE__ */ jsx(MenuContent, { align: "end" })
146
+ ]
147
+ }
148
+ ) : /* @__PURE__ */ jsx(
149
+ Button,
150
+ {
151
+ appearance: "primary",
152
+ ml: "auto",
153
+ onClick: () => downloadFile(images[0].src),
154
+ children: "Download"
155
+ }
156
+ )
157
+ ] })
158
+ ]
159
+ }
160
+ );
161
+ }
162
+ ProteusImageCarousel.displayName = "@optiaxiom/proteus/ProteusImageCarousel";
163
+
164
+ export { ProteusImageCarousel };
@@ -1,9 +1,9 @@
1
- import './../assets/src/proteus-question/ProteusQuestion.css.ts.vanilla-nFTJBsO_.css';
1
+ import './../assets/src/proteus-question/ProteusQuestion.css.ts.vanilla-0cQoAJ0-.css';
2
2
  import { recipe } from '@optiaxiom/react/css-runtime';
3
3
 
4
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
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
6
  var choiceGroup = recipe({base:{flexDirection:'column'}});
7
- var question = recipe({base:[{flexDirection:'column',gap:'16',p:'4'},'ProteusQuestion__8f590p0']});
7
+ var question = recipe({base:[{flexDirection:'column',gap:'16'},'ProteusQuestion__8f590p0']});
8
8
 
9
9
  export { addon, choice, choiceGroup, question };
@@ -45,9 +45,9 @@ function ProteusQuestion({ questions }) {
45
45
  }
46
46
  );
47
47
  lastIndexRef.current = currentIndex;
48
- const item = questionRef.current?.querySelector("[data-selected]") ?? questionRef.current?.querySelector("[tabindex]");
49
- item?.focus();
50
48
  }
49
+ const item = questionRef.current?.querySelector("[data-selected]") ?? questionRef.current?.querySelector("[tabindex]");
50
+ item?.focus();
51
51
  }, [currentIndex]);
52
52
  const otherInputRef = useRef(null);
53
53
  const otherItemRef = useRef(null);
@@ -90,7 +90,7 @@ A: ${value2 || "[Not specified]"}`
90
90
  onKeyDown: (event) => {
91
91
  if (event.key === "Escape") {
92
92
  event.preventDefault();
93
- if (valid) {
93
+ if (type === "multi_select" && valid) {
94
94
  onValueChange(null);
95
95
  } else {
96
96
  onSkip();
@@ -328,28 +328,28 @@ A: ${value2 || "[Not specified]"}`
328
328
  value?.length || 0,
329
329
  " selected"
330
330
  ] }),
331
- (value?.length || 0) > 0 ? /* @__PURE__ */ jsx(
331
+ /* @__PURE__ */ jsx(
332
332
  Button,
333
333
  {
334
- appearance: "primary",
335
- "aria-label": isLast ? "Submit" : "Next",
336
- disabled: !valid,
337
- icon: /* @__PURE__ */ jsx(IconNorth, {}),
338
334
  ml: "auto",
339
335
  onClick: (event) => {
340
336
  event.preventDefault();
341
- onSubmit();
342
- }
337
+ onSkip();
338
+ },
339
+ children: "Skip"
343
340
  }
344
- ) : /* @__PURE__ */ jsx(
341
+ ),
342
+ /* @__PURE__ */ jsx(
345
343
  Button,
346
344
  {
347
- ml: "auto",
345
+ appearance: valid ? "primary" : "default",
346
+ "aria-label": isLast ? "Submit" : "Next",
347
+ disabled: !valid,
348
+ icon: /* @__PURE__ */ jsx(IconNorth, {}),
348
349
  onClick: (event) => {
349
350
  event.preventDefault();
350
- onSkip();
351
- },
352
- children: "Skip"
351
+ onSubmit();
352
+ }
353
353
  }
354
354
  )
355
355
  ]