@bspk/ui 1.1.17 → 1.1.19

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 (144) hide show
  1. package/dist/Avatar.d.ts +17 -6
  2. package/dist/Avatar.js +15 -6
  3. package/dist/Avatar.js.map +1 -1
  4. package/dist/AvatarGroup.d.ts +49 -0
  5. package/dist/AvatarGroup.js +18 -0
  6. package/dist/AvatarGroup.js.map +1 -0
  7. package/dist/Badge.js +1 -1
  8. package/dist/Button.d.ts +14 -4
  9. package/dist/Button.js +1 -1
  10. package/dist/Button.js.map +1 -1
  11. package/dist/Checkbox.d.ts +15 -2
  12. package/dist/Checkbox.js.map +1 -1
  13. package/dist/CheckboxGroup.d.ts +6 -3
  14. package/dist/CheckboxGroup.js.map +1 -1
  15. package/dist/CheckboxOption.d.ts +8 -1
  16. package/dist/CheckboxOption.js.map +1 -1
  17. package/dist/Chip.d.ts +3 -1
  18. package/dist/Chip.js.map +1 -1
  19. package/dist/Dialog.d.ts +3 -3
  20. package/dist/Dialog.js.map +1 -1
  21. package/dist/Divider.js +1 -1
  22. package/dist/Dropdown.d.ts +33 -7
  23. package/dist/Dropdown.js +5 -5
  24. package/dist/Dropdown.js.map +1 -1
  25. package/dist/ListItem.js +1 -1
  26. package/dist/Menu.d.ts +40 -21
  27. package/dist/Menu.js +63 -41
  28. package/dist/Menu.js.map +1 -1
  29. package/dist/NumberInput.d.ts +5 -1
  30. package/dist/NumberInput.js +7 -5
  31. package/dist/NumberInput.js.map +1 -1
  32. package/dist/Portal.d.ts +5 -1
  33. package/dist/Portal.js.map +1 -1
  34. package/dist/ProgressBar.d.ts +4 -0
  35. package/dist/ProgressBar.js.map +1 -1
  36. package/dist/ProgressionStepper.d.ts +9 -2
  37. package/dist/ProgressionStepper.js +1 -1
  38. package/dist/ProgressionStepper.js.map +1 -1
  39. package/dist/ProgressionStepperBar.d.ts +6 -0
  40. package/dist/ProgressionStepperBar.js.map +1 -1
  41. package/dist/Radio.d.ts +16 -2
  42. package/dist/Radio.js +2 -2
  43. package/dist/Radio.js.map +1 -1
  44. package/dist/RadioGroup.d.ts +26 -3
  45. package/dist/RadioGroup.js +3 -3
  46. package/dist/RadioGroup.js.map +1 -1
  47. package/dist/RadioOption.d.ts +9 -1
  48. package/dist/RadioOption.js.map +1 -1
  49. package/dist/SearchBar.d.ts +30 -4
  50. package/dist/SearchBar.js +16 -15
  51. package/dist/SearchBar.js.map +1 -1
  52. package/dist/SegmentedControl.d.ts +21 -2
  53. package/dist/SegmentedControl.js +1 -1
  54. package/dist/SegmentedControl.js.map +1 -1
  55. package/dist/Switch.d.ts +1 -1
  56. package/dist/SwitchGroup.d.ts +13 -6
  57. package/dist/SwitchGroup.js +1 -1
  58. package/dist/SwitchGroup.js.map +1 -1
  59. package/dist/TabGroup.d.ts +23 -5
  60. package/dist/TabGroup.js +1 -1
  61. package/dist/TabGroup.js.map +1 -1
  62. package/dist/Tag.d.ts +5 -2
  63. package/dist/Tag.js +1 -1
  64. package/dist/Tag.js.map +1 -1
  65. package/dist/TextInput.d.ts +15 -6
  66. package/dist/TextInput.js +11 -5
  67. package/dist/TextInput.js.map +1 -1
  68. package/dist/Textarea.d.ts +3 -3
  69. package/dist/avatar-group.css +1 -0
  70. package/dist/avatar.css +1 -1
  71. package/dist/badge.css +1 -1
  72. package/dist/button.css +1 -1
  73. package/dist/demo/ExampleModalRender.d.ts +7 -0
  74. package/dist/demo/ExampleModalRender.js +16 -0
  75. package/dist/demo/ExampleModalRender.js.map +1 -0
  76. package/dist/demo/ExamplePlaceholder.d.ts +7 -0
  77. package/dist/demo/ExamplePlaceholder.js +13 -0
  78. package/dist/demo/ExamplePlaceholder.js.map +1 -0
  79. package/dist/demo/examples.d.ts +101 -0
  80. package/dist/demo/examples.js +483 -0
  81. package/dist/demo/examples.js.map +1 -0
  82. package/dist/divider.css +1 -1
  83. package/dist/dropdown.css +1 -1
  84. package/dist/hooks/useKeyboardNavigation.js +5 -2
  85. package/dist/hooks/useKeyboardNavigation.js.map +1 -1
  86. package/dist/hooks/useOptionIconsInvalid.d.ts +10 -1
  87. package/dist/hooks/useOptionIconsInvalid.js.map +1 -1
  88. package/dist/index.d.ts +4 -26
  89. package/dist/index.js.map +1 -1
  90. package/dist/list-item.css +1 -1
  91. package/dist/menu.css +1 -1
  92. package/dist/segmented-control.css +1 -1
  93. package/dist/tab-group.css +1 -1
  94. package/dist/tag.css +1 -1
  95. package/dist/text-input.css +1 -1
  96. package/dist/utils/children.js.map +1 -1
  97. package/meta-types.ts +2 -0
  98. package/meta.ts +76 -42
  99. package/package.json +1 -1
  100. package/src/Avatar.tsx +35 -8
  101. package/src/AvatarGroup.tsx +71 -0
  102. package/src/Button.tsx +14 -4
  103. package/src/Checkbox.tsx +25 -11
  104. package/src/CheckboxGroup.tsx +6 -3
  105. package/src/CheckboxOption.tsx +9 -2
  106. package/src/Chip.tsx +3 -1
  107. package/src/Dialog.tsx +3 -3
  108. package/src/Dropdown.tsx +40 -11
  109. package/src/Menu.tsx +159 -108
  110. package/src/NumberInput.tsx +15 -6
  111. package/src/Portal.tsx +5 -1
  112. package/src/ProgressBar.tsx +4 -0
  113. package/src/ProgressionStepper.tsx +9 -2
  114. package/src/ProgressionStepperBar.tsx +6 -0
  115. package/src/Radio.tsx +21 -4
  116. package/src/RadioGroup.tsx +34 -6
  117. package/src/RadioOption.tsx +11 -2
  118. package/src/SearchBar.tsx +87 -44
  119. package/src/SegmentedControl.tsx +21 -2
  120. package/src/Switch.tsx +1 -1
  121. package/src/SwitchGroup.tsx +19 -7
  122. package/src/TabGroup.tsx +23 -5
  123. package/src/Tag.tsx +5 -2
  124. package/src/TextInput.tsx +25 -15
  125. package/src/Textarea.tsx +3 -3
  126. package/src/avatar-group.scss +17 -0
  127. package/src/avatar.scss +26 -2
  128. package/src/badge.scss +1 -0
  129. package/src/button.scss +1 -0
  130. package/src/demo/ExampleModalRender.tsx +37 -0
  131. package/src/demo/ExamplePlaceholder.tsx +40 -0
  132. package/src/demo/examples.tsx +699 -0
  133. package/src/divider.scss +2 -0
  134. package/src/dropdown.scss +5 -0
  135. package/src/hooks/useKeyboardNavigation.ts +3 -2
  136. package/src/hooks/useOptionIconsInvalid.ts +10 -1
  137. package/src/index.ts +5 -32
  138. package/src/list-item.scss +5 -1
  139. package/src/menu.scss +5 -1
  140. package/src/segmented-control.scss +1 -0
  141. package/src/tab-group.scss +1 -0
  142. package/src/tag.scss +1 -0
  143. package/src/text-input.scss +13 -18
  144. package/src/utils/children.ts +1 -1
