Improved list view for scenes, galleries and performers (#4368)

Co-authored-by: InfiniteStash <117855276+InfiniteStash@users.noreply.github.com>
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
CJ
2024-01-16 17:46:09 -06:00
committed by GitHub
parent e7311a60d2
commit dd8da7f339
15 changed files with 1553 additions and 423 deletions

View File

@@ -0,0 +1,141 @@
import React, { useMemo } from "react";
import { Table, Form } from "react-bootstrap";
import { CheckBoxSelect } from "../Shared/Select";
import cx from "classnames";
export interface IColumn {
label: string;
value: string;
mandatory?: boolean;
}
export const ColumnSelector: React.FC<{
selected: string[];
allColumns: IColumn[];
setSelected: (selected: string[]) => void;
}> = ({ selected, allColumns, setSelected }) => {
const disableOptions = useMemo(() => {
return allColumns.map((col) => {
return {
...col,
disabled: col.mandatory,
};
});
}, [allColumns]);
const selectedColumns = useMemo(() => {
return disableOptions.filter((col) => selected.includes(col.value));
}, [selected, disableOptions]);
return (
<CheckBoxSelect
options={disableOptions}
selectedOptions={selectedColumns}
onChange={(v) => {
setSelected(v.map((col) => col.value));
}}
/>
);
};
interface IListTableProps<T> {
className?: string;
items: T[];
columns: string[];
setColumns: (columns: string[]) => void;
allColumns: IColumn[];
selectedIds: Set<string>;
onSelectChange: (id: string, selected: boolean, shiftKey: boolean) => void;
renderCell: (column: IColumn, item: T, index: number) => React.ReactNode;
}
export const ListTable = <T extends { id: string }>(
props: IListTableProps<T>
) => {
const {
className,
items,
columns,
setColumns,
allColumns,
selectedIds,
onSelectChange,
renderCell,
} = props;
const visibleColumns = useMemo(() => {
return allColumns.filter(
(col) => col.mandatory || columns.includes(col.value)
);
}, [columns, allColumns]);
const renderObjectRow = (item: T, index: number) => {
let shiftKey = false;
return (
<tr key={item.id}>
<td className="select-col">
<label>
<Form.Control
type="checkbox"
checked={selectedIds.has(item.id)}
onChange={() =>
onSelectChange(item.id, !selectedIds.has(item.id), shiftKey)
}
onClick={(
event: React.MouseEvent<HTMLInputElement, MouseEvent>
) => {
shiftKey = event.shiftKey;
event.stopPropagation();
}}
/>
</label>
</td>
{visibleColumns.map((column) => (
<td key={column.value} className={`${column.value}-data`}>
{renderCell(column, item, index)}
</td>
))}
</tr>
);
};
const columnHeaders = useMemo(() => {
return visibleColumns.map((column) => (
<th key={column.value} className={`${column.value}-head`}>
{column.label}
</th>
));
}, [visibleColumns]);
return (
<div className={cx("table-list", className)}>
<Table striped bordered>
<thead>
<tr>
<th className="select-col">
<div
className="d-inline-block"
data-toggle="popover"
data-trigger="focus"
>
<ColumnSelector
allColumns={allColumns}
selected={columns}
setSelected={setColumns}
/>
</div>
</th>
{columnHeaders}
</tr>
<tr>
<th className="border-row" colSpan={100}></th>
</tr>
</thead>
<tbody>{items.map(renderObjectRow)}</tbody>
</Table>
</div>
);
};

View File

@@ -370,3 +370,138 @@ input[type="range"].zoom-slider {
.tilted {
transform: rotate(45deg);
}
.table-list {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
margin-bottom: 1rem;
margin-left: 0;
margin-right: 0;
max-height: 78dvh;
min-width: min-content;
overflow-x: auto;
position: relative;
table {
margin: 0;
thead {
background-color: #202b33;
position: sticky;
top: 0;
z-index: 100;
}
}
.column-select {
margin: 0;
padding: 7px;
}
.comma-list {
list-style: none;
margin: 0;
overflow: hidden;
padding: 4px 2px;
text-overflow: ellipsis;
white-space: nowrap;
width: 190px;
li {
display: inline;
}
li::after {
content: ", ";
}
li:last-child::after {
content: "";
}
}
.comma-list:hover {
background: #28343c;
border: 1px solid #414c53;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.28);
display: block;
height: auto;
margin-left: -0.4rem;
margin-top: -0.9rem;
max-width: 40rem;
overflow: hidden;
padding: 0.1rem 0.5rem;
position: absolute;
top: auto;
white-space: normal;
width: max-content;
z-index: 100;
}
.comma-list li .ellips-data:hover {
max-width: fit-content;
}
td {
position: relative;
text-align: left;
white-space: nowrap;
.ellips-data {
display: block;
max-width: 190px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.star-rating-number {
display: none;
}
a {
font-weight: 600;
white-space: nowrap;
}
}
td.select-col {
text-align: center;
}
.table thead th {
border: none;
white-space: nowrap;
}
tr {
border-collapse: collapse;
}
.date-head {
width: 97px;
}
}
.table-list tbody tr:hover {
background-color: #2d3942;
}
.table-list a {
color: $text-color;
}
.table-list .table-striped td,
.table-list .table-striped th {
font-size: 1rem;
vertical-align: middle;
h5,
h6 {
font-size: 1rem;
}
&:first-child {
border-left: none;
}
}