@bspk/ui 1.3.2 → 1.3.3
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/DateInput/DateInput.js +1 -0
- package/dist/components/DateInput/DateInput.js.map +1 -1
- package/dist/components/ListItem/ListItem.d.ts +2 -2
- package/dist/components/ListItem/ListItem.js +11 -7
- package/dist/components/ListItem/ListItem.js.map +1 -1
- package/dist/components/ListItem/ListItemExample.d.ts +1 -1
- package/dist/components/ListItem/ListItemExample.js +72 -3
- package/dist/components/ListItem/ListItemExample.js.map +1 -1
- package/dist/components/ListItem/list-item.css +8 -6
- package/dist/components/ListItem/list-item.css.js +8 -6
- package/dist/components/ListItemMenu/ListItemMenu.d.ts +1 -1
- package/dist/components/ListItemMenu/ListItemMenu.js.map +1 -1
- package/dist/components/NumberInput/NumberInput.d.ts +2 -2
- package/dist/components/NumberInput/NumberInput.js +7 -20
- package/dist/components/NumberInput/NumberInput.js.map +1 -1
- package/dist/components/Popover/Popover.js +1 -0
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/ProgressionStepper/ProgressionStepper.d.ts +3 -7
- package/dist/components/ProgressionStepper/ProgressionStepper.js +9 -5
- package/dist/components/ProgressionStepper/ProgressionStepper.js.map +1 -1
- package/dist/components/ProgressionStepper/ProgressionStepperExample.js +20 -4
- package/dist/components/ProgressionStepper/ProgressionStepperExample.js.map +1 -1
- package/dist/components/SearchBar/SearchBar.d.ts +36 -35
- package/dist/components/SearchBar/SearchBar.js +100 -49
- package/dist/components/SearchBar/SearchBar.js.map +1 -1
- package/dist/components/SearchBar/SearchBarExample.js +2 -1
- package/dist/components/SearchBar/SearchBarExample.js.map +1 -1
- package/dist/components/Select/Select.d.ts +2 -2
- package/dist/components/Select/Select.js +3 -5
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/TabList/TabList.js +2 -1
- package/dist/components/TabList/TabList.js.map +1 -1
- package/dist/components/TabList/tab-list.css +6 -0
- package/dist/components/TabList/tab-list.css.js +6 -0
- package/dist/hooks/useArrowNavigation.js +6 -1
- package/dist/hooks/useArrowNavigation.js.map +1 -1
- package/dist/hooks/useOutsideClick.d.ts +4 -3
- package/dist/hooks/useOutsideClick.js +13 -2
- package/dist/hooks/useOutsideClick.js.map +1 -1
- package/package.json +1 -1
- package/src/components/DateInput/DateInput.tsx +1 -0
- package/src/components/ListItem/ListItem.rtl.test.tsx +4 -1
- package/src/components/ListItem/ListItem.tsx +19 -12
- package/src/components/ListItem/ListItemExample.tsx +74 -4
- package/src/components/ListItem/list-item.scss +21 -17
- package/src/components/ListItemMenu/ListItemMenu.tsx +4 -3
- package/src/components/NumberInput/NumberInput.tsx +14 -27
- package/src/components/Popover/Popover.tsx +1 -0
- package/src/components/ProgressionStepper/ProgressionStepper.tsx +24 -15
- package/src/components/ProgressionStepper/ProgressionStepperExample.tsx +20 -4
- package/src/components/SearchBar/SearchBar.rtl.test.tsx +0 -1
- package/src/components/SearchBar/SearchBar.tsx +192 -115
- package/src/components/SearchBar/SearchBarExample.tsx +2 -1
- package/src/components/Select/Select.tsx +6 -12
- package/src/components/TabList/TabList.tsx +2 -1
- package/src/components/TabList/tab-list.scss +12 -0
- package/src/hooks/useArrowNavigation.ts +6 -1
- package/src/hooks/useOutsideClick.ts +16 -3
|
@@ -13,9 +13,7 @@ import { Truncated } from '-/components/Truncated';
|
|
|
13
13
|
import { useId } from '-/hooks/useId';
|
|
14
14
|
import { CommonProps, ElementProps, SetRef } from '-/types/common';
|
|
15
15
|
|
|
16
|
-
export type ListItemProps<As extends ElementType = ElementType> = CommonProps<
|
|
17
|
-
'active' | 'disabled' | 'owner' | 'readOnly'
|
|
18
|
-
> &
|
|
16
|
+
export type ListItemProps<As extends ElementType = ElementType> = CommonProps<'active' | 'owner'> &
|
|
19
17
|
Pick<AriaAttributes, 'aria-label'> & {
|
|
20
18
|
/**
|
|
21
19
|
* The element type to render as.
|
|
@@ -123,14 +121,12 @@ export type ListItemProps<As extends ElementType = ElementType> = CommonProps<
|
|
|
123
121
|
* @name ListItem
|
|
124
122
|
* @phase UXReview
|
|
125
123
|
*/
|
|
126
|
-
function ListItem<As extends ElementType =
|
|
124
|
+
function ListItem<As extends ElementType = 'div'>({
|
|
127
125
|
active,
|
|
128
126
|
as,
|
|
129
|
-
disabled,
|
|
130
127
|
innerRef,
|
|
131
128
|
label,
|
|
132
129
|
leading,
|
|
133
|
-
readOnly,
|
|
134
130
|
owner,
|
|
135
131
|
role: roleProp,
|
|
136
132
|
subText,
|
|
@@ -138,6 +134,8 @@ function ListItem<As extends ElementType = ElementType>({
|
|
|
138
134
|
id: idProp,
|
|
139
135
|
'aria-label': ariaLabel,
|
|
140
136
|
'aria-selected': ariaSelected,
|
|
137
|
+
'aria-disabled': ariaDisabled,
|
|
138
|
+
'aria-readonly': ariaReadonly,
|
|
141
139
|
hideAriaLabel,
|
|
142
140
|
...props
|
|
143
141
|
}: ElementProps<ListItemProps<As>, As>) {
|
|
@@ -146,24 +144,30 @@ function ListItem<As extends ElementType = ElementType>({
|
|
|
146
144
|
if (!label) return null;
|
|
147
145
|
|
|
148
146
|
const As = asLogic(as, props);
|
|
149
|
-
|
|
150
|
-
const
|
|
147
|
+
|
|
148
|
+
const isReadOnly = Boolean(props.readOnly || ariaReadonly);
|
|
149
|
+
const isDisabled = Boolean(props.disabled || ariaDisabled);
|
|
150
|
+
|
|
151
|
+
const actionable = !!(props.href || props.onClick || as === 'button') && !isReadOnly && !isDisabled;
|
|
152
|
+
|
|
153
|
+
const role = roleLogic(roleProp, { as: As, props, actionable });
|
|
151
154
|
|
|
152
155
|
return (
|
|
153
156
|
<As
|
|
154
157
|
{...props}
|
|
155
|
-
aria-disabled={disabled || undefined}
|
|
156
158
|
aria-label={ariaLabel || undefined}
|
|
157
159
|
aria-selected={ariaSelected}
|
|
158
160
|
data-action={actionable || undefined}
|
|
159
161
|
data-active={active || undefined}
|
|
160
162
|
data-bspk="list-item"
|
|
161
163
|
data-bspk-owner={owner || undefined}
|
|
162
|
-
data-
|
|
164
|
+
data-disabled={isDisabled || undefined}
|
|
165
|
+
data-readonly={isReadOnly || undefined}
|
|
163
166
|
id={id}
|
|
167
|
+
onClick={isReadOnly || isDisabled ? undefined : props.onClick}
|
|
164
168
|
ref={innerRef}
|
|
165
169
|
role={role}
|
|
166
|
-
tabIndex={props.tabIndex || (actionable ? 0 :
|
|
170
|
+
tabIndex={props.tabIndex || (actionable ? 0 : -1)}
|
|
167
171
|
>
|
|
168
172
|
{leading && (
|
|
169
173
|
<span aria-hidden data-leading>
|
|
@@ -190,18 +194,21 @@ function asLogic<As extends ElementType>(as: As | undefined, props: Partial<List
|
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
function roleLogic(
|
|
197
|
+
/** User provided role prop */
|
|
193
198
|
existingRole: AriaRole | undefined,
|
|
194
199
|
{
|
|
195
200
|
as: As,
|
|
196
201
|
props,
|
|
202
|
+
actionable,
|
|
197
203
|
}: {
|
|
198
204
|
as: ElementType;
|
|
199
205
|
props: Partial<ListItemProps>;
|
|
206
|
+
actionable?: boolean;
|
|
200
207
|
},
|
|
201
208
|
): HTMLAttributes<HTMLElement>['role'] | undefined {
|
|
202
209
|
if (existingRole) return existingRole;
|
|
203
210
|
|
|
204
|
-
if (
|
|
211
|
+
if (!actionable) return undefined;
|
|
205
212
|
|
|
206
213
|
if (props.onClick && As !== 'button' && As !== 'label') return 'button';
|
|
207
214
|
|
|
@@ -1,22 +1,90 @@
|
|
|
1
1
|
import { ElementType } from 'react';
|
|
2
2
|
import { ListItem, ListItemProps } from './ListItem';
|
|
3
|
+
import { ElementProps } from '-/types/common';
|
|
3
4
|
import { createExampleChildElement } from '-/utils/createExampleChildElement';
|
|
4
5
|
import { ComponentExampleFn, Preset } from '-/utils/demo';
|
|
5
6
|
|
|
6
|
-
export const presets: Preset<ListItemProps>[]
|
|
7
|
+
export const presets = (action: (param: string) => void): Preset<ListItemProps>[] => [
|
|
7
8
|
{
|
|
8
9
|
label: 'Long Label',
|
|
9
10
|
propState: {
|
|
10
11
|
label: 'This is a really long label that should be truncated if it exceeds the width of the ListItem',
|
|
12
|
+
as: undefined,
|
|
11
13
|
subText: 'See below for other leading and trailing examples',
|
|
12
14
|
trailing: 'Checkbox',
|
|
13
15
|
leading: 'Avatar',
|
|
14
|
-
|
|
16
|
+
disabled: undefined,
|
|
17
|
+
readOnly: undefined,
|
|
18
|
+
'aria-disabled': undefined,
|
|
19
|
+
'aria-readonly': undefined,
|
|
20
|
+
} as ElementProps<ListItemProps<'div'>, 'div'>,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: 'As Role Button',
|
|
24
|
+
propState: {
|
|
25
|
+
as: undefined,
|
|
26
|
+
label: 'as="button"',
|
|
27
|
+
role: 'button',
|
|
28
|
+
subText: 'Button example',
|
|
29
|
+
trailing: undefined,
|
|
30
|
+
leading: undefined,
|
|
31
|
+
'aria-disabled': true,
|
|
32
|
+
disabled: undefined,
|
|
33
|
+
readOnly: undefined,
|
|
34
|
+
'aria-readonly': undefined,
|
|
35
|
+
onClick: () => action('This is aria disabled and should not show'),
|
|
36
|
+
} as ElementProps<ListItemProps<'div'>, 'div'>,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: 'As Button',
|
|
40
|
+
propState: {
|
|
41
|
+
label: 'as="button"',
|
|
42
|
+
as: 'button',
|
|
43
|
+
subText: 'Button example',
|
|
44
|
+
trailing: undefined,
|
|
45
|
+
leading: undefined,
|
|
46
|
+
disabled: undefined,
|
|
47
|
+
readOnly: undefined,
|
|
48
|
+
'aria-disabled': undefined,
|
|
49
|
+
'aria-readonly': undefined,
|
|
50
|
+
onClick: () => action('This is not disabled or readonly and should show'),
|
|
51
|
+
} as ElementProps<ListItemProps<'button'>, 'button'>,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
label: 'As Button Disabled',
|
|
55
|
+
propState: {
|
|
56
|
+
label: 'as="button"',
|
|
57
|
+
as: 'button',
|
|
58
|
+
subText: 'Disabled button example',
|
|
59
|
+
trailing: undefined,
|
|
60
|
+
leading: undefined,
|
|
61
|
+
disabled: true,
|
|
62
|
+
readOnly: false,
|
|
63
|
+
'aria-disabled': undefined,
|
|
64
|
+
'aria-readonly': undefined,
|
|
65
|
+
onClick: () => action('This is disabled and should not show'),
|
|
66
|
+
} as ElementProps<ListItemProps<'button'>, 'button'>,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
label: 'As Button Read Only',
|
|
70
|
+
propState: {
|
|
71
|
+
label: 'as="button"',
|
|
72
|
+
as: 'button',
|
|
73
|
+
subText: 'Read only button example',
|
|
74
|
+
trailing: undefined,
|
|
75
|
+
leading: undefined,
|
|
76
|
+
readOnly: true,
|
|
77
|
+
disabled: undefined,
|
|
78
|
+
'aria-disabled': undefined,
|
|
79
|
+
'aria-readonly': undefined,
|
|
80
|
+
onClick: () => action('This is readonly and should not show'),
|
|
81
|
+
} as ElementProps<ListItemProps<'button'>, 'button'>,
|
|
15
82
|
},
|
|
16
83
|
];
|
|
17
84
|
|
|
18
85
|
export const ListItemExample: ComponentExampleFn<ListItemProps> = ({ action, setState }) => ({
|
|
19
|
-
|
|
86
|
+
presets: presets(action),
|
|
87
|
+
render: ({ props, id, preset }) => {
|
|
20
88
|
const leading = createExampleChildElement({
|
|
21
89
|
exampleState: props,
|
|
22
90
|
name: 'leading',
|
|
@@ -34,9 +102,11 @@ export const ListItemExample: ComponentExampleFn<ListItemProps> = ({ action, set
|
|
|
34
102
|
|
|
35
103
|
let as: ElementType = props.as || 'div';
|
|
36
104
|
|
|
105
|
+
if (!preset) return null;
|
|
106
|
+
|
|
37
107
|
if (trailing.componentName && ['Checkbox', 'Radio', 'Switch'].includes(trailing.componentName)) as = 'label';
|
|
38
108
|
|
|
39
109
|
return <ListItem {...props} as={as} leading={leading.element} trailing={trailing.element} />;
|
|
40
110
|
},
|
|
41
|
-
|
|
111
|
+
variants: false,
|
|
42
112
|
});
|
|
@@ -25,18 +25,20 @@
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
28
|
+
&:not([data-disabled], [data-readonly]) {
|
|
29
|
+
&[data-action],
|
|
30
|
+
&:is(label) {
|
|
31
|
+
&[data-active],
|
|
32
|
+
[data-pseudo='hover'] &,
|
|
33
|
+
&:hover {
|
|
34
|
+
background-color: var(--interactions-neutral-hover-opacity);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
[data-pseudo='active'] &,
|
|
37
38
|
// pressed state
|
|
38
39
|
&:active {
|
|
39
|
-
|
|
40
|
+
background-color: var(--interactions-neutral-press-opacity);
|
|
41
|
+
}
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -78,13 +80,6 @@
|
|
|
78
80
|
}
|
|
79
81
|
}
|
|
80
82
|
|
|
81
|
-
&[aria-disabled] {
|
|
82
|
-
[data-text],
|
|
83
|
-
[data-sub-text] {
|
|
84
|
-
color: var(--foreground-neutral-disabled-on-surface);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
83
|
[data-trailing]:has(input) {
|
|
89
84
|
pointer-events: none;
|
|
90
85
|
}
|
|
@@ -105,6 +100,15 @@
|
|
|
105
100
|
&[aria-selected='true'] {
|
|
106
101
|
background-color: var(--surface-brand-primary-highlight);
|
|
107
102
|
}
|
|
103
|
+
|
|
104
|
+
&[data-disabled],
|
|
105
|
+
&[data-readonly] {
|
|
106
|
+
[data-text],
|
|
107
|
+
[data-sub-text] {
|
|
108
|
+
color: var(--foreground-neutral-disabled-on-surface);
|
|
109
|
+
cursor: not-allowed;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
108
112
|
}
|
|
109
113
|
|
|
110
114
|
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
|
@@ -20,9 +20,10 @@ import { CommonProps, SetRef } from '-/types/common';
|
|
|
20
20
|
import { getElementById } from '-/utils/dom';
|
|
21
21
|
import { handleKeyDown } from '-/utils/handleKeyDown';
|
|
22
22
|
|
|
23
|
-
export type MenuListItem =
|
|
24
|
-
id
|
|
25
|
-
|
|
23
|
+
export type MenuListItem = CommonProps<'disabled'> &
|
|
24
|
+
Omit<ListItemProps, 'id'> & {
|
|
25
|
+
id: string;
|
|
26
|
+
};
|
|
26
27
|
|
|
27
28
|
export type ListItemMenuRole = keyof typeof LIST_ITEM_ROLES;
|
|
28
29
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import './number-input.scss';
|
|
2
|
-
import {
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
3
|
import { IncrementButton } from './IncrementButton';
|
|
4
4
|
import { useId } from '-/hooks/useId';
|
|
5
5
|
import { CommonProps, FormFieldControlProps } from '-/types/common';
|
|
@@ -16,7 +16,7 @@ export type NumberInputProps = CommonProps<
|
|
|
16
16
|
> &
|
|
17
17
|
FormFieldControlProps & {
|
|
18
18
|
/** The value of the control. */
|
|
19
|
-
value?: number
|
|
19
|
+
value?: number;
|
|
20
20
|
/**
|
|
21
21
|
* Callback when the value changes.
|
|
22
22
|
*
|
|
@@ -75,7 +75,7 @@ export type NumberInputProps = CommonProps<
|
|
|
75
75
|
* @phase Utility
|
|
76
76
|
*/
|
|
77
77
|
export function NumberInput({
|
|
78
|
-
value,
|
|
78
|
+
value: valueProp,
|
|
79
79
|
onChange,
|
|
80
80
|
align = 'center',
|
|
81
81
|
size = 'medium',
|
|
@@ -95,27 +95,12 @@ export function NumberInput({
|
|
|
95
95
|
const max = typeof maxProp === 'number' && maxProp >= min ? maxProp : Number.MAX_SAFE_INTEGER;
|
|
96
96
|
const centered = align !== 'left';
|
|
97
97
|
const inputId = useId(inputIdProp);
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null);
|
|
98
|
+
const value = useMemo(() => isNumber(valueProp) || 0, [valueProp]);
|
|
101
99
|
|
|
102
100
|
const handleIncrement = (increment: -1 | 1) => {
|
|
103
|
-
|
|
104
|
-
const nextValue = (isNumber(inputElement.value) || 0) + increment * step;
|
|
105
|
-
handleUpdate(nextValue);
|
|
101
|
+
onChange(value + increment * step);
|
|
106
102
|
};
|
|
107
103
|
|
|
108
|
-
const handleUpdate = useCallback(
|
|
109
|
-
(nextValue: number | undefined) => {
|
|
110
|
-
if (!inputElement) return;
|
|
111
|
-
let nextVal = isNumber(nextValue);
|
|
112
|
-
if (nextVal !== undefined) nextVal = Math.min(Math.max(nextVal, min), max);
|
|
113
|
-
onChange(nextVal);
|
|
114
|
-
inputElement.value = nextVal?.toString() || '';
|
|
115
|
-
},
|
|
116
|
-
[inputElement, min, max, onChange],
|
|
117
|
-
);
|
|
118
|
-
|
|
119
104
|
return (
|
|
120
105
|
<div
|
|
121
106
|
data-bspk="number-input"
|
|
@@ -128,7 +113,7 @@ export function NumberInput({
|
|
|
128
113
|
>
|
|
129
114
|
{!!centered && (
|
|
130
115
|
<IncrementButton
|
|
131
|
-
disabled={disabled ? true :
|
|
116
|
+
disabled={disabled ? true : value + -1 < min}
|
|
132
117
|
increment={-1}
|
|
133
118
|
inputId={inputId}
|
|
134
119
|
onIncrement={handleIncrement}
|
|
@@ -141,25 +126,27 @@ export function NumberInput({
|
|
|
141
126
|
aria-invalid={invalid}
|
|
142
127
|
aria-label={ariaLabel}
|
|
143
128
|
autoComplete="off"
|
|
144
|
-
|
|
129
|
+
data-input
|
|
130
|
+
data-stepper-input-element
|
|
145
131
|
disabled={disabled}
|
|
146
132
|
id={inputId}
|
|
133
|
+
inputMode="numeric"
|
|
147
134
|
max={max}
|
|
148
135
|
min={min}
|
|
149
136
|
name={name}
|
|
150
|
-
|
|
151
|
-
|
|
137
|
+
onChange={(e) => {
|
|
138
|
+
onChange(isNumber(e.target.value));
|
|
152
139
|
}}
|
|
153
140
|
readOnly={readOnly}
|
|
154
|
-
ref={(node) => node && setInputElement(node)}
|
|
155
141
|
step={step}
|
|
156
142
|
type="number"
|
|
143
|
+
value={value}
|
|
157
144
|
/>
|
|
158
145
|
{!centered && (
|
|
159
146
|
<>
|
|
160
147
|
<div aria-hidden data-divider />
|
|
161
148
|
<IncrementButton
|
|
162
|
-
disabled={disabled
|
|
149
|
+
disabled={!!disabled || value + -1 < min}
|
|
163
150
|
increment={-1}
|
|
164
151
|
inputId={inputId}
|
|
165
152
|
onIncrement={handleIncrement}
|
|
@@ -167,7 +154,7 @@ export function NumberInput({
|
|
|
167
154
|
</>
|
|
168
155
|
)}
|
|
169
156
|
<IncrementButton
|
|
170
|
-
disabled={disabled
|
|
157
|
+
disabled={!!disabled || value + 1 > max}
|
|
171
158
|
increment={1}
|
|
172
159
|
inputId={inputId}
|
|
173
160
|
onIncrement={handleIncrement}
|
|
@@ -37,16 +37,12 @@ export type ProgressionStepperProps = {
|
|
|
37
37
|
*/
|
|
38
38
|
steps: ProgressionStepperItem[];
|
|
39
39
|
/**
|
|
40
|
-
* The
|
|
41
|
-
*
|
|
42
|
-
* If the current step is greater than the number of steps, all steps with be completed.
|
|
43
|
-
*
|
|
44
|
-
* If the current step is less than 1, all steps will be incomplete.
|
|
40
|
+
* The last completed step in the progress bar. This is only applicable for the 'widget' variant.
|
|
45
41
|
*
|
|
46
42
|
* @default 0
|
|
47
43
|
* @minimum 0
|
|
48
44
|
*/
|
|
49
|
-
|
|
45
|
+
completedStep?: number;
|
|
50
46
|
/**
|
|
51
47
|
* The variant of the progress bar. Can be either horizontal, vertical, or widget.
|
|
52
48
|
*
|
|
@@ -72,22 +68,35 @@ export type ProgressionStepperProps = {
|
|
|
72
68
|
*/
|
|
73
69
|
export function ProgressionStepper({
|
|
74
70
|
steps = [],
|
|
75
|
-
|
|
71
|
+
completedStep: completedStepProp = 0,
|
|
76
72
|
variant = 'horizontal',
|
|
77
73
|
...containerProps
|
|
78
74
|
}: ElementProps<ProgressionStepperProps, 'div'>) {
|
|
79
|
-
const
|
|
80
|
-
|
|
75
|
+
const completedStepNumber = Math.max(0, Math.min(completedStepProp, steps.length));
|
|
76
|
+
|
|
77
|
+
const currentStepNumber = Math.min(completedStepNumber + 1, steps.length);
|
|
78
|
+
|
|
79
|
+
const currentStep = steps[currentStepNumber - 1];
|
|
80
|
+
|
|
81
|
+
if (!steps?.length) return null;
|
|
82
|
+
|
|
83
|
+
return (
|
|
81
84
|
<div {...containerProps} data-bspk="progression-stepper" data-variant={variant}>
|
|
82
85
|
{variant === 'widget' && (
|
|
83
86
|
<label>
|
|
84
|
-
<span data-title>{
|
|
87
|
+
<span data-title>{currentStep.name}</span>
|
|
85
88
|
<span data-subtitle>
|
|
86
|
-
{currentStep
|
|
87
|
-
|
|
89
|
+
{currentStep.subtext ? (
|
|
90
|
+
currentStep.subtext
|
|
88
91
|
) : (
|
|
89
92
|
<>
|
|
90
|
-
|
|
93
|
+
{completedStepNumber === steps.length ? (
|
|
94
|
+
'Completed'
|
|
95
|
+
) : (
|
|
96
|
+
<>
|
|
97
|
+
Step {currentStepNumber} of {steps.length}
|
|
98
|
+
</>
|
|
99
|
+
)}
|
|
91
100
|
</>
|
|
92
101
|
)}
|
|
93
102
|
</span>
|
|
@@ -97,8 +106,8 @@ export function ProgressionStepper({
|
|
|
97
106
|
{steps.map(({ name, subtext }, index) => {
|
|
98
107
|
const stepNum = index + 1;
|
|
99
108
|
let status: 'complete' | 'current' | 'incomplete' = 'incomplete';
|
|
100
|
-
if (
|
|
101
|
-
else if (
|
|
109
|
+
if (completedStepNumber >= stepNum) status = 'complete';
|
|
110
|
+
else if (completedStepNumber + 1 === stepNum) status = 'current';
|
|
102
111
|
|
|
103
112
|
return (
|
|
104
113
|
<li
|
|
@@ -6,7 +6,7 @@ export const presets: Preset<ProgressionStepperProps>[] = [
|
|
|
6
6
|
label: 'Horizontal',
|
|
7
7
|
propState: {
|
|
8
8
|
variant: 'horizontal',
|
|
9
|
-
|
|
9
|
+
completedStep: 2,
|
|
10
10
|
steps: [{ name: 'Name of step 1' }, { name: 'Name of step 2' }, { name: 'Name of step 3' }],
|
|
11
11
|
},
|
|
12
12
|
},
|
|
@@ -14,7 +14,7 @@ export const presets: Preset<ProgressionStepperProps>[] = [
|
|
|
14
14
|
label: 'Vertical',
|
|
15
15
|
propState: {
|
|
16
16
|
variant: 'vertical',
|
|
17
|
-
|
|
17
|
+
completedStep: 2,
|
|
18
18
|
steps: [
|
|
19
19
|
{
|
|
20
20
|
name: 'Name of step 1',
|
|
@@ -35,7 +35,7 @@ export const presets: Preset<ProgressionStepperProps>[] = [
|
|
|
35
35
|
label: 'Widget',
|
|
36
36
|
propState: {
|
|
37
37
|
variant: 'widget',
|
|
38
|
-
|
|
38
|
+
completedStep: 3,
|
|
39
39
|
steps: [
|
|
40
40
|
{ name: 'Name of step 1' },
|
|
41
41
|
{ name: 'Name of step 2' },
|
|
@@ -43,7 +43,23 @@ export const presets: Preset<ProgressionStepperProps>[] = [
|
|
|
43
43
|
{ name: 'Name of step 4' },
|
|
44
44
|
{ name: 'Name of step 5' },
|
|
45
45
|
{ name: 'Name of step 6' },
|
|
46
|
-
{ name: '
|
|
46
|
+
{ name: 'Review' },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'Widget (w/ subtext)',
|
|
52
|
+
propState: {
|
|
53
|
+
variant: 'widget',
|
|
54
|
+
completedStep: 3,
|
|
55
|
+
steps: [
|
|
56
|
+
{ name: 'Name of step 1' },
|
|
57
|
+
{ name: 'Name of step 2' },
|
|
58
|
+
{ name: 'Name of step 3' },
|
|
59
|
+
{ name: 'Name of step 4' },
|
|
60
|
+
{ name: 'Name of step 5' },
|
|
61
|
+
{ name: 'Name of step 6' },
|
|
62
|
+
{ name: 'Review', subtext: `Check your information...` },
|
|
47
63
|
],
|
|
48
64
|
},
|
|
49
65
|
},
|