@bspk/ui 1.1.13 → 1.1.15

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.
Files changed (87) hide show
  1. package/dist/Checkbox.d.ts +1 -1
  2. package/dist/Checkbox.js +2 -2
  3. package/dist/Checkbox.js.map +1 -1
  4. package/dist/CheckboxOption.d.ts +2 -1
  5. package/dist/Dropdown.d.ts +3 -3
  6. package/dist/Dropdown.js +2 -1
  7. package/dist/Dropdown.js.map +1 -1
  8. package/dist/FormField.d.ts +5 -4
  9. package/dist/FormField.js +3 -3
  10. package/dist/FormField.js.map +1 -1
  11. package/dist/InlineAlert.js +1 -1
  12. package/dist/Link.d.ts +2 -1
  13. package/dist/Link.js.map +1 -1
  14. package/dist/ListItem.d.ts +1 -1
  15. package/dist/ListItem.js +2 -2
  16. package/dist/ListItem.js.map +1 -1
  17. package/dist/NumberField.d.ts +2 -1
  18. package/dist/NumberInput.d.ts +23 -13
  19. package/dist/NumberInput.js +15 -4
  20. package/dist/NumberInput.js.map +1 -1
  21. package/dist/Radio.d.ts +2 -2
  22. package/dist/Radio.js.map +1 -1
  23. package/dist/RadioOption.d.ts +2 -1
  24. package/dist/RadioOption.js.map +1 -1
  25. package/dist/SegmentedControl.d.ts +11 -7
  26. package/dist/SegmentedControl.js +7 -6
  27. package/dist/SegmentedControl.js.map +1 -1
  28. package/dist/StylesProviderAnywhere.js +1 -1
  29. package/dist/StylesProviderBetterHomesGardens.js +1 -1
  30. package/dist/StylesProviderCartus.js +1 -1
  31. package/dist/StylesProviderCentury21.js +1 -1
  32. package/dist/StylesProviderColdwellBanker.js +1 -1
  33. package/dist/StylesProviderCorcoran.js +1 -1
  34. package/dist/StylesProviderDenaliBoss.js +1 -1
  35. package/dist/StylesProviderEra.js +1 -1
  36. package/dist/StylesProviderSothebys.js +1 -1
  37. package/dist/TabGroup.d.ts +15 -6
  38. package/dist/TabGroup.js +13 -5
  39. package/dist/TabGroup.js.map +1 -1
  40. package/dist/TextField.d.ts +2 -1
  41. package/dist/TextField.js.map +1 -1
  42. package/dist/TextInput.d.ts +3 -3
  43. package/dist/TextInput.js +2 -2
  44. package/dist/TextInput.js.map +1 -1
  45. package/dist/Textarea.d.ts +20 -10
  46. package/dist/Textarea.js +15 -8
  47. package/dist/Textarea.js.map +1 -1
  48. package/dist/base.css +1 -1
  49. package/dist/form-field.css +1 -1
  50. package/dist/hooks/useFloatingMenu.d.ts +3 -2
  51. package/dist/hooks/useFloatingMenu.js +1 -0
  52. package/dist/hooks/useFloatingMenu.js.map +1 -1
  53. package/dist/hooks/{useNavOptions.d.ts → useOptionIconsInvalid.d.ts} +4 -5
  54. package/dist/hooks/useOptionIconsInvalid.js +21 -0
  55. package/dist/hooks/useOptionIconsInvalid.js.map +1 -0
  56. package/dist/index.d.ts +22 -13
  57. package/dist/index.js.map +1 -1
  58. package/dist/inline-alert.css +1 -1
  59. package/dist/number-input.css +1 -1
  60. package/dist/textarea.css +1 -1
  61. package/package.json +2 -2
  62. package/src/Checkbox.tsx +3 -0
  63. package/src/CheckboxOption.tsx +4 -4
  64. package/src/Dropdown.tsx +5 -2
  65. package/src/FormField.tsx +35 -24
  66. package/src/Link.tsx +28 -25
  67. package/src/ListItem.tsx +2 -0
  68. package/src/NumberField.tsx +5 -5
  69. package/src/NumberInput.tsx +51 -30
  70. package/src/Radio.tsx +3 -5
  71. package/src/RadioOption.tsx +4 -1
  72. package/src/SegmentedControl.tsx +18 -13
  73. package/src/TabGroup.tsx +33 -12
  74. package/src/TextField.tsx +19 -17
  75. package/src/TextInput.tsx +33 -30
  76. package/src/Textarea.tsx +72 -56
  77. package/src/base.scss +4 -3
  78. package/src/form-field.scss +2 -1
  79. package/src/hooks/useFloatingMenu.ts +4 -2
  80. package/src/hooks/useOptionIconsInvalid.ts +49 -0
  81. package/src/index.ts +24 -13
  82. package/src/inline-alert.scss +1 -1
  83. package/src/number-input.scss +6 -3
  84. package/src/textarea.scss +5 -2
  85. package/dist/hooks/useNavOptions.js +0 -43
  86. package/dist/hooks/useNavOptions.js.map +0 -1
  87. package/src/hooks/useNavOptions.ts +0 -76
