@westpac/ui 0.4.0 → 0.6.0

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 (226) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/components/accordion/accordion.component.d.ts +2 -2
  3. package/dist/components/accordion/components/accordion-item/accordion-item.component.js +5 -3
  4. package/dist/components/accordion/components/accordion-item/accordion-item.styles.d.ts +16 -0
  5. package/dist/components/accordion/components/accordion-item/accordion-item.styles.js +17 -9
  6. package/dist/components/alert/alert.component.js +2 -1
  7. package/dist/components/alert/alert.styles.js +2 -2
  8. package/dist/components/autocomplete/autocomplete.styles.js +3 -3
  9. package/dist/components/button/button.component.d.ts +18 -2
  10. package/dist/components/button/button.component.js +6 -6
  11. package/dist/components/button/button.styles.d.ts +16 -0
  12. package/dist/components/button/button.styles.js +8 -0
  13. package/dist/components/button-dropdown/button-dropdown.styles.js +1 -1
  14. package/dist/components/button-dropdown/components/panel/panel.component.js +6 -3
  15. package/dist/components/button-dropdown/components/panel/panel.styles.d.ts +14 -2
  16. package/dist/components/button-dropdown/components/panel/panel.styles.js +8 -2
  17. package/dist/components/checkbox-group/components/checkbox/checkbox.component.js +2 -2
  18. package/dist/components/checkbox-group/components/checkbox/checkbox.styles.d.ts +2 -2
  19. package/dist/components/checkbox-group/components/checkbox/checkbox.styles.js +4 -4
  20. package/dist/components/collapsible/collapsible.styles.js +1 -1
  21. package/dist/components/compacta/compacta.component.js +4 -3
  22. package/dist/components/compacta/compacta.styles.js +1 -1
  23. package/dist/components/flexi-cell/flexi-cell.component.js +6 -3
  24. package/dist/components/flexi-cell/flexi-cell.styles.d.ts +10 -0
  25. package/dist/components/flexi-cell/flexi-cell.styles.js +7 -2
  26. package/dist/components/flexi-cell/flexi-cell.types.d.ts +4 -0
  27. package/dist/components/flexi-cell/index.d.ts +1 -0
  28. package/dist/components/flexi-cell/index.js +1 -0
  29. package/dist/components/icon/index.d.ts +1 -0
  30. package/dist/components/icon/index.js +1 -0
  31. package/dist/components/index.d.ts +3 -1
  32. package/dist/components/index.js +3 -1
  33. package/dist/components/input/input.component.d.ts +7 -1
  34. package/dist/components/input/input.component.js +5 -2
  35. package/dist/components/input/input.styles.d.ts +7 -1
  36. package/dist/components/input/input.styles.js +4 -1
  37. package/dist/components/link/link.component.d.ts +1 -1
  38. package/dist/components/link/link.component.js +5 -3
  39. package/dist/components/link/link.styles.d.ts +16 -0
  40. package/dist/components/link/link.styles.js +11 -3
  41. package/dist/components/list/components/item/item.component.d.ts +1 -1
  42. package/dist/components/list/components/item/item.component.js +6 -3
  43. package/dist/components/list/components/item/item.styles.d.ts +10 -0
  44. package/dist/components/list/components/item/item.styles.js +7 -2
  45. package/dist/components/list/list.component.d.ts +1 -1
  46. package/dist/components/list/list.stories.js +7 -1
  47. package/dist/components/list/list.styles.js +1 -1
  48. package/dist/components/list/list.utils.d.ts +1 -1
  49. package/dist/components/modal/components/backdrop/backdrop.styles.js +2 -2
  50. package/dist/components/modal/components/dialog/dialog.component.js +6 -4
  51. package/dist/components/modal/components/dialog/dialog.styles.d.ts +16 -0
  52. package/dist/components/modal/components/dialog/dialog.styles.js +11 -3
  53. package/dist/components/popover/components/panel/panel.styles.js +1 -1
  54. package/dist/components/popover/popover.component.js +0 -1
  55. package/dist/components/popover/popover.styles.d.ts +0 -2
  56. package/dist/components/popover/popover.styles.js +1 -2
  57. package/dist/components/radio-group/components/radio/radio.component.js +2 -2
  58. package/dist/components/radio-group/components/radio/radio.styles.d.ts +2 -2
  59. package/dist/components/radio-group/components/radio/radio.styles.js +5 -5
  60. package/dist/components/repeater/index.d.ts +2 -0
  61. package/dist/components/repeater/index.js +1 -0
  62. package/dist/components/repeater/repeater.component.d.ts +3 -0
  63. package/dist/components/repeater/repeater.component.js +144 -0
  64. package/dist/components/repeater/repeater.stories.d.ts +13 -0
  65. package/dist/components/repeater/repeater.stories.js +34 -0
  66. package/dist/components/repeater/repeater.styles.d.ts +49 -0
  67. package/dist/components/repeater/repeater.styles.js +36 -0
  68. package/dist/components/repeater/repeater.types.d.ts +19 -0
  69. package/dist/components/repeater/repeater.types.js +1 -0
  70. package/dist/components/repeater/repeater.utils.d.ts +2 -0
  71. package/dist/components/repeater/repeater.utils.js +2 -0
  72. package/dist/components/select/select.component.d.ts +11 -1
  73. package/dist/components/select/select.component.js +5 -2
  74. package/dist/components/select/select.styles.d.ts +11 -1
  75. package/dist/components/select/select.styles.js +6 -1
  76. package/dist/components/selector/components/index.d.ts +2 -0
  77. package/dist/components/selector/components/index.js +2 -0
  78. package/dist/components/selector/components/selector-checkbox-group/components/index.d.ts +1 -0
  79. package/dist/components/selector/components/selector-checkbox-group/components/index.js +1 -0
  80. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/index.d.ts +2 -0
  81. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/index.js +1 -0
  82. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.component.d.ts +12 -0
  83. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.component.js +67 -0
  84. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.styles.d.ts +59 -0
  85. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.styles.js +57 -0
  86. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.types.d.ts +10 -0
  87. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.types.js +1 -0
  88. package/dist/components/selector/components/selector-checkbox-group/index.d.ts +2 -0
  89. package/dist/components/selector/components/selector-checkbox-group/index.js +1 -0
  90. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.d.ts +15 -0
  91. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.js +49 -0
  92. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.styles.d.ts +3 -0
  93. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.styles.js +13 -0
  94. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.types.d.ts +20 -0
  95. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.types.js +1 -0
  96. package/dist/components/selector/components/selector-radio-group/components/index.d.ts +1 -0
  97. package/dist/components/selector/components/selector-radio-group/components/index.js +1 -0
  98. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/index.d.ts +2 -0
  99. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/index.js +1 -0
  100. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.d.ts +12 -0
  101. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.js +67 -0
  102. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.styles.d.ts +59 -0
  103. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.styles.js +57 -0
  104. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.types.d.ts +15 -0
  105. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.types.js +1 -0
  106. package/dist/components/selector/components/selector-radio-group/index.d.ts +2 -0
  107. package/dist/components/selector/components/selector-radio-group/index.js +1 -0
  108. package/dist/components/selector/components/selector-radio-group/selector-radio-group.component.d.ts +15 -0
  109. package/dist/components/selector/components/selector-radio-group/selector-radio-group.component.js +58 -0
  110. package/dist/components/selector/components/selector-radio-group/selector-radio-group.styles.d.ts +13 -0
  111. package/dist/components/selector/components/selector-radio-group/selector-radio-group.styles.js +18 -0
  112. package/dist/components/selector/components/selector-radio-group/selector-radio-group.types.d.ts +17 -0
  113. package/dist/components/selector/components/selector-radio-group/selector-radio-group.types.js +1 -0
  114. package/dist/components/selector/index.d.ts +2 -0
  115. package/dist/components/selector/index.js +1 -0
  116. package/dist/components/selector/selector.component.d.ts +30 -0
  117. package/dist/components/selector/selector.component.js +34 -0
  118. package/dist/components/selector/selector.stories.d.ts +57 -0
  119. package/dist/components/selector/selector.stories.js +515 -0
  120. package/dist/components/selector/selector.types.d.ts +20 -0
  121. package/dist/components/selector/selector.types.js +1 -0
  122. package/dist/components/switch/switch.component.d.ts +1 -1
  123. package/dist/components/switch/switch.component.js +11 -7
  124. package/dist/components/switch/switch.stories.js +1 -1
  125. package/dist/components/switch/switch.styles.d.ts +2 -2
  126. package/dist/components/switch/switch.styles.js +2 -2
  127. package/dist/components/switch/switch.types.d.ts +5 -1
  128. package/dist/components/tabs/components/tab/tab.component.js +5 -3
  129. package/dist/components/tabs/components/tab/tab.styles.d.ts +9 -1
  130. package/dist/components/tabs/components/tab/tab.styles.js +12 -8
  131. package/dist/components/tabs/components/tab-panel/tab-panel.component.js +5 -3
  132. package/dist/components/tabs/components/tab-panel/tab-panel.styles.d.ts +7 -1
  133. package/dist/components/tabs/components/tab-panel/tab-panel.styles.js +5 -2
  134. package/dist/components/textarea/textarea.component.d.ts +7 -1
  135. package/dist/components/textarea/textarea.component.js +5 -2
  136. package/dist/components/textarea/textarea.styles.d.ts +7 -1
  137. package/dist/components/textarea/textarea.styles.js +4 -1
  138. package/dist/css/westpac-ui.css +165 -3
  139. package/dist/css/westpac-ui.min.css +165 -3
  140. package/dist/tailwind/constants/colors.d.ts +9 -9
  141. package/dist/tailwind/themes/index.d.ts +11 -11
  142. package/dist/utils/generateId.d.ts +1 -0
  143. package/dist/utils/generateId.js +6 -0
  144. package/dist/utils/index.d.ts +1 -0
  145. package/dist/utils/index.js +1 -0
  146. package/package.json +7 -1
  147. package/src/components/accordion/components/accordion-item/accordion-item.component.tsx +4 -3
  148. package/src/components/accordion/components/accordion-item/accordion-item.styles.ts +17 -9
  149. package/src/components/alert/alert.component.tsx +1 -1
  150. package/src/components/alert/alert.styles.ts +2 -2
  151. package/src/components/autocomplete/autocomplete.styles.ts +3 -3
  152. package/src/components/button/button.component.tsx +9 -10
  153. package/src/components/button/button.styles.ts +4 -0
  154. package/src/components/button-dropdown/button-dropdown.styles.ts +1 -1
  155. package/src/components/button-dropdown/components/panel/panel.component.tsx +4 -3
  156. package/src/components/button-dropdown/components/panel/panel.styles.ts +8 -2
  157. package/src/components/checkbox-group/components/checkbox/checkbox.component.tsx +2 -2
  158. package/src/components/checkbox-group/components/checkbox/checkbox.styles.ts +4 -4
  159. package/src/components/collapsible/collapsible.styles.ts +1 -1
  160. package/src/components/compacta/compacta.component.tsx +4 -3
  161. package/src/components/compacta/compacta.styles.ts +1 -1
  162. package/src/components/flexi-cell/flexi-cell.component.tsx +5 -2
  163. package/src/components/flexi-cell/flexi-cell.styles.ts +5 -2
  164. package/src/components/flexi-cell/flexi-cell.types.ts +4 -0
  165. package/src/components/flexi-cell/index.ts +1 -0
  166. package/src/components/icon/index.ts +1 -0
  167. package/src/components/index.ts +3 -1
  168. package/src/components/input/input.component.tsx +10 -1
  169. package/src/components/input/input.styles.ts +4 -1
  170. package/src/components/link/link.component.tsx +10 -3
  171. package/src/components/link/link.styles.ts +7 -3
  172. package/src/components/list/components/item/item.component.tsx +4 -1
  173. package/src/components/list/components/item/item.styles.ts +5 -2
  174. package/src/components/list/list.stories.tsx +3 -3
  175. package/src/components/list/list.styles.ts +1 -1
  176. package/src/components/modal/components/backdrop/backdrop.styles.ts +2 -2
  177. package/src/components/modal/components/dialog/dialog.component.tsx +4 -3
  178. package/src/components/modal/components/dialog/dialog.styles.ts +7 -3
  179. package/src/components/popover/components/panel/panel.styles.ts +1 -1
  180. package/src/components/popover/popover.component.tsx +0 -1
  181. package/src/components/popover/popover.styles.ts +0 -1
  182. package/src/components/radio-group/components/radio/radio.component.tsx +2 -2
  183. package/src/components/radio-group/components/radio/radio.styles.ts +5 -5
  184. package/src/components/repeater/index.ts +2 -0
  185. package/src/components/repeater/repeater.component.tsx +124 -0
  186. package/src/components/repeater/repeater.stories.tsx +49 -0
  187. package/src/components/repeater/repeater.styles.ts +32 -0
  188. package/src/components/repeater/repeater.types.ts +20 -0
  189. package/src/components/repeater/repeater.utils.tsx +3 -0
  190. package/src/components/select/select.component.tsx +3 -1
  191. package/src/components/select/select.styles.ts +6 -1
  192. package/src/components/selector/components/index.ts +2 -0
  193. package/src/components/selector/components/selector-checkbox-group/components/index.ts +1 -0
  194. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/index.ts +2 -0
  195. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.component.tsx +96 -0
  196. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.styles.ts +53 -0
  197. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.types.ts +15 -0
  198. package/src/components/selector/components/selector-checkbox-group/index.ts +2 -0
  199. package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.tsx +46 -0
  200. package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.styles.ts +9 -0
  201. package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.types.ts +25 -0
  202. package/src/components/selector/components/selector-radio-group/components/index.ts +1 -0
  203. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/index.ts +2 -0
  204. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.tsx +90 -0
  205. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.styles.ts +53 -0
  206. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.types.ts +21 -0
  207. package/src/components/selector/components/selector-radio-group/index.ts +2 -0
  208. package/src/components/selector/components/selector-radio-group/selector-radio-group.component.tsx +48 -0
  209. package/src/components/selector/components/selector-radio-group/selector-radio-group.styles.ts +14 -0
  210. package/src/components/selector/components/selector-radio-group/selector-radio-group.types.ts +22 -0
  211. package/src/components/selector/index.ts +2 -0
  212. package/src/components/selector/selector.component.tsx +34 -0
  213. package/src/components/selector/selector.stories.tsx +621 -0
  214. package/src/components/selector/selector.types.ts +24 -0
  215. package/src/components/switch/switch.component.tsx +19 -7
  216. package/src/components/switch/switch.stories.tsx +1 -1
  217. package/src/components/switch/switch.styles.ts +2 -2
  218. package/src/components/switch/switch.types.ts +5 -1
  219. package/src/components/tabs/components/tab/tab.component.tsx +4 -3
  220. package/src/components/tabs/components/tab/tab.styles.ts +12 -8
  221. package/src/components/tabs/components/tab-panel/tab-panel.component.tsx +3 -2
  222. package/src/components/tabs/components/tab-panel/tab-panel.styles.ts +5 -2
  223. package/src/components/textarea/textarea.component.tsx +9 -1
  224. package/src/components/textarea/textarea.styles.ts +4 -1
  225. package/src/utils/generateId.ts +6 -0
  226. package/src/utils/index.ts +1 -0
