Files
euzuandGitHub 62aea2a2fe persist table layouts and unify column metadata (#906)
* **New Features**
  * Customize table columns by showing, hiding, and reordering them, with keyboard or pointer controls.
  * Save layouts per account, restore defaults, and resolve conflicts when preferences change elsewhere. On installations without web authentication, settings are shared.
  * CSV tables now support saved layouts for recognized table structures.
* **Bug Fixes**
  * Improved pagination bounds and page-size validation.
  * Settings requests now return clearer errors for oversized or unreadable request bodies, and recheck access after waiting for settings to become available.
* **Documentation**
  * Added guidance for table customization and the settings API.
2026-10-08 13:28:41 +02:00

278 lines
7.3 KiB
SCSS

.tp__table {
display: flex;
flex-flow: column;
width: 100%;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
gap: var(--gap-default);
background-color: var(--card-background-color);
border-radius: var(--border-radius);
border: 1px solid var(--border-color);
overflow: hidden;
fill: var(--modest-text-color);
&__container {
display: flex;
flex-flow: column;
width: 100%;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
padding-inline-end: 1px;
overflow: auto; pointer-events: auto;
touch-action: pan-x pan-y;
-webkit-overflow-scrolling: touch;
}
&__nowrap {
white-space: nowrap;
}
&__table {
width: 100%;
border-collapse: collapse;
position: relative;
thead {
tr {
th {
position: sticky;
top: 0;
z-index: 2;
font-size: var(--font-size-md);
font-weight: bold;
//text-align: center;
padding: var(--padding-mini);
line-height: 1.2;
background-color: var(--table-header-background-color);
box-shadow: inset 0 -1px 0 var(--border-color);
&:not(:last-child) {
border-inline-end: 1px solid var(--border-color);
}
svg,
.svg-icon {
display: block;
width: 1rem;
height: 1rem;
}
}
.tp__table-header {
display: flex;
flex-flow: row;
gap: var(--gap-small);
align-items: center;
justify-content: center;
cursor: pointer;
width: 100%;
background: var(--table-header-background-color);
border: 0;
margin: 0;
padding: 0;
color: var(--text-color);
font: inherit;
svg,
.svg-icon {
display: block;
width: 1rem;
height: 1rem;
}
}
}
}
tbody {
tr {
&:nth-child(even) {
background-color: var(--table-alternate-background-color);
}
&:not(:last-child) {
border-bottom: 1px solid var(--border-color);
}
td {
height: fit-content;
vertical-align: top;
padding: var(--padding-mini);
/* cell border
&:not(:last-child) {
border-inline-end: 1px solid var(--border-color);
}
*/
svg,
.svg-icon {
display: block;
width: 1rem;
height: 1rem;
}
}
}
}
}
&__cell--center {
text-align: center;
}
&__number-cell {
text-align: end;
}
}
.tp__paged-table {
display: flex;
flex-flow: column;
width: 100%;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
&__controls {
display: flex;
flex-flow: column;
gap: var(--gap-default);
padding: var(--padding-default) 0;
}
&__paging-info {
display: flex;
flex-flow: row nowrap;
gap: var(--gap-larger);
align-items: center;
}
&__size {
display: flex;
flex-flow: row nowrap;
gap: var(--gap-default);
align-items: center;
}
&__buttons {
display: flex;
flex-flow: row nowrap;
gap: var(--gap-default);
align-items: center;
}
&__pages {
display: flex;
flex-flow: row nowrap;
gap: var(--gap-default);
align-items: center;
}
}
.tp__table-shell {
display: flex;
flex-direction: row;
min-inline-size: 0;
inline-size: 100%;
&__content { flex: 1 1 auto; min-inline-size: 0; }
.tp__table-header { min-inline-size: max-content; }
.tp__table-header svg { flex-shrink: 0; }
&__rail {
box-sizing: border-box;
flex: 0 0 1.75rem;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0;
inline-size: 1.75rem;
min-inline-size: 1.75rem;
border-inline-start: 1px solid var(--border-color);
}
&__corner {
flex: 0 0 auto;
block-size: var(--table-header-size, 0px);
background-color: var(--table-header-background-color);
box-shadow: inset 0 -1px 0 var(--border-color);
}
.tp__text-button.tp__table-shell__columns {
--text-button-background-color: transparent;
flex: 0 0 auto;
flex-direction: column;
gap: var(--gap-small);
inline-size: 100%;
min-block-size: 0;
max-block-size: none;
padding-block: var(--padding-mini);
padding-inline: 0;
border: 0;
border-radius: 0;
span { writing-mode: vertical-rl; font-size: var(--font-size-sm); line-height: 1; }
svg { flex: 0 0 auto; min-inline-size: 1rem; inline-size: 1rem; block-size: 1rem; }
}
}
.tp__custom-dialog.tp__table-columns {
position: fixed;
inset-block-start: var(--table-columns-top, 0px);
inset-block-end: auto;
inset-inline-start: auto;
inset-inline-end: var(--table-columns-inline-end, 0px);
left: var(--table-columns-left, auto);
margin: 0;
inline-size: min(20rem, var(--table-columns-max-width, 100vw));
min-inline-size: min(20rem, var(--table-columns-max-width, 100vw));
max-inline-size: min(20rem, var(--table-columns-max-width, 100vw));
max-block-size: var(--table-columns-max-height, 100dvh);
block-size: auto;
box-sizing: border-box;
padding: var(--padding-default);
border-radius: 0;
gap: var(--gap-default);
background: var(--card-background-color);
color: var(--text-color);
box-shadow: var(--popup-shadow);
&:dir(rtl) { left: var(--table-columns-left, 0px); }
header { font-weight: bold; }
header, footer { flex-shrink: 0; }
p { margin: 0; }
footer { display: flex; flex-flow: row wrap; align-items: center; gap: var(--gap-small); }
.tp__text-button { min-inline-size: 0; max-inline-size: 100%; }
.tp__text-button span { overflow-wrap: anywhere; }
.tp__text-button.tp__table-columns__reapply { max-block-size: none; padding-block: var(--padding-mini); }
.tp__table-columns__list { flex: 0 1 auto; min-block-size: 0; overflow-y: auto; margin: 0; padding: 0; list-style: none; touch-action: pan-y; }
.tp__table-columns__entry {
display: grid;
grid-template-columns: 1.75rem minmax(0, 1fr) auto;
align-items: center;
gap: var(--gap-small);
min-block-size: 2.75rem;
border-block-start: 2px solid var(--border-color);
}
.tp__table-columns__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tp__table-columns__required { display: block; font-size: var(--font-size-sm); color: var(--modest-text-color); }
.tp__table-columns__entry--moving { position: relative; z-index: 1; border-block-start-color: var(--input-focus-border-color); background: var(--card-background-color); box-shadow: var(--popup-shadow); }
.tp__table-columns__handle { touch-action: none; cursor: grab; inline-size: 1.75rem; min-block-size: 2.75rem; padding-inline: 0; }
.tp__table-columns__handle:active { cursor: grabbing; }
.tp__table-columns__handle * { cursor: inherit; }
.tp__table-columns__list--dragging,
.tp__table-columns__list--dragging * { cursor: grabbing; }
.tp__table-columns__live {
position: absolute;
inline-size: 1px;
block-size: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
}
.tp__custom-dialog-backdrop:has(.tp__table-columns__list--dragging),
.tp__custom-dialog-backdrop:has(.tp__table-columns__list--dragging) * {
cursor: grabbing;
}