@optiaxiom/proteus 0.1.15 → 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._1kiv8z5;
2
- @layer optiaxiom._1kiv8z5 {
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._1kiv8z5;
2
- @layer optiaxiom._1kiv8z5 {
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._1kiv8z5;
2
- @layer optiaxiom._1kiv8z5 {
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-DIh5iVZ2.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-RKpsJzZl.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-BRuBo7tD.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
  ]
@@ -2715,6 +2715,9 @@ var definitions = {
2715
2715
  {
2716
2716
  $ref: "#/definitions/ProteusImage"
2717
2717
  },
2718
+ {
2719
+ $ref: "#/definitions/ProteusImageCarousel"
2720
+ },
2718
2721
  {
2719
2722
  $ref: "#/definitions/ProteusInput"
2720
2723
  },
@@ -4168,6 +4171,26 @@ var definitions = {
4168
4171
  justifyItems: {
4169
4172
  $ref: "#/definitions/SprinkleProp_justifyItems"
4170
4173
  },
4174
+ lineClamp: {
4175
+ anyOf: [
4176
+ {
4177
+ "const": "2"
4178
+ },
4179
+ {
4180
+ "const": "4"
4181
+ },
4182
+ {
4183
+ "const": "1"
4184
+ },
4185
+ {
4186
+ "const": "3"
4187
+ },
4188
+ {
4189
+ $ref: "#/definitions/ProteusValue"
4190
+ }
4191
+ ],
4192
+ description: "Truncate the text at specific number of lines."
4193
+ },
4171
4194
  m: {
4172
4195
  $ref: "#/definitions/SprinkleProp_m"
4173
4196
  },
@@ -5758,6 +5781,89 @@ var definitions = {
5758
5781
  ],
5759
5782
  type: "object"
5760
5783
  },
5784
+ ProteusImageCarousel: {
5785
+ additionalProperties: false,
5786
+ examples: [
5787
+ {
5788
+ $type: "ImageCarousel",
5789
+ images: [
5790
+ {
5791
+ alt: "Image 1",
5792
+ src: "https://placehold.co/600x400"
5793
+ },
5794
+ {
5795
+ alt: "Image 2",
5796
+ src: "https://placehold.co/600x400"
5797
+ }
5798
+ ]
5799
+ }
5800
+ ],
5801
+ properties: {
5802
+ $type: {
5803
+ "const": "ImageCarousel"
5804
+ },
5805
+ downloadAll: {
5806
+ anyOf: [
5807
+ {
5808
+ $ref: "#/definitions/ProteusValue"
5809
+ },
5810
+ {
5811
+ type: "string"
5812
+ }
5813
+ ],
5814
+ description: "URL to download all images (e.g. as a zip file). Falls back to downloading each image individually."
5815
+ },
5816
+ images: {
5817
+ anyOf: [
5818
+ {
5819
+ description: "Inline array of image objects",
5820
+ items: {
5821
+ additionalProperties: false,
5822
+ properties: {
5823
+ alt: {
5824
+ description: "Alternative text for the image",
5825
+ type: "string"
5826
+ },
5827
+ src: {
5828
+ description: "The image source URL",
5829
+ type: "string"
5830
+ },
5831
+ thumb: {
5832
+ description: "The URL to the image thumbnail. Falls back to src if not provided.",
5833
+ type: "string"
5834
+ }
5835
+ },
5836
+ required: [
5837
+ "src"
5838
+ ],
5839
+ type: "object"
5840
+ },
5841
+ type: "array"
5842
+ },
5843
+ {
5844
+ $ref: "#/definitions/ProteusValue"
5845
+ }
5846
+ ],
5847
+ description: "Array of image data to display in the carousel"
5848
+ },
5849
+ title: {
5850
+ anyOf: [
5851
+ {
5852
+ $ref: "#/definitions/ProteusValue"
5853
+ },
5854
+ {
5855
+ type: "string"
5856
+ }
5857
+ ],
5858
+ description: "Accessible label for the carousel region."
5859
+ }
5860
+ },
5861
+ required: [
5862
+ "$type",
5863
+ "images"
5864
+ ],
5865
+ type: "object"
5866
+ },
5761
5867
  ProteusInput: {
5762
5868
  additionalProperties: false,
5763
5869
  examples: [