@helpwave/hightide
Version:
helpwave's component and theming library
370 lines (359 loc) • 14.9 kB
JavaScript
// src/components/Table.tsx
import { Scrollbars } from "react-custom-scrollbars-2";
import { useEffect as useEffect3, useRef, useState as useState4 } from "react";
// src/util/noop.ts
var noop = () => void 0;
// src/components/user-input/Checkbox.tsx
import { useState } from "react";
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
import { Check, Minus } from "lucide-react";
import clsx from "clsx";
// src/components/user-input/Label.tsx
import { jsx } from "react/jsx-runtime";
var styleMapping = {
labelSmall: "textstyle-label-sm",
labelMedium: "textstyle-label-md",
labelBig: "textstyle-label-lg"
};
var Label = ({
children,
name,
labelType = "labelSmall",
...props
}) => {
return /* @__PURE__ */ jsx("label", { ...props, children: children ? children : /* @__PURE__ */ jsx("span", { className: styleMapping[labelType], children: name }) });
};
// src/components/user-input/Checkbox.tsx
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
var checkboxSizeMapping = {
small: "size-4",
medium: "size-6",
large: "size-8"
};
var checkboxIconSizeMapping = {
small: "size-3",
medium: "size-5",
large: "size-7"
};
var ControlledCheckbox = ({
id,
label,
checked,
disabled,
onChange,
onChangeTristate,
size = "medium",
className = "",
containerClassName
}) => {
const usedSizeClass = checkboxSizeMapping[size];
const innerIconSize = checkboxIconSizeMapping[size];
const propagateChange = (checked2) => {
if (onChangeTristate) {
onChangeTristate(checked2);
}
if (onChange) {
onChange(checked2 === "indeterminate" ? false : checked2);
}
};
const changeValue = () => {
const newValue = checked === "indeterminate" ? false : !checked;
propagateChange(newValue);
};
return /* @__PURE__ */ jsxs("div", { className: clsx("row justify-center items-center", containerClassName), children: [
/* @__PURE__ */ jsx2(
CheckboxPrimitive.Root,
{
onCheckedChange: propagateChange,
checked,
disabled,
id,
className: clsx(usedSizeClass, `items-center border-2 rounded outline-none focus:border-primary`, {
"text-disabled-text border-disabled-text": disabled,
"border-on-background": !disabled,
"bg-primary/30 border-primary text-primary": checked === true || checked === "indeterminate",
"hover:border-gray-400 focus:hover:border-primary": !checked
}, className),
children: /* @__PURE__ */ jsxs(CheckboxPrimitive.Indicator, { children: [
checked === true && /* @__PURE__ */ jsx2(Check, { className: innerIconSize }),
checked === "indeterminate" && /* @__PURE__ */ jsx2(Minus, { className: innerIconSize })
] })
}
),
label && /* @__PURE__ */ jsx2(Label, { ...label, className: clsx("cursor-pointer", label.className), htmlFor: id, onClick: changeValue })
] });
};
// src/components/Pagination.tsx
import { ChevronLast, ChevronLeft, ChevronFirst, ChevronRight } from "lucide-react";
import clsx2 from "clsx";
// src/hooks/useLanguage.tsx
import { createContext, useContext, useEffect as useEffect2, useState as useState3 } from "react";
// src/hooks/useLocalStorage.tsx
import { useCallback, useEffect, useState as useState2 } from "react";
// src/hooks/useLanguage.tsx
import { jsx as jsx3 } from "react/jsx-runtime";
var DEFAULT_LANGUAGE = "en";
var LanguageContext = createContext({ language: DEFAULT_LANGUAGE, setLanguage: (v) => v });
var useLanguage = () => useContext(LanguageContext);
// src/hooks/useTranslation.ts
var useTranslation = (defaults, translationOverwrite = {}) => {
const { language: languageProp, translation: overwrite } = translationOverwrite;
const { language: inferredLanguage } = useLanguage();
const usedLanguage = languageProp ?? inferredLanguage;
let defaultValues = defaults[usedLanguage];
if (overwrite && overwrite[usedLanguage]) {
defaultValues = { ...defaultValues, ...overwrite[usedLanguage] };
}
return defaultValues;
};
// src/components/Pagination.tsx
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
var defaultPaginationTranslations = {
en: {
of: "of"
},
de: {
of: "von"
}
};
var Pagination = ({
overwriteTranslation,
page,
numberOfPages,
onPageChanged
}) => {
const translation = useTranslation(defaultPaginationTranslations, overwriteTranslation);
const changePage = (page2) => {
onPageChanged(page2);
};
const noPages = numberOfPages === 0;
const onFirstPage = page === 0 && !noPages;
const onLastPage = page === numberOfPages - 1;
return /* @__PURE__ */ jsxs2("div", { className: clsx2("row", { "opacity-30": noPages }), children: [
/* @__PURE__ */ jsx4("button", { onClick: () => changePage(0), disabled: onFirstPage, children: /* @__PURE__ */ jsx4(ChevronFirst, { className: clsx2({ "opacity-30": onFirstPage }) }) }),
/* @__PURE__ */ jsx4("button", { onClick: () => changePage(page - 1), disabled: onFirstPage, children: /* @__PURE__ */ jsx4(ChevronLeft, { className: clsx2({ "opacity-30": onFirstPage }) }) }),
/* @__PURE__ */ jsxs2("div", { className: "min-w-[80px] justify-center mx-2", children: [
/* @__PURE__ */ jsx4("span", { className: "select-none text-right flex-1", children: noPages ? 0 : page + 1 }),
/* @__PURE__ */ jsx4("span", { className: "select-none mx-2", children: translation.of }),
/* @__PURE__ */ jsx4("span", { className: "select-none text-left flex-1", children: numberOfPages })
] }),
/* @__PURE__ */ jsx4("button", { onClick: () => changePage(page + 1), disabled: onLastPage || noPages, children: /* @__PURE__ */ jsx4(ChevronRight, { className: clsx2({ "opacity-30": onLastPage }) }) }),
/* @__PURE__ */ jsx4("button", { onClick: () => changePage(numberOfPages - 1), disabled: onLastPage || noPages, children: /* @__PURE__ */ jsx4(ChevronLast, { className: clsx2({ "opacity-30": onLastPage }) }) })
] });
};
// src/components/Table.tsx
import clsx3 from "clsx";
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
var defaultTableStatePagination = {
currentPage: 0,
entriesPerPage: 5
};
var defaultTableStateSelection = {
currentSelection: [],
hasSelectedAll: false,
hasSelectedSome: false,
hasSelectedNone: true
};
var isDataObjectSelected = (tableState, dataObject, identifierMapping) => {
if (!tableState.selection) {
return false;
}
return !!tableState.selection.currentSelection.find((value) => value.localeCompare(identifierMapping(dataObject)) === 0);
};
var pageForItem = (data, item, entriesPerPage, identifierMapping) => {
const index = data.findIndex((value) => identifierMapping(value) === identifierMapping(item));
if (index !== -1) {
return Math.floor(index / entriesPerPage);
}
console.warn("item doesn't exist on data", item, data);
return 0;
};
var updatePagination = (pagination, dataLength) => ({
...pagination,
currentPage: Math.min(Math.max(Math.ceil(dataLength / pagination.entriesPerPage) - 1, 0), pagination.currentPage)
});
var addElementToTable = (tableState, data, dataObject, identifierMapping) => {
return {
...tableState,
pagination: tableState.pagination ? {
...tableState.pagination,
currentPage: pageForItem(data, dataObject, tableState.pagination.entriesPerPage, identifierMapping)
} : void 0,
selection: tableState.selection ? {
...tableState.selection,
hasSelectedAll: false,
hasSelectedSome: tableState.selection.hasSelectedAll || tableState.selection.hasSelectedSome
} : void 0
};
};
var removeFromTableSelection = (tableState, deletedObjects, dataLength, identifierMapping) => {
if (!tableState.selection) {
return tableState;
}
const deletedObjectIds = deletedObjects.map(identifierMapping);
const elementsBefore = tableState.selection.currentSelection.length;
const currentSelection = tableState.selection.currentSelection.filter((value) => !deletedObjectIds.includes(value));
dataLength -= elementsBefore - currentSelection.length;
return {
...tableState,
selection: {
currentSelection,
hasSelectedAll: currentSelection.length === dataLength && dataLength !== 0,
hasSelectedSome: currentSelection.length > 0 && currentSelection.length !== dataLength,
hasSelectedNone: currentSelection.length === 0
},
pagination: tableState.pagination ? updatePagination(tableState.pagination, dataLength) : void 0
};
};
var changeTableSelectionSingle = (tableState, dataObject, dataLength, identifierMapping) => {
if (!tableState.selection) {
return tableState;
}
const hasSelectedObject = isDataObjectSelected(tableState, dataObject, identifierMapping);
let currentSelection = [...tableState.selection.currentSelection, identifierMapping(dataObject)];
if (hasSelectedObject) {
currentSelection = tableState.selection.currentSelection.filter((value) => value.localeCompare(identifierMapping(dataObject)) !== 0);
}
return {
...tableState,
selection: {
currentSelection,
hasSelectedAll: currentSelection.length === dataLength,
hasSelectedSome: currentSelection.length > 0 && currentSelection.length !== dataLength,
hasSelectedNone: currentSelection.length === 0
}
};
};
var changeTableSelectionAll = (tableState, data, identifierMapping) => {
if (!tableState.selection) {
return tableState;
}
if (data.length === 0) {
return {
...tableState,
selection: {
currentSelection: [],
hasSelectedAll: false,
hasSelectedSome: false,
hasSelectedNone: true
}
};
}
const hasSelectedAll = !(tableState.selection.hasSelectedSome || tableState.selection.hasSelectedAll);
return {
...tableState,
selection: {
currentSelection: hasSelectedAll ? data.map(identifierMapping) : [],
hasSelectedAll,
hasSelectedSome: false,
hasSelectedNone: !hasSelectedAll
}
};
};
var Table = ({
data,
stateManagement = [{}, noop],
identifierMapping,
header,
rowMappingToCells,
sorting,
focusElement,
className
}) => {
const sortedData = [...data];
if (sorting) {
const [sortingFunction, sortingType] = sorting;
sortedData.sort((a, b) => sortingFunction(a, b) * (sortingType === "ascending" ? 1 : -1));
}
let currentPage = 0;
let pageCount = 1;
let entriesPerPage = 5;
const [tableState, updateTableState] = stateManagement;
let shownElements = sortedData;
if (tableState?.pagination) {
if (tableState.pagination.entriesPerPage < 1) {
console.error("tableState.pagination.entriesPerPage must be >= 1", tableState.pagination.entriesPerPage);
}
entriesPerPage = Math.max(1, tableState.pagination.entriesPerPage);
pageCount = Math.ceil(sortedData.length / entriesPerPage);
if (tableState.pagination.currentPage < 0 || tableState.pagination.currentPage >= pageCount && pageCount !== 0) {
console.error(
"tableState.pagination.currentPage < 0 || (tableState.pagination.currentPage >= pageCount && pageCount !== 0) must be fullfilled",
[`pageCount: ${pageCount}`, `tableState.pagination.currentPage: ${tableState.pagination.currentPage}`]
);
} else {
currentPage = tableState.pagination.currentPage;
}
if (focusElement) {
currentPage = pageForItem(sortedData, focusElement, entriesPerPage, identifierMapping);
}
shownElements = sortedData.slice(currentPage * entriesPerPage, Math.min(sortedData.length, (currentPage + 1) * entriesPerPage));
} else {
currentPage = 0;
}
const headerRow = "border-b-2 border-gray-300";
const headerPaddingHead = "pb-2";
const headerPaddingBody = "pt-2";
const cellPadding = "py-1 px-2";
const [scrollbarsAutoHeightMin, setScrollbarsAutoHeightMin] = useState4(0);
const tableRef = useRef(null);
const calculateHeight = () => {
if (tableRef.current) {
const tableHeight = tableRef.current.offsetHeight;
const offset = 25;
setScrollbarsAutoHeightMin(tableHeight + offset);
}
};
useEffect3(() => {
calculateHeight();
const handleResize = () => {
calculateHeight();
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [data, currentPage]);
return /* @__PURE__ */ jsxs3("div", { className: clsx3("col gap-y-4 overflow-hidden", className), children: [
/* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsx5(Scrollbars, { autoHeight: true, autoHeightMin: scrollbarsAutoHeightMin, children: /* @__PURE__ */ jsxs3("table", { ref: tableRef, className: "w-full mb-[12px]", children: [
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs3("tr", { className: headerRow, children: [
header && tableState.selection && /* @__PURE__ */ jsx5("th", { className: headerPaddingHead, children: /* @__PURE__ */ jsx5(
ControlledCheckbox,
{
checked: tableState.selection.hasSelectedSome ? "indeterminate" : tableState.selection.hasSelectedAll,
onChange: () => updateTableState(changeTableSelectionAll(tableState, data, identifierMapping))
}
) }),
header && header.map((value, index) => /* @__PURE__ */ jsx5("th", { className: headerPaddingHead, children: /* @__PURE__ */ jsx5("div", { className: "row justify-start px-2", children: value }) }, `tableHeader${index}`))
] }) }),
/* @__PURE__ */ jsx5("tbody", { children: shownElements.map((value, rowIndex) => /* @__PURE__ */ jsxs3("tr", { children: [
tableState.selection && /* @__PURE__ */ jsx5("td", { className: clsx3(cellPadding, { [headerPaddingBody]: rowIndex === 0 }), children: /* @__PURE__ */ jsx5(
ControlledCheckbox,
{
checked: isDataObjectSelected(tableState, value, identifierMapping),
onChange: () => {
updateTableState(changeTableSelectionSingle(tableState, value, data.length, identifierMapping));
}
}
) }),
rowMappingToCells(value).map((value1, index) => /* @__PURE__ */ jsx5("td", { className: clsx3(cellPadding, { [headerPaddingBody]: rowIndex === 0 }), children: value1 }, index))
] }, identifierMapping(value))) })
] }) }) }),
/* @__PURE__ */ jsx5("div", { className: "row justify-center", children: tableState.pagination && /* @__PURE__ */ jsx5(Pagination, { page: currentPage, numberOfPages: pageCount, onPageChanged: (page) => updateTableState({
...tableState,
pagination: { entriesPerPage, currentPage: page }
}) }) })
] });
};
export {
Table,
addElementToTable,
changeTableSelectionSingle,
defaultTableStatePagination,
defaultTableStateSelection,
isDataObjectSelected,
pageForItem,
removeFromTableSelection,
updatePagination
};
//# sourceMappingURL=Table.mjs.map