package/src/SearchBar.tsx CHANGED
@@ -5,21 +5,22 @@ import { useRef } from 'react';
5
5
  import { MenuItem, MenuProps, Menu } from './Menu';
6
6
  import { Portal } from './Portal';
7
7
  import { TextInputProps, TextInput } from './TextInput';
8
+ import { Txt } from './Txt';
8
9
  import { useFloatingMenu } from './hooks/useFloatingMenu';
9
10
  import { useId } from './hooks/useId';
10
11
  //import { useFloatingMenu } from './hooks/useFloatingMenu';
11
12
 
12
- export type SearchBarProps = Pick<MenuProps, 'itemCount' | 'items' | 'noResultsMessage'> &
13
+ export type SearchBarProps<T extends MenuItem = MenuItem> = Pick<MenuProps<T>, 'itemCount' | 'noResultsMessage'> &
13
14
  Pick<TextInputProps, 'aria-label' | 'id' | 'inputRef' | 'name' | 'placeholder' | 'size'> & {
14
15
  /** The current value of the search bar. */
15
- searchValue?: string;
16
+ value?: string;
16
17
  /**
17
18
  * Handler for state updates.
18
19
  *
19
20
  * @type (value: String) => void
20
21
  * @required
21
22
  */
22
- setSearchValue: (value: string) => void;
23
+ onChange: (value: string) => void;
23
24
  /*
24
25
  * Handler for item selection.
25
26
  *
@@ -27,6 +28,32 @@ export type SearchBarProps = Pick<MenuProps, 'itemCount' | 'items' | 'noResultsM
27
28
  * @required
28
29
  */
29
30
  onSelect: (item?: MenuItem) => void;
31
+ /**
32
+ * Content to display in the menu.
33
+ *
34
+ * @example
35
+ * [
36
+ * { value: '1', label: 'Apple Pie' },
37
+ * { value: '2', label: 'Banana Split' },
38
+ * { value: '3', label: 'Cherry Tart' },
39
+ * { value: '4', label: 'Dragonfruit Sorbet' },
40
+ * { value: '5', label: 'Elderberry Jam' },
41
+ * { value: '6', label: 'Fig Newton' },
42
+ * { value: '7', label: 'Grape Soda' },
43
+ * { value: '8', label: 'Honeydew Smoothie' },
44
+ * { value: '9', label: 'Ice Cream Sandwich' },
45
+ * { value: '10', label: 'Jackfruit Pudding' },
46
+ * ];
47
+ *
48
+ * @type Array<MenuItem>
49
+ */
50
+ items?: T[];
51
+ /**
52
+ * Message to display when no results are found
53
+ *
54
+ * @type multiline
55
+ */
56
+ noResultsMessage?: string;
30
57
  };
31
58
 
32
59
  /**
@@ -45,8 +72,8 @@ function SearchBar({
45
72
  name,
46
73
  size = 'medium',
47
74
  onSelect,
48
- searchValue,
49
- setSearchValue,
75
+ value,
76
+ onChange,
50
77
  }: SearchBarProps) {
51
78
  const id = useId(idProp);
52
79
  const {
@@ -61,49 +88,65 @@ function SearchBar({
61
88
 
62
89
  return (
63
90
  <>
64
- <TextInput
65
- aria-label={ariaLabel}
66
- autoComplete="off"
67
- containerRef={triggerRef}
68
- data-bspk="search-bar"
69
- id={id}
70
- inputRef={(node) => {
71
- inputRef?.(node || null);
72
- inputRefLocal.current = node;
73
- }}
74
- leading={<SvgSearch />}
75
- name={name}
76
- onChange={(str) => setSearchValue(str)}
77
- placeholder={placeholder}
78
- size={size}
79
- value={searchValue}
80
- {...triggerProps}
81
- onClick={(event) => {
82
- if (items?.length) onClick(event);
83
- }}
84
- onKeyDownCapture={(event) => {
85
- const handled = onKeyDownCapture(event);
91
+ <div data-bspk="search-bar">
92
+ <TextInput
93
+ aria-label={ariaLabel}
94
+ autoComplete="off"
95
+ containerRef={triggerRef}
96
+ id={id}
97
+ inputRef={(node) => {
98
+ inputRef?.(node || null);
99
+ inputRefLocal.current = node;
100
+ }}
101
+ leading={<SvgSearch />}
102
+ name={name}
103
+ onChange={(str) => onChange(str)}
104
+ placeholder={placeholder}
105
+ size={size}
106
+ value={value}
107
+ {...triggerProps}
108
+ onClick={(event) => {
109
+ if (items?.length) onClick(event);
110
+ }}
111
+ onKeyDownCapture={(event) => {
112
+ const handled = onKeyDownCapture(event);
86
113
 
87
- if (handled) return;
114
+ if (handled) return;
88
115
 
89
- inputRefLocal.current?.focus();
90
- }}
91
- />
92
- <Portal>
93
- <Menu
94
- itemCount={itemCount}
95
- items={items}
96
- noResultsMessage={noResultsMessage}
97
- onChange={(selectedValues, event) => {
98
- event?.preventDefault();
99
- const item = items?.find((i) => i.value === selectedValues[0]);
100
- onSelect?.(item);
101
- setSearchValue(item?.label || '');
102
- closeMenu();
116
+ inputRefLocal.current?.focus();
103
117
  }}
104
- {...menuProps}
105
118
  />
106
- </Portal>
119
+ </div>
120
+ {!!value?.trim().length && (
121
+ <Portal>
122
+ <Menu
123
+ itemCount={itemCount}
124
+ items={items}
125
+ noResultsMessage={
126
+ !!value?.length && (
127
+ <>
128
+ <Txt as="div" variant="heading-h5">
129
+ No results found
130
+ </Txt>
131
+ {noResultsMessage && (
132
+ <Txt as="div" variant="body-base">
133
+ {noResultsMessage}
134
+ </Txt>
135
+ )}
136
+ </>
137
+ )
138
+ }
139
+ onChange={(selectedValues, event) => {
140
+ event?.preventDefault();
141
+ const item = items?.find((i) => i.value === selectedValues[0]);
142
+ onSelect?.(item);
143
+ onChange(item?.label || '');
144
+ closeMenu();
145
+ }}
146
+ {...menuProps}
147
+ />
148
+ </Portal>
149
+ )}
107
150
  </>
108
151
  );
109
152
  }
@@ -21,8 +21,17 @@ export type SegmentedControlOption = {
21
21
  disabled?: boolean;
22
22
  /** The value of the option. If not provided, the label will be used as the value. */
23
23
  value?: string;
24
- /** The the icon to display before the label. */
24
+ /**
25
+ * The the icon to display before the label.
26
+ *
27
+ * @type BspkIcon
28
+ */
25
29
  icon?: React.ReactNode;
30
+ /**
31
+ * The icon to display before the label when the option is active.
32
+ *
33
+ * @type BspkIcon
34
+ */
26
35
  iconActive?: React.ReactNode;
27
36
  };
28
37
 
@@ -30,13 +39,23 @@ export type SegmentedControlProps = {
30
39
  /**
31
40
  * The options to display. Each option has a label and an optional leading icon.
32
41
  *
33
- * @type SegmentedControlOption[]
42
+ * @example
43
+ * [
44
+ * { value: '1', label: 'Option 1' },
45
+ * { value: '2', label: 'Option 2' },
46
+ * { value: '3', label: 'Option 3' },
47
+ * ];
48
+ *
49
+ * @type Array<SegmentedControlOption>
34
50
  * @required
35
51
  */
36
52
  options: SegmentedControlOption[];
37
53
  /**
38
54
  * The id of the selected option.
39
55
  *
56
+ * @example
57
+ * 1;
58
+ *
40
59
  * @required
41
60
  */
42
61
  value: SegmentedControlOption['value'];
package/src/Switch.tsx CHANGED
@@ -3,7 +3,7 @@ import { ChangeEvent } from 'react';
3
3
 
4
4
  import { CommonProps } from './';
5
5
 
6
- export type SwitchProps = CommonProps<'aria-label' | 'disabled' | 'name' | 'onClick' | 'value'> & {
6
+ export type SwitchProps = CommonProps<'aria-label' | 'disabled' | 'name' | 'value'> & {
7
7
  /**
8
8
  * Marks the control as checked.
9
9
  *
@@ -1,10 +1,9 @@
1
1
  import { Switch } from './Switch';
2
2
  import { ToggleOptionProps, ToggleOption } from './ToggleOption';
3
3
 
4
- import { ToggleControlProps, ElementProps, CommonProps } from './';
4
+ import { ElementProps, CommonProps } from './';
5
5
 
6
- export type SwitchGroupOption = Pick<ToggleControlProps<HTMLInputElement>, 'value'> &
7
- Pick<ToggleOptionProps, 'description' | 'label'>;
6
+ export type SwitchGroupOption = Pick<ToggleOptionProps, 'description' | 'label'> & Required<CommonProps<'value'>>;
8
7
 
9
8
  export type SwitchGroupProps = CommonProps<'aria-label' | 'name'> & {
10
9
  /**
@@ -16,16 +15,23 @@ export type SwitchGroupProps = CommonProps<'aria-label' | 'name'> & {
16
15
  /**
17
16
  * The options for the switches.
18
17
  *
19
- * @type SwitchGroupOption[]
18
+ * @example
19
+ * [
20
+ * { value: '1', label: 'Option 1' },
21
+ * { value: '2', label: 'Option 2' },
22
+ * { value: '3', label: 'Option 3' },
23
+ * ];
24
+ *
25
+ * @type Array<SwitchGroupOption>
20
26
  * @required
21
27
  */
22
28
  options: SwitchGroupOption[];
23
29
  /**
24
30
  * The values of the switches in the on state.
25
31
  *
26
- * @type string[]
32
+ * @type Array<string>
27
33
  */
28
- values?: SwitchGroupProps['options'][number]['value'][];
34
+ value?: SwitchGroupProps['options'][number]['value'][];
29
35
  };
30
36
 
31
37
  /**
@@ -33,7 +39,13 @@ export type SwitchGroupProps = CommonProps<'aria-label' | 'name'> & {
33
39
  *
34
40
  * @name SwitchGroup
35
41
  */
36
- function SwitchGroup({ onChange, options = [], name, values = [], ...props }: ElementProps<SwitchGroupProps, 'div'>) {
42
+ function SwitchGroup({
43
+ onChange,
44
+ options = [],
45
+ name,
46
+ value: values = [],
47
+ ...props
48
+ }: ElementProps<SwitchGroupProps, 'div'>) {
37
49
  return (
38
50
  <div {...props} data-bspk="switch-group" role="group">
39
51
  {options.map(({ label, description, value }) => (
package/src/TabGroup.tsx CHANGED
@@ -33,11 +33,19 @@ export type TabGroupOption = {
33
33
  * If not provided, the label will be used as the value.
34
34
  */
35
35
  value?: string;
36
- /** The icon to display on the left side of the tab. */
36
+ /**
37
+ * The icon to display on the left side of the tab.
38
+ *
39
+ * @type BspkIcon
40
+ */
37
41
  icon?: ReactNode;
38
- /** The icon to display on the left side of the tab when the tab is active. */
42
+ /**
43
+ * The icon to display on the left side of the tab when the tab is active.
44
+ *
45
+ * @type BspkIcon
46
+ */
39
47
  iconActive?: ReactNode;
40
- /** The badge count to display on the tab. */
48
+ /** The badge count to display on the tab */
41
49
  badge?: number;
42
50
  };
43
51
 
@@ -45,12 +53,22 @@ export type TabGroupProps = {
45
53
  /**
46
54
  * The tabs to display. Each tab has a label and an optional leading icon.
47
55
  *
48
- * @type TabGroupOption[]
56
+ * @example
57
+ * [
58
+ * { value: '1', label: 'Option 1' },
59
+ * { value: '2', label: 'Option 2' },
60
+ * { value: '3', label: 'Option 3' },
61
+ * ];
62
+ *
63
+ * @type Array<TabGroupOption>
49
64
  * @required
50
65
  */
51
66
  options: TabGroupOption[];
52
67
  /**
53
- * The id of the selected tab.
68
+ * The value of the selected tab.
69
+ *
70
+ * @example
71
+ * 1;
54
72
  *
55
73
  * @required
56
74
  */
package/src/Tag.tsx CHANGED
@@ -1,9 +1,9 @@
1
- import './tag.scss';
2
1
  import { ElementType, ReactNode } from 'react';
3
2
 
4
3
  import { ColorVariant } from './utils/colorVariants';
5
4
 
6
5
  import { ElementProps } from './';
6
+ import './tag.scss';
7
7
 
8
8
  export type TagProps<As extends ElementType = 'span'> = {
9
9
  /**
@@ -16,6 +16,9 @@ export type TagProps<As extends ElementType = 'span'> = {
16
16
  /**
17
17
  * The content of the tag.
18
18
  *
19
+ * @example
20
+ * New;
21
+ *
19
22
  * @required
20
23
  */
21
24
  children: ReactNode;
@@ -28,7 +31,7 @@ export type TagProps<As extends ElementType = 'span'> = {
28
31
  /**
29
32
  * The color of the tag.
30
33
  *
31
- * @default white
34
+ * @default grey
32
35
  */
33
36
  color?: ColorVariant;
34
37
  /**
package/src/TextInput.tsx CHANGED
@@ -1,10 +1,18 @@
1
1
  import { SvgCancel } from '@bspk/icons/Cancel';
2
- import './text-input.scss';
3
2
  import { ChangeEvent, HTMLInputAutoCompleteAttribute, HTMLInputTypeAttribute, ReactNode } from 'react';
4
3
 
5
4
  import { useId } from './hooks/useId';
6
5
 
7
- import { ElementProps, CommonProps, InvalidPropsLibrary } from '.';
6
+ import { ElementProps, CommonProps, InvalidPropsLibrary, SetRef } from '.';
7
+
8
+ import './text-input.scss';
9
+
10
+ export const DEFAULT = {
11
+ size: 'medium',
12
+ value: '',
13
+ type: 'text' as Extract<HTMLInputTypeAttribute, 'number' | 'text'>,
14
+ autoComplete: 'off',
15
+ } as const;
8
16
 
9
17
  export type TextInputProps = CommonProps<
10
18
  'aria-label' | 'disabled' | 'id' | 'name' | 'readOnly' | 'required' | 'size' | 'value'
@@ -13,21 +21,24 @@ export type TextInputProps = CommonProps<
13
21
  /**
14
22
  * Callback when the value of the field changes.
15
23
  *
16
- * @type (next: String, Event) => void
17
24
  * @required
18
25
  */
19
26
  onChange: (next: string, event?: ChangeEvent<HTMLInputElement>) => void;
20
27
  /** The ref of the container. */
21
- containerRef?: (node: HTMLElement | null) => void;
28
+ containerRef?: SetRef<HTMLDivElement>;
22
29
  /** The ref of the input. */
23
- inputRef?: (node: HTMLElement | null) => void;
30
+ inputRef?: SetRef<HTMLInputElement>;
24
31
  /** The trailing element to display in the field. */
25
32
  trailing?: ReactNode;
26
33
  /** The leading element to display in the field. */
27
34
  leading?: ReactNode;
28
35
  /** The placeholder of the field. */
29
36
  placeholder?: string;
30
- /** The type of the input. */
37
+ /**
38
+ * The type of the input.
39
+ *
40
+ * @default text
41
+ */
31
42
  type?: Extract<HTMLInputTypeAttribute, 'number' | 'text'>;
32
43
  /**
33
44
  * Specifies if user agent has any permission to provide automated assistance in filling out form field values.
@@ -49,8 +60,8 @@ export type TextInputProps = CommonProps<
49
60
  function TextInput({
50
61
  invalid: invalidProp,
51
62
  onChange,
52
- size = 'medium',
53
- value = '',
63
+ size = DEFAULT.size,
64
+ value = DEFAULT.value,
54
65
  name,
55
66
  'aria-label': ariaLabel,
56
67
  inputRef,
@@ -59,10 +70,10 @@ function TextInput({
59
70
  id: idProp,
60
71
  leading,
61
72
  trailing,
62
- type,
73
+ type = DEFAULT.type,
63
74
  readOnly,
64
75
  disabled,
65
- autoComplete = 'off',
76
+ autoComplete = DEFAULT.autoComplete,
66
77
  containerRef,
67
78
  errorMessage,
68
79
  ...otherProps
@@ -75,6 +86,7 @@ function TextInput({
75
86
  <div
76
87
  data-bspk="text-input"
77
88
  data-disabled={disabled || undefined}
89
+ data-empty={!value.toString().length || undefined}
78
90
  data-invalid={invalid || undefined}
79
91
  data-readonly={readOnly || undefined}
80
92
  data-required={required || undefined}
@@ -102,11 +114,9 @@ function TextInput({
102
114
  value={value}
103
115
  />
104
116
  {trailing && <span data-trailing>{trailing}</span>}
105
- {value?.toString().length > 0 && !readOnly && !disabled && (
106
- <button aria-label="clear" data-clear onClick={() => onChange('')}>
107
- <SvgCancel />
108
- </button>
109
- )}
117
+ <button aria-label="clear" data-clear onClick={() => onChange('')}>
118
+ <SvgCancel />
119
+ </button>
110
120
  </div>
111
121
  );
112
122
  }
package/src/Textarea.tsx CHANGED
@@ -1,9 +1,9 @@
1
1
  import './textarea.scss';
2
- import { ChangeEvent, CSSProperties, Ref } from 'react';
2
+ import { ChangeEvent, CSSProperties } from 'react';
3
3
 
4
4
  import { useId } from './hooks/useId';
5
5
 
6
- import { CommonProps, InvalidPropsLibrary } from './';
6
+ import { CommonProps, InvalidPropsLibrary, SetRef } from './';
7
7
 
8
8
  const DEFAULT = {
9
9
  minRows: 3,
@@ -39,7 +39,7 @@ export type TextareaProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'read
39
39
  */
40
40
  name: string;
41
41
  /** The ref of the field. */
42
- innerRef?: Ref<HTMLTextAreaElement>;
42
+ innerRef?: SetRef<HTMLTextAreaElement>;
43
43
  /** The placeholder of the field. */
44
44
  placeholder?: string;
45
45
  /**
@@ -0,0 +1,17 @@
1
+ [data-bspk='avatar-group'] {
2
+ width: 100%;
3
+
4
+ [data-wrap] {
5
+ width: 100%;
6
+ display: flex;
7
+ flex-direction: row;
8
+ align-items: end;
9
+ justify-content: end;
10
+ gap: var(--spacing-sizing-02);
11
+ overflow: hidden;
12
+
13
+ & > * + * {
14
+ // margin-left: calc(var(--spacing-sizing-01) * -1);
15
+ }
16
+ }
17
+ }
package/src/avatar.scss CHANGED
@@ -1,8 +1,12 @@
1
1
  [data-bspk='avatar'] {
2
2
  --height: var(--spacing-sizing-10);
3
3
  --font: var(--labels-base);
4
- --foreground: var(--foreground-neutral-on-surface);
5
- --background: var(--surface-neutral-t3-low);
4
+ --svg-size: var(--spacing-sizing-10);
5
+
6
+ &:not([data-color]) {
7
+ --foreground: var(--foreground-neutral-on-surface);
8
+ --background: var(--surface-neutral-t3-low);
9
+ }
6
10
 
7
11
  display: flex;
8
12
  flex-direction: column;
@@ -25,49 +29,69 @@
25
29
  max-width: 100%;
26
30
  }
27
31
 
32
+ svg {
33
+ width: var(--svg-size);
34
+ height: var(--svg-size);
35
+ }
36
+
37
+ [data-icon] {
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ }
42
+
28
43
  &[data-size='x-small'] {
29
44
  --height: var(--spacing-sizing-06);
30
45
  --font: var(--labels-x-small);
46
+ --svg-size: var(--spacing-sizing-04);
31
47
  }
32
48
 
33
49
  &[data-size='small'] {
34
50
  --height: var(--spacing-sizing-08);
35
51
  --font: var(--labels-small);
52
+ --svg-size: var(--spacing-sizing-05);
36
53
  }
37
54
 
38
55
  &[data-size='medium'] {
39
56
  --height: var(--spacing-sizing-10);
40
57
  --font: var(--labels-base);
58
+ --svg-size: var(--spacing-sizing-05);
41
59
  }
42
60
 
43
61
  &[data-size='large'] {
44
62
  --height: var(--spacing-sizing-12);
45
63
  --font: var(--labels-large);
64
+ --svg-size: var(--spacing-sizing-06);
46
65
  }
47
66
 
48
67
  &[data-size='x-large'] {
49
68
  --height: var(--spacing-sizing-14);
50
69
  --font: var(--subheader-x-large);
70
+ --svg-size: var(--spacing-sizing-08);
51
71
  }
52
72
 
53
73
  &[data-size='xx-large'] {
54
74
  --height: var(--spacing-sizing-17);
55
75
  --font: var(--subheader-xx-large);
76
+ --svg-size: var(--spacing-sizing-09);
56
77
  }
57
78
 
58
79
  &[data-size='xxx-large'] {
59
80
  --height: var(--spacing-sizing-19);
60
81
  --font: var(--display-regular-small);
82
+ --svg-size: var(--spacing-sizing-12);
61
83
  }
62
84
 
63
85
  &[data-size='xxxx-large'] {
64
86
  --height: var(--spacing-sizing-21);
65
87
  --font: var(--display-regular-medium);
88
+ --svg-size: var(--spacing-sizing-15);
66
89
  }
67
90
 
68
91
  &[data-size='xxxxx-large'] {
69
92
  --height: var(--spacing-sizing-23);
70
93
  --font: var(--display-regular-large);
94
+ --svg-size: var(--spacing-sizing-17);
71
95
  }
72
96
  }
73
97
 
package/src/badge.scss CHANGED
@@ -6,6 +6,7 @@
6
6
  justify-content: center;
7
7
  border-radius: var(--radius-circular);
8
8
  height: var(--size);
9
+ width: fit-content;
9
10
  min-width: var(--size);
10
11
  padding: 0 var(--spacing-sizing-02);
11
12
 
package/src/button.scss CHANGED
@@ -10,6 +10,7 @@
10
10
  background: transparent;
11
11
  text-decoration: none;
12
12
  width: fit-content;
13
+ font-family: var(--typeface);
13
14
  position: relative;
14
15
 
15
16
  [data-touch-target] {
@@ -0,0 +1,37 @@
1
+ import { useId } from 'react';
2
+
3
+ import { Button } from '../Button';
4
+ import { ModalProps, Modal } from '../Modal';
5
+ import { useModalState } from '../hooks/useModalState';
6
+
7
+ import { Preset, DemoSetState } from './examples';
8
+
9
+ export function ExampleModalRender({
10
+ props,
11
+ preset,
12
+ setState,
13
+ }: {
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ props: ModalProps & Record<string, any>;
16
+ preset?: Preset<ModalProps>;
17
+ setState: DemoSetState;
18
+ }) {
19
+ let label = 'Open Modal';
20
+
21
+ const dialogId = useId();
22
+ const openKey = `${dialogId}-open`;
23
+
24
+ const { open, onClose, onOpen } = useModalState(!!props[openKey], (next) => setState({ [openKey]: next }));
25
+
26
+ const presetState: Partial<ModalProps> = preset?.state || {};
27
+ if (presetState?.placement) label += ` (${presetState?.placement})`;
28
+
29
+ return (
30
+ <>
31
+ <Button label={label} onClick={() => onOpen()} />
32
+ <Modal data-example-component id="exampleId" {...props} onClose={onClose} open={open}>
33
+ <pre>{JSON.stringify(props, null, '\t')}</pre>
34
+ </Modal>
35
+ </>
36
+ );
37
+ }
@@ -0,0 +1,40 @@
1
+ import { useRef } from 'react';
2
+
3
+ import { ElementProps } from '..';
4
+ import { Txt } from '../Txt';
5
+
6
+ const dimension = (value: number | string) => (typeof value === 'number' ? `${value}px` : `${value}`);
7
+
8
+ export function ExamplePlaceholder({
9
+ hideSize = false,
10
+ height = 100,
11
+ width = '100%',
12
+ direction = 'row',
13
+ ...props
14
+ }: ElementProps<
15
+ { hideSize?: boolean; height?: number | string; width?: number | string; direction?: 'column' | 'row' },
16
+ 'div'
17
+ >) {
18
+ const ref = useRef<HTMLDivElement | null>(null);
19
+
20
+ return (
21
+ <div
22
+ {...props}
23
+ data-example-placeholder
24
+ ref={ref}
25
+ style={{
26
+ width: dimension(width),
27
+ height: dimension(height),
28
+ flexDirection: direction,
29
+ }}
30
+ >
31
+ {!hideSize && (
32
+ <>
33
+ <Txt variant="labels-large">{dimension(width)}</Txt>
34
+ <Txt>&times;</Txt>
35
+ <Txt variant="labels-large">{dimension(height)}</Txt>
36
+ </>
37
+ )}
38
+ </div>
39
+ );
40
+ }