@ultraviolet/ui 1.8.4 → 1.10.0

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.
@@ -0,0 +1,151 @@
1
+ import _styled from '@emotion/styled/base';
2
+ import { useRef, useEffect, useCallback } from 'react';
3
+ import { createPortal } from 'react-dom';
4
+ import { MODAL_WIDTH, MODAL_PLACEMENT } from './constants.js';
5
+ import { jsx } from '@emotion/react/jsx-runtime';
6
+
7
+ const StyledBackdrop = /*#__PURE__*/_styled("div", {
8
+ target: "e1cqen9h1"
9
+ })("position:fixed;top:0;right:0;height:0;width:0;overflow:hidden;background-color:", _ref => {
10
+ let {
11
+ theme
12
+ } = _ref;
13
+ return theme.colors.overlay;
14
+ }, ";&[data-open='true']{padding:", _ref2 => {
15
+ let {
16
+ theme
17
+ } = _ref2;
18
+ return theme.space['2'];
19
+ }, ";overflow:auto;display:flex;bottom:0;left:0;height:100%;width:100%;}");
20
+ const StyledDialog = /*#__PURE__*/_styled("dialog", {
21
+ target: "e1cqen9h0"
22
+ })("background-color:", _ref3 => {
23
+ let {
24
+ theme
25
+ } = _ref3;
26
+ return theme.colors.neutral.backgroundWeakElevated;
27
+ }, ";position:relative;border-radius:", _ref4 => {
28
+ let {
29
+ theme
30
+ } = _ref4;
31
+ return theme.radii.default;
32
+ }, ";border:0;padding:", _ref5 => {
33
+ let {
34
+ theme
35
+ } = _ref5;
36
+ return theme.space['3'];
37
+ }, ";width:", MODAL_WIDTH.medium, "px;box-shadow:", _ref6 => {
38
+ let {
39
+ theme
40
+ } = _ref6;
41
+ return theme.shadows.modal;
42
+ }, ";", /*#__PURE__*/Object.entries(MODAL_WIDTH).map(_ref7 => {
43
+ let [size, value] = _ref7;
44
+ return `
45
+ &[data-size="${size}"] {
46
+ width: ${value}px;
47
+ }
48
+ `;
49
+ }), " ", /*#__PURE__*/Object.entries(MODAL_PLACEMENT).map(_ref8 => {
50
+ let [placement, value] = _ref8;
51
+ return `
52
+ &[data-placement="${placement}"] {
53
+ ${value}
54
+ }
55
+ `;
56
+ }), ";");
57
+ const Dialog = _ref9 => {
58
+ let {
59
+ children,
60
+ open,
61
+ placement,
62
+ onClose,
63
+ hideOnClickOutside,
64
+ size,
65
+ id,
66
+ ariaLabel,
67
+ className,
68
+ 'data-testid': dataTestId,
69
+ preventBodyScroll,
70
+ hideOnEsc,
71
+ backdropClassName,
72
+ dialogCss,
73
+ backdropCss
74
+ } = _ref9;
75
+ const containerRef = useRef(document.createElement('div'));
76
+ const onCloseRef = useRef(onClose);
77
+
78
+ // Portal to put the modal in
79
+ useEffect(() => {
80
+ const element = containerRef.current;
81
+ if (open) {
82
+ document.body.appendChild(element);
83
+ }
84
+ return () => {
85
+ if (document.body.contains(element)) {
86
+ document.body.removeChild(element);
87
+ }
88
+ };
89
+ }, [open]);
90
+
91
+ // Save the reassignment of eventHandler in the useEffect below
92
+ useEffect(() => {
93
+ onCloseRef.current = onClose;
94
+ }, [onClose]);
95
+
96
+ // Handle hide on esc press
97
+ useEffect(() => {
98
+ const handleEscPress = event => {
99
+ if (event.key === 'Escape' && hideOnEsc) {
100
+ onCloseRef.current();
101
+ }
102
+ };
103
+ if (open) {
104
+ document.addEventListener('keyup', handleEscPress);
105
+ }
106
+ return () => {
107
+ document.removeEventListener('keyup', handleEscPress);
108
+ };
109
+ }, [open, onCloseRef, hideOnEsc]);
110
+
111
+ // Handle body scroll
112
+ useEffect(() => {
113
+ if (open && preventBodyScroll) {
114
+ document.body.style.overflow = 'hidden';
115
+ } else {
116
+ document.body.style.overflow = 'auto';
117
+ }
118
+ }, [preventBodyScroll, open]);
119
+
120
+ // Stop click to prevent unexpected dialog close
121
+ const stopClick = useCallback(event => {
122
+ event.stopPropagation();
123
+ }, []);
124
+
125
+ // Stop key up : used when having inputs in modals
126
+ const stopKeyUp = useCallback(event => {
127
+ event.stopPropagation();
128
+ }, []);
129
+ return /*#__PURE__*/createPortal(jsx(StyledBackdrop, {
130
+ "data-open": open,
131
+ onClick: hideOnClickOutside ? onClose : undefined,
132
+ className: backdropClassName,
133
+ css: backdropCss,
134
+ "data-testid": dataTestId ? `${dataTestId}-backdrop` : undefined,
135
+ children: jsx(StyledDialog, {
136
+ css: dialogCss,
137
+ onKeyUp: stopKeyUp,
138
+ className: className,
139
+ id: id,
140
+ "data-testid": dataTestId,
141
+ "aria-label": ariaLabel,
142
+ "data-placement": placement,
143
+ "data-size": size,
144
+ open: open,
145
+ onClick: stopClick,
146
+ children: open ? children : null
147
+ })
148
+ }), containerRef.current);
149
+ };
150
+
151
+ export { Dialog };
@@ -0,0 +1,38 @@
1
+ import { useEffect, isValidElement, cloneElement, createRef } from 'react';
2
+
3
+ const Disclosure = _ref => {
4
+ let {
5
+ disclosure,
6
+ handleOpen,
7
+ visible,
8
+ handleClose,
9
+ toggle,
10
+ id
11
+ } = _ref;
12
+ const disclosureRef = /*#__PURE__*/createRef();
13
+ useEffect(() => {
14
+ const element = disclosureRef.current;
15
+ element?.addEventListener('click', handleOpen);
16
+ return () => {
17
+ element?.removeEventListener('click', handleOpen);
18
+ };
19
+ }, [handleOpen, disclosureRef]);
20
+ if (typeof disclosure === 'function') {
21
+ return disclosure({
22
+ visible,
23
+ onClose: handleClose,
24
+ toggle,
25
+ onOpen: handleOpen,
26
+ modalId: id
27
+ });
28
+ }
29
+ if ( /*#__PURE__*/isValidElement(disclosure)) {
30
+ return /*#__PURE__*/cloneElement(disclosure, {
31
+ ...disclosure.props,
32
+ ref: disclosureRef
33
+ });
34
+ }
35
+ return null;
36
+ };
37
+
38
+ export { Disclosure };
@@ -0,0 +1,110 @@
1
+ import _styled from '@emotion/styled/base';
2
+ import { useState, useId, useCallback } from 'react';
3
+ import { Button } from '../Button/index.js';
4
+ import { Dialog } from './Dialog.js';
5
+ import { Disclosure } from './Disclosure.js';
6
+ import { jsxs, Fragment, jsx } from '@emotion/react/jsx-runtime';
7
+
8
+ const StyledContainer = /*#__PURE__*/_styled("div", {
9
+ target: "e227mx40"
10
+ })("position:absolute;top:", _ref => {
11
+ let {
12
+ theme
13
+ } = _ref;
14
+ return theme.space['2'];
15
+ }, ";right:", _ref2 => {
16
+ let {
17
+ theme
18
+ } = _ref2;
19
+ return theme.space['2'];
20
+ }, ";");
21
+ const Modal = _ref3 => {
22
+ let {
23
+ ariaLabel = 'modal',
24
+ id,
25
+ children,
26
+ disclosure,
27
+ hideOnClickOutside = true,
28
+ hideOnEsc = true,
29
+ isClosable = true,
30
+ onClose,
31
+ onBeforeClose,
32
+ opened = false,
33
+ placement = 'center',
34
+ preventBodyScroll = true,
35
+ size,
36
+ className,
37
+ 'data-testid': dataTestId,
38
+ backdropClassName,
39
+ width = 'small',
40
+ customDialogStyles,
41
+ customDialogBackdropStyles
42
+ } = _ref3;
43
+ const [visible, setVisible] = useState(opened);
44
+ const controlId = useId();
45
+ const handleOpen = useCallback(() => {
46
+ setVisible(true);
47
+ }, []);
48
+ const handleClose = useCallback(() => {
49
+ if (onClose) {
50
+ onClose();
51
+ } else {
52
+ const promise = onBeforeClose?.();
53
+ if (promise && 'catch' in promise) {
54
+ promise.catch(undefined);
55
+ }
56
+ setVisible(false);
57
+ }
58
+ }, [onBeforeClose, onClose]);
59
+ const handleToggle = useCallback(() => {
60
+ setVisible(current => !current);
61
+ }, []);
62
+ const finalId = id ?? controlId;
63
+ const finalSize = size ?? width;
64
+ return jsxs(Fragment, {
65
+ children: [jsx(Disclosure, {
66
+ id: finalId,
67
+ handleOpen: handleOpen,
68
+ disclosure: disclosure,
69
+ handleClose: handleClose,
70
+ visible: visible,
71
+ toggle: handleToggle
72
+ }), jsx(Dialog, {
73
+ open: visible,
74
+ placement: placement,
75
+ size: finalSize,
76
+ ariaLabel: ariaLabel,
77
+ hideOnClickOutside: hideOnClickOutside,
78
+ hideOnEsc: hideOnEsc,
79
+ preventBodyScroll: preventBodyScroll,
80
+ onClose: handleClose,
81
+ className: className,
82
+ backdropClassName: backdropClassName,
83
+ "data-testid": dataTestId,
84
+ id: finalId,
85
+ dialogCss: customDialogStyles,
86
+ backdropCss: customDialogBackdropStyles,
87
+ children: jsxs(Fragment, {
88
+ children: [typeof children === 'function' ? children({
89
+ visible,
90
+ onClose: handleClose,
91
+ onOpen: handleOpen,
92
+ toggle: handleToggle,
93
+ modalId: finalId
94
+ }) : children, jsx(StyledContainer, {
95
+ children: isClosable ? jsx(Button, {
96
+ "data-testid": dataTestId ? `${dataTestId}-close-button` : undefined,
97
+ onClick: handleClose,
98
+ variant: "ghost",
99
+ size: "small",
100
+ icon: "close",
101
+ sentiment: "neutral",
102
+ "aria-label": "close"
103
+ }) : null
104
+ })]
105
+ })
106
+ })]
107
+ });
108
+ };
109
+
110
+ export { Modal };
@@ -0,0 +1,50 @@
1
+ const MODAL_WIDTH = {
2
+ large: 850,
3
+ medium: 708,
4
+ small: 616,
5
+ xsmall: 400,
6
+ xxsmall: 360
7
+ };
8
+ const MODAL_PLACEMENT = {
9
+ bottom: `
10
+ margin: auto;
11
+ margin-bottom: 0;
12
+ `,
13
+ 'bottom-left': `
14
+ margin: auto;
15
+ margin-left: 0;
16
+ margin-bottom: 0;
17
+ `,
18
+ 'bottom-right': `
19
+ margin: auto;
20
+ margin-right: 0;
21
+ margin-bottom: 0;
22
+ `,
23
+ center: `
24
+ margin: auto;
25
+ `,
26
+ left: `
27
+ margin: auto;
28
+ margin-left: 0;
29
+ `,
30
+ right: `
31
+ margin: auto;
32
+ margin-right: 0;
33
+ `,
34
+ top: `
35
+ margin: auto;
36
+ margin-top: 0px;
37
+ `,
38
+ 'top-left': `
39
+ margin: auto;
40
+ margin-left: 0;
41
+ margin-top: 0;
42
+ `,
43
+ 'top-right': `
44
+ margin: auto;
45
+ margin-right: 0;
46
+ margin-top: 0;
47
+ `
48
+ };
49
+
50
+ export { MODAL_PLACEMENT, MODAL_WIDTH };
@@ -0,0 +1,131 @@
1
+ import _styled from '@emotion/styled/base';
2
+ import { Icon } from '@ultraviolet/icons';
3
+ import { useMemo, createContext, useContext } from 'react';
4
+ import { Radio } from '../Radio/index.js';
5
+ import { Stack } from '../Stack/index.js';
6
+ import { Text } from '../Text/index.js';
7
+ import { jsx, jsxs } from '@emotion/react/jsx-runtime';
8
+
9
+ function _EMOTION_STRINGIFIED_CSS_ERROR__() { return "You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop)."; }
10
+ const RadioGroupContext = /*#__PURE__*/createContext(undefined);
11
+ const RadioGroupRadio = _ref => {
12
+ let {
13
+ onFocus,
14
+ onBlur,
15
+ disabled,
16
+ error,
17
+ name,
18
+ value,
19
+ label,
20
+ helper,
21
+ className,
22
+ autoFocus,
23
+ onKeyDown,
24
+ 'data-testid': dataTestId
25
+ } = _ref;
26
+ const context = useContext(RadioGroupContext);
27
+ if (!context) {
28
+ throw new Error('RadioGroup.Radio can only be used inside a RadioGroup');
29
+ }
30
+ const {
31
+ groupName,
32
+ onChange,
33
+ groupValue
34
+ } = context;
35
+ const radioName = `${groupName}-${name ?? ''}`;
36
+ return jsx(Radio, {
37
+ onChange: onChange,
38
+ checked: `${groupName}-${groupValue}` === radioName,
39
+ onFocus: onFocus,
40
+ onBlur: onBlur,
41
+ disabled: disabled,
42
+ error: error,
43
+ name: radioName,
44
+ value: value,
45
+ label: label,
46
+ helper: helper,
47
+ className: className,
48
+ autoFocus: autoFocus,
49
+ onKeyDown: onKeyDown,
50
+ "data-testid": dataTestId
51
+ });
52
+ };
53
+ const FieldSet = /*#__PURE__*/_styled("fieldset", {
54
+ target: "e15shfap1"
55
+ })(process.env.NODE_ENV === "production" ? {
56
+ name: "7o2an9",
57
+ styles: "border:none;padding:0;margin:0"
58
+ } : {
59
+ name: "7o2an9",
60
+ styles: "border:none;padding:0;margin:0",
61
+ toString: _EMOTION_STRINGIFIED_CSS_ERROR__
62
+ });
63
+ const StyledRequiredIcon = /*#__PURE__*/_styled(Icon, {
64
+ target: "e15shfap0"
65
+ })(process.env.NODE_ENV === "production" ? {
66
+ name: "1nglpc5",
67
+ styles: "vertical-align:super"
68
+ } : {
69
+ name: "1nglpc5",
70
+ styles: "vertical-align:super",
71
+ toString: _EMOTION_STRINGIFIED_CSS_ERROR__
72
+ });
73
+ const RadioGroup = _ref2 => {
74
+ let {
75
+ legend,
76
+ value,
77
+ className,
78
+ helper,
79
+ error,
80
+ direction = 'column',
81
+ children,
82
+ onChange,
83
+ name,
84
+ required = false
85
+ } = _ref2;
86
+ const contextValue = useMemo(() => ({
87
+ groupName: name,
88
+ groupValue: value,
89
+ onChange,
90
+ required
91
+ }), [name, value, onChange, required]);
92
+ return jsx(RadioGroupContext.Provider, {
93
+ value: contextValue,
94
+ children: jsxs(Stack, {
95
+ gap: 1,
96
+ children: [jsx(FieldSet, {
97
+ className: className,
98
+ children: jsxs(Stack, {
99
+ gap: 1.5,
100
+ children: [jsxs(Text, {
101
+ as: "legend",
102
+ variant: "bodyStrong",
103
+ children: [legend, "\xA0", required ? jsx(StyledRequiredIcon, {
104
+ name: "asterisk",
105
+ color: "danger",
106
+ size: 8
107
+ }) : null]
108
+ }), jsx(Stack, {
109
+ gap: direction === 'column' ? 1 : 2,
110
+ direction: direction,
111
+ children: children
112
+ })]
113
+ })
114
+ }), helper ? jsx(Text, {
115
+ as: "p",
116
+ variant: "bodySmall",
117
+ prominence: "weak",
118
+ children: helper
119
+ }) : null, error ? jsx(Text, {
120
+ as: "p",
121
+ variant: "bodySmall",
122
+ sentiment: "danger",
123
+ prominence: "weak",
124
+ children: error
125
+ }) : null]
126
+ })
127
+ });
128
+ };
129
+ RadioGroup.Radio = RadioGroupRadio;
130
+
131
+ export { RadioGroup };
@@ -1,8 +1,11 @@
1
1
  import _styled from '@emotion/styled/base';
