lecom-ui 3.8.0 → 3.8.2

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.
@@ -1,55 +1,135 @@
1
- import * as React from 'react';
1
+ import { useState } from 'react';
2
2
  import { useReactTable, getCoreRowModel, getSortedRowModel, flexRender } from '@tanstack/react-table';
3
- import { Table, TableHeader, TableRowHeader, TableHead, TableBody, TableRowBody, TableCell } from '../Table/Table.js';
3
+ import { Pagination } from '../Pagination/Pagination.js';
4
+ import { ScrollArea, ScrollBar } from '../ScrollArea/ScrollArea.js';
5
+ import { Skeleton } from '../Skeleton/Skeleton.js';
4
6
 
5
7
  function DataTable({
8
+ columnFullSize,
9
+ fixedColumns,
10
+ isLoading,
6
11
  columns,
7
12
  data,
8
13
  noResults,
9
- onRowSelectionChange
14
+ pagination,
15
+ vwDiff,
16
+ vhDiff,
17
+ onIsSelected
10
18
  }) {
11
- const [sorting, setSorting] = React.useState([]);
12
- const [rowSelection, setRowSelection] = React.useState({});
19
+ const [sorting, setSorting] = useState([]);
13
20
  const table = useReactTable({
14
21
  data,
15
22
  columns,
16
23
  getCoreRowModel: getCoreRowModel(),
17
24
  onSortingChange: setSorting,
18
25
  getSortedRowModel: getSortedRowModel(),
19
- onRowSelectionChange: setRowSelection,
20
26
  state: {
21
27
  sorting,
22
- rowSelection
28
+ columnPinning: {
29
+ left: fixedColumns || []
30
+ }
23
31
  }
24
32
  });
25
- const getMetaStyle = (header) => {
26
- const meta = header.column.columnDef.meta;
27
- if (!meta) {
28
- return {};
33
+ const styleFixed = (column, elemIndex, elem) => {
34
+ const isPinned = column.getIsPinned();
35
+ if (!isPinned) {
36
+ return {
37
+ width: column.id === columnFullSize ? "100%" : undefined
38
+ };
29
39
  }
30
- return meta["style"] ?? {};
40
+ const selector = elem === "th" ? "thead" : "tbody";
41
+ const columnIndex = column.getIndex();
42
+ const elemBefore = document.querySelector(
43
+ `table#data-table ${selector} tr:nth-child(${elemIndex + 1}) ${elem}:nth-child(${columnIndex})`
44
+ );
45
+ const isLastLeftPinnedColumn = isPinned === "left" && column.getIsLastColumn("left");
46
+ const leftStyleElemBefore = elemBefore?.style?.left ? Number(elemBefore.style.left.replace(/px/g, "")) : 0;
47
+ const widthElemBefore = elemBefore?.getBoundingClientRect()?.width ?? 0;
48
+ const left = leftStyleElemBefore + widthElemBefore;
49
+ const lastRow = table.getRowCount() === elemIndex + 1;
50
+ const boxShadow = elem === "td" ? `-0.5px ${lastRow ? "0px" : "-0.5px"}` : "-0.5px -1.5px";
51
+ return {
52
+ boxShadow: isLastLeftPinnedColumn ? `#c9c9c9 ${boxShadow} 0px 0px inset` : undefined,
53
+ position: "sticky",
54
+ left,
55
+ zIndex: 1,
56
+ width: column.id === columnFullSize ? "100%" : undefined
57
+ };
31
58
  };
32
- React.useEffect(() => {
33
- onRowSelectionChange?.(rowSelection);
34
- }, [rowSelection]);
35
- return /* @__PURE__ */ React.createElement(Table, null, /* @__PURE__ */ React.createElement(TableHeader, null, table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ React.createElement(TableRowHeader, { key: headerGroup.id }, headerGroup.headers.map((header) => /* @__PURE__ */ React.createElement(
36
- TableHead,
37
- {
38
- key: header.id,
39
- style: getMetaStyle(header)
40
- },
41
- header.isPlaceholder ? null : flexRender(
42
- header.column.columnDef.header,
43
- header.getContext()
44
- )
45
- ))))), /* @__PURE__ */ React.createElement(TableBody, null, table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ React.createElement(
46
- TableRowBody,
59
+ const styleDataTableContainer = () => ({
60
+ width: vwDiff ? `calc(100vw - ${vwDiff}px)` : "100%",
61
+ height: vhDiff ? `calc(100vh - ${vhDiff}px)` : "100%"
62
+ });
63
+ const hasPagination = () => {
64
+ if (!pagination) {
65
+ return null;
66
+ }
67
+ return /* @__PURE__ */ React.createElement(Pagination, { ...pagination });
68
+ };
69
+ const arrSkeleton = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
70
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
71
+ "div",
47
72
  {
48
- key: row.id,
49
- "data-state": row.getIsSelected() && "selected"
73
+ className: "bg-white rounded-[8px] p-2 transition-all duration-500",
74
+ style: { width: styleDataTableContainer().width }
50
75
  },
51
- row.getVisibleCells().map((cell) => /* @__PURE__ */ React.createElement(TableCell, { key: cell.id }, flexRender(cell.column.columnDef.cell, cell.getContext())))
52
- )) : /* @__PURE__ */ React.createElement(TableRowBody, null, /* @__PURE__ */ React.createElement(TableCell, { colSpan: columns.length }, noResults ?? /* @__PURE__ */ React.createElement("div", { className: "text-center" }, "No results.")))));
76
+ /* @__PURE__ */ React.createElement(ScrollArea, { type: "always", className: "p-2" }, /* @__PURE__ */ React.createElement(
77
+ "div",
78
+ {
79
+ className: "transition-all duration-500",
80
+ style: { maxHeight: styleDataTableContainer().height }
81
+ },
82
+ isLoading ? /* @__PURE__ */ React.createElement("div", { className: "flex flex-col gap-4" }, arrSkeleton.map((id) => /* @__PURE__ */ React.createElement(
83
+ Skeleton,
84
+ {
85
+ key: id,
86
+ className: "w-full h-[25px] rounded-lg bg-grey-200"
87
+ }
88
+ ))) : /* @__PURE__ */ React.createElement("table", { id: "data-table", className: "w-full" }, /* @__PURE__ */ React.createElement("thead", { className: "sticky top-0 z-10" }, table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ React.createElement("tr", { key: headerGroup.id }, headerGroup.headers.map((header) => /* @__PURE__ */ React.createElement(
89
+ "th",
90
+ {
91
+ key: header.id,
92
+ className: "h-12 px-4 [&:has([role=checkbox])]:pr-0 bg-white shadow-[0_-1.5px_0px_0px_#c9c9c9_inset]",
93
+ style: styleFixed(header.column, 0, "th")
94
+ },
95
+ header.isPlaceholder ? null : flexRender(
96
+ header.column.columnDef.header,
97
+ header.getContext()
98
+ )
99
+ ))))), /* @__PURE__ */ React.createElement("tbody", { className: "[&_tr:last-child]:border-0 [&_tr:last-child_td]:shadow-none" }, table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ React.createElement(
100
+ "tr",
101
+ {
102
+ key: row.id,
103
+ "data-state": !!onIsSelected?.(row) ? "selected" : "",
104
+ className: "[&_td]:hover:bg-grey-100 [&_td]:data-[state=selected]:bg-grey-100"
105
+ },
106
+ row.getVisibleCells().map((cell) => /* @__PURE__ */ React.createElement(
107
+ "td",
108
+ {
109
+ key: cell.id,
110
+ className: "p-4 py-0 h-10 [&:has([role=checkbox])]:pr-0 transition-colors bg-white shadow-[0_-0.5px_0px_0px_#c9c9c9_inset]",
111
+ style: styleFixed(
112
+ cell.column,
113
+ cell.row.index,
114
+ "td"
115
+ )
116
+ },
117
+ flexRender(
118
+ cell.column.columnDef.cell,
119
+ cell.getContext()
120
+ )
121
+ ))
122
+ )) : /* @__PURE__ */ React.createElement("tr", { className: "border-b-[0.5px] [&_td]:hover:bg-grey-100 [&_td]:data-[state=selected]:bg-grey-100" }, /* @__PURE__ */ React.createElement(
123
+ "td",
124
+ {
125
+ colSpan: columns.length,
126
+ className: "p-4 py-0 h-10 align-middle [&:has([role=checkbox])]:pr-0 transition-colors bg-white"
127
+ },
128
+ noResults ?? /* @__PURE__ */ React.createElement("div", { className: "text-center" }, "-")
129
+ ))))
130
+ ), /* @__PURE__ */ React.createElement(ScrollBar, { orientation: "horizontal" }))
131
+ ), hasPagination());
53
132
  }
