@bspk/ui 1.3.9 → 1.3.10
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/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 +5 -1
- package/dist/components/OTPInput/OTPInput.js +5 -1
- package/dist/components/OTPInput/OTPInput.js.map +1 -1
- 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/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.tsx +5 -1
- 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,50 +1,34 @@
|
|
|
1
|
-
import { useRef
|
|
1
|
+
import { useRef } from 'react';
|
|
2
2
|
import { useTimeout } from './useTimeout';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
element.getAttribute('disabled') === 'true' ||
|
|
9
|
-
element.getAttribute('aria-disabled') === 'true' ||
|
|
10
|
-
element.getAttribute('data-disabled') === 'true' ||
|
|
11
|
-
!element.isConnected ||
|
|
12
|
-
!element.offsetParent;
|
|
13
|
-
export function useLongPress(callback, disabled) {
|
|
3
|
+
/**
|
|
4
|
+
* A hook that provides long press functionality. The provided callback will be invoked once immediately on press, and
|
|
5
|
+
* then repeatedly after a delay, with the delay decreasing by a specified amount after each repeat.
|
|
6
|
+
*/
|
|
7
|
+
export function useLongPress({ callback, delay: initialDelay = 750, delayDecrement = 15, delayMin = 100, }) {
|
|
14
8
|
const timeout = useTimeout();
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
9
|
+
const isPressing = useRef(false);
|
|
10
|
+
const delay = useRef(initialDelay);
|
|
11
|
+
const setPressing = (pressing) => {
|
|
12
|
+
isPressing.current = pressing;
|
|
13
|
+
if (pressing) {
|
|
14
|
+
if (!callback())
|
|
15
|
+
return setPressing(false);
|
|
16
|
+
timeout.set(() => {
|
|
17
|
+
if (!isPressing.current)
|
|
18
|
+
return setPressing(false);
|
|
19
|
+
const decrementMs = (delay.current * delayDecrement) / 100;
|
|
20
|
+
delay.current = delay.current <= delayMin ? delayMin : delay.current - decrementMs;
|
|
21
|
+
setPressing(true);
|
|
22
|
+
}, delay.current);
|
|
26
23
|
return;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
intervalRef.current = intervalRef.current * INTERVAL_DECREASE_FACTOR;
|
|
31
|
-
timeout.set(run, intervalRef.current);
|
|
24
|
+
}
|
|
25
|
+
delay.current = initialDelay;
|
|
26
|
+
timeout.clear();
|
|
32
27
|
};
|
|
33
28
|
return {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
callback();
|
|
38
|
-
timeout.set(run, intervalRef.current);
|
|
39
|
-
},
|
|
40
|
-
onMouseUp: () => {
|
|
41
|
-
timeout.clear();
|
|
42
|
-
},
|
|
43
|
-
onMouseLeave: () => {
|
|
44
|
-
timeout.clear();
|
|
45
|
-
},
|
|
46
|
-
setTriggerElement,
|
|
29
|
+
onPointerDown: () => setPressing(true),
|
|
30
|
+
onPointerLeave: () => setPressing(false),
|
|
31
|
+
onPointerUp: () => setPressing(false),
|
|
47
32
|
};
|
|
48
33
|
}
|
|
49
|
-
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
|
50
34
|
//# sourceMappingURL=useLongPress.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLongPress.js","sourceRoot":"","sources":["../../src/hooks/useLongPress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"useLongPress.js","sourceRoot":"","sources":["../../src/hooks/useLongPress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAyB1C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EACzB,QAAQ,EACR,KAAK,EAAE,YAAY,GAAG,GAAG,EACzB,cAAc,GAAG,EAAE,EACnB,QAAQ,GAAG,GAAG,GACD;IACb,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;IAEnC,MAAM,WAAW,GAAG,CAAC,QAAiB,EAAE,EAAE;QACtC,UAAU,CAAC,OAAO,GAAG,QAAQ,CAAC;QAE9B,IAAI,QAAQ,EAAE,CAAC;YACX,IAAI,CAAC,QAAQ,EAAE;gBAAE,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;YAE3C,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE;gBACb,IAAI,CAAC,UAAU,CAAC,OAAO;oBAAE,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;gBAEnD,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,OAAO,GAAG,cAAc,CAAC,GAAG,GAAG,CAAC;gBAC3D,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,WAAW,CAAC;gBAEnF,WAAW,CAAC,IAAI,CAAC,CAAC;YACtB,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAClB,OAAO;QACX,CAAC;QAED,KAAK,CAAC,OAAO,GAAG,YAAY,CAAC;QAC7B,OAAO,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC;IAEF,OAAO;QACH,aAAa,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC;QACtC,cAAc,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC;QACxC,WAAW,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC;KACxC,CAAC;AACN,CAAC"}
|
package/dist/styles/base.css
CHANGED
|
@@ -241,6 +241,15 @@ body[data-bspk] [data-bspk=link][data-subtle-inverse][disabled] {
|
|
|
241
241
|
left: auto;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
[data-truncated] {
|
|
245
|
+
max-width: 100%;
|
|
246
|
+
display: inline-block;
|
|
247
|
+
overflow: hidden;
|
|
248
|
+
text-overflow: ellipsis;
|
|
249
|
+
white-space: nowrap;
|
|
250
|
+
background-color: transparent;
|
|
251
|
+
}
|
|
252
|
+
|
|
244
253
|
[data-sr-only] {
|
|
245
254
|
height: 1px;
|
|
246
255
|
overflow: hidden;
|
package/dist/styles/base.css.js
CHANGED
|
@@ -244,6 +244,15 @@ body[data-bspk] [data-bspk=link][data-subtle-inverse][disabled] {
|
|
|
244
244
|
left: auto;
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
[data-truncated] {
|
|
248
|
+
max-width: 100%;
|
|
249
|
+
display: inline-block;
|
|
250
|
+
overflow: hidden;
|
|
251
|
+
text-overflow: ellipsis;
|
|
252
|
+
white-space: nowrap;
|
|
253
|
+
background-color: transparent;
|
|
254
|
+
}
|
|
255
|
+
|
|
247
256
|
[data-sr-only] {
|
|
248
257
|
height: 1px;
|
|
249
258
|
overflow: hidden;
|
package/package.json
CHANGED
|
@@ -56,13 +56,13 @@ export type BannerAlertProps = {
|
|
|
56
56
|
*
|
|
57
57
|
* <BannerAlert
|
|
58
58
|
* elevated={true}
|
|
59
|
-
* variant="
|
|
60
|
-
* header="
|
|
61
|
-
* body="
|
|
59
|
+
* variant="success"
|
|
60
|
+
* header="Success"
|
|
61
|
+
* body="Your request was processed successfully."
|
|
62
62
|
* onClose={() => sendSnackbar('Alert closed')}
|
|
63
63
|
* callToAction={{
|
|
64
|
-
* label
|
|
65
|
-
* onClick
|
|
64
|
+
* label: 'Click me',
|
|
65
|
+
* onClick: () => action('Call to action clicked!'),
|
|
66
66
|
* }}
|
|
67
67
|
* />;
|
|
68
68
|
*
|
|
@@ -37,6 +37,12 @@ export type BreadcrumbDropdownProps = ScrollListItemsStyleProps & {
|
|
|
37
37
|
id: string;
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* The BreadcrumbDropdown component is used to display a dropdown menu within a breadcrumb navigation.
|
|
42
|
+
*
|
|
43
|
+
* @name BreadcrumbDropdown
|
|
44
|
+
* @parent Breadcrumb
|
|
45
|
+
*/
|
|
40
46
|
export function BreadcrumbDropdown({ items: itemsProp, id, scrollLimit }: BreadcrumbDropdownProps) {
|
|
41
47
|
const menuId = `${id}-menu`;
|
|
42
48
|
|
|
@@ -58,6 +58,7 @@ export const presets: Preset<CheckboxGroupProps>[] = [
|
|
|
58
58
|
export const CheckboxGroupExample: ComponentExample<CheckboxGroupProps> = {
|
|
59
59
|
presets,
|
|
60
60
|
defaultState: {
|
|
61
|
+
selectAll: true,
|
|
61
62
|
options: [
|
|
62
63
|
{ label: 'Option 1', value: 'option1', description: 'Description for option 1' },
|
|
63
64
|
{
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { useFieldContext, describedById } from './utils';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* FieldDescription component displays a description associated with a form field.
|
|
5
|
+
*
|
|
6
|
+
* @name FieldDescription
|
|
7
|
+
* @parent Field
|
|
8
|
+
*/
|
|
9
|
+
export function FieldDescription({ children }: { children?: string }) {
|
|
4
10
|
const { id } = useFieldContext();
|
|
5
11
|
|
|
6
12
|
return children ? (
|
|
@@ -9,7 +15,3 @@ function FieldDescription({ children }: { children?: string }) {
|
|
|
9
15
|
</p>
|
|
10
16
|
) : null;
|
|
11
17
|
}
|
|
12
|
-
|
|
13
|
-
FieldDescription.displayName = 'FieldDescription';
|
|
14
|
-
|
|
15
|
-
export { FieldDescription };
|
|
@@ -6,6 +6,12 @@ export type FieldErrorProps = {
|
|
|
6
6
|
children?: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* FieldError component displays an error message associated with a form field.
|
|
11
|
+
*
|
|
12
|
+
* @name FieldError
|
|
13
|
+
* @parent Field
|
|
14
|
+
*/
|
|
9
15
|
export function FieldError({ children }: FieldErrorProps) {
|
|
10
16
|
const { id } = useFieldContext();
|
|
11
17
|
|
|
@@ -14,6 +14,12 @@ export type FieldLabelProps<As extends ElementType = ElementType> = Pick<FieldCo
|
|
|
14
14
|
as?: As;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* FieldLabel component displays a label associated with a form field.
|
|
19
|
+
*
|
|
20
|
+
* @name FieldLabel
|
|
21
|
+
* @parent Field
|
|
22
|
+
*/
|
|
17
23
|
export function FieldLabel<As extends ElementType = ElementType>({
|
|
18
24
|
children,
|
|
19
25
|
labelTrailing,
|
|
@@ -28,6 +28,11 @@ export type FieldContext = FieldContextProps & {
|
|
|
28
28
|
|
|
29
29
|
export const fieldContext = createContext<FieldContext | null>(null);
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the current Field context.
|
|
33
|
+
*
|
|
34
|
+
* Will return a default context if used outside of a Field component.
|
|
35
|
+
*/
|
|
31
36
|
export function useFieldContext(): FieldContext {
|
|
32
37
|
return (
|
|
33
38
|
useContext(fieldContext) || {
|
|
@@ -3,29 +3,39 @@ import { SvgRemove } from '@bspk/icons/Remove';
|
|
|
3
3
|
import { useLongPress } from '-/hooks/useLongPress';
|
|
4
4
|
|
|
5
5
|
export type IncrementButtonProps = {
|
|
6
|
+
/** Whether the button is disabled. */
|
|
6
7
|
disabled: boolean;
|
|
7
|
-
increment
|
|
8
|
-
|
|
8
|
+
/** The kind of increment button, either 'add' or 'remove'. */
|
|
9
|
+
kind: 'add' | 'remove';
|
|
10
|
+
/** The ID of the associated input element. */
|
|
9
11
|
inputId: string;
|
|
12
|
+
/** Function to trigger the increment action. */
|
|
13
|
+
triggerIncrement: (kind: 'add' | 'remove') => boolean;
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* A button component for incrementing or decrementing the InputNumber.
|
|
18
|
+
*
|
|
19
|
+
* @name IncrementButton
|
|
20
|
+
* @parent InputNumber
|
|
21
|
+
*/
|
|
22
|
+
export function IncrementButton({ inputId, kind, disabled, triggerIncrement }: IncrementButtonProps) {
|
|
23
|
+
const { ...pressHandlers } = useLongPress({
|
|
24
|
+
callback: () => triggerIncrement(kind),
|
|
25
|
+
});
|
|
16
26
|
|
|
17
27
|
return (
|
|
18
28
|
<button
|
|
19
|
-
{...
|
|
29
|
+
{...pressHandlers}
|
|
20
30
|
aria-controls={inputId}
|
|
21
|
-
aria-
|
|
22
|
-
data-
|
|
31
|
+
aria-label={kind === 'add' ? 'Increase value' : 'Decrease value'}
|
|
32
|
+
data-bspk="input-number--increment-button"
|
|
33
|
+
data-kind={kind}
|
|
23
34
|
disabled={disabled}
|
|
24
|
-
ref={setTriggerElement}
|
|
25
35
|
tabIndex={-1}
|
|
26
36
|
type="button"
|
|
27
37
|
>
|
|
28
|
-
{add ? <SvgAdd /> : <SvgRemove />}
|
|
38
|
+
{kind === 'add' ? <SvgAdd aria-hidden /> : <SvgRemove aria-hidden />}
|
|
29
39
|
</button>
|
|
30
40
|
);
|
|
31
41
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import './input-number.scss';
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { IncrementButton } from './IncrementButton';
|
|
4
4
|
import { useFieldInit } from '-/components/Field';
|
|
5
5
|
import { useId } from '-/hooks/useId';
|
|
6
6
|
import { CommonProps, FieldControlProps } from '-/types/common';
|
|
7
7
|
|
|
8
|
-
function isNumber(value: unknown
|
|
8
|
+
function isNumber(value: unknown): number | undefined;
|
|
9
|
+
function isNumber(value: unknown, fallbackValue: number): number;
|
|
10
|
+
function isNumber(value: unknown, fallbackValue?: number): number | undefined {
|
|
9
11
|
if (typeof value === 'number') return value;
|
|
10
12
|
if (typeof value !== 'string') return fallbackValue;
|
|
11
|
-
const num =
|
|
13
|
+
const num = parseFloat(value);
|
|
12
14
|
return isNaN(num) ? fallbackValue : num;
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -108,14 +110,25 @@ export function InputNumber({
|
|
|
108
110
|
readOnly,
|
|
109
111
|
invalidProp,
|
|
110
112
|
});
|
|
111
|
-
|
|
112
113
|
const max = typeof maxProp === 'number' && maxProp >= min ? maxProp : Number.MAX_SAFE_INTEGER;
|
|
113
114
|
const centered = align !== 'left';
|
|
114
115
|
const inputId = useId(id);
|
|
115
|
-
const value =
|
|
116
|
+
const value = isNumber(valueProp, min);
|
|
117
|
+
const removeDisabled = disabled || value + step * -1 < min;
|
|
118
|
+
const addDisabled = disabled || value + step > max;
|
|
119
|
+
|
|
120
|
+
const valueRef = useRef(value);
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
valueRef.current = value;
|
|
124
|
+
}, [value]);
|
|
125
|
+
|
|
126
|
+
const incrementHandler = (kind: 'add' | 'remove') => {
|
|
127
|
+
const increment = kind === 'add' ? step : step * -1;
|
|
128
|
+
const next = valueRef.current + increment;
|
|
129
|
+
if (next < min || next > max) return false;
|
|
130
|
+
onChange(next);
|
|
131
|
+
return true;
|
|
119
132
|
};
|
|
120
133
|
|
|
121
134
|
return (
|
|
@@ -128,14 +141,6 @@ export function InputNumber({
|
|
|
128
141
|
data-size={size}
|
|
129
142
|
data-stepper-input
|
|
130
143
|
>
|
|
131
|
-
{!!centered && (
|
|
132
|
-
<IncrementButton
|
|
133
|
-
disabled={disabled ? true : value + -1 < min}
|
|
134
|
-
increment={-1}
|
|
135
|
-
inputId={inputId}
|
|
136
|
-
onIncrement={handleIncrement}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
139
144
|
<input
|
|
140
145
|
{...inputElementProps}
|
|
141
146
|
aria-describedby={ariaDescribedBy || undefined}
|
|
@@ -151,32 +156,29 @@ export function InputNumber({
|
|
|
151
156
|
max={max}
|
|
152
157
|
min={min}
|
|
153
158
|
name={name}
|
|
159
|
+
onBlur={(e) => {
|
|
160
|
+
const next = isNumber(e.target.value, min);
|
|
161
|
+
e.target.value = next?.toString() || '';
|
|
162
|
+
onChange(next);
|
|
163
|
+
}}
|
|
154
164
|
onChange={(e) => {
|
|
155
|
-
|
|
165
|
+
const next = isNumber(e.target.value, min);
|
|
166
|
+
onChange(next);
|
|
156
167
|
}}
|
|
157
168
|
readOnly={readOnly}
|
|
158
169
|
required={required}
|
|
159
170
|
step={step}
|
|
160
171
|
type="number"
|
|
161
|
-
value={value}
|
|
172
|
+
value={value !== undefined ? value : ''}
|
|
162
173
|
/>
|
|
163
|
-
|
|
164
|
-
<>
|
|
165
|
-
<div aria-hidden data-divider />
|
|
166
|
-
<IncrementButton
|
|
167
|
-
disabled={!!disabled || value + -1 < min}
|
|
168
|
-
increment={-1}
|
|
169
|
-
inputId={inputId}
|
|
170
|
-
onIncrement={handleIncrement}
|
|
171
|
-
/>
|
|
172
|
-
</>
|
|
173
|
-
)}
|
|
174
|
+
<div aria-hidden data-divider />
|
|
174
175
|
<IncrementButton
|
|
175
|
-
disabled={
|
|
176
|
-
increment={1}
|
|
176
|
+
disabled={removeDisabled}
|
|
177
177
|
inputId={inputId}
|
|
178
|
-
|
|
178
|
+
kind="remove"
|
|
179
|
+
triggerIncrement={incrementHandler}
|
|
179
180
|
/>
|
|
181
|
+
<IncrementButton disabled={addDisabled} inputId={inputId} kind="add" triggerIncrement={incrementHandler} />
|
|
180
182
|
</div>
|
|
181
183
|
);
|
|
182
184
|
}
|
|
@@ -129,6 +129,16 @@
|
|
|
129
129
|
--height: var(--spacing-sizing-12);
|
|
130
130
|
--svg-width: var(--spacing-sizing-06);
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
&[data-centered] {
|
|
134
|
+
button:first-of-type {
|
|
135
|
+
order: -1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-divider] {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
|
@@ -46,7 +46,7 @@ export type LinkProps = Pick<CommonPropsLibrary, 'disabled'> & {
|
|
|
46
46
|
* @example
|
|
47
47
|
* import { Link } from '@bspk/ui/Link';
|
|
48
48
|
*
|
|
49
|
-
* <Link href="https://
|
|
49
|
+
* <Link href="https://anywhere.re" label="Example label" trailingIcon="external" />;
|
|
50
50
|
*
|
|
51
51
|
* @name Link
|
|
52
52
|
* @phase UXReview
|
|
@@ -34,7 +34,11 @@ export type OTPInputProps = CommonProps<'id' | 'invalid' | 'name' | 'size'> & {
|
|
|
34
34
|
* @example
|
|
35
35
|
* import { OTPInput } from '@bspk/ui/OTPInput';
|
|
36
36
|
*
|
|
37
|
-
*
|
|
37
|
+
* () => {
|
|
38
|
+
* const [otpValue, setOtpValue] = useState('');
|
|
39
|
+
*
|
|
40
|
+
* <OTPInput name="2-auth" length={4} value={otpValue} onChange={setOtpValue} />;
|
|
41
|
+
* };
|
|
38
42
|
*
|
|
39
43
|
* @name OTPInput
|
|
40
44
|
* @phase UXReview
|
|
@@ -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
|
|