@react-ui-org/react-ui 0.56.0 → 0.58.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.
- package/.nvmrc +1 -1
- package/dist/react-ui.css +17 -17
- package/dist/react-ui.development.css +2551 -831
- package/dist/react-ui.development.js +106 -66
- package/dist/react-ui.js +1 -1
- package/package.json +13 -2
- package/src/CNAME +1 -0
- package/src/components/Alert/Alert.jsx +5 -6
- package/src/components/Alert/Alert.module.scss +12 -34
- package/src/components/Alert/README.md +28 -15
- package/src/components/Alert/_settings.scss +5 -0
- package/src/components/Alert/_theme.scss +0 -43
- package/src/components/Badge/Badge.jsx +5 -3
- package/src/components/Badge/Badge.module.scss +29 -74
- package/src/components/Badge/README.md +19 -2
- package/src/components/Badge/_settings.scss +8 -0
- package/src/components/Button/Button.jsx +4 -3
- package/src/components/Button/Button.module.scss +183 -2
- package/src/components/Button/README.md +8 -6
- package/src/components/Button/_settings.scss +8 -3
- package/src/components/Button/_theme.scss +0 -3
- package/src/components/Button/_tools.scss +7 -71
- package/src/components/ButtonGroup/ButtonGroup.jsx +2 -2
- package/src/components/Card/Card.jsx +3 -2
- package/src/components/Card/Card.module.scss +13 -33
- package/src/components/Card/CardBody.jsx +1 -1
- package/src/components/Card/CardFooter.jsx +1 -1
- package/src/components/Card/README.md +28 -6
- package/src/components/Card/_settings.scss +5 -0
- package/src/components/Card/_theme.scss +0 -43
- package/src/components/CheckboxField/CheckboxField.jsx +9 -3
- package/src/components/CheckboxField/README.md +110 -5
- package/src/components/FileInputField/FileInputField.jsx +1 -1
- package/src/components/FormLayout/FormLayout.jsx +1 -1
- package/src/components/FormLayout/FormLayoutCustomField.jsx +1 -1
- package/src/components/Grid/Grid.jsx +1 -1
- package/src/components/Grid/Grid.module.scss +9 -2
- package/src/components/Grid/GridSpan.jsx +1 -1
- package/src/components/InputGroup/InputGroup.jsx +2 -2
- package/src/components/InputGroup/InputGroup.module.scss +9 -5
- package/src/components/Modal/Modal.jsx +1 -1
- package/src/components/Modal/ModalBody.jsx +1 -1
- package/src/components/Modal/ModalCloseButton.jsx +3 -5
- package/src/components/Modal/ModalContent.jsx +1 -1
- package/src/components/Modal/ModalFooter.jsx +1 -1
- package/src/components/Modal/ModalHeader.jsx +1 -1
- package/src/components/Modal/ModalTitle.jsx +1 -1
- package/src/components/Modal/README.md +18 -18
- package/src/components/Paper/Paper.jsx +1 -1
- package/src/components/Popover/Popover.jsx +58 -13
- package/src/components/Popover/Popover.module.scss +51 -23
- package/src/components/Popover/PopoverWrapper.jsx +1 -1
- package/src/components/Popover/README.md +60 -3
- package/src/components/Popover/_helpers/cleanPlacementStyle.js +20 -0
- package/src/components/Popover/_theme.scss +4 -2
- package/src/components/Radio/README.md +103 -0
- package/src/components/Radio/Radio.jsx +9 -3
- package/src/components/Radio/Radio.module.scss +4 -0
- package/src/components/ScrollView/ScrollView.jsx +3 -5
- package/src/components/SelectField/README.md +103 -0
- package/src/components/SelectField/SelectField.jsx +9 -3
- package/src/components/Table/Table.jsx +1 -1
- package/src/components/Tabs/Tabs.jsx +1 -1
- package/src/components/Tabs/TabsItem.jsx +1 -1
- package/src/components/Text/Text.jsx +1 -1
- package/src/components/TextArea/TextArea.jsx +1 -1
- package/src/components/TextField/README.md +14 -2
- package/src/components/TextField/TextField.jsx +1 -1
- package/src/components/TextLink/README.md +10 -3
- package/src/components/TextLink/TextLink.jsx +1 -1
- package/src/components/TextLink/_theme.scss +3 -3
- package/src/components/Toggle/README.md +83 -1
- package/src/components/Toggle/Toggle.jsx +9 -3
- package/src/components/Toolbar/Toolbar.jsx +1 -1
- package/src/components/Toolbar/ToolbarGroup.jsx +1 -1
- package/src/components/Toolbar/ToolbarItem.jsx +1 -1
- package/src/components/_helpers/getRootPriorityClassName.js +1 -1
- package/src/components/_helpers/resolveContextOrProp.js +6 -3
- package/src/index.js +3 -2
- package/src/providers/globalProps/GlobalPropsContext.jsx +5 -0
- package/src/providers/globalProps/GlobalPropsProvider.jsx +33 -0
- package/src/providers/globalProps/index.js +3 -0
- package/src/{provider → providers/globalProps}/withGlobalProps.jsx +16 -16
- package/src/providers/translations/TranslationsContext.jsx +6 -0
- package/src/providers/translations/TranslationsProvider.jsx +33 -0
- package/src/providers/translations/index.js +2 -0
- package/src/styles/elements/_links.scss +7 -2
- package/src/styles/settings/_collections.scss +9 -0
- package/src/styles/theme/_form-fields.scss +19 -0
- package/src/styles/theme/_links.scss +4 -3
- package/src/styles/tools/_collections.scss +265 -0
- package/src/styles/tools/_string.scss +5 -2
- package/src/styles/tools/form-fields/_box-field-layout.scss +7 -1
- package/src/styles/tools/form-fields/_foundation.scss +6 -4
- package/src/styles/tools/form-fields/_variants.scss +5 -1
- package/src/theme.scss +66 -1
- package/src/components/Alert/_tools.scss +0 -10
- package/src/components/Button/_base.scss +0 -156
- package/src/components/Button/_priorities.scss +0 -149
- package/src/components/Card/_tools.scss +0 -10
- package/src/provider/RUIContext.jsx +0 -9
- package/src/provider/RUIProvider.jsx +0 -42
- package/src/provider/index.js +0 -3
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
// 1.
|
|
2
|
-
//
|
|
3
|
-
//
|
|
1
|
+
// 1. Hide the popover by default. This is needed because the popover is
|
|
2
|
+
// controlled via CSS with the help of the helper popover. The popover can't
|
|
3
|
+
// be displayed directly, because relative positioning doesn't work with
|
|
4
|
+
// elements on the top-layer, so this CSS hack is needed.
|
|
5
|
+
// 2. Hide the popover helper element.
|
|
6
|
+
// 3. If the popover helper is open, show the actual popover.
|
|
7
|
+
// 4. Reset positioning for controlled variant.
|
|
8
|
+
// 5. Shift Popover so there is space for the arrow between Popover and reference element.
|
|
9
|
+
// 6. Add top offset in case it's not defined by external library.
|
|
4
10
|
|
|
5
11
|
@use "theme";
|
|
6
12
|
|
|
@@ -49,54 +55,76 @@
|
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
57
|
|
|
58
|
+
// Controlled popover
|
|
59
|
+
.controlledPopover {
|
|
60
|
+
display: none; // 1.
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.helper {
|
|
64
|
+
position: fixed; // 2.
|
|
65
|
+
inset: unset;
|
|
66
|
+
top: 0;
|
|
67
|
+
right: 0;
|
|
68
|
+
width: auto;
|
|
69
|
+
height: auto;
|
|
70
|
+
padding: 0;
|
|
71
|
+
border: none;
|
|
72
|
+
background: transparent;
|
|
73
|
+
pointer-events: none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.helper:popover-open ~ .controlledPopover {
|
|
77
|
+
display: block; // 3.
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
// Sides
|
|
53
81
|
.isRootAtTop {
|
|
54
|
-
bottom: 100
|
|
82
|
+
bottom: calc(100% + #{theme.$arrow-gap} - #{theme.$arrow-safe-rendering-overlap});
|
|
55
83
|
}
|
|
56
84
|
|
|
57
85
|
.isRootAtBottom {
|
|
58
|
-
top: 100
|
|
86
|
+
top: calc(100% + #{theme.$arrow-gap} - #{theme.$arrow-safe-rendering-overlap});
|
|
59
87
|
}
|
|
60
88
|
|
|
61
89
|
.isRootAtLeft {
|
|
62
|
-
right: 100
|
|
90
|
+
right: calc(100% + #{theme.$arrow-gap} - #{theme.$arrow-safe-rendering-overlap});
|
|
63
91
|
}
|
|
64
92
|
|
|
65
93
|
.isRootAtRight {
|
|
66
|
-
left: 100
|
|
94
|
+
left: calc(100% + #{theme.$arrow-gap} - #{theme.$arrow-safe-rendering-overlap});
|
|
67
95
|
}
|
|
68
96
|
|
|
69
97
|
// Arrows
|
|
70
98
|
.isRootAtTop > .arrow {
|
|
71
|
-
top: 100
|
|
99
|
+
top: calc(100% - #{theme.$arrow-safe-rendering-overlap});
|
|
72
100
|
}
|
|
73
101
|
|
|
74
102
|
.isRootAtBottom > .arrow {
|
|
75
|
-
bottom: 100
|
|
103
|
+
bottom: calc(100% - #{theme.$arrow-safe-rendering-overlap});
|
|
76
104
|
}
|
|
77
105
|
|
|
78
106
|
.isRootAtLeft > .arrow {
|
|
79
|
-
left: 100
|
|
107
|
+
left: calc(100% - #{theme.$arrow-safe-rendering-overlap});
|
|
80
108
|
}
|
|
81
109
|
|
|
82
110
|
.isRootAtRight > .arrow {
|
|
83
|
-
right: 100
|
|
111
|
+
right: calc(100% - #{theme.$arrow-safe-rendering-overlap});
|
|
84
112
|
}
|
|
85
113
|
|
|
86
114
|
// Side alignments: top
|
|
87
115
|
.isRootAtTop.isRootAtCenter {
|
|
88
116
|
left: 50%;
|
|
89
|
-
transform: translate(-50%,
|
|
117
|
+
transform: translate(-50%, calc(-1 * #{theme.$arrow-height}));
|
|
90
118
|
}
|
|
91
119
|
|
|
92
120
|
.isRootAtTop.isRootAtStart {
|
|
93
121
|
left: 0;
|
|
94
|
-
transform: translate(0,
|
|
122
|
+
transform: translate(0, calc(-1 * #{theme.$arrow-height}));
|
|
95
123
|
}
|
|
96
124
|
|
|
97
125
|
.isRootAtTop.isRootAtEnd {
|
|
98
126
|
right: 0;
|
|
99
|
-
transform: translate(0,
|
|
127
|
+
transform: translate(0, calc(-1 * #{theme.$arrow-height}));
|
|
100
128
|
}
|
|
101
129
|
|
|
102
130
|
.isRootAtTop.isRootAtCenter > .arrow {
|
|
@@ -148,17 +176,17 @@
|
|
|
148
176
|
// Side alignments: left
|
|
149
177
|
.isRootAtLeft.isRootAtCenter {
|
|
150
178
|
top: 50%;
|
|
151
|
-
transform: translate(
|
|
179
|
+
transform: translate(calc(-1 * #{theme.$arrow-height}), -50%);
|
|
152
180
|
}
|
|
153
181
|
|
|
154
182
|
.isRootAtLeft.isRootAtStart {
|
|
155
183
|
top: 0;
|
|
156
|
-
transform: translate(
|
|
184
|
+
transform: translate(calc(-1 * #{theme.$arrow-height}), 0);
|
|
157
185
|
}
|
|
158
186
|
|
|
159
187
|
.isRootAtLeft.isRootAtEnd {
|
|
160
188
|
bottom: 0;
|
|
161
|
-
transform: translate(
|
|
189
|
+
transform: translate(calc(-1 * #{theme.$arrow-height}), 0);
|
|
162
190
|
}
|
|
163
191
|
|
|
164
192
|
.isRootAtLeft.isRootAtCenter > .arrow {
|
|
@@ -212,27 +240,27 @@
|
|
|
212
240
|
.isRootControlled.isRootAtBottom,
|
|
213
241
|
.isRootControlled.isRootAtLeft,
|
|
214
242
|
.isRootControlled.isRootAtRight {
|
|
215
|
-
inset: unset; //
|
|
243
|
+
inset: unset; // 4.
|
|
216
244
|
}
|
|
217
245
|
|
|
218
246
|
.isRootControlled.isRootAtTop {
|
|
219
|
-
transform: translate(0,
|
|
247
|
+
transform: translate(0, calc(-1 * #{theme.$arrow-height})); // 5.
|
|
220
248
|
}
|
|
221
249
|
|
|
222
250
|
.isRootControlled.isRootAtBottom {
|
|
223
|
-
transform: translate(0, #{theme.$arrow-height}); //
|
|
251
|
+
transform: translate(0, #{theme.$arrow-height}); // 5.
|
|
224
252
|
}
|
|
225
253
|
|
|
226
254
|
.isRootControlled.isRootAtLeft {
|
|
227
|
-
transform: translate(
|
|
255
|
+
transform: translate(calc(-1 * #{theme.$arrow-height}), 0); // 5.
|
|
228
256
|
}
|
|
229
257
|
|
|
230
258
|
.isRootControlled.isRootAtRight {
|
|
231
|
-
transform: translate(#{theme.$arrow-height}, 0); //
|
|
259
|
+
transform: translate(#{theme.$arrow-height}, 0); // 5.
|
|
232
260
|
}
|
|
233
261
|
|
|
234
262
|
.isRootControlled.isRootAtLeft.isRootAtStart,
|
|
235
263
|
.isRootControlled.isRootAtRight.isRootAtStart {
|
|
236
|
-
top: 0; //
|
|
264
|
+
top: 0; // 6.
|
|
237
265
|
}
|
|
238
266
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { transferProps } from '../../utils/transferProps';
|
|
5
5
|
import styles from './PopoverWrapper.module.scss';
|
|
6
6
|
|
|
@@ -162,6 +162,29 @@ automatically, including smart position updates to ensure Popover visibility,
|
|
|
162
162
|
we recommend to involve an external library designed specifically for this
|
|
163
163
|
purpose.
|
|
164
164
|
|
|
165
|
+
To position the popover, you need to provide the `placementStyle` prop with the
|
|
166
|
+
style you want to apply to the popover. This prop should only be used to
|
|
167
|
+
position the popover. The allowed props are:
|
|
168
|
+
|
|
169
|
+
- `position`
|
|
170
|
+
- `inset`
|
|
171
|
+
- `inset-inline-start`
|
|
172
|
+
- `inset-inline-end`
|
|
173
|
+
- `inset-block-start`
|
|
174
|
+
- `inset-block-end`
|
|
175
|
+
- `top`
|
|
176
|
+
- `right`
|
|
177
|
+
- `bottom`
|
|
178
|
+
- `left`
|
|
179
|
+
- `translate`
|
|
180
|
+
- `transform-origin`
|
|
181
|
+
|
|
182
|
+
⚠️ [`inset`][mdn-inset] is a shorthand for `top right bottom left`, not for
|
|
183
|
+
`inset-*` properties.
|
|
184
|
+
|
|
185
|
+
As opposed to `top right bottom left` and the `inset` shorthand, `inset-*`
|
|
186
|
+
properties are writing-direction aware.
|
|
187
|
+
|
|
165
188
|
ℹ️ The following example is using external library [Floating UI]. To use
|
|
166
189
|
Floating UI, install it first:
|
|
167
190
|
|
|
@@ -267,10 +290,10 @@ React.createElement(() => {
|
|
|
267
290
|
<Popover
|
|
268
291
|
id="my-advanced-popover"
|
|
269
292
|
placement={finalPlacement}
|
|
270
|
-
|
|
293
|
+
placementStyle={{
|
|
271
294
|
position: strategy,
|
|
272
|
-
top: y
|
|
273
|
-
left: x
|
|
295
|
+
top: `${y}px`,
|
|
296
|
+
left: `${x}px`,
|
|
274
297
|
}}
|
|
275
298
|
ref={floating}
|
|
276
299
|
>
|
|
@@ -284,6 +307,39 @@ React.createElement(() => {
|
|
|
284
307
|
});
|
|
285
308
|
```
|
|
286
309
|
|
|
310
|
+
## Controlled Popover
|
|
311
|
+
|
|
312
|
+
Popover API can be used to control visibility of Popover component. You need to
|
|
313
|
+
set `id` on the trigger element and matching `popoverTargetId` attribute on the
|
|
314
|
+
Popover component. This leverages the browser's Popover API to control the
|
|
315
|
+
popover, automatically closing it when the trigger or the backdrop is pressed.
|
|
316
|
+
|
|
317
|
+
```docoff-react-preview
|
|
318
|
+
React.createElement(() => {
|
|
319
|
+
// All inline styles in this example are for demonstration purposes only.
|
|
320
|
+
return (
|
|
321
|
+
<div
|
|
322
|
+
style={{
|
|
323
|
+
display: 'grid',
|
|
324
|
+
placeContent: 'center',
|
|
325
|
+
minWidth: '20rem',
|
|
326
|
+
minHeight: '10rem',
|
|
327
|
+
}}
|
|
328
|
+
>
|
|
329
|
+
<PopoverWrapper>
|
|
330
|
+
<Button
|
|
331
|
+
label="Want to see a popover? Click me!"
|
|
332
|
+
popovertarget="my-popover-helper"
|
|
333
|
+
/>
|
|
334
|
+
<Popover id="my-popover" popoverTargetId="my-popover-helper">
|
|
335
|
+
Hello there!
|
|
336
|
+
</Popover>
|
|
337
|
+
</PopoverWrapper>
|
|
338
|
+
</div>
|
|
339
|
+
);
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
287
343
|
## Forwarding HTML Attributes
|
|
288
344
|
|
|
289
345
|
In addition to the options below in the [component's API](#api) section, you
|
|
@@ -326,5 +382,6 @@ which enables [Advanced Positioning](#advanced-positioning).
|
|
|
326
382
|
|
|
327
383
|
[div-attributes]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div#attributes
|
|
328
384
|
[Floating UI]: https://floating-ui.com/docs/react-dom
|
|
385
|
+
[mdn-inset]: https://developer.mozilla.org/en-US/docs/Web/CSS/inset
|
|
329
386
|
[React common props]: https://react.dev/reference/react-dom/components/common#common-props
|
|
330
387
|
[ref]: https://reactjs.org/docs/refs-and-the-dom.html
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default (placementStyle) => {
|
|
2
|
+
const validProps = [
|
|
3
|
+
'position',
|
|
4
|
+
'inset',
|
|
5
|
+
'inset-inline-start',
|
|
6
|
+
'inset-inline-end',
|
|
7
|
+
'inset-block-start',
|
|
8
|
+
'inset-block-end',
|
|
9
|
+
'top',
|
|
10
|
+
'right',
|
|
11
|
+
'bottom',
|
|
12
|
+
'left',
|
|
13
|
+
'translate',
|
|
14
|
+
'transform-origin',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
return Object.fromEntries(
|
|
18
|
+
Object.entries(placementStyle).filter(([prop]) => validProps.includes(prop)),
|
|
19
|
+
);
|
|
20
|
+
};
|
|
@@ -11,6 +11,8 @@ $color: var(--rui-Popover__color);
|
|
|
11
11
|
$background-color: var(--rui-Popover__background-color);
|
|
12
12
|
$box-shadow: var(--rui-Popover__box-shadow);
|
|
13
13
|
|
|
14
|
-
$arrow-
|
|
15
|
-
$arrow-
|
|
14
|
+
$arrow-safe-rendering-overlap: 1px;
|
|
15
|
+
$arrow-gap: 1px;
|
|
16
|
+
$arrow-width: calc(1rem + #{$arrow-safe-rendering-overlap * 2});
|
|
17
|
+
$arrow-height: calc($arrow-width / 2); // 1.
|
|
16
18
|
$arrow-corner-offset: 0.75rem;
|
|
@@ -237,6 +237,109 @@ have.
|
|
|
237
237
|
})
|
|
238
238
|
```
|
|
239
239
|
|
|
240
|
+
### Required State
|
|
241
|
+
|
|
242
|
+
The required state indicates that the input is mandatory.
|
|
243
|
+
|
|
244
|
+
```docoff-react-preview
|
|
245
|
+
React.createElement(() => {
|
|
246
|
+
const [fruit, setFruit] = React.useState('apple');
|
|
247
|
+
return (
|
|
248
|
+
<Radio
|
|
249
|
+
label="Your favourite fruit"
|
|
250
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
251
|
+
options={[
|
|
252
|
+
{
|
|
253
|
+
label: 'Apple',
|
|
254
|
+
value: 'apple',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
label: 'Banana',
|
|
258
|
+
value: 'banana',
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
label: 'Grapefruit',
|
|
262
|
+
value: 'grapefruit',
|
|
263
|
+
},
|
|
264
|
+
]}
|
|
265
|
+
value={fruit}
|
|
266
|
+
required
|
|
267
|
+
/>
|
|
268
|
+
);
|
|
269
|
+
})
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Styling the Required State
|
|
273
|
+
|
|
274
|
+
All form fields in React UI can be
|
|
275
|
+
[styled](/docs/customize/theming/forms/#required-state)
|
|
276
|
+
to indicate the required state.
|
|
277
|
+
|
|
278
|
+
However, you may find yourself in a situation where a form field is valid in
|
|
279
|
+
both selected and unselected states, for example to turn on or off a feature.
|
|
280
|
+
If your project uses the label color as the primary means to indicate the
|
|
281
|
+
required state of input fields and the usual asterisk `*` is omitted, you may
|
|
282
|
+
want to keep the label color consistent for both states to avoid confusion.
|
|
283
|
+
|
|
284
|
+
For this edge case, there is the `renderAsRequired` prop:
|
|
285
|
+
|
|
286
|
+
```docoff-react-preview
|
|
287
|
+
React.createElement(() => {
|
|
288
|
+
const [fruit, setFruit] = React.useState('apple');
|
|
289
|
+
const options = [
|
|
290
|
+
{
|
|
291
|
+
label: 'Apple',
|
|
292
|
+
value: 'apple',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
label: 'Banana',
|
|
296
|
+
value: 'banana',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
label: 'Grapefruit',
|
|
300
|
+
value: 'grapefruit',
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
return (
|
|
304
|
+
<React.Fragment>
|
|
305
|
+
<style>
|
|
306
|
+
{`
|
|
307
|
+
.example {
|
|
308
|
+
display: flex;
|
|
309
|
+
flex-wrap: wrap;
|
|
310
|
+
gap: 1rem 0.5rem;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.example--themed-form-fields {
|
|
314
|
+
--rui-FormField__label__color: var(--rui-color-text-secondary);
|
|
315
|
+
--rui-FormField--required__label__color: var(--rui-color-text-primary);
|
|
316
|
+
--rui-FormField--required__sign: '';
|
|
317
|
+
}
|
|
318
|
+
`}
|
|
319
|
+
</style>
|
|
320
|
+
<div class="example example--themed-form-fields">
|
|
321
|
+
<Radio
|
|
322
|
+
label="This field is optional"
|
|
323
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
324
|
+
options={options}
|
|
325
|
+
value={fruit}
|
|
326
|
+
/>
|
|
327
|
+
<Radio
|
|
328
|
+
label="This field is optional but looks like required"
|
|
329
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
330
|
+
options={options}
|
|
331
|
+
value={fruit}
|
|
332
|
+
renderAsRequired
|
|
333
|
+
/>
|
|
334
|
+
</div>
|
|
335
|
+
</React.Fragment>
|
|
336
|
+
);
|
|
337
|
+
})
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
It renders the field as if it was required, but doesn't add the `required`
|
|
341
|
+
attribute to the actual input.
|
|
342
|
+
|
|
240
343
|
### Disabled State
|
|
241
344
|
|
|
242
345
|
It's possible to disable just some options or the whole set.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React, { useContext } from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { classNames } from '../../utils/classNames';
|
|
5
5
|
import { transferProps } from '../../utils/transferProps';
|
|
6
6
|
import { getRootValidationStateClassName } from '../_helpers/getRootValidationStateClassName';
|
|
@@ -16,6 +16,7 @@ export const Radio = ({
|
|
|
16
16
|
label,
|
|
17
17
|
layout,
|
|
18
18
|
options,
|
|
19
|
+
renderAsRequired,
|
|
19
20
|
required,
|
|
20
21
|
validationState,
|
|
21
22
|
validationText,
|
|
@@ -33,7 +34,7 @@ export const Radio = ({
|
|
|
33
34
|
? styles.isRootLayoutHorizontal
|
|
34
35
|
: styles.isRootLayoutVertical,
|
|
35
36
|
disabled && styles.isRootDisabled,
|
|
36
|
-
required && styles.isRootRequired,
|
|
37
|
+
(renderAsRequired || required) && styles.isRootRequired,
|
|
37
38
|
getRootValidationStateClassName(validationState, styles),
|
|
38
39
|
)}
|
|
39
40
|
disabled={disabled}
|
|
@@ -116,6 +117,7 @@ Radio.defaultProps = {
|
|
|
116
117
|
id: undefined,
|
|
117
118
|
isLabelVisible: true,
|
|
118
119
|
layout: 'vertical',
|
|
120
|
+
renderAsRequired: false,
|
|
119
121
|
required: false,
|
|
120
122
|
validationState: null,
|
|
121
123
|
validationText: null,
|
|
@@ -181,7 +183,11 @@ Radio.propTypes = {
|
|
|
181
183
|
]),
|
|
182
184
|
})).isRequired,
|
|
183
185
|
/**
|
|
184
|
-
* If `true`, the input will be required.
|
|
186
|
+
* If `true`, the input will be rendered as if it was required.
|
|
187
|
+
*/
|
|
188
|
+
renderAsRequired: PropTypes.bool,
|
|
189
|
+
/**
|
|
190
|
+
* If `true`, the input will be made and rendered as required, regardless of the `renderAsRequired` prop.
|
|
185
191
|
*/
|
|
186
192
|
required: PropTypes.bool,
|
|
187
193
|
/**
|
|
@@ -6,10 +6,8 @@ import React, {
|
|
|
6
6
|
useRef,
|
|
7
7
|
useState,
|
|
8
8
|
} from 'react';
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
withGlobalProps,
|
|
12
|
-
} from '../../provider';
|
|
9
|
+
import { TranslationsContext } from '../../providers/translations';
|
|
10
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
13
11
|
import { classNames } from '../../utils/classNames';
|
|
14
12
|
import { transferProps } from '../../utils/transferProps';
|
|
15
13
|
import { getElementsPositionDifference } from './_helpers/getElementsPositionDifference';
|
|
@@ -48,7 +46,7 @@ export const ScrollView = React.forwardRef((props, ref) => {
|
|
|
48
46
|
...restProps
|
|
49
47
|
} = props;
|
|
50
48
|
|
|
51
|
-
const
|
|
49
|
+
const translations = useContext(TranslationsContext);
|
|
52
50
|
|
|
53
51
|
const [isAutoScrollInProgress, setIsAutoScrollInProgress] = useState(false);
|
|
54
52
|
const [isScrolledAtStart, setIsScrolledAtStart] = useState(false);
|
|
@@ -592,6 +592,109 @@ React.createElement(() => {
|
|
|
592
592
|
})
|
|
593
593
|
```
|
|
594
594
|
|
|
595
|
+
### Required State
|
|
596
|
+
|
|
597
|
+
The required state indicates that the input is mandatory.
|
|
598
|
+
|
|
599
|
+
```docoff-react-preview
|
|
600
|
+
React.createElement(() => {
|
|
601
|
+
const [fruit, setFruit] = React.useState('apple');
|
|
602
|
+
return (
|
|
603
|
+
<SelectField
|
|
604
|
+
label="Your favourite fruit"
|
|
605
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
606
|
+
options={[
|
|
607
|
+
{
|
|
608
|
+
label: 'Apple',
|
|
609
|
+
value: 'apple',
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
label: 'Banana',
|
|
613
|
+
value: 'banana',
|
|
614
|
+
},
|
|
615
|
+
{
|
|
616
|
+
label: 'Grapefruit',
|
|
617
|
+
value: 'grapefruit',
|
|
618
|
+
},
|
|
619
|
+
]}
|
|
620
|
+
value={fruit}
|
|
621
|
+
required
|
|
622
|
+
/>
|
|
623
|
+
);
|
|
624
|
+
});
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
#### Styling the Required State
|
|
628
|
+
|
|
629
|
+
All form fields in React UI can be
|
|
630
|
+
[styled](/docs/customize/theming/forms/#required-state)
|
|
631
|
+
to indicate the required state.
|
|
632
|
+
|
|
633
|
+
However, you may find yourself in a situation where a form field is valid in
|
|
634
|
+
both selected and unselected states, for example to turn on or off a feature.
|
|
635
|
+
If your project uses the label color as the primary means to indicate the
|
|
636
|
+
required state of input fields and the usual asterisk `*` is omitted, you may
|
|
637
|
+
want to keep the label color consistent for both states to avoid confusion.
|
|
638
|
+
|
|
639
|
+
For this edge case, there is the `renderAsRequired` prop:
|
|
640
|
+
|
|
641
|
+
```docoff-react-preview
|
|
642
|
+
React.createElement(() => {
|
|
643
|
+
const [fruit, setFruit] = React.useState('apple');
|
|
644
|
+
const options = [
|
|
645
|
+
{
|
|
646
|
+
label: 'Apple',
|
|
647
|
+
value: 'apple',
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
label: 'Banana',
|
|
651
|
+
value: 'banana',
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
label: 'Grapefruit',
|
|
655
|
+
value: 'grapefruit',
|
|
656
|
+
},
|
|
657
|
+
];
|
|
658
|
+
return (
|
|
659
|
+
<React.Fragment>
|
|
660
|
+
<style>
|
|
661
|
+
{`
|
|
662
|
+
.example {
|
|
663
|
+
display: flex;
|
|
664
|
+
flex-wrap: wrap;
|
|
665
|
+
gap: 1rem 0.5rem;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.example--themed-form-fields {
|
|
669
|
+
--rui-FormField__label__color: var(--rui-color-text-secondary);
|
|
670
|
+
--rui-FormField--required__label__color: var(--rui-color-text-primary);
|
|
671
|
+
--rui-FormField--required__sign: '';
|
|
672
|
+
}
|
|
673
|
+
`}
|
|
674
|
+
</style>
|
|
675
|
+
<div class="example example--themed-form-fields">
|
|
676
|
+
<SelectField
|
|
677
|
+
label="This field is optional"
|
|
678
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
679
|
+
options={options}
|
|
680
|
+
value={fruit}
|
|
681
|
+
/>
|
|
682
|
+
<SelectField
|
|
683
|
+
label="This field is optional but looks like required"
|
|
684
|
+
onChange={(e) => setFruit(e.target.value)}
|
|
685
|
+
options={options}
|
|
686
|
+
value={fruit}
|
|
687
|
+
renderAsRequired
|
|
688
|
+
/>
|
|
689
|
+
</div>
|
|
690
|
+
</React.Fragment>
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
It renders the field as if it was required, but doesn't add the `required`
|
|
696
|
+
attribute to the actual input.
|
|
697
|
+
|
|
595
698
|
### Disabled State
|
|
596
699
|
|
|
597
700
|
It's possible to disable just some options or the whole input.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React, { useContext } from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { classNames } from '../../utils/classNames';
|
|
5
5
|
import { transferProps } from '../../utils/transferProps';
|
|
6
6
|
import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
|
|
@@ -21,6 +21,7 @@ export const SelectField = React.forwardRef((props, ref) => {
|
|
|
21
21
|
label,
|
|
22
22
|
layout,
|
|
23
23
|
options,
|
|
24
|
+
renderAsRequired,
|
|
24
25
|
required,
|
|
25
26
|
size,
|
|
26
27
|
validationState,
|
|
@@ -43,7 +44,7 @@ export const SelectField = React.forwardRef((props, ref) => {
|
|
|
43
44
|
? styles.isRootLayoutHorizontal
|
|
44
45
|
: styles.isRootLayoutVertical,
|
|
45
46
|
inputGroupContext && styles.isRootGrouped,
|
|
46
|
-
required && styles.isRootRequired,
|
|
47
|
+
(renderAsRequired || required) && styles.isRootRequired,
|
|
47
48
|
getRootSizeClassName(
|
|
48
49
|
resolveContextOrProp(inputGroupContext && inputGroupContext.size, size),
|
|
49
50
|
styles,
|
|
@@ -136,6 +137,7 @@ SelectField.defaultProps = {
|
|
|
136
137
|
id: undefined,
|
|
137
138
|
isLabelVisible: true,
|
|
138
139
|
layout: 'vertical',
|
|
140
|
+
renderAsRequired: false,
|
|
139
141
|
required: false,
|
|
140
142
|
size: 'medium',
|
|
141
143
|
validationState: null,
|
|
@@ -227,7 +229,11 @@ SelectField.propTypes = {
|
|
|
227
229
|
})),
|
|
228
230
|
]).isRequired,
|
|
229
231
|
/**
|
|
230
|
-
* If `true`, the input will be required.
|
|
232
|
+
* If `true`, the input will be rendered as if it was required.
|
|
233
|
+
*/
|
|
234
|
+
renderAsRequired: PropTypes.bool,
|
|
235
|
+
/**
|
|
236
|
+
* If `true`, the input will be made and rendered as required, regardless of the `renderAsRequired` prop.
|
|
231
237
|
*/
|
|
232
238
|
required: PropTypes.bool,
|
|
233
239
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { transferProps } from '../../utils/transferProps';
|
|
5
5
|
import { TableHeaderCell } from './_components/TableHeaderCell';
|
|
6
6
|
import { TableBodyCell } from './_components/TableBodyCell';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { transferProps } from '../../utils/transferProps';
|
|
5
5
|
import styles from './Tabs.module.scss';
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { classNames } from '../../utils/classNames';
|
|
5
5
|
import { transferProps } from '../../utils/transferProps';
|
|
6
6
|
import styles from './TabsItem.module.scss';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { classNames } from '../../utils/classNames';
|
|
5
5
|
import { transferProps } from '../../utils/transferProps';
|
|
6
6
|
import { isChildrenEmpty } from '../_helpers/isChildrenEmpty';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types';
|
|
2
2
|
import React, { useContext } from 'react';
|
|
3
|
-
import { withGlobalProps } from '../../
|
|
3
|
+
import { withGlobalProps } from '../../providers/globalProps';
|
|
4
4
|
import { classNames } from '../../utils/classNames';
|
|
5
5
|
import { transferProps } from '../../utils/transferProps';
|
|
6
6
|
import { getRootSizeClassName } from '../_helpers/getRootSizeClassName';
|