@kloudlite/design-system
Version:
A design system for building ambitious products.
673 lines (667 loc) • 23 kB
JavaScript
// components/molecule/pagination.tsx
import * as RovingFocusGroup from "@radix-ui/react-roving-focus";
import { useEffect, useId, useRef, useState } from "react";
// components/icons.tsx
import {
BellSimple,
Warning,
WarningCircleFill,
Domain,
ArrowLeftLg,
ArrowRightLg,
ArrowUpLg,
ArrowDownLg,
ArrowsDownUp,
Plus,
Trash,
PencilLine,
PencilSimple,
GithubLogoFill,
GitlabLogoFill,
GitBranchFill,
Users,
Check,
ChevronLeft,
ChevronRight,
X,
SmileySad,
InfoFill,
CheckCircleFill,
WarningFill,
WarningOctagonFill,
LockSimple,
XCircleFill,
LockSimpleOpen,
MinusCircle,
Search,
ArrowsCounterClockwise,
ArrowClockwise,
Copy,
GearSix,
QrCode,
WireGuardlogo,
ChevronUpDown,
ChevronDown,
Buildings,
Project,
InfraAsCode,
Container,
File,
TreeStructure,
CirclesFour,
BackingServices,
VirtualMachine,
Database,
ArrowsClockwise,
Info,
Fan,
WarningCircle,
ChecksFill,
CircleNotch,
Circle,
CircleFill,
Spinner,
Globe,
ShieldCheck,
NoOps,
Nodeless,
GitMerge,
PencilLine as PencilLine2,
AWSlogoFill,
GoogleCloudlogo,
ArrowCounterClockwise,
CopySimple,
RecordFill,
CheckCircle,
ArrowLeftLg as ArrowLeftLg2,
EyeSlash,
Eye,
CaretUpFill,
CaretDownFill,
XFill,
HamburgerFill,
CalendarCheckFill,
GearFill,
EnvelopeSimple
} from "@jengaicons/react";
import { jsx } from "react/jsx-runtime";
// components/atoms/button.tsx
import { AnimatePresence, motion } from "framer-motion";
import React from "react";
// components/utils.tsx
import classNames from "classnames";
import { useMemo } from "react";
import { v4 } from "uuid";
var cn = (...props) => {
return classNames(...props);
};
// components/atoms/button.tsx
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
var ButtonBase = React.forwardRef((props, ref) => {
const {
onClick = () => {
},
to = "",
linkComponent = motion.button,
disabled = false,
suffix,
prefix,
block = false,
type = "button",
variant = "primary",
// noRing,
noRounded = false,
noBorder = false,
sharpLeft = false,
sharpRight = false,
selected = false,
iconOnly = false,
className = "",
content,
size = "md",
loading = false,
tabIndex,
toLabel = "to",
target,
iconSize,
...mprops
} = props;
let Component = linkComponent;
let tempToLabel = toLabel;
let extraProps = {};
if (to) {
if (linkComponent === motion.button) {
Component = motion.a;
tempToLabel = "href";
} else {
Component = linkComponent;
}
}
if (Component === motion.button || Component === motion.a) {
extraProps = {
initial: { scale: 1 },
whileTap: { scale: 0.99 }
};
}
const noRing = false;
return /* @__PURE__ */ jsxs(
Component,
{
...mprops,
...{ [tempToLabel]: to },
disabled,
onClick,
...extraProps,
ref,
type,
tabIndex,
target,
className: cn(
"pulsable kl-flex-nowrap",
{
"kl-w-full": !!block,
"kl-w-fit": !block,
selected
},
{
"kl-pointer-events-none": loading
},
{
"kl-bodyMd-medium": !variant?.includes("plain"),
"kl-bodyMd": variant?.includes("plain")
},
{
"kl-pointer-events-none !kl-text-text-disabled kl-bg-surface-basic-disabled": disabled,
"!kl-border-border-disabled": disabled && ![
"plain",
"primary-plain",
"critical-plain",
"secondary-plain"
].includes(variant)
},
"kl-relative kl-ring-offset-1",
"kl-outline-none",
"kl-flex kl-flex-row kl-gap-lg kl-items-center kl-justify-center",
"disabled:kl-text-text-disabled disabled:kl-bg-surface-basic-disabled",
{
// noRing
"focus-visible:kl-ring-2 focus:kl-ring-border-focus focus:kl-z-10": !noRing
},
{
...!noRounded && {
"kl-rounded-none": sharpLeft && sharpRight,
"kl-rounded-r": sharpLeft && !sharpRight,
"kl-rounded-l": !sharpLeft && sharpRight,
"kl-rounded": !sharpLeft && !sharpRight
}
},
"disabled:kl-pointer-events-none",
{
"kl-border-none": noBorder,
...!noBorder && {
"kl-border-border-default disabled:kl-border-border-disabled": variant === "basic" || variant === "outline" || variant === "secondary-outline",
"kl-border-border-primary disabled:kl-border-border-disabled": variant === "primary" || variant === "primary-outline",
"kl-border-border-secondary disabled:kl-border-border-disabled": variant === "secondary",
"kl-border-border-critical disabled:kl-border-border-disabled": variant === "critical-outline" || variant === "critical",
"kl-border-border-purple": variant === "purple",
"kl-border-border-warning": variant === "warning",
"kl-border-border-tertiary": variant === "tertiary",
"kl-border-none": variant === "plain" || variant === "primary-plain" || variant === "critical-plain" || variant === "secondary-plain",
"kl-border": !(variant === "plain" || variant === "primary-plain" || variant === "critical-plain" || variant === "secondary-plain")
}
},
!disabled ? {
"kl-bg-surface-basic-default hover:kl-bg-surface-basic-hovered active:kl-bg-surface-basic-pressed disabled:kl-bg-surface-basic-default": variant === "basic" && !selected,
"kl-bg-surface-basic-pressed hover:kl-bg-surface-basic-pressed active:kl-bg-surface-basic-pressed disabled:kl-bg-surface-basic-default": variant === "basic" && selected,
"kl-bg-surface-primary-default hover:kl-bg-surface-primary-hovered active:kl-bg-surface-primary-pressed disabled:kl-bg-surface-basic-default": variant === "primary",
"kl-bg-surface-secondary-default hover:kl-bg-surface-secondary-hovered active:kl-bg-surface-secondary-pressed disabled:kl-bg-surface-basic-default": variant === "secondary",
"kl-bg-surface-critical-default hover:kl-bg-surface-critical-hovered active:kl-bg-surface-critical-pressed disabled:kl-bg-surface-basic-default": variant === "critical",
"kl-bg-none hover:kl-bg-surface-critical-subdued active:kl-bg-surface-critical-subdued": variant === "critical-outline",
"kl-bg-none hover:kl-bg-surface-primary-subdued active:kl-bg-surface-primary-subdued": variant === "primary-outline",
"kl-bg-none hover:kl-bg-surface-secondary-subdued active:kl-bg-surface-secondary-subdued": variant === "secondary-outline",
"kl-bg-none hover:kl-bg-surface-basic-hovered active:kl-bg-surface-basic-pressed": variant === "outline",
"kl-bg-surface-basic-pressed kl-shadow-none hover:kl-bg-surface-basic-hovered active:kl-bg-surface-basic-pressed hover:kl-shadow-button": variant === "outline" && selected,
"kl-bg-none kl-shadow-none": (variant === "plain" || variant === "primary-plain" || variant === "secondary-plain" || variant === "critical-plain") && !iconOnly,
"kl-shadow-none active:kl-shadow-button kl-bg-surface-basic-pressed": variant === "plain" && !iconOnly && selected,
"kl-bg-none kl-shadow-none hover:kl-bg-surface-basic-hovered active:kl-bg-surface-basic-pressed active:kl-shadow-button active:kl-shadow-button": variant === "plain" && iconOnly,
"kl-bg-surface-basic-pressed kl-shadow-none hover:kl-bg-surface-basic-hovered active:kl-bg-surface-basic-pressed active:kl-shadow-button": variant === "plain" && iconOnly && selected,
"kl-bg-surface-purple-default hover:kl-bg-surface-purple-hovered active:kl-bg-surface-purple-pressed": variant === "purple",
"kl-bg-surface-tertiary-default hover:kl-bg-surface-tertiary-hovered active:kl-bg-surface-tertiary-pressed": variant === "tertiary",
"kl-bg-surface-warning-default hover:kl-bg-surface-warning-hovered active:kl-bg-surface-warning-pressed": variant === "warning"
} : {},
{
"kl-text-text-default": variant === "basic" || variant === "plain" || variant === "outline",
"kl-text-text-on-primary": variant === "primary" || variant === "critical" || variant === "secondary" || variant === "secondary-outline" || variant === "purple" || variant === "warning",
"kl-text-text-critical": variant === "critical-outline" || variant === "critical-plain",
"kl-text-text-primary": variant === "primary-outline" || variant === "primary-plain",
"kl-text-text-secondary": variant === "secondary-plain",
"kl-text-text-on-secondary": variant === "tertiary"
},
{
"focus:kl-underline": noRing
},
{
"hover:kl-underline": variant === "plain" || variant === "primary-plain" || variant === "critical-plain" || variant === "secondary-plain"
},
{
// icon
...!iconOnly && !(variant === "plain" || variant === "primary-plain" || variant === "critical-plain" || variant === "secondary-plain") && {
"kl-py-md kl-px-lg": size === "sm",
"kl-py-lg kl-px-2xl": size === "md",
"kl-py-xl kl-px-4xl": size === "lg",
"kl-py-2xl kl-px-6xl": size === "xl",
"kl-py-2xl kl-px-9xl": size === "2xl"
}
},
{
...!iconOnly && (variant === "plain" || variant === "primary-plain" || variant === "critical-plain" || variant === "secondary-plain") && {
"kl-px-md kl-py-sm": size === "sm",
"kl-py-sm kl-px-md": size === "md",
"kl-py-md kl-px-lg": size === "lg"
}
},
{
"kl-p-lg !kl-h-[36px] !kl-w-[36px]": iconOnly && (size === "md" || size === "lg"),
"kl-p-md": iconOnly && size === "sm",
"kl-p-sm": iconOnly && size === "xs"
},
className
),
children: [
/* @__PURE__ */ jsx2(AnimatePresence, { children: loading && /* @__PURE__ */ jsx2(
motion.span,
{
initial: { width: 0 },
animate: { width: "auto", paddingRight: 0 },
exit: { width: 0 },
className: cn(
"kl-flex kl-items-center kl-justify-center kl-aspect-square kl-overflow-hidden"
),
children: /* @__PURE__ */ jsx2("span", { className: cn("kl-animate-spin"), children: /* @__PURE__ */ jsx2(Spinner, { color: "currentColor", weight: 2, size: 18 }) })
}
) }),
!!prefix && React.cloneElement(prefix, {
size: iconSize || (iconOnly && size === "lg" ? 20 : 16),
color: "currentColor"
}),
!iconOnly && /* @__PURE__ */ jsx2("span", { className: cn("kl-block kl-truncate"), children: content }),
!!suffix && React.cloneElement(suffix, {
size: iconSize || 16,
color: "currentColor"
})
]
}
);
});
var IconButton = React.forwardRef(
(props, ref) => {
const { icon, block } = props;
return /* @__PURE__ */ jsx2(
ButtonBase,
{
...props,
ref,
iconOnly: true,
content: null,
prefix: icon,
block: !!block
}
);
}
);
var Button = React.forwardRef(
(props, ref) => {
const { block } = props;
return /* @__PURE__ */ jsx2(ButtonBase, { ...props, iconOnly: false, ref, block: !!block });
}
);
// components/molecule/pagination.tsx
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
var usePagination = ({
items,
itemsPerPage
}) => {
const [listItems, setListItems] = useState(items);
const [page, setPage] = useState();
const [pageNumber, setPageNumber] = useState(1);
const [hasNext, setHasNext] = useState(false);
const [hasPrevious, setHasPrevious] = useState(false);
useEffect(() => {
if (listItems.length > 0) {
let tempItems = listItems.slice(
(pageNumber - 1) * itemsPerPage,
pageNumber * itemsPerPage
);
if (tempItems.length === 0) {
tempItems = listItems.slice(
(Math.ceil(listItems.length / itemsPerPage) - 1) * itemsPerPage,
listItems.length
);
setPageNumber((prev) => prev - 1);
}
setPage(tempItems);
} else {
setPageNumber(1);
}
}, [listItems]);
useEffect(() => {
if (pageNumber * itemsPerPage >= listItems.length) {
setHasNext(false);
} else {
setHasNext(true);
}
if (pageNumber * itemsPerPage > itemsPerPage) {
setHasPrevious(true);
} else {
setHasPrevious(false);
}
}, [page]);
const onNext = () => {
if (pageNumber < Math.ceil(listItems.length / itemsPerPage)) {
setPage(
listItems.slice(
pageNumber * itemsPerPage,
(pageNumber + 1) * itemsPerPage
)
);
setPageNumber((prev) => prev + 1);
}
};
const onPrev = () => {
if (pageNumber > 1) {
setPage(
listItems.slice(
(pageNumber - 1 - 1) * itemsPerPage,
(pageNumber - 1) * itemsPerPage
)
);
setPageNumber((prev) => prev - 1);
}
};
const onPageChange = () => {
};
const setPageNumberExt = (extpage) => {
if (extpage <= Math.ceil(listItems.length / itemsPerPage)) {
setPage(
listItems.slice(
(extpage - 1) * itemsPerPage,
extpage * itemsPerPage
)
);
setPageNumber(extpage);
}
};
return {
page: page || [],
pageNumber,
hasNext,
hasPrevious,
onNext,
onPrev,
onPageChange,
setPageNumber: setPageNumberExt,
setItems: setListItems,
items: listItems,
itemsPerPage
};
};
var ITEMS_PER_PAGE = [
1,
5,
10,
15,
20,
25,
30,
35,
40,
45,
50,
55,
60,
65,
70,
75,
80,
85,
90,
95,
100
];
var Pagination = ({
currentPage = 3,
totalItems = 90,
itemsPerPage = 15,
onPageChanged = () => {
},
onItemsPerPageChanged = () => {
},
disabled = false,
itemPerPageDisabled = false,
onClickNext = () => {
},
onClickPrev = () => {
},
isNextDisabled = false,
isPrevDisabled = false,
showNumbers = true,
showItemsPerPage = true
}) => {
const [focusItem, setFocusItem] = useState(null);
const [focusCallback, setFocusCallback] = useState(false);
const [itemsPerPageValue, setItemsPerPageValue] = useState(itemsPerPage);
const [startPages, setStartPages] = useState([]);
const [middlePages, setMiddlePages] = useState([]);
const [endPages, setEndPages] = useState([]);
const totalPages = Math.ceil(totalItems / itemsPerPage);
const itemsPerPageId = useId();
const ref = useRef(null);
useEffect(() => {
if (onItemsPerPageChanged)
onItemsPerPageChanged(itemsPerPageValue);
}, [itemsPerPageValue]);
useEffect(() => {
if (currentPage > totalPages) {
onPageChanged(totalPages);
}
if (totalPages < 7) {
setStartPages(
new Array(totalPages).fill(0).map((value, index) => index + 1)
);
setMiddlePages([]);
setEndPages([]);
return;
}
if (currentPage <= 3) {
setStartPages([1, 2, 3, 4]);
setMiddlePages([]);
setEndPages([totalPages]);
} else if (currentPage > 3 && currentPage <= totalPages - 3) {
setStartPages([1]);
setMiddlePages([currentPage - 1, currentPage, currentPage + 1]);
setEndPages([totalPages]);
} else {
setStartPages([1]);
setMiddlePages([]);
setEndPages([totalPages - 3, totalPages - 2, totalPages - 1, totalPages]);
}
setFocusCallback(true);
}, [currentPage, itemsPerPage, totalItems]);
useEffect(() => {
if (focusCallback) {
if (focusItem && ref?.current?.children) {
const itemsArray = Array.from(
ref.current?.children
);
if (itemsArray.find((e) => e.value === `${focusItem}`)) {
itemsArray?.find((e) => e.value === `${focusItem}`)?.focus();
} else {
const divElement = itemsArray?.find(
(e) => e.tagName.toLowerCase() === "div"
);
if (divElement) {
const divElementArray = Array.from(
divElement.children
);
if (divElementArray) {
divElementArray.find((e) => e.value === `${focusItem}`)?.focus();
}
}
}
}
}
setFocusItem(null);
setFocusCallback(false);
}, [focusCallback]);
const restoreFocus = (index) => {
setFocusItem(index);
};
return /* @__PURE__ */ jsxs2(
"div",
{
className: cn(
"kl-flex kl-flex-row kl-items-center kl-gap-3xl kl-w-full",
{
"kl-justify-end": !showItemsPerPage
}
),
children: [
showItemsPerPage && /* @__PURE__ */ jsxs2("div", { className: "kl-flex kl-flex-row kl-items-center kl-flex-1 kl-gap-lg kl-text-icon-default kl-bodyMd", children: [
/* @__PURE__ */ jsx3("label", { htmlFor: itemsPerPageId, children: "Item per page" }),
/* @__PURE__ */ jsx3(
"select",
{
name: "itemperpage",
id: itemsPerPageId,
disabled: itemPerPageDisabled,
value: itemsPerPageValue,
onChange: (e) => {
setItemsPerPageValue(Number(e.target.value));
},
className: cn(
"kl-py-md kl-pl-lg kl-pr-5xl kl-text-text-default kl-border-border-default kl-bg-surface-basic-input kl-transition-all kl-rounded kl-border kl-flex kl-flex-row kl-items-center kl-relative kl-outline-none disabled:kl-bg-surface-basic-input disabled:kl-text-text-disabled kl-ring-offset-1 focus-within:kl-ring-2 focus-within:kl-ring-border-focus kl-appearance-none",
{
"kl-text-text-disabled kl-border-border-disabled kl-bg-surface-basic-input": disabled
}
),
children: ITEMS_PER_PAGE.map((ipp) => /* @__PURE__ */ jsx3("option", { value: ipp, children: ipp }, ipp))
}
),
showNumbers ? /* @__PURE__ */ jsxs2("span", { children: [
currentPage * itemsPerPage - itemsPerPage + 1,
" -",
" ",
currentPage * itemsPerPage,
" of ",
totalItems,
" items"
] }) : /* @__PURE__ */ jsxs2("span", { children: [
" total ",
totalItems,
" items"
] })
] }),
/* @__PURE__ */ jsx3(RovingFocusGroup.Root, { loop: true, children: /* @__PURE__ */ jsxs2("div", { className: "kl-flex kl-flex-row kl-items-center kl-gap-xl", children: [
/* @__PURE__ */ jsx3(RovingFocusGroup.Item, { asChild: true, focusable: true, children: /* @__PURE__ */ jsx3(
Button,
{
content: "Previous",
prefix: /* @__PURE__ */ jsx3(ChevronLeft, {}),
variant: "plain",
onClick: () => {
if (onPageChanged) {
onPageChanged(currentPage - 1);
}
onClickPrev();
},
disabled: showNumbers && currentPage < 2 || disabled || isPrevDisabled
}
) }),
showNumbers && /* @__PURE__ */ jsxs2(
"div",
{
className: "kl-flex kl-flex-row kl-items-center kl-gap-lg",
ref,
children: [
startPages.map((sP) => /* @__PURE__ */ jsx3(RovingFocusGroup.Item, { asChild: true, focusable: true, children: /* @__PURE__ */ jsx3(
Button,
{
content: sP.toString().padStart(2, "0"),
variant: "plain",
selected: sP === currentPage,
onClick: () => onPageChanged && onPageChanged(sP),
disabled,
value: sP,
onKeyDown: (e) => {
if (e.key === " " || e.key === "Enter") {
restoreFocus(sP);
}
}
}
) }, sP)),
/* @__PURE__ */ jsxs2("div", { className: "kl-flex kl-flex-row kl-items-center kl-gap-lg", children: [
middlePages.length > 0 && /* @__PURE__ */ jsx3("span", { className: "kl-bodyMd kl-text-text-default", children: "....." }),
middlePages.map((mP) => /* @__PURE__ */ jsx3(RovingFocusGroup.Item, { asChild: true, focusable: true, children: /* @__PURE__ */ jsx3(
Button,
{
content: mP.toString().padStart(2, "0"),
variant: "plain",
selected: mP === currentPage,
onClick: () => onPageChanged && onPageChanged(mP),
disabled,
value: mP,
onKeyDown: (e) => {
if (e.key === " " || e.key === "Enter") {
restoreFocus(mP);
}
}
},
mP
) }, mP)),
totalPages >= 7 && /* @__PURE__ */ jsx3("span", { className: "kl-bodyMd kl-text-text-default", children: "....." })
] }),
endPages.map((eP) => /* @__PURE__ */ jsx3(RovingFocusGroup.Item, { asChild: true, focusable: true, children: /* @__PURE__ */ jsx3(
Button,
{
content: eP.toString().padStart(2, "0"),
variant: "plain",
selected: eP === currentPage,
onClick: () => onPageChanged && onPageChanged(eP),
disabled,
value: eP,
onKeyDown: (e) => {
if (e.key === " " || e.key === "Enter") {
restoreFocus(eP);
}
}
},
eP
) }, eP))
]
}
),
/* @__PURE__ */ jsx3(RovingFocusGroup.Item, { asChild: true, focusable: true, children: /* @__PURE__ */ jsx3(
Button,
{
content: "Next",
suffix: /* @__PURE__ */ jsx3(ChevronRight, {}),
variant: "plain",
onClick: () => {
if (onPageChanged) {
onPageChanged(currentPage + 1);
}
onClickNext();
},
disabled: showNumbers && currentPage >= totalPages || disabled || isNextDisabled
}
) })
] }) })
]
}
);
};
var pagination_default = Pagination;
export {
pagination_default as default,
usePagination
};