mirror of
https://github.com/portainer/portainer.git
synced 2025-07-22 23:09:41 +02:00
feat(ui): add sorting icon component and table header cell styling EE-3626 (#7165)
* feat(ui): add sorting icons EE-3626 feat(ui): Add react component for sorting icons feat(ui) make component usable in angular * feat(ui): update angular example EE-3626
This commit is contained in:
parent
712207e69f
commit
14a8b1d897
17 changed files with 272 additions and 136 deletions
|
@ -1,5 +1,4 @@
|
|||
.sort-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
display: inline-block;
|
||||
/* highlight the sort icons for columns that aren't actively sorting */
|
||||
button:not(.sortingActive):hover path {
|
||||
fill: var(--sort-icon-hover);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,8 @@ import clsx from 'clsx';
|
|||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
import { TableHeaderProps } from 'react-table';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
|
||||
import { useTableContext } from './TableContainer';
|
||||
import { TableHeaderSortIcons } from './TableHeaderSortIcons';
|
||||
import styles from './TableHeaderCell.module.css';
|
||||
|
||||
interface Props {
|
||||
|
@ -32,15 +31,17 @@ export function TableHeaderCell({
|
|||
|
||||
return (
|
||||
<th role={role} style={style} className={className}>
|
||||
<SortWrapper
|
||||
canSort={canSort}
|
||||
onClick={onSortClick}
|
||||
isSorted={isSorted}
|
||||
isSortedDesc={isSortedDesc}
|
||||
>
|
||||
{render()}
|
||||
</SortWrapper>
|
||||
{canFilter ? renderFilter() : null}
|
||||
<div className="flex flex-row flex-nowrap h-full items-center gap-1">
|
||||
<SortWrapper
|
||||
canSort={canSort}
|
||||
onClick={onSortClick}
|
||||
isSorted={isSorted}
|
||||
isSortedDesc={isSortedDesc}
|
||||
>
|
||||
{render()}
|
||||
</SortWrapper>
|
||||
{canFilter ? renderFilter() : null}
|
||||
</div>
|
||||
</th>
|
||||
);
|
||||
}
|
||||
|
@ -49,13 +50,13 @@ interface SortWrapperProps {
|
|||
canSort: boolean;
|
||||
isSorted: boolean;
|
||||
isSortedDesc?: boolean;
|
||||
onClick: (desc: boolean) => void;
|
||||
onClick?: (desc: boolean) => void;
|
||||
}
|
||||
|
||||
function SortWrapper({
|
||||
canSort,
|
||||
children,
|
||||
onClick,
|
||||
onClick = () => {},
|
||||
isSorted,
|
||||
isSortedDesc,
|
||||
}: PropsWithChildren<SortWrapperProps>) {
|
||||
|
@ -64,27 +65,47 @@ function SortWrapper({
|
|||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
color="link"
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onClick(!isSortedDesc)}
|
||||
className="sortable"
|
||||
>
|
||||
<span className="sortable-label">{children}</span>
|
||||
|
||||
{isSorted ? (
|
||||
<i
|
||||
className={clsx(
|
||||
'fa',
|
||||
'space-left',
|
||||
isSortedDesc ? 'fa-sort-alpha-up' : 'fa-sort-alpha-down',
|
||||
styles.sortIcon
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
<div className={styles.sortIcon} />
|
||||
className={clsx(
|
||||
'sortable !bg-transparent w-full h-full !ml-0 !px-0 border-none focus:border-none',
|
||||
isSorted && styles.sortingActive
|
||||
)}
|
||||
</Button>
|
||||
>
|
||||
<div className="flex flex-row justify-start items-center w-full h-full">
|
||||
{children}
|
||||
<TableHeaderSortIcons
|
||||
sorted={isSorted}
|
||||
descending={isSorted && !!isSortedDesc}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
interface TableColumnHeaderAngularProps {
|
||||
colTitle: string;
|
||||
canSort: boolean;
|
||||
isSorted?: boolean;
|
||||
isSortedDesc?: boolean;
|
||||
}
|
||||
|
||||
export function TableColumnHeaderAngular({
|
||||
canSort,
|
||||
isSorted,
|
||||
colTitle,
|
||||
isSortedDesc,
|
||||
}: TableColumnHeaderAngularProps) {
|
||||
return (
|
||||
<div className="flex flex-row flex-nowrap h-full">
|
||||
<SortWrapper
|
||||
canSort={canSort}
|
||||
isSorted={!!isSorted}
|
||||
isSortedDesc={isSortedDesc}
|
||||
>
|
||||
{colTitle}
|
||||
</SortWrapper>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
.sort-icon > path {
|
||||
fill: var(--sort-icon-muted);
|
||||
}
|
||||
|
||||
.active-sort-icon > path {
|
||||
fill: var(--sort-icon);
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
display: inline-block;
|
||||
font-size: 12px !important;
|
||||
}
|
33
app/react/components/datatables/TableHeaderSortIcons.tsx
Normal file
33
app/react/components/datatables/TableHeaderSortIcons.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import SortDownIcon from './sort-arrow-down.svg?c';
|
||||
import SortUpIcon from './sort-arrow-up.svg?c';
|
||||
import styles from './TableHeaderSortIcons.module.css';
|
||||
|
||||
interface Props {
|
||||
sorted: boolean;
|
||||
descending: boolean;
|
||||
}
|
||||
|
||||
export function TableHeaderSortIcons({ sorted, descending }: Props) {
|
||||
return (
|
||||
<div className="flex flex-row no-wrap w-min-max">
|
||||
<SortDownIcon
|
||||
className={clsx(
|
||||
'space-left',
|
||||
sorted && !descending && styles.activeSortIcon,
|
||||
styles.sortIcon
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<SortUpIcon
|
||||
className={clsx(
|
||||
'-ml-1', // shift closer to SortDownIcon to match the mockup
|
||||
sorted && descending && styles.activeSortIcon,
|
||||
styles.sortIcon
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
3
app/react/components/datatables/sort-arrow-down.svg
Normal file
3
app/react/components/datatables/sort-arrow-down.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M4.4734 0.727041C4.4734 0.399398 4.2078 0.133789 3.88015 0.133789C3.55251 0.133789 3.2869 0.399398 3.2869 0.727041H4.4734ZM3.88015 11.2737L3.47355 11.7057C3.70705 11.9255 4.07293 11.9199 4.29966 11.6931L3.88015 11.2737ZM6.93628 9.0563C7.16795 8.82464 7.16795 8.44901 6.93618 8.21734C6.70452 7.98568 6.32891 7.98568 6.09723 8.21734L6.93628 9.0563ZM1.48521 8.20479C1.24663 7.98024 0.871167 7.99161 0.646611 8.2302C0.422046 8.46878 0.433416 8.84431 0.672003 9.06886L1.48521 8.20479ZM3.2869 0.727041V11.2737H4.4734V0.727041H3.2869ZM6.09723 8.21734L3.46064 10.8542L4.29966 11.6931L6.93628 9.0563L6.09723 8.21734ZM4.28676 10.8417L1.48521 8.20479L0.672003 9.06886L3.47355 11.7057L4.28676 10.8417Z" fill="#D0D5DD"/>
|
||||
</svg>
|
After Width: | Height: | Size: 818 B |
3
app/react/components/datatables/sort-arrow-up.svg
Normal file
3
app/react/components/datatables/sort-arrow-up.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="8" height="12" viewBox="0 0 8 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3.52627 11.2732C3.52627 11.6008 3.79185 11.8664 4.11952 11.8664C4.4472 11.8664 4.71278 11.6008 4.71278 11.2732H3.52627ZM4.11952 0.726556L4.5261 0.29456C4.29265 0.0747795 3.92672 0.0803264 3.7 0.307077L4.11952 0.726556ZM1.06338 2.94393C0.83172 3.17561 0.83172 3.55124 1.06348 3.7829C1.29515 4.01458 1.67078 4.01456 1.90244 3.78286L1.06338 2.94393ZM6.51448 3.79539C6.75307 4.01996 7.1285 4.00859 7.35304 3.77C7.57759 3.53141 7.56622 3.15595 7.32763 2.9314L6.51448 3.79539ZM4.71278 11.2732V0.726556H3.52627V11.2732H4.71278ZM1.90244 3.78286L4.53905 1.14602L3.7 0.307077L1.06338 2.94393L1.90244 3.78286ZM3.71295 1.15855L6.51448 3.79539L7.32763 2.9314L4.5261 0.29456L3.71295 1.15855Z" fill="#D0D5DD"/>
|
||||
</svg>
|
After Width: | Height: | Size: 807 B |
Loading…
Add table
Add a link
Reference in a new issue