@westpac/ui 0.4.0 → 0.5.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 (122) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/components/alert/alert.component.js +2 -1
  3. package/dist/components/alert/alert.styles.js +2 -2
  4. package/dist/components/compacta/compacta.component.js +4 -3
  5. package/dist/components/flexi-cell/flexi-cell.component.js +2 -2
  6. package/dist/components/flexi-cell/flexi-cell.styles.js +1 -1
  7. package/dist/components/flexi-cell/flexi-cell.types.d.ts +4 -0
  8. package/dist/components/flexi-cell/index.d.ts +1 -0
  9. package/dist/components/flexi-cell/index.js +1 -0
  10. package/dist/components/icon/index.d.ts +1 -0
  11. package/dist/components/icon/index.js +1 -0
  12. package/dist/components/index.d.ts +3 -1
  13. package/dist/components/index.js +3 -1
  14. package/dist/components/list/components/item/item.styles.js +1 -1
  15. package/dist/components/list/list.styles.js +1 -1
  16. package/dist/components/repeater/index.d.ts +2 -0
  17. package/dist/components/repeater/index.js +1 -0
  18. package/dist/components/repeater/repeater.component.d.ts +3 -0
  19. package/dist/components/repeater/repeater.component.js +141 -0
  20. package/dist/components/repeater/repeater.stories.d.ts +13 -0
  21. package/dist/components/repeater/repeater.stories.js +34 -0
  22. package/dist/components/repeater/repeater.styles.d.ts +39 -0
  23. package/dist/components/repeater/repeater.styles.js +31 -0
  24. package/dist/components/repeater/repeater.types.d.ts +19 -0
  25. package/dist/components/repeater/repeater.types.js +1 -0
  26. package/dist/components/repeater/repeater.utils.d.ts +2 -0
  27. package/dist/components/repeater/repeater.utils.js +2 -0
  28. package/dist/components/selector/components/index.d.ts +2 -0
  29. package/dist/components/selector/components/index.js +2 -0
  30. package/dist/components/selector/components/selector-checkbox-group/components/index.d.ts +1 -0
  31. package/dist/components/selector/components/selector-checkbox-group/components/index.js +1 -0
  32. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/index.d.ts +2 -0
  33. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/index.js +1 -0
  34. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.component.d.ts +12 -0
  35. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.component.js +67 -0
  36. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.styles.d.ts +59 -0
  37. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.styles.js +57 -0
  38. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.types.d.ts +10 -0
  39. package/dist/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.types.js +1 -0
  40. package/dist/components/selector/components/selector-checkbox-group/index.d.ts +2 -0
  41. package/dist/components/selector/components/selector-checkbox-group/index.js +1 -0
  42. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.d.ts +15 -0
  43. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.js +49 -0
  44. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.styles.d.ts +3 -0
  45. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.styles.js +13 -0
  46. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.types.d.ts +20 -0
  47. package/dist/components/selector/components/selector-checkbox-group/selector-checkbox-group.types.js +1 -0
  48. package/dist/components/selector/components/selector-radio-group/components/index.d.ts +1 -0
  49. package/dist/components/selector/components/selector-radio-group/components/index.js +1 -0
  50. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/index.d.ts +2 -0
  51. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/index.js +1 -0
  52. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.d.ts +12 -0
  53. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.js +67 -0
  54. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.styles.d.ts +59 -0
  55. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.styles.js +57 -0
  56. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.types.d.ts +15 -0
  57. package/dist/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.types.js +1 -0
  58. package/dist/components/selector/components/selector-radio-group/index.d.ts +2 -0
  59. package/dist/components/selector/components/selector-radio-group/index.js +1 -0
  60. package/dist/components/selector/components/selector-radio-group/selector-radio-group.component.d.ts +15 -0
  61. package/dist/components/selector/components/selector-radio-group/selector-radio-group.component.js +58 -0
  62. package/dist/components/selector/components/selector-radio-group/selector-radio-group.styles.d.ts +13 -0
  63. package/dist/components/selector/components/selector-radio-group/selector-radio-group.styles.js +18 -0
  64. package/dist/components/selector/components/selector-radio-group/selector-radio-group.types.d.ts +17 -0
  65. package/dist/components/selector/components/selector-radio-group/selector-radio-group.types.js +1 -0
  66. package/dist/components/selector/index.d.ts +2 -0
  67. package/dist/components/selector/index.js +1 -0
  68. package/dist/components/selector/selector.component.d.ts +30 -0
  69. package/dist/components/selector/selector.component.js +34 -0
  70. package/dist/components/selector/selector.stories.d.ts +57 -0
  71. package/dist/components/selector/selector.stories.js +515 -0
  72. package/dist/components/selector/selector.types.d.ts +20 -0
  73. package/dist/components/selector/selector.types.js +1 -0
  74. package/dist/css/westpac-ui.css +150 -0
  75. package/dist/css/westpac-ui.min.css +150 -0
  76. package/dist/utils/generateId.d.ts +1 -0
  77. package/dist/utils/generateId.js +6 -0
  78. package/dist/utils/index.d.ts +1 -0
  79. package/dist/utils/index.js +1 -0
  80. package/package.json +7 -1
  81. package/src/components/alert/alert.component.tsx +1 -1
  82. package/src/components/alert/alert.styles.ts +2 -2
  83. package/src/components/compacta/compacta.component.tsx +4 -3
  84. package/src/components/flexi-cell/flexi-cell.component.tsx +2 -1
  85. package/src/components/flexi-cell/flexi-cell.styles.ts +1 -1
  86. package/src/components/flexi-cell/flexi-cell.types.ts +4 -0
  87. package/src/components/flexi-cell/index.ts +1 -0
  88. package/src/components/icon/index.ts +1 -0
  89. package/src/components/index.ts +3 -1
  90. package/src/components/list/components/item/item.styles.ts +1 -1
  91. package/src/components/list/list.styles.ts +1 -1
  92. package/src/components/repeater/index.ts +2 -0
  93. package/src/components/repeater/repeater.component.tsx +121 -0
  94. package/src/components/repeater/repeater.stories.tsx +49 -0
  95. package/src/components/repeater/repeater.styles.ts +27 -0
  96. package/src/components/repeater/repeater.types.ts +20 -0
  97. package/src/components/repeater/repeater.utils.tsx +3 -0
  98. package/src/components/selector/components/index.ts +2 -0
  99. package/src/components/selector/components/selector-checkbox-group/components/index.ts +1 -0
  100. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/index.ts +2 -0
  101. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.component.tsx +96 -0
  102. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.styles.ts +53 -0
  103. package/src/components/selector/components/selector-checkbox-group/components/selector-checkbox-group-option/selector-checkbox-group-option.types.ts +15 -0
  104. package/src/components/selector/components/selector-checkbox-group/index.ts +2 -0
  105. package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.component.tsx +46 -0
  106. package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.styles.ts +9 -0
  107. package/src/components/selector/components/selector-checkbox-group/selector-checkbox-group.types.ts +25 -0
  108. package/src/components/selector/components/selector-radio-group/components/index.ts +1 -0
  109. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/index.ts +2 -0
  110. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.component.tsx +90 -0
  111. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.styles.ts +53 -0
  112. package/src/components/selector/components/selector-radio-group/components/selector-radio-group-option/selector-radio-group-option.types.ts +21 -0
  113. package/src/components/selector/components/selector-radio-group/index.ts +2 -0
  114. package/src/components/selector/components/selector-radio-group/selector-radio-group.component.tsx +48 -0
  115. package/src/components/selector/components/selector-radio-group/selector-radio-group.styles.ts +14 -0
  116. package/src/components/selector/components/selector-radio-group/selector-radio-group.types.ts +22 -0
  117. package/src/components/selector/index.ts +2 -0
  118. package/src/components/selector/selector.component.tsx +34 -0
  119. package/src/components/selector/selector.stories.tsx +621 -0
  120. package/src/components/selector/selector.types.ts +24 -0
  121. package/src/utils/generateId.ts +6 -0
  122. package/src/utils/index.ts +1 -0
@@ -0,0 +1,121 @@
1
+ import { AnimatePresence, LazyMotion, m } from 'framer-motion';
2
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
3
+
4
+ import { generateID } from '../../utils/index.js';
5
+ import { Button } from '../button/index.js';
6
+ import { AddCircleIcon, IconProps, RemoveCircleIcon } from '../icon/index.js';
7
+ import { VisuallyHidden } from '../index.js';
8
+
9
+ import { styles as repeaterStyles } from './repeater.styles.js';
10
+ import { type RepeaterProps } from './repeater.types.js';
11
+
12
+ interface Action {
13
+ id?: string;
14
+ index: number;
15
+ type: string;
16
+ }
17
+
18
+ const loadAnimations = () => import('./repeater.utils.js').then(res => res.default);
19
+
20
+ export function Repeater({
21
+ addText = 'Add another item',
22
+ indexTag: ItemIndex = 'h3',
23
+ children,
24
+ separator = false,
25
+ className,
26
+ }: RepeaterProps) {
27
+ const [items, setItems] = useState([{ id: generateID() }]);
28
+ const [action, setAction] = useState<Action>({ type: '', index: 0 });
29
+ const [status, setStatus] = useState('');
30
+ const refArr = useRef<HTMLElement[]>([]);
31
+
32
+ const handleAdd = useCallback(() => {
33
+ setItems([...items, { id: generateID() }]);
34
+ setAction({ type: 'add', index: items.length });
35
+ }, [items]);
36
+
37
+ const handleRemove = useCallback(
38
+ (id: string, index: number) => {
39
+ const newItems = items.filter(item => item.id !== id);
40
+ setItems(newItems);
41
+ setAction({ type: 'remove', index, id });
42
+ },
43
+ [items],
44
+ );
45
+
46
+ useEffect(() => {
47
+ if (action.type === 'add') {
48
+ refArr.current[items.length - 1]?.focus();
49
+ setStatus(`Item added`);
50
+ }
51
+
52
+ if (action.type === 'remove') {
53
+ refArr.current.splice(action.index, 1);
54
+ const focusIndex = action.index === 0 ? 0 : action.index - 1;
55
+ refArr.current[focusIndex]?.focus();
56
+ setStatus(`Item ${action.index + 1} removed`);
57
+ }
58
+ }, [items.length, action]);
59
+
60
+ const Tag = separator ? 'ol' : 'ul';
61
+ const styles = repeaterStyles({ separator });
62
+
63
+ return (
64
+ <div className={styles.base({ className })}>
65
+ <Tag className={styles.list()}>
66
+ <LazyMotion features={loadAnimations}>
67
+ <AnimatePresence>
68
+ {items.map((item, index) => {
69
+ return (
70
+ <m.li
71
+ initial={{ opacity: 1 }}
72
+ animate={{ opacity: 1 }}
73
+ exit={{ opacity: 0 }}
74
+ transition={{ duration: 0.15 }}
75
+ key={index}
76
+ >
77
+ <div
78
+ ref={(el: HTMLDivElement) => {
79
+ refArr.current[index] = el;
80
+ }}
81
+ tabIndex={-1}
82
+ className={styles.item()}
83
+ >
84
+ {separator && <ItemIndex className={styles.itemIndex()}>{index + 1}.</ItemIndex>}
85
+ <div className={styles.content()}>{children}</div>
86
+ {items.length > 1 && (
87
+ <Button
88
+ className={styles.removeBtn()}
89
+ aria-label={`remove item ${index + 1}`}
90
+ iconBefore={(props: IconProps) => <RemoveCircleIcon {...props} aria-hidden size="xsmall" />}
91
+ look="link"
92
+ size="small"
93
+ soft
94
+ onClick={() => handleRemove(item.id, index)}
95
+ >
96
+ Remove
97
+ </Button>
98
+ )}
99
+ </div>
100
+ </m.li>
101
+ );
102
+ })}
103
+ </AnimatePresence>
104
+ </LazyMotion>
105
+ </Tag>
106
+ <div className={styles.footer()}>
107
+ <Button
108
+ className={styles.addBtn()}
109
+ iconBefore={(props: IconProps) => <AddCircleIcon {...props} aria-hidden look="outlined" />}
110
+ look="link"
111
+ size="small"
112
+ soft
113
+ onClick={() => handleAdd()}
114
+ >
115
+ {addText}
116
+ </Button>
117
+ </div>
118
+ <VisuallyHidden role="status">{status}</VisuallyHidden>
119
+ </div>
120
+ );
121
+ }
@@ -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,27 @@
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 focus:outline-none',
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
+ },
25
+ },
26
+ { responsiveVariants: ['xsl', 'sm', 'md', 'lg', 'xl'] },
27
+ );
@@ -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;
@@ -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, 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 { isFocusVisible, focusProps } = useFocusRing();
41
+ const styles = selectorCheckboxGroupOptionStyles({
42
+ className,
43
+ isSelected,
44
+ isFocusVisible,
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 {...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
+ isFocusVisible: {
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';
@@ -0,0 +1,90 @@
1
+ import React, { forwardRef, useContext, useRef } from 'react';
2
+ import { VisuallyHidden, useFocusRing, useRadio } 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 '../../../../../index.js';
15
+ import { SelectorRadioGroupContext } from '../../selector-radio-group.component.js';
16
+
17
+ import { styles as selectorRadioGroupOptionStyles } from './selector-radio-group-option.styles.js';
18
+ import { type SelectorRadioGroupOptionProps } from './selector-radio-group-option.types.js';
19
+
20
+ function BaseSelectorRadioGroupOption(
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
+ }: SelectorRadioGroupOptionProps,
35
+ ref: any,
36
+ ) {
37
+ const state = useContext(SelectorRadioGroupContext);
38
+ const localRef = useRef(null);
39
+ const { inputProps, isSelected, isDisabled } = useRadio({ ...props, value, children }, state, localRef);
40
+ const { isFocusVisible, focusProps } = useFocusRing();
41
+ const styles = selectorRadioGroupOptionStyles({ className, isSelected, isFocusVisible, isDisabled, checkIcon });
42
+
43
+ const FinalIcon = checkIcon === 'checkbox' ? TickIcon : ArrowRightIcon;
44
+
45
+ return (
46
+ <FlexiCell
47
+ after={
48
+ <div className="flex gap-2">
49
+ {after}
50
+ <FinalIcon aria-hidden="true" className={styles.icon({})} />
51
+ </div>
52
+ }
53
+ badge={badge}
54
+ badgeZIndex={badgeZIndex}
55
+ before={before}
56
+ body={body}
57
+ withBorder={withBorder}
58
+ withArrow={withArrow}
59
+ tag="label"
60
+ ref={ref}
61
+ className={styles.base({})}
62
+ withHoverEffect
63
+ >
64
+ <VisuallyHidden>
65
+ <input {...inputProps} {...focusProps} ref={localRef} />
66
+ </VisuallyHidden>
67
+ {children}
68
+ </FlexiCell>
69
+ );
70
+ }
71
+
72
+ export const SelectorRadioGroupOption = forwardRef(BaseSelectorRadioGroupOption) as React.ForwardRefExoticComponent<
73
+ SelectorRadioGroupOptionProps & React.RefAttributes<unknown>
74
+ > & {
75
+ Adornment: typeof FlexiCell.Adornment;
76
+ Body: typeof FlexiCell.Body;
77
+ Button: typeof FlexiCell.Button;
78
+ Circle: typeof FlexiCell.Circle;
79
+ Footer: typeof FlexiCell.Footer;
80
+ Hint: typeof FlexiCell.Hint;
81
+ Label: typeof FlexiCell.Label;
82
+ };
83
+
84
+ SelectorRadioGroupOption.Body = FlexiCellBody;
85
+ SelectorRadioGroupOption.Footer = FlexiCellFooter;
86
+ SelectorRadioGroupOption.Adornment = FlexiCellAdornment;
87
+ SelectorRadioGroupOption.Hint = FlexiCellHint;
88
+ SelectorRadioGroupOption.Label = FlexiCellLabel;
89
+ SelectorRadioGroupOption.Button = FlexiCellButton;
90
+ SelectorRadioGroupOption.Circle = FlexiCellCircle;
@@ -0,0 +1,53 @@
1
+ import { tv } from 'tailwind-variants';
2
+
3
+ export const styles = tv(
4
+ {
5
+ slots: {
6
+ base: 'group/radio-option cursor-pointer',
7
+ icon: 'transition-transform',
8
+ },
9
+ variants: {
10
+ checkIcon: {
11
+ arrow: {
12
+ icon: 'text-primary group-hover/radio-option:translate-x-1',
13
+ },
14
+ checkbox: {},
15
+ },
16
+ isSelected: {
17
+ true: {
18
+ base: 'border-hero shadow-[0_0_0_2px_inset] shadow-hero',
19
+ },
20
+ false: {},
21
+ },
22
+ isFocusVisible: {
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,21 @@
1
+ import { type HTMLAttributes } from 'react';
2
+ import { type AriaRadioProps } from 'react-aria';
3
+ import { type VariantProps } from 'tailwind-variants';
4
+
5
+ import { type FlexiCellProps } from '../../../../../../index.js';
6
+
7
+ import { styles } from './selector-radio-group-option.styles.js';
8
+
9
+ export type SelectorRadioGroupOptionProps = {
10
+ /**
11
+ * Check icon to render
12
+ */
13
+ checkIcon?: 'checkbox' | 'arrow';
14
+ /**
15
+ * Tag to render
16
+ */
17
+ tag?: keyof JSX.IntrinsicElements;
18
+ } & FlexiCellProps &
19
+ AriaRadioProps &
20
+ VariantProps<typeof styles> &
21
+ HTMLAttributes<Element>;
@@ -0,0 +1,2 @@
1
+ export { SelectorRadioGroup } from './selector-radio-group.component.js';
2
+ export { type SelectorRadioGroupProps } from './selector-radio-group.types.js';