Skip to content

Commit 6e8f098

Browse files
authored
Merge pull request #117 from cptKNJO/ui
UI
2 parents 7178d99 + 6817ded commit 6e8f098

File tree

7 files changed

+633
-299
lines changed

7 files changed

+633
-299
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import {
2+
Pagination as FUIPagination,
3+
IPaginationStyles,
4+
} from "@fluentui/react-experiments";
5+
import { Select, tokens } from "@fluentui/react-components";
6+
import { registerIcons } from "@fluentui/react-experiments/lib/Styling";
7+
import {
8+
ArrowNext20Filled,
9+
ArrowPrevious20Filled,
10+
ChevronLeft20Filled,
11+
ChevronRight20Filled,
12+
} from "@fluentui/react-icons";
13+
import { useState } from "react";
14+
15+
import { chunk } from "../utils/chunk";
16+
17+
// Register icons used in Pagination @fluentui/react-experiments. See https://github.com/microsoft/fluentui/wiki/Using-icons#registering-custom-icons.
18+
registerIcons({
19+
icons: {
20+
CaretSolidLeft: <ChevronLeft20Filled />,
21+
CaretSolidRight: <ChevronRight20Filled />,
22+
Next: <ArrowNext20Filled />,
23+
Previous: <ArrowPrevious20Filled />,
24+
},
25+
});
26+
27+
const PAGE_SIZES = [10, 25, 50, 100];
28+
29+
const paginationStyles: Partial<IPaginationStyles> = {
30+
root: {
31+
alignItems: "end",
32+
marginBlockStart: tokens.spacingHorizontalM,
33+
// Fix centering of page selection buttons
34+
"[role=radiogroup]": {
35+
display: "flex",
36+
alignItems: "center",
37+
},
38+
i: {
39+
display: "flex",
40+
alignItems: "center",
41+
},
42+
// First, previous, next, last
43+
"button:not([aria-disabled]) i": {
44+
color: tokens.colorBrandForeground1,
45+
},
46+
// Selected page
47+
"button[aria-checked='true']": {
48+
fontWeight: "bold",
49+
color: tokens.colorBrandForeground1,
50+
position: "relative",
51+
borderBottom: `1px solid ${tokens.colorBrandForeground1}`,
52+
},
53+
},
54+
pageNumber: {
55+
color: "currentColor",
56+
},
57+
visibleItemLabel: {
58+
marginBlockStart: tokens.spacingVerticalS,
59+
},
60+
};
61+
62+
const pageSizeStyles = {
63+
display: "flex",
64+
alignItems: "center",
65+
gap: tokens.spacingHorizontalS,
66+
};
67+
68+
export function usePagination<T>(
69+
data: T[],
70+
config: { itemsPerPage?: number } = {},
71+
) {
72+
const [itemsPerPage, setItemsPerPage] = useState<number>(
73+
config.itemsPerPage ?? PAGE_SIZES[0],
74+
);
75+
76+
const [page, setPage] = useState(0);
77+
78+
const pageData = chunk(data, itemsPerPage);
79+
80+
return { itemsPerPage, setItemsPerPage, pageData, page, setPage };
81+
}
82+
83+
export function Pagination({
84+
page,
85+
setPage,
86+
pageCount,
87+
itemsPerPage,
88+
totalItemCount,
89+
}: {
90+
page: number;
91+
setPage: React.Dispatch<React.SetStateAction<number>>;
92+
pageCount: number;
93+
itemsPerPage: number;
94+
totalItemCount: number;
95+
}) {
96+
return (
97+
<FUIPagination
98+
selectedPageIndex={page}
99+
pageCount={pageCount}
100+
itemsPerPage={itemsPerPage}
101+
totalItemCount={totalItemCount}
102+
format={"buttons"}
103+
previousPageAriaLabel={"previous page"}
104+
nextPageAriaLabel={"next page"}
105+
firstPageAriaLabel={"first page"}
106+
lastPageAriaLabel={"last page"}
107+
pageAriaLabel={"page"}
108+
selectedAriaLabel={"selected"}
109+
onPageChange={(index) => setPage(index)}
110+
styles={paginationStyles}
111+
/>
112+
);
113+
}
114+
115+
export function PaginationItemsPerPageSelector({
116+
itemsPerPage,
117+
setItemsPerPage,
118+
}: {
119+
itemsPerPage: number;
120+
setItemsPerPage: React.Dispatch<React.SetStateAction<number>>;
121+
}) {
122+
return (
123+
<label style={pageSizeStyles}>
124+
Show
125+
<Select
126+
id="page-size"
127+
defaultValue={itemsPerPage}
128+
onChange={(_, data) => setItemsPerPage(+data.value)}
129+
>
130+
{PAGE_SIZES.map((size, id) => (
131+
<option key={id}>{size}</option>
132+
))}
133+
</Select>
134+
entries
135+
</label>
136+
);
137+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import {
2+
Field,
3+
SearchBox as FUISearchBox,
4+
tokens,
5+
} from "@fluentui/react-components";
6+
import { useState } from "react";
7+
8+
const styles: Record<string, React.CSSProperties> = {
9+
search: {
10+
display: "flex",
11+
alignItems: "center",
12+
gap: tokens.spacingHorizontalS,
13+
},
14+
searchBox: {
15+
width: "28ch",
16+
},
17+
};
18+
19+
export function useFilteredBySearch<T>(
20+
data: T[],
21+
filterFn: (d: T, search: string) => boolean,
22+
) {
23+
const [search, setSearch] = useState("");
24+
25+
const filteredItems = data.filter((d) => filterFn(d, search));
26+
27+
return {
28+
search,
29+
setSearch,
30+
filteredItems,
31+
};
32+
}
33+
34+
export function SearchBox({
35+
onSearch,
36+
}: {
37+
onSearch: (search: string) => void;
38+
}) {
39+
return (
40+
<Field label="Search" style={styles.search}>
41+
<FUISearchBox
42+
autoComplete="off"
43+
onChange={(_, data) => {
44+
const search = data.value.toLowerCase();
45+
46+
onSearch(search);
47+
}}
48+
style={styles.searchBox}
49+
/>
50+
</Field>
51+
);
52+
}
53+
54+
export function filterBySearch(
55+
search: string,
56+
searchableFields: Record<string, string>,
57+
): boolean {
58+
if (search.length) {
59+
// Find matching search term in data values
60+
const match = Object.values(searchableFields).some((v) =>
61+
String(v).toLowerCase().includes(search),
62+
);
63+
64+
return match;
65+
}
66+
67+
return true;
68+
}

urbackupserver/www2/src/css/global.css

+41
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
@import url("reset.css") layer(reset);
22

3+
:root {
4+
/* FluentUI Spacing - https://react.fluentui.dev/?path=/docs/theme-spacing--docs */
5+
--spacingXXS: 2px;
6+
--spacingXS: 4px;
7+
--spacingSNudge: 6px;
8+
--spacingS: 8px;
9+
--spacingMNudge: 10px;
10+
--spacingM: 12px;
11+
--spacingL: 16px;
12+
--spacingXL: 20px;
13+
--spacingXXL: 24px;
14+
--spacingXXXL: 32px;
15+
16+
/* App variables */
17+
--gutter: var(--spacingM);
18+
}
19+
320
@layer composition {
421
/*
522
FLOW COMPOSITION
@@ -9,6 +26,30 @@
926
.flow > * + * {
1027
margin-top: var(--flow-space, 1em);
1128
}
29+
30+
/*
31+
CLUSTER
32+
More info: https://every-layout.dev/layouts/cluster/
33+
A layout that lets you distribute items with consitent
34+
spacing, regardless of their size
35+
36+
CUSTOM PROPERTIES AND CONFIGURATION
37+
--gutter: This defines the space
38+
between each item.
39+
40+
--cluster-horizontal-alignment (flex-start) How items should align
41+
horizontally. Can be any acceptable flexbox aligmnent value.
42+
43+
--cluster-vertical-alignment How items should align vertically.
44+
Can be any acceptable flexbox alignment value.
45+
*/
46+
.cluster {
47+
display: flex;
48+
flex-wrap: wrap;
49+
gap: var(--gutter);
50+
justify-content: var(--cluster-horizontal-alignment, flex-start);
51+
align-items: var(--cluster-vertical-alignment, center);
52+
}
1253
}
1354

1455
.table-wrapper {

0 commit comments

Comments
 (0)