2
2
  import { Icon } from '@ultraviolet/icons';
3
3
  import { forwardRef, useState, useId, useCallback, useEffect } from 'react';
4
+ import { Row } from '../Row/index.js';
5
+ import { Stack } from '../Stack/index.js';
6
+ import { Text } from '../Text/index.js';
4
7
  import { Tooltip } from '../Tooltip/index.js';
5
- import { jsx, jsxs, Fragment } from '@emotion/react/jsx-runtime';
8
+ import { jsx, jsxs } from '@emotion/react/jsx-runtime';
6
9
 
7
10
  function _EMOTION_STRINGIFIED_CSS_ERROR__() { return "You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop)."; }
8
11
  const SIZES = {
@@ -39,65 +42,75 @@ const StyledToggle = /*#__PURE__*/_styled("div", {
39
42
  size
40
43
  } = _ref4;
41
44
  return SIZES[size].height;
42
- }, "px;&:after{content:'';position:absolute;top:", _ref5 => {
45
+ }, "px;&:hover{background-color:", _ref5 => {
43
46
  let {
44
- size
47
+ theme
45
48
  } = _ref5;
46
- return SIZES[size].height / 2 - SIZES[size].ball / 2;
47
- }, "px;left:5px;width:", _ref6 => {
49
+ return theme.colors.neutral.backgroundStrongHover;
50
+ }, ";}&:after{content:'';position:absolute;top:", _ref6 => {
48
51
  let {
49
52
  size
50
53
  } = _ref6;
51
- return SIZES[size].ball;
52
- }, "px;height:", _ref7 => {
54
+ return SIZES[size].height / 2 - SIZES[size].ball / 2;
55
+ }, "px;left:5px;width:", _ref7 => {
53
56
  let {
54
57
  size
55
58
  } = _ref7;
56
59
  return SIZES[size].ball;
57
- }, "px;border-radius:", _ref8 => {
60
+ }, "px;height:", _ref8 => {
58
61
  let {
59
- theme
62
+ size
60
63
  } = _ref8;
61
- return theme.radii.circle;
62
- }, ";background-color:", _ref9 => {
64
+ return SIZES[size].ball;
65
+ }, "px;border-radius:", _ref9 => {
63
66
  let {
64
67
  theme
65
68
  } = _ref9;
66
- return theme.colors.neutral.background;
67
- }, ";transition:all 300ms;}&:focus-within,&:focus{box-shadow:", _ref10 => {
69
+ return theme.radii.circle;
70
+ }, ";background-color:", _ref10 => {
68
71
  let {
69
72
  theme
70
73
  } = _ref10;
71
- return theme.shadows.focusPrimary;
72
- }, ";}&[data-disabled='false']:active:after{width:", _ref11 => {
74
+ return theme.colors.neutral.background;
75
+ }, ";transition:all 300ms;}&:focus-within,&:focus{box-shadow:", _ref11 => {
73
76
  let {
74
- size
77
+ theme
75
78
  } = _ref11;
79
+ return theme.shadows.focusNeutral;
80
+ }, ";}&[data-disabled='false']:active:after{width:", _ref12 => {
81
+ let {
82
+ size
83
+ } = _ref12;
76
84
  return SIZES[size].ball * 1.3775;
77
- }, "px;}&[data-checked='true']{color:", _ref12 => {
85
+ }, "px;}&[data-checked='true']{color:", _ref13 => {
78
86
  let {
79
87
  theme
80
- } = _ref12;
88
+ } = _ref13;
81
89
  return theme.colors.neutral.textStrong;
82
- }, ";background-color:", _ref13 => {
90
+ }, ";background-color:", _ref14 => {
83
91
  let {
84
92
  theme
85
- } = _ref13;
93
+ } = _ref14;
86
94
  return theme.colors.primary.backgroundStrong;
