@scripso-homepad/ui 0.4.2 → 0.4.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/{chunk-WDSMJWKC.js → chunk-66WR57JJ.js} +35 -3
- package/dist/chunk-66WR57JJ.js.map +1 -0
- package/dist/index.cjs +2260 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -1
- package/dist/index.d.ts +133 -1
- package/dist/index.js +1960 -97
- package/dist/index.js.map +1 -1
- package/dist/web/index.cjs +30650 -1009
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.css +78 -0
- package/dist/web/index.css.map +1 -0
- package/dist/web/index.d.cts +267 -6
- package/dist/web/index.d.ts +267 -6
- package/dist/web/index.js +30179 -601
- package/dist/web/index.js.map +1 -1
- package/package.json +5 -4
- package/src/components/Calendar.stories.tsx +337 -0
- package/src/components/Calendar.tsx +441 -0
- package/src/components/DatePicker.stories.tsx +219 -0
- package/src/components/DatePicker.tsx +366 -0
- package/src/components/Select.stories.tsx +134 -0
- package/src/components/Select.tsx +462 -0
- package/src/css.d.ts +1 -0
- package/src/icons/CalendarIcon.tsx +28 -0
- package/src/icons/CalendarIcon.web.tsx +34 -0
- package/src/icons/ChevronDownIcon.tsx +11 -4
- package/src/icons/ChevronDownIcon.web.tsx +6 -4
- package/src/icons/ChevronNavIcons.tsx +44 -0
- package/src/icons/ChevronNavIcons.web.tsx +57 -0
- package/src/icons/calendarIconPaths.ts +2 -0
- package/src/icons/chevronDownPath.ts +1 -0
- package/src/icons/chevronNavPaths.ts +2 -0
- package/src/index.ts +41 -0
- package/src/theme/calendar.ts +357 -0
- package/src/theme/select.ts +249 -0
- package/src/utils/calendarDays.ts +273 -0
- package/src/utils/calendarLocaleContext.tsx +95 -0
- package/src/utils/calendarLocalization.ts +187 -0
- package/src/utils/countryDropdownScrollStyles.ts +53 -13
- package/src/utils/formatMultiSelectDisplay.ts +56 -0
- package/src/web/components/Badge.stories.tsx +43 -0
- package/src/web/components/Badge.tsx +35 -0
- package/src/web/components/Pagination.stories.tsx +78 -0
- package/src/web/components/Pagination.tsx +153 -0
- package/src/web/components/StatusBadge.tsx +20 -0
- package/src/web/components/Table.stories.tsx +415 -0
- package/src/web/components/Table.tsx +3 -0
- package/src/web/components/TableActionButton.tsx +67 -0
- package/src/web/components/TableActionsMenu.tsx +208 -0
- package/src/web/components/TableIconText.tsx +23 -0
- package/src/web/components/TableRowStatusActions.tsx +33 -0
- package/src/web/components/TableStatusActionsCell.tsx +29 -0
- package/src/web/components/pagination-utils.ts +39 -0
- package/src/web/components/table/DataTable.tsx +123 -0
- package/src/web/components/table/TablePrimitives.tsx +214 -0
- package/src/web/components/table/TableScrollArea.tsx +51 -0
- package/src/web/components/table/constants.ts +59 -0
- package/src/web/components/table/data-table-class-names.ts +26 -0
- package/src/web/components/table/index.ts +44 -0
- package/src/web/components/table/table-row-context.tsx +19 -0
- package/src/web/components/table/use-horizontal-scroll-state.ts +72 -0
- package/src/web/components/table-scroll.css +81 -0
- package/src/web/hooks/useOnClickOutside.ts +20 -5
- package/src/web/icons/TableMenuActivateIcon.tsx +27 -0
- package/src/web/icons/TableMenuCheckIcon.tsx +27 -0
- package/src/web/icons/TableMenuCloseIcon.tsx +27 -0
- package/src/web/icons/TableMenuEditIcon.tsx +27 -0
- package/src/web/icons/TableMenuInfoIcon.tsx +27 -0
- package/src/web/icons/TableMenuRefreshIcon.tsx +27 -0
- package/src/web/icons/TableMenuSuspendIcon.tsx +34 -0
- package/src/web/icons/TableMenuTrashIcon.tsx +27 -0
- package/src/web/icons/TableMoreIcon.tsx +41 -0
- package/src/web/icons/TableSortIcon.tsx +37 -0
- package/src/web/index.ts +74 -0
- package/src/web/layout/AppHeader.stories.tsx +2 -0
- package/src/web/layout/AppHeader.tsx +18 -14
- package/src/web/layout/DashboardLayout.stories.tsx +1 -0
- package/src/web/layout/DashboardLayout.tsx +4 -4
- package/src/web/layout/story-helpers.tsx +10 -2
- package/dist/chunk-WDSMJWKC.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { fontSize } from "../theme/tokens";
|
|
2
|
+
|
|
3
|
+
const MULTI_SELECT_SUFFIX = " ... + ";
|
|
4
|
+
const MULTI_SELECT_SUFFIX_UNIT = " items";
|
|
5
|
+
|
|
6
|
+
/** Rough width estimate for Noto Sans Armenian at 14px (single-select value text). */
|
|
7
|
+
function estimateTextWidth(text: string, size = fontSize.md): number {
|
|
8
|
+
return text.length * size * 0.58;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function formatMultiSelectDisplay(
|
|
12
|
+
labels: string[],
|
|
13
|
+
maxWidth: number,
|
|
14
|
+
textSize = fontSize.md,
|
|
15
|
+
): string {
|
|
16
|
+
if (labels.length === 0) {
|
|
17
|
+
return "";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (labels.length === 1) {
|
|
21
|
+
return labels[0];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const fits = (text: string) => estimateTextWidth(text, textSize) <= maxWidth;
|
|
25
|
+
const fullText = labels.join(", ");
|
|
26
|
+
|
|
27
|
+
if (fits(fullText)) {
|
|
28
|
+
return fullText;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (let visibleCount = labels.length - 1; visibleCount >= 1; visibleCount -= 1) {
|
|
32
|
+
const hiddenCount = labels.length - visibleCount;
|
|
33
|
+
const truncatedText = `${labels.slice(0, visibleCount).join(", ")}${MULTI_SELECT_SUFFIX}${hiddenCount}${MULTI_SELECT_SUFFIX_UNIT}`;
|
|
34
|
+
|
|
35
|
+
if (fits(truncatedText)) {
|
|
36
|
+
return truncatedText;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return `... + ${labels.length}${MULTI_SELECT_SUFFIX_UNIT}`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function resolveSelectedLabels(
|
|
44
|
+
options: { value: string; label: string }[],
|
|
45
|
+
selectedValues: string[],
|
|
46
|
+
): string[] {
|
|
47
|
+
if (selectedValues.length === 0) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const labelByValue = new Map(options.map((option) => [option.value, option.label]));
|
|
52
|
+
|
|
53
|
+
return selectedValues
|
|
54
|
+
.map((value) => labelByValue.get(value))
|
|
55
|
+
.filter((label): label is string => Boolean(label));
|
|
56
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
|
|
3
|
+
import { WebLayoutCanvas } from '../layout/story-helpers';
|
|
4
|
+
import { Badge } from './Badge';
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: 'Web Components/Badge',
|
|
8
|
+
component: Badge,
|
|
9
|
+
parameters: {
|
|
10
|
+
layout: 'padded',
|
|
11
|
+
webLayout: true,
|
|
12
|
+
},
|
|
13
|
+
} satisfies Meta<typeof Badge>;
|
|
14
|
+
|
|
15
|
+
export default meta;
|
|
16
|
+
type Story = StoryObj<typeof meta>;
|
|
17
|
+
|
|
18
|
+
export const AllVariants: Story = {
|
|
19
|
+
args: {
|
|
20
|
+
children: 'Badge',
|
|
21
|
+
},
|
|
22
|
+
render: () => (
|
|
23
|
+
<WebLayoutCanvas>
|
|
24
|
+
<div className="flex w-full max-w-sm flex-col gap-0 divide-y divide-storm-gray-50 rounded-xl border border-storm-gray-50 bg-white">
|
|
25
|
+
<div className="px-4 py-4">
|
|
26
|
+
<Badge variant="info">Սպասում է հաստատման</Badge>
|
|
27
|
+
</div>
|
|
28
|
+
<div className="px-4 py-4">
|
|
29
|
+
<Badge variant="danger">Մերժված</Badge>
|
|
30
|
+
</div>
|
|
31
|
+
<div className="px-4 py-4">
|
|
32
|
+
<Badge variant="warning">Սպասում է վճարման</Badge>
|
|
33
|
+
</div>
|
|
34
|
+
<div className="px-4 py-4">
|
|
35
|
+
<Badge variant="success">Հաստատված</Badge>
|
|
36
|
+
</div>
|
|
37
|
+
<div className="px-4 py-4">
|
|
38
|
+
<Badge variant="neutral">Կասեցված</Badge>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</WebLayoutCanvas>
|
|
42
|
+
),
|
|
43
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../utils/cn';
|
|
4
|
+
|
|
5
|
+
export type BadgeVariant = 'info' | 'danger' | 'warning' | 'success' | 'neutral';
|
|
6
|
+
|
|
7
|
+
const badgeVariantClasses: Record<BadgeVariant, string> = {
|
|
8
|
+
info: 'bg-[#0F52C71A] text-[#0F52C7]',
|
|
9
|
+
danger: 'bg-[#FFF5F6] text-[#AE0011]',
|
|
10
|
+
warning: 'bg-[#F2A8001A] text-[#F2A800]',
|
|
11
|
+
success: 'bg-[#F0FBF5] text-[#279D54]',
|
|
12
|
+
neutral: 'bg-[#ECEEF0] text-[#455765]',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type BadgeProps = {
|
|
16
|
+
children: ReactNode;
|
|
17
|
+
variant?: BadgeVariant;
|
|
18
|
+
icon?: ReactNode;
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export function Badge({ children, variant = 'neutral', icon, className }: BadgeProps) {
|
|
23
|
+
return (
|
|
24
|
+
<span
|
|
25
|
+
className={cn(
|
|
26
|
+
'inline-flex items-center gap-2 rounded-[28px] px-3 py-1.5 font-sans text-xs font-semibold leading-none whitespace-nowrap',
|
|
27
|
+
badgeVariantClasses[variant],
|
|
28
|
+
className,
|
|
29
|
+
)}
|
|
30
|
+
>
|
|
31
|
+
{icon ? <span className="inline-flex shrink-0 items-center justify-center">{icon}</span> : null}
|
|
32
|
+
{children}
|
|
33
|
+
</span>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { fn } from '@storybook/test';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
import { WebLayoutCanvas } from '../layout/story-helpers';
|
|
6
|
+
import { Pagination, type PaginationProps } from './Pagination';
|
|
7
|
+
|
|
8
|
+
const armenianLabels: PaginationProps['labels'] = {
|
|
9
|
+
previousPage: 'Նախորդ էջ',
|
|
10
|
+
nextPage: 'Հաջորդ էջ',
|
|
11
|
+
page: (pageNumber) => `Էջ ${pageNumber}`,
|
|
12
|
+
showing: ({ start, end, total }) => `Ցուցադրված է ${start}-${end}-ը ${total}-ից`,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: 'Web Components/Pagination',
|
|
17
|
+
component: Pagination,
|
|
18
|
+
parameters: {
|
|
19
|
+
layout: 'fullscreen',
|
|
20
|
+
webLayout: true,
|
|
21
|
+
},
|
|
22
|
+
args: {
|
|
23
|
+
page: 1,
|
|
24
|
+
pageSize: 4,
|
|
25
|
+
total: 4,
|
|
26
|
+
onPageChange: fn(),
|
|
27
|
+
labels: armenianLabels,
|
|
28
|
+
},
|
|
29
|
+
} satisfies Meta<typeof Pagination>;
|
|
30
|
+
|
|
31
|
+
export default meta;
|
|
32
|
+
type Story = StoryObj<typeof meta>;
|
|
33
|
+
|
|
34
|
+
export const Default: Story = {
|
|
35
|
+
render: (args) => (
|
|
36
|
+
<WebLayoutCanvas>
|
|
37
|
+
<div className="rounded-2xl border border-storm-gray-50 bg-white p-5">
|
|
38
|
+
<Pagination {...args} />
|
|
39
|
+
</div>
|
|
40
|
+
</WebLayoutCanvas>
|
|
41
|
+
),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function InteractiveStory(args: PaginationProps) {
|
|
45
|
+
const [page, setPage] = useState(args.page);
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<WebLayoutCanvas>
|
|
49
|
+
<div className="rounded-2xl border border-storm-gray-50 bg-white p-5">
|
|
50
|
+
<Pagination
|
|
51
|
+
{...args}
|
|
52
|
+
page={page}
|
|
53
|
+
onPageChange={setPage}
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
</WebLayoutCanvas>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const Interactive: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
page: 1,
|
|
63
|
+
pageSize: 4,
|
|
64
|
+
total: 12,
|
|
65
|
+
labels: armenianLabels,
|
|
66
|
+
},
|
|
67
|
+
render: (args) => <InteractiveStory {...args} />,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const SinglePage: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
page: 1,
|
|
73
|
+
pageSize: 4,
|
|
74
|
+
total: 4,
|
|
75
|
+
labels: armenianLabels,
|
|
76
|
+
},
|
|
77
|
+
render: (args) => <InteractiveStory {...args} />,
|
|
78
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
2
|
+
|
|
3
|
+
import { cn } from '../utils/cn';
|
|
4
|
+
import { useDataTableClassNames } from './table/data-table-class-names';
|
|
5
|
+
import {
|
|
6
|
+
getPaginationRange,
|
|
7
|
+
getTotalPages,
|
|
8
|
+
getVisiblePageNumbers,
|
|
9
|
+
} from './pagination-utils';
|
|
10
|
+
|
|
11
|
+
export type PaginationLabels = {
|
|
12
|
+
previousPage: string;
|
|
13
|
+
nextPage: string;
|
|
14
|
+
page: (pageNumber: number) => string;
|
|
15
|
+
showing: (range: { start: number; end: number; total: number }) => string;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const defaultLabels: PaginationLabels = {
|
|
19
|
+
previousPage: 'Previous page',
|
|
20
|
+
nextPage: 'Next page',
|
|
21
|
+
page: (pageNumber) => `Page ${pageNumber}`,
|
|
22
|
+
showing: ({ start, end, total }) => `Showing ${start}-${end} of ${total}`,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const paginationControlClassName =
|
|
26
|
+
'inline-flex size-7 items-center justify-center rounded-lg border border-storm-gray-50 p-1 text-xs font-medium leading-none text-storm-gray-500 transition-colors';
|
|
27
|
+
|
|
28
|
+
const paginationHoverClassName = 'cursor-pointer hover:bg-storm-gray-0';
|
|
29
|
+
|
|
30
|
+
export type PaginationVariant = 'standalone' | 'attached';
|
|
31
|
+
|
|
32
|
+
export type PaginationProps = {
|
|
33
|
+
page: number;
|
|
34
|
+
pageSize: number;
|
|
35
|
+
total: number;
|
|
36
|
+
onPageChange: (page: number) => void;
|
|
37
|
+
labels?: Partial<PaginationLabels>;
|
|
38
|
+
maxPageButtons?: number;
|
|
39
|
+
/**
|
|
40
|
+
* `attached` — below a table; no top radius (footer shell handles bottom radius).
|
|
41
|
+
* `standalone` — full `rounded-xl` shell.
|
|
42
|
+
*/
|
|
43
|
+
variant?: PaginationVariant;
|
|
44
|
+
className?: string;
|
|
45
|
+
hideOnSinglePage?: boolean;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export function Pagination({
|
|
49
|
+
page,
|
|
50
|
+
pageSize,
|
|
51
|
+
total,
|
|
52
|
+
onPageChange,
|
|
53
|
+
labels,
|
|
54
|
+
maxPageButtons = 3,
|
|
55
|
+
variant = 'standalone',
|
|
56
|
+
className,
|
|
57
|
+
hideOnSinglePage = false,
|
|
58
|
+
}: PaginationProps) {
|
|
59
|
+
const mergedLabels = { ...defaultLabels, ...labels };
|
|
60
|
+
const dataTableClassNames = useDataTableClassNames();
|
|
61
|
+
const totalPages = getTotalPages(total, pageSize);
|
|
62
|
+
const safePage = Math.min(Math.max(page, 1), totalPages);
|
|
63
|
+
const { start, end } = getPaginationRange(safePage, pageSize, total);
|
|
64
|
+
const visiblePages = getVisiblePageNumbers(safePage, totalPages, maxPageButtons);
|
|
65
|
+
const canGoPrevious = safePage > 1;
|
|
66
|
+
const canGoNext = safePage < totalPages;
|
|
67
|
+
|
|
68
|
+
if (hideOnSinglePage && totalPages <= 1) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div
|
|
74
|
+
className={cn(
|
|
75
|
+
'flex flex-col gap-4 px-4 pb-4 pt-4 font-[family-name:var(--font-sans)] sm:flex-row sm:items-center sm:justify-between',
|
|
76
|
+
variant === 'standalone' && cn('rounded-xl border bg-storm-gray-0', 'border-storm-gray-50'),
|
|
77
|
+
dataTableClassNames.pagination,
|
|
78
|
+
className,
|
|
79
|
+
)}
|
|
80
|
+
>
|
|
81
|
+
<p className="text-xs font-medium leading-none text-storm-gray-200">
|
|
82
|
+
{mergedLabels.showing({ start, end, total })}
|
|
83
|
+
</p>
|
|
84
|
+
|
|
85
|
+
<nav
|
|
86
|
+
aria-label="Pagination"
|
|
87
|
+
className="flex items-center gap-2.5"
|
|
88
|
+
>
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
onClick={() => onPageChange(safePage - 1)}
|
|
92
|
+
disabled={!canGoPrevious}
|
|
93
|
+
aria-label={mergedLabels.previousPage}
|
|
94
|
+
className={cn(
|
|
95
|
+
paginationControlClassName,
|
|
96
|
+
canGoPrevious ? paginationHoverClassName : 'cursor-not-allowed opacity-40',
|
|
97
|
+
)}
|
|
98
|
+
>
|
|
99
|
+
<ChevronLeft
|
|
100
|
+
className="size-3.5 text-storm-gray-500"
|
|
101
|
+
strokeWidth={1.75}
|
|
102
|
+
aria-hidden
|
|
103
|
+
/>
|
|
104
|
+
</button>
|
|
105
|
+
|
|
106
|
+
{visiblePages.map((pageNumber) => {
|
|
107
|
+
const isActive = pageNumber === safePage;
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<button
|
|
111
|
+
key={pageNumber}
|
|
112
|
+
type="button"
|
|
113
|
+
onClick={() => onPageChange(pageNumber)}
|
|
114
|
+
aria-label={mergedLabels.page(pageNumber)}
|
|
115
|
+
aria-current={isActive ? 'page' : undefined}
|
|
116
|
+
className={cn(
|
|
117
|
+
paginationControlClassName,
|
|
118
|
+
isActive
|
|
119
|
+
? 'border-storm-gray-50 font-bold text-storm-gray-900'
|
|
120
|
+
: paginationHoverClassName,
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{pageNumber}
|
|
124
|
+
</button>
|
|
125
|
+
);
|
|
126
|
+
})}
|
|
127
|
+
|
|
128
|
+
<button
|
|
129
|
+
type="button"
|
|
130
|
+
onClick={() => onPageChange(safePage + 1)}
|
|
131
|
+
disabled={!canGoNext}
|
|
132
|
+
aria-label={mergedLabels.nextPage}
|
|
133
|
+
className={cn(
|
|
134
|
+
paginationControlClassName,
|
|
135
|
+
canGoNext ? paginationHoverClassName : 'cursor-not-allowed opacity-40',
|
|
136
|
+
)}
|
|
137
|
+
>
|
|
138
|
+
<ChevronRight
|
|
139
|
+
className="size-3.5 text-storm-gray-500"
|
|
140
|
+
strokeWidth={1.75}
|
|
141
|
+
aria-hidden
|
|
142
|
+
/>
|
|
143
|
+
</button>
|
|
144
|
+
</nav>
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
getPaginationRange,
|
|
151
|
+
getTotalPages,
|
|
152
|
+
getVisiblePageNumbers,
|
|
153
|
+
} from './pagination-utils';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Badge, type BadgeVariant } from './Badge';
|
|
4
|
+
|
|
5
|
+
export type StatusBadgeTone = BadgeVariant;
|
|
6
|
+
|
|
7
|
+
export type StatusBadgeProps = {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
tone?: StatusBadgeTone;
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function StatusBadge({ children, tone = 'neutral', icon, className }: StatusBadgeProps) {
|
|
15
|
+
return (
|
|
16
|
+
<Badge variant={tone} icon={icon} className={className}>
|
|
17
|
+
{children}
|
|
18
|
+
</Badge>
|
|
19
|
+
);
|
|
20
|
+
}
|