Three related /lignes fixes: 1. Column widths: shrink Date/Facture/Libellé/Signalement (the four editable text columns) to free up room for the amount columns (Montant HT/TTC, the 8 compte columns, Écart), which were cramped. Date/Facture stay nowrap (already short: jj/mm/aa, Fxxxxxxx); Libellé/ Signalement keep wrapping. 2. Signalement editing: replaced the comma-separated inline text input with a small modal -- one tag per line, each with its own remove button, plus an add field at the bottom. Clearer than parsing/ retyping a whole comma list to drop one tag. Backend endpoint is unchanged (still takes a comma-joined value); only the front-end interaction model changed. 3. Scroll storm: a continuous scroll gesture fires many native 'scroll' events, and checkEdges() ran on every one of them -- each qualifying event queued its own loadOlder()/loadNewer() call (queuing, not dropping, was the previous session's fix for a *different* bug), and every queued call did a real fetch + scroll compensation regardless of whether an earlier one already moved the window away from the edge. That pileup is what looked like the same request firing over and over and dragged the scroll position around unpredictably. Fixed by guarding checkEdges() with the existing loadingOlder/ loadingNewer flags so it stops queuing once one's already in flight. (A first attempt at this suppressed the compensation write's own resulting scroll event via a flag cleared on requestAnimationFrame -- reproduced, live, the exact "stuck forever" failure already fixed once this session for the old rAF-based scroll throttle, because rAF doesn't reliably fire in this environment. Removed: turns out no suppression is needed at all, since that event finds loadingOlder already true and the checkEdges() guard blocks it on its own.) Also added a re-entrancy guard to pollForChanges(), which had no protection against a slow response overlapping with the next setInterval tick. Verified with scripted scroll stress tests (dense bursts of 40-100 events, and repeated attempts to scroll back from the very top): exactly one fetch per genuine edge crossing, no duplicate requests, scroll position stays correctly anchored, never snaps back down. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
688 lines
20 KiB
CSS
688 lines
20 KiB
CSS
/* Self-contained colors (not relying on Gin CSS custom properties, which
|
|
don't reliably resolve on this route) + explicit dark-mode overrides,
|
|
since Gin's dark mode (html.gin--dark-mode) is enabled by default here. */
|
|
#figli-home-app {
|
|
--figli-bg: #ffffff;
|
|
--figli-bg-alt: #f5f6f8;
|
|
--figli-text: #1a1a1a;
|
|
--figli-text-light: #6b7280;
|
|
--figli-border: #dcdee2;
|
|
--figli-error: #c9312b;
|
|
--figli-positive: #1a7f37;
|
|
--figli-warning: #b8860b;
|
|
--figli-col-hover: rgba(15, 23, 42, 0.05);
|
|
|
|
font-family: Inter, -apple-system, sans-serif;
|
|
margin: 1rem 0;
|
|
color: var(--figli-text);
|
|
}
|
|
|
|
html.gin--dark-mode #figli-home-app {
|
|
--figli-bg: #232426;
|
|
--figli-bg-alt: #2c2d30;
|
|
--figli-text: #e8e9ea;
|
|
--figli-text-light: #a1a5ab;
|
|
--figli-border: #3d3e42;
|
|
--figli-error: #ff6b6b;
|
|
--figli-positive: #4ade80;
|
|
--figli-warning: #f0b429;
|
|
--figli-col-hover: rgba(255, 255, 255, 0.07);
|
|
}
|
|
|
|
#figli-home-app .figli-toolbar {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 0.75rem 1.25rem;
|
|
margin-bottom: 1rem;
|
|
padding: 0.75rem 1rem;
|
|
background: var(--figli-bg-alt);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 6px;
|
|
color: var(--figli-text);
|
|
}
|
|
|
|
#figli-home-app .figli-toolbar label {
|
|
display: flex;
|
|
flex-direction: column;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
color: var(--figli-text-light);
|
|
gap: 0.15rem;
|
|
}
|
|
|
|
#figli-home-app .figli-toolbar select {
|
|
font-size: 0.85rem;
|
|
padding: 0.2rem 0.4rem;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* Each filter control + its "✕ effacer" button, side by side. */
|
|
#figli-home-app .figli-filter-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.25rem;
|
|
}
|
|
#figli-home-app .figli-filter-clear {
|
|
background: transparent;
|
|
border: none;
|
|
color: var(--figli-text-light);
|
|
cursor: pointer;
|
|
font-size: 0.8rem;
|
|
line-height: 1;
|
|
padding: 0.15rem;
|
|
}
|
|
#figli-home-app .figli-filter-clear:hover {
|
|
color: var(--figli-error);
|
|
}
|
|
|
|
/* Compte/Type filters: a native <select multiple> can't collapse to a
|
|
single-line trigger and expand as an overlay -- it's either a
|
|
single-line dropdown, or an always-open multi-row list box. Custom
|
|
dropdown instead: a trigger showing the current selection, and a
|
|
checkbox list overlay shown on hover (pure CSS, no open/close state
|
|
needed) -- checkboxes rather than ctrl/shift+click, but same result
|
|
(several comptes/types at once) with an even more discoverable
|
|
interaction (no keyboard modifier to know about).*/
|
|
#figli-home-app .figli-multiselect {
|
|
position: relative;
|
|
}
|
|
#figli-home-app .figli-multiselect-trigger {
|
|
font-size: 0.85rem;
|
|
padding: 0.2rem 0.4rem;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
width: 9rem;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
cursor: default;
|
|
}
|
|
#figli-home-app .figli-multiselect-arrow {
|
|
float: right;
|
|
color: var(--figli-text-light);
|
|
}
|
|
#figli-home-app .figli-multiselect-panel {
|
|
display: none;
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
z-index: 20;
|
|
min-width: 100%;
|
|
white-space: nowrap;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
padding: 0.3rem 0.5rem;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
}
|
|
#figli-home-app .figli-multiselect:hover .figli-multiselect-panel {
|
|
display: block;
|
|
}
|
|
#figli-home-app .figli-multiselect-option {
|
|
display: flex !important;
|
|
flex-direction: row !important;
|
|
align-items: center;
|
|
gap: 0.35rem;
|
|
font-size: 0.8rem;
|
|
font-weight: 400;
|
|
padding: 0.15rem 0;
|
|
}
|
|
|
|
#figli-home-app .figli-client-input {
|
|
font-size: 0.85rem;
|
|
padding: 0.2rem 0.4rem;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
width: 12rem;
|
|
}
|
|
|
|
#figli-home-app .figli-checkbox {
|
|
flex-direction: row !important;
|
|
align-items: center;
|
|
gap: 0.35rem !important;
|
|
}
|
|
|
|
#figli-home-app .figli-count {
|
|
margin-left: auto;
|
|
font-size: 0.8rem;
|
|
color: var(--figli-text-light);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
#figli-home-app .figli-table-wrap {
|
|
/* Fixed columns below are sized to always fit -- hidden (not auto) so
|
|
there's never a horizontal scrollbar to fight the vertical one for
|
|
layout space. With both set to auto, the browser has to guess
|
|
whether a vertical scrollbar will appear before it can lay out the
|
|
table's width:100%, and getting that guess wrong understates the
|
|
available width by one scrollbar's worth, which was enough to tip
|
|
this from "just barely fits" into "needs to scroll horizontally
|
|
too" -- and any actual residual overflow (a column pushed a few px
|
|
over by content this doesn't attempt to break) is silently clipped
|
|
here instead of surfacing a scrollbar for it. */
|
|
overflow-x: hidden;
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 6px;
|
|
max-height: 75vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
/* table-layout: fixed -- auto layout was recomputing every column's
|
|
width from whatever happened to be currently loaded, so the table
|
|
visibly reflowed (and drifted wider than the viewport, forcing
|
|
horizontal scroll) every time loadOlder()/loadNewer() brought in rows
|
|
with different content. Fixed layout locks each column to the widths
|
|
set below regardless of content, so the table's total width never
|
|
changes -- no more horizontal scroll, and loadOlder()'s scroll
|
|
compensation (which assumes the height delta after prepending is
|
|
*only* the new rows' own height) is now accurate again, since existing
|
|
rows no longer reflow when new ones are added. */
|
|
#figli-home-app table {
|
|
width: 100%;
|
|
table-layout: fixed;
|
|
border-collapse: collapse;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
#figli-home-app th,
|
|
#figli-home-app td {
|
|
/* border-box -- otherwise each column's percentage width (fixed
|
|
layout, above) sets only the content box, and this cell's own
|
|
padding/border get added on top of it, so 18 columns' worth of
|
|
padding quietly pushes the table wider than 100% again. */
|
|
box-sizing: border-box;
|
|
text-align: left;
|
|
padding: 0.3rem 0.6rem;
|
|
border-bottom: 1px solid var(--figli-border);
|
|
color: var(--figli-text);
|
|
}
|
|
|
|
#figli-home-app thead th {
|
|
position: sticky;
|
|
top: 0;
|
|
background: var(--figli-bg-alt);
|
|
color: var(--figli-text);
|
|
font-weight: 600;
|
|
font-size: 0.72rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.02em;
|
|
z-index: 2;
|
|
}
|
|
|
|
#figli-home-app td.amount,
|
|
#figli-home-app th.amount {
|
|
text-align: right;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
#figli-home-app th:nth-child(6),
|
|
#figli-home-app td.figli-libelle {
|
|
width: 10%;
|
|
white-space: normal;
|
|
}
|
|
|
|
#figli-home-app th:nth-child(7),
|
|
#figli-home-app td.figli-flag-cell {
|
|
width: 6%;
|
|
white-space: normal;
|
|
}
|
|
|
|
#figli-home-app tr.figli-group-row td {
|
|
background: var(--figli-bg-alt);
|
|
color: var(--figli-text);
|
|
font-weight: 700;
|
|
position: sticky;
|
|
top: 2rem;
|
|
z-index: 1;
|
|
}
|
|
|
|
#figli-home-app tfoot {
|
|
position: sticky;
|
|
bottom: 0;
|
|
/* Higher than thead (2) and the sticky group-row headers (1) -- without
|
|
this, tfoot had no explicit z-index (auto), so a group-row header
|
|
scrolling underneath it painted on top instead of behind. */
|
|
z-index: 3;
|
|
}
|
|
|
|
#figli-home-app tr.figli-totals-row td {
|
|
background: var(--figli-bg-alt);
|
|
color: var(--figli-text);
|
|
font-weight: 700;
|
|
border-top: 2px solid var(--figli-border);
|
|
border-bottom: none;
|
|
}
|
|
|
|
#figli-home-app td.figli-solde-crediteur {
|
|
color: var(--figli-positive);
|
|
}
|
|
#figli-home-app td.figli-solde-debiteur {
|
|
color: var(--figli-error);
|
|
}
|
|
/* Footer totals row sets its own `color` (higher specificity: tr.class +
|
|
td beats a lone td.class) which otherwise overrides the solde colors
|
|
above -- repeat them scoped to the row so red/green still wins there. */
|
|
#figli-home-app tr.figli-totals-row td.figli-solde-crediteur {
|
|
color: var(--figli-positive);
|
|
}
|
|
#figli-home-app tr.figli-totals-row td.figli-solde-debiteur {
|
|
color: var(--figli-error);
|
|
}
|
|
|
|
/* Single colored + bold anchor per row (Montant HT only) so entrées vs
|
|
sorties are scannable at a glance -- everything else in the row (TTC,
|
|
the 8 compte columns) stays neutral, not a red/green garland. */
|
|
#figli-home-app td.figli-montant-positif {
|
|
color: var(--figli-positive);
|
|
font-weight: 700;
|
|
}
|
|
#figli-home-app td.figli-montant-negatif {
|
|
color: var(--figli-error);
|
|
font-weight: 700;
|
|
}
|
|
|
|
/* Écart is called out by the Écart column itself (bold red, see
|
|
.figli-ecart below) -- no separate row-level treatment needed. */
|
|
#figli-home-app td.figli-ecart {
|
|
color: var(--figli-error);
|
|
font-weight: 700;
|
|
}
|
|
|
|
/* Signalé rows: a thick amber accent bar on the left edge. */
|
|
#figli-home-app tr.figli-flag-row td:first-child {
|
|
box-shadow: inset 4px 0 0 var(--figli-warning);
|
|
}
|
|
|
|
#figli-home-app .figli-flag-badge {
|
|
display: inline-block;
|
|
padding: 0.05rem 0.4rem;
|
|
margin: 0 0.2rem 0.15rem 0;
|
|
border-radius: 8px;
|
|
font-size: 0.68rem;
|
|
font-weight: 600;
|
|
background: color-mix(in srgb, var(--figli-warning) 15%, transparent);
|
|
color: var(--figli-warning);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Column highlight to pair with the row hover, forming a crosshair over
|
|
the hovered cell. box-shadow (not background) so it layers on top of
|
|
whatever the cell already has -- sticky header/footer backgrounds,
|
|
error-row outlines -- instead of overwriting them. */
|
|
#figli-home-app td.figli-col-hover,
|
|
#figli-home-app th.figli-col-hover {
|
|
box-shadow: inset 0 0 0 9999px var(--figli-col-hover);
|
|
}
|
|
|
|
#figli-home-app .figli-badge {
|
|
display: inline-block;
|
|
padding: 0.1rem 0.45rem;
|
|
border-radius: 10px;
|
|
font-size: 0.68rem;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
}
|
|
#figli-home-app .type-entree { background: #1a7f371a; color: var(--figli-positive); }
|
|
#figli-home-app .type-charge { background: #6b72801a; color: var(--figli-text-light); }
|
|
#figli-home-app .type-versement { background: #d97a0a1a; color: #d97a0a; }
|
|
#figli-home-app .type-achat { background: #1d4ed81a; color: #3b6fe0; }
|
|
#figli-home-app .type-hebergement { background: #0e91821a; color: #0e9182; }
|
|
#figli-home-app .type-ouverture { background: #7c3aed1a; color: #9061f0; }
|
|
#figli-home-app .type-sous_traitant { background: #c9312b1a; color: #c9312b; }
|
|
#figli-home-app .type-salaire_stage { background: #0891b21a; color: #0891b2; }
|
|
#figli-home-app .type-charges_local_pro { background: #65a30d1a; color: #65a30d; }
|
|
#figli-home-app .type-autre { background: #6b72801a; color: var(--figli-text-light); }
|
|
|
|
/* Click-to-edit type badge -- swaps for a native <select> in place
|
|
(figli-type-select below), no modal needed for this one field. */
|
|
#figli-home-app .figli-badge-editable {
|
|
cursor: pointer;
|
|
border: 1px solid transparent;
|
|
}
|
|
#figli-home-app .figli-badge-editable:hover {
|
|
border-color: currentColor;
|
|
}
|
|
#figli-home-app .figli-type-select {
|
|
font-size: 0.72rem;
|
|
padding: 0.05rem 0.2rem;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
/* Client/Facture/Libellé: click-to-edit like the type badge above, but
|
|
plain text rather than a pill -- a dotted underline is enough of an
|
|
affordance without implying a fixed set of choices the way the type
|
|
badge's pill shape does. */
|
|
/* display: block + min-height (not just the default inline text-only
|
|
box) so the whole cell is a forgiving click target even when empty --
|
|
the "—" placeholder alone is a tiny, easy-to-miss target otherwise. */
|
|
#figli-home-app .figli-editable-cell {
|
|
display: block;
|
|
min-height: 1.2em;
|
|
cursor: pointer;
|
|
border-bottom: 1px dotted transparent;
|
|
}
|
|
#figli-home-app .figli-editable-cell:hover {
|
|
border-bottom-color: var(--figli-text-light);
|
|
}
|
|
#figli-home-app .figli-inline-input {
|
|
font-size: 0.8rem;
|
|
padding: 0.1rem 0.3rem;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
#figli-home-app .figli-note {
|
|
color: var(--figli-text-light);
|
|
font-weight: 400;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
/* One column per icon (link, edit) rather than one shared cell -- with a
|
|
shared cell, text-align: center puts a lone button (rows with no link
|
|
icon) in a different spot than a row with both, so the edit pencil
|
|
never lined up from row to row. Separate columns line up by
|
|
construction, and get a tight, icon-sized width instead of the
|
|
table's default 0.6rem horizontal cell padding. */
|
|
#figli-home-app td.actions-col,
|
|
#figli-home-app th.actions-col {
|
|
width: 3%;
|
|
padding: 0.2rem 0.1rem;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Column widths for the fixed layout above -- sized so the widest
|
|
realistic content (longest client/type label, an 8-figure amount)
|
|
fits without pushing the table past 100%, while the two free-text
|
|
columns (Client, Libellé/Détail) get most of the remaining room.
|
|
Every column is a percentage (including actions-col/Date above,
|
|
deliberately not rem) -- mixing units meant the rem columns' pixel
|
|
width was added *on top of* the percentage budget instead of coming
|
|
out of it, silently pushing the table a few pixels past 100% and
|
|
reintroducing the horizontal scrollbar this is meant to avoid.
|
|
Percentages intentionally sum to a little under 100%: table-layout:
|
|
fixed treats them as relative weights, not a hard budget, so slightly
|
|
under leaves headroom rather than risking every column getting
|
|
scaled down to fit. */
|
|
#figli-home-app th:nth-child(2),
|
|
#figli-home-app td:nth-child(2) {
|
|
width: 5%;
|
|
white-space: nowrap;
|
|
}
|
|
#figli-home-app th:nth-child(3),
|
|
#figli-home-app td:nth-child(3) {
|
|
width: 10%;
|
|
}
|
|
#figli-home-app th:nth-child(4),
|
|
#figli-home-app td:nth-child(4) {
|
|
width: 7%;
|
|
}
|
|
#figli-home-app th:nth-child(5),
|
|
#figli-home-app td:nth-child(5) {
|
|
width: 5%;
|
|
white-space: nowrap;
|
|
}
|
|
#figli-home-app .amount:not(.compte-col) {
|
|
width: 6.2%;
|
|
white-space: nowrap;
|
|
}
|
|
#figli-home-app .amount.compte-col {
|
|
width: 4.3%;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Buttons stay inline (not flex) inside the <td> -- display: flex on a
|
|
table cell breaks the table's own column-width calculation (observed:
|
|
a ~55px gap opening up between this column and the next, and the
|
|
buttons rendering outside their cell's actual boundary). */
|
|
#figli-home-app .figli-edit-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 1.4rem;
|
|
height: 1.4rem;
|
|
padding: 0;
|
|
background: transparent;
|
|
border: 1px solid transparent;
|
|
border-radius: 4px;
|
|
color: var(--figli-text-light);
|
|
cursor: pointer;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
#figli-home-app .figli-edit-btn:hover {
|
|
background: var(--figli-bg-alt);
|
|
border-color: var(--figli-border);
|
|
color: var(--figli-text);
|
|
}
|
|
|
|
/* Reconciliation badge on entrée rows: répartition (money owed) vs the
|
|
combined répartition of every linked sortie (money actually paid out). */
|
|
#figli-home-app .figli-recon-badge {
|
|
display: inline-block;
|
|
margin-left: 0.4rem;
|
|
padding: 0.05rem 0.4rem;
|
|
border-radius: 10px;
|
|
font-size: 0.68rem;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
background: #1a7f371a;
|
|
color: var(--figli-positive);
|
|
}
|
|
/* Not every badge using this class does something on click (the
|
|
ouverture/clôture écart badge is purely informational) -- only show
|
|
the pointer cursor where a click actually goes somewhere. */
|
|
#figli-home-app .figli-recon-badge.is-clickable {
|
|
cursor: pointer;
|
|
}
|
|
#figli-home-app .figli-recon-badge.is-reste {
|
|
background: #d97a0a1a;
|
|
color: #d97a0a;
|
|
}
|
|
#figli-home-app .figli-recon-badge.is-anomalie {
|
|
background: #c9312b1a;
|
|
color: var(--figli-error);
|
|
}
|
|
|
|
#figli-home-app .figli-clear-drilldown {
|
|
font-size: 0.8rem;
|
|
padding: 0.2rem 0.6rem;
|
|
}
|
|
|
|
/* Entrée + sorties liées drill-down: a modal overlay rather than
|
|
replacing the main table's rows in place, so opening/closing it never
|
|
disturbs the main table's scroll position. */
|
|
#figli-home-app .figli-modal-backdrop {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.5);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 100;
|
|
}
|
|
#figli-home-app .figli-modal {
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 8px;
|
|
width: 96vw;
|
|
max-height: 85vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
|
|
}
|
|
#figli-home-app .figli-modal-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 0.75rem 1rem;
|
|
border-bottom: 1px solid var(--figli-border);
|
|
}
|
|
#figli-home-app .figli-modal-header h3 {
|
|
margin: 0;
|
|
font-size: 0.95rem;
|
|
font-weight: 700;
|
|
}
|
|
#figli-home-app .figli-modal-close {
|
|
background: transparent;
|
|
border: 1px solid transparent;
|
|
border-radius: 4px;
|
|
color: var(--figli-text-light);
|
|
cursor: pointer;
|
|
font-size: 0.9rem;
|
|
padding: 0.15rem 0.45rem;
|
|
}
|
|
#figli-home-app .figli-modal-close:hover {
|
|
background: var(--figli-bg-alt);
|
|
border-color: var(--figli-border);
|
|
color: var(--figli-text);
|
|
}
|
|
/* Signalement modal -- much smaller than the entrées/sorties drill-down
|
|
above (a short list of tags, not a table), so it gets its own narrow
|
|
width instead of the 96vw default. */
|
|
#figli-home-app .figli-flag-modal {
|
|
width: 24rem;
|
|
max-width: 90vw;
|
|
}
|
|
#figli-home-app .figli-flag-modal-body {
|
|
padding: 0.75rem 1rem 1rem;
|
|
}
|
|
#figli-home-app .figli-flag-modal-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
padding: 0.35rem 0;
|
|
border-bottom: 1px solid var(--figli-border);
|
|
}
|
|
#figli-home-app .figli-flag-remove {
|
|
background: transparent;
|
|
border: 1px solid transparent;
|
|
border-radius: 4px;
|
|
color: var(--figli-text-light);
|
|
cursor: pointer;
|
|
font-size: 0.8rem;
|
|
padding: 0.1rem 0.4rem;
|
|
flex: none;
|
|
}
|
|
#figli-home-app .figli-flag-remove:hover {
|
|
background: var(--figli-bg-alt);
|
|
border-color: var(--figli-border);
|
|
color: var(--figli-error);
|
|
}
|
|
#figli-home-app .figli-flag-modal-add {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
margin-top: 0.75rem;
|
|
}
|
|
#figli-home-app .figli-flag-modal-add input {
|
|
flex: 1;
|
|
font-size: 0.85rem;
|
|
padding: 0.3rem 0.5rem;
|
|
border: 1px solid var(--figli-border);
|
|
border-radius: 4px;
|
|
background: var(--figli-bg);
|
|
color: var(--figli-text);
|
|
}
|
|
|
|
#figli-home-app .figli-modal-body {
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
padding: 0;
|
|
}
|
|
#figli-home-app .figli-modal-body table {
|
|
font-size: 0.8rem;
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
/* Only a handful of rows in this view -- wrap instead of the main
|
|
table's nowrap, so 14 columns fit the modal width without ever
|
|
needing horizontal scroll. */
|
|
white-space: normal;
|
|
}
|
|
#figli-home-app .figli-modal-body thead th {
|
|
position: sticky;
|
|
top: 0;
|
|
}
|
|
/* table-layout: fixed otherwise spreads all 16 columns evenly, squeezing
|
|
the libellé text into the same width as an empty compte column --
|
|
give the columns that actually carry content (client/type/libellé)
|
|
proportionally more room, comptes/montants less. */
|
|
#figli-home-app .figli-modal-body th:nth-child(1),
|
|
#figli-home-app .figli-modal-body td:nth-child(1) {
|
|
width: 2rem;
|
|
}
|
|
#figli-home-app .figli-modal-body th:nth-child(2),
|
|
#figli-home-app .figli-modal-body td:nth-child(2) {
|
|
width: 4.5rem;
|
|
}
|
|
#figli-home-app .figli-modal-body th:nth-child(3),
|
|
#figli-home-app .figli-modal-body td:nth-child(3) {
|
|
width: 9%;
|
|
}
|
|
#figli-home-app .figli-modal-body th:nth-child(4),
|
|
#figli-home-app .figli-modal-body td:nth-child(4) {
|
|
width: 8%;
|
|
}
|
|
#figli-home-app .figli-modal-body th:nth-child(5),
|
|
#figli-home-app .figli-modal-body td.figli-libelle {
|
|
width: 24%;
|
|
min-width: 0;
|
|
max-width: none;
|
|
}
|
|
#figli-home-app .figli-modal-body .amount.compte-col {
|
|
width: 4.5%;
|
|
}
|
|
#figli-home-app .figli-modal-body .amount:not(.compte-col) {
|
|
width: 7%;
|
|
}
|
|
|
|
/* Sliding-window edge markers (IntersectionObserver targets) -- kept
|
|
short so they don't add visible dead space when idle, tall enough
|
|
(min-height) to reliably intersect the observer's root margin. */
|
|
#figli-home-app tr.figli-sentinel-row td {
|
|
padding: 0.3rem 0.6rem;
|
|
min-height: 1.5rem;
|
|
text-align: center;
|
|
color: var(--figli-text-light);
|
|
font-size: 0.75rem;
|
|
font-style: italic;
|
|
}
|
|
|
|
#figli-home-app .figli-error {
|
|
background: #fde8e8;
|
|
border: 1px solid #f4a3a3;
|
|
color: #7a1a1a;
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
/* Dismissible, doesn't replace the table like the top-level fetch error
|
|
does -- a failed inline type change is a small hiccup, not a reason to
|
|
hide everything that's already loaded. */
|
|
#figli-home-app .figli-inline-error {
|
|
margin-bottom: 1rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
}
|