87
- }, ";&:after{left:calc(100% - 5px);transform:translateX(-100%);}&:focus-within,&:focus{box-shadow:", _ref14 => {
95
+ }, ";&:hover{background-color:", _ref15 => {
88
96
  let {
89
97
  theme
90
- } = _ref14;
98
+ } = _ref15;
99
+ return theme.colors.primary.backgroundStrongHover;
100
+ }, ";}&:after{left:calc(100% - 5px);transform:translateX(-100%);}&:focus-within,&:focus{box-shadow:", _ref16 => {
101
+ let {
102
+ theme
103
+ } = _ref16;
91
104
  return theme.shadows.focusPrimary;
92
- }, ";}}&[data-disabled='true']{background:", _ref15 => {
105
+ }, ";}}&[data-disabled='true']{background:", _ref17 => {
93
106
  let {
94
107
  theme
95
- } = _ref15;
108
+ } = _ref17;
96
109
  return theme.colors.neutral.backgroundStrongDisabled;
97
- }, ";&[data-checked='true']{background:", _ref16 => {
110
+ }, ";&[data-checked='true']{background:", _ref18 => {
98
111
  let {
99
112
  theme
100
- } = _ref16;
113
+ } = _ref18;
101
114
  return theme.colors.primary.backgroundStrongDisabled;
