@knowmax/genericlist-fluentuiv9
Version:
Knowmax Generic list with basic CRUD support with Fluent V9 user interface implementation.
1,089 lines (1,086 loc) • 42.1 kB
JavaScript
import { observer } from 'mobx-react-lite';
import { makeStyles, mergeClasses } from '@griffel/react';
import { tokens } from '@fluentui/tokens';
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { useState, useEffect, useCallback, Fragment as Fragment$1, useRef, useMemo } from 'react';
import { Text, Title3 } from '@fluentui/react-text';
import { useGenericListSettings, getParamId, PARAM_ORDER, parseOrderExpression, FilterValueType, FilterOperator, fieldIdForOperator, PARAM_PAGE, PARAM_DELETED } from '@knowmax/genericlist-core';
import { Spinner } from '@fluentui/react-spinner';
import { MessageBar, MessageBarBody, MessageBarTitle } from '@fluentui/react-message-bar';
import { makeStyles as makeStyles$1, ToggleButton, tokens as tokens$1, Caption1Strong, Dropdown, Option, Menu, MenuTrigger, SplitButton, MenuPopover, MenuList, MenuItem, RadioGroup, Radio, Tooltip, MenuButton } from '@fluentui/react-components';
import { ChevronRight24Regular, TextSortDescendingRegular, TextSortAscendingRegular, ArrowSortDownRegular, ArrowSortUpRegular, DismissRegular, SearchRegular, Filter28Filled, AddRegular, ArrowClockwiseRegular, EqualCircleRegular, EqualOffRegular, ChevronRightRegular, ChevronLeftRegular, TextBulletListLtrRegular, GridRegular, TableRegular, SquareMultipleRegular } from '@fluentui/react-icons';
import { List as List$1 } from 'react-window';
import { AutoSizer } from 'react-virtualized-auto-sizer';
import { Button } from '@fluentui/react-button';
import { Field } from '@fluentui/react-field';
import { Input } from '@fluentui/react-input';
import { Switch } from '@fluentui/react-switch';
import { Checkbox } from '@fluentui/react-checkbox';
import { Pager } from '@knowmax/pager-fluentuiv9';
// src/components/list/ListPanel.tsx
var useClasses = makeStyles({
root: {
display: "flex",
flexDirection: "column",
rowGap: tokens.spacingVerticalS
},
loading: {
backgroundColor: tokens.colorNeutralBackground3
}
});
var ListPanel = observer(({ loading, children, className }) => {
const classes = useClasses();
return /* @__PURE__ */ jsx("div", { className: mergeClasses(classes.root, loading && classes.loading, className), children });
});
var useClasses2 = makeStyles({
root: {
padding: tokens.spacingHorizontalS,
boxShadow: tokens.shadow8
}
});
var ListItemPanel = observer(({ children, className }) => {
const classes = useClasses2();
return /* @__PURE__ */ jsx("div", { className: mergeClasses(classes.root, className), children });
});
var useClasses3 = makeStyles({
root: {
display: "flex",
justifyContent: "center",
alignItems: "center",
padding: tokens.spacingHorizontalL
},
spinner: {
position: "absolute"
}
});
var Loader = ({ size }) => {
const classes = useClasses3();
return /* @__PURE__ */ jsx("div", { className: classes.root, children: /* @__PURE__ */ jsx(Spinner, { className: classes.spinner, size: size === "sm" ? "tiny" : size === "lg" ? "huge" : "medium" }) });
};
var ErrorMessage = ({ error, title }) => {
const genericlistsettings = useGenericListSettings();
const [message, setMessage] = useState();
useEffect(() => {
if (typeof error === "string") {
setMessage(error);
} else if (error) {
genericlistsettings.onErrorMessage(error).then((m) => setMessage(m));
}
}, [error, genericlistsettings]);
return /* @__PURE__ */ jsx(MessageBar, { intent: "error", role: "alert", children: /* @__PURE__ */ jsxs(MessageBarBody, { children: [
title && /* @__PURE__ */ jsx(MessageBarTitle, { children: title }),
message
] }) });
};
var useClasses4 = makeStyles$1({
root: {
"& .toggle-icon": {
transformOrigin: "center",
transition: "transform 200ms cubic-bezier(0.4, 0, 0.2, 1)"
},
'&[aria-pressed="true"] .toggle-icon': {
transform: "rotate(90deg)"
},
'&[aria-pressed="false"] .toggle-icon': {
transform: "rotate(0deg)"
}
}
});
var ToggleOpenButton = observer(({ value = false, disabled, onToggle }) => {
const [open, setOpen] = useState(value);
const classes = useClasses4();
useEffect(() => {
setOpen(value);
}, [value]);
const toggle = useCallback(() => {
if (onToggle) {
onToggle();
}
setOpen((o) => !o);
}, [onToggle]);
return /* @__PURE__ */ jsx(
ToggleButton,
{
appearance: "transparent",
className: classes.root,
disabled,
checked: open,
icon: /* @__PURE__ */ jsx(ChevronRight24Regular, { className: "toggle-icon" }),
onClick: toggle
}
);
});
// src/types/language.ts
var GROUP_PLACEHOLDER = "placeholder";
var GROUP_LABEL = "label";
var GROUP_LAYOUT = "layout";
var LANGUAGE_PLACEHOLDERSEARCH = GROUP_PLACEHOLDER + ".search";
var LANGUAGE_LABELNORESULTS = GROUP_LABEL + ".noresults";
var LANGUAGE_LABELRESULTCOUNT = GROUP_LABEL + ".resultcount";
var LANGUAGE_LABELRESET = GROUP_LABEL + ".reset";
var LANGUAGE_LABELRECYCLEBIN = GROUP_LABEL + ".recyclebin";
var LANGUAGE_LAYOUTLIST = GROUP_LAYOUT + ".list";
var LANGUAGE_LAYOUTGRID = GROUP_LAYOUT + ".grid";
var LANGUAGE_LAYOUTCOMPACT = GROUP_LAYOUT + ".compact";
var LANGUAGE_LAYOUTCARD = GROUP_LAYOUT + ".card";
var useClasses5 = makeStyles({
listroot: {
display: "flex",
flexDirection: "column",
rowGap: tokens.spacingVerticalM
},
grid: {
display: "grid",
gridTemplateColumns: "repeat(auto-fill, minmax(280px, 1fr))",
gap: tokens.spacingVerticalM
},
compact: {
rowGap: tokens.spacingVerticalXS
},
card: {
display: "grid",
gridTemplateColumns: "repeat(3, 1fr)",
gap: tokens.spacingVerticalM
}
});
var layoutClassMap = (classes) => ({
grid: classes.grid,
compact: classes.compact,
card: classes.card
});
var List = observer(({ list, onRenderListItem, showPager = true, layout = "list", className }) => {
const classes = useClasses5();
const settings = useGenericListSettings();
return /* @__PURE__ */ jsxs(Fragment, { children: [
list.error && /* @__PURE__ */ jsx(ErrorMessage, { error: list.error }),
(!list.loaded || list.list.length === 0) && list.loading && /* @__PURE__ */ jsx(Loader, {}),
list.loaded && !list.loading && list.list.length === 0 && /* @__PURE__ */ jsx(Text, { children: settings.onTranslate(LANGUAGE_LABELNORESULTS) }),
/* @__PURE__ */ jsxs("div", { className: mergeClasses(classes.listroot, className), children: [
list.loaded && /* @__PURE__ */ jsx(ListPanel, { loading: list.loading, className: layoutClassMap(classes)[layout], children: list.list.map((item, index) => /* @__PURE__ */ jsx(Fragment$1, { children: onRenderListItem(item, index) }, index)) }),
showPager && /* @__PURE__ */ jsx(PagerWrapper, { list })
] })
] });
});
var ITEM_HEIGHT_BY_LAYOUT = {
list: 90,
compact: 64,
grid: 90,
card: 90
};
var RowRenderer = ({ index, style, ariaAttributes, items, onRenderListItem }) => {
const item = items[index];
return /* @__PURE__ */ jsx("div", { style, ...ariaAttributes, children: item !== void 0 && onRenderListItem(item, index) });
};
var useClasses6 = makeStyles({
root: {
display: "flex",
flexDirection: "column",
rowGap: tokens.spacingVerticalS
},
listContainer: {
flex: "1 1 auto",
overflow: "hidden"
},
loadingFooter: {
display: "flex",
justifyContent: "center",
padding: tokens.spacingVerticalS
}
});
var VirtualizedList = observer(({
list,
onRenderListItem,
itemHeight,
height,
layout = "list",
overscanCount = 3,
className
}) => {
const effectiveItemHeight = itemHeight ?? ITEM_HEIGHT_BY_LAYOUT[layout];
const classes = useClasses6();
const settings = useGenericListSettings();
const [allItems, setAllItems] = useState([]);
const processedPageRef = useRef(0);
const configHash = `${list.filter ?? ""}|${list.orderExpression}|${list.pageSize}`;
const isRequestingMoreRef = useRef(false);
useEffect(() => {
list.setPage(1);
processedPageRef.current = 0;
setAllItems([]);
isRequestingMoreRef.current = false;
list.load();
}, [list, configHash]);
useEffect(() => {
if (!list.loaded || list.loading) return;
const incomingPage = list.page;
if (incomingPage === 1) {
setAllItems(list.list.slice());
processedPageRef.current = 1;
} else if (incomingPage > processedPageRef.current) {
setAllItems((prev) => [...prev, ...list.list.slice()]);
processedPageRef.current = incomingPage;
} else {
processedPageRef.current = 0;
isRequestingMoreRef.current = false;
setAllItems([]);
list.setPage(1);
list.load();
return;
}
isRequestingMoreRef.current = false;
}, [list.loading, list.page, list.loaded]);
const loadMore = useCallback(() => {
if (list.loading || isRequestingMoreRef.current || allItems.length >= list.totalCount) return;
isRequestingMoreRef.current = true;
list.setPage(list.page + 1);
list.load();
}, [list, allItems.length]);
const handleRowsRendered = useCallback(
(_visibleRows, allRows) => {
if (allRows.stopIndex >= allItems.length - 1 - overscanCount) {
loadMore();
}
},
[allItems.length, overscanCount, loadMore]
);
const hasItems = allItems.length > 0;
const isInitialLoading = (!list.loaded || allItems.length === 0) && list.loading;
const isEmpty = list.loaded && !list.loading && allItems.length === 0;
const isLoadingMore = list.loading && hasItems;
const rowProps = {
items: allItems,
onRenderListItem
};
return /* @__PURE__ */ jsxs("div", { className: mergeClasses(classes.root, className), children: [
list.error && /* @__PURE__ */ jsx(ErrorMessage, { error: list.error }),
isInitialLoading && /* @__PURE__ */ jsx(Loader, {}),
isEmpty && /* @__PURE__ */ jsx(Text, { children: settings.onTranslate(LANGUAGE_LABELNORESULTS) }),
hasItems && /* @__PURE__ */ jsx("div", { style: height !== void 0 ? { height } : void 0, className: classes.listContainer, children: height !== void 0 ? /* @__PURE__ */ jsx(
List$1,
{
rowComponent: RowRenderer,
rowCount: allItems.length,
rowHeight: effectiveItemHeight,
rowProps,
overscanCount,
onRowsRendered: handleRowsRendered,
style: { height: "100%", width: "100%" }
}
) : /* @__PURE__ */ jsx(
AutoSizer,
{
renderProp: ({ height: autoHeight, width }) => /* @__PURE__ */ jsx(
List$1,
{
rowComponent: RowRenderer,
rowCount: allItems.length,
rowHeight: effectiveItemHeight,
rowProps,
overscanCount,
onRowsRendered: handleRowsRendered,
style: { height: autoHeight ?? "100%", width: width ?? "100%" }
}
)
}
) }),
isLoadingMore && /* @__PURE__ */ jsx("div", { className: classes.loadingFooter, children: /* @__PURE__ */ jsx(Loader, { size: "sm" }) })
] });
});
var useClasses7 = makeStyles({
root: {
display: "flex",
flexDirection: "column",
minWidth: 0,
padding: tokens$1.spacingHorizontalS,
backgroundColor: tokens$1.colorBrandBackground2,
boxShadow: tokens$1.shadow8,
position: "sticky",
overflowX: "hidden",
transitionProperty: "row-gap",
transitionDuration: "350ms",
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)"
},
rootOpen: {
rowGap: tokens$1.spacingHorizontalM
},
rootClosed: {
rowGap: "0px"
},
head: {
display: "flex",
alignItems: "center",
justifyContent: "space-between"
},
headCollapsible: {
cursor: "pointer"
},
contents: {
display: "flex",
flexDirection: "column",
rowGap: tokens$1.spacingHorizontalL,
overflow: "hidden",
transitionProperty: "max-height, opacity, transform",
transitionDuration: "350ms",
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)"
},
contentsOpen: {
maxHeight: "2000px",
// Large enough to accommodate content
opacity: "1",
transform: "translateY(0px)"
},
contentsClosed: {
maxHeight: "0px",
opacity: "0",
transform: "translateY(-8px)"
}
});
var SidePanel = observer(({
children,
caption,
showResetFilters = true,
showRecycleBinFilter = false,
collapsible = false,
collapsed = false,
list,
listId,
className,
captionClassName
}) => {
const [open, setOpen] = useState(!collapsed || !collapsible);
const classes = useClasses7();
const toggle = useCallback(() => {
if (collapsible) {
setOpen((o) => !o);
}
}, [collapsible]);
return /* @__PURE__ */ jsxs("div", { className: mergeClasses(
classes.root,
open ? classes.rootOpen : classes.rootClosed,
className
), children: [
/* @__PURE__ */ jsxs("section", { className: mergeClasses(classes.head, collapsible && classes.headCollapsible), onClick: toggle, "aria-expanded": collapsible ? open : void 0, children: [
caption && /* @__PURE__ */ jsx(Caption1Strong, { className: captionClassName, children: caption }),
collapsible && /* @__PURE__ */ jsx(ToggleOpenButton, { value: open })
] }),
/* @__PURE__ */ jsxs("section", { className: mergeClasses(
classes.contents,
open ? classes.contentsOpen : classes.contentsClosed
), children: [
showResetFilters && /* @__PURE__ */ jsx(ResetFilters, { list, listId }),
showRecycleBinFilter && /* @__PURE__ */ jsx(RecycleBinFilter, { listId }),
children
] })
] });
});
var useClasses8 = makeStyles({
dropdown: {
width: "100%",
minWidth: 0
},
dropdownButton: {
minWidth: 0,
width: "100%"
},
withIcon: {
display: "flex",
alignItems: "center",
columnGap: "6px"
}
});
var OrderSelector = observer(({ list, listId }) => {
const classes = useClasses8();
const settings = useGenericListSettings();
const selectedkey = useMemo(() => {
const ordervalue = settings.params.get(getParamId(PARAM_ORDER, listId));
if (ordervalue) {
const [field, descending] = parseOrderExpression(ordervalue);
const order = list.find((order2) => keyForOrder(order2.field, order2.descending) === keyForOrder(field, descending));
return order ? keyForOrder(order.field, order.descending) : void 0;
} else {
const order = list.find((order2) => order2.default === true);
return order ? keyForOrder(order.field, order.descending) : void 0;
}
}, [settings.params, list, listId]);
const options = useMemo(() => {
return list.filter((order) => !order.isAvailable || order.isAvailable(order, settings.params, listId)).map((order) => ({
key: keyForOrder(order.field, order.descending),
text: order.name,
data: order
}));
}, [list, settings.params, listId]);
const selectedOrder = useMemo(() => options.find((o) => o.key === selectedkey)?.data, [options, selectedkey]);
const change = useCallback((_ev, data) => {
if (data.optionValue) {
const neworder = list.find((order) => keyForOrder(order.field, order.descending) === data.optionValue);
const params = new URLSearchParams(settings.params);
if (neworder) {
params.set(getParamId(PARAM_ORDER, listId), `${neworder.field}${neworder.descending ? " desc" : ""}`);
}
settings.serialize(params);
}
}, [list, settings.params, listId]);
return /* @__PURE__ */ jsx(
Dropdown,
{
className: classes.dropdown,
value: selectedOrder?.name ?? "",
selectedOptions: selectedkey ? [selectedkey] : [],
onOptionSelect: change,
button: {
className: classes.dropdownButton,
children: /* @__PURE__ */ jsxs("span", { className: classes.withIcon, children: [
/* @__PURE__ */ jsx(SortIcon, { descending: selectedOrder?.descending, fieldType: selectedOrder?.fieldType }),
selectedOrder?.name
] })
},
children: options.map((option) => /* @__PURE__ */ jsx(Option, { value: option.key, text: option.text, children: /* @__PURE__ */ jsxs("span", { className: classes.withIcon, children: [
/* @__PURE__ */ jsx(SortIcon, { descending: option.data.descending, fieldType: option.data.fieldType }),
option.text
] }) }, option.key))
}
);
});
var SortIcon = ({ descending, fieldType }) => {
if (fieldType === FilterValueType.string) {
return descending ? /* @__PURE__ */ jsx(TextSortDescendingRegular, {}) : /* @__PURE__ */ jsx(TextSortAscendingRegular, {});
}
return descending ? /* @__PURE__ */ jsx(ArrowSortDownRegular, {}) : /* @__PURE__ */ jsx(ArrowSortUpRegular, {});
};
var keyForOrder = (field, descending) => `${field}${descending === true}`;
var useClasses9 = makeStyles({
unicodeIcon: {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
width: "20px",
height: "20px",
fontSize: "14px",
fontWeight: "bold",
color: tokens.colorNeutralForeground1
}
});
var UnicodeIcon = ({ symbol, className }) => /* @__PURE__ */ jsx("span", { className, "aria-hidden": "true", children: symbol });
var buildOperatorConfigs = (unicodeIconClass) => [
{ operator: FilterOperator.eq, icon: () => /* @__PURE__ */ jsx(EqualCircleRegular, {}), ariaLabel: "Equals" },
{ operator: FilterOperator.ne, icon: () => /* @__PURE__ */ jsx(EqualOffRegular, {}), ariaLabel: "Not equals" },
{ operator: FilterOperator.gt, icon: () => /* @__PURE__ */ jsx(ChevronRightRegular, {}), ariaLabel: "Greater than" },
{ operator: FilterOperator.ge, icon: () => /* @__PURE__ */ jsx(UnicodeIcon, { symbol: "\u2265", className: unicodeIconClass }), ariaLabel: "Greater or equal" },
{ operator: FilterOperator.lt, icon: () => /* @__PURE__ */ jsx(ChevronLeftRegular, {}), ariaLabel: "Less than" },
{ operator: FilterOperator.le, icon: () => /* @__PURE__ */ jsx(UnicodeIcon, { symbol: "\u2264", className: unicodeIconClass }), ariaLabel: "Less or equal" },
{ operator: FilterOperator.contains, icon: () => /* @__PURE__ */ jsx(SearchRegular, {}), ariaLabel: "Contains" }
];
var OperatorSelector = observer(({ filter, listId }) => {
const classes = useClasses9();
const settings = useGenericListSettings();
const filterparamid = useMemo(() => getParamId(filter.id, listId), [filter.id, listId]);
const operators = useMemo(() => filter.operators ?? [FilterOperator.eq], [filter.operators]);
const selected = useMemo(() => {
return settings.params.get(fieldIdForOperator(filterparamid)) ?? filter.operator;
}, [filter.operator, filterparamid, settings.params]);
const operatorConfigs = useMemo(() => buildOperatorConfigs(classes.unicodeIcon), [classes.unicodeIcon]);
const getConfig = useCallback(
(op) => operatorConfigs.find((c) => c.operator === op),
[operatorConfigs]
);
const handleSelect = useCallback((operator) => {
const params = new URLSearchParams(settings.params);
if (operator === filter.operator) {
params.delete(fieldIdForOperator(filterparamid));
} else {
params.set(fieldIdForOperator(filterparamid), operator);
}
settings.serialize(params);
}, [filterparamid, filter.operator, settings.params]);
const selectedConfig = getConfig(selected);
return /* @__PURE__ */ jsx(Fragment, { children: operators.length > 1 && /* @__PURE__ */ jsxs(Menu, { children: [
/* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(
SplitButton,
{
appearance: "subtle",
icon: selectedConfig?.icon(),
"aria-label": selectedConfig?.ariaLabel
}
) }),
/* @__PURE__ */ jsx(MenuPopover, { children: /* @__PURE__ */ jsx(MenuList, { children: operators.map((op) => {
const config = getConfig(op);
if (!config) return null;
return /* @__PURE__ */ jsx(
MenuItem,
{
icon: config.icon(),
"aria-label": config.ariaLabel,
onClick: () => handleSelect(op)
},
op
);
}) }) })
] }) });
});
var Filter = observer(({ filter, listId }) => {
const settings = useGenericListSettings();
const visible = useMemo(() => {
return !filter.isAvailable || filter.isAvailable(filter, settings.params);
}, [filter, settings.params]);
return /* @__PURE__ */ jsx(Fragment, { children: visible && /* @__PURE__ */ jsx(Fragment, { children: filter.renderCustom ? /* @__PURE__ */ jsx(FilterCustom, { filter, listId }) : filter.valueType === FilterValueType.boolean ? /* @__PURE__ */ jsx(FilterBoolean, { filter, listId }) : filter.options ? /* @__PURE__ */ jsx(FilterOptions, { filter, listId }) : /* @__PURE__ */ jsx(FilterText, { filter, listId }) }) });
});
var useClasses10 = makeStyles({
rootvertical: {
display: "flex",
flexDirection: "column",
rowGap: tokens.spacingVerticalXS
},
buttoncontainer: {
display: "flex",
flexDirection: "row",
alignItems: "end",
justifyContent: "end",
columnGap: tokens.spacingHorizontalXS
}
});
var FilterText = observer(({ filter, listId }) => {
const classes = useClasses10();
const [value, setValue] = useState("");
const settings = useGenericListSettings();
const filterparamid = useMemo(() => getParamId(filter.id, listId), [filter.id, listId]);
useEffect(() => {
setValue(settings.params.get(filterparamid) ?? "");
}, [settings.params, filterparamid]);
const execute = useCallback((query) => {
const params = new URLSearchParams(settings.params);
params.delete(filterparamid);
if (query !== "") {
params.set(filterparamid, query);
} else {
params.delete(fieldIdForOperator(filterparamid));
}
settings.serialize(params);
}, [settings.params, filterparamid]);
const changevalue = useCallback((ev, data) => {
setValue(data.value ?? "");
}, []);
const clearvalue = useCallback(() => {
setValue("");
execute("");
}, [execute]);
const onenter = useCallback((e) => {
if (e.code === "Enter") {
execute(value);
}
}, [execute, value]);
const onexecute = useCallback(() => {
execute(value);
}, [execute, value]);
const allowoperatorselection = useMemo(() => filter.operators && filter.operators.length > 0, [filter.operators]);
return /* @__PURE__ */ jsxs("div", { className: classes.rootvertical, children: [
/* @__PURE__ */ jsx(Field, { label: filter.name, children: /* @__PURE__ */ jsx(
Input,
{
autoComplete: "off",
"data-1p-ignore": true,
placeholder: filter.placeholderText ?? settings.onTranslate(LANGUAGE_PLACEHOLDERSEARCH),
value,
spellCheck: false,
onChange: changevalue,
onKeyUp: onenter
}
) }),
/* @__PURE__ */ jsxs("div", { className: classes.buttoncontainer, children: [
allowoperatorselection && /* @__PURE__ */ jsx(OperatorSelector, { filter, listId }),
/* @__PURE__ */ jsx(
Button,
{
onClick: clearvalue,
disabled: !value,
appearance: "transparent",
icon: /* @__PURE__ */ jsx(DismissRegular, {})
}
),
/* @__PURE__ */ jsx(
Button,
{
onClick: onexecute,
disabled: !value,
appearance: "transparent",
icon: /* @__PURE__ */ jsx(SearchRegular, {})
}
)
] })
] });
});
var FilterCustom = observer(({ filter, listId }) => {
const settings = useGenericListSettings();
const filterparamid = useMemo(() => getParamId(filter.id, listId), [filter.id, listId]);
const selected = useMemo(() => {
return settings.params.get(filterparamid) ?? void 0;
}, [filter, settings.params]);
const select = useCallback((value) => {
const params = new URLSearchParams(settings.params);
params.delete(filterparamid);
if (value && value !== "") {
params.set(filterparamid, value);
} else {
params.delete(fieldIdForOperator(filterparamid));
}
settings.serialize(params);
}, [filter, settings.params, filterparamid]);
return /* @__PURE__ */ jsx(Fragment, { children: filter.renderCustom && /* @__PURE__ */ jsx(Fragment, { children: filter.renderCustom(filter, selected, select, listId) }) });
});
var useClasses11 = makeStyles({
head: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
gap: tokens.spacingVerticalM,
cursor: "pointer",
":hover": {
backgroundColor: tokens.colorSubtleBackgroundHover
}
},
actions: {
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: tokens.spacingVerticalM
}
});
var FilterOptions = observer(({ filter, listId }) => {
const [open, setOpen] = useState(true);
const settings = useGenericListSettings();
const classes = useClasses11();
const filterparamid = useMemo(() => getParamId(filter.id, listId), [filter.id, listId]);
useEffect(() => {
if (filter.collapsed === true && !settings.params.get(filterparamid)) {
setOpen(false);
}
}, [filter.collapsed, filterparamid, settings.params]);
const toggleopen = useCallback(() => {
setOpen(!open);
}, [open]);
const selected = useMemo(() => {
const id = settings.params.get(filterparamid);
if (id) {
return id;
} else {
return filter.options?.find((o) => o.value === void 0 && o.expression === void 0)?.id;
}
}, [filter, filterparamid, settings.params]);
const select = useCallback((ev, data) => {
const params = new URLSearchParams(settings.params);
params.delete(filterparamid);
if (data.value) {
const o = filter.options?.find((o2) => o2.id === data.value);
if (o && (o.expression !== void 0 && o.expression !== "" || o.value !== void 0)) {
params.set(filterparamid, o.id);
}
settings.serialize(params);
}
}, [filter, filterparamid, settings.params]);
const clear = useCallback((event) => {
event.stopPropagation();
const params = new URLSearchParams(settings.params);
if (params.has(filterparamid)) {
params.delete(filterparamid);
settings.serialize(params);
}
}, [filterparamid, settings.params]);
return /* @__PURE__ */ jsx(Field, { label: /* @__PURE__ */ jsxs("div", { className: classes.head, onClick: toggleopen, children: [
filter.name,
/* @__PURE__ */ jsxs("div", { className: classes.actions, children: [
selected !== void 0 && /* @__PURE__ */ jsx(
Button,
{
onClick: clear,
appearance: "transparent",
icon: /* @__PURE__ */ jsx(DismissRegular, {})
}
),
/* @__PURE__ */ jsx(ToggleOpenButton, { value: open })
] })
] }), children: open && filter.options && /* @__PURE__ */ jsx(RadioGroup, { value: selected ?? "", onChange: select, children: filter.options.map((option) => /* @__PURE__ */ jsx(Radio, { value: option.id, label: option.name ?? option.id }, option.id)) }) });
});
var useClasses12 = makeStyles({
horizontal: {
display: "flex",
flexDirection: "row",
alignItems: "center"
}
});
var VALUETRUE = "true";
var VALUEFALSE = "false";
var FilterBoolean = observer(({ filter, listId }) => {
const classes = useClasses12();
const settings = useGenericListSettings();
const filterparamid = useMemo(() => getParamId(filter.id, listId), [filter.id, listId]);
const checked = useMemo(() => {
const value = settings.params.get(filterparamid) ?? void 0;
return value === VALUETRUE || value === "1" ? true : value === VALUEFALSE || value === "0" ? false : void 0;
}, [filter, settings.params]);
const enabled = useMemo(() => checked !== void 0, [checked]);
const toggle = useCallback((ev, data) => {
if (typeof data.checked === "boolean") {
const params = new URLSearchParams(settings.params);
if (data.checked) {
params.delete(filterparamid);
params.set(filterparamid, VALUEFALSE);
} else if (params.has(filterparamid)) {
params.delete(filterparamid);
}
settings.serialize(params);
}
}, [settings.params, filterparamid]);
const select = useCallback((ev, data) => {
const params = new URLSearchParams(settings.params);
params.delete(filterparamid);
params.set(filterparamid, data.checked ? VALUETRUE : VALUEFALSE);
settings.serialize(params);
}, [filter, settings.params, filterparamid]);
return /* @__PURE__ */ jsx(Field, { label: filter.name, children: /* @__PURE__ */ jsxs("div", { className: classes.horizontal, children: [
/* @__PURE__ */ jsx(
Checkbox,
{
checked: enabled,
onChange: toggle
}
),
/* @__PURE__ */ jsx(
Switch,
{
disabled: !enabled,
checked: checked === void 0 ? false : checked,
onChange: select
}
)
] }) });
});
var useClasses13 = makeStyles({
root: {
display: "flex",
flexDirection: "column",
rowGap: tokens.spacingVerticalM
}
});
var FilterList = observer(({ list, listId, className }) => {
const classes = useClasses13();
const visiblelist = useMemo(() => list.filter((filter) => filter.hidden !== true), [list]);
return /* @__PURE__ */ jsx("div", { className: mergeClasses(classes.root, className), children: visiblelist.map((filter) => /* @__PURE__ */ jsx(Filter, { filter, listId }, filter.id)) });
});
var useClasses14 = makeStyles({
root: {
display: "flex",
flexDirection: "row",
columnGap: tokens.spacingHorizontalS,
boxShadow: tokens.shadow8,
padding: tokens.spacingHorizontalS,
borderRadius: tokens.borderRadiusMedium,
backgroundColor: tokens.colorBrandBackgroundSelected
},
leftright: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "100%"
},
textblock: {
display: "flex",
flexDirection: "column",
overFlowX: "hidden",
flexGrow: 1
},
textcolor: {
color: tokens.colorNeutralForegroundStaticInverted
}
});
var SelectedFilter = observer(({ filter, value, allowClear, listId }) => {
const classes = useClasses14();
const settings = useGenericListSettings();
const clear = useCallback(() => {
const params = new URLSearchParams(settings.params);
params.delete(getParamId(filter.id, listId));
settings.serialize(params);
}, [settings.params, filter.id, listId]);
return /* @__PURE__ */ jsxs("div", { className: classes.root, children: [
/* @__PURE__ */ jsx(Filter28Filled, { color: tokens.colorNeutralForegroundStaticInverted, style: { minWidth: "22px" } }),
/* @__PURE__ */ jsxs("div", { className: classes.leftright, children: [
/* @__PURE__ */ jsxs("div", { className: classes.textblock, children: [
/* @__PURE__ */ jsx(Text, { className: classes.textcolor, weight: "bold", children: filter.name }),
/* @__PURE__ */ jsx(Text, { className: classes.textcolor, block: true, wrap: true, children: value })
] }),
allowClear && /* @__PURE__ */ jsx(
Button,
{
appearance: "transparent",
icon: /* @__PURE__ */ jsx(DismissRegular, { color: tokens.colorNeutralForegroundStaticInverted }),
onClick: clear
}
)
] })
] });
});
var useClasses15 = makeStyles({
list: {
display: "flex",
flexDirection: "column",
rowGap: tokens.spacingVerticalXS
}
});
var SelectedFilterList = observer(({ list, hiddenOnly = true, allowClear = true, listId, className }) => {
const classes = useClasses15();
const settings = useGenericListSettings();
const infolist = useMemo(() => {
return list.map((filter) => {
const value = settings.params.get(getParamId(filter.id, listId));
return {
filter,
value: value ?? ""
};
}).filter((info) => {
return info.value.trim() !== "" && (!hiddenOnly || info.filter.hidden === true);
});
}, [list, settings.params, hiddenOnly, listId]);
return /* @__PURE__ */ jsx("div", { className: mergeClasses(classes.list, className), children: infolist.map((info) => /* @__PURE__ */ jsx(SelectedFilter, { filter: info.filter, value: info.value, allowClear, listId }, info.filter.id)) });
});
var TotalItems = observer(({ list }) => {
const settings = useGenericListSettings();
return /* @__PURE__ */ jsx(Text, { children: settings.onTranslate(LANGUAGE_LABELRESULTCOUNT, { count: list.totalCount }) });
});
var useClasses16 = makeStyles({
root: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center"
}
});
var PagerWrapper = observer(({ list, className }) => {
const classes = useClasses16();
const settings = useGenericListSettings();
const goto = useCallback((page) => {
const params = new URLSearchParams(settings.params);
if (page > 1) {
params.set(getParamId(PARAM_PAGE, list.id), page.toString());
} else {
params.delete(getParamId(PARAM_PAGE, list.id));
}
settings.serialize(params);
}, [settings.params, list.id]);
return /* @__PURE__ */ jsx(Fragment, { children: list.loaded && list.totalCount !== 0 && /* @__PURE__ */ jsxs("div", { className: mergeClasses(classes.root, className), children: [
/* @__PURE__ */ jsx(
Pager,
{
pageSize: list.pageSize,
totalResults: list.totalCount,
currentPage: list.page,
onChange: goto,
display: "buttons"
}
),
/* @__PURE__ */ jsx("div", { children: list.loading ? /* @__PURE__ */ jsx(Loader, { size: "sm" }) : /* @__PURE__ */ jsx(TotalItems, { list }) })
] }) });
});
var useClasses17 = makeStyles({
button: {
width: "100%"
}
});
var ResetFilters = observer(({ list, listId }) => {
const classes = useClasses17();
const settings = useGenericListSettings();
const hasfilter = useMemo(() => {
if (list?.filterList) {
const hasActiveFilter = list.filterList.some((filter) => {
const paramId = getParamId(filter.id, listId);
return settings.params.has(paramId);
});
if (hasActiveFilter) return true;
return settings.params.has(getParamId(PARAM_ORDER, listId));
} else if (listId !== void 0 && listId !== "") {
for (const key of settings.params.keys()) {
if (key.startsWith(listId + ".")) {
return true;
}
}
return false;
} else {
return settings.params.toString() !== "";
}
}, [settings.params, listId, list?.filterList]);
const reset = useCallback(() => {
if (listId !== void 0 && listId !== "") {
const params = new URLSearchParams(settings.params);
const keysToDelete = Array.from(params.keys()).filter((key) => key.startsWith(listId + "."));
keysToDelete.forEach((key) => params.delete(key));
if (keysToDelete.length > 0) {
settings.serialize(params);
}
} else {
if (settings.params.toString() !== "") {
settings.serialize(new URLSearchParams());
}
}
if (list) {
const defaultOrder = list.orderList?.find((o) => o.default === true);
list.setOrder(defaultOrder);
}
}, [settings, list]);
return /* @__PURE__ */ jsx(Fragment, { children: hasfilter && /* @__PURE__ */ jsxs(Button, { appearance: "primary", className: classes.button, onClick: reset, children: [
settings.onTranslate(LANGUAGE_LABELRESET),
" "
] }) });
});
var RecycleBinFilter = observer(({ listId }) => {
const settings = useGenericListSettings();
const paramid = useMemo(() => getParamId(PARAM_DELETED, listId), [listId]);
const checked = useMemo(() => {
const value = settings.params.get(paramid);
return settings.params.has(paramid) && value !== "false";
}, [paramid, settings.params]);
const select = useCallback((ev, data) => {
const params = new URLSearchParams(settings.params);
params.delete(paramid);
if (data.checked) {
params.set(paramid, "");
}
settings.serialize(params);
}, [settings.params, paramid]);
return /* @__PURE__ */ jsx(
Switch,
{
label: settings.onTranslate(LANGUAGE_LABELRECYCLEBIN),
checked,
onChange: select
}
);
});
var CreateItem = observer((props) => {
const { list, template, ...buttonProps } = props;
const create = useCallback(() => {
list.create(template);
}, [list, template]);
return /* @__PURE__ */ jsx(Button, { ...buttonProps, icon: buttonProps.icon || /* @__PURE__ */ jsx(AddRegular, {}), onClick: create });
});
var RefreshList = observer((props) => {
const { list, ...buttonProps } = props;
const refresh = useCallback(() => {
list.setPage(1);
list.load(true);
}, [list]);
return /* @__PURE__ */ jsx(Button, { ...buttonProps, icon: buttonProps.icon || /* @__PURE__ */ jsx(ArrowClockwiseRegular, {}), onClick: refresh });
});
var ALL_LAYOUT_OPTIONS = [
{ layout: "list", labelKey: LANGUAGE_LAYOUTLIST, defaultLabel: "List", icon: /* @__PURE__ */ jsx(TextBulletListLtrRegular, {}) },
{ layout: "grid", labelKey: LANGUAGE_LAYOUTGRID, defaultLabel: "Grid", icon: /* @__PURE__ */ jsx(GridRegular, {}) },
{ layout: "compact", labelKey: LANGUAGE_LAYOUTCOMPACT, defaultLabel: "Compact", icon: /* @__PURE__ */ jsx(TableRegular, {}) },
{ layout: "card", labelKey: LANGUAGE_LAYOUTCARD, defaultLabel: "Cards", icon: /* @__PURE__ */ jsx(SquareMultipleRegular, {}) }
];
var useClasses18 = makeStyles({
toggleGroup: {
display: "inline-flex",
flexDirection: "row",
border: `1px solid ${tokens.colorNeutralStroke1}`,
borderRadius: tokens.borderRadiusMedium,
overflow: "hidden"
},
toggleButton: {
borderRadius: "0",
border: "none",
minWidth: "32px"
},
toggleButtonFirst: {
borderTopLeftRadius: tokens.borderRadiusMedium,
borderBottomLeftRadius: tokens.borderRadiusMedium
},
toggleButtonLast: {
borderTopRightRadius: tokens.borderRadiusMedium,
borderBottomRightRadius: tokens.borderRadiusMedium
}
});
var LayoutSelector = observer(({ value, onChange, availableLayouts }) => {
const classes = useClasses18();
const settings = useGenericListSettings();
const options = useMemo(() => {
const base = availableLayouts ? ALL_LAYOUT_OPTIONS.filter((o) => availableLayouts.includes(o.layout)) : ALL_LAYOUT_OPTIONS;
return base.map((o) => ({ ...o, label: settings.onTranslate(o.labelKey) ?? o.defaultLabel }));
}, [availableLayouts, settings.onTranslate]);
const handleSelect = useCallback((layout) => {
if (layout !== value) {
onChange(layout);
}
}, [value, onChange]);
if (options.length === 0) return null;
if (options.length === 2) {
return /* @__PURE__ */ jsx("div", { className: classes.toggleGroup, role: "toolbar", "aria-label": "Layout", children: options.map((option, i) => /* @__PURE__ */ jsx(Tooltip, { content: option.label, relationship: "label", withArrow: true, children: /* @__PURE__ */ jsx(
ToggleButton,
{
appearance: "subtle",
icon: option.icon,
checked: option.layout === value,
onClick: () => handleSelect(option.layout),
className: mergeClasses(
classes.toggleButton,
i === 0 && classes.toggleButtonFirst,
i === options.length - 1 && classes.toggleButtonLast
)
}
) }, option.layout)) });
}
const current = options.find((o) => o.layout === value) ?? options[0];
return /* @__PURE__ */ jsxs(Menu, { children: [
/* @__PURE__ */ jsx(MenuTrigger, { disableButtonEnhancement: true, children: /* @__PURE__ */ jsx(Tooltip, { content: `Layout: ${current.label}`, relationship: "label", withArrow: true, children: /* @__PURE__ */ jsx(MenuButton, { appearance: "subtle", icon: current.icon, "aria-label": `Layout: ${current.label}` }) }) }),
/* @__PURE__ */ jsx(MenuPopover, { children: /* @__PURE__ */ jsx(MenuList, { children: options.map((option) => /* @__PURE__ */ jsx(
MenuItem,
{
icon: option.icon,
onClick: () => handleSelect(option.layout),
"aria-pressed": option.layout === value,
children: option.label
},
option.layout
)) }) })
] });
});
var isCudList = (list) => {
return !!list && typeof list.create === "function";
};
var useClasses19 = makeStyles({
root: {
display: "flex",
flexDirection: "row",
gap: tokens.spacingHorizontalL,
alignItems: "end"
},
far: {
marginLeft: "auto",
display: "flex",
flexDirection: "row",
gap: tokens.spacingHorizontalXS
}
});
var HeadPanel = observer((props) => {
const classes = useClasses19();
const canShowRefreshList = !!(props.showRefreshList && props.list);
const canShowCreateItem = !!(props.showCreateItem && isCudList(props.list));
const refreshListChildren = props.refreshListProps?.children ?? props.refreshListText;
const createItemChildren = props.createItemProps?.children ?? props.createItemText;
const showFar = canShowCreateItem || canShowRefreshList || props.showLayoutSelector && props.layout !== void 0 && props.onLayoutChange !== void 0;
return /* @__PURE__ */ jsxs("div", { className: mergeClasses(classes.root, props.className), children: [
props.title && /* @__PURE__ */ jsx(Title3, { children: props.title }),
props.children,
showFar && /* @__PURE__ */ jsxs("div", { className: classes.far, children: [
props.showLayoutSelector && props.layout !== void 0 && props.onLayoutChange && /* @__PURE__ */ jsx(
LayoutSelector,
{
value: props.layout,
onChange: props.onLayoutChange,
availableLayouts: props.availableLayouts
}
),
canShowRefreshList && props.list && /* @__PURE__ */ jsx(RefreshList, { list: props.list, ...props.refreshListProps, children: refreshListChildren }),
canShowCreateItem && props.list && /* @__PURE__ */ jsx(CreateItem, { list: props.list, ...props.createItemProps, children: createItemChildren })
] })
] });
});
export { ALL_LAYOUT_OPTIONS, CreateItem, Filter, FilterBoolean, FilterCustom, FilterList, FilterOptions, FilterText, HeadPanel, LayoutSelector, List, ListItemPanel, ListPanel, OperatorSelector, OrderSelector, PagerWrapper, RecycleBinFilter, RefreshList, ResetFilters, SelectedFilter, SelectedFilterList, SidePanel, TotalItems, VirtualizedList };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map