133
+ DataTable.displayName = "DataTable";
54
134
 
55
135
  export { DataTable };
@@ -4,7 +4,7 @@ import { Button } from '../Button/Button.js';
4
4
  import { Popover, PopoverContent, PopoverTrigger } from '../Popover/Popover.js';
5
5
  import { ScrollArea } from '../ScrollArea/ScrollArea.js';
6
6
  import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../Select/Select.js';
7
- import { useIsMobile } from '../../hooks/use-mobile.js';
7
+ import { useIsMobile } from '../../hooks/useIsMobile.js';
8
8
  import { User } from 'lucide-react';
9
9
  import { i18n } from '../../i18n/index.js';
10
10
  import { Tooltip, TooltipTrigger, TooltipPortal, TooltipContent } from '../Tooltip/Tooltip.js';
@@ -1,18 +1,11 @@
1
1
  import * as React from 'react';
2
+ import { useTranslation } from 'react-i18next';
2
3
  import { Button } from '../Button/Button.js';
3
4
  import { cn } from '../../lib/utils.js';
4
- import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react';
5
+ import { ChevronFirst, ChevronLast, ChevronLeft, ChevronRight, MoreHorizontal, ChevronDown } from 'lucide-react';
6
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '../DropdownMenu/DropdownMenu.js';
7
+ import { Typography } from '../Typography/Typography.js';
5
8
 
6
- const Pagination = ({ className, ...props }) => /* @__PURE__ */ React.createElement(
7
- "nav",
8
- {
9
- role: "navigation",
10
- "aria-label": "pagination",
11
- className: cn("", className),
12
- ...props
13
- }
14
- );
15
- Pagination.displayName = "Pagination";
16
9
  const PaginationContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ React.createElement(
17
10
  "ul",
18
11
  {
@@ -145,5 +138,65 @@ const PaginationIndex = ({
145
138
  children
146
139
  );
147
140
  PaginationIndex.displayName = "PaginationIndex";
141
+ const Pagination = ({
142
+ currentPage,
143
+ itemsPage,
144
+ perPage,
145
+ totalRowsSelected = 0,
146
+ onChangePerPage
147
+ }) => {
148
+ const { t } = useTranslation();
149
+ const perPageOptions = [
150
+ {
151
+ value: 50
152
+ },
153
+ {
154
+ value: 100
155
+ },
156
+ {
157
+ value: 200
158
+ },
159
+ {
160
+ value: 300
161
+ }
162
+ ];
163
+ const mapPaginationItem = (item) => {
164
+ const componentProps = {
165
+ onClick: item.onClick,
166
+ disabled: item.disabled
167
+ };
168
+ if (item.type === "page") {
169
+ return /* @__PURE__ */ React.createElement(
170
+ PaginationIndex,
171
+ {
172
+ isActive: item.page === currentPage,
173
+ ...componentProps
174
+ },
175
+ item.page
176
+ );
177
+ }
178
+ if (item.type.includes("ellipsis")) {
179
+ return /* @__PURE__ */ React.createElement(PaginationEllipsis, { asChild: true });
180
+ }
181
+ const mappedComponents = {
182
+ first: PaginationFirst,
183
+ previous: PaginationPrevious,
184
+ next: PaginationNext,
185
+ last: PaginationLast
186
+ };
187
+ return React.createElement(mappedComponents[item.type], componentProps);
188
+ };
189
+ return /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between mt-6 gap-4" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-4" }, /* @__PURE__ */ React.createElement(DropdownMenu, null, /* @__PURE__ */ React.createElement(DropdownMenuTrigger, { asChild: true }, /* @__PURE__ */ React.createElement(Button, { size: "small", color: "grey", variant: "outlined" }, /* @__PURE__ */ React.createElement("span", { className: "max-md:hidden" }, t("process_list.table.show")), " ", perPage, /* @__PURE__ */ React.createElement(ChevronDown, { size: 20 }))), /* @__PURE__ */ React.createElement(DropdownMenuContent, { side: "top", align: "center" }, perPageOptions.map((option) => /* @__PURE__ */ React.createElement(
190
+ DropdownMenuItem,
191
+ {
192
+ key: option.value,
193
+ onSelect: () => {
194
+ onChangePerPage?.(option.value);
195
+ }
196
+ },
197
+ option.value.toString()
198
+ )))), totalRowsSelected > 0 && /* @__PURE__ */ React.createElement(Typography, { variant: "heading-xxsmall-600", textColor: "text-grey-800" }, totalRowsSelected, " ", t("process_list.table.selected_process"))), /* @__PURE__ */ React.createElement("nav", { role: "navigation", "aria-label": "pagination" }, /* @__PURE__ */ React.createElement(PaginationContent, null, itemsPage.map((item) => /* @__PURE__ */ React.createElement(PaginationItem, { key: item.id }, mapPaginationItem(item))))));
199
+ };
200
+ Pagination.displayName = "Pagination";
148
201
 
149
202
  export { Pagination, PaginationContent, PaginationEllipsis, PaginationFirst, PaginationIndex, PaginationItem, PaginationLast, PaginationNext, PaginationPrevious };
@@ -5,7 +5,7 @@ import { Separator } from '../Separator/Separator.js';
5
5
  import { Sheet, SheetContent } from '../Sheet/Sheet.js';
6
6
  import { Skeleton } from '../Skeleton/Skeleton.js';
7
7
  import { TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '../Tooltip/Tooltip.js';
8
- import { useIsMobile } from '../../hooks/use-mobile.js';
8
+ import { useIsMobile } from '../../hooks/useIsMobile.js';
9
9
  import { cn } from '../../lib/utils.js';
10
10
  import { Slot } from '@radix-ui/react-slot';
11
11
  import { cva } from 'class-variance-authority';
@@ -99,7 +99,6 @@ const SidebarProvider = React.forwardRef(
99
99
  );
100
100
  SidebarProvider.displayName = "SidebarProvider";
101
101
  const Sidebar = React.forwardRef(
102
- // eslint-disable-next-line complexity
103
102
  ({
104
103
  side = "left",
105
104
  variant = "sidebar",
@@ -260,7 +259,7 @@ const SidebarFooter = React.forwardRef(({ className, ...props }, ref) => /* @__P
260
259
  ref,
261
260
  "data-sidebar": "footer",
262
261
  className: cn(
263
- "flex flex-col gap-2 p-2 group-data-[state=collapsed]:opacity-0 transition-all truncate",
262
+ "flex flex-col gap-2 p-2 group-data-[state=collapsed]:opacity-0 transition-all",
264
263
  className
265
264
  ),
266
265
  ...props
@@ -396,7 +395,6 @@ const sidebarMenuButtonVariants = cva(
396
395
  }
397
396
  );
398
397
  const SidebarMenuButton = React.forwardRef(
399
- // eslint-disable-next-line complexity
400
398
  ({
401
399
  asChild = false,
402
400
  isActive = false,
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+
3
+ const MOBILE_BREAKPOINT = 768;
4
+ function useIsMobile() {
5
+ const [isMobile, setIsMobile] = React.useState(
6
+ undefined
7
+ );
8
+ React.useEffect(() => {
9
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
+ const onChange = () => {
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
+ };
13
+ mql.addEventListener("change", onChange);
14
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
15
+ return () => mql.removeEventListener("change", onChange);
16
+ }, []);
17
+ return !!isMobile;
18
+ }
19
+
20
+ export { useIsMobile };
@@ -0,0 +1,97 @@
1
+ import { useState, useMemo } from 'react';
2
+
3
+ const usePagination = ({
4
+ count = 1,
5
+ defaultPage = 1,
6
+ boundaryCount = 1,
7
+ siblingCount = 1,
8
+ disabled = false,
9
+ hideNextButton = false,
10
+ hidePrevButton = false,
11
+ showFirstButton = true,
12
+ showLastButton = true,
13
+ onChange
14
+ } = {}) => {
15
+ const [page, setPage] = useState(defaultPage);
16
+ const handleClick = (value) => {
17
+ if (!disabled && value >= 1 && value <= count) {
18
+ setPage(value);
19
+ if (onChange) {
20
+ onChange(value);
21
+ }
22
+ }
23
+ };
24
+ const range = (start, end) => Array.from({ length: end - start + 1 }, (_, i) => start + i);
25
+ const startPages = range(1, Math.min(boundaryCount, count));
26
+ const endPages = range(
27
+ Math.max(count - boundaryCount + 1, boundaryCount + 1),
28
+ count
29
+ );
30
+ const siblingsStart = Math.max(
31
+ Math.min(page - siblingCount, count - boundaryCount - siblingCount * 2 - 1),
32
+ boundaryCount + 2
33
+ );
34
+ const siblingsEnd = Math.min(
35
+ Math.max(page + siblingCount, boundaryCount + siblingCount * 2 + 2),
36
+ count - boundaryCount - 1
37
+ );
38
+ const itemList = useMemo(
39
+ () => [
40
+ ...showFirstButton ? ["first"] : [],
41
+ ...hidePrevButton ? [] : ["previous"],
42
+ ...startPages,
43
+ ...siblingsStart > boundaryCount + 2 ? ["start-ellipsis"] : boundaryCount + 1 < count - boundaryCount ? [boundaryCount + 1] : [],
44
+ ...range(siblingsStart, siblingsEnd),
45
+ ...siblingsEnd < count - boundaryCount - 1 ? ["end-ellipsis"] : count - boundaryCount > boundaryCount ? [count - boundaryCount] : [],
46
+ ...endPages,
47
+ ...hideNextButton ? [] : ["next"],
48
+ ...showLastButton ? ["last"] : []
49
+ ],
50
+ // eslint-disable-next-line react-hooks/exhaustive-deps
51
+ [
52
+ page,
53
+ count,
54
+ boundaryCount,
55
+ siblingCount,
56
+ showFirstButton,
57
+ showLastButton,
58
+ hidePrevButton,
59
+ hideNextButton
60
+ ]
61
+ );
62
+ const buttonPage = (type) => {
63
+ switch (type) {
64
+ case "first":
65
+ return 1;
66
+ case "previous":
67
+ return page - 1;
68
+ case "next":
69
+ return page + 1;
70
+ case "last":
71
+ return count;
72
+ default:
73
+ return null;
74
+ }
75
+ };
76
+ const items = itemList.map(
77
+ (item, i) => typeof item === "number" ? {
78
+ id: `page_${i}`,
79
+ onClick: () => handleClick(item),
80
+ type: "page",
81
+ page: item,
82
+ selected: item === page,
83
+ disabled,
84
+ "aria-current": item === page ? "page" : undefined
85
+ } : {
86
+ id: `${item}_${i}`,
87
+ onClick: () => handleClick(buttonPage(item)),
88
+ type: item,
89
+ page: buttonPage(item),
90
+ selected: false,
91
+ disabled: disabled || !item.includes("ellipsis") && (item === "next" || item === "last" ? page >= count : page <= 1)
92
+ }
93
+ );
94
+ return { page, setPage, items };
95
+ };
96
+
97
+ export { usePagination };