102
115
  }, ";}}");
103
116
  const StyledCheckbox = /*#__PURE__*/_styled("input", {
@@ -112,20 +125,25 @@ const StyledCheckbox = /*#__PURE__*/_styled("input", {
112
125
  });
113
126
  const StyledLabel = /*#__PURE__*/_styled("label", {
114
127
  target: "e1wstm610"
115
- })("display:flex;gap:", _ref17 => {
128
+ })("display:flex;gap:", _ref19 => {
116
129
  let {
117
130
  theme
118
- } = _ref17;
131
+ } = _ref19;
119
132
  return theme.space['1'];
120
- }, ";align-items:center;width:fit-content;cursor:pointer;&:active ", StyledToggle, "[data-disabled='false']:after{width:", _ref18 => {
133
+ }, ";align-items:start;width:fit-content;cursor:pointer;flex-direction:", _ref20 => {
134
+ let {
135
+ labelPosition
136
+ } = _ref20;
137
+ return labelPosition === 'left' ? 'row' : 'row-reverse';
138
+ }, ";&:active ", StyledToggle, "[data-disabled='false']:after{width:", _ref21 => {
121
139
  let {
122
140
  size
123
- } = _ref18;
141
+ } = _ref21;
124
142
  return SIZES[size].ball * 1.3775;
125
- }, "px;}&[aria-disabled='true']{cursor:not-allowed;color:", _ref19 => {
143
+ }, "px;}&[aria-disabled='true']{cursor:not-allowed;color:", _ref22 => {
126
144
  let {
127
145
  theme
128
- } = _ref19;
146
+ } = _ref22;
129
147
  return theme.colors.neutral.textDisabled;
130
148
  }, ";}");
