@eml-payments/ui-kit 1.5.1 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +2 -2
- package/dist/index.d.cts +488 -0
- package/dist/index.d.ts +488 -0
- package/dist/src/components/Input/Input.js +12 -8
- package/dist/src/components/Input/Input.stories.d.ts +3 -2
- package/dist/src/components/Input/Input.stories.js +9 -2
- package/dist/src/components/Select/Select.js +69 -50
- package/dist/src/components/Select/Select.stories.d.ts +3 -0
- package/dist/src/components/Select/Select.stories.js +11 -0
- package/dist/src/components/Select/Select.types.d.ts +3 -0
- package/dist/src/components/SelectWrapper/SelectWrapper.d.ts +1 -1
- package/dist/src/components/SelectWrapper/SelectWrapper.js +2 -2
- package/dist/src/components/SelectWrapper/SelectWrapper.stories.d.ts +4 -1
- package/dist/src/components/SelectWrapper/SelectWrapper.stories.js +38 -28
- package/dist/src/components/SelectWrapper/SelectWrapper.types.d.ts +3 -0
- package/dist/src/components/Stepper/Stepper.js +1 -1
- package/dist/src/components/Table/BaseTable/index.d.ts +1 -0
- package/dist/src/components/Table/BaseTable/index.js +1 -0
- package/dist/src/components/Table/StandardTable/StandardTable.stories.js +5 -1
- package/dist/src/components/Table/Table.js +1 -1
- package/dist/src/components/Textarea/Textarea.js +12 -7
- package/dist/src/components/Textarea/Textarea.stories.d.ts +2 -1
- package/dist/src/components/Textarea/Textarea.stories.js +6 -2
- package/dist/src/components/Tooltip/Tooltip.stories.js +1 -1
- package/package.json +9 -4
|
@@ -5,6 +5,7 @@ import { FaCheck } from 'react-icons/fa6';
|
|
|
5
5
|
import { IoMdArrowDropdown, IoMdArrowDropup } from 'react-icons/io';
|
|
6
6
|
import { FaSearch } from 'react-icons/fa';
|
|
7
7
|
import { cn } from '../../lib/utils';
|
|
8
|
+
import { Button } from '../Button';
|
|
8
9
|
const Select = SelectPrimitive.Root;
|
|
9
10
|
const SelectGroup = SelectPrimitive.Group;
|
|
10
11
|
const SelectValue = SelectPrimitive.Value;
|
|
@@ -14,31 +15,72 @@ const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) =>
|
|
|
14
15
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
15
16
|
const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.ScrollDownButton, { ref: ref, "aria-label": "Scroll down", className: cn('flex cursor-default items-center justify-center py-1', className), ...props, children: _jsx(IoMdArrowDropdown, { className: "h-4 w-4" }) })));
|
|
16
17
|
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
17
|
-
const
|
|
18
|
+
const isSelectItemElement = (node) => {
|
|
19
|
+
return React.isValidElement(node) && node.type === SelectItem;
|
|
20
|
+
};
|
|
21
|
+
const getNodeText = (node) => {
|
|
22
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
23
|
+
return String(node);
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(node)) {
|
|
26
|
+
return node.map(getNodeText).join('');
|
|
27
|
+
}
|
|
28
|
+
if (React.isValidElement(node)) {
|
|
29
|
+
const nodeProps = node.props;
|
|
30
|
+
return getNodeText(nodeProps.children);
|
|
31
|
+
}
|
|
32
|
+
return '';
|
|
33
|
+
};
|
|
34
|
+
const countSelectItems = (node) => {
|
|
35
|
+
return React.Children.toArray(node).reduce((count, child) => {
|
|
36
|
+
if (isSelectItemElement(child)) {
|
|
37
|
+
return count + 1;
|
|
38
|
+
}
|
|
39
|
+
if (!React.isValidElement(child)) {
|
|
40
|
+
return count;
|
|
41
|
+
}
|
|
42
|
+
const childProps = child.props;
|
|
43
|
+
if (!childProps.children) {
|
|
44
|
+
return count;
|
|
45
|
+
}
|
|
46
|
+
return count + countSelectItems(childProps.children);
|
|
47
|
+
}, 0);
|
|
48
|
+
};
|
|
49
|
+
const filterSelectChildren = (node, normalizedSearch) => {
|
|
50
|
+
return React.Children.toArray(node).reduce((acc, child) => {
|
|
51
|
+
if (!React.isValidElement(child)) {
|
|
52
|
+
acc.push(child);
|
|
53
|
+
return acc;
|
|
54
|
+
}
|
|
55
|
+
if (isSelectItemElement(child)) {
|
|
56
|
+
const childProps = child.props;
|
|
57
|
+
const itemText = getNodeText(childProps.children).toLowerCase();
|
|
58
|
+
if (itemText.includes(normalizedSearch)) {
|
|
59
|
+
acc.push(child);
|
|
60
|
+
}
|
|
61
|
+
return acc;
|
|
62
|
+
}
|
|
63
|
+
const childProps = child.props;
|
|
64
|
+
if (!childProps.children) {
|
|
65
|
+
acc.push(child);
|
|
66
|
+
return acc;
|
|
67
|
+
}
|
|
68
|
+
const filteredNestedChildren = filterSelectChildren(childProps.children, normalizedSearch);
|
|
69
|
+
if (filteredNestedChildren.length > 0) {
|
|
70
|
+
acc.push(React.cloneElement(child, {}, filteredNestedChildren));
|
|
71
|
+
}
|
|
72
|
+
return acc;
|
|
73
|
+
}, []);
|
|
74
|
+
};
|
|
75
|
+
const SelectContent = React.forwardRef(({ className, children, position = 'popper', showAlwaysSearch = false, searchPlaceholder = 'Search...', noResultsText = 'No results found', noResultsButtonText, onNoResultsButtonClick, ...props }, ref) => {
|
|
18
76
|
const [searchValue, setSearchValue] = React.useState('');
|
|
19
77
|
const searchRef = React.useRef(null);
|
|
20
78
|
const viewportRef = React.useRef(null);
|
|
21
79
|
const totalItems = React.useMemo(() => {
|
|
22
|
-
|
|
23
|
-
const countItems = (node) => {
|
|
24
|
-
React.Children.forEach(node, (child) => {
|
|
25
|
-
if (React.isValidElement(child)) {
|
|
26
|
-
if (child.type === SelectItem) {
|
|
27
|
-
count++;
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
const childProps = child.props;
|
|
31
|
-
if (childProps.children) {
|
|
32
|
-
countItems(childProps.children);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
countItems(children);
|
|
39
|
-
return count;
|
|
80
|
+
return countSelectItems(children);
|
|
40
81
|
}, [children]);
|
|
41
82
|
const shouldShowSearch = showAlwaysSearch || totalItems > 7;
|
|
83
|
+
const normalizedSearch = searchValue.trim().toLowerCase();
|
|
42
84
|
const handleSearchChange = (value) => {
|
|
43
85
|
setSearchValue(value);
|
|
44
86
|
setTimeout(() => {
|
|
@@ -66,45 +108,22 @@ const SelectContent = React.forwardRef(({ className, children, position = 'poppe
|
|
|
66
108
|
}
|
|
67
109
|
};
|
|
68
110
|
const filteredChildren = React.useMemo(() => {
|
|
69
|
-
|
|
70
|
-
if (!shouldShowSearch || !searchValue) {
|
|
111
|
+
if (!shouldShowSearch || !normalizedSearch) {
|
|
71
112
|
return children;
|
|
72
113
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
? childProps.children
|
|
80
|
-
: React.Children.toArray(childProps.children || []).join('');
|
|
81
|
-
if (!itemText.toLowerCase().includes(searchValue.toLowerCase())) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
else if (React.isValidElement(child) && child.props) {
|
|
86
|
-
const childProps = child.props;
|
|
87
|
-
if (childProps.children) {
|
|
88
|
-
const filteredGrandChildren = (_a = React.Children.map(childProps.children, filterChild)) === null || _a === void 0 ? void 0 : _a.filter(Boolean);
|
|
89
|
-
if (!filteredGrandChildren || filteredGrandChildren.length === 0) {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
return React.cloneElement(child, {}, filteredGrandChildren);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return child;
|
|
97
|
-
};
|
|
98
|
-
const filtered = (_a = React.Children.map(children, filterChild)) === null || _a === void 0 ? void 0 : _a.filter(Boolean);
|
|
99
|
-
return filtered || [];
|
|
100
|
-
}, [children, shouldShowSearch, searchValue]);
|
|
114
|
+
return filterSelectChildren(children, normalizedSearch);
|
|
115
|
+
}, [children, shouldShowSearch, normalizedSearch]);
|
|
116
|
+
const filteredItemsCount = React.useMemo(() => {
|
|
117
|
+
return countSelectItems(filteredChildren);
|
|
118
|
+
}, [filteredChildren]);
|
|
119
|
+
const noResultsContent = (_jsxs("div", { className: "px-2 py-1.5 flex flex-col items-start gap-1", children: [_jsx("span", { className: "text-xs text-muted-foreground", children: noResultsText }), noResultsButtonText && onNoResultsButtonClick && (_jsx(Button, { variant: "secondaryText", onMouseDown: (event) => event.preventDefault(), onClick: onNoResultsButtonClick, className: "h-auto p-0 text-sm", children: noResultsButtonText }))] }));
|
|
101
120
|
return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { ref: ref, className: cn('relative z-50 max-h-(--radix-select-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-(--uikit-radius) border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-select-content-transform-origin)', position === 'popper' &&
|
|
102
121
|
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1', className), position: position, onCloseAutoFocus: () => {
|
|
103
122
|
if (shouldShowSearch) {
|
|
104
123
|
setSearchValue('');
|
|
105
124
|
}
|
|
106
125
|
}, ...props, children: [_jsx(SelectScrollUpButton, {}), _jsxs(SelectPrimitive.Viewport, { ref: viewportRef, className: cn('p-1', position === 'popper' &&
|
|
107
|
-
'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)'), children: [shouldShowSearch && (_jsxs("div", { className: "flex items-center px-2 py-1.5 mb-1", children: [_jsx(FaSearch, { className: "h-4 w-4 mr-2 text-muted-foreground" }), _jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchValue, onChange: (e) => handleSearchChange(e.target.value), onKeyDown: handleSearchKeyDown, className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground", autoComplete: "off" })] })), filteredChildren] }), _jsx(SelectScrollDownButton, {})] }) }));
|
|
126
|
+
'h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)'), children: [shouldShowSearch && (_jsxs("div", { className: "flex items-center px-2 py-1.5 mb-1", children: [_jsx(FaSearch, { className: "h-4 w-4 mr-2 text-muted-foreground" }), _jsx("input", { ref: searchRef, type: "text", placeholder: searchPlaceholder, value: searchValue, onChange: (e) => handleSearchChange(e.target.value), onKeyDown: handleSearchKeyDown, className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground", autoComplete: "off" })] })), filteredItemsCount === 0 ? noResultsContent : filteredChildren] }), _jsx(SelectScrollDownButton, {})] }) }));
|
|
108
127
|
});
|
|
109
128
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
110
129
|
const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (_jsx(SelectPrimitive.Label, { ref: ref, className: cn('px-2 py-1.5 text-sm font-semibold', className), ...props })));
|
|
@@ -10,3 +10,6 @@ export declare const Size: Story;
|
|
|
10
10
|
export declare const WithError: Story;
|
|
11
11
|
export declare const WithManyOptions: Story;
|
|
12
12
|
export declare const WithSearchEnabled: Story;
|
|
13
|
+
export declare const EmptyOptions: Story;
|
|
14
|
+
export declare const EmptyOptionsWithCustomText: Story;
|
|
15
|
+
export declare const EmptyOptionsWithActionButton: Story;
|
|
@@ -37,3 +37,14 @@ export const WithManyOptions = {
|
|
|
37
37
|
export const WithSearchEnabled = {
|
|
38
38
|
render: (args) => (_jsxs(Select, { ...args, children: [_jsx(SelectTrigger, { className: "w-[320px]", children: _jsx(SelectValue, { placeholder: "Select a fruit..." }) }), _jsxs(SelectContent, { showAlwaysSearch: true, children: [_jsx(SelectScrollUpButton, { children: _jsx(FaChevronUp, { className: "h-4 w-4" }) }), _jsx(SelectScrollDownButton, { children: _jsx(FaChevronDown, { className: "h-4 w-4" }) }), _jsxs(SelectGroup, { children: [_jsx(SelectItem, { value: "apple", children: "Apple" }), _jsx(SelectItem, { value: "banana", children: "Banana" })] })] })] })),
|
|
39
39
|
};
|
|
40
|
+
export const EmptyOptions = {
|
|
41
|
+
render: (args) => (_jsxs(Select, { ...args, children: [_jsx(SelectTrigger, { className: "w-[320px]", children: _jsx(SelectValue, { placeholder: "Select an option..." }) }), _jsx(SelectContent, {})] })),
|
|
42
|
+
};
|
|
43
|
+
export const EmptyOptionsWithCustomText = {
|
|
44
|
+
render: (args) => (_jsxs(Select, { ...args, children: [_jsx(SelectTrigger, { className: "w-[320px]", children: _jsx(SelectValue, { placeholder: "Select an option..." }) }), _jsx(SelectContent, { noResultsText: "No matching options available" })] })),
|
|
45
|
+
};
|
|
46
|
+
export const EmptyOptionsWithActionButton = {
|
|
47
|
+
render: (args) => (_jsxs(Select, { ...args, children: [_jsx(SelectTrigger, { className: "w-[320px]", children: _jsx(SelectValue, { placeholder: "Select an option..." }) }), _jsx(SelectContent, { noResultsText: "No results found", noResultsButtonText: "Add new option", onNoResultsButtonClick: () => {
|
|
48
|
+
console.log('Add new option clicked');
|
|
49
|
+
} })] })),
|
|
50
|
+
};
|
|
@@ -12,4 +12,7 @@ export interface SelectTriggerProps extends ComponentProps<typeof ShadCNSelectTr
|
|
|
12
12
|
export interface SelectContentProps extends ComponentProps<typeof ShadCNSelectContent> {
|
|
13
13
|
showAlwaysSearch?: boolean;
|
|
14
14
|
searchPlaceholder?: string;
|
|
15
|
+
noResultsText?: string;
|
|
16
|
+
noResultsButtonText?: string;
|
|
17
|
+
onNoResultsButtonClick?: () => void;
|
|
15
18
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { SelectWrapperProps } from './SelectWrapper.types';
|
|
2
|
-
export declare const SelectWrapper: ({ label, options, value, onChange, size, disabled, error, placeholder, className, }: SelectWrapperProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare const SelectWrapper: ({ label, options, value, onChange, size, disabled, error, placeholder, noResultsText, noResultsButtonText, onNoResultsButtonClick, className, }: SelectWrapperProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cn } from '../../lib/utils';
|
|
3
3
|
import { Label } from '../Label';
|
|
4
4
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../Select/Select';
|
|
5
|
-
export const SelectWrapper = ({ label, options, value, onChange, size = 'default', disabled = false, error = false, placeholder = '', className = '', }) => {
|
|
5
|
+
export const SelectWrapper = ({ label, options, value, onChange, size = 'default', disabled = false, error = false, placeholder = '', noResultsText = 'No results found', noResultsButtonText, onNoResultsButtonClick, className = '', }) => {
|
|
6
6
|
// Component implementation goes here
|
|
7
|
-
return (_jsxs("div", { className: "flex flex-col gap-1 w-full", children: [label && _jsx(Label, { className: "text-sm font-medium", children: label }), _jsxs(Select, { value: value, onValueChange: onChange, disabled: disabled, children: [_jsx(SelectTrigger, { "aria-invalid": error, className: cn('w-full', error && 'border-(--uikit-error) ring-1 ring-(--uikit-error)', size === 'small' ? 'h-8 text-xs' : 'h-10 text-sm', className), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { children: options.map((option) => (_jsx(SelectItem, { value: option.value, className: cn(size === 'small' ? 'h-8 text-xs' : 'h-10 text-sm'), children: _jsx("span", { children: option.label }) }, option.value))) })] }), error && _jsx("span", { className: "text-(--uikit-error) text-sm", children: "This field is required." })] }));
|
|
7
|
+
return (_jsxs("div", { className: "flex flex-col gap-1 w-full", children: [label && _jsx(Label, { className: "text-sm font-medium", children: label }), _jsxs(Select, { value: value, onValueChange: onChange, disabled: disabled, children: [_jsx(SelectTrigger, { "aria-invalid": error, className: cn('w-full', error && 'border-(--uikit-error) ring-1 ring-(--uikit-error)', size === 'small' ? 'h-8 text-xs' : 'h-10 text-sm', className), children: _jsx(SelectValue, { placeholder: placeholder }) }), _jsx(SelectContent, { noResultsText: noResultsText, noResultsButtonText: noResultsButtonText, onNoResultsButtonClick: onNoResultsButtonClick, children: options.map((option) => (_jsx(SelectItem, { value: option.value, className: cn(size === 'small' ? 'h-8 text-xs' : 'h-10 text-sm'), children: _jsx("span", { children: option.label }) }, option.value))) })] }), error && _jsx("span", { className: "text-(--uikit-error) text-sm", children: "This field is required." })] }));
|
|
8
8
|
};
|
|
@@ -6,7 +6,10 @@ type Story = StoryObj<SelectWrapperProps>;
|
|
|
6
6
|
export declare const Default: Story;
|
|
7
7
|
export declare const WithValue: Story;
|
|
8
8
|
export declare const Disabled: Story;
|
|
9
|
-
export declare const
|
|
9
|
+
export declare const WithError: Story;
|
|
10
10
|
export declare const WithLabel: Story;
|
|
11
11
|
export declare const WithPlaceholder: Story;
|
|
12
12
|
export declare const WithSize: Story;
|
|
13
|
+
export declare const EmptyOptions: Story;
|
|
14
|
+
export declare const EmptyOptionsWithCustomText: Story;
|
|
15
|
+
export declare const EmptyOptionsWithActionButton: Story;
|
|
@@ -18,66 +18,76 @@ const meta = {
|
|
|
18
18
|
disabled: false,
|
|
19
19
|
error: false,
|
|
20
20
|
className: '',
|
|
21
|
+
noResultsText: 'No results found',
|
|
22
|
+
noResultsButtonText: undefined,
|
|
23
|
+
onNoResultsButtonClick: undefined,
|
|
21
24
|
},
|
|
22
25
|
};
|
|
23
26
|
export default meta;
|
|
27
|
+
const ControlledSelectWrapper = (args) => {
|
|
28
|
+
const [value, setValue] = React.useState(args.value);
|
|
29
|
+
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
30
|
+
};
|
|
24
31
|
export const Default = {
|
|
25
|
-
render: (args) => {
|
|
26
|
-
const [value, setValue] = React.useState(args.value);
|
|
27
|
-
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
28
|
-
},
|
|
32
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
29
33
|
};
|
|
30
34
|
export const WithValue = {
|
|
31
35
|
args: {
|
|
32
36
|
value: 'option2',
|
|
33
37
|
},
|
|
34
|
-
render: (args) => {
|
|
35
|
-
const [value, setValue] = React.useState(args.value);
|
|
36
|
-
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
37
|
-
},
|
|
38
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
38
39
|
};
|
|
39
40
|
export const Disabled = {
|
|
40
41
|
args: {
|
|
41
42
|
disabled: true,
|
|
42
43
|
},
|
|
43
|
-
render: (args) => {
|
|
44
|
-
const [value, setValue] = React.useState(args.value);
|
|
45
|
-
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
46
|
-
},
|
|
44
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
47
45
|
};
|
|
48
|
-
export const
|
|
46
|
+
export const WithError = {
|
|
49
47
|
args: {
|
|
50
48
|
error: true,
|
|
51
49
|
},
|
|
52
|
-
render: (args) => {
|
|
53
|
-
const [value, setValue] = React.useState(args.value);
|
|
54
|
-
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
55
|
-
},
|
|
50
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
56
51
|
};
|
|
57
52
|
export const WithLabel = {
|
|
58
53
|
args: {
|
|
59
54
|
label: 'Select an option',
|
|
60
55
|
},
|
|
61
|
-
render: (args) => {
|
|
62
|
-
const [value, setValue] = React.useState(args.value);
|
|
63
|
-
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
64
|
-
},
|
|
56
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
65
57
|
};
|
|
66
58
|
export const WithPlaceholder = {
|
|
67
59
|
args: {
|
|
68
60
|
placeholder: 'Select an option...',
|
|
69
61
|
},
|
|
70
|
-
render: (args) => {
|
|
71
|
-
const [value, setValue] = React.useState(args.value);
|
|
72
|
-
return _jsx(SelectWrapper, { ...args, value: value, onChange: setValue });
|
|
73
|
-
},
|
|
62
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
74
63
|
};
|
|
75
64
|
export const WithSize = {
|
|
76
65
|
args: {
|
|
77
66
|
size: 'small',
|
|
78
67
|
},
|
|
79
|
-
render: (args) => {
|
|
80
|
-
|
|
81
|
-
|
|
68
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
69
|
+
};
|
|
70
|
+
export const EmptyOptions = {
|
|
71
|
+
args: {
|
|
72
|
+
options: [],
|
|
73
|
+
},
|
|
74
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
75
|
+
};
|
|
76
|
+
export const EmptyOptionsWithCustomText = {
|
|
77
|
+
args: {
|
|
78
|
+
options: [],
|
|
79
|
+
noResultsText: 'No matching options available',
|
|
80
|
+
},
|
|
81
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
82
|
+
};
|
|
83
|
+
export const EmptyOptionsWithActionButton = {
|
|
84
|
+
args: {
|
|
85
|
+
options: [],
|
|
86
|
+
noResultsText: 'No results found',
|
|
87
|
+
noResultsButtonText: 'Add new option',
|
|
88
|
+
onNoResultsButtonClick: () => {
|
|
89
|
+
console.log('Add new option clicked');
|
|
90
|
+
},
|
|
82
91
|
},
|
|
92
|
+
render: (args) => _jsx(ControlledSelectWrapper, { ...args }),
|
|
83
93
|
};
|
|
@@ -19,7 +19,7 @@ export function Stepper({ steps, currentStep, onStepChange, contentProps, classN
|
|
|
19
19
|
onStepChange === null || onStepChange === void 0 ? void 0 : onStepChange(step.id);
|
|
20
20
|
}, children: [_jsx("div", { className: cn('w-9 h-9 rounded-full flex items-center justify-center', isPastStep && 'bg-(--uikit-success)/10 text-(--uikit-success)', isActive &&
|
|
21
21
|
!isPastStep &&
|
|
22
|
-
'bg-(--uikit-
|
|
22
|
+
'bg-(--uikit-secondary)/10 text-[var(--uikit-secondary)]', !isPastStep && !isActive && 'bg-muted/50 text-muted-foreground'), children: isPastStep ? _jsx(Check, { size: 16, strokeWidth: 3 }) : ((_a = step.icon) !== null && _a !== void 0 ? _a : index + 1) }), _jsx("span", { className: cn('text-sm font-normal not-italic', isPastStep && 'text-(--uikit-success)', isActive && 'text-[var(--uikit-secondary)]', !isPastStep && !isActive && 'text-muted-foreground'), children: step.label })] }), index < steps.length - 1 && (_jsx("div", { className: cn(isVertical ? 'w-[3px] h-14 my-2' : 'h-[3px] flex-1 mx-3 mt-4', isPastStep ? 'bg-(--uikit-success)' : 'bg-muted') }))] }, step.id));
|
|
23
23
|
}) })), _jsx("div", { className: "flex-1", children: (() => {
|
|
24
24
|
if (React.isValidElement(currentContent)) {
|
|
25
25
|
return currentContent;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BaseTable } from './BaseTable';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BaseTable } from './BaseTable';
|
|
@@ -56,7 +56,11 @@ export const EmptyState = {
|
|
|
56
56
|
render: () => (_jsx(StandardTable, { id: "empty-table", data: [], columns: columns, customNoRows: { message: 'No users found' }, paginationMode: "client" })),
|
|
57
57
|
};
|
|
58
58
|
export const EmptyStateWithIcon = {
|
|
59
|
-
render: () => (_jsx(StandardTable, { id: "empty-table-with-icon", data: [], columns: columns, paginationMode: "none", customNoRows: {
|
|
59
|
+
render: () => (_jsx(StandardTable, { id: "empty-table-with-icon", data: [], columns: columns, paginationMode: "none", customNoRows: {
|
|
60
|
+
message: 'No transactions',
|
|
61
|
+
icon: _jsx(MdReceiptLong, { size: 56 }),
|
|
62
|
+
messageClassName: 'text-xl font-bold',
|
|
63
|
+
} })),
|
|
60
64
|
};
|
|
61
65
|
export const Loading = {
|
|
62
66
|
render: () => _jsx(StandardTable, { id: "loading-table", data: [], columns: columns, isLoading: true, paginationMode: "client" }),
|
|
@@ -59,7 +59,7 @@ export function Table(props) {
|
|
|
59
59
|
const paddingBottom = Math.max(totalSize - end, 0);
|
|
60
60
|
const tableRows = table.getRowModel().rows;
|
|
61
61
|
const isTrulyEmpty = tableRows.length === 0 && !props.isLoading && !hasNextPage;
|
|
62
|
-
return (_jsxs("div", { ref: parentScrollRef, style: { height }, className: "relative w-full overflow-auto", children: [props.isLoading &&
|
|
62
|
+
return (_jsxs("div", { ref: parentScrollRef, style: { height }, className: "relative w-full overflow-auto", children: [props.isLoading && _jsx("div", { className: "mui-loader mt-4" }), _jsx("div", { className: "rounded-t-(--uikit-radius) overflow-y-hidden overflow-x-auto", children: _jsxs("table", { className: classNames('min-w-full text-sm table-fixed bg-[#ffffff]', props.className), role: "table", id: props.id, children: [showHeader && (_jsx("thead", { className: "bg-(--uikit-tertiary)", children: table.getHeaderGroups().map((headerGroup) => (_jsxs("tr", { className: "p-4", children: [headerGroup.headers.map((header) => {
|
|
63
63
|
return (_jsx("th", { scope: "col", style: { width: header.getSize() }, className: classNames('select-none text-[14px] font-bold ', header.id === 'select' ? 'p-0' : ' p-4 text-left cursor-pointer'), onClick: !(infiniteScroll === null || infiniteScroll === void 0 ? void 0 : infiniteScroll.enabled) && header.column.getCanSort()
|
|
64
64
|
? header.column.getToggleSortingHandler()
|
|
65
65
|
: undefined, children: _jsxs("div", { className: classNames('w-full h-full', header.id === 'select'
|
|
@@ -2,20 +2,25 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { cn } from '../../lib/utils';
|
|
4
4
|
import { useUIKitTheme } from '../../context/useUIKitTheme';
|
|
5
|
-
export const Textarea = React.forwardRef(({ label, error, helperText, className, maxLength, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, ...props }, ref) => {
|
|
6
|
-
var _a
|
|
5
|
+
export const Textarea = React.forwardRef(({ label, error, helperText, className, value, defaultValue, onChange, maxLength, 'aria-describedby': ariaDescribedBy, 'aria-invalid': ariaInvalid, ...props }, ref) => {
|
|
6
|
+
var _a;
|
|
7
7
|
const theme = useUIKitTheme();
|
|
8
8
|
const generatedId = React.useId();
|
|
9
9
|
const textareaId = (_a = props.id) !== null && _a !== void 0 ? _a : generatedId;
|
|
10
10
|
const isInvalid = Boolean(error) || (ariaInvalid !== undefined && ariaInvalid !== false && ariaInvalid !== 'false');
|
|
11
|
-
const isControlled =
|
|
12
|
-
const
|
|
13
|
-
const currentLength =
|
|
14
|
-
const showLengthIndicator =
|
|
11
|
+
const isControlled = value !== undefined;
|
|
12
|
+
const [uncontrolledLength, setUncontrolledLength] = React.useState(() => String(defaultValue !== null && defaultValue !== void 0 ? defaultValue : '').length);
|
|
13
|
+
const currentLength = isControlled ? String(value !== null && value !== void 0 ? value : '').length : uncontrolledLength;
|
|
14
|
+
const showLengthIndicator = typeof maxLength === 'number' && !error;
|
|
15
15
|
const showHelperRow = !error && (helperText || showLengthIndicator);
|
|
16
16
|
const errorId = error ? `${textareaId}-error` : undefined;
|
|
17
17
|
const helperId = showHelperRow ? `${textareaId}-helper` : undefined;
|
|
18
18
|
const describedBy = [ariaDescribedBy, errorId, helperId].filter(Boolean).join(' ') || undefined;
|
|
19
|
-
return (_jsxs("div", { className: cn(className), children: [label && (_jsx("label", { htmlFor: textareaId, className: "text-
|
|
19
|
+
return (_jsxs("div", { className: cn(className), children: [label && (_jsx("label", { htmlFor: textareaId, className: "text-xs font-normal text-muted-foreground", children: label })), _jsx("textarea", { id: textareaId, "aria-invalid": isInvalid, "aria-describedby": describedBy, value: value, defaultValue: defaultValue, onChange: (event) => {
|
|
20
|
+
if (!isControlled) {
|
|
21
|
+
setUncontrolledLength(event.target.value.length);
|
|
22
|
+
}
|
|
23
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(event);
|
|
24
|
+
}, maxLength: maxLength, style: { fontFamily: theme.fontFamily }, className: cn('flex min-h-[80px] w-full text-(--uikit-textSecondary) rounded-(--uikit-radius) border border-(--uikit-inputEnabledBorder) bg-background hover:border-(--uikit-inputHoverBorder) px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 md:text-sm transition focus-visible:outline-none focus-visible:border-(--uikit-primary) focus-visible:ring-2 focus-visible:ring-(--uikit-primary) aria-invalid:border-(--uikit-error) aria-invalid:focus-visible:ring-(--uikit-error)'), ref: ref, ...props }), error && (_jsx("p", { id: errorId, className: "text-sm font-medium text-error", children: error })), showHelperRow && (_jsxs("div", { id: helperId, className: "mt-1 flex items-center justify-between gap-2", children: [helperText ? (_jsx("p", { className: "text-xs font-normal text-muted-foreground", children: helperText })) : (_jsx("span", {})), showLengthIndicator && (_jsxs("span", { className: "text-xs font-normal text-muted-foreground", children: [currentLength, "/", maxLength] }))] }))] }));
|
|
20
25
|
});
|
|
21
26
|
Textarea.displayName = 'Textarea';
|
|
@@ -5,6 +5,7 @@ export default meta;
|
|
|
5
5
|
type Story = StoryObj<TextareaProps>;
|
|
6
6
|
export declare const Default: Story;
|
|
7
7
|
export declare const WithHelperText: Story;
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const WithMaxLengthIndicator: Story;
|
|
9
|
+
export declare const WithMaxLengthIndicatorControlled: Story;
|
|
9
10
|
export declare const WithError: Story;
|
|
10
11
|
export declare const Disabled: Story;
|
|
@@ -21,12 +21,16 @@ export const WithHelperText = {
|
|
|
21
21
|
helperText: 'This is a helper text',
|
|
22
22
|
},
|
|
23
23
|
};
|
|
24
|
-
export const
|
|
25
|
-
render: (args) => _jsx(ControlledLengthIndicator, { ...args }),
|
|
24
|
+
export const WithMaxLengthIndicator = {
|
|
26
25
|
args: {
|
|
26
|
+
maxLength: 50,
|
|
27
27
|
helperText: 'This is a helper text',
|
|
28
|
+
placeholder: 'Type here to see counter...',
|
|
28
29
|
},
|
|
29
30
|
};
|
|
31
|
+
export const WithMaxLengthIndicatorControlled = {
|
|
32
|
+
render: (args) => _jsx(ControlledLengthIndicator, { ...args }),
|
|
33
|
+
};
|
|
30
34
|
export const WithError = {
|
|
31
35
|
args: {
|
|
32
36
|
error: 'Something went wrong',
|
|
@@ -12,7 +12,7 @@ export const Default = {
|
|
|
12
12
|
render: () => (_jsx(TooltipStoryWrapper, { content: "Simple tooltip", children: _jsx(Button, { children: "Hover me" }) })),
|
|
13
13
|
};
|
|
14
14
|
export const LongTextResponsive = {
|
|
15
|
-
render: () => (_jsx(TooltipStoryWrapper, { content: "This tooltip contains a longer message that wraps gracefully across multiple lines. On smaller\
|
|
15
|
+
render: () => (_jsx(TooltipStoryWrapper, { content: "This tooltip contains a longer message that wraps gracefully across multiple lines. On smaller\n\t\tscreens, it narrows and stacks vertically so it's easier to read\u2014especially useful for\n\t\taccessibility, mobile devices, or multi-language support.", side: "top", children: _jsx(Button, { children: "Hover me" }) })),
|
|
16
16
|
};
|
|
17
17
|
export const OnDifferentSides = {
|
|
18
18
|
render: () => (_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsx(TooltipStoryWrapper, { content: "Top tooltip", side: "top", children: _jsx(Button, { children: "Top" }) }), _jsx(TooltipStoryWrapper, { content: "Bottom tooltip", side: "bottom", children: _jsx(Button, { children: "Bottom" }) }), _jsx(TooltipStoryWrapper, { content: "Left tooltip", side: "left", children: _jsx(Button, { children: "Left" }) }), _jsx(TooltipStoryWrapper, { content: "Right tooltip", side: "right", children: _jsx(Button, { children: "Right" }) })] })),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eml-payments/ui-kit",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ARLO UIKit",
|
|
6
6
|
"homepage": "https://github.com/EML-Payments/arlo.npm.uikit#readme",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"format:fix": "prettier --write \"**/*.{js,jsx,ts,tsx,json,htm,html,yml,yaml}\"",
|
|
44
44
|
"task:prettier": "prettier \"./src/**/*.{ts,tsx}\" --check --log-level error",
|
|
45
45
|
"task:prettier:fix": "prettier --write \"./src/**/*.{ts,tsx}\" --check --log-level error",
|
|
46
|
-
"task:eslint": "eslint \"
|
|
47
|
-
"task:eslint:fix": "eslint \"
|
|
46
|
+
"task:eslint": "eslint \"src/**/*.{ts,tsx}\" --cache",
|
|
47
|
+
"task:eslint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix --cache",
|
|
48
48
|
"task:typecheck": "tsc",
|
|
49
49
|
"formatx": "npm run task:prettier:fix && npm run task:eslint:fix && npm run task:typecheck"
|
|
50
50
|
},
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"@storybook/react-vite": "^10.0.6",
|
|
88
88
|
"@tailwindcss/cli": "^4.1.12",
|
|
89
89
|
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
|
90
|
+
"baseline-browser-mapping": "^2.10.0",
|
|
90
91
|
"eslint": "^9.31.0",
|
|
91
92
|
"eslint-config-prettier": "^10.1.5",
|
|
92
93
|
"eslint-plugin-import": "^2.32.0",
|
|
@@ -113,6 +114,10 @@
|
|
|
113
114
|
]
|
|
114
115
|
},
|
|
115
116
|
"lint-staged": {
|
|
116
|
-
"*.{js,ts,tsx}":
|
|
117
|
+
"*.{js,ts,tsx}": [
|
|
118
|
+
"prettier --write",
|
|
119
|
+
"eslint --fix"
|
|
120
|
+
],
|
|
121
|
+
"*.{json,md,css}": "prettier --write"
|
|
117
122
|
}
|
|
118
123
|
}
|