@@ -1,5 +1,5 @@
1
1
  import React, { useRef } from 'react';
2
- import { useDialog } from 'react-aria';
2
+ import { useDialog, useFocusRing } from 'react-aria';
3
3
 
4
4
  import { CloseIcon } from '../../../../components/icon/index.js';
5
5
 
@@ -10,7 +10,8 @@ import { type DialogProps } from './dialog.types.js';
10
10
 
11
11
  export function Dialog({ className, body, onClose, size = 'md', ...props }: DialogProps) {
12
12
  const { children } = props;
13
- const styles = dialogStyles({ className, size });
13
+ const { isFocusVisible, focusProps } = useFocusRing();
14
+ const styles = dialogStyles({ className, size, isFocusVisible });
14
15
 
15
16
  const ref = useRef(null);
16
17
 
@@ -19,7 +20,7 @@ export function Dialog({ className, body, onClose, size = 'md', ...props }: Dial
19
20
  return (
20
21
  <div {...dialogProps} ref={ref} className={styles.base()}>
21
22
  {onClose && (
22
- <button className={styles.close()} onClick={onClose} aria-label="Close modal">
23
+ <button className={styles.close()} onClick={onClose} aria-label="Close modal" {...focusProps}>
23
24
  <CloseIcon className="block" size="small" />
24
25
  </button>
25
26
  )}
@@ -3,9 +3,9 @@ import { tv } from 'tailwind-variants';
3
3
  export const styles = tv(
4
4
  {
5
5
  slots: {
6
- base: 'relative mx-auto max-w-full overflow-hidden rounded-[0.1875rem] bg-white focus:focus-outline',
7
- title: 'border-b border-b-heading pb-2 pl-4 pr-5 pt-3 text-lg font-medium',
8
- close: 'absolute right-1 top-1 block p-1 focus:focus-outline',
6
+ base: 'relative mx-auto max-w-full overflow-hidden rounded-[0.1875rem] bg-white outline-none',
7
+ title: 'border-b-heading border-b pb-2 pl-4 pr-5 pt-3 text-lg font-medium',
8
+ close: 'absolute right-1 top-1 block p-1',
9
9
  },
10
10
  variants: {
11
11
  size: {
@@ -19,6 +19,10 @@ export const styles = tv(
19
19
  base: 'w-[18.75rem]',
20
20
  },
21
21
  },
22
+ isFocusVisible: {
23
+ true: { close: 'focus-outline' },
24
+ false: { close: 'outline-none' },
25
+ },
22
26
  },
23
27
  },
24
28
  { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
@@ -7,7 +7,7 @@ export const styles = tv(
7
7
  popover: 'border-muted absolute z-[999] rounded-[3px] border bg-white shadow-[0_5px_10px_rgba(0,0,0,0.2)]',
8
8
  arrow:
9
9
  'border-t-muted absolute h-0 w-0 border-x-[8px] border-t-[12px] border-x-[transparent] after:absolute after:h-0 after:w-0 after:border-x-[7px] after:border-t-[11px] after:border-x-[transparent] after:border-t-white',
10
- closeBtn: 'focus:focus-outline absolute right-1 top-1 m-1 p-0 hover:opacity-80',
10
+ closeBtn: 'absolute right-1 top-1 m-1 p-0 hover:opacity-80',
11
11
  content: 'w-[17.625rem] py-4 pl-3 pr-5',
12
12
  heading: 'typography-body-9 mb-2 font-bold',
13
13
  body: 'typography-body-10',
@@ -47,7 +47,6 @@ export function Popover({
47
47
  <Button
48
48
  look={icon && !children ? 'link' : look}
49
49
  iconAfter={icon}
50
- className={styles.button()}
51
50
  aria-expanded={state.isOpen}
52
51
  aria-controls={panelId}
53
52
  onClick={handleClick}
@@ -4,7 +4,6 @@ export const styles = tv(
4
4
  {
5
5
  slots: {
6
6
  base: 'relative inline-block',
7
- button: 'focus:focus-outline',
8
7
  },
9
8
  variants: {},
10
9
  },
@@ -11,8 +11,8 @@ function BaseRadio({ className, hint, children, ...props }: RadioProps, ref: any
11
11
  const { size, orientation } = state;
12
12
  const localRef = useRef(null);
13
13
  const { inputProps, isSelected, isDisabled } = useRadio({ ...props, children }, state, localRef);
14
- const { isFocusVisible, focusProps } = useFocusRing();
15
- const styles = radioStyles({ isDisabled, isSelected, isFocusVisible, size, orientation });
14
+ const { isFocused, focusProps } = useFocusRing();
15
+ const styles = radioStyles({ isDisabled, isSelected, isFocused, size, orientation });
16
16
 
17
17
  return (
18
18
  <label className={styles.base({ className })} ref={ref}>
@@ -6,8 +6,8 @@ export const styles = tv(
6
6
  base: 'flex',
7
7
  textWrapper: 'flex flex-col justify-center',
8
8
  labelText: 'typography-body-10 py-[2px] pl-1',
9
- hintText: 'typography-body-10 pl-1 text-muted',
10
- selector: 'flex h-4 w-4 items-center justify-center rounded-full border border-hero',
9
+ hintText: 'typography-body-10 text-muted pl-1',
10
+ selector: 'border-hero flex h-4 w-4 items-center justify-center rounded-full border',
11
11
  },
12
12
  variants: {
13
13
  isDisabled: {
@@ -21,11 +21,11 @@ export const styles = tv(
21
21
  },
22
22
  isSelected: {
23
23
  true: {
24
- selector: 'before:block before:h-2 before:w-2 before:rounded-full before:bg-hero',
24
+ selector: 'before:bg-hero before:block before:h-2 before:w-2 before:rounded-full',
25
25
  },
26
26
  },
27
- isFocusVisible: {
28
- true: { selector: 'rounded-full focus-outline' },
27
+ isFocused: {
28
+ true: { selector: 'focus-outline' },
29
29
  },
30
30
  orientation: {
31
31
  horizontal: {
@@ -0,0 +1,2 @@
1
+ export { Repeater } from './repeater.component.js';
2
+ export { type RepeaterProps } from './repeater.types.js';
@@ -0,0 +1,124 @@
1
+ import { AnimatePresence, LazyMotion, m } from 'framer-motion';
2
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { useFocusRing } from 'react-aria';
4
+
5
+ import { generateID } from '../../utils/index.js';
6
+ import { Button } from '../button/index.js';
7
+ import { AddCircleIcon, IconProps, RemoveCircleIcon } from '../icon/index.js';
8
+ import { VisuallyHidden } from '../index.js';
9
+
10
+ import { styles as repeaterStyles } from './repeater.styles.js';
11
+ import { type RepeaterProps } from './repeater.types.js';
12
+
13
+ interface Action {
14
+ id?: string;
15
+ index: number;
16
+ type: string;
17
+ }
18
+
19
+ const loadAnimations = () => import('./repeater.utils.js').then(res => res.default);
20
+
21
+ export function Repeater({
22
+ addText = 'Add another item',
23
+ indexTag: ItemIndex = 'h3',
24
+ children,
25
+ separator = false,
26
+ className,
27
+ }: RepeaterProps) {
28
+ const [items, setItems] = useState([{ id: generateID() }]);
29
+ const [action, setAction] = useState<Action>({ type: '', index: 0 });
30
+ const [status, setStatus] = useState('');
31
+ const refArr = useRef<HTMLElement[]>([]);
32
+ const { isFocused, focusProps } = useFocusRing();
33
+
34
+ const handleAdd = useCallback(() => {
35
+ setItems([...items, { id: generateID() }]);
36
+ setAction({ type: 'add', index: items.length });
37
+ }, [items]);
38
+
39
+ const handleRemove = useCallback(
40
+ (id: string, index: number) => {
41
+ const newItems = items.filter(item => item.id !== id);
42
+ setItems(newItems);
43
+ setAction({ type: 'remove', index, id });
44
+ },
45
+ [items],
46
+ );
47
+
48
+ useEffect(() => {
49
+ if (action.type === 'add') {
50
+ refArr.current[items.length - 1]?.focus();
51
+ setStatus(`Item added`);
52
+ }
53
+
54
+ if (action.type === 'remove') {
55
+ refArr.current.splice(action.index, 1);
56
+ const focusIndex = action.index === 0 ? 0 : action.index - 1;
57
+ refArr.current[focusIndex]?.focus();
58
+ setStatus(`Item ${action.index + 1} removed`);
59
+ }
60
+ }, [items.length, action]);
61
+
62
+ const Tag = separator ? 'ol' : 'ul';
63
+ const styles = repeaterStyles({ separator, isFocused });
64
+
65
+ return (
66
+ <div className={styles.base({ className })}>
67
+ <Tag className={styles.list()}>
68
+ <LazyMotion features={loadAnimations}>
69
+ <AnimatePresence>
70
+ {items.map((item, index) => {
71
+ return (
72
+ <m.li
73
+ initial={{ opacity: 1 }}
74
+ animate={{ opacity: 1 }}
75
+ exit={{ opacity: 0 }}
76
+ transition={{ duration: 0.15 }}
77
+ key={index}
78
+ >
79
+ <div
80
+ ref={(el: HTMLDivElement) => {
81
+ refArr.current[index] = el;
82
+ }}
83
+ tabIndex={-1}
84
+ className={styles.item()}
85
+ {...focusProps}
86
+ >
87
+ {separator && <ItemIndex className={styles.itemIndex()}>{index + 1}.</ItemIndex>}
88
+ <div className={styles.content()}>{children}</div>
89
+ {items.length > 1 && (
90
+ <Button
91
+ className={styles.removeBtn()}
92
+ aria-label={`remove item ${index + 1}`}
93
+ iconBefore={(props: IconProps) => <RemoveCircleIcon {...props} aria-hidden size="xsmall" />}
94
+ look="link"
95
+ size="small"
96
+ soft
97
+ onClick={() => handleRemove(item.id, index)}
98
+ >
99
+ Remove
100
+ </Button>
101
+ )}
102
+ </div>
103
+ </m.li>
104
+ );
105
+ })}
106
+ </AnimatePresence>
107
+ </LazyMotion>
108
+ </Tag>
109
+ <div className={styles.footer()}>
110
+ <Button
111
+ className={styles.addBtn()}
112
+ iconBefore={(props: IconProps) => <AddCircleIcon {...props} aria-hidden look="outlined" />}
113
+ look="link"
114
+ size="small"
115
+ soft
116
+ onClick={() => handleAdd()}
117
+ >
118
+ {addText}
119
+ </Button>
120
+ </div>
121
+ <VisuallyHidden role="status">{status}</VisuallyHidden>
122
+ </div>
123
+ );
124
+ }
@@ -0,0 +1,49 @@
1
+ import { type Meta, StoryFn, type StoryObj } from '@storybook/react';
2
+
3
+ import { Form, Input } from '../index.js';
4
+
5
+ import { Repeater } from './repeater.component.js';
6
+
7
+ const meta: Meta<typeof Repeater> = {
8
+ title: 'Example/Repeater',
9
+ component: Repeater,
10
+ tags: ['autodocs'],
11
+ decorators: [
12
+ (Story: StoryFn) => (
13
+ <div className="p-5">
14
+ <Story />
15
+ </div>
16
+ ),
17
+ ],
18
+ parameters: {
19
+ layout: 'fullscreen',
20
+ },
21
+ args: {
22
+ children: (
23
+ <Form>
24
+ <Form.Group>
25
+ <Form.Label htmlFor={`test`}>Primary</Form.Label>
26
+ <Form.Hint>Primary title text</Form.Hint>
27
+ <Input name={`test`} onChange={(e: any) => console.log(e)} />
28
+ </Form.Group>
29
+ </Form>
30
+ ),
31
+ },
32
+ };
33
+
34
+ export default meta;
35
+ type Story = StoryObj<typeof meta>;
36
+
37
+ /**
38
+ * > Default usage example
39
+ */
40
+ export const DefaultStory: Story = {
41
+ args: {},
42
+ };
43
+
44
+ /**
45
+ * > Example using the separator prop
46
+ */
47
+ export const SeparatedRepeater: Story = {
48
+ args: { separator: true },
49
+ };
@@ -0,0 +1,32 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ slots: {
6
+ base: '',
7
+ list: 'm-0 list-none pl-0',
8
+ item: 'relative',
9
+ itemIndex: 'mb-[1.125rem] font-bold',
10
+ content: '',
11
+ removeBtn: 'absolute right-0 top-0 h-auto p-0 no-underline hover:underline',
12
+ footer: 'flex justify-between',
13
+ addBtn: 'height-auto p-0 no-underline hover:underline',
14
+ },
15
+ variants: {
16
+ separator: {
17
+ true: {
18
+ item: 'border-neutral border-t-2 pt-[0.625rem]',
19
+ content: 'p-[0_1.125rem_2.625rem]',
20
+ removeBtn: 'relative m-[0_0_1.875rem_1.125rem]',
21
+ footer: 'border-neutral border-t-2 pt-[0.875rem]',
22
+ },
23
+ },
24
+ isFocused: {
25
+ true: {
26
+ item: 'outline-none',
27
+ },
28
+ },
29
+ },
30
+ },
31
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
32
+ );
@@ -0,0 +1,20 @@
1
+ import { HTMLAttributes } from 'react';
2
+
3
+ export type RepeaterProps = {
4
+ /**
5
+ * Text for add button
6
+ */
7
+ addText?: string;
8
+ /**
9
+ * Component to repeat
10
+ */
11
+ children: React.ReactNode;
12
+ /**
13
+ * Index heading tag to use for index on separator version
14
+ */
15
+ indexTag?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
16
+ /**
17
+ * Enable separator version
18
+ */
19
+ separator?: boolean;
20
+ } & HTMLAttributes<Element>;
@@ -0,0 +1,3 @@
1
+ import { domAnimation } from 'framer-motion';
2
+
3
+ export default domAnimation;
@@ -1,4 +1,5 @@
1
1
  import React, { ForwardedRef, forwardRef } from 'react';
2
+ import { mergeProps, useFocusRing } from 'react-aria';
2
3
 
3
4
  import { styles } from './select.styles.js';
4
5
  import { type SelectProps } from './select.types.js';
@@ -7,8 +8,9 @@ function BaseSelect(
7
8
  { className, size = 'medium', invalid = false, children, ...props }: SelectProps,
8
9
  ref: ForwardedRef<HTMLSelectElement>,
9
10
  ) {
11
+ const { isFocused, focusProps } = useFocusRing();
10
12
  return (
11
- <select ref={ref} className={styles({ className, size, invalid })} {...props}>
13
+ <select ref={ref} className={styles({ className, size, invalid, isFocused })} {...mergeProps(props, focusProps)}>
12
14
  {children}
13
15
  </select>
14
16
  );
@@ -2,7 +2,7 @@ import { tv } from 'tailwind-variants';
2
2
 
3
3
  export const styles = tv(
4
4
  {
5
- base: 'form-control bg-no-repeat select-caret disabled:form-control-disabled focus:focus-outline group-[.add-on-after]:rounded-l-none group-[.add-on-before]:rounded-r-none',
5
+ base: 'form-control select-caret disabled:form-control-disabled bg-no-repeat group-[.add-on-after]:rounded-l-none group-[.add-on-before]:rounded-r-none',
6
6
  variants: {
7
7
  size: {
8
8
  small: 'form-control-small bg-[right_0.5625rem_center] pr-[calc(0.5rem+14px+0.5625rem)]',
@@ -14,6 +14,11 @@ export const styles = tv(
14
14
  true: 'border-danger',
15
15
  false: 'border-borderDark',
16
16
  },
17
+ isFocused: {
18
+ true: {
19
+ base: 'focus-outline',
20
+ },
21
+ },
17
22
  },
18
23
  },
19
24
  { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
@@ -0,0 +1,2 @@
1
+ export * from './selector-checkbox-group/index.js';
2
+ export * from './selector-radio-group/index.js';
@@ -0,0 +1 @@
1
+ export * from './selector-checkbox-group-option/index.js';
@@ -0,0 +1,2 @@
1
+ export { SelectorCheckboxGroupOption } from './selector-checkbox-group-option.component.js';
2
+ export { type SelectorCheckboxGroupOptionProps } from './selector-checkbox-group-option.types.js';
@@ -0,0 +1,96 @@
1
+ import React, { forwardRef, useContext, useRef } from 'react';
2
+ import { VisuallyHidden, mergeProps, useCheckboxGroupItem, useFocusRing } from 'react-aria';
3
+
4
+ import {
5
+ FlexiCellAdornment,
6
+ FlexiCellBody,
7
+ FlexiCellButton,
8
+ FlexiCellCircle,
9
+ FlexiCellFooter,
10
+ FlexiCellHint,
11
+ FlexiCellLabel,
12
+ } from '../../../../../../components/flexi-cell/index.js';
13
+ import { ArrowRightIcon, TickIcon } from '../../../../../../components/icon/index.js';
14
+ import { FlexiCell } from '../../../../../../components/index.js';
15
+ import { SelectorCheckboxGroupContext } from '../../selector-checkbox-group.component.js';
16
+
17
+ import { styles as selectorCheckboxGroupOptionStyles } from './selector-checkbox-group-option.styles.js';
18
+ import { type SelectorCheckboxGroupOptionProps } from './selector-checkbox-group-option.types.js';
19
+
20
+ function BaseSelectorCheckboxGroupOption(
21
+ {
22
+ className,
23
+ children,
24
+ value,
25
+ withBorder = true,
26
+ withArrow,
27
+ after,
28
+ badge,
29
+ badgeZIndex,
30
+ before,
31
+ body = true,
32
+ checkIcon = 'checkbox',
33
+ ...props
34
+ }: SelectorCheckboxGroupOptionProps,
35
+ ref: any,
36
+ ) {
37
+ const state = useContext(SelectorCheckboxGroupContext);
38
+ const localRef = useRef(null);
39
+ const { inputProps, isDisabled, isSelected } = useCheckboxGroupItem({ ...props, value, children }, state, localRef);
40
+ const { isFocused, focusProps } = useFocusRing();
41
+ const styles = selectorCheckboxGroupOptionStyles({
42
+ className,
43
+ isSelected,
44
+ isFocused,
45
+ isDisabled,
46
+ checkIcon,
47
+ });
48
+
49
+ const FinalIcon = checkIcon === 'checkbox' ? TickIcon : ArrowRightIcon;
50
+
51
+ return (
52
+ <FlexiCell
53
+ after={
54
+ <div className="flex gap-2">
55
+ {after}
56
+ <FinalIcon aria-hidden="true" className={styles.icon({})} />
57
+ </div>
58
+ }
59
+ badge={badge}
60
+ badgeZIndex={badgeZIndex}
61
+ before={before}
62
+ body={body}
63
+ withBorder={withBorder}
64
+ withArrow={withArrow}
65
+ tag="label"
66
+ ref={ref}
67
+ withHoverEffect
68
+ className={styles.base({})}
69
+ >
70
+ <VisuallyHidden>
71
+ <input {...mergeProps(inputProps, focusProps)} ref={localRef} />
72
+ </VisuallyHidden>
73
+ {children}
74
+ </FlexiCell>
75
+ );
76
+ }
77
+
78
+ export const SelectorCheckboxGroupOption = forwardRef(
79
+ BaseSelectorCheckboxGroupOption,
80
+ ) as React.ForwardRefExoticComponent<SelectorCheckboxGroupOptionProps & React.RefAttributes<unknown>> & {
81
+ Adornment: typeof FlexiCell.Adornment;
82
+ Body: typeof FlexiCell.Body;
83
+ Button: typeof FlexiCell.Button;
84
+ Circle: typeof FlexiCell.Circle;
85
+ Footer: typeof FlexiCell.Footer;
86
+ Hint: typeof FlexiCell.Hint;
87
+ Label: typeof FlexiCell.Label;
88
+ };
89
+
90
+ SelectorCheckboxGroupOption.Body = FlexiCellBody;
91
+ SelectorCheckboxGroupOption.Footer = FlexiCellFooter;
92
+ SelectorCheckboxGroupOption.Adornment = FlexiCellAdornment;
93
+ SelectorCheckboxGroupOption.Hint = FlexiCellHint;
94
+ SelectorCheckboxGroupOption.Label = FlexiCellLabel;
95
+ SelectorCheckboxGroupOption.Button = FlexiCellButton;
96
+ SelectorCheckboxGroupOption.Circle = FlexiCellCircle;
@@ -0,0 +1,53 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ slots: {
6
+ base: 'group/checkbox-option cursor-pointer',
7
+ icon: 'transition-transform',
8
+ },
9
+ variants: {
10
+ checkIcon: {
11
+ arrow: {
12
+ icon: 'text-primary group-hover/checkbox-option:translate-x-1',
13
+ },
14
+ checkbox: {},
15
+ },
16
+ isSelected: {
17
+ true: {
18
+ base: 'border-hero shadow-[0_0_0_2px_inset]',
19
+ },
20
+ false: {},
21
+ },
22
+ isFocused: {
23
+ true: {
24
+ base: 'focus-outline',
25
+ },
26
+ false: {},
27
+ },
28
+ isDisabled: {
29
+ true: {
30
+ base: 'opacity-50',
31
+ },
32
+ false: {},
33
+ },
34
+ },
35
+ compoundVariants: [
36
+ {
37
+ checkIcon: 'checkbox',
38
+ isSelected: false,
39
+ className: {
40
+ icon: 'opacity-0',
41
+ },
42
+ },
43
+ {
44
+ checkIcon: 'checkbox',
45
+ isSelected: true,
46
+ className: {
47
+ icon: 'opacity-100',
48
+ },
49
+ },
50
+ ],
51
+ },
52
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
53
+ );
@@ -0,0 +1,15 @@
1
+ import { type AriaCheckboxGroupItemProps } from 'react-aria';
2
+ import { type VariantProps } from 'tailwind-variants';
3
+
4
+ import { FlexiCellProps } from '../../../../../index.js';
5
+
6
+ import { styles } from './selector-checkbox-group-option.styles.js';
7
+
8
+ export type SelectorCheckboxGroupOptionProps = {
9
+ /**
10
+ * Check icon to render
11
+ */
12
+ checkIcon?: 'checkbox' | 'arrow';
13
+ } & FlexiCellProps &
14
+ VariantProps<typeof styles> &
15
+ Omit<AriaCheckboxGroupItemProps, 'isIndeterminate'>;
@@ -0,0 +1,2 @@
1
+ export { SelectorCheckboxGroup } from './selector-checkbox-group.component.js';
2
+ export { type SelectorCheckboxGroupProps } from './selector-checkbox-group.types.js';
@@ -0,0 +1,46 @@
1
+ import React, { createContext } from 'react';
2
+ import { useCheckboxGroup } from 'react-aria';
3
+ import { useCheckboxGroupState } from 'react-stately';
4
+
5
+ import { SelectorCheckboxGroupOption } from './components/index.js';
6
+ import { styles } from './selector-checkbox-group.styles.js';
7
+ import {
8
+ type SelectorCheckboxGroupContextState,
9
+ type SelectorCheckboxGroupProps,
10
+ } from './selector-checkbox-group.types.js';
11
+
12
+ export const SelectorCheckboxGroupContext = createContext<SelectorCheckboxGroupContextState>({
13
+ value: [],
14
+ isDisabled: false,
15
+ isReadOnly: false,
16
+ isSelected: () => false,
17
+ setValue: () => null,
18
+ addValue: () => null,
19
+ removeValue: () => null,
20
+ toggleValue: () => null,
21
+ validationState: 'valid',
22
+ });
23
+
24
+ export function SelectorCheckboxGroup(props: SelectorCheckboxGroupProps) {
25
+ const { children, label, description, errorMessage } = props;
26
+ const state = useCheckboxGroupState(props);
27
+ const { groupProps, labelProps, descriptionProps, errorMessageProps } = useCheckboxGroup(props, state);
28
+
29
+ return (
30
+ <div {...groupProps} className={styles({ className: groupProps.className })}>
31
+ <span {...labelProps}>{label}</span>
32
+ <SelectorCheckboxGroupContext.Provider value={state}>{children}</SelectorCheckboxGroupContext.Provider>
33
+ {description && (
34
+ <div {...descriptionProps} style={{ fontSize: 12 }}>
35
+ {description}
36
+ </div>
37
+ )}
38
+ {errorMessage && state.validationState === 'invalid' && (
39
+ <div {...errorMessageProps} className="typography-body-10 text-danger">
40
+ {errorMessage}
41
+ </div>
42
+ )}
43
+ </div>
44
+ );
45
+ }
46
+ SelectorCheckboxGroup.Option = SelectorCheckboxGroupOption;
@@ -0,0 +1,9 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ base: 'flex flex-col gap-2 md:gap-3',
6
+ variants: {},
7
+ },
8
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
9
+ );
@@ -0,0 +1,25 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ import { AriaCheckboxGroupProps } from 'react-aria';
3
+ import { CheckboxGroupState } from 'react-stately';
4
+ import { type VariantProps } from 'tailwind-variants';
5
+
6
+ import { styles } from './selector-checkbox-group.styles.js';
7
+
8
+ export type SelectorCheckboxGroupContextState = CheckboxGroupState;
9
+
10
+ export type SelectorCheckboxGroupProps = {
11
+ /**
12
+ * String to override base style
13
+ */
14
+ className?: string;
15
+ /**
16
+ * Orientation of checkbox items
17
+ */
18
+ orientation?: 'horizontal' | 'vertical';
19
+ /**
20
+ * Controls size of `CheckboxItem` components, can't be applied directly to `CheckboxItem`
21
+ */
22
+ size?: 'medium' | 'large';
23
+ } & AriaCheckboxGroupProps &
24
+ VariantProps<typeof styles> &
25
+ Omit<HTMLAttributes<Element>, 'onChange'>;
@@ -0,0 +1 @@
1
+ export * from './selector-radio-group-option/index.js';
@@ -0,0 +1,2 @@
1
+ export { SelectorRadioGroupOption } from './selector-radio-group-option.component.js';
2
+ export { type SelectorRadioGroupOptionProps } from './selector-radio-group-option.types.js';