periplo-ui 3.15.1 → 3.17.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.
- package/README.md +31 -0
- package/dist/components/Badge/Badge.d.ts +7 -2
- package/dist/components/Badge/Badge.js +48 -18
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/DataTable/DataTable.d.ts +6 -2
- package/dist/components/DataTable/DataTable.js +17 -3
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.d.ts +1 -0
- package/dist/components/DatePicker/DatePicker.js +3 -3
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/InlineMultiSelect/InlineMultiSelect.d.ts +62 -0
- package/dist/components/InlineMultiSelect/InlineMultiSelect.js +173 -0
- package/dist/components/InlineMultiSelect/InlineMultiSelect.js.map +1 -0
- package/dist/components/InlineMultiSelect/index.d.ts +1 -0
- package/dist/components/InlineMultiSelect/index.js +2 -0
- package/dist/components/InlineMultiSelect/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,6 +74,37 @@
|
|
|
74
74
|
|
|
75
75
|
- Refer to documentation for individual component usage and API reference: https://periplo-ui.vercel.app/?path=/docs/getting-started--docs
|
|
76
76
|
|
|
77
|
+
## 🔧 Local Storybook Package Installation
|
|
78
|
+
|
|
79
|
+
You can test this Storybook package locally before publishing by following these steps:
|
|
80
|
+
|
|
81
|
+
### 📦 In the Storybook project:
|
|
82
|
+
|
|
83
|
+
1. **Install dependencies:**
|
|
84
|
+
```bash
|
|
85
|
+
npm install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
2. **Build the project (if needed):**
|
|
89
|
+
```bash
|
|
90
|
+
npm run build
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
3. **Package the project:**
|
|
94
|
+
This will generate a .tgz file in the project root.
|
|
95
|
+
```bash
|
|
96
|
+
npm pack
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 📥 In the target project:
|
|
100
|
+
|
|
101
|
+
1. **Install the package locally** (replace the path with your .tgz file path):
|
|
102
|
+
```bash
|
|
103
|
+
npm install /path/to/your/file/periplo-ui-1.0.0.tgz --force
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
⚠️ Make sure to use the correct absolute or relative path to the .tgz file generated with npm pack.
|
|
107
|
+
|
|
77
108
|
## Available Commands:
|
|
78
109
|
|
|
79
110
|
| Command | Description |
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { VariantProps } from 'class-variance-authority';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
declare const badgeVariants: (props?: ({
|
|
4
|
-
|
|
4
|
+
color?: "neutral" | "primary" | "accent" | "success" | "warning" | "error" | null | undefined;
|
|
5
5
|
size?: "sm" | "md" | "lg" | null | undefined;
|
|
6
6
|
position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | null | undefined;
|
|
7
|
+
numeric?: boolean | null | undefined;
|
|
7
8
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
8
9
|
export interface BadgeProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof badgeVariants> {
|
|
9
10
|
/**
|
|
@@ -15,7 +16,7 @@ export interface BadgeProps extends React.HTMLAttributes<HTMLElement>, VariantPr
|
|
|
15
16
|
* The color variant of the badge
|
|
16
17
|
* @default 'accent'
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
+
color?: 'neutral' | 'accent' | 'primary' | 'success' | 'warning' | 'error';
|
|
19
20
|
/**
|
|
20
21
|
* The size of the badge
|
|
21
22
|
* @default 'md'
|
|
@@ -26,6 +27,10 @@ export interface BadgeProps extends React.HTMLAttributes<HTMLElement>, VariantPr
|
|
|
26
27
|
* @default 'top-right'
|
|
27
28
|
*/
|
|
28
29
|
position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
|
|
30
|
+
/**
|
|
31
|
+
* The number to display in the badge)
|
|
32
|
+
*/
|
|
33
|
+
count?: number;
|
|
29
34
|
}
|
|
30
35
|
/**
|
|
31
36
|
* A status indicator component that can be used to show notifications,
|
|
@@ -9,17 +9,19 @@ const badgeVariants = cva("rounded-full", {
|
|
|
9
9
|
* Controls the color scheme of the badge
|
|
10
10
|
* @default 'accent'
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
color: {
|
|
13
13
|
/** Default gray appearance */
|
|
14
|
-
neutral: "bg-neutral",
|
|
14
|
+
neutral: "bg-neutral text-neutral-foreground",
|
|
15
|
+
/** Accent brand color */
|
|
16
|
+
accent: "bg-accent text-accent-foreground",
|
|
15
17
|
/** Primary brand color */
|
|
16
|
-
|
|
18
|
+
primary: "bg-primary text-primary-foreground",
|
|
17
19
|
/** Indicates successful or positive state */
|
|
18
|
-
success: "bg-success",
|
|
20
|
+
success: "bg-success text-success-foreground ",
|
|
19
21
|
/** Indicates warning or cautionary state */
|
|
20
|
-
warning: "bg-warning",
|
|
22
|
+
warning: "bg-warning text-warning-foreground",
|
|
21
23
|
/** Indicates error or critical state */
|
|
22
|
-
error: "bg-error"
|
|
24
|
+
error: "bg-error text-error-foreground"
|
|
23
25
|
},
|
|
24
26
|
/**
|
|
25
27
|
* Controls the size of the badge
|
|
@@ -46,30 +48,58 @@ const badgeVariants = cva("rounded-full", {
|
|
|
46
48
|
"bottom-right": "right-0 bottom-0",
|
|
47
49
|
/** Places the badge in the bottom-left corner */
|
|
48
50
|
"bottom-left": "left-0 bottom-0"
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* Controls whether the badge displays a number
|
|
54
|
+
* @default false
|
|
55
|
+
*/
|
|
56
|
+
numeric: {
|
|
57
|
+
true: "min-w-[16px] h-4 px-1 flex items-center justify-center text-[10px] font-medium",
|
|
58
|
+
false: ""
|
|
49
59
|
}
|
|
50
60
|
},
|
|
51
61
|
defaultVariants: {
|
|
52
|
-
|
|
62
|
+
color: "primary",
|
|
53
63
|
size: "md",
|
|
54
|
-
position: "top-right"
|
|
64
|
+
position: "top-right",
|
|
65
|
+
numeric: false
|
|
55
66
|
}
|
|
56
67
|
});
|
|
57
68
|
const Badge = React.forwardRef(
|
|
58
|
-
({ className,
|
|
69
|
+
({ className, color = "accent", size = "md", position = "top-right", ping = true, count, ...props }, ref) => {
|
|
59
70
|
const sizeClasses = {
|
|
60
71
|
sm: "h-2 w-2",
|
|
61
72
|
md: "h-3 w-3",
|
|
62
73
|
lg: "h-4 w-4"
|
|
63
74
|
}[size || "md"];
|
|
64
|
-
return /* @__PURE__ */ jsxs(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
return /* @__PURE__ */ jsxs(
|
|
76
|
+
"span",
|
|
77
|
+
{
|
|
78
|
+
className: cn(
|
|
79
|
+
"absolute flex",
|
|
80
|
+
badgeVariants({ color, position, numeric: !!count }),
|
|
81
|
+
!count && sizeClasses,
|
|
82
|
+
className
|
|
83
|
+
),
|
|
84
|
+
ref,
|
|
85
|
+
...props,
|
|
86
|
+
children: [
|
|
87
|
+
ping && /* @__PURE__ */ jsx(
|
|
88
|
+
"span",
|
|
89
|
+
{
|
|
90
|
+
className: cn(badgeVariants({ color }), "absolute inline-flex h-full w-full animate-ping opacity-75")
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
"span",
|
|
95
|
+
{
|
|
96
|
+
className: cn(badgeVariants({ color }), "relative inline-flex h-full w-full items-center justify-center"),
|
|
97
|
+
children: count
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
);
|
|
73
103
|
}
|
|
74
104
|
);
|
|
75
105
|
Badge.displayName = "Badge";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Badge.js","sources":["../../../src/components/Badge/Badge.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\nimport { cn } from '../../lib/utils'\n\nconst badgeVariants = cva('rounded-full', {\n variants: {\n /**\n * Controls the color scheme of the badge\n * @default 'accent'\n */\n
|
|
1
|
+
{"version":3,"file":"Badge.js","sources":["../../../src/components/Badge/Badge.tsx"],"sourcesContent":["import { cva, type VariantProps } from 'class-variance-authority'\nimport * as React from 'react'\nimport { cn } from '../../lib/utils'\n\nconst badgeVariants = cva('rounded-full', {\n variants: {\n /**\n * Controls the color scheme of the badge\n * @default 'accent'\n */\n color: {\n /** Default gray appearance */\n neutral: 'bg-neutral text-neutral-foreground',\n /** Accent brand color */\n accent: 'bg-accent text-accent-foreground',\n /** Primary brand color */\n primary: 'bg-primary text-primary-foreground',\n /** Indicates successful or positive state */\n success: 'bg-success text-success-foreground ',\n /** Indicates warning or cautionary state */\n warning: 'bg-warning text-warning-foreground',\n /** Indicates error or critical state */\n error: 'bg-error text-error-foreground',\n },\n /**\n * Controls the size of the badge\n * @default 'md'\n */\n size: {\n /** Small size - 8px */\n sm: 'h-2 w-2',\n /** Medium size - 12px */\n md: 'h-3 w-3',\n /** Large size - 16px */\n lg: 'h-4 w-4',\n },\n /**\n * Controls the position of the badge relative to its container\n * @default 'top-right'\n */\n position: {\n /** Places the badge in the top-right corner */\n 'top-right': 'right-0 top-0',\n /** Places the badge in the top-left corner */\n 'top-left': 'left-0 top-0',\n /** Places the badge in the bottom-right corner */\n 'bottom-right': 'right-0 bottom-0',\n /** Places the badge in the bottom-left corner */\n 'bottom-left': 'left-0 bottom-0',\n },\n /**\n * Controls whether the badge displays a number\n * @default false\n */\n numeric: {\n true: 'min-w-[16px] h-4 px-1 flex items-center justify-center text-[10px] font-medium',\n false: '',\n },\n },\n defaultVariants: {\n color: 'primary',\n size: 'md',\n position: 'top-right',\n numeric: false,\n },\n})\n\nexport interface BadgeProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof badgeVariants> {\n /**\n * Controls the visibility of the pulsing animation effect\n * @default true\n */\n ping?: boolean\n\n /**\n * The color variant of the badge\n * @default 'accent'\n */\n color?: 'neutral' | 'accent' | 'primary' | 'success' | 'warning' | 'error'\n\n /**\n * The size of the badge\n * @default 'md'\n */\n size?: 'sm' | 'md' | 'lg'\n\n /**\n * The position of the badge relative to its container\n * @default 'top-right'\n */\n position?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'\n\n /**\n * The number to display in the badge)\n */\n count?: number\n}\n\n/**\n * A status indicator component that can be used to show notifications,\n * status, or draw attention to an element.\n *\n * @example\n * ```tsx\n * // Simple notification badge\n * <div className=\"relative\">\n * <Bell className=\"h-6 w-6\" />\n * <Badge intent=\"error\" />\n * </div>\n *\n * // Custom size and position\n * <div className=\"relative\">\n * <Avatar />\n * <Badge\n * intent=\"success\"\n * size=\"sm\"\n * position=\"bottom-right\"\n * ping={false}\n * />\n * </div>\n * ```\n */\nconst Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n ({ className, color = 'accent', size = 'md', position = 'top-right', ping = true, count, ...props }, ref) => {\n const sizeClasses = {\n sm: 'h-2 w-2',\n md: 'h-3 w-3',\n lg: 'h-4 w-4',\n }[size || 'md']\n\n return (\n <span\n className={cn(\n 'absolute flex',\n badgeVariants({ color, position, numeric: !!count }),\n !count && sizeClasses,\n className,\n )}\n ref={ref}\n {...props}\n >\n {ping && (\n <span\n className={cn(badgeVariants({ color }), 'absolute inline-flex h-full w-full animate-ping opacity-75')}\n />\n )}\n <span\n className={cn(badgeVariants({ color }), 'relative inline-flex h-full w-full items-center justify-center')}\n >\n {count}\n </span>\n </span>\n )\n },\n)\n\nBadge.displayName = 'Badge'\n\nexport { Badge, badgeVariants }\n"],"names":[],"mappings":";;;;;AAIM,MAAA,aAAA,GAAgB,IAAI,cAAgB,EAAA;AAAA,EACxC,QAAU,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKR,KAAO,EAAA;AAAA;AAAA,MAEL,OAAS,EAAA,oCAAA;AAAA;AAAA,MAET,MAAQ,EAAA,kCAAA;AAAA;AAAA,MAER,OAAS,EAAA,oCAAA;AAAA;AAAA,MAET,OAAS,EAAA,sCAAA;AAAA;AAAA,MAET,OAAS,EAAA,oCAAA;AAAA;AAAA,MAET,KAAO,EAAA;AAAA,KACT;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,IAAM,EAAA;AAAA;AAAA,MAEJ,EAAI,EAAA,SAAA;AAAA;AAAA,MAEJ,EAAI,EAAA,SAAA;AAAA;AAAA,MAEJ,EAAI,EAAA;AAAA,KACN;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,QAAU,EAAA;AAAA;AAAA,MAER,WAAa,EAAA,eAAA;AAAA;AAAA,MAEb,UAAY,EAAA,cAAA;AAAA;AAAA,MAEZ,cAAgB,EAAA,kBAAA;AAAA;AAAA,MAEhB,aAAe,EAAA;AAAA,KACjB;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,OAAS,EAAA;AAAA,MACP,IAAM,EAAA,gFAAA;AAAA,MACN,KAAO,EAAA;AAAA;AACT,GACF;AAAA,EACA,eAAiB,EAAA;AAAA,IACf,KAAO,EAAA,SAAA;AAAA,IACP,IAAM,EAAA,IAAA;AAAA,IACN,QAAU,EAAA,WAAA;AAAA,IACV,OAAS,EAAA;AAAA;AAEb,CAAC;AAyDD,MAAM,QAAQ,KAAM,CAAA,UAAA;AAAA,EAClB,CAAC,EAAE,SAAW,EAAA,KAAA,GAAQ,UAAU,IAAO,GAAA,IAAA,EAAM,QAAW,GAAA,WAAA,EAAa,OAAO,IAAM,EAAA,KAAA,EAAO,GAAG,KAAA,IAAS,GAAQ,KAAA;AAC3G,IAAA,MAAM,WAAc,GAAA;AAAA,MAClB,EAAI,EAAA,SAAA;AAAA,MACJ,EAAI,EAAA,SAAA;AAAA,MACJ,EAAI,EAAA;AAAA,KACN,CAAE,QAAQ,IAAI,CAAA;AAEd,IACE,uBAAA,IAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,SAAW,EAAA,EAAA;AAAA,UACT,eAAA;AAAA,UACA,aAAA,CAAc,EAAE,KAAO,EAAA,QAAA,EAAU,SAAS,CAAC,CAAC,OAAO,CAAA;AAAA,UACnD,CAAC,KAAS,IAAA,WAAA;AAAA,UACV;AAAA,SACF;AAAA,QACA,GAAA;AAAA,QACC,GAAG,KAAA;AAAA,QAEH,QAAA,EAAA;AAAA,UACC,IAAA,oBAAA,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,WAAW,EAAG,CAAA,aAAA,CAAc,EAAE,KAAM,EAAC,GAAG,4DAA4D;AAAA;AAAA,WACtG;AAAA,0BAEF,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cACC,WAAW,EAAG,CAAA,aAAA,CAAc,EAAE,KAAM,EAAC,GAAG,gEAAgE,CAAA;AAAA,cAEvG,QAAA,EAAA;AAAA;AAAA;AACH;AAAA;AAAA,KACF;AAAA;AAGN;AAEA,KAAA,CAAM,WAAc,GAAA,OAAA;;;;"}
|
|
@@ -54,8 +54,12 @@ export type DataTableProps<TData> = {
|
|
|
54
54
|
readonly primaryFilters?: React.ReactNode;
|
|
55
55
|
/** Secondary filters that appear in the filters dropdown */
|
|
56
56
|
readonly secondaryFilters?: React.ReactNode;
|
|
57
|
+
/** Number of active primary filters */
|
|
58
|
+
readonly activePrimaryFiltersCount?: number;
|
|
59
|
+
/** Number of active secondary filters */
|
|
60
|
+
readonly activeSecondaryFiltersCount?: number;
|
|
57
61
|
/** Text customization for filters */
|
|
58
|
-
readonly
|
|
62
|
+
readonly labels?: {
|
|
59
63
|
/** Text for the column visibility button (default: "Hide columns") */
|
|
60
64
|
columnVisibilityButton?: string;
|
|
61
65
|
/** Text for the filters button when only secondary filters are present (default: "Filters") */
|
|
@@ -115,5 +119,5 @@ type RowIdentifierFn<T> = (row: T) => string;
|
|
|
115
119
|
* <DataTable columns={columns} data={data} />
|
|
116
120
|
* ```
|
|
117
121
|
*/
|
|
118
|
-
export declare function DataTable<TData extends object = any>({ columns: userColumns, data, getRowId, showColumnVisibilityControls, isLoading, pagination, primaryFilters, secondaryFilters,
|
|
122
|
+
export declare function DataTable<TData extends object = any>({ columns: userColumns, data, getRowId, showColumnVisibilityControls, isLoading, pagination, primaryFilters, secondaryFilters, activePrimaryFiltersCount, activeSecondaryFiltersCount, labels, onSelectAll, onSelect, className, tableClassName, }: DataTableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
119
123
|
export {};
|
|
@@ -13,6 +13,7 @@ import { cn } from '../../lib/utils.js';
|
|
|
13
13
|
import { Typography } from '../Typography/Typography.js';
|
|
14
14
|
import { PopoverRoot, PopoverTrigger, PopoverContent } from '../Popover/Popover.js';
|
|
15
15
|
import { useIsMobile } from '../../lib/useMobile.js';
|
|
16
|
+
import { Badge } from '../Badge/Badge.js';
|
|
16
17
|
|
|
17
18
|
function DataTable({
|
|
18
19
|
columns: userColumns,
|
|
@@ -23,7 +24,9 @@ function DataTable({
|
|
|
23
24
|
pagination,
|
|
24
25
|
primaryFilters,
|
|
25
26
|
secondaryFilters,
|
|
26
|
-
|
|
27
|
+
activePrimaryFiltersCount = 0,
|
|
28
|
+
activeSecondaryFiltersCount = 0,
|
|
29
|
+
labels = {
|
|
27
30
|
columnVisibilityButton: "Hide columns",
|
|
28
31
|
filters: "Filters",
|
|
29
32
|
moreFilters: "More filters"
|
|
@@ -158,14 +161,25 @@ function DataTable({
|
|
|
158
161
|
!isMobile && (primaryFilters ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: primaryFilters }) : /* @__PURE__ */ jsx("div", {})),
|
|
159
162
|
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", isMobile ? "w-full justify-end" : ""), children: [
|
|
160
163
|
(isMobile ? primaryFilters || secondaryFilters : secondaryFilters) && /* @__PURE__ */ jsxs(PopoverRoot, { children: [
|
|
161
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */
|
|
164
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "text", size: "sm", StartIcon: FunnelSimple, className: "relative whitespace-nowrap", children: [
|
|
165
|
+
(isMobile && activePrimaryFiltersCount + activeSecondaryFiltersCount > 0 || !isMobile && activeSecondaryFiltersCount > 0) && /* @__PURE__ */ jsx(
|
|
166
|
+
Badge,
|
|
167
|
+
{
|
|
168
|
+
count: isMobile ? activePrimaryFiltersCount + activeSecondaryFiltersCount : activeSecondaryFiltersCount,
|
|
169
|
+
color: "primary",
|
|
170
|
+
ping: false,
|
|
171
|
+
className: "absolute -right-1 -top-1"
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
!primaryFilters || isMobile ? labels.filters : labels.moreFilters
|
|
175
|
+
] }) }),
|
|
162
176
|
/* @__PURE__ */ jsx(PopoverContent, { align: "center", className: "w-fit p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
163
177
|
isMobile && primaryFilters && primaryFilters,
|
|
164
178
|
secondaryFilters
|
|
165
179
|
] }) })
|
|
166
180
|
] }),
|
|
167
181
|
showColumnVisibilityControls && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
168
|
-
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", className: cn("whitespace-nowrap"), StartIcon: TextColumns, children:
|
|
182
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", className: cn("whitespace-nowrap"), StartIcon: TextColumns, children: labels.columnVisibilityButton ?? "Hide columns" }) }),
|
|
169
183
|
/* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
|
|
170
184
|
/* @__PURE__ */ jsx(
|
|
171
185
|
DropdownMenuCheckboxItem,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["'use client'\n\nimport {\n ColumnDef,\n flexRender,\n getCoreRowModel,\n getPaginationRowModel,\n useReactTable,\n VisibilityState,\n} from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from '../Button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../DropdownMenu'\nimport { Skeleton } from '../Skeleton'\nimport { Table as TableComponent, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../Table'\nimport { DataTablePagination } from './DataTablePagination'\nimport { Checkbox } from '../Checkbox'\nimport { TextColumns, FunnelSimple } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\nimport { Typography } from '../Typography'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { useIsMobile } from '@/lib/useMobile'\n\ntype BasePaginationProps = {\n /** Number of rows per page */\n readonly pageSize: number\n /** Whether the pagination is in a loading state */\n readonly isLoading?: boolean\n /** Text customization for pagination */\n readonly labels?: {\n /** Text shown before the page size number (default: \"Showing\") */\n showing?: string\n /** Text shown before the total number (default: \"of\") */\n of?: string\n /** Text shown after the total number (default: \"results\") */\n results?: string\n /** Aria label for previous page button (default: \"Previous page\") */\n previousPage?: string\n /** Aria label for next page button (default: \"Next page\") */\n nextPage?: string\n /** Aria label for page number (default: \"Page {number}\") */\n pageLabel?: string\n }\n}\n\ntype BackendPaginationProps = BasePaginationProps & {\n /** Current page */\n readonly currentPage: number\n /** Total number of items */\n readonly total: number\n /** Callback when page changes */\n readonly onPageChange: (page: number) => void\n}\n\n/**\n * Type helper to check if a type has an 'id' property\n */\ntype HasId<T> = T extends { id: string | number } ? true : false\n\n/**\n * Props for the DataTable component\n * @template TData The type of data being displayed in the table\n */\nexport type DataTableProps<TData> = {\n /** Array of column definitions that describe the table structure */\n readonly columns: Array<ColumnDef<TData>>\n /** Array of data items to be displayed in the table */\n readonly data: Array<TData>\n /** Whether to show the column visibility toggle menu */\n readonly showColumnVisibilityControls?: boolean\n /** Whether the table is in a loading state */\n readonly isLoading?: boolean\n /** Pagination configuration. If not provided, pagination is disabled */\n readonly pagination?: BasePaginationProps | BackendPaginationProps\n /** Primary filters that appear directly above the table */\n readonly primaryFilters?: React.ReactNode\n /** Secondary filters that appear in the filters dropdown */\n readonly secondaryFilters?: React.ReactNode\n /** Text customization for filters */\n readonly customLabels?: {\n /** Text for the column visibility button (default: \"Hide columns\") */\n columnVisibilityButton?: string\n /** Text for the filters button when only secondary filters are present (default: \"Filters\") */\n filters?: string\n /** Text for the more filters button when both primary and secondary filters are present (default: \"More filters\") */\n moreFilters?: string\n }\n /** Callback when all rows are selected */\n readonly onSelectAll?: (selected: boolean) => void\n /** Callback when a row is selected */\n readonly onSelect?: (selected: boolean, row: TData) => void\n /** Optional className for the table container */\n readonly className?: string\n /** Optional className for the table */\n readonly tableClassName?: string\n} & (HasId<TData> extends true\n ? {\n /** Function to get unique identifier from a row. Not needed when data has 'id' property */\n readonly getRowId?: never\n }\n : {\n /** Function to get unique identifier from a row. Required when data doesn't have 'id' property */\n readonly getRowId: RowIdentifierFn<TData>\n })\n\n/**\n * Function to get a unique identifier from a row\n */\ntype RowIdentifierFn<T> = (row: T) => string\n\n/**\n * A feature-rich data table component built on top of TanStack Table.\n * Provides sorting, filtering, pagination, and column visibility controls.\n *\n * @template TData The type of data being displayed in the table\n *\n * @example\n * ```tsx\n * type User = {\n * id: string;\n * name: string;\n * email: string;\n * };\n *\n * const columns: ColumnDef<User>[] = [\n * {\n * accessorKey: 'name',\n * header: 'Name',\n * },\n * {\n * accessorKey: 'email',\n * header: 'Email',\n * },\n * ];\n *\n * const data: User[] = [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ];\n *\n * <DataTable columns={columns} data={data} />\n * ```\n */\nexport function DataTable<TData extends object = any>({\n columns: userColumns,\n data,\n getRowId = (row: TData) => (row as { id: string })?.id,\n showColumnVisibilityControls = true,\n isLoading = false,\n pagination,\n primaryFilters,\n secondaryFilters,\n customLabels = {\n columnVisibilityButton: 'Hide columns',\n filters: 'Filters',\n moreFilters: 'More filters',\n },\n onSelectAll,\n onSelect,\n className,\n tableClassName,\n}: DataTableProps<TData>) {\n const isMobile = useIsMobile()\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})\n const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false)\n const [deselectedRows, setDeselectedRows] = React.useState<Record<string, boolean>>({})\n const [selectedRows, setSelectedRows] = React.useState<Record<string, boolean>>({})\n const [pageIndex, setPageIndex] = React.useState(0)\n\n const isBackendPagination = pagination && 'onPageChange' in pagination\n const total = isBackendPagination ? pagination.total : data.length\n const pageSize = pagination?.pageSize ?? data.length\n const totalPages = Math.ceil(total / pageSize)\n\n const isSelectable = typeof onSelectAll === 'function' || typeof onSelect === 'function'\n\n const handleSelectAll = (checked: boolean) => {\n setIsAllRowsSelected(checked)\n setDeselectedRows({})\n setSelectedRows({})\n onSelectAll?.(checked)\n }\n\n const handleRowSelect = (checked: boolean, rowData: TData) => {\n const rowId = (getRowId as (row: TData) => string)(rowData)\n\n if (isAllRowsSelected) {\n setDeselectedRows((prev) => {\n const newDeselections = { ...prev }\n if (!checked) {\n newDeselections[rowId] = true\n } else {\n delete newDeselections[rowId]\n }\n return newDeselections\n })\n } else {\n setSelectedRows((prev) => {\n const newSelection = { ...prev }\n if (checked) {\n newSelection[rowId] = true\n } else {\n delete newSelection[rowId]\n }\n return newSelection\n })\n }\n\n onSelect?.(checked, rowData)\n }\n\n const rowSelection = React.useMemo(() => {\n if (isAllRowsSelected) {\n return Object.fromEntries(\n data.map((row) => [\n (getRowId as (row: TData) => string)(row),\n !deselectedRows[(getRowId as (row: TData) => string)(row)],\n ]),\n )\n }\n return selectedRows\n }, [data, isAllRowsSelected, deselectedRows, selectedRows, getRowId])\n\n const table = useReactTable({\n data,\n columns: userColumns,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: pagination && !isBackendPagination ? getPaginationRowModel() : undefined,\n onColumnVisibilityChange: setColumnVisibility,\n enableRowSelection: isSelectable,\n getRowId: getRowId,\n state: {\n columnVisibility,\n rowSelection,\n pagination: pagination\n ? {\n pageIndex: isBackendPagination ? pagination.currentPage - 1 : pageIndex,\n pageSize,\n }\n : undefined,\n },\n manualPagination: isBackendPagination,\n onPaginationChange: isBackendPagination\n ? undefined\n : (updater) => {\n if (typeof updater === 'function') {\n const newState = updater({ pageIndex, pageSize })\n setPageIndex(newState.pageIndex)\n }\n },\n })\n\n const renderTableBody = () => {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 10 }).map((_, rowIndex) => (\n <TableRow key={`skeleton-row-${rowIndex.toString()}`}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox checked={false} disabled />\n </TableCell>\n )}\n {table\n .getAllColumns()\n .filter((column) => column.getIsVisible())\n .map((column) => (\n <TableCell\n key={`skeleton-cell-${rowIndex.toString()}-${column.id}`}\n style={{ width: column.columnDef.size }}\n >\n <div className={cn('flex items-center justify-center', column.id === 'actions' && 'justify-end')}>\n <Skeleton className={cn(column.id === 'actions' ? 'h-8 w-8' : 'h-[20px] w-full')} />\n </div>\n </TableCell>\n ))}\n </TableRow>\n ))\n }\n\n if (data.length === 0) {\n return (\n <TableRow>\n <TableCell\n colSpan={isSelectable ? table.getAllColumns().length + 1 : table.getAllColumns().length}\n className=\"h-[200px] text-center\"\n >\n <Typography color=\"neutral\">No data available</Typography>\n </TableCell>\n </TableRow>\n )\n }\n\n return table.getRowModel().rows.map((row) => {\n const rowId = (getRowId as (row: TData) => string)(row.original)\n const isSelected = rowSelection[rowId] ?? false\n\n return (\n <TableRow key={rowId} data-selected={isSelected}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) => {\n const isChecked = checked === true\n handleRowSelect(isChecked, row.original)\n }}\n aria-label={`Select row ${rowId}`}\n />\n </TableCell>\n )}\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} style={{ width: cell.column.columnDef.size }}>\n <Typography>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Typography>\n </TableCell>\n ))}\n </TableRow>\n )\n })\n }\n\n return (\n <div className={cn('flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden', className)}>\n {(showColumnVisibilityControls || primaryFilters || secondaryFilters) && (\n <div className=\"flex flex-shrink-0 items-end justify-between p-1\">\n {!isMobile && (primaryFilters ? <div className=\"flex items-center gap-2\">{primaryFilters}</div> : <div />)}\n <div className={cn('flex items-center gap-2', isMobile ? 'w-full justify-end' : '')}>\n {(isMobile ? primaryFilters || secondaryFilters : secondaryFilters) && (\n <PopoverRoot>\n <PopoverTrigger asChild>\n <Button variant=\"text\" size=\"sm\" StartIcon={FunnelSimple}>\n {!primaryFilters || isMobile ? customLabels.filters : customLabels.moreFilters}\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"w-fit p-4\">\n <div className=\"flex flex-col gap-4\">\n {isMobile && primaryFilters && primaryFilters}\n {secondaryFilters}\n </div>\n </PopoverContent>\n </PopoverRoot>\n )}\n {showColumnVisibilityControls && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"text\" size=\"sm\" className={cn('whitespace-nowrap')} StartIcon={TextColumns}>\n {customLabels.columnVisibilityButton ?? 'Hide columns'}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuCheckboxItem\n key={'all-columns'}\n className=\"capitalize\"\n checked={table.getAllColumns().every((column) => column.getIsVisible())}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) =>\n table.getAllColumns().forEach((column) => column.toggleVisibility(!!value))\n }\n >\n Select all\n </DropdownMenuCheckboxItem>\n <DropdownMenuSeparator className=\"bg-neutral-100\" />\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n >\n {column.columnDef.header?.toString()}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n </div>\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col rounded-md border bg-white\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <div className=\"h-full overflow-auto\">\n <TableComponent className=\"w-full\" tableClassName={cn('table-fixed', tableClassName)}>\n <TableHeader className=\"sticky top-0 z-10 bg-neutral-50\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {isSelectable && (\n <TableHead className=\"w-[50px]\">\n <Checkbox\n checked={isAllRowsSelected}\n onCheckedChange={handleSelectAll}\n disabled={isLoading || !data.length}\n aria-label=\"Select all rows\"\n />\n </TableHead>\n )}\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n className=\"whitespace-normal\"\n style={{ width: header.column.columnDef.size }}\n >\n <Typography weight=\"medium\">\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </Typography>\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>{renderTableBody()}</TableBody>\n </TableComponent>\n </div>\n </div>\n {!!pagination && (\n <div className=\"border-t px-4 py-2\">\n <DataTablePagination\n table={table}\n total={total}\n pageSize={pageSize}\n currentPage={\n isBackendPagination ? pagination?.currentPage : table.getState().pagination?.pageIndex + 1 || 1\n }\n totalPages={totalPages}\n onPageChange={isBackendPagination ? pagination?.onPageChange : undefined}\n isLoading={pagination?.isLoading}\n labels={pagination?.labels}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAsJO;AAA+C;AAC3C;AACT;AACoD;AACrB;AACnB;AACZ;AACA;AACA;AACe;AACW;AACf;AACI;AACf;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACE;AACA;AACA;AACA;AAAqB;AAGvB;AACE;AAEA;AACE;AACE;AACA;AACE;AAAyB;AAEzB;AAA4B;AAE9B;AAAO;AACR;AAED;AACE;AACA;AACE;AAAsB;AAEtB;AAAyB;AAE3B;AAAO;AACR;AAGH;AAA2B;AAG7B;AACE;AACE;AAAc;AACM;AACwB;AACiB;AAC1D;AACH;AAEF;AAAO;AAGT;AAA4B;AAC1B;AACS;AACwB;AACqD;AAC5D;AACN;AACpB;AACO;AACL;AACA;AAEI;AACgE;AAC9D;AAEF;AACN;AACkB;AAIZ;AACE;AACA;AAA+B;AACjC;AACF;AAGN;AACE;AACE;AAEK;AAGC;AAME;AAAC;AAAA;AAEuC;AAItC;AAAA;AALsD;AAOzD;AAEN;AAGH;AACE;AAEI;AAAC;AAAA;AACkF;AACvE;AAEmC;AAAA;AAEjD;AAIJ;AACE;AACA;AAEA;AAEK;AAEG;AAAC;AAAA;AACU;AAEP;AACA;AAAuC;AACzC;AAC+B;AAAA;AAEnC;AAMD;AACH;AAEH;AAGH;AAEM;AAEG;AAAsG;AAEnG;AAEE;AAIA;AAGK;AAA8B;AAC9B;AAEL;AACF;AAIE;AAIA;AAEE;AAAA;AAAC;AAAA;AAEW;AAC4D;AAC5B;AAEkC;AAE7E;AAAA;AAPM;AASP;AACkD;AAK9C;AACE;AAAC;AAAA;AAEW;AACmB;AACa;AACiB;AAExB;AAAA;AANvB;AAOd;AAEH;AACL;AACF;AAEJ;AACF;AAIA;AAGM;AAGO;AAEG;AAAC;AAAA;AACU;AACQ;AACY;AAClB;AAAA;AAEf;AAGA;AAAC;AAAA;AAEW;AACmC;AAM7C;AAAA;AARY;AAUf;AAGP;AAC8B;AAGpC;AAGI;AAAC;AAAA;AACC;AACA;AACA;AAEgG;AAEhG;AAC+D;AACxC;AACH;AAAA;AAExB;AAEJ;AAGN;;"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["'use client'\n\nimport {\n ColumnDef,\n flexRender,\n getCoreRowModel,\n getPaginationRowModel,\n useReactTable,\n VisibilityState,\n} from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from '../Button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../DropdownMenu'\nimport { Skeleton } from '../Skeleton'\nimport { Table as TableComponent, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../Table'\nimport { DataTablePagination } from './DataTablePagination'\nimport { Checkbox } from '../Checkbox'\nimport { TextColumns, FunnelSimple } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\nimport { Typography } from '../Typography'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { useIsMobile } from '@/lib/useMobile'\nimport { Badge } from '../Badge'\n\ntype BasePaginationProps = {\n /** Number of rows per page */\n readonly pageSize: number\n /** Whether the pagination is in a loading state */\n readonly isLoading?: boolean\n /** Text customization for pagination */\n readonly labels?: {\n /** Text shown before the page size number (default: \"Showing\") */\n showing?: string\n /** Text shown before the total number (default: \"of\") */\n of?: string\n /** Text shown after the total number (default: \"results\") */\n results?: string\n /** Aria label for previous page button (default: \"Previous page\") */\n previousPage?: string\n /** Aria label for next page button (default: \"Next page\") */\n nextPage?: string\n /** Aria label for page number (default: \"Page {number}\") */\n pageLabel?: string\n }\n}\n\ntype BackendPaginationProps = BasePaginationProps & {\n /** Current page */\n readonly currentPage: number\n /** Total number of items */\n readonly total: number\n /** Callback when page changes */\n readonly onPageChange: (page: number) => void\n}\n\n/**\n * Type helper to check if a type has an 'id' property\n */\ntype HasId<T> = T extends { id: string | number } ? true : false\n\n/**\n * Props for the DataTable component\n * @template TData The type of data being displayed in the table\n */\nexport type DataTableProps<TData> = {\n /** Array of column definitions that describe the table structure */\n readonly columns: Array<ColumnDef<TData>>\n /** Array of data items to be displayed in the table */\n readonly data: Array<TData>\n /** Whether to show the column visibility toggle menu */\n readonly showColumnVisibilityControls?: boolean\n /** Whether the table is in a loading state */\n readonly isLoading?: boolean\n /** Pagination configuration. If not provided, pagination is disabled */\n readonly pagination?: BasePaginationProps | BackendPaginationProps\n /** Primary filters that appear directly above the table */\n readonly primaryFilters?: React.ReactNode\n /** Secondary filters that appear in the filters dropdown */\n readonly secondaryFilters?: React.ReactNode\n /** Number of active primary filters */\n readonly activePrimaryFiltersCount?: number\n /** Number of active secondary filters */\n readonly activeSecondaryFiltersCount?: number\n /** Text customization for filters */\n readonly labels?: {\n /** Text for the column visibility button (default: \"Hide columns\") */\n columnVisibilityButton?: string\n /** Text for the filters button when only secondary filters are present (default: \"Filters\") */\n filters?: string\n /** Text for the more filters button when both primary and secondary filters are present (default: \"More filters\") */\n moreFilters?: string\n }\n /** Callback when all rows are selected */\n readonly onSelectAll?: (selected: boolean) => void\n /** Callback when a row is selected */\n readonly onSelect?: (selected: boolean, row: TData) => void\n /** Optional className for the table container */\n readonly className?: string\n /** Optional className for the table */\n readonly tableClassName?: string\n} & (HasId<TData> extends true\n ? {\n /** Function to get unique identifier from a row. Not needed when data has 'id' property */\n readonly getRowId?: never\n }\n : {\n /** Function to get unique identifier from a row. Required when data doesn't have 'id' property */\n readonly getRowId: RowIdentifierFn<TData>\n })\n\n/**\n * Function to get a unique identifier from a row\n */\ntype RowIdentifierFn<T> = (row: T) => string\n\n/**\n * A feature-rich data table component built on top of TanStack Table.\n * Provides sorting, filtering, pagination, and column visibility controls.\n *\n * @template TData The type of data being displayed in the table\n *\n * @example\n * ```tsx\n * type User = {\n * id: string;\n * name: string;\n * email: string;\n * };\n *\n * const columns: ColumnDef<User>[] = [\n * {\n * accessorKey: 'name',\n * header: 'Name',\n * },\n * {\n * accessorKey: 'email',\n * header: 'Email',\n * },\n * ];\n *\n * const data: User[] = [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ];\n *\n * <DataTable columns={columns} data={data} />\n * ```\n */\nexport function DataTable<TData extends object = any>({\n columns: userColumns,\n data,\n getRowId = (row: TData) => (row as { id: string })?.id,\n showColumnVisibilityControls = true,\n isLoading = false,\n pagination,\n primaryFilters,\n secondaryFilters,\n activePrimaryFiltersCount = 0,\n activeSecondaryFiltersCount = 0,\n labels = {\n columnVisibilityButton: 'Hide columns',\n filters: 'Filters',\n moreFilters: 'More filters',\n },\n onSelectAll,\n onSelect,\n className,\n tableClassName,\n}: DataTableProps<TData>) {\n const isMobile = useIsMobile()\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})\n const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false)\n const [deselectedRows, setDeselectedRows] = React.useState<Record<string, boolean>>({})\n const [selectedRows, setSelectedRows] = React.useState<Record<string, boolean>>({})\n const [pageIndex, setPageIndex] = React.useState(0)\n\n const isBackendPagination = pagination && 'onPageChange' in pagination\n const total = isBackendPagination ? pagination.total : data.length\n const pageSize = pagination?.pageSize ?? data.length\n const totalPages = Math.ceil(total / pageSize)\n\n const isSelectable = typeof onSelectAll === 'function' || typeof onSelect === 'function'\n\n const handleSelectAll = (checked: boolean) => {\n setIsAllRowsSelected(checked)\n setDeselectedRows({})\n setSelectedRows({})\n onSelectAll?.(checked)\n }\n\n const handleRowSelect = (checked: boolean, rowData: TData) => {\n const rowId = (getRowId as (row: TData) => string)(rowData)\n\n if (isAllRowsSelected) {\n setDeselectedRows((prev) => {\n const newDeselections = { ...prev }\n if (!checked) {\n newDeselections[rowId] = true\n } else {\n delete newDeselections[rowId]\n }\n return newDeselections\n })\n } else {\n setSelectedRows((prev) => {\n const newSelection = { ...prev }\n if (checked) {\n newSelection[rowId] = true\n } else {\n delete newSelection[rowId]\n }\n return newSelection\n })\n }\n\n onSelect?.(checked, rowData)\n }\n\n const rowSelection = React.useMemo(() => {\n if (isAllRowsSelected) {\n return Object.fromEntries(\n data.map((row) => [\n (getRowId as (row: TData) => string)(row),\n !deselectedRows[(getRowId as (row: TData) => string)(row)],\n ]),\n )\n }\n return selectedRows\n }, [data, isAllRowsSelected, deselectedRows, selectedRows, getRowId])\n\n const table = useReactTable({\n data,\n columns: userColumns,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: pagination && !isBackendPagination ? getPaginationRowModel() : undefined,\n onColumnVisibilityChange: setColumnVisibility,\n enableRowSelection: isSelectable,\n getRowId: getRowId,\n state: {\n columnVisibility,\n rowSelection,\n pagination: pagination\n ? {\n pageIndex: isBackendPagination ? pagination.currentPage - 1 : pageIndex,\n pageSize,\n }\n : undefined,\n },\n manualPagination: isBackendPagination,\n onPaginationChange: isBackendPagination\n ? undefined\n : (updater) => {\n if (typeof updater === 'function') {\n const newState = updater({ pageIndex, pageSize })\n setPageIndex(newState.pageIndex)\n }\n },\n })\n\n const renderTableBody = () => {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 10 }).map((_, rowIndex) => (\n <TableRow key={`skeleton-row-${rowIndex.toString()}`}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox checked={false} disabled />\n </TableCell>\n )}\n {table\n .getAllColumns()\n .filter((column) => column.getIsVisible())\n .map((column) => (\n <TableCell\n key={`skeleton-cell-${rowIndex.toString()}-${column.id}`}\n style={{ width: column.columnDef.size }}\n >\n <div className={cn('flex items-center justify-center', column.id === 'actions' && 'justify-end')}>\n <Skeleton className={cn(column.id === 'actions' ? 'h-8 w-8' : 'h-[20px] w-full')} />\n </div>\n </TableCell>\n ))}\n </TableRow>\n ))\n }\n\n if (data.length === 0) {\n return (\n <TableRow>\n <TableCell\n colSpan={isSelectable ? table.getAllColumns().length + 1 : table.getAllColumns().length}\n className=\"h-[200px] text-center\"\n >\n <Typography color=\"neutral\">No data available</Typography>\n </TableCell>\n </TableRow>\n )\n }\n\n return table.getRowModel().rows.map((row) => {\n const rowId = (getRowId as (row: TData) => string)(row.original)\n const isSelected = rowSelection[rowId] ?? false\n\n return (\n <TableRow key={rowId} data-selected={isSelected}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) => {\n const isChecked = checked === true\n handleRowSelect(isChecked, row.original)\n }}\n aria-label={`Select row ${rowId}`}\n />\n </TableCell>\n )}\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} style={{ width: cell.column.columnDef.size }}>\n <Typography>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Typography>\n </TableCell>\n ))}\n </TableRow>\n )\n })\n }\n\n return (\n <div className={cn('flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden', className)}>\n {(showColumnVisibilityControls || primaryFilters || secondaryFilters) && (\n <div className=\"flex flex-shrink-0 items-end justify-between p-1\">\n {!isMobile && (primaryFilters ? <div className=\"flex items-center gap-2\">{primaryFilters}</div> : <div />)}\n <div className={cn('flex items-center gap-2', isMobile ? 'w-full justify-end' : '')}>\n {(isMobile ? primaryFilters || secondaryFilters : secondaryFilters) && (\n <PopoverRoot>\n <PopoverTrigger asChild>\n <Button variant=\"text\" size=\"sm\" StartIcon={FunnelSimple} className=\"relative whitespace-nowrap\">\n {((isMobile && activePrimaryFiltersCount + activeSecondaryFiltersCount > 0) ||\n (!isMobile && activeSecondaryFiltersCount > 0)) && (\n <Badge\n count={\n isMobile\n ? activePrimaryFiltersCount + activeSecondaryFiltersCount\n : activeSecondaryFiltersCount\n }\n color=\"primary\"\n ping={false}\n className=\"absolute -right-1 -top-1\"\n />\n )}\n {!primaryFilters || isMobile ? labels.filters : labels.moreFilters}\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"w-fit p-4\">\n <div className=\"flex flex-col gap-4\">\n {isMobile && primaryFilters && primaryFilters}\n {secondaryFilters}\n </div>\n </PopoverContent>\n </PopoverRoot>\n )}\n {showColumnVisibilityControls && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"text\" size=\"sm\" className={cn('whitespace-nowrap')} StartIcon={TextColumns}>\n {labels.columnVisibilityButton ?? 'Hide columns'}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuCheckboxItem\n key={'all-columns'}\n className=\"capitalize\"\n checked={table.getAllColumns().every((column) => column.getIsVisible())}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) =>\n table.getAllColumns().forEach((column) => column.toggleVisibility(!!value))\n }\n >\n Select all\n </DropdownMenuCheckboxItem>\n <DropdownMenuSeparator className=\"bg-neutral-100\" />\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n >\n {column.columnDef.header?.toString()}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n </div>\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col rounded-md border bg-white\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <div className=\"h-full overflow-auto\">\n <TableComponent className=\"w-full\" tableClassName={cn('table-fixed', tableClassName)}>\n <TableHeader className=\"sticky top-0 z-10 bg-neutral-50\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {isSelectable && (\n <TableHead className=\"w-[50px]\">\n <Checkbox\n checked={isAllRowsSelected}\n onCheckedChange={handleSelectAll}\n disabled={isLoading || !data.length}\n aria-label=\"Select all rows\"\n />\n </TableHead>\n )}\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n className=\"whitespace-normal\"\n style={{ width: header.column.columnDef.size }}\n >\n <Typography weight=\"medium\">\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </Typography>\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>{renderTableBody()}</TableBody>\n </TableComponent>\n </div>\n </div>\n {!!pagination && (\n <div className=\"border-t px-4 py-2\">\n <DataTablePagination\n table={table}\n total={total}\n pageSize={pageSize}\n currentPage={\n isBackendPagination ? pagination?.currentPage : table.getState().pagination?.pageIndex + 1 || 1\n }\n totalPages={totalPages}\n onPageChange={isBackendPagination ? pagination?.onPageChange : undefined}\n isLoading={pagination?.isLoading}\n labels={pagination?.labels}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AA2JO;AAA+C;AAC3C;AACT;AACoD;AACrB;AACnB;AACZ;AACA;AACA;AAC4B;AACE;AACrB;AACiB;AACf;AACI;AACf;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACE;AACA;AACA;AACA;AAAqB;AAGvB;AACE;AAEA;AACE;AACE;AACA;AACE;AAAyB;AAEzB;AAA4B;AAE9B;AAAO;AACR;AAED;AACE;AACA;AACE;AAAsB;AAEtB;AAAyB;AAE3B;AAAO;AACR;AAGH;AAA2B;AAG7B;AACE;AACE;AAAc;AACM;AACwB;AACiB;AAC1D;AACH;AAEF;AAAO;AAGT;AAA4B;AAC1B;AACS;AACwB;AACqD;AAC5D;AACN;AACpB;AACO;AACL;AACA;AAEI;AACgE;AAC9D;AAEF;AACN;AACkB;AAIZ;AACE;AACA;AAA+B;AACjC;AACF;AAGN;AACE;AACE;AAEK;AAGC;AAME;AAAC;AAAA;AAEuC;AAItC;AAAA;AALsD;AAOzD;AAEN;AAGH;AACE;AAEI;AAAC;AAAA;AACkF;AACvE;AAEmC;AAAA;AAEjD;AAIJ;AACE;AACA;AAEA;AAEK;AAEG;AAAC;AAAA;AACU;AAEP;AACA;AAAuC;AACzC;AAC+B;AAAA;AAEnC;AAMD;AACH;AAEH;AAGH;AAEM;AAEG;AAAsG;AAEnG;AAEE;AAEO;AAED;AAAC;AAAA;AAIO;AAEA;AACA;AACI;AAAA;AACZ;AAEqD;AAE3D;AAGK;AAA8B;AAC9B;AAEL;AACF;AAIE;AAIA;AAEE;AAAA;AAAC;AAAA;AAEW;AAC4D;AAC5B;AAEkC;AAE7E;AAAA;AAPM;AASP;AACkD;AAK9C;AACE;AAAC;AAAA;AAEW;AACmB;AACa;AACiB;AAExB;AAAA;AANvB;AAOd;AAEH;AACL;AACF;AAEJ;AACF;AAIA;AAGM;AAGO;AAEG;AAAC;AAAA;AACU;AACQ;AACY;AAClB;AAAA;AAEf;AAGA;AAAC;AAAA;AAEW;AACmC;AAM7C;AAAA;AARY;AAUf;AAGP;AAC8B;AAGpC;AAGI;AAAC;AAAA;AACC;AACA;AACA;AAEgG;AAEhG;AAC+D;AACxC;AACH;AAAA;AAExB;AAEJ;AAGN;;"}
|
|
@@ -86,15 +86,15 @@ function DatePicker(props) {
|
|
|
86
86
|
id: rest.id,
|
|
87
87
|
variant: "ghost",
|
|
88
88
|
className: cn(
|
|
89
|
-
"w-fit justify-start text-left font-normal",
|
|
89
|
+
"relative flex w-fit items-center justify-start text-left font-normal",
|
|
90
90
|
!(variant === "single" ? singleDate : dateRange) && "text-muted-foreground",
|
|
91
91
|
buttonVariants({ variant: "input" }),
|
|
92
92
|
buttonClassName
|
|
93
93
|
),
|
|
94
94
|
disabled: typeof rest.disabled === "boolean" ? rest.disabled : false,
|
|
95
95
|
children: [
|
|
96
|
-
/* @__PURE__ */ jsx(CalendarBlank, { className: "
|
|
97
|
-
/* @__PURE__ */ jsx("span", { children: formatDate() })
|
|
96
|
+
/* @__PURE__ */ jsx(CalendarBlank, { className: "absolute left-4 h-4 w-4 shrink-0" }),
|
|
97
|
+
/* @__PURE__ */ jsx("span", { className: "w-full pl-7 text-center", children: formatDate() })
|
|
98
98
|
]
|
|
99
99
|
}
|
|
100
100
|
) }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport { DateRange, PropsBase } from 'react-day-picker'\n\nimport { cn } from '@/lib/utils'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { Button, buttonVariants } from '../Button'\nimport { Calendar } from '../Calendar'\nimport { CalendarBlank } from '@phosphor-icons/react'\n\ntype BaseDatePickerProps = {\n /**\n * Placeholder text displayed when no date is selected\n * @default 'Pick a date'\n */\n placeholder?: string\n /**\n * Format string to use when displaying the selected date\n * @default 'MMM d, yyyy'\n */\n format?: string\n /**\n * ClassName for the button\n */\n buttonClassName?: string\n} & Omit<PropsBase, 'mode' | 'selected' | 'onSelect'>\n\ntype SingleDatePickerProps = {\n variant?: 'single'\n value?: Date\n initialValue?: Date\n onChange?: (date: Date | undefined) => void\n closeOnSelect?: boolean\n showYearSwitcher?: boolean\n} & BaseDatePickerProps\n\ntype RangeDatePickerProps = {\n variant: 'range'\n value?: DateRange\n initialValue?: DateRange\n onChange?: (date: DateRange | undefined) => void\n closeOnSelect?: never\n showYearSwitcher?: never\n} & BaseDatePickerProps\n\nexport type DatePickerProps = SingleDatePickerProps | RangeDatePickerProps\n\nfunction isDateRange(value: any): value is DateRange {\n return typeof value === 'object' && value !== null && 'from' in value\n}\n\nfunction DatePicker(props: DatePickerProps) {\n const {\n variant = 'single',\n placeholder = variant === 'single' ? 'Pick a date' : 'Pick a date range',\n initialValue,\n value,\n onChange,\n buttonClassName,\n closeOnSelect = variant === 'single',\n showYearSwitcher = variant === 'single',\n ...rest\n } = props\n\n const [isOpen, setIsOpen] = React.useState(false)\n\n const [internalSingleDate, setInternalSingleDate] = React.useState<Date | undefined>(\n variant === 'single' && initialValue instanceof Date ? initialValue : undefined,\n )\n const [internalDateRange, setInternalDateRange] = React.useState<DateRange | undefined>(\n variant === 'range' && isDateRange(initialValue) ? initialValue : undefined,\n )\n\n const singleDate = variant === 'single' && value instanceof Date ? value : internalSingleDate\n const dateRange = variant === 'range' && isDateRange(value) ? value : internalDateRange\n\n const handleSelect = React.useCallback(\n (selectedDate: Date | DateRange | undefined) => {\n if (variant === 'single') {\n const date = selectedDate as Date | undefined\n setInternalSingleDate(date)\n if (onChange && typeof onChange === 'function') (onChange as (date: Date | undefined) => void)(date)\n if (closeOnSelect) {\n setIsOpen(false)\n }\n } else {\n const range = selectedDate as DateRange | undefined\n setInternalDateRange(range)\n if (onChange && typeof onChange === 'function') (onChange as (range: DateRange | undefined) => void)(range)\n }\n },\n [variant, onChange, closeOnSelect],\n )\n\n const formatDate = () => {\n if (variant === 'single') {\n return singleDate ? format(singleDate, rest.format ?? 'MMM d, yyyy') : placeholder\n }\n\n if (!dateRange) return placeholder\n return `${dateRange.from ? format(dateRange.from, rest.format ?? 'MMM d, yyyy') : ''} - ${\n dateRange.to ? format(dateRange.to, rest.format ?? 'MMM d, yyyy') : ''\n }`\n }\n\n const calendarProps = React.useMemo(() => {\n const baseProps = {\n ...rest,\n initialFocus: true,\n }\n\n if (variant === 'single') {\n return {\n ...baseProps,\n mode: 'single' as const,\n selected: singleDate,\n onSelect: (date: Date | undefined) => handleSelect(date),\n defaultMonth: singleDate ?? new Date(),\n }\n }\n\n return {\n ...baseProps,\n mode: 'range' as const,\n selected: dateRange,\n onSelect: (range: DateRange | undefined) => handleSelect(range),\n defaultMonth: dateRange?.from ?? new Date(),\n numberOfMonths: rest.numberOfMonths ?? 2,\n }\n }, [variant, rest, singleDate, dateRange, handleSelect])\n\n return (\n <PopoverRoot open={isOpen} onOpenChange={setIsOpen}>\n <PopoverTrigger asChild>\n <Button\n id={rest.id}\n variant=\"ghost\"\n className={cn(\n 'w-fit justify-start text-left font-normal',\n !(variant === 'single' ? singleDate : dateRange) && 'text-muted-foreground',\n buttonVariants({ variant: 'input' }),\n buttonClassName,\n )}\n disabled={typeof rest.disabled === 'boolean' ? rest.disabled : false}\n >\n <CalendarBlank className=\"
|
|
1
|
+
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport { DateRange, PropsBase } from 'react-day-picker'\n\nimport { cn } from '@/lib/utils'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { Button, buttonVariants } from '../Button'\nimport { Calendar } from '../Calendar'\nimport { CalendarBlank } from '@phosphor-icons/react'\n\ntype BaseDatePickerProps = {\n /**\n * Placeholder text displayed when no date is selected\n * @default 'Pick a date'\n */\n placeholder?: string\n /**\n * Format string to use when displaying the selected date\n * @default 'MMM d, yyyy'\n */\n format?: string\n /**\n * ClassName for the button\n */\n buttonClassName?: string\n} & Omit<PropsBase, 'mode' | 'selected' | 'onSelect'>\n\ntype SingleDatePickerProps = {\n variant?: 'single'\n value?: Date\n initialValue?: Date\n onChange?: (date: Date | undefined) => void\n closeOnSelect?: boolean\n showYearSwitcher?: boolean\n} & BaseDatePickerProps\n\ntype RangeDatePickerProps = {\n variant: 'range'\n value?: DateRange\n initialValue?: DateRange\n onChange?: (date: DateRange | undefined) => void\n closeOnSelect?: never\n showYearSwitcher?: never\n} & BaseDatePickerProps\n\nexport type DatePickerProps = SingleDatePickerProps | RangeDatePickerProps\n\nfunction isDateRange(value: any): value is DateRange {\n return typeof value === 'object' && value !== null && 'from' in value\n}\n\nfunction DatePicker(props: DatePickerProps) {\n const {\n variant = 'single',\n placeholder = variant === 'single' ? 'Pick a date' : 'Pick a date range',\n initialValue,\n value,\n onChange,\n buttonClassName,\n closeOnSelect = variant === 'single',\n showYearSwitcher = variant === 'single',\n ...rest\n } = props\n\n const [isOpen, setIsOpen] = React.useState(false)\n\n const [internalSingleDate, setInternalSingleDate] = React.useState<Date | undefined>(\n variant === 'single' && initialValue instanceof Date ? initialValue : undefined,\n )\n const [internalDateRange, setInternalDateRange] = React.useState<DateRange | undefined>(\n variant === 'range' && isDateRange(initialValue) ? initialValue : undefined,\n )\n\n const singleDate = variant === 'single' && value instanceof Date ? value : internalSingleDate\n const dateRange = variant === 'range' && isDateRange(value) ? value : internalDateRange\n\n const handleSelect = React.useCallback(\n (selectedDate: Date | DateRange | undefined) => {\n if (variant === 'single') {\n const date = selectedDate as Date | undefined\n setInternalSingleDate(date)\n if (onChange && typeof onChange === 'function') (onChange as (date: Date | undefined) => void)(date)\n if (closeOnSelect) {\n setIsOpen(false)\n }\n } else {\n const range = selectedDate as DateRange | undefined\n setInternalDateRange(range)\n if (onChange && typeof onChange === 'function') (onChange as (range: DateRange | undefined) => void)(range)\n }\n },\n [variant, onChange, closeOnSelect],\n )\n\n const formatDate = () => {\n if (variant === 'single') {\n return singleDate ? format(singleDate, rest.format ?? 'MMM d, yyyy') : placeholder\n }\n\n if (!dateRange) return placeholder\n return `${dateRange.from ? format(dateRange.from, rest.format ?? 'MMM d, yyyy') : ''} - ${\n dateRange.to ? format(dateRange.to, rest.format ?? 'MMM d, yyyy') : ''\n }`\n }\n\n const calendarProps = React.useMemo(() => {\n const baseProps = {\n ...rest,\n initialFocus: true,\n }\n\n if (variant === 'single') {\n return {\n ...baseProps,\n mode: 'single' as const,\n selected: singleDate,\n onSelect: (date: Date | undefined) => handleSelect(date),\n defaultMonth: singleDate ?? new Date(),\n }\n }\n\n return {\n ...baseProps,\n mode: 'range' as const,\n selected: dateRange,\n onSelect: (range: DateRange | undefined) => handleSelect(range),\n defaultMonth: dateRange?.from ?? new Date(),\n numberOfMonths: rest.numberOfMonths ?? 2,\n }\n }, [variant, rest, singleDate, dateRange, handleSelect])\n\n return (\n <PopoverRoot open={isOpen} onOpenChange={setIsOpen}>\n <PopoverTrigger asChild>\n <Button\n id={rest.id}\n variant=\"ghost\"\n className={cn(\n 'relative flex w-fit items-center justify-start text-left font-normal',\n !(variant === 'single' ? singleDate : dateRange) && 'text-muted-foreground',\n buttonVariants({ variant: 'input' }),\n buttonClassName,\n )}\n disabled={typeof rest.disabled === 'boolean' ? rest.disabled : false}\n >\n <CalendarBlank className=\"absolute left-4 h-4 w-4 shrink-0\" />\n <span className=\"w-full pl-7 text-center\">{formatDate()}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"center\">\n <Calendar\n {...calendarProps}\n className=\"border-0\"\n showYearSwitcher={variant === 'single' ? showYearSwitcher : false}\n />\n </PopoverContent>\n </PopoverRoot>\n )\n}\n\nDatePicker.displayName = 'DatePicker'\n\nexport { DatePicker }\nexport type { DateRange }\n"],"names":[],"mappings":";;;;;;;;;;AAiDA;AACE;AACF;AAEA;AACE;AAAM;AACM;AAC2C;AACrD;AACA;AACA;AACA;AAC4B;AACG;AAC5B;AAGL;AAEA;AAA0D;AACc;AAExE;AAAwD;AACY;AAGpE;AACA;AAEA;AAA2B;AAEvB;AACE;AACA;AACA;AACA;AACE;AAAe;AACjB;AAEA;AACA;AACA;AAA0G;AAC5G;AACF;AACiC;AAGnC;AACE;AACE;AAAuE;AAGzE;AACA;AAEA;AAGF;AACE;AAAkB;AACb;AACW;AAGhB;AACE;AAAO;AACF;AACG;AACI;AAC6C;AAClB;AACvC;AAGF;AAAO;AACF;AACG;AACI;AACoD;AACpB;AACH;AACzC;AAGF;AAEI;AACE;AAAC;AAAA;AACU;AACD;AACG;AACT;AACoD;AACjB;AACnC;AACF;AAC+D;AAE/D;AAA4D;AACJ;AAAA;AAAA;AAE5D;AAEE;AAAC;AAAA;AACK;AACM;AACkD;AAAA;AAEhE;AAGN;AAEA;;"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ControllerFieldState } from 'react-hook-form';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
/** Represents a selectable option in the InlineMultiSelect */
|
|
4
|
+
type Option = {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
};
|
|
8
|
+
/** Props for the InlineMultiSelect component */
|
|
9
|
+
export type InlineMultiSelectProps = {
|
|
10
|
+
/** Array of options to display in the select */
|
|
11
|
+
options: Array<Option>;
|
|
12
|
+
/** Currently selected values (controlled) */
|
|
13
|
+
value?: Array<string>;
|
|
14
|
+
/** Callback fired when selection changes */
|
|
15
|
+
onChange?: (value: string[]) => void;
|
|
16
|
+
/** Form field state from react-hook-form */
|
|
17
|
+
fieldState?: ControllerFieldState;
|
|
18
|
+
/** Default selected values (uncontrolled) */
|
|
19
|
+
defaultValue?: Array<string>;
|
|
20
|
+
/** Placeholder text when no items are selected */
|
|
21
|
+
placeholder?: string;
|
|
22
|
+
/** Text to show when no options match the search */
|
|
23
|
+
notFoundText?: string;
|
|
24
|
+
/** Placeholder for the search input */
|
|
25
|
+
commandInputPlaceholder?: string;
|
|
26
|
+
/** Maximum number of items that can be selected */
|
|
27
|
+
max?: number;
|
|
28
|
+
/** Additional CSS classes */
|
|
29
|
+
className?: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* InlineMultiSelect Component
|
|
33
|
+
*
|
|
34
|
+
* A controlled multi-select component that displays selected items inline with search functionality.
|
|
35
|
+
* Supports keyboard navigation, maximum selection limit, and custom styling.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```tsx
|
|
39
|
+
* // Controlled usage
|
|
40
|
+
* const [value, setValue] = useState<string[]>([])
|
|
41
|
+
* <InlineMultiSelect
|
|
42
|
+
* options={[{ value: '1', label: 'Option 1' }]}
|
|
43
|
+
* value={value}
|
|
44
|
+
* onChange={setValue}
|
|
45
|
+
* />
|
|
46
|
+
*
|
|
47
|
+
* // With react-hook-form
|
|
48
|
+
* const { control } = useForm()
|
|
49
|
+
* <Controller
|
|
50
|
+
* name="myField"
|
|
51
|
+
* control={control}
|
|
52
|
+
* render={({ field }) => (
|
|
53
|
+
* <InlineMultiSelect
|
|
54
|
+
* {...field}
|
|
55
|
+
* options={options}
|
|
56
|
+
* />
|
|
57
|
+
* )}
|
|
58
|
+
* />
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare const InlineMultiSelect: React.ForwardRefExoticComponent<InlineMultiSelectProps & React.RefAttributes<HTMLInputElement>>;
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from '../Command/Command.js';
|
|
5
|
+
import { X, CaretUpDown, Check } from '@phosphor-icons/react';
|
|
6
|
+
import { PopoverRoot, PopoverTrigger, PopoverContent } from '../Popover/Popover.js';
|
|
7
|
+
import { cn } from '../../lib/utils.js';
|
|
8
|
+
import { Button } from '../Button/Button.js';
|
|
9
|
+
|
|
10
|
+
const InlineMultiSelect = React.forwardRef(
|
|
11
|
+
({
|
|
12
|
+
options,
|
|
13
|
+
value,
|
|
14
|
+
onChange,
|
|
15
|
+
defaultValue,
|
|
16
|
+
placeholder,
|
|
17
|
+
notFoundText,
|
|
18
|
+
fieldState,
|
|
19
|
+
commandInputPlaceholder,
|
|
20
|
+
max,
|
|
21
|
+
className
|
|
22
|
+
}, _ref) => {
|
|
23
|
+
const inputRef = React.useRef(null);
|
|
24
|
+
const containerRef = React.useRef(null);
|
|
25
|
+
const [open, setOpen] = React.useState(false);
|
|
26
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
27
|
+
const [visibleItems, setVisibleItems] = React.useState([]);
|
|
28
|
+
const [hiddenCount, setHiddenCount] = React.useState(0);
|
|
29
|
+
const [internalSelected, setInternalSelected] = React.useState(
|
|
30
|
+
options.filter((option) => defaultValue?.includes(option.value) ?? [])
|
|
31
|
+
);
|
|
32
|
+
const selected = value ? options.filter((option) => value.includes(option.value)) : internalSelected;
|
|
33
|
+
const handleSelectionChange = React.useCallback(
|
|
34
|
+
(newSelected) => {
|
|
35
|
+
if (!value) {
|
|
36
|
+
setInternalSelected(newSelected);
|
|
37
|
+
}
|
|
38
|
+
onChange?.(newSelected.map((s) => s.value));
|
|
39
|
+
},
|
|
40
|
+
[value, onChange]
|
|
41
|
+
);
|
|
42
|
+
const removeItem = React.useCallback(
|
|
43
|
+
(itemToRemove) => {
|
|
44
|
+
const newSelected = itemToRemove ? selected.filter((s) => s.value !== itemToRemove.value) : selected.slice(0, -1);
|
|
45
|
+
handleSelectionChange(newSelected);
|
|
46
|
+
},
|
|
47
|
+
[selected, handleSelectionChange]
|
|
48
|
+
);
|
|
49
|
+
const handleUnselect = React.useCallback(
|
|
50
|
+
(item, e) => {
|
|
51
|
+
e.stopPropagation();
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
removeItem(item);
|
|
54
|
+
},
|
|
55
|
+
[removeItem]
|
|
56
|
+
);
|
|
57
|
+
const handleKeyDown = React.useCallback(
|
|
58
|
+
(e) => {
|
|
59
|
+
const input = inputRef.current;
|
|
60
|
+
if (input) {
|
|
61
|
+
if ((e.key === "Delete" || e.key === "Backspace") && input.value === "") {
|
|
62
|
+
removeItem();
|
|
63
|
+
}
|
|
64
|
+
if (e.key === "Escape") {
|
|
65
|
+
input.blur();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[removeItem]
|
|
70
|
+
);
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
const updateVisibleItems = () => {
|
|
73
|
+
if (!containerRef.current || selected.length === 0) return;
|
|
74
|
+
const maxVisibleItems = 2;
|
|
75
|
+
const visibleCount = Math.min(selected.length, maxVisibleItems);
|
|
76
|
+
setVisibleItems(selected.slice(0, visibleCount));
|
|
77
|
+
setHiddenCount(selected.length - visibleCount);
|
|
78
|
+
};
|
|
79
|
+
updateVisibleItems();
|
|
80
|
+
window.addEventListener("resize", updateVisibleItems);
|
|
81
|
+
return () => window.removeEventListener("resize", updateVisibleItems);
|
|
82
|
+
}, [selected]);
|
|
83
|
+
return /* @__PURE__ */ jsx(PopoverRoot, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs(Command, { onKeyDown: handleKeyDown, children: [
|
|
84
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
85
|
+
Button,
|
|
86
|
+
{
|
|
87
|
+
variant: "ghost",
|
|
88
|
+
"aria-expanded": open,
|
|
89
|
+
className: cn(
|
|
90
|
+
"w-full justify-between bg-[#fff] pl-2 pr-2",
|
|
91
|
+
fieldState?.invalid ? "border-error-400 focus-within:border-error-700" : "",
|
|
92
|
+
className
|
|
93
|
+
),
|
|
94
|
+
children: [
|
|
95
|
+
/* @__PURE__ */ jsx("div", { ref: containerRef, className: "flex flex-1 flex-wrap items-center gap-1 overflow-hidden pr-2", children: /* @__PURE__ */ jsx("div", { className: "flex items-center whitespace-nowrap", children: selected.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
96
|
+
visibleItems.map((item) => /* @__PURE__ */ jsxs("div", { className: "bg-muted flex items-center gap-1 rounded px-1 py-0.5", children: [
|
|
97
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: item.label }),
|
|
98
|
+
/* @__PURE__ */ jsx(
|
|
99
|
+
"span",
|
|
100
|
+
{
|
|
101
|
+
role: "button",
|
|
102
|
+
tabIndex: 0,
|
|
103
|
+
onClick: (e) => handleUnselect(item, e),
|
|
104
|
+
onKeyDown: (e) => {
|
|
105
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
106
|
+
handleUnselect(item, e);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"aria-label": `Eliminar ${item.label}`,
|
|
110
|
+
className: "hover:bg-muted-foreground/20 flex h-4 w-4 items-center justify-center rounded-full",
|
|
111
|
+
children: /* @__PURE__ */ jsx(X, { className: "text-muted-foreground h-3 w-3" })
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] }, item.value)),
|
|
115
|
+
hiddenCount > 0 && /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground inline-flex flex-shrink-0 items-center whitespace-nowrap text-sm", children: [
|
|
116
|
+
"+",
|
|
117
|
+
hiddenCount
|
|
118
|
+
] })
|
|
119
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }) }) }),
|
|
120
|
+
/* @__PURE__ */ jsx(CaretUpDown, { className: "h-4 w-4 shrink-0 opacity-50" })
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
) }),
|
|
124
|
+
/* @__PURE__ */ jsxs(PopoverContent, { className: "px-0 py-0", children: [
|
|
125
|
+
/* @__PURE__ */ jsx(
|
|
126
|
+
CommandInput,
|
|
127
|
+
{
|
|
128
|
+
placeholder: commandInputPlaceholder ?? "Search...",
|
|
129
|
+
value: inputValue,
|
|
130
|
+
onValueChange: setInputValue,
|
|
131
|
+
children: max && /* @__PURE__ */ jsxs("div", { className: "bg-muted rounded-md px-2 py-1 text-sm", children: [
|
|
132
|
+
selected.length,
|
|
133
|
+
" / ",
|
|
134
|
+
max
|
|
135
|
+
] })
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ jsxs(CommandList, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx(CommandEmpty, { children: notFoundText ?? "Options not found" }),
|
|
140
|
+
open && options.length > 0 && /* @__PURE__ */ jsx(CommandGroup, { children: options.map((item) => /* @__PURE__ */ jsx(
|
|
141
|
+
CommandItem,
|
|
142
|
+
{
|
|
143
|
+
onMouseDown: (e) => {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
e.stopPropagation();
|
|
146
|
+
},
|
|
147
|
+
onSelect: () => {
|
|
148
|
+
const isSelected = selected.find((s) => s.value === item.value);
|
|
149
|
+
if (isSelected) {
|
|
150
|
+
handleSelectionChange(selected.filter((s) => s.value !== item.value));
|
|
151
|
+
} else {
|
|
152
|
+
if (max && selected.length >= max) return;
|
|
153
|
+
setInputValue("");
|
|
154
|
+
handleSelectionChange([...selected, item]);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
className: "cursor-pointer",
|
|
158
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
159
|
+
/* @__PURE__ */ jsx("div", { className: "mr-2", children: selected.find((s) => s.value === item.value) ? /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx("div", { className: "h-4 w-4" }) }),
|
|
160
|
+
/* @__PURE__ */ jsx("div", { children: item.label })
|
|
161
|
+
] })
|
|
162
|
+
},
|
|
163
|
+
`item-${item.value}`
|
|
164
|
+
)) })
|
|
165
|
+
] })
|
|
166
|
+
] })
|
|
167
|
+
] }) });
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
InlineMultiSelect.displayName = "InlineMultiSelect";
|
|
171
|
+
|
|
172
|
+
export { InlineMultiSelect };
|
|
173
|
+
//# sourceMappingURL=InlineMultiSelect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InlineMultiSelect.js","sources":["../../../src/components/InlineMultiSelect/InlineMultiSelect.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '../Command'\nimport { CaretUpDown, Check, X } from '@phosphor-icons/react'\nimport { PopoverRoot, PopoverContent, PopoverTrigger } from '../Popover'\nimport { ControllerFieldState } from 'react-hook-form'\nimport { cn } from '@/lib/utils'\nimport { Button } from '../Button'\n\n/** Represents a selectable option in the InlineMultiSelect */\ntype Option = { value: string; label: string }\n\n/** Props for the InlineMultiSelect component */\nexport type InlineMultiSelectProps = {\n /** Array of options to display in the select */\n options: Array<Option>\n /** Currently selected values (controlled) */\n value?: Array<string>\n /** Callback fired when selection changes */\n onChange?: (value: string[]) => void\n /** Form field state from react-hook-form */\n fieldState?: ControllerFieldState\n /** Default selected values (uncontrolled) */\n defaultValue?: Array<string>\n /** Placeholder text when no items are selected */\n placeholder?: string\n /** Text to show when no options match the search */\n notFoundText?: string\n /** Placeholder for the search input */\n commandInputPlaceholder?: string\n /** Maximum number of items that can be selected */\n max?: number\n /** Additional CSS classes */\n className?: string\n}\n\n/**\n * InlineMultiSelect Component\n *\n * A controlled multi-select component that displays selected items inline with search functionality.\n * Supports keyboard navigation, maximum selection limit, and custom styling.\n *\n * @example\n * ```tsx\n * // Controlled usage\n * const [value, setValue] = useState<string[]>([])\n * <InlineMultiSelect\n * options={[{ value: '1', label: 'Option 1' }]}\n * value={value}\n * onChange={setValue}\n * />\n *\n * // With react-hook-form\n * const { control } = useForm()\n * <Controller\n * name=\"myField\"\n * control={control}\n * render={({ field }) => (\n * <InlineMultiSelect\n * {...field}\n * options={options}\n * />\n * )}\n * />\n * ```\n */\nexport const InlineMultiSelect = React.forwardRef<HTMLInputElement, InlineMultiSelectProps>(\n (\n {\n options,\n value,\n onChange,\n defaultValue,\n placeholder,\n notFoundText,\n fieldState,\n commandInputPlaceholder,\n max,\n className,\n },\n _ref,\n ) => {\n const inputRef = React.useRef<HTMLInputElement>(null)\n const containerRef = React.useRef<HTMLDivElement>(null)\n const [open, setOpen] = React.useState(false)\n const [inputValue, setInputValue] = React.useState('')\n const [visibleItems, setVisibleItems] = React.useState<Option[]>([])\n const [hiddenCount, setHiddenCount] = React.useState(0)\n\n // Use controlled value if provided, otherwise use internal state\n const [internalSelected, setInternalSelected] = React.useState<InlineMultiSelectProps['options']>(\n options.filter((option) => defaultValue?.includes(option.value) ?? []),\n )\n\n const selected = value ? options.filter((option) => value.includes(option.value)) : internalSelected\n\n const handleSelectionChange = React.useCallback(\n (newSelected: Option[]) => {\n if (!value) {\n // Uncontrolled mode\n setInternalSelected(newSelected)\n }\n // Always call onChange with the new values\n onChange?.(newSelected.map((s) => s.value))\n },\n [value, onChange],\n )\n\n /**\n * Removes an item from the selection\n * @param itemToRemove - Optional item to remove. If not provided, removes the last item\n */\n const removeItem = React.useCallback(\n (itemToRemove?: Option) => {\n const newSelected = itemToRemove\n ? selected.filter((s) => s.value !== itemToRemove.value)\n : selected.slice(0, -1)\n\n handleSelectionChange(newSelected)\n },\n [selected, handleSelectionChange],\n )\n\n /**\n * Handles the unselect action when clicking the remove button\n * @param item - The item to remove\n * @param e - The event object\n */\n const handleUnselect = React.useCallback(\n (item: Option, e: React.MouseEvent | React.KeyboardEvent) => {\n e.stopPropagation()\n e.preventDefault()\n removeItem(item)\n },\n [removeItem],\n )\n\n /**\n * Handles keyboard events for the component\n * - Delete/Backspace: Removes last item when input is empty\n * - Escape: Blurs the input\n */\n const handleKeyDown = React.useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n const input = inputRef.current\n if (input) {\n if ((e.key === 'Delete' || e.key === 'Backspace') && input.value === '') {\n removeItem()\n }\n if (e.key === 'Escape') {\n input.blur()\n }\n }\n },\n [removeItem],\n )\n\n /**\n * Effect to update visible items and handle responsive behavior\n * Shows a maximum of 2 items and displays a count for the rest\n */\n React.useEffect(() => {\n const updateVisibleItems = () => {\n if (!containerRef.current || selected.length === 0) return\n\n const maxVisibleItems = 2\n const visibleCount = Math.min(selected.length, maxVisibleItems)\n\n setVisibleItems(selected.slice(0, visibleCount))\n setHiddenCount(selected.length - visibleCount)\n }\n\n updateVisibleItems()\n window.addEventListener('resize', updateVisibleItems)\n return () => window.removeEventListener('resize', updateVisibleItems)\n }, [selected])\n\n return (\n <PopoverRoot open={open} onOpenChange={setOpen}>\n <Command onKeyDown={handleKeyDown}>\n <PopoverTrigger asChild>\n <Button\n variant=\"ghost\"\n aria-expanded={open}\n className={cn(\n 'w-full justify-between bg-[#fff] pl-2 pr-2',\n fieldState?.invalid ? 'border-error-400 focus-within:border-error-700' : '',\n className,\n )}\n >\n <div ref={containerRef} className=\"flex flex-1 flex-wrap items-center gap-1 overflow-hidden pr-2\">\n <div className=\"flex items-center whitespace-nowrap\">\n {selected.length > 0 ? (\n <>\n {visibleItems.map((item) => (\n <div key={item.value} className=\"bg-muted flex items-center gap-1 rounded px-1 py-0.5\">\n <span className=\"text-sm\">{item.label}</span>\n <span\n role=\"button\"\n tabIndex={0}\n onClick={(e) => handleUnselect(item, e)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n handleUnselect(item, e)\n }\n }}\n aria-label={`Eliminar ${item.label}`}\n className=\"hover:bg-muted-foreground/20 flex h-4 w-4 items-center justify-center rounded-full\"\n >\n <X className=\"text-muted-foreground h-3 w-3\" />\n </span>\n </div>\n ))}\n {hiddenCount > 0 && (\n <span className=\"text-muted-foreground inline-flex flex-shrink-0 items-center whitespace-nowrap text-sm\">\n +{hiddenCount}\n </span>\n )}\n </>\n ) : (\n <span className=\"text-muted-foreground\">{placeholder}</span>\n )}\n </div>\n </div>\n <CaretUpDown className=\"h-4 w-4 shrink-0 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"px-0 py-0\">\n <CommandInput\n placeholder={commandInputPlaceholder ?? 'Search...'}\n value={inputValue}\n onValueChange={setInputValue}\n >\n {max && (\n <div className=\"bg-muted rounded-md px-2 py-1 text-sm\">\n {selected.length} / {max}\n </div>\n )}\n </CommandInput>\n <CommandList>\n <CommandEmpty>{notFoundText ?? 'Options not found'}</CommandEmpty>\n {open && options.length > 0 && (\n <CommandGroup>\n {options.map((item) => (\n <CommandItem\n key={`item-${item.value}`}\n onMouseDown={(e) => {\n e.preventDefault()\n e.stopPropagation()\n }}\n onSelect={() => {\n const isSelected = selected.find((s) => s.value === item.value)\n if (isSelected) {\n handleSelectionChange(selected.filter((s) => s.value !== item.value))\n } else {\n if (max && selected.length >= max) return\n setInputValue('')\n handleSelectionChange([...selected, item])\n }\n }}\n className=\"cursor-pointer\"\n >\n <div className=\"flex items-center\">\n <div className=\"mr-2\">\n {selected.find((s) => s.value === item.value) ? (\n <Check className=\"h-4 w-4\" />\n ) : (\n <div className=\"h-4 w-4\" />\n )}\n </div>\n <div>{item.label}</div>\n </div>\n </CommandItem>\n ))}\n </CommandGroup>\n )}\n </CommandList>\n </PopoverContent>\n </Command>\n </PopoverRoot>\n )\n },\n)\n\nInlineMultiSelect.displayName = 'InlineMultiSelect'\n"],"names":[],"mappings":";;;;;;;;;AAmEO;AAAgC;AAEnC;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAIF;AACA;AACA;AACA;AACA;AACA;AAGA;AAAsD;AACiB;AAGvE;AAEA;AAAoC;AAEhC;AAEE;AAA+B;AAGjC;AAA0C;AAC5C;AACgB;AAOlB;AAAyB;AAErB;AAIA;AAAiC;AACnC;AACgC;AAQlC;AAA6B;AAEzB;AACA;AACA;AAAe;AACjB;AACW;AAQb;AAA4B;AAExB;AACA;AACE;AACE;AAAW;AAEb;AACE;AAAW;AACb;AACF;AACF;AACW;AAOb;AACE;AACE;AAEA;AACA;AAEA;AACA;AAA6C;AAG/C;AACA;AACA;AAAoE;AAGtE;AAGM;AACE;AAAC;AAAA;AACS;AACO;AACJ;AACT;AACyE;AACzE;AACF;AAEA;AAIS;AAEG;AAAsC;AACtC;AAAC;AAAA;AACM;AACK;AAC4B;AAEpC;AACE;AAAsB;AACxB;AACF;AACkC;AACxB;AAEmC;AAAA;AAC/C;AAEH;AAE0G;AAAA;AACrG;AACJ;AAOV;AACqD;AAAA;AAAA;AAEzD;AAEE;AAAA;AAAC;AAAA;AACyC;AACjC;AACQ;AAIV;AAAS;AAAO;AAAI;AACvB;AAAA;AAEJ;AAEE;AAAmD;AAI7C;AAAC;AAAA;AAGG;AACA;AAAkB;AACpB;AAEE;AACA;AACE;AAAoE;AAEpE;AACA;AACA;AAAyC;AAC3C;AACF;AACU;AAGR;AAMA;AACiB;AACnB;AAAA;AA1BuB;AA6B7B;AAEJ;AACF;AAEJ;AAGN;AAEA;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './InlineMultiSelect';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -38,4 +38,5 @@ export { SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, Sideba
|
|
|
38
38
|
export { Combobox } from './components/Combobox/Combobox.js';
|
|
39
39
|
export { DataTable } from './components/DataTable/DataTable.js';
|
|
40
40
|
export { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from './components/Collapsible/Collapsible.js';
|
|
41
|
+
export { InlineMultiSelect } from './components/InlineMultiSelect/InlineMultiSelect.js';
|
|
41
42
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|