@bspk/ui 1.3.20 → 1.3.22
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/dist/components/Accordion/Accordion.js +1 -1
- package/dist/components/Accordion/Accordion.js.map +1 -1
- package/dist/components/Accordion/accordion.css +19 -12
- package/dist/components/Accordion/accordion.css.js +19 -12
- package/dist/components/BottomNavigation/BottomNavigation.d.ts +0 -1
- package/dist/components/BottomNavigation/BottomNavigation.js +0 -1
- package/dist/components/BottomNavigation/BottomNavigation.js.map +1 -1
- package/dist/components/BottomNavigation/bottom-navigation.css +1 -0
- package/dist/components/BottomNavigation/bottom-navigation.css.js +1 -0
- package/dist/components/Button/button.css +4 -0
- package/dist/components/Button/button.css.js +4 -0
- package/dist/components/Checkbox/Checkbox.js +3 -3
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroup.d.ts +4 -5
- package/dist/components/CheckboxGroup/CheckboxGroup.js +8 -13
- package/dist/components/CheckboxGroup/CheckboxGroup.js.map +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroupExample.js +5 -0
- package/dist/components/CheckboxGroup/CheckboxGroupExample.js.map +1 -1
- package/dist/components/CheckboxGroup/checkbox-group.css +2 -0
- package/dist/components/CheckboxGroup/checkbox-group.css.js +2 -0
- package/dist/components/CheckboxGroupField/CheckboxGroupField.d.ts +3 -3
- package/dist/components/CheckboxGroupField/CheckboxGroupField.js +6 -4
- package/dist/components/CheckboxGroupField/CheckboxGroupField.js.map +1 -1
- package/dist/components/ChipGroup/ChipGroup.d.ts +0 -1
- package/dist/components/ChipGroup/ChipGroup.js +0 -1
- package/dist/components/ChipGroup/ChipGroup.js.map +1 -1
- package/dist/components/ChipGroup/chip-group.css +3 -0
- package/dist/components/ChipGroup/chip-group.css.js +3 -0
- package/dist/components/DatePicker/DatePicker.d.ts +4 -8
- package/dist/components/DatePicker/DatePicker.js +6 -18
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DatePicker/DatePickerExample.js +3 -3
- package/dist/components/DatePicker/DatePickerExample.js.map +1 -1
- package/dist/components/DatePickerField/DatePickerField.d.ts +4 -4
- package/dist/components/DatePickerField/DatePickerField.js +6 -4
- package/dist/components/DatePickerField/DatePickerField.js.map +1 -1
- package/dist/components/Dialog/Dialog.d.ts +2 -3
- package/dist/components/Dialog/Dialog.js +2 -3
- package/dist/components/Dialog/Dialog.js.map +1 -1
- package/dist/components/Fab/Fab.d.ts +8 -5
- package/dist/components/Fab/Fab.js +8 -5
- package/dist/components/Fab/Fab.js.map +1 -1
- package/dist/components/Fab/fab.css +1 -0
- package/dist/components/Fab/fab.css.js +1 -0
- package/dist/components/Field/Field.d.ts +32 -18
- package/dist/components/Field/Field.js +8 -38
- package/dist/components/Field/Field.js.map +1 -1
- package/dist/components/Field/FieldExample.d.ts +1 -0
- package/dist/components/Field/FieldExample.js +10 -3
- package/dist/components/Field/FieldExample.js.map +1 -1
- package/dist/components/Field/Fieldset.d.ts +40 -0
- package/dist/components/Field/Fieldset.js +44 -0
- package/dist/components/Field/Fieldset.js.map +1 -0
- package/dist/components/Field/field.css +5 -7
- package/dist/components/Field/field.css.js +5 -7
- package/dist/components/Field/index.d.ts +1 -3
- package/dist/components/Field/index.js +1 -3
- package/dist/components/Field/index.js.map +1 -1
- package/dist/components/Field/utils.d.ts +8 -38
- package/dist/components/Field/utils.js +6 -33
- package/dist/components/Field/utils.js.map +1 -1
- package/dist/components/FileUpload/FileUpload.js +1 -1
- package/dist/components/FileUpload/FileUpload.js.map +1 -1
- package/dist/components/FileUploadItem/FileUploadItem.js +1 -1
- package/dist/components/FileUploadItem/FileUploadItem.js.map +1 -1
- package/dist/components/Flex/Flex.d.ts +11 -7
- package/dist/components/Flex/Flex.js +12 -8
- package/dist/components/Flex/Flex.js.map +1 -1
- package/dist/components/InlineAlert/InlineAlert.d.ts +2 -2
- package/dist/components/InlineAlert/InlineAlert.js +2 -2
- package/dist/components/InlineAlert/InlineAlert.js.map +1 -1
- package/dist/components/Input/Input.d.ts +60 -15
- package/dist/components/Input/Input.js +42 -25
- package/dist/components/Input/Input.js.map +1 -1
- package/dist/components/Input/InputExample.js +4 -4
- package/dist/components/Input/InputExample.js.map +1 -1
- package/dist/components/Input/index.d.ts +0 -1
- package/dist/components/Input/index.js +0 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/InputField/InputField.d.ts +4 -4
- package/dist/components/InputField/InputField.js +6 -4
- package/dist/components/InputField/InputField.js.map +1 -1
- package/dist/components/InputNumber/InputNumber.d.ts +8 -7
- package/dist/components/InputNumber/InputNumber.js +9 -16
- package/dist/components/InputNumber/InputNumber.js.map +1 -1
- package/dist/components/InputNumber/InputNumberExample.js +3 -3
- package/dist/components/InputNumber/InputNumberExample.js.map +1 -1
- package/dist/components/InputNumber/input-number.css +1 -0
- package/dist/components/InputNumber/input-number.css.js +1 -0
- package/dist/components/InputNumberField/InputNumberField.d.ts +4 -4
- package/dist/components/InputNumberField/InputNumberField.js +6 -4
- package/dist/components/InputNumberField/InputNumberField.js.map +1 -1
- package/dist/components/InputPhone/InputPhone.d.ts +8 -8
- package/dist/components/InputPhone/InputPhone.js +12 -18
- package/dist/components/InputPhone/InputPhone.js.map +1 -1
- package/dist/components/InputPhone/InputPhoneExample.js +3 -3
- package/dist/components/InputPhone/InputPhoneExample.js.map +1 -1
- package/dist/components/InputPhone/input-phone.css +1 -0
- package/dist/components/InputPhone/input-phone.css.js +1 -0
- package/dist/components/InputPhoneField/InputPhoneField.d.ts +4 -4
- package/dist/components/InputPhoneField/InputPhoneField.js +6 -4
- package/dist/components/InputPhoneField/InputPhoneField.js.map +1 -1
- package/dist/components/ListItem/list-item.css +1 -0
- package/dist/components/ListItem/list-item.css.js +1 -0
- package/dist/components/Modal/Modal.js +22 -26
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Modal/ModalExample.js +4 -1
- package/dist/components/Modal/ModalExample.js.map +1 -1
- package/dist/components/Modal/modal.css +4 -2
- package/dist/components/Modal/modal.css.js +4 -2
- package/dist/components/OTPInput/otp-input.css +1 -0
- package/dist/components/OTPInput/otp-input.css.js +1 -0
- package/dist/components/Pagination/Pagination.js +2 -2
- package/dist/components/Pagination/Pagination.js.map +1 -1
- package/dist/components/Password/Password.d.ts +9 -7
- package/dist/components/Password/Password.js +13 -17
- package/dist/components/Password/Password.js.map +1 -1
- package/dist/components/Password/PasswordExample.js +3 -3
- package/dist/components/Password/PasswordExample.js.map +1 -1
- package/dist/components/PasswordField/PasswordField.d.ts +4 -4
- package/dist/components/PasswordField/PasswordField.js +6 -4
- package/dist/components/PasswordField/PasswordField.js.map +1 -1
- package/dist/components/Popover/Popover.d.ts +0 -1
- package/dist/components/Popover/Popover.js +0 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/RadioGroup/RadioGroup.d.ts +1 -2
- package/dist/components/RadioGroup/RadioGroup.js +5 -11
- package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/components/RadioGroupField/RadioGroupField.d.ts +3 -3
- package/dist/components/RadioGroupField/RadioGroupField.js +6 -4
- package/dist/components/RadioGroupField/RadioGroupField.js.map +1 -1
- package/dist/components/SearchBar/SearchBar.d.ts +0 -1
- package/dist/components/SearchBar/SearchBar.js +0 -1
- package/dist/components/SearchBar/SearchBar.js.map +1 -1
- package/dist/components/SegmentedControl/SegmentedControl.d.ts +0 -1
- package/dist/components/SegmentedControl/SegmentedControl.js +0 -1
- package/dist/components/SegmentedControl/SegmentedControl.js.map +1 -1
- package/dist/components/Select/Select.d.ts +7 -6
- package/dist/components/Select/Select.js +10 -15
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectExample.js +3 -3
- package/dist/components/Select/SelectExample.js.map +1 -1
- package/dist/components/SelectField/SelectField.d.ts +4 -4
- package/dist/components/SelectField/SelectField.js +6 -4
- package/dist/components/SelectField/SelectField.js.map +1 -1
- package/dist/components/Slider/Slider.d.ts +1 -2
- package/dist/components/Slider/Slider.js +1 -2
- package/dist/components/Slider/Slider.js.map +1 -1
- package/dist/components/Snackbar/Snackbar.d.ts +0 -1
- package/dist/components/Snackbar/Snackbar.js +0 -1
- package/dist/components/Snackbar/Snackbar.js.map +1 -1
- package/dist/components/Switch/Switch.d.ts +0 -1
- package/dist/components/Switch/Switch.js +0 -1
- package/dist/components/Switch/Switch.js.map +1 -1
- package/dist/components/TabGroup/TabGroup.d.ts +0 -1
- package/dist/components/TabGroup/TabGroup.js +0 -1
- package/dist/components/TabGroup/TabGroup.js.map +1 -1
- package/dist/components/TabList/TabList.d.ts +0 -1
- package/dist/components/TabList/TabList.js +0 -1
- package/dist/components/TabList/TabList.js.map +1 -1
- package/dist/components/TabList/tab-list.css +1 -0
- package/dist/components/TabList/tab-list.css.js +1 -0
- package/dist/components/Table/table.css +2 -1
- package/dist/components/Table/table.css.js +2 -1
- package/dist/components/Textarea/Textarea.d.ts +4 -7
- package/dist/components/Textarea/Textarea.js +5 -16
- package/dist/components/Textarea/Textarea.js.map +1 -1
- package/dist/components/Textarea/TextareaExample.js +3 -3
- package/dist/components/Textarea/TextareaExample.js.map +1 -1
- package/dist/components/TextareaField/TextareaField.d.ts +4 -4
- package/dist/components/TextareaField/TextareaField.js +6 -4
- package/dist/components/TextareaField/TextareaField.js.map +1 -1
- package/dist/components/TimePicker/TimePicker.d.ts +8 -6
- package/dist/components/TimePicker/TimePicker.js +10 -14
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/TimePicker/TimePickerExample.js +3 -3
- package/dist/components/TimePicker/TimePickerExample.js.map +1 -1
- package/dist/components/TimePickerField/TimePickerField.d.ts +4 -4
- package/dist/components/TimePickerField/TimePickerField.js +6 -4
- package/dist/components/TimePickerField/TimePickerField.js.map +1 -1
- package/dist/types/common.d.ts +2 -0
- package/dist/types/common.js.map +1 -1
- package/dist/utils/demo.js +1 -1
- package/dist/utils/demo.js.map +1 -1
- package/package.json +3 -4
- package/src/components/Accordion/Accordion.tsx +2 -2
- package/src/components/Accordion/accordion.scss +10 -1
- package/src/components/BottomNavigation/BottomNavigation.tsx +0 -1
- package/src/components/BottomNavigation/bottom-navigation.scss +1 -0
- package/src/components/Button/button.scss +4 -0
- package/src/components/Checkbox/Checkbox.tsx +3 -3
- package/src/components/CheckboxGroup/CheckboxGroup.tsx +37 -52
- package/src/components/CheckboxGroup/CheckboxGroupExample.tsx +6 -0
- package/src/components/CheckboxGroup/checkbox-group.scss +2 -0
- package/src/components/CheckboxGroupField/CheckboxGroupField.tsx +15 -11
- package/src/components/ChipGroup/ChipGroup.tsx +0 -1
- package/src/components/ChipGroup/chip-group.scss +4 -0
- package/src/components/DatePicker/DatePicker.tsx +9 -20
- package/src/components/DatePicker/DatePickerExample.tsx +3 -5
- package/src/components/DatePickerField/DatePickerField.rtl.test.tsx +1 -1
- package/src/components/DatePickerField/DatePickerField.tsx +10 -6
- package/src/components/Dialog/Dialog.tsx +2 -3
- package/src/components/Fab/Fab.tsx +8 -5
- package/src/components/Fab/fab.scss +1 -0
- package/src/components/Field/Field.rtl.test.tsx +8 -6
- package/src/components/Field/Field.tsx +64 -61
- package/src/components/Field/FieldExample.tsx +27 -5
- package/src/components/Field/Fieldset.tsx +78 -0
- package/src/components/Field/field.scss +23 -27
- package/src/components/Field/index.tsx +1 -3
- package/src/components/Field/utils.ts +15 -77
- package/src/components/FileUpload/FileUpload.tsx +1 -1
- package/src/components/FileUploadItem/FileUploadItem.tsx +1 -1
- package/src/components/Flex/Flex.tsx +12 -7
- package/src/components/InlineAlert/InlineAlert.rtl.test.tsx +1 -1
- package/src/components/InlineAlert/InlineAlert.tsx +3 -3
- package/src/components/Input/Input.tsx +140 -48
- package/src/components/Input/InputExample.tsx +4 -6
- package/src/components/Input/index.tsx +0 -1
- package/src/components/InputField/InputField.tsx +10 -6
- package/src/components/InputNumber/InputNumber.tsx +11 -16
- package/src/components/InputNumber/InputNumberExample.tsx +7 -4
- package/src/components/InputNumber/input-number.scss +1 -0
- package/src/components/InputNumberField/InputNumberField.tsx +10 -6
- package/src/components/InputPhone/InputPhone.tsx +14 -18
- package/src/components/InputPhone/InputPhoneExample.tsx +7 -6
- package/src/components/InputPhone/input-phone.scss +1 -0
- package/src/components/InputPhoneField/InputPhoneField.tsx +10 -6
- package/src/components/ListItem/list-item.scss +1 -0
- package/src/components/Modal/Modal.tsx +26 -30
- package/src/components/Modal/ModalExample.tsx +7 -2
- package/src/components/Modal/modal.scss +1 -1
- package/src/components/OTPInput/otp-input.scss +1 -0
- package/src/components/Pagination/Pagination.tsx +2 -2
- package/src/components/Password/Password.tsx +15 -17
- package/src/components/Password/PasswordExample.tsx +13 -5
- package/src/components/PasswordField/PasswordField.tsx +10 -6
- package/src/components/Popover/Popover.tsx +0 -1
- package/src/components/RadioGroup/RadioGroup.tsx +14 -20
- package/src/components/RadioGroupField/RadioGroupField.tsx +16 -11
- package/src/components/SearchBar/SearchBar.tsx +0 -1
- package/src/components/SegmentedControl/SegmentedControl.tsx +0 -1
- package/src/components/Select/Select.tsx +13 -14
- package/src/components/Select/SelectExample.tsx +7 -4
- package/src/components/SelectField/SelectField.rtl.test.tsx +7 -18
- package/src/components/SelectField/SelectField.tsx +10 -6
- package/src/components/Slider/Slider.tsx +1 -2
- package/src/components/Snackbar/Snackbar.tsx +0 -1
- package/src/components/Switch/Switch.tsx +0 -1
- package/src/components/TabGroup/TabGroup.tsx +0 -1
- package/src/components/TabList/TabList.tsx +0 -1
- package/src/components/TabList/tab-list.scss +1 -0
- package/src/components/Table/table.scss +2 -1
- package/src/components/Textarea/Textarea.tsx +8 -17
- package/src/components/Textarea/TextareaExample.tsx +3 -5
- package/src/components/TextareaField/TextareaField.tsx +10 -6
- package/src/components/TimePicker/TimePicker.tsx +12 -15
- package/src/components/TimePicker/TimePickerExample.tsx +3 -5
- package/src/components/TimePickerField/TimePickerField.tsx +10 -6
- package/src/types/common.ts +8 -0
- package/src/utils/demo.ts +1 -1
- package/dist/components/Field/FieldDescription.d.ts +0 -9
- package/dist/components/Field/FieldDescription.js +0 -13
- package/dist/components/Field/FieldDescription.js.map +0 -1
- package/dist/components/Field/FieldError.d.ts +0 -11
- package/dist/components/Field/FieldError.js +0 -14
- package/dist/components/Field/FieldError.js.map +0 -1
- package/dist/components/Field/FieldLabel.d.ts +0 -21
- package/dist/components/Field/FieldLabel.js +0 -14
- package/dist/components/Field/FieldLabel.js.map +0 -1
- package/dist/components/FormField/FormField.d.ts +0 -66
- package/dist/components/FormField/FormField.js +0 -31
- package/dist/components/FormField/FormField.js.map +0 -1
- package/dist/components/FormField/FormFieldExample.d.ts +0 -9
- package/dist/components/FormField/FormFieldExample.js +0 -99
- package/dist/components/FormField/FormFieldExample.js.map +0 -1
- package/dist/components/FormField/index.d.ts +0 -1
- package/dist/components/FormField/index.js +0 -2
- package/dist/components/FormField/index.js.map +0 -1
- package/dist/components/Input/InputElement.d.ts +0 -82
- package/dist/components/Input/InputElement.js +0 -64
- package/dist/components/Input/InputElement.js.map +0 -1
- package/src/components/Field/FieldDescription.tsx +0 -17
- package/src/components/Field/FieldError.tsx +0 -25
- package/src/components/Field/FieldLabel.tsx +0 -44
- package/src/components/FormField/FormField.rtl.test.tsx +0 -20
- package/src/components/FormField/FormField.tsx +0 -95
- package/src/components/FormField/FormFieldExample.tsx +0 -277
- package/src/components/FormField/index.tsx +0 -1
- package/src/components/Input/InputElement.tsx +0 -192
|
@@ -1,84 +1,22 @@
|
|
|
1
|
-
import { createContext, useContext, useEffect, useMemo } from 'react';
|
|
2
|
-
import { useId } from '-/hooks/useId';
|
|
3
|
-
import { CommonProps } from '-/types/common';
|
|
4
|
-
|
|
5
1
|
export const errorMessageId = (id: string) => `${id}-field-error`;
|
|
6
2
|
export const labelledById = (id: string) => `${id}-label`;
|
|
7
3
|
export const describedById = (id: string) => `${id}-description`;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/** The aria-errormessage attribute value that should be applied to the field input element. */
|
|
14
|
-
ariaErrorMessage?: string;
|
|
15
|
-
/** Text that appears after the label, but before the input (e.g. "optional"). */
|
|
16
|
-
labelTrailing?: string;
|
|
17
|
-
/** The aria-labelledby attribute value that should be applied to the field input element. */
|
|
18
|
-
ariaLabelledBy?: string;
|
|
19
|
-
/** The htmlFor attribute value that should be applied to the field label element. */
|
|
20
|
-
htmlFor?: string;
|
|
21
|
-
/** The id of the field. */
|
|
22
|
-
id: string;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type FieldContext = FieldContextProps & {
|
|
26
|
-
setContext: (next: Pick<FieldContextProps, 'htmlFor' | 'required'>) => void;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export const fieldContext = createContext<FieldContext | null>(null);
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Retrieves the current Field context.
|
|
33
|
-
*
|
|
34
|
-
* Will return a default context if used outside of a Field component.
|
|
35
|
-
*/
|
|
36
|
-
export function useFieldContext(): FieldContext {
|
|
37
|
-
return (
|
|
38
|
-
useContext(fieldContext) || {
|
|
39
|
-
id: undefined as unknown as string,
|
|
40
|
-
setContext: () => {},
|
|
41
|
-
}
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Initializes field-related attributes and state for a form control component.
|
|
47
|
-
*
|
|
48
|
-
* Creates id if not provided, manages invalid state, and syncs with Field context.
|
|
49
|
-
*/
|
|
50
|
-
export function useFieldInit({
|
|
51
|
-
idProp,
|
|
52
|
-
required,
|
|
53
|
-
disabled,
|
|
54
|
-
readOnly,
|
|
55
|
-
invalidProp,
|
|
4
|
+
export function propsWithAria<T extends Record<string, unknown>>({
|
|
5
|
+
errorMessage,
|
|
6
|
+
helperText,
|
|
7
|
+
controlProps,
|
|
8
|
+
id,
|
|
56
9
|
}: {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}): Pick<FieldContext, 'ariaDescribedBy' | 'ariaErrorMessage'> & { invalid: boolean; id: string } {
|
|
63
|
-
const context = useContext(fieldContext);
|
|
64
|
-
const controlId = useId(idProp || context?.htmlFor);
|
|
65
|
-
|
|
66
|
-
const invalid = useMemo(
|
|
67
|
-
() => !disabled && !readOnly && (invalidProp || !!context?.ariaErrorMessage),
|
|
68
|
-
[disabled, readOnly, invalidProp, context?.ariaErrorMessage],
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
if (!context) return;
|
|
73
|
-
|
|
74
|
-
if (controlId !== context?.htmlFor || !!required !== !!context?.required) {
|
|
75
|
-
context.setContext({ htmlFor: controlId, required: required });
|
|
76
|
-
}
|
|
77
|
-
}, [context, controlId, required]);
|
|
78
|
-
|
|
10
|
+
errorMessage?: string;
|
|
11
|
+
helperText?: string;
|
|
12
|
+
controlProps: T;
|
|
13
|
+
id: string;
|
|
14
|
+
}) {
|
|
79
15
|
return {
|
|
80
|
-
...
|
|
81
|
-
|
|
82
|
-
id:
|
|
16
|
+
...controlProps,
|
|
17
|
+
id,
|
|
18
|
+
'aria-errormessage': errorMessage ? errorMessageId(id) : undefined,
|
|
19
|
+
'aria-describedby': helperText ? describedById(id) : undefined,
|
|
20
|
+
'aria-invalid': errorMessage ? true : undefined,
|
|
83
21
|
};
|
|
84
22
|
}
|
|
@@ -205,7 +205,7 @@ export function FileUpload({
|
|
|
205
205
|
/>
|
|
206
206
|
<Button label="Browse" onClick={handleBrowseClick} />
|
|
207
207
|
{files.length === 1 && files[0].status === 'error' && (
|
|
208
|
-
<InlineAlert
|
|
208
|
+
<InlineAlert label={files[0].errorMessage || DEFAULT_ERROR_MESSAGE} variant="error" />
|
|
209
209
|
)}
|
|
210
210
|
</div>
|
|
211
211
|
<div data-bspk-owner="file-upload" data-file-entries>
|
|
@@ -44,13 +44,17 @@ export type FlexProps<As extends ElementType = ElementType> = {
|
|
|
44
44
|
* Utility component used within other components for layout purposes.
|
|
45
45
|
*
|
|
46
46
|
* @example
|
|
47
|
-
* import { Flex } from '
|
|
47
|
+
* import { Flex } from '-/components/Flex';
|
|
48
48
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* () => {
|
|
50
|
+
* return (
|
|
51
|
+
* <Flex gap="24" justify="center" style={{ width: '100%' }}>
|
|
52
|
+
* <div>Alpha</div>
|
|
53
|
+
* <div>Beta</div>
|
|
54
|
+
* <div>Gamma</div>
|
|
55
|
+
* </Flex>
|
|
56
|
+
* );
|
|
57
|
+
* };
|
|
54
58
|
*
|
|
55
59
|
* @name Flex
|
|
56
60
|
* @phase Utility
|
|
@@ -65,6 +69,7 @@ export function Flex<As extends ElementType = ElementType>({
|
|
|
65
69
|
wrap,
|
|
66
70
|
direction = 'row',
|
|
67
71
|
padding,
|
|
72
|
+
full,
|
|
68
73
|
...props
|
|
69
74
|
}: ElementProps<FlexProps<As>, As>) {
|
|
70
75
|
const As: ElementType = as || 'div';
|
|
@@ -85,7 +90,7 @@ export function Flex<As extends ElementType = ElementType>({
|
|
|
85
90
|
justifyContent: justify || 'flex-start',
|
|
86
91
|
flexWrap: wrap ? 'wrap' : 'nowrap',
|
|
87
92
|
padding: paddingValue,
|
|
88
|
-
width:
|
|
93
|
+
width: full ? '100%' : undefined,
|
|
89
94
|
...style,
|
|
90
95
|
}}
|
|
91
96
|
>
|
|
@@ -2,7 +2,7 @@ import { InlineAlert } from './InlineAlert';
|
|
|
2
2
|
import { hasNoBasicA11yIssues } from '-/rtl/hasNoBasicA11yIssues';
|
|
3
3
|
import { render } from '-/rtl/util';
|
|
4
4
|
|
|
5
|
-
const TestBed = () => <InlineAlert
|
|
5
|
+
const TestBed = () => <InlineAlert label="Example informational inline alert" variant="informational" />;
|
|
6
6
|
|
|
7
7
|
describe('InlineAlert (RTL)', () => {
|
|
8
8
|
it('has no basic a11y issues', hasNoBasicA11yIssues(<TestBed />));
|
|
@@ -14,7 +14,7 @@ export type InlineAlertProps = CommonProps<'owner'> & {
|
|
|
14
14
|
* @type multiline
|
|
15
15
|
* @required
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
label: string;
|
|
18
18
|
/**
|
|
19
19
|
* The color variant of the inline alert.
|
|
20
20
|
*
|
|
@@ -37,11 +37,11 @@ export type InlineAlertProps = CommonProps<'owner'> & {
|
|
|
37
37
|
* @name InlineAlert
|
|
38
38
|
* @phase Stable
|
|
39
39
|
*/
|
|
40
|
-
export function InlineAlert({
|
|
40
|
+
export function InlineAlert({ label, variant = 'informational', id, owner }: InlineAlertProps) {
|
|
41
41
|
return (
|
|
42
42
|
<div data-bspk="inline-alert" data-bspk-owner={owner || undefined} data-variant={variant} id={id} role="alert">
|
|
43
43
|
{VARIANT_ICON[variant]}
|
|
44
|
-
<Txt variant="body-small">{
|
|
44
|
+
<Txt variant="body-small">{label}</Txt>
|
|
45
45
|
</div>
|
|
46
46
|
);
|
|
47
47
|
}
|
|
@@ -1,39 +1,89 @@
|
|
|
1
1
|
import './input.scss';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { SvgCancel } from '@bspk/icons/Cancel';
|
|
3
|
+
import { HTMLInputTypeAttribute, ReactNode, useMemo, useRef } from 'react';
|
|
4
|
+
import { Button } from '-/components/Button';
|
|
5
|
+
import { useTimeout } from '-/hooks/useTimeout';
|
|
6
|
+
import { CommonProps, ElementProps, SetRef, FieldControlProps } from '-/types/common';
|
|
5
7
|
|
|
6
|
-
export
|
|
8
|
+
export const DEFAULT = {
|
|
9
|
+
size: 'medium',
|
|
10
|
+
value: '',
|
|
11
|
+
type: 'text' as Extract<HTMLInputTypeAttribute, 'number' | 'text'>,
|
|
12
|
+
autoComplete: 'off',
|
|
13
|
+
} as const;
|
|
14
|
+
|
|
15
|
+
export type InputBaseProps = CommonProps<'owner' | 'size'> &
|
|
16
|
+
FieldControlProps & {
|
|
17
|
+
/** The ref of the container. */
|
|
18
|
+
containerRef?: SetRef<HTMLDivElement>;
|
|
19
|
+
/** The ref of the input. */
|
|
20
|
+
inputRef?: SetRef<HTMLInputElement>;
|
|
21
|
+
/**
|
|
22
|
+
* The trailing element to display in the field.
|
|
23
|
+
*
|
|
24
|
+
* @exampleType string
|
|
25
|
+
*/
|
|
26
|
+
trailing?: ReactNode;
|
|
27
|
+
/**
|
|
28
|
+
* The leading element to display in the field.
|
|
29
|
+
*
|
|
30
|
+
* @exampleType string
|
|
31
|
+
*/
|
|
32
|
+
leading?: ReactNode;
|
|
33
|
+
/** The placeholder of the field. */
|
|
34
|
+
placeholder?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The type of the input.
|
|
37
|
+
*
|
|
38
|
+
* @default text
|
|
39
|
+
*/
|
|
40
|
+
type?: Extract<HTMLInputTypeAttribute, 'number' | 'password' | 'text'>;
|
|
41
|
+
/**
|
|
42
|
+
* Specifies if user agent has any permission to provide automated assistance in filling out form field values
|
|
43
|
+
*
|
|
44
|
+
* @default off
|
|
45
|
+
*/
|
|
46
|
+
autoComplete?: 'off' | 'on';
|
|
47
|
+
/**
|
|
48
|
+
* Specifies if the clear button should be shown. This should almost always be true, but can be set to false.
|
|
49
|
+
*
|
|
50
|
+
* @default true
|
|
51
|
+
*/
|
|
52
|
+
showClearButton?: boolean;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export type InputProps = InputBaseProps & {
|
|
56
|
+
inputProps?: Omit<React.InputHTMLAttributes<HTMLInputElement>, keyof InputBaseProps>;
|
|
57
|
+
};
|
|
7
58
|
|
|
8
59
|
/**
|
|
9
|
-
* An input that allows users to enter text, numbers or symbols in a singular line.
|
|
10
|
-
*
|
|
11
|
-
* For a more complete example with field usage, see the InputField component.
|
|
60
|
+
* An input that allows users to enter text, numbers or symbols in a singular line. This is a utility element and is not
|
|
61
|
+
* intended to be used directly but rather through the Input, and other components.
|
|
12
62
|
*
|
|
13
63
|
* @example
|
|
14
|
-
* import { useState } from 'react';
|
|
15
64
|
* import { Input } from '@bspk/ui/Input';
|
|
16
|
-
* import { Field, FieldLabel, FieldDescription } from '@bspk/ui/Field';
|
|
17
65
|
*
|
|
18
66
|
* () => {
|
|
19
|
-
* const [
|
|
67
|
+
* const [value, setValue] = useState<string>();
|
|
20
68
|
*
|
|
21
69
|
* return (
|
|
22
70
|
* <div style={{ width: 320 }}>
|
|
23
|
-
* <Field
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
71
|
+
* <Field
|
|
72
|
+
* controlId="example-control-id"
|
|
73
|
+
* helperText="This is an example input field."
|
|
74
|
+
* label="Example Input"
|
|
75
|
+
* >
|
|
76
|
+
* <Input id="example-control-id" name="example-name" onChange={setValue} value={value} />
|
|
27
77
|
* </Field>
|
|
28
78
|
* </div>
|
|
29
79
|
* );
|
|
30
80
|
* };
|
|
31
81
|
*
|
|
32
82
|
* @name Input
|
|
33
|
-
* @phase
|
|
83
|
+
* @phase Utility
|
|
34
84
|
*/
|
|
35
85
|
export function Input({
|
|
36
|
-
invalid
|
|
86
|
+
invalid,
|
|
37
87
|
onChange,
|
|
38
88
|
size = DEFAULT.size,
|
|
39
89
|
value = DEFAULT.value,
|
|
@@ -42,7 +92,7 @@ export function Input({
|
|
|
42
92
|
inputRef,
|
|
43
93
|
required = false,
|
|
44
94
|
placeholder,
|
|
45
|
-
id
|
|
95
|
+
id,
|
|
46
96
|
leading,
|
|
47
97
|
trailing,
|
|
48
98
|
type = DEFAULT.type,
|
|
@@ -53,42 +103,84 @@ export function Input({
|
|
|
53
103
|
showClearButton: showClearButtonProp = true,
|
|
54
104
|
owner,
|
|
55
105
|
inputProps,
|
|
106
|
+
'aria-describedby': ariaDescribedBy,
|
|
107
|
+
'aria-errormessage': ariaErrorMessage,
|
|
56
108
|
...props
|
|
57
109
|
}: ElementProps<InputProps, 'div'>) {
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
110
|
+
const showClearButton = useMemo(
|
|
111
|
+
() => showClearButtonProp !== false && !readOnly && !disabled && !!value?.toString().length,
|
|
112
|
+
[showClearButtonProp, readOnly, disabled, value],
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const inputRefInternal = useRef<HTMLInputElement | null>(null);
|
|
116
|
+
|
|
117
|
+
const focusTimeout = useTimeout();
|
|
65
118
|
|
|
66
119
|
return (
|
|
67
|
-
|
|
68
|
-
<InputElement
|
|
120
|
+
<div
|
|
69
121
|
{...props}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
leading
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
122
|
+
data-bspk="input"
|
|
123
|
+
data-bspk-owner={owner || undefined}
|
|
124
|
+
data-disabled={disabled || undefined}
|
|
125
|
+
data-empty={!value.toString().length || undefined}
|
|
126
|
+
data-invalid={invalid || undefined}
|
|
127
|
+
data-readonly={readOnly || undefined}
|
|
128
|
+
data-show-clear-button={showClearButton || undefined}
|
|
129
|
+
data-size={size}
|
|
130
|
+
ref={containerRef}
|
|
131
|
+
>
|
|
132
|
+
{leading && <span data-leading>{leading}</span>}
|
|
133
|
+
|
|
134
|
+
<input
|
|
135
|
+
{...inputProps}
|
|
136
|
+
aria-describedby={ariaDescribedBy || undefined}
|
|
137
|
+
aria-errormessage={ariaErrorMessage || undefined}
|
|
138
|
+
aria-invalid={invalid || undefined}
|
|
139
|
+
aria-label={ariaLabel}
|
|
140
|
+
autoComplete={autoComplete}
|
|
141
|
+
data-main-input
|
|
142
|
+
disabled={disabled || undefined}
|
|
143
|
+
id={id}
|
|
144
|
+
name={name}
|
|
145
|
+
onBlur={(event) => {
|
|
146
|
+
inputProps?.onBlur?.(event);
|
|
147
|
+
}}
|
|
148
|
+
onChange={(event) => {
|
|
149
|
+
onChange(event.target.value, event);
|
|
150
|
+
}}
|
|
151
|
+
onFocus={(event) => {
|
|
152
|
+
inputProps?.onFocus?.(event);
|
|
153
|
+
}}
|
|
154
|
+
placeholder={placeholder || ' '}
|
|
155
|
+
readOnly={readOnly || undefined}
|
|
156
|
+
ref={(node) => {
|
|
157
|
+
if (!node) return;
|
|
158
|
+
inputRef?.(node);
|
|
159
|
+
inputRefInternal.current = node;
|
|
160
|
+
}}
|
|
161
|
+
required={required || undefined}
|
|
162
|
+
type={type}
|
|
163
|
+
value={value || ''}
|
|
164
|
+
/>
|
|
165
|
+
{trailing && <span data-trailing>{trailing}</span>}
|
|
166
|
+
{showClearButton && (
|
|
167
|
+
<Button
|
|
168
|
+
data-clear-button
|
|
169
|
+
icon={<SvgCancel />}
|
|
170
|
+
iconOnly
|
|
171
|
+
label="Clear"
|
|
172
|
+
onClick={() => {
|
|
173
|
+
onChange('');
|
|
174
|
+
inputRefInternal.current?.focus();
|
|
175
|
+
}}
|
|
176
|
+
onFocus={() => {
|
|
177
|
+
focusTimeout.clear();
|
|
178
|
+
}}
|
|
179
|
+
size={size}
|
|
180
|
+
variant="tertiary"
|
|
181
|
+
/>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
92
184
|
);
|
|
93
185
|
}
|
|
94
186
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { Input, InputProps } from '.';
|
|
3
|
-
import { Field
|
|
3
|
+
import { Field } from '-/components/Field';
|
|
4
4
|
import { ComponentExample } from '-/utils/demo';
|
|
5
5
|
|
|
6
6
|
export type InputExampleProps = InputProps & { label: string; description?: string };
|
|
@@ -62,14 +62,12 @@ export const InputExample: ComponentExample<InputExampleProps> = {
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
export const Usage = () => {
|
|
65
|
-
const [
|
|
65
|
+
const [value, setValue] = useState<string>();
|
|
66
66
|
|
|
67
67
|
return (
|
|
68
68
|
<div style={{ width: 320 }}>
|
|
69
|
-
<Field>
|
|
70
|
-
<
|
|
71
|
-
<Input name="example-name" onChange={setFieldDate} value={fieldDate} />
|
|
72
|
-
<FieldDescription>This is an example input field.</FieldDescription>
|
|
69
|
+
<Field controlId="example-control-id" helperText="This is an example input field." label="Example Input">
|
|
70
|
+
<Input id="example-control-id" name="example-name" onChange={setValue} value={value} />
|
|
73
71
|
</Field>
|
|
74
72
|
</div>
|
|
75
73
|
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Field, FieldControlProps, propsWithAria } from '-/components/Field';
|
|
2
2
|
import { Input, InputProps } from '-/components/Input';
|
|
3
|
+
import { useId } from '-/hooks/useId';
|
|
3
4
|
|
|
4
|
-
export type InputFieldProps =
|
|
5
|
+
export type InputFieldProps = FieldControlProps<InputProps>;
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* A field wrapper for the Input component.
|
|
@@ -9,7 +10,7 @@ export type InputFieldProps = FormFieldControlProps<InputProps>;
|
|
|
9
10
|
* This component takes properties from the FormField and Input components.
|
|
10
11
|
*
|
|
11
12
|
* @name InputField
|
|
12
|
-
* @phase
|
|
13
|
+
* @phase UXReview
|
|
13
14
|
*
|
|
14
15
|
* @generated
|
|
15
16
|
*/
|
|
@@ -19,18 +20,21 @@ export function InputField({
|
|
|
19
20
|
labelTrailing,
|
|
20
21
|
errorMessage,
|
|
21
22
|
style,
|
|
23
|
+
id: idProp,
|
|
22
24
|
...controlProps
|
|
23
25
|
}: InputFieldProps) {
|
|
26
|
+
const id = useId(idProp);
|
|
24
27
|
return (
|
|
25
|
-
<
|
|
28
|
+
<Field
|
|
29
|
+
controlId={id}
|
|
26
30
|
errorMessage={errorMessage}
|
|
27
31
|
helperText={helperText}
|
|
28
32
|
label={label}
|
|
29
33
|
labelTrailing={labelTrailing}
|
|
30
34
|
style={style}
|
|
31
35
|
>
|
|
32
|
-
<Input {...controlProps} />
|
|
33
|
-
</
|
|
36
|
+
<Input {...propsWithAria({ id, controlProps, errorMessage, helperText })} />
|
|
37
|
+
</Field>
|
|
34
38
|
);
|
|
35
39
|
}
|
|
36
40
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import './input-number.scss';
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { IncrementButton } from './IncrementButton';
|
|
4
|
-
import { useFieldInit } from '-/components/Field';
|
|
5
4
|
import { useId } from '-/hooks/useId';
|
|
6
5
|
import { CommonProps, FieldControlProps } from '-/types/common';
|
|
7
6
|
|
|
@@ -51,24 +50,25 @@ export type InputNumberProps = CommonProps<'size'> &
|
|
|
51
50
|
* For a more complete example with field usage, see the InputNumberField component.
|
|
52
51
|
*
|
|
53
52
|
* @example
|
|
54
|
-
* import { InputNumber } from '
|
|
53
|
+
* import { InputNumber } from '-/components/InputNumber';
|
|
55
54
|
*
|
|
56
55
|
* () => {
|
|
57
56
|
* const [value, setValue] = useState<number | undefined>();
|
|
58
57
|
*
|
|
59
58
|
* return (
|
|
60
59
|
* <div style={{ width: 320 }}>
|
|
61
|
-
* <Field
|
|
62
|
-
*
|
|
60
|
+
* <Field
|
|
61
|
+
* controlId="example-input-number"
|
|
62
|
+
* helperText="The input number allows you to increment or decrement a value."
|
|
63
|
+
* label="Example Input Number"
|
|
64
|
+
* >
|
|
63
65
|
* <InputNumber
|
|
64
66
|
* aria-label="Example aria-label"
|
|
67
|
+
* id="example-input-number"
|
|
65
68
|
* name="example-name"
|
|
66
69
|
* onChange={(nextValue) => setValue(nextValue)}
|
|
67
70
|
* value={value}
|
|
68
71
|
* />
|
|
69
|
-
* <FieldDescription>
|
|
70
|
-
* The input number allows you to increment or decrement a value.
|
|
71
|
-
* </FieldDescription>
|
|
72
72
|
* </Field>
|
|
73
73
|
* </div>
|
|
74
74
|
* );
|
|
@@ -89,21 +89,16 @@ export function InputNumber({
|
|
|
89
89
|
'aria-label': ariaLabel = 'Input number',
|
|
90
90
|
max: maxProp,
|
|
91
91
|
min = 0,
|
|
92
|
-
invalid
|
|
92
|
+
invalid = false,
|
|
93
93
|
step = 1,
|
|
94
94
|
required = false,
|
|
95
|
+
'aria-describedby': ariaDescribedBy,
|
|
96
|
+
'aria-errormessage': ariaErrorMessage,
|
|
95
97
|
...inputElementProps
|
|
96
98
|
}: InputNumberProps) {
|
|
97
|
-
const
|
|
98
|
-
idProp,
|
|
99
|
-
required,
|
|
100
|
-
disabled,
|
|
101
|
-
readOnly,
|
|
102
|
-
invalidProp,
|
|
103
|
-
});
|
|
99
|
+
const inputId = useId(idProp);
|
|
104
100
|
const max = typeof maxProp === 'number' && maxProp >= min ? maxProp : Number.MAX_SAFE_INTEGER;
|
|
105
101
|
const centered = align !== 'left';
|
|
106
|
-
const inputId = useId(id);
|
|
107
102
|
const value = isNumber(valueProp, min);
|
|
108
103
|
const removeDisabled = disabled || value + step * -1 < min;
|
|
109
104
|
const addDisabled = disabled || value + step > max;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
import { InputNumber, InputNumberProps } from './InputNumber';
|
|
3
|
-
import { Field
|
|
3
|
+
import { Field } from '-/components/Field';
|
|
4
4
|
import { ComponentExample } from '-/utils/demo';
|
|
5
5
|
|
|
6
6
|
export const InputNumberExample: ComponentExample<InputNumberProps> = {
|
|
@@ -18,15 +18,18 @@ export const Usage = () => {
|
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
20
|
<div style={{ width: 320 }}>
|
|
21
|
-
<Field
|
|
22
|
-
|
|
21
|
+
<Field
|
|
22
|
+
controlId="example-input-number"
|
|
23
|
+
helperText="The input number allows you to increment or decrement a value."
|
|
24
|
+
label="Example Input Number"
|
|
25
|
+
>
|
|
23
26
|
<InputNumber
|
|
24
27
|
aria-label="Example aria-label"
|
|
28
|
+
id="example-input-number"
|
|
25
29
|
name="example-name"
|
|
26
30
|
onChange={(nextValue) => setValue(nextValue)}
|
|
27
31
|
value={value}
|
|
28
32
|
/>
|
|
29
|
-
<FieldDescription>The input number allows you to increment or decrement a value.</FieldDescription>
|
|
30
33
|
</Field>
|
|
31
34
|
</div>
|
|
32
35
|
);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Field, FieldControlProps, propsWithAria } from '-/components/Field';
|
|
2
2
|
import { InputNumber, InputNumberProps } from '-/components/InputNumber';
|
|
3
|
+
import { useId } from '-/hooks/useId';
|
|
3
4
|
|
|
4
|
-
export type InputNumberFieldProps =
|
|
5
|
+
export type InputNumberFieldProps = FieldControlProps<InputNumberProps>;
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* A field wrapper for the InputNumber component.
|
|
@@ -9,7 +10,7 @@ export type InputNumberFieldProps = FormFieldControlProps<InputNumberProps>;
|
|
|
9
10
|
* This component takes properties from the FormField and InputNumber components.
|
|
10
11
|
*
|
|
11
12
|
* @name InputNumberField
|
|
12
|
-
* @phase
|
|
13
|
+
* @phase UXReview
|
|
13
14
|
*
|
|
14
15
|
* @generated
|
|
15
16
|
*/
|
|
@@ -19,18 +20,21 @@ export function InputNumberField({
|
|
|
19
20
|
labelTrailing,
|
|
20
21
|
errorMessage,
|
|
21
22
|
style,
|
|
23
|
+
id: idProp,
|
|
22
24
|
...controlProps
|
|
23
25
|
}: InputNumberFieldProps) {
|
|
26
|
+
const id = useId(idProp);
|
|
24
27
|
return (
|
|
25
|
-
<
|
|
28
|
+
<Field
|
|
29
|
+
controlId={id}
|
|
26
30
|
errorMessage={errorMessage}
|
|
27
31
|
helperText={helperText}
|
|
28
32
|
label={label}
|
|
29
33
|
labelTrailing={labelTrailing}
|
|
30
34
|
style={style}
|
|
31
35
|
>
|
|
32
|
-
<InputNumber {...controlProps} />
|
|
33
|
-
</
|
|
36
|
+
<InputNumber {...propsWithAria({ id, controlProps, errorMessage, helperText })} />
|
|
37
|
+
</Field>
|
|
34
38
|
);
|
|
35
39
|
}
|
|
36
40
|
|