package/src/TabGroup.tsx CHANGED
@@ -1,11 +1,19 @@
1
1
  import './tab-group.scss';
2
2
  import { ReactNode } from 'react';
3
3
 
4
- import { Badge } from './Badge';
5
- import { useNavOptions } from './hooks/useNavOptions';
4
+ import { Badge, BadgeProps } from './Badge';
5
+ import { useOptionIconsInvalid } from './hooks/useOptionIconsInvalid';
6
6
 
7
7
  import { ElementProps } from './';
8
8
 
9
+ export type TabGroupSize = 'large' | 'medium' | 'small';
10
+
11
+ const TAB_BADGE_SIZES: Record<TabGroupSize, BadgeProps['size']> = {
12
+ large: 'small',
13
+ medium: 'x-small',
14
+ small: 'x-small',
15
+ };
16
+
9
17
  export type TabGroupOption = {
10
18
  /**
11
19
  * The label of the tab. This is the text that will be displayed on the tab.
@@ -19,7 +27,11 @@ export type TabGroupOption = {
19
27
  * @default false
20
28
  */
21
29
  disabled?: boolean;
22
- /** The value of the tab. If not provided, the label will be used as the value. */
30
+ /**
31
+ * The value of the tab sent to onChange when selected.
32
+ *
33
+ * If not provided, the label will be used as the value.
34
+ */
23
35
  value?: string;
24
36
  /** The icon to display on the left side of the tab. */
25
37
  icon?: ReactNode;
@@ -37,20 +49,24 @@ export type TabGroupProps = {
37
49
  * @required
38
50
  */
39
51
  options: TabGroupOption[];
40
- /** The id of the selected tab. */
41
- value?: TabGroupOption['value'];
52
+ /**
53
+ * The id of the selected tab.
54
+ *
55
+ * @required
56
+ */
57
+ value: TabGroupOption['value'];
42
58
  /**
43
59
  * The function to call when the tab is clicked.
44
60
  *
45
61
  * @required
46
62
  */
47
- onChange: (tabId: TabGroupOption['value']) => void;
63
+ onChange: (tabValue: string, index: number) => void;
48
64
  /**
49
65
  * The size of the tabs.
50
66
  *
51
67
  * @default medium
52
68
  */
53
- size?: 'large' | 'medium' | 'small';
69
+ size?: TabGroupSize;
54
70
  /**
55
71
  * When 'fill' the options will fill the width of the container. When 'hug', the options will be as wide as their
56
72
  * content.
@@ -77,12 +93,13 @@ function TabGroup({
77
93
  onChange: onTabChange,
78
94
  value,
79
95
  size = 'medium',
80
- options,
96
+ options: optionsProp,
81
97
  width = 'hug',
82
98
  showTrail = false,
83
99
  ...containerProps
84
100
  }: ElementProps<TabGroupProps, 'div'>) {
85
- const items = useNavOptions(options);
101
+ const options = Array.isArray(optionsProp) ? optionsProp : [];
102
+ useOptionIconsInvalid(options);
86
103
 
87
104
  return (
88
105
  <div
@@ -92,7 +109,7 @@ function TabGroup({
92
109
  data-size={size}
93
110
  data-width={width}
94
111
  >
95
- {items.map((item) => {
112
+ {options.map((item, itemIndex) => {
96
113
  const isActive = item.value === value;
97
114
 
98
115
  return (
@@ -100,12 +117,16 @@ function TabGroup({
100
117
  data-active={isActive || undefined}
101
118
  disabled={item.disabled || undefined}
102
119
  key={item.value}
103
- onClick={() => onTabChange(item.value)}
120
+ onClick={() => {
121
+ onTabChange(item.value || item.label, itemIndex);
122
+ }}
104
123
  >
105
124
  <span>
106
125
  {(isActive && item.iconActive) || item.icon}
107
126
  {item.label}
108
- {item.badge && <Badge count={item.badge} size="x-small" />}
127
+ {item.badge && !item.disabled && !isActive && (
128
+ <Badge count={item.badge} size={TAB_BADGE_SIZES[size]} />
129
+ )}
109
130
  </span>
110
131
  </button>
111
132
  );
package/src/TextField.tsx CHANGED
@@ -1,23 +1,25 @@
1
1
  import { FormFieldProps, FormField } from './FormField';
2
2
  import { TextInputProps, TextInput } from './TextInput';
3
3
 
4
- export type TextFieldProps = Pick<
5
- TextInputProps,
6
- | 'autoComplete'
7
- | 'disabled'
8
- | 'inputRef'
9
- | 'invalid'
10
- | 'leading'
11
- | 'name'
12
- | 'onChange'
13
- | 'placeholder'
14
- | 'readOnly'
15
- | 'required'
16
- | 'size'
17
- | 'trailing'
18
- | 'type'
19
- | 'value'
20
- > &
4
+ import { InvalidPropsLibrary } from '.';
5
+
6
+ export type TextFieldProps = InvalidPropsLibrary &
7
+ Pick<
8
+ TextInputProps,
9
+ | 'autoComplete'
10
+ | 'disabled'
11
+ | 'inputRef'
12
+ | 'leading'
13
+ | 'name'
14
+ | 'onChange'
15
+ | 'placeholder'
16
+ | 'readOnly'
17
+ | 'required'
18
+ | 'size'
19
+ | 'trailing'
20
+ | 'type'
21
+ | 'value'
22
+ > &
21
23
  Pick<FormFieldProps, 'controlId' | 'errorMessage' | 'helperText' | 'label' | 'labelTrailing'>;
22
24
 
23
25
  /**
package/src/TextInput.tsx CHANGED
@@ -4,38 +4,39 @@ import { ChangeEvent, HTMLInputAutoCompleteAttribute, HTMLInputTypeAttribute, Re
4
4
 
5
5
  import { useId } from './hooks/useId';
6
6
 
7
- import { ElementProps, CommonProps } from '.';
7
+ import { ElementProps, CommonProps, InvalidPropsLibrary } from '.';
8
8
 
9
9
  export type TextInputProps = CommonProps<
10
- 'aria-label' | 'disabled' | 'id' | 'invalid' | 'name' | 'readOnly' | 'required' | 'size' | 'value'
11
- > & {
12
- /**
13
- * Callback when the value of the field changes.
14
- *
15
- * @type (next: String, Event) => void
16
- * @required
17
- */
18
- onChange: (next: string, event?: ChangeEvent<HTMLInputElement>) => void;
19
- /** The ref of the container. */
20
- containerRef?: (node: HTMLElement | null) => void;
21
- /** The ref of the input. */
22
- inputRef?: (node: HTMLElement | null) => void;
23
- /** The trailing element to display in the field. */
24
- trailing?: ReactNode;
25
- /** The leading element to display in the field. */
26
- leading?: ReactNode;
27
- /** The placeholder of the field. */
28
- placeholder?: string;
29
- /** The type of the input. */
30
- type?: Extract<HTMLInputTypeAttribute, 'number' | 'text'>;
31
- /**
32
- * Specifies if user agent has any permission to provide automated assistance in filling out form field values.
33
- * https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
34
- *
35
- * @default off
36
- */
37
- autoComplete?: HTMLInputAutoCompleteAttribute;
38
- };
10
+ 'aria-label' | 'disabled' | 'id' | 'name' | 'readOnly' | 'required' | 'size' | 'value'
11
+ > &
12
+ InvalidPropsLibrary & {
13
+ /**
14
+ * Callback when the value of the field changes.
15
+ *
16
+ * @type (next: String, Event) => void
17
+ * @required
18
+ */
19
+ onChange: (next: string, event?: ChangeEvent<HTMLInputElement>) => void;
20
+ /** The ref of the container. */
21
+ containerRef?: (node: HTMLElement | null) => void;
22
+ /** The ref of the input. */
23
+ inputRef?: (node: HTMLElement | null) => void;
24
+ /** The trailing element to display in the field. */
25
+ trailing?: ReactNode;
26
+ /** The leading element to display in the field. */
27
+ leading?: ReactNode;
28
+ /** The placeholder of the field. */
29
+ placeholder?: string;
30
+ /** The type of the input. */
31
+ type?: Extract<HTMLInputTypeAttribute, 'number' | 'text'>;
32
+ /**
33
+ * Specifies if user agent has any permission to provide automated assistance in filling out form field values.
34
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
35
+ *
36
+ * @default off
37
+ */
38
+ autoComplete?: HTMLInputAutoCompleteAttribute;
39
+ };
39
40
 
40
41
  /**
41
42
  * A text input that allows users to enter text, numbers or symbols in a singular line. This is the base element and is
@@ -63,6 +64,7 @@ function TextInput({
63
64
  disabled,
64
65
  autoComplete = 'off',
65
66
  containerRef,
67
+ errorMessage,
66
68
  ...otherProps
67
69
  }: ElementProps<TextInputProps, 'div'>) {
68
70
  const id = useId(idProp);
@@ -82,6 +84,7 @@ function TextInput({
82
84
  >
83
85
  {leading && <span data-leading>{leading}</span>}
84
86
  <input
87
+ aria-errormessage={errorMessage || undefined}
85
88
  aria-invalid={invalid || undefined}
86
89
  aria-label={ariaLabel}
87
90
  autoComplete={autoComplete}
package/src/Textarea.tsx CHANGED
@@ -3,62 +3,76 @@ import { ChangeEvent, CSSProperties, Ref } from 'react';
3
3
 
4
4
  import { useId } from './hooks/useId';
5
5
 
6
- import { CommonProps } from './';
6
+ import { CommonProps, InvalidPropsLibrary } from './';
7
7
 
8
- export type TextareaProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'invalid' | 'readOnly' | 'required'> & {
9
- /**
10
- * Callback when the value of the field changes.
11
- *
12
- * @type (next: String, Event) => void
13
- * @required
14
- */
15
- onChange: (next: string, event?: ChangeEvent<HTMLTextAreaElement>) => void;
16
- /**
17
- * The size of the field.
18
- *
19
- * @default medium
20
- */
21
- size?: 'large' | 'medium' | 'small';
22
- /** The value of the field. */
23
- value?: string;
24
- /**
25
- * The textarea control name of the field.
26
- *
27
- * @required
28
- */
29
- name: string;
30
- /** The ref of the field. */
31
- innerRef?: Ref<HTMLTextAreaElement>;
32
- /** The placeholder of the field. */
33
- placeholder?: string;
34
- /**
35
- * The maximum number of characters that the field will accept.
36
- *
37
- * @minimum 1
38
- */
39
- maxLength?: number;
40
- /**
41
- * The minimum number of rows that the textarea should have. If set the textarea will automatically grow and shrink
42
- * to fit the content.
43
- *
44
- * @minimum 3
45
- */
46
- minRows?: number;
47
- /**
48
- * The maximum number of rows that the textarea should have. If set the textarea will automatically grow and shrink
49
- * to fit the content.
50
- *
51
- * @maximum 10
52
- */
53
- maxRows?: number;
54
- };
8
+ const DEFAULT = {
9
+ minRows: 3,
10
+ maxRows: 10,
11
+ textSize: 'medium',
12
+ } as const;
55
13
 
56
- const MIN_ROWS = 3;
57
- const MAX_ROWS = 10;
14
+ export type TextareaProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'readOnly' | 'required'> &
15
+ InvalidPropsLibrary & {
16
+ /**
17
+ * Callback when the value of the field changes.
18
+ *
19
+ * @type (next: String, Event) => void
20
+ * @required
21
+ */
22
+ onChange: (next: string, event?: ChangeEvent<HTMLTextAreaElement>) => void;
23
+ /**
24
+ * The text size of the field.
25
+ *
26
+ * @default medium
27
+ */
28
+ textSize?: 'large' | 'medium' | 'small';
29
+ /**
30
+ * The value of the field.
31
+ *
32
+ * @type multiline
33
+ */
34
+ value?: string;
35
+ /**
36
+ * The textarea control name of the field.
37
+ *
38
+ * @required
39
+ */
40
+ name: string;
41
+ /** The ref of the field. */
42
+ innerRef?: Ref<HTMLTextAreaElement>;
43
+ /** The placeholder of the field. */
44
+ placeholder?: string;
45
+ /**
46
+ * The maximum number of characters that the field will accept.
47
+ *
48
+ * @minimum 1
49
+ */
50
+ maxLength?: number;
51
+ /**
52
+ * The minimum number of rows that the textarea will show.
53
+ *
54
+ * @default 3
55
+ * @minimum 3
56
+ * @maximum 10
57
+ */
58
+ minRows?: number;
59
+ /**
60
+ * The maximum number of rows that the textarea will show.
61
+ *
62
+ * @default 10
63
+ * @minimum 3
64
+ * @maximum 10
65
+ */
66
+ maxRows?: number;
67
+ };
58
68
 
59
69
  /**
60
70
  * A component that allows users to input large amounts of text that could span multiple lines.
61
71
  *
72
+ * This component gives you a textarea HTML element that automatically adjusts its height to match the length of the
73
+ * content within maximum and minimum rows. A character counter when a maxLength is set to show the number of characters
74
+ * remaining below the limit.
75
+ *
62
76
  * @element
63
77
  *
64
78
  * @name Textarea
@@ -66,27 +80,28 @@ const MAX_ROWS = 10;
66
80
  function Textarea({
67
81
  invalid: invalidProp,
68
82
  onChange,
69
- size = 'medium',
83
+ textSize = DEFAULT.textSize,
70
84
  value = '',
71
85
  name,
72
86
  'aria-label': ariaLabel,
73
87
  innerRef,
74
88
  placeholder,
75
89
  id: idProp,
76
- minRows: minRowsProp = MIN_ROWS,
77
- maxRows: maxRowsProp = MAX_ROWS,
90
+ minRows: minRowsProp = DEFAULT.minRows,
91
+ maxRows: maxRowsProp = DEFAULT.maxRows,
92
+ errorMessage,
78
93
  ...otherProps
79
94
  }: TextareaProps) {
80
95
  const id = useId(idProp);
81
96
  const invalid = !otherProps.readOnly && !otherProps.disabled && invalidProp;
82
97
  // ensure minRows and maxRows are within bounds
83
- const minRows = Math.min(MAX_ROWS, Math.max(minRowsProp, MIN_ROWS));
84
- const maxRows = Math.max(MIN_ROWS, Math.min(maxRowsProp, MAX_ROWS));
98
+ const minRows = Math.min(DEFAULT.maxRows, Math.max(minRowsProp, DEFAULT.minRows));
99
+ const maxRows = Math.max(DEFAULT.minRows, Math.min(maxRowsProp, DEFAULT.maxRows));
85
100
 
86
101
  return (
87
102
  <div
88
103
  data-bspk="textarea"
89
- data-size={size}
104
+ data-size={textSize}
90
105
  style={
91
106
  {
92
107
  '--min-rows': minRows,
@@ -96,6 +111,7 @@ function Textarea({
96
111
  >
97
112
  <textarea
98
113
  {...otherProps}
114
+ aria-errormessage={errorMessage || undefined}
99
115
  aria-invalid={invalid || undefined}
100
116
  aria-label={ariaLabel}
101
117
  id={id}
package/src/base.scss CHANGED
@@ -51,16 +51,17 @@ body {
51
51
  a {
52
52
  color: var(--foreground-link-text-default);
53
53
 
54
- &:hover {
54
+ &:not([disabled]):hover {
55
55
  color: var(--foreground-link-text-default-hovered);
56
56
  }
57
57
 
58
- &:visited {
58
+ &:not([disabled]):visited {
59
59
  color: var(--foreground-link-text-default-visited);
60
60
  }
61
61
 
62
- &:disabled {
62
+ &[disabled] {
63
63
  pointer-events: none;
64
+ cursor: text;
64
65
  color: var(--foreground-link-text-default-disabled);
65
66
  }
66
67
  }
@@ -1,6 +1,7 @@
1
1
  [data-bspk='form-field'] {
2
2
  box-sizing: border-box;
3
- width: 100%;
3
+ max-width: 100%;
4
+ width: fit-content;
4
5
  display: flex;
5
6
  flex-direction: column;
6
7
  gap: var(--spacing-sizing-01);
@@ -1,6 +1,6 @@
1
1
  import { AriaAttributes, useId, useState } from 'react';
2
2
 
3
- import { CommonProps } from '..';
3
+ import { CommonProps, InvalidPropsLibrary } from '..';
4
4
  import { EVENT_KEY } from '../utils/keyboard';
5
5
 
6
6
  import { Placement, useFloating } from './useFloating';
@@ -9,7 +9,7 @@ import { useOutsideClick } from './useOutsideClick';
9
9
 
10
10
  export type UseFloatingMenuProps = {
11
11
  placement: Placement;
12
- triggerProps?: CommonProps<'disabled' | 'invalid' | 'readOnly'>;
12
+ triggerProps?: CommonProps<'disabled' | 'readOnly'> & InvalidPropsLibrary;
13
13
  };
14
14
 
15
15
  export type UseFloatingMenuReturn = {
@@ -30,6 +30,7 @@ export type UseFloatingMenuReturn = {
30
30
  'aria-invalid': boolean | undefined;
31
31
  'aria-owns': string;
32
32
  'aria-readonly': boolean | undefined;
33
+ 'aria-errormessage': string | undefined;
33
34
  role: 'combobox';
34
35
  tabIndex: number;
35
36
  ref: (node: HTMLElement | null) => void;
@@ -75,6 +76,7 @@ export function useFloatingMenu({ placement, triggerProps }: UseFloatingMenuProp
75
76
  tabIndex: -1,
76
77
  },
77
78
  triggerProps: {
79
+ 'aria-errormessage': triggerProps?.errorMessage || undefined,
78
80
  'aria-activedescendant': selectedId || undefined,
79
81
  'aria-controls': menuId,
80
82
  'aria-expanded': show,
@@ -0,0 +1,49 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { isValidIcon } from '../utils/children';
4
+ import { useErrorLogger } from '../utils/errors';
5
+
6
+ /**
7
+ * A utility hook used within navigation components. Returns true if the icons are invalid.
8
+ *
9
+ * @param options [NavOption[]] The options to display. Each option has an optional leading icon.
10
+ * @returns {boolean} True if the icons are invalid.
11
+ */
12
+ export function useOptionIconsInvalid<T extends NavOption>(options: T[] | undefined): boolean {
13
+ const { logError } = useErrorLogger();
14
+
15
+ return useMemo(() => {
16
+ if (!options || !Array.isArray(options)) return true;
17
+
18
+ const iconsInvalid = options.some((o) => o.icon) && !options.every((option) => isValidIcon(option.icon));
19
+
20
+ logError(
21
+ iconsInvalid,
22
+ 'useNavOptions - Every option either must have a valid icon or none at all. All icons removed.',
23
+ );
24
+
25
+ return iconsInvalid;
26
+ }, [logError, options]);
27
+ }
28
+
29
+ export type NavOption = {
30
+ /**
31
+ * The label of the option. This is the text that will be displayed on the option.
32
+ *
33
+ * @required
34
+ */
35
+ label: string;
36
+ /**
37
+ * Determines if the element is [disabled](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled).
38
+ *
39
+ * @default false
40
+ */
41
+ disabled?: boolean;
42
+ /** The value of the option. If not provided, the label will be used as the value. */
43
+ value?: string;
44
+ /** The the icon to display before the label. */
45
+ icon?: React.ReactNode;
46
+ iconActive?: React.ReactNode;
47
+ };
48
+
49
+ /** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
package/src/index.ts CHANGED
@@ -34,7 +34,8 @@ export type CallToActionButton = {
34
34
  size?: ButtonSize;
35
35
  };
36
36
 
37
- export type ToggleControlProps<T extends HTMLElement> = CommonProps<'aria-label' | 'disabled' | 'invalid' | 'name', T> &
37
+ export type ToggleControlProps<T extends HTMLElement> = CommonProps<'aria-label' | 'disabled' | 'name', T> &
38
+ InvalidPropsLibrary &
38
39
  Required<CommonProps<'value'>> & {
39
40
  /**
40
41
  * Marks the control as checked.
@@ -56,6 +57,28 @@ export type CommonProps<K extends keyof CommonPropsLibrary, T extends HTMLElemen
56
57
  K
57
58
  >;
58
59
 
60
+ /**
61
+ * The props that are common to input elements.
62
+ *
63
+ * If an element is invalid it must have an errorMessage.
64
+ */
65
+ export type InvalidPropsLibrary = {
66
+ /**
67
+ * Marks the element as invalid and displays error state theme.
68
+ *
69
+ * If the errorMessage is empty the error state theme will not appear.
70
+ *
71
+ * @default false
72
+ */
73
+ invalid?: boolean;
74
+ /**
75
+ * Marks the element as invalid and displays error message.
76
+ *
77
+ * When an element is invalid it must display an error message explaining why it is invalid.
78
+ */
79
+ errorMessage?: string;
80
+ };
81
+
59
82
  export type CommonPropsLibrary<T extends HTMLElement = HTMLElement> = {
60
83
  /** The id of the element. If not provided one will be generated. */
61
84
  id?: string;
@@ -89,12 +112,6 @@ export type CommonPropsLibrary<T extends HTMLElement = HTMLElement> = {
89
112
  * @default false
90
113
  */
91
114
  disabled?: boolean;
92
- /**
93
- * Marks the element as invalid and displays error state theme.
94
- *
95
- * @default false
96
- */
97
- invalid?: boolean;
98
115
  /**
99
116
  * Determines if the element is [readonly](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly).
100
117
  *
@@ -107,12 +124,6 @@ export type CommonPropsLibrary<T extends HTMLElement = HTMLElement> = {
107
124
  * @required
108
125
  */
109
126
  name: string;
110
- /**
111
- * Marks the element as invalid and displays error message.
112
- *
113
- * When an element is invalid it must display an error message explaining why it is invalid.
114
- */
115
- errorMessage?: string;
116
127
  /**
117
128
  * The value of the control.
118
129
  *
@@ -1,7 +1,7 @@
1
1
  [data-bspk='inline-alert'] {
2
2
  display: flex;
3
3
  align-items: start;
4
- justify-content: center;
4
+ justify-content: start;
5
5
  flex-direction: row;
6
6
  gap: var(--spacing-sizing-02);
7
7
 
@@ -1,5 +1,5 @@
1
1
  [data-bspk='number-input'] {
2
- // medium
2
+ // medium is the default size
3
3
  --font: var(--body-base);
4
4
  --height: var(--spacing-sizing-10);
5
5
  --svg-width: var(--spacing-sizing-05);
@@ -13,10 +13,10 @@
13
13
  border: 1px solid var(--stroke-neutral-base);
14
14
  border-radius: var(--radius-small);
15
15
  background: var(--surface-neutral-t1-base);
16
+ max-width: 280px;
16
17
 
17
18
  &:focus-within {
18
19
  border-color: var(--stroke-brand-primary);
19
- outline: 1px solid var(--stroke-brand-primary);
20
20
  }
21
21
 
22
22
  [data-divider] {
@@ -29,11 +29,13 @@
29
29
  }
30
30
 
31
31
  button {
32
- min-width: var(--height);
32
+ width: var(--height);
33
+ height: var(--height);
33
34
  background: none;
34
35
  border: none;
35
36
  cursor: pointer;
36
37
  font: var(--font);
38
+ flex-shrink: 0;
37
39
 
38
40
  svg {
39
41
  width: var(--svg-width);
@@ -59,6 +61,7 @@
59
61
  padding: 0 var(--spacing-sizing-03);
60
62
  background: transparent;
61
63
  border: none;
64
+ flex-grow: 1;
62
65
 
63
66
  &:focus {
64
67
  outline: none;
package/src/textarea.scss CHANGED
@@ -43,7 +43,11 @@
43
43
  textarea {
44
44
  --border-color: var(--stroke-neutral-base);
45
45
 
46
- resize: vertical;
46
+ &::placeholder {
47
+ color: var(--foreground-neutral-on-surface-variant-03);
48
+ }
49
+
50
+ resize: none;
47
51
  color: var(--foreground-neutral-on-surface);
48
52
  background-color: var(--surface-neutral-t1-base);
49
53
  border-radius: var(--radius-small);
@@ -71,7 +75,6 @@
71
75
 
72
76
  linear-gradient(var(--interactions-disabled-opacity), var(--interactions-disabled-opacity)),
73
77
  linear-gradient(var(--surface-neutral-t1-base), var(--surface-neutral-t1-base));
74
- color: var(--foreground-neutral-on-surface-variant-02);
75
78
  cursor: not-allowed;
76
79
  }
77
80