131
149
  const RequiredIcon = () => jsx("sup", {
@@ -135,7 +153,7 @@ const RequiredIcon = () => jsx("sup", {
135
153
  color: "danger"
136
154
  })
137
155
  });
138
- const Toggle = /*#__PURE__*/forwardRef((_ref20, ref) => {
156
+ const Toggle = /*#__PURE__*/forwardRef((_ref23, ref) => {
139
157
  let {
140
158
  checked = false,
141
159
  disabled = false,
@@ -146,10 +164,11 @@ const Toggle = /*#__PURE__*/forwardRef((_ref20, ref) => {
146
164
  tooltip,
147
165
  labelPosition = 'right',
148
166
  label,
167
+ helper,
149
168
  required,
150
169
  className,
151
170
  'data-testid': dataTestId
152
- } = _ref20;
171
+ } = _ref23;
153
172
  const [state, setState] = useState(checked);
154
173
  const uniqueId = useId();
155
174
  const onLocalChange = useCallback(event => {
@@ -166,9 +185,22 @@ const Toggle = /*#__PURE__*/forwardRef((_ref20, ref) => {
166
185
  onClick: evt => evt.stopPropagation(),
167
186
  className: className,
168
187
  "data-testid": dataTestId,
169
- children: [label && labelPosition === 'left' ? jsxs(Fragment, {
170
- children: [label, required ? jsx(RequiredIcon, {}) : null]
171
- }) : null, jsx(StyledToggle, {
188
+ labelPosition: labelPosition,
189
+ children: [jsxs(Stack, {
190
+ gap: 0.25,
191
+ alignItems: "baseline",
192
+ children: [label ? jsxs(Row, {
193
+ templateColumns: "auto 1fr",
194
+ gap: 1,
195
+ alignItems: "center",
196
+ children: [label, required ? jsx(RequiredIcon, {}) : null]
197
+ }) : null, helper ? jsx(Text, {
198
+ as: "p",
199
+ variant: "bodySmall",
200
+ prominence: "weak",
201
+ children: helper
202
+ }) : null]
203
+ }), jsx(StyledToggle, {
172
204
  size: size,
173
205
  "data-checked": state,
174
206
  "data-disabled": disabled,
@@ -182,9 +214,7 @@ const Toggle = /*#__PURE__*/forwardRef((_ref20, ref) => {
182
214
  type: "checkbox",
183
215
  ref: ref
184
216
  })
185
- }), label && labelPosition === 'right' ? jsxs(Fragment, {
186
- children: [label, required ? jsx(RequiredIcon, {}) : null]
187
- }) : null]
217
+ })]
188
218
  })
189
219
  });
190
220
  });
package/dist/src/index.js CHANGED
@@ -28,7 +28,7 @@ export { List } from './components/List/index.js';
28
28
  export { Loader } from './components/Loader/index.js';
29
29
  export { Menu } from './components/Menu/index.js';
30
30
  export { Meter } from './components/Meter/Meter.js';
31
- export { Modal } from './components/Modal/index.js';
31
+ export { Modal } from './components/Modal/Modal.js';
32
32
  export { Notice } from './components/Notice/index.js';
33
33
  export { NumberInput } from './components/NumberInput/index.js';
34
34
  export { Pagination } from './components/Pagination/index.js';
@@ -61,4 +61,5 @@ export { ToastContainer, toast } from './components/Toaster/index.js';
61
61
  export { Toggle } from './components/Toggle/index.js';
62
62
  export { Tooltip } from './components/Tooltip/index.js';
63
63
  export { VerificationCode } from './components/VerificationCode/index.js';
64
+ export { RadioGroup } from './components/RadioGroup/index.js';
64
65
  export { Icon } from '@ultraviolet/icons';