uibee 2.9.3 → 2.10.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/dist/src/components/buttons/button.d.ts +2 -2
- package/dist/src/components/buttons/button.js +10 -4
- package/dist/src/components/table/body.d.ts +15 -0
- package/dist/src/components/table/body.js +116 -0
- package/dist/src/components/table/format.d.ts +1 -0
- package/dist/src/components/table/format.js +27 -0
- package/dist/src/components/table/header.d.ts +8 -0
- package/dist/src/components/table/header.js +40 -0
- package/dist/src/components/table/menu.d.ts +17 -0
- package/dist/src/components/table/menu.js +30 -0
- package/dist/src/components/table/pagination.d.ts +6 -0
- package/dist/src/components/table/pagination.js +86 -0
- package/dist/src/components/table/table.d.ts +14 -0
- package/dist/src/components/table/table.js +14 -0
- package/dist/src/globals.css +310 -5
- package/dist/src/hooks/useClickOutside.d.ts +2 -1
- package/dist/src/hooks/useClickOutside.js +21 -14
- package/eslint.config.js +0 -3
- package/package.json +16 -17
- package/src/components/buttons/button.tsx +12 -5
- package/src/components/table/body.tsx +186 -0
- package/src/components/table/format.ts +35 -0
- package/src/components/table/header.tsx +86 -0
- package/src/components/table/menu.tsx +76 -0
- package/src/components/table/pagination.tsx +179 -0
- package/src/components/table/table.tsx +44 -0
- package/src/hooks/useClickOutside.ts +21 -15
- package/src/types/components.d.ts +32 -0
|
@@ -5,9 +5,9 @@ type ButtonProps = {
|
|
|
5
5
|
icon: string | JSX.Element;
|
|
6
6
|
path?: string;
|
|
7
7
|
type?: 'button' | 'submit' | 'reset';
|
|
8
|
-
|
|
8
|
+
variant?: 'primary' | 'secondary' | 'warning' | 'danger' | 'success' | 'info';
|
|
9
9
|
onClick?: (_: object | string) => void;
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
};
|
|
12
|
-
export default function Button({ text, className, icon, path,
|
|
12
|
+
export default function Button({ text, className, icon, path, variant, type, onClick, disabled }: ButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
13
13
|
export {};
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import Link from 'next/link';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
const variants = {
|
|
4
|
+
primary: 'bg-login/70 outline-login hover:bg-login/90',
|
|
5
|
+
secondary: 'bg-login-500/70 outline-login-500 hover:bg-login-500/90',
|
|
6
|
+
warning: 'bg-yellow-500/70 outline-yellow-500 hover:bg-yellow-500/90',
|
|
7
|
+
danger: 'bg-red-600/70 outline-red-600 hover:bg-red-600/90',
|
|
8
|
+
success: 'bg-green-600/70 outline-green-600 hover:bg-green-600/90',
|
|
9
|
+
info: 'bg-blue-600/70 outline-blue-600 hover:bg-blue-600/90'
|
|
10
|
+
};
|
|
11
|
+
export default function Button({ text, className, icon, path, variant = 'primary', type, onClick, disabled }) {
|
|
12
|
+
const bg = variants[variant];
|
|
7
13
|
if (!path) {
|
|
8
14
|
return (_jsxs("button", { type: type || 'button', disabled: disabled, onClick: onClick, "aria-label": text, className: `
|
|
9
15
|
${bg} cursor-pointer px-4 rounded-md min-h-8 h-8 flex
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Column } from 'uibee/components';
|
|
3
|
+
type BodyProps = {
|
|
4
|
+
list: object[];
|
|
5
|
+
columns: Column[];
|
|
6
|
+
menuItems?: (data: object, id: string) => React.ReactNode;
|
|
7
|
+
redirectPath?: string | {
|
|
8
|
+
path: string;
|
|
9
|
+
key?: string;
|
|
10
|
+
};
|
|
11
|
+
variant?: 'default' | 'minimal';
|
|
12
|
+
idKey?: string;
|
|
13
|
+
};
|
|
14
|
+
export default function Body({ list, columns, menuItems, redirectPath, variant, idKey }: BodyProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { EllipsisVertical } from 'lucide-react';
|
|
3
|
+
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { useState, useRef, useEffect } from 'react';
|
|
5
|
+
import useClickOutside from '../../hooks/useClickOutside';
|
|
6
|
+
import Menu from './menu';
|
|
7
|
+
import { formatValue } from './format';
|
|
8
|
+
export default function Body({ list, columns, menuItems, redirectPath, variant = 'default', idKey }) {
|
|
9
|
+
const [openMenuId, setOpenMenuId] = useState(null);
|
|
10
|
+
const [anchor, setAnchor] = useState(null);
|
|
11
|
+
const router = useRouter();
|
|
12
|
+
const menuRef = useRef(null);
|
|
13
|
+
const tbodyRef = useRef(null);
|
|
14
|
+
const menuWasOpenOnMouseDown = useRef(false);
|
|
15
|
+
useClickOutside(menuRef, () => setOpenMenuId(null));
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const el = tbodyRef.current;
|
|
18
|
+
if (!el)
|
|
19
|
+
return;
|
|
20
|
+
const close = () => setOpenMenuId(null);
|
|
21
|
+
el.addEventListener('scroll', close);
|
|
22
|
+
return () => el.removeEventListener('scroll', close);
|
|
23
|
+
}, []);
|
|
24
|
+
return (_jsx("tbody", { ref: tbodyRef, className: `
|
|
25
|
+
divide-login-600 block overflow-y-auto flex-1 min-h-0
|
|
26
|
+
${variant === 'default' ? 'bg-login-500/50 divide-y' : 'bg-transparent divide-y'}
|
|
27
|
+
`, children: list.map((item, index) => {
|
|
28
|
+
const itemRecord = item;
|
|
29
|
+
let id = '';
|
|
30
|
+
if (idKey && itemRecord[idKey] !== undefined) {
|
|
31
|
+
id = String(itemRecord[idKey]);
|
|
32
|
+
}
|
|
33
|
+
else if (itemRecord['id'] !== undefined) {
|
|
34
|
+
id = String(itemRecord['id']);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
const firstKey = columns[0]?.key || Object.keys(itemRecord)[0];
|
|
38
|
+
id = String(itemRecord[firstKey]);
|
|
39
|
+
}
|
|
40
|
+
const redirectConfig = (typeof redirectPath === 'object' && redirectPath) ? redirectPath : { path: redirectPath };
|
|
41
|
+
const redirectId = redirectConfig.key ? String(itemRecord[redirectConfig.key]) : id;
|
|
42
|
+
const menuButtonColors = variant === 'minimal'
|
|
43
|
+
? {
|
|
44
|
+
active: 'bg-login-600 text-login-100',
|
|
45
|
+
inactive: 'hover:bg-login-600 text-login-200 hover:text-login-100'
|
|
46
|
+
}
|
|
47
|
+
: {
|
|
48
|
+
active: 'bg-login-400 text-login-100',
|
|
49
|
+
inactive: 'hover:bg-login-400 text-login-200 hover:text-login-100'
|
|
50
|
+
};
|
|
51
|
+
const buttonClass = openMenuId === id ? menuButtonColors.active : menuButtonColors.inactive;
|
|
52
|
+
return (_jsxs("tr", { className: `
|
|
53
|
+
flex w-full group/row transition-colors
|
|
54
|
+
${redirectConfig.path ? 'cursor-pointer' : ''}
|
|
55
|
+
${variant === 'default' && redirectConfig.path ? 'hover:bg-login-600/30' : ''}
|
|
56
|
+
${variant === 'minimal' ? 'hover:bg-white/5 border-b border-login-600/50 last:border-0' : ''}
|
|
57
|
+
`, onMouseDown: () => {
|
|
58
|
+
menuWasOpenOnMouseDown.current = openMenuId !== null;
|
|
59
|
+
}, onClick: () => {
|
|
60
|
+
if (menuWasOpenOnMouseDown.current) {
|
|
61
|
+
menuWasOpenOnMouseDown.current = false;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (redirectConfig.path) {
|
|
65
|
+
if (redirectConfig.path.includes('?')) {
|
|
66
|
+
router.push(`${redirectConfig.path}${redirectId}`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
router.push(`${redirectConfig.path}/${redirectId}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}, onContextMenu: (e) => {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
setAnchor({ top: e.clientY, right: window.innerWidth - e.clientX });
|
|
75
|
+
setOpenMenuId(id);
|
|
76
|
+
}, children: [columns.map((col) => {
|
|
77
|
+
const val = itemRecord[col.key];
|
|
78
|
+
const value = val;
|
|
79
|
+
let badgeClass = '';
|
|
80
|
+
if (col.highlight) {
|
|
81
|
+
const highlightKey = String(value);
|
|
82
|
+
const colorName = col.highlight[highlightKey] || col.highlight.default;
|
|
83
|
+
if (colorName) {
|
|
84
|
+
switch (colorName) {
|
|
85
|
+
case 'green':
|
|
86
|
+
badgeClass = 'bg-green-500/20 text-green-400';
|
|
87
|
+
break;
|
|
88
|
+
case 'yellow':
|
|
89
|
+
badgeClass = 'bg-yellow-500/20 text-yellow-400';
|
|
90
|
+
break;
|
|
91
|
+
case 'red':
|
|
92
|
+
badgeClass = 'bg-red-500/20 text-red-400';
|
|
93
|
+
break;
|
|
94
|
+
case 'blue':
|
|
95
|
+
badgeClass = 'bg-blue-500/20 text-blue-400';
|
|
96
|
+
break;
|
|
97
|
+
case 'gray':
|
|
98
|
+
badgeClass = 'bg-gray-500/20 text-gray-400';
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
badgeClass += ' px-2 py-1 rounded';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return (_jsx("td", { className: `
|
|
105
|
+
flex-1 px-6 py-4 whitespace-nowrap text-sm min-w-40 flex items-center text-login-100
|
|
106
|
+
${variant === 'minimal' ? 'py-3' : ''}
|
|
107
|
+
`, children: _jsx("div", { className: 'relative', children: _jsx("h1", { className: badgeClass, children: formatValue(col.key, value) }) }) }, col.key));
|
|
108
|
+
}), menuItems && (_jsx("td", { className: 'shrink-0 w-16 flex flex-row justify-end p-2 px-4\n whitespace-nowrap text-right text-sm font-medium', children: _jsxs("div", { className: 'relative', children: [_jsx("button", { type: 'button', className: `p-1.5 rounded flex items-start justify-center transition-colors ${buttonClass}`, onMouseDown: (e) => e.nativeEvent.stopImmediatePropagation(), onClick: (e) => {
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
111
|
+
const coords = { top: rect.bottom + 4, right: window.innerWidth - rect.right };
|
|
112
|
+
setAnchor(openMenuId === id ? null : coords);
|
|
113
|
+
setOpenMenuId(openMenuId === id ? null : id);
|
|
114
|
+
}, children: _jsx("span", { className: 'text-xl leading-none select-none', children: _jsx(EllipsisVertical, { className: 'h-5 w-5' }) }) }), openMenuId === id && anchor && (_jsx(Menu, { ref: menuRef, anchor: anchor, onClose: () => setOpenMenuId(null), children: menuItems?.(item, id) }))] }) }))] }, index));
|
|
115
|
+
}) }));
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatValue(key: string, value: string | number): string | number;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const nullableTimeKeys = ['date', 'last_sent', 'time'];
|
|
2
|
+
const ISODateTimeReg = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/;
|
|
3
|
+
const ISODateReg = /^\d{4}-\d{2}-\d{2}$/;
|
|
4
|
+
const ISOTimeReg = /^\d{2}:\d{2}(:\d{2})?$/;
|
|
5
|
+
const OsloTZ = { timeZone: 'Europe/Oslo', second: undefined };
|
|
6
|
+
const OsloTime = { ...OsloTZ, hour: '2-digit', minute: '2-digit' };
|
|
7
|
+
const OsloDateTime = { ...OsloTime, year: 'numeric', month: '2-digit', day: '2-digit' };
|
|
8
|
+
export function formatValue(key, value) {
|
|
9
|
+
if (nullableTimeKeys.includes(key) && !value) {
|
|
10
|
+
return 'Never';
|
|
11
|
+
}
|
|
12
|
+
if (typeof value === 'string') {
|
|
13
|
+
if (ISODateTimeReg.test(value)) {
|
|
14
|
+
return new Date(value).toLocaleString('nb-NO', OsloDateTime);
|
|
15
|
+
}
|
|
16
|
+
if (ISODateReg.test(value)) {
|
|
17
|
+
return new Date(value).toLocaleDateString('nb-NO', OsloTZ);
|
|
18
|
+
}
|
|
19
|
+
if (ISOTimeReg.test(value)) {
|
|
20
|
+
return new Date(`1970-01-01T${value}`).toLocaleTimeString('nb-NO', OsloTime);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
if (key.includes('capacity')) {
|
|
24
|
+
return value === 0 ? 'Unlimited' : value;
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Column } from 'uibee/components';
|
|
2
|
+
type HeaderProps = {
|
|
3
|
+
columns: Column[];
|
|
4
|
+
hideMenu?: boolean;
|
|
5
|
+
variant?: 'default' | 'minimal';
|
|
6
|
+
};
|
|
7
|
+
export default function Header({ columns, hideMenu, variant }: HeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown, ChevronUp } from 'lucide-react';
|
|
3
|
+
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
4
|
+
import { useEffect, useState } from 'react';
|
|
5
|
+
export default function Header({ columns, hideMenu, variant = 'default' }) {
|
|
6
|
+
const [column, setColumn] = useState(columns[0]?.key || '');
|
|
7
|
+
const [order, setOrder] = useState('asc');
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
const pathname = usePathname();
|
|
10
|
+
const searchParams = useSearchParams();
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
13
|
+
if (searchParams.get('order') !== order ||
|
|
14
|
+
searchParams.get('column') !== column) {
|
|
15
|
+
params.set('order', order);
|
|
16
|
+
params.set('column', column);
|
|
17
|
+
params.set('page', '1');
|
|
18
|
+
router.push(`${pathname}?${params.toString()}`);
|
|
19
|
+
}
|
|
20
|
+
}, [order, column, pathname, router, searchParams]);
|
|
21
|
+
function handleChange(key) {
|
|
22
|
+
setColumn(key);
|
|
23
|
+
setOrder((prev) => (key === column && prev === 'asc' ? 'desc' : 'asc'));
|
|
24
|
+
}
|
|
25
|
+
return (_jsx("thead", { className: `
|
|
26
|
+
block w-full
|
|
27
|
+
${variant === 'default' ? 'bg-login-700' : 'bg-transparent border-b border-login-600'}
|
|
28
|
+
`, children: _jsxs("tr", { className: 'flex w-full divide-x divide-transparent', children: [columns.map((col) => {
|
|
29
|
+
const key = col.key;
|
|
30
|
+
const value = col.label || (key.length < 3
|
|
31
|
+
? key.toUpperCase()
|
|
32
|
+
: `${key[0].toUpperCase()}${key
|
|
33
|
+
.slice(1)
|
|
34
|
+
.replaceAll('_', ' ')}`);
|
|
35
|
+
return (_jsx("th", { className: `
|
|
36
|
+
flex-1 px-6 py-3 text-xs font-medium uppercase tracking-wider text-left
|
|
37
|
+
${variant === 'default' ? 'text-login-200' : 'text-login-100'}
|
|
38
|
+
`, children: _jsxs("button", { className: 'flex flex-row items-center gap-2 group uppercase', onClick: () => handleChange(key), children: [value, _jsx("span", { className: 'flex flex-col', children: column === key ? (order === 'asc' ? (_jsx(ChevronUp, { className: 'h-4 w-4' })) : (_jsx(ChevronDown, { className: 'h-4 w-4' }))) : (_jsx(ChevronUp, { className: 'h-4 w-4 stroke-login-200 opacity-0 group-hover:opacity-100' })) })] }) }, key));
|
|
39
|
+
}), !hideMenu && _jsx("th", { className: 'shrink-0 w-16 px-6 py-3' })] }) }));
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export default function Menu({ ref, children, anchor, onClose }: {
|
|
3
|
+
ref: React.RefObject<HTMLDivElement | null>;
|
|
4
|
+
children: React.ReactNode;
|
|
5
|
+
anchor: {
|
|
6
|
+
top: number;
|
|
7
|
+
right: number;
|
|
8
|
+
};
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
}): React.ReactPortal;
|
|
11
|
+
export declare function MenuButton({ icon, text, hotKey, onClick, className, }: {
|
|
12
|
+
icon: React.ReactNode;
|
|
13
|
+
text: string;
|
|
14
|
+
hotKey?: string;
|
|
15
|
+
onClick: () => void;
|
|
16
|
+
className?: string;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { createContext, useContext, useEffect } from 'react';
|
|
4
|
+
import { createPortal } from 'react-dom';
|
|
5
|
+
const MenuContext = createContext({});
|
|
6
|
+
export default function Menu({ ref, children, anchor, onClose }) {
|
|
7
|
+
return createPortal(_jsx("div", { ref: ref, style: { top: anchor.top, right: anchor.right }, className: 'fixed bg-login-500 border border-login-600 rounded-lg shadow-lg z-9999 w-44', children: _jsx(MenuContext.Provider, { value: { onClose }, children: children }) }), document.body);
|
|
8
|
+
}
|
|
9
|
+
export function MenuButton({ icon, text, hotKey, onClick, className, }) {
|
|
10
|
+
const { onClose } = useContext(MenuContext);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!hotKey)
|
|
13
|
+
return;
|
|
14
|
+
function handleKeyDown(e) {
|
|
15
|
+
if (e.key.toLowerCase() === hotKey.toLowerCase()) {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
onClick();
|
|
18
|
+
onClose?.();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
22
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
23
|
+
}, [hotKey, onClick, onClose]);
|
|
24
|
+
return (_jsxs("button", { onClick: () => {
|
|
25
|
+
onClick();
|
|
26
|
+
onClose?.();
|
|
27
|
+
}, className: `flex items-center justify-between w-full px-3 py-2 text-sm hover:bg-login-600 cursor-pointer
|
|
28
|
+
${className || ''}
|
|
29
|
+
`, children: [_jsxs("div", { className: 'flex items-center', children: [React.cloneElement(icon, { className: 'w-4 h-4 mr-2' }), text] }), _jsx("span", { className: 'text-xs opacity-50 font-mono', children: hotKey })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
|
4
|
+
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
export default function Pagination({ pageSize = 10, totalRows, }) {
|
|
7
|
+
const router = useRouter();
|
|
8
|
+
const pathname = usePathname();
|
|
9
|
+
const searchParams = useSearchParams();
|
|
10
|
+
const rawPage = parseInt(searchParams.get('page') || '1', 10);
|
|
11
|
+
const initialPage = Math.max(1, Number.isNaN(rawPage) ? 1 : rawPage);
|
|
12
|
+
const [current, setCurrent] = useState(initialPage);
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
const raw = parseInt(searchParams.get('page') || '1', 10);
|
|
15
|
+
const p = Math.max(1, Number.isNaN(raw) ? 1 : raw);
|
|
16
|
+
const computedTotalPages = Math.max(1, Math.ceil(totalRows !== undefined && pageSize > 0
|
|
17
|
+
? totalRows / pageSize
|
|
18
|
+
: 1));
|
|
19
|
+
setCurrent(Math.max(1, Math.min(computedTotalPages, p)));
|
|
20
|
+
}, [searchParams, totalRows, pageSize]);
|
|
21
|
+
function updateQuery(p) {
|
|
22
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
23
|
+
params.set('page', String(p));
|
|
24
|
+
router.push(`${pathname}?${params.toString()}`);
|
|
25
|
+
}
|
|
26
|
+
function goPrevious() {
|
|
27
|
+
if (current <= 1)
|
|
28
|
+
return;
|
|
29
|
+
const next = current - 1;
|
|
30
|
+
setCurrent(next);
|
|
31
|
+
updateQuery(next);
|
|
32
|
+
}
|
|
33
|
+
const totalPages = Math.max(1, Math.ceil(totalRows !== undefined && pageSize > 0
|
|
34
|
+
? totalRows / pageSize
|
|
35
|
+
: 1));
|
|
36
|
+
function goNext() {
|
|
37
|
+
if (current >= totalPages)
|
|
38
|
+
return;
|
|
39
|
+
const next = current + 1;
|
|
40
|
+
setCurrent(next);
|
|
41
|
+
updateQuery(next);
|
|
42
|
+
}
|
|
43
|
+
function setPage(p) {
|
|
44
|
+
if (p === current)
|
|
45
|
+
return;
|
|
46
|
+
setCurrent(p);
|
|
47
|
+
updateQuery(p);
|
|
48
|
+
}
|
|
49
|
+
function getPages(curr, total) {
|
|
50
|
+
const delta = 2;
|
|
51
|
+
const left = Math.max(1, curr - delta);
|
|
52
|
+
const right = Math.min(total, curr + delta);
|
|
53
|
+
const pages = [];
|
|
54
|
+
if (left > 1) {
|
|
55
|
+
pages.push(1);
|
|
56
|
+
if (left > 2)
|
|
57
|
+
pages.push('...');
|
|
58
|
+
}
|
|
59
|
+
for (let i = left; i <= right; i++)
|
|
60
|
+
pages.push(i);
|
|
61
|
+
if (right < total) {
|
|
62
|
+
if (right < total - 1)
|
|
63
|
+
pages.push('...');
|
|
64
|
+
pages.push(total);
|
|
65
|
+
}
|
|
66
|
+
return pages;
|
|
67
|
+
}
|
|
68
|
+
const pages = getPages(current, totalPages);
|
|
69
|
+
const start = Math.max(1, (current - 1) * pageSize + 1);
|
|
70
|
+
const end = Math.min(current * pageSize, totalRows !== undefined ? totalRows : current * pageSize);
|
|
71
|
+
return (_jsxs("div", { className: 'flex items-center justify-between w-full pt-4', children: [_jsx("div", { className: 'text-sm /70', children: totalRows !== undefined ? (totalRows === 0 ? (_jsx("span", { children: "Showing 0 results" })) : (_jsxs("span", { children: ["Showing ", start, " to ", end, " of ", totalRows, " results"] }))) : null }), _jsxs("div", { className: 'flex items-center gap-3', children: [_jsx("button", { type: 'button', onClick: goPrevious, disabled: current <= 1, className: `
|
|
72
|
+
flex items-center gap-2 p-1 rounded-lg
|
|
73
|
+
bg-login-50/5 hover:bg-login-500 disabled:opacity-50
|
|
74
|
+
border-[0.10rem] border-login-200 text-sm
|
|
75
|
+
`, children: _jsx(ChevronLeft, { className: 'h-5 w-5' }) }), _jsx("nav", { className: 'flex items-center gap-1', "aria-label": 'Pagination', children: pages.map((p, i) => typeof p === 'string' ? (_jsx("span", { className: 'px-3 py-1 text-sm', children: p }, `e-${i}`)) : (_jsx("button", { type: 'button', onClick: () => setPage(p), "aria-current": p === current ? 'page' : undefined, className: `
|
|
76
|
+
px-3 py-1 rounded-lg text-sm
|
|
77
|
+
border-[0.10rem] ${p === current
|
|
78
|
+
? 'bg-login-50/5 border-login-50'
|
|
79
|
+
: `bg-login-50/0 border-login-200
|
|
80
|
+
'hover:bg-login-400`}
|
|
81
|
+
`, children: p }, p))) }), _jsx("button", { type: 'button', onClick: goNext, disabled: current >= totalPages, className: `
|
|
82
|
+
flex items-center gap-2 p-1 rounded-lg bg-login-50/5
|
|
83
|
+
hover:bg-login-500 disabled:opacity-50 select-none
|
|
84
|
+
border-[0.10rem] border-login-200 text-sm
|
|
85
|
+
`, children: _jsx(ChevronRight, { className: 'h-5 w-5' }) })] })] }));
|
|
86
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Column } from 'uibee/components';
|
|
2
|
+
type TableProps = {
|
|
3
|
+
data: object[];
|
|
4
|
+
columns: Column[];
|
|
5
|
+
menuItems?: (data: object, id: string) => React.ReactNode;
|
|
6
|
+
redirectPath?: string | {
|
|
7
|
+
path: string;
|
|
8
|
+
key?: string;
|
|
9
|
+
};
|
|
10
|
+
variant?: 'default' | 'minimal';
|
|
11
|
+
idKey?: string;
|
|
12
|
+
};
|
|
13
|
+
export default function Table({ data, columns, menuItems, redirectPath, variant, idKey }: TableProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import Body from './body';
|
|
4
|
+
import Header from './header';
|
|
5
|
+
export default function Table({ data, columns, menuItems, redirectPath, variant = 'default', idKey }) {
|
|
6
|
+
if (data.length === 0) {
|
|
7
|
+
return _jsx("div", { className: 'p-4 text-center text-login-200', children: "No data found" });
|
|
8
|
+
}
|
|
9
|
+
return (_jsx("div", { className: `
|
|
10
|
+
flex-1 flex flex-col min-h-0 overflow-x-auto h-full w-full
|
|
11
|
+
${variant === 'default' ? 'bg-login-500/50 rounded-lg shadow border border-login-600' : ''}
|
|
12
|
+
${variant === 'minimal' ? 'bg-transparent' : ''}
|
|
13
|
+
`, children: _jsxs("table", { className: 'min-w-full w-max divide-y divide-login-600 flex flex-col flex-1 min-h-0', children: [_jsx(Header, { columns: columns, hideMenu: !menuItems, variant: variant }), _jsx(Body, { list: data, columns: columns, menuItems: menuItems, redirectPath: redirectPath, variant: variant, idKey: idKey })] }) }));
|
|
14
|
+
}
|