@bspk/ui 1.3.9 → 1.3.11
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/BannerAlert/BannerAlert.d.ts +5 -5
- package/dist/components/BannerAlert/BannerAlert.js +5 -5
- package/dist/components/Breadcrumb/BreadcrumbDropdown.d.ts +6 -0
- package/dist/components/Breadcrumb/BreadcrumbDropdown.js +6 -0
- package/dist/components/Breadcrumb/BreadcrumbDropdown.js.map +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroupExample.js +1 -0
- package/dist/components/CheckboxGroup/CheckboxGroupExample.js.map +1 -1
- package/dist/components/ChipGroup/ChipGroup.d.ts +15 -28
- package/dist/components/ChipGroup/ChipGroup.js +12 -22
- package/dist/components/ChipGroup/ChipGroup.js.map +1 -1
- package/dist/components/ChipGroup/ChipGroupExample.js +61 -6
- package/dist/components/ChipGroup/ChipGroupExample.js.map +1 -1
- package/dist/components/ChipGroup/chip-group.css +5 -3
- package/dist/components/ChipGroup/chip-group.css.js +5 -3
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/Field/FieldDescription.d.ts +7 -5
- package/dist/components/Field/FieldDescription.js +7 -3
- package/dist/components/Field/FieldDescription.js.map +1 -1
- package/dist/components/Field/FieldError.d.ts +6 -0
- package/dist/components/Field/FieldError.js +6 -0
- package/dist/components/Field/FieldError.js.map +1 -1
- package/dist/components/Field/FieldLabel.d.ts +6 -0
- package/dist/components/Field/FieldLabel.js +6 -0
- package/dist/components/Field/FieldLabel.js.map +1 -1
- package/dist/components/Field/utils.d.ts +5 -0
- package/dist/components/Field/utils.js +5 -0
- package/dist/components/Field/utils.js.map +1 -1
- package/dist/components/InlineAlert/SvgWarningTwoTone.d.ts +6 -0
- package/dist/components/InlineAlert/SvgWarningTwoTone.js +6 -0
- package/dist/components/InlineAlert/SvgWarningTwoTone.js.map +1 -1
- package/dist/components/InputNumber/IncrementButton.d.ts +13 -3
- package/dist/components/InputNumber/IncrementButton.js +11 -4
- package/dist/components/InputNumber/IncrementButton.js.map +1 -1
- package/dist/components/InputNumber/InputNumber.js +26 -10
- package/dist/components/InputNumber/InputNumber.js.map +1 -1
- package/dist/components/InputNumber/InputNumberExample.js +1 -0
- package/dist/components/InputNumber/InputNumberExample.js.map +1 -1
- package/dist/components/InputNumber/input-number.css +6 -0
- package/dist/components/InputNumber/input-number.css.js +6 -0
- package/dist/components/Link/Link.d.ts +1 -1
- package/dist/components/Link/Link.js +1 -1
- package/dist/components/OTPInput/OTPInput.d.ts +13 -3
- package/dist/components/OTPInput/OTPInput.js +11 -39
- package/dist/components/OTPInput/OTPInput.js.map +1 -1
- package/dist/components/OTPInput/OTPInputExample.js +6 -1
- package/dist/components/OTPInput/OTPInputExample.js.map +1 -1
- package/dist/components/OTPInput/otp-input.css +18 -17
- package/dist/components/OTPInput/otp-input.css.js +18 -17
- package/dist/components/Pagination/PageList.d.ts +6 -0
- package/dist/components/Pagination/PageList.js +6 -0
- package/dist/components/Pagination/PageList.js.map +1 -1
- package/dist/components/Scrim/Scrim.d.ts +0 -1
- package/dist/components/Scrim/Scrim.js +0 -1
- package/dist/components/Scrim/Scrim.js.map +1 -1
- package/dist/components/Select/Select.d.ts +11 -11
- package/dist/components/Select/Select.js +11 -11
- package/dist/components/Skeleton/Circular.d.ts +6 -0
- package/dist/components/Skeleton/Circular.js +6 -0
- package/dist/components/Skeleton/Circular.js.map +1 -1
- package/dist/components/Skeleton/Photo.d.ts +6 -0
- package/dist/components/Skeleton/Photo.js +6 -0
- package/dist/components/Skeleton/Photo.js.map +1 -1
- package/dist/components/Skeleton/Profile.d.ts +6 -0
- package/dist/components/Skeleton/Profile.js +6 -0
- package/dist/components/Skeleton/Profile.js.map +1 -1
- package/dist/components/Skeleton/Rectangular.d.ts +6 -0
- package/dist/components/Skeleton/Rectangular.js +6 -0
- package/dist/components/Skeleton/Rectangular.js.map +1 -1
- package/dist/components/Skeleton/Thumbnail.d.ts +6 -0
- package/dist/components/Skeleton/Thumbnail.js +6 -0
- package/dist/components/Skeleton/Thumbnail.js.map +1 -1
- package/dist/components/Slider/SliderIntervalDots.d.ts +6 -0
- package/dist/components/Slider/SliderIntervalDots.js +6 -0
- package/dist/components/Slider/SliderIntervalDots.js.map +1 -1
- package/dist/components/Snackbar/Manager.d.ts +0 -1
- package/dist/components/Snackbar/Manager.js +0 -1
- package/dist/components/Snackbar/Manager.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/TabList/TabList.js +1 -2
- package/dist/components/TabList/TabList.js.map +1 -1
- package/dist/components/Table/Footer.d.ts +6 -0
- package/dist/components/Table/Footer.js +6 -0
- package/dist/components/Table/Footer.js.map +1 -1
- package/dist/components/TimePicker/Listbox.d.ts +6 -0
- package/dist/components/TimePicker/Listbox.js +6 -0
- package/dist/components/TimePicker/Listbox.js.map +1 -1
- package/dist/components/TimePicker/Segment.d.ts +6 -0
- package/dist/components/TimePicker/Segment.js +6 -0
- package/dist/components/TimePicker/Segment.js.map +1 -1
- package/dist/components/Truncated/Truncated.d.ts +0 -1
- package/dist/components/Truncated/Truncated.js +1 -2
- package/dist/components/Truncated/Truncated.js.map +1 -1
- package/dist/components/UIProvider/UIProvider.d.ts +0 -1
- package/dist/components/UIProvider/UIProvider.js +0 -1
- package/dist/components/UIProvider/UIProvider.js.map +1 -1
- package/dist/hooks/useLongPress.d.ts +30 -15
- package/dist/hooks/useLongPress.js +26 -42
- package/dist/hooks/useLongPress.js.map +1 -1
- package/dist/styles/base.css +9 -0
- package/dist/styles/base.css.js +9 -0
- package/package.json +1 -1
- package/src/components/BannerAlert/BannerAlert.tsx +5 -5
- package/src/components/Breadcrumb/BreadcrumbDropdown.tsx +6 -0
- package/src/components/CheckboxGroup/CheckboxGroupExample.tsx +1 -0
- package/src/components/ChipGroup/ChipGroup.rtl.test.tsx +16 -11
- package/src/components/ChipGroup/ChipGroup.tsx +18 -36
- package/src/components/ChipGroup/ChipGroupExample.tsx +64 -36
- package/src/components/ChipGroup/chip-group.scss +5 -3
- package/src/components/Drawer/Drawer.tsx +0 -1
- package/src/components/Field/FieldDescription.tsx +7 -5
- package/src/components/Field/FieldError.tsx +6 -0
- package/src/components/Field/FieldLabel.tsx +6 -0
- package/src/components/Field/utils.ts +5 -0
- package/src/components/InlineAlert/SvgWarningTwoTone.tsx +6 -0
- package/src/components/InputNumber/IncrementButton.tsx +21 -11
- package/src/components/InputNumber/InputNumber.tsx +33 -31
- package/src/components/InputNumber/InputNumberExample.tsx +1 -0
- package/src/components/InputNumber/input-number.scss +10 -0
- package/src/components/Link/Link.tsx +1 -1
- package/src/components/OTPInput/OTPInput.rtl.test.tsx +4 -2
- package/src/components/OTPInput/OTPInput.tsx +34 -63
- package/src/components/OTPInput/OTPInputExample.tsx +6 -1
- package/src/components/OTPInput/otp-input.scss +50 -45
- package/src/components/Pagination/PageList.tsx +6 -0
- package/src/components/Scrim/Scrim.tsx +0 -1
- package/src/components/Select/Select.tsx +11 -11
- package/src/components/Skeleton/Circular.tsx +6 -0
- package/src/components/Skeleton/Photo.tsx +6 -0
- package/src/components/Skeleton/Profile.tsx +6 -0
- package/src/components/Skeleton/Rectangular.tsx +6 -0
- package/src/components/Skeleton/Thumbnail.tsx +6 -0
- package/src/components/Slider/SliderIntervalDots.tsx +6 -0
- package/src/components/Snackbar/Manager.tsx +0 -1
- package/src/components/Snackbar/Snackbar.tsx +0 -1
- package/src/components/TabList/TabList.tsx +1 -2
- package/src/components/Table/Footer.tsx +6 -0
- package/src/components/TimePicker/Listbox.tsx +6 -0
- package/src/components/TimePicker/Segment.tsx +6 -0
- package/src/components/Truncated/Truncated.tsx +1 -2
- package/src/components/UIProvider/UIProvider.tsx +0 -1
- package/src/hooks/useLongPress.ts +58 -48
- package/src/styles/base.scss +9 -0
- package/dist/components/Truncated/truncated.css +0 -8
- package/dist/components/Truncated/truncated.css.js +0 -13
- package/src/components/Truncated/truncated.scss +0 -8
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import './otp-input.scss';
|
|
2
|
-
import { useRef } from 'react';
|
|
3
2
|
import { useId } from '-/hooks/useId';
|
|
4
3
|
import { CommonProps } from '-/types/common';
|
|
5
4
|
|
|
6
|
-
export type OTPInputProps = CommonProps<'id' | 'invalid' | 'name' | 'size'> & {
|
|
5
|
+
export type OTPInputProps = CommonProps<'aria-label' | 'id' | 'invalid' | 'name' | 'size'> & {
|
|
7
6
|
/**
|
|
8
7
|
* The value of the otp-input.
|
|
9
8
|
*
|
|
@@ -26,6 +25,12 @@ export type OTPInputProps = CommonProps<'id' | 'invalid' | 'name' | 'size'> & {
|
|
|
26
25
|
* @maximum 10
|
|
27
26
|
*/
|
|
28
27
|
length?: number;
|
|
28
|
+
/**
|
|
29
|
+
* The mode of the otp-input.
|
|
30
|
+
*
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
alphanumeric?: boolean;
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
/**
|
|
@@ -34,7 +39,11 @@ export type OTPInputProps = CommonProps<'id' | 'invalid' | 'name' | 'size'> & {
|
|
|
34
39
|
* @example
|
|
35
40
|
* import { OTPInput } from '@bspk/ui/OTPInput';
|
|
36
41
|
*
|
|
37
|
-
*
|
|
42
|
+
* () => {
|
|
43
|
+
* const [otpValue, setOtpValue] = useState('');
|
|
44
|
+
*
|
|
45
|
+
* return <OTPInput name="2-auth" length={6} value={otpValue} onChange={setOtpValue} alphanumeric={false} />;
|
|
46
|
+
* };
|
|
38
47
|
*
|
|
39
48
|
* @name OTPInput
|
|
40
49
|
* @phase UXReview
|
|
@@ -47,70 +56,32 @@ export function OTPInput({
|
|
|
47
56
|
length = 6,
|
|
48
57
|
size = 'medium',
|
|
49
58
|
invalid = false,
|
|
59
|
+
alphanumeric = false,
|
|
60
|
+
'aria-label': ariaLabel = 'OTP input',
|
|
50
61
|
}: OTPInputProps) {
|
|
51
62
|
const id = useId(idProp);
|
|
52
|
-
const value = valueProp
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
const element = (index: number) => parentRef.current?.children[index + 1] as HTMLElement;
|
|
56
|
-
|
|
57
|
-
const setIndex = (index: number, character: string) => {
|
|
58
|
-
const charArray = value.split('');
|
|
59
|
-
charArray[index] = character;
|
|
60
|
-
return charArray.join('');
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const updateValue = (next: string) => onChange(next.slice(0, length).toUpperCase());
|
|
63
|
+
const value = valueProp || '';
|
|
64
|
+
const activeIndex = Math.min(value.length, length - 1);
|
|
64
65
|
|
|
65
66
|
return (
|
|
66
|
-
<div
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
element(value.length)?.focus();
|
|
85
|
-
}
|
|
86
|
-
}}
|
|
87
|
-
onKeyDown={(event) => {
|
|
88
|
-
if (event.key === 'Backspace') {
|
|
89
|
-
if (value) {
|
|
90
|
-
// delete the last value if there is one and focus the first empty input
|
|
91
|
-
const next = value.slice(0, -1);
|
|
92
|
-
updateValue(next);
|
|
93
|
-
element(next.length)?.focus();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// if a single character key is pressed at at the current index and focus the next input
|
|
98
|
-
if (event.key.length === 1) {
|
|
99
|
-
updateValue(setIndex(index, event.key));
|
|
100
|
-
element(index + 1)?.focus();
|
|
101
|
-
}
|
|
102
|
-
}}
|
|
103
|
-
onPaste={(event) => {
|
|
104
|
-
const pastedData = event.clipboardData.getData('text').trim();
|
|
105
|
-
updateValue(pastedData);
|
|
106
|
-
element(length - 1)?.focus();
|
|
107
|
-
}}
|
|
108
|
-
role="textbox"
|
|
109
|
-
tabIndex={0}
|
|
110
|
-
>
|
|
111
|
-
{value?.[index] || ''}
|
|
112
|
-
</span>
|
|
113
|
-
))}
|
|
67
|
+
<div data-bspk="otp-input" data-invalid={invalid || undefined} data-size={size || 'medium'} id={id}>
|
|
68
|
+
<input
|
|
69
|
+
aria-label={ariaLabel}
|
|
70
|
+
inputMode={alphanumeric ? 'text' : 'numeric'}
|
|
71
|
+
name={name}
|
|
72
|
+
onChange={(event) => {
|
|
73
|
+
onChange(event.target.value.trim().toUpperCase().slice(0, length));
|
|
74
|
+
}}
|
|
75
|
+
type={alphanumeric ? 'text' : 'number'}
|
|
76
|
+
value={value}
|
|
77
|
+
/>
|
|
78
|
+
<span data-digits>
|
|
79
|
+
{Array.from({ length }, (_, index) => (
|
|
80
|
+
<span data-active={index === activeIndex || undefined} data-digit key={index}>
|
|
81
|
+
{value[index]}
|
|
82
|
+
</span>
|
|
83
|
+
))}
|
|
84
|
+
</span>
|
|
114
85
|
</div>
|
|
115
86
|
);
|
|
116
87
|
}
|
|
@@ -22,7 +22,12 @@ export const presets: Preset<OTPInputProps>[] = [
|
|
|
22
22
|
|
|
23
23
|
export const OTPInputExample: ComponentExample<OTPInputProps> = {
|
|
24
24
|
containerStyle: { width: '100%' },
|
|
25
|
-
defaultState: {
|
|
25
|
+
defaultState: {
|
|
26
|
+
value: '',
|
|
27
|
+
length: 6,
|
|
28
|
+
name: 'OTP Input',
|
|
29
|
+
alphanumeric: false,
|
|
30
|
+
},
|
|
26
31
|
disableProps: [],
|
|
27
32
|
presets,
|
|
28
33
|
render: ({ props, Component }) => <Component {...props} />,
|
|
@@ -1,64 +1,69 @@
|
|
|
1
1
|
[data-bspk='otp-input'] {
|
|
2
|
-
display: flex;
|
|
3
|
-
flex-direction: row;
|
|
4
|
-
justify-content: center;
|
|
5
|
-
gap: var(--spacing-sizing-02);
|
|
6
|
-
font: var(--font);
|
|
7
2
|
width: fit-content;
|
|
3
|
+
position: relative;
|
|
8
4
|
|
|
9
|
-
[
|
|
5
|
+
input[inputMode] {
|
|
6
|
+
position: absolute;
|
|
7
|
+
inset: 0;
|
|
8
|
+
opacity: 0;
|
|
9
|
+
border: none;
|
|
10
|
+
caret-color: transparent;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
[data-digits] {
|
|
10
14
|
display: flex;
|
|
11
|
-
|
|
15
|
+
flex-direction: row;
|
|
12
16
|
justify-content: center;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
aspect-ratio: 1;
|
|
17
|
-
width: var(--width);
|
|
17
|
+
gap: var(--spacing-sizing-02);
|
|
18
|
+
font: var(--font);
|
|
19
|
+
width: fit-content;
|
|
18
20
|
position: relative;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
&:hover:not(:focus)::after {
|
|
22
|
-
background: var(--interaction-hover-opacity);
|
|
23
|
-
content: '';
|
|
24
|
-
position: absolute;
|
|
25
|
-
inset: 0;
|
|
26
|
-
border-radius: var(--radius-sm);
|
|
27
|
-
}
|
|
21
|
+
pointer-events: none;
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
[data-digit] {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: row;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: center;
|
|
34
28
|
border-radius: var(--radius-sm);
|
|
29
|
+
border: 1px solid var(--stroke-neutral-base);
|
|
30
|
+
flex-grow: 1;
|
|
31
|
+
aspect-ratio: 1;
|
|
32
|
+
width: var(--width);
|
|
33
|
+
position: relative;
|
|
34
|
+
outline: none;
|
|
35
|
+
text-align: center;
|
|
35
36
|
}
|
|
37
|
+
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// caret
|
|
42
|
-
content: '';
|
|
43
|
-
width: 2px;
|
|
44
|
-
height: calc(var(--caret-height) - 8px);
|
|
45
|
-
background: var(--stroke-neutral-high);
|
|
46
|
-
animation: blink-caret 1s step-end infinite;
|
|
47
|
-
|
|
48
|
-
@keyframes blink-caret {
|
|
49
|
-
0%,
|
|
50
|
-
100% {
|
|
51
|
-
opacity: 0;
|
|
52
|
-
}
|
|
39
|
+
input[inputMode]:focus + [data-digits] {
|
|
40
|
+
[data-digit] {
|
|
41
|
+
&[data-active] {
|
|
42
|
+
outline: solid 2px var(--stroke-neutral-focus);
|
|
53
43
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
44
|
+
&:empty::before {
|
|
45
|
+
// caret
|
|
46
|
+
content: '';
|
|
47
|
+
width: 2px;
|
|
48
|
+
height: calc(var(--caret-height) - 8px);
|
|
49
|
+
background: var(--stroke-neutral-high);
|
|
50
|
+
animation: blink-caret 1s step-end infinite;
|
|
57
51
|
}
|
|
58
52
|
}
|
|
59
53
|
}
|
|
60
54
|
}
|
|
61
55
|
|
|
56
|
+
@keyframes blink-caret {
|
|
57
|
+
0%,
|
|
58
|
+
100% {
|
|
59
|
+
opacity: 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
50% {
|
|
63
|
+
opacity: 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
62
67
|
&[data-size='small'] {
|
|
63
68
|
--width: var(--spacing-sizing-08);
|
|
64
69
|
--font: var(--subheader-medium);
|
|
@@ -3,6 +3,12 @@ import { Button } from '-/components/Button';
|
|
|
3
3
|
|
|
4
4
|
export type PageListProps = Pick<PaginationProps, 'numPages' | 'onChange' | 'value'>;
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* PageList component displays a list of page buttons for pagination.
|
|
8
|
+
*
|
|
9
|
+
* @name PageList
|
|
10
|
+
* @parent Pagination
|
|
11
|
+
*/
|
|
6
12
|
export function PageList({ numPages, onChange, value }: PageListProps) {
|
|
7
13
|
return Array.from({ length: numPages }, (_, index) => {
|
|
8
14
|
const page = index + 1;
|
|
@@ -68,16 +68,16 @@ export type SelectProps = CommonProps<'size'> &
|
|
|
68
68
|
* @example
|
|
69
69
|
* import { Select } from '@bspk/ui/Select';
|
|
70
70
|
*
|
|
71
|
-
* const OPTIONS = [
|
|
72
|
-
* { id: '1', label: 'Option 1' },
|
|
73
|
-
* { id: '2', label: 'Option 2' },
|
|
74
|
-
* { id: '3', label: 'Option 3' },
|
|
75
|
-
* { id: '4', label: 'Option 4' },
|
|
76
|
-
* { id: '5', label: 'Option 5' },
|
|
77
|
-
* { id: '6', label: 'Option 6' },
|
|
78
|
-
* ];
|
|
79
|
-
*
|
|
80
71
|
* () => {
|
|
72
|
+
* const OPTIONS = [
|
|
73
|
+
* { id: '1', label: 'Option 1' },
|
|
74
|
+
* { id: '2', label: 'Option 2' },
|
|
75
|
+
* { id: '3', label: 'Option 3' },
|
|
76
|
+
* { id: '4', label: 'Option 4' },
|
|
77
|
+
* { id: '5', label: 'Option 5' },
|
|
78
|
+
* { id: '6', label: 'Option 6' },
|
|
79
|
+
* ];
|
|
80
|
+
*
|
|
81
81
|
* const [selected, setSelected] = useState<string[]>([]);
|
|
82
82
|
*
|
|
83
83
|
* return (
|
|
@@ -85,7 +85,7 @@ export type SelectProps = CommonProps<'size'> &
|
|
|
85
85
|
* // standalone select example
|
|
86
86
|
* <Select
|
|
87
87
|
* aria-label="Select an option"
|
|
88
|
-
*
|
|
88
|
+
* scrollLimit={5}
|
|
89
89
|
* name="example-select"
|
|
90
90
|
* onChange={setSelected}
|
|
91
91
|
* options={OPTIONS}
|
|
@@ -98,7 +98,7 @@ export type SelectProps = CommonProps<'size'> &
|
|
|
98
98
|
* <Field>
|
|
99
99
|
* <FieldLabel>Select an option</FieldLabel>
|
|
100
100
|
* <Select
|
|
101
|
-
*
|
|
101
|
+
* scrollLimit={5}
|
|
102
102
|
* name="example-select"
|
|
103
103
|
* onChange={setSelected}
|
|
104
104
|
* options={OPTIONS}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Skeleton, SkeletonProps } from './Skeleton';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* SkeletonCircular component displays a circular skeleton loader.
|
|
5
|
+
*
|
|
6
|
+
* @name SkeletonCircular
|
|
7
|
+
* @parent Skeleton
|
|
8
|
+
*/
|
|
3
9
|
export function SkeletonCircular(props: Pick<SkeletonProps, 'height' | 'width'>) {
|
|
4
10
|
return <Skeleton {...props} variant="circular" />;
|
|
5
11
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Skeleton, SkeletonProps } from './Skeleton';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* SkeletonPhoto component displays a photo skeleton loader.
|
|
5
|
+
*
|
|
6
|
+
* @name SkeletonPhoto
|
|
7
|
+
* @parent Skeleton
|
|
8
|
+
*/
|
|
3
9
|
export function SkeletonPhoto(props: Pick<SkeletonProps, 'height' | 'width'>) {
|
|
4
10
|
return <Skeleton {...props} variant="photo" />;
|
|
5
11
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Skeleton, SkeletonProps } from './Skeleton';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* SkeletonProfile component displays a profile skeleton loader.
|
|
5
|
+
*
|
|
6
|
+
* @name SkeletonProfile
|
|
7
|
+
* @parent Skeleton
|
|
8
|
+
*/
|
|
3
9
|
export function SkeletonProfile(props: Pick<SkeletonProps, 'height' | 'width'>) {
|
|
4
10
|
return <Skeleton {...props} variant="profile" />;
|
|
5
11
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Skeleton, SkeletonProps } from './Skeleton';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* SkeletonRectangular component displays a rectangular skeleton loader.
|
|
5
|
+
*
|
|
6
|
+
* @name SkeletonRectangular
|
|
7
|
+
* @parent Skeleton
|
|
8
|
+
*/
|
|
3
9
|
export function SkeletonRectangular(props: Pick<SkeletonProps, 'height' | 'width'>) {
|
|
4
10
|
return <Skeleton {...props} variant="rectangular" />;
|
|
5
11
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Skeleton, SkeletonProps } from './Skeleton';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* SkeletonThumbnail component displays a thumbnail skeleton loader.
|
|
5
|
+
*
|
|
6
|
+
* @name SkeletonThumbnail
|
|
7
|
+
* @parent Skeleton
|
|
8
|
+
*/
|
|
3
9
|
export function SkeletonThumbnail(props: Pick<SkeletonProps, 'height' | 'width'>) {
|
|
4
10
|
return <Skeleton {...props} variant="thumbnail" />;
|
|
5
11
|
}
|
|
@@ -4,6 +4,12 @@ export type IntervalDotProps = Pick<SliderProps<SliderValue>, 'max' | 'min' | 'v
|
|
|
4
4
|
step: number;
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* SliderIntervalDots component displays interval dots along a slider track.
|
|
9
|
+
*
|
|
10
|
+
* @name SliderIntervalDots
|
|
11
|
+
* @parent Slider
|
|
12
|
+
*/
|
|
7
13
|
export function SliderIntervalDots({ step, max, min, value }: IntervalDotProps) {
|
|
8
14
|
if (step <= 0) return null;
|
|
9
15
|
|
|
@@ -2,7 +2,6 @@ import './tab-list.scss';
|
|
|
2
2
|
import { Fragment, ReactNode, useMemo } from 'react';
|
|
3
3
|
import { Badge, BadgeProps } from '-/components/Badge';
|
|
4
4
|
import { Tooltip } from '-/components/Tooltip';
|
|
5
|
-
import { Truncated } from '-/components/Truncated';
|
|
6
5
|
import { useArrowNavigation } from '-/hooks/useArrowNavigation';
|
|
7
6
|
import { useId } from '-/hooks/useId';
|
|
8
7
|
import { ElementProps } from '-/types/common';
|
|
@@ -238,7 +237,7 @@ export function TabList({
|
|
|
238
237
|
tabIndex={focusableOption.id === item.id ? 0 : -1}
|
|
239
238
|
>
|
|
240
239
|
{icon && <span aria-hidden="true">{icon}</span>}
|
|
241
|
-
{!iconsOnly && <
|
|
240
|
+
{!iconsOnly && <span data-label>{item.label}</span>}
|
|
242
241
|
{item.badge && !item.disabled && (
|
|
243
242
|
<Badge count={item.badge} size={TAB_BADGE_SIZES[size]} />
|
|
244
243
|
)}
|
|
@@ -17,6 +17,12 @@ export type TimePickerListboxProps = {
|
|
|
17
17
|
onTab?: (e: React.KeyboardEvent) => void;
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* TimePickerListbox component displays a listbox for selecting time values.
|
|
22
|
+
*
|
|
23
|
+
* @name TimePickerListbox
|
|
24
|
+
* @parent TimePicker
|
|
25
|
+
*/
|
|
20
26
|
export function TimePickerListbox({ options, selectedValue, type: kind, onSelect, onTab }: TimePickerListboxProps) {
|
|
21
27
|
const { activeElementId, arrowKeyCallbacks } = useArrowNavigation({
|
|
22
28
|
ids: options.map((option) => option.id),
|
|
@@ -19,6 +19,12 @@ export type TimePickerSegmentProps<T extends string> = {
|
|
|
19
19
|
setRef?: (element: HTMLElement | null) => void;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* TimePickerSegment component displays an individual segment of a time picker (hours, minutes, or meridiem).
|
|
24
|
+
*
|
|
25
|
+
* @name TimePickerSegment
|
|
26
|
+
* @parent TimePicker
|
|
27
|
+
*/
|
|
22
28
|
export function TimePickerSegment<T extends string>({
|
|
23
29
|
disabled,
|
|
24
30
|
name,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import './truncated.scss';
|
|
2
1
|
import { ElementType } from 'react';
|
|
3
2
|
import { Tooltip, TooltipTriggerProps } from '-/components/Tooltip';
|
|
4
3
|
import { useTruncatedText } from '-/hooks/useTruncatedText';
|
|
@@ -45,7 +44,7 @@ export function Truncated<As extends ElementType = ElementType>({
|
|
|
45
44
|
const span = (triggerProps: TooltipTriggerProps) => (
|
|
46
45
|
<span
|
|
47
46
|
{...props}
|
|
48
|
-
data-
|
|
47
|
+
data-truncated
|
|
49
48
|
ref={(node) => setElement(node)}
|
|
50
49
|
{...triggerProps}
|
|
51
50
|
role={isTruncated ? 'note' : props.role}
|
|
@@ -22,7 +22,6 @@ export type UIProviderProps = {
|
|
|
22
22
|
* This provider should wrap the root of your application to ensure that all components have access to the UI context.
|
|
23
23
|
*
|
|
24
24
|
* @name UIProvider
|
|
25
|
-
*
|
|
26
25
|
* @phase Utility
|
|
27
26
|
*/
|
|
28
27
|
export function UIProvider({ children }: UIProviderProps) {
|
|
@@ -1,57 +1,67 @@
|
|
|
1
|
-
import { useRef
|
|
2
|
-
|
|
1
|
+
import { useRef } from 'react';
|
|
3
2
|
import { useTimeout } from './useTimeout';
|
|
4
3
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
export type LongPressProps = {
|
|
5
|
+
/** The callback to be invoked on long press. If false is returned, the repeating will stop. */
|
|
6
|
+
callback: () => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* The initial delay (in ms) before repeating starts. Default is 750ms.
|
|
9
|
+
*
|
|
10
|
+
* @default 750
|
|
11
|
+
*/
|
|
12
|
+
delay?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The amount (in percent) to decrement the delay after each repeat. Default is 15%.
|
|
15
|
+
*
|
|
16
|
+
* @default 15
|
|
17
|
+
*/
|
|
18
|
+
delayDecrement?: number;
|
|
19
|
+
/**
|
|
20
|
+
* The minimum delay (in ms) between repeats. Default is 100ms.
|
|
21
|
+
*
|
|
22
|
+
* @default 100
|
|
23
|
+
*/
|
|
24
|
+
delayMin?: number;
|
|
25
|
+
};
|
|
17
26
|
|
|
18
|
-
|
|
27
|
+
/**
|
|
28
|
+
* A hook that provides long press functionality. The provided callback will be invoked once immediately on press, and
|
|
29
|
+
* then repeatedly after a delay, with the delay decreasing by a specified amount after each repeat.
|
|
30
|
+
*/
|
|
31
|
+
export function useLongPress({
|
|
32
|
+
callback,
|
|
33
|
+
delay: initialDelay = 750,
|
|
34
|
+
delayDecrement = 15,
|
|
35
|
+
delayMin = 100,
|
|
36
|
+
}: LongPressProps) {
|
|
19
37
|
const timeout = useTimeout();
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
const isPressing = useRef(false);
|
|
39
|
+
const delay = useRef(initialDelay);
|
|
40
|
+
|
|
41
|
+
const setPressing = (pressing: boolean) => {
|
|
42
|
+
isPressing.current = pressing;
|
|
43
|
+
|
|
44
|
+
if (pressing) {
|
|
45
|
+
if (!callback()) return setPressing(false);
|
|
46
|
+
|
|
47
|
+
timeout.set(() => {
|
|
48
|
+
if (!isPressing.current) return setPressing(false);
|
|
49
|
+
|
|
50
|
+
const decrementMs = (delay.current * delayDecrement) / 100;
|
|
51
|
+
delay.current = delay.current <= delayMin ? delayMin : delay.current - decrementMs;
|
|
52
|
+
|
|
53
|
+
setPressing(true);
|
|
54
|
+
}, delay.current);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
delay.current = initialDelay;
|
|
59
|
+
timeout.clear();
|
|
38
60
|
};
|
|
39
61
|
|
|
40
62
|
return {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
callback();
|
|
45
|
-
timeout.set(run, intervalRef.current);
|
|
46
|
-
},
|
|
47
|
-
onMouseUp: () => {
|
|
48
|
-
timeout.clear();
|
|
49
|
-
},
|
|
50
|
-
onMouseLeave: () => {
|
|
51
|
-
timeout.clear();
|
|
52
|
-
},
|
|
53
|
-
setTriggerElement,
|
|
63
|
+
onPointerDown: () => setPressing(true),
|
|
64
|
+
onPointerLeave: () => setPressing(false),
|
|
65
|
+
onPointerUp: () => setPressing(false),
|
|
54
66
|
};
|
|
55
67
|
}
|
|
56
|
-
|
|
57
|
-
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
package/src/styles/base.scss
CHANGED
|
@@ -179,6 +179,15 @@ body[data-bspk] {
|
|
|
179
179
|
left: auto;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
[data-truncated] {
|
|
183
|
+
max-width: 100%;
|
|
184
|
+
display: inline-block;
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
text-overflow: ellipsis;
|
|
187
|
+
white-space: nowrap;
|
|
188
|
+
background-color: transparent;
|
|
189
|
+
}
|
|
190
|
+
|
|
182
191
|
[data-sr-only] {
|
|
183
192
|
height: 1px;
|
|
184
193
|
overflow: hidden;
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/** * This file is generated by the build script.
|
|
2
|
-
* Do not edit this file directly. */
|
|
3
|
-
const style = document.createElement('style');
|
|
4
|
-
style.appendChild(document.createTextNode(`[data-bspk-utility=truncated] {
|
|
5
|
-
max-width: 100%;
|
|
6
|
-
display: inline-block;
|
|
7
|
-
overflow: hidden;
|
|
8
|
-
text-overflow: ellipsis;
|
|
9
|
-
white-space: nowrap;
|
|
10
|
-
background-color: transparent;
|
|
11
|
-
}
|
|
12
|
-
`));
|
|
13
|
-
document.head.appendChild(style);
|