feat(core): detail drawer + address book list redesign
Introduces a reusable core detail-drawer primitive that slides in from the right and loads any view via a `*-fragment(none).phtml` endpoint. Bundles the address book list overhaul that is its first consumer. Core additions: - `app-detail-drawer.js` — generic drawer with stepper, focus trap, body scroll-lock, URL-hash deep-linking, session-expiry detection - `app-fragment-init.js` — auto-wires tabs/lookups/confirm/file-upload/ fslightbox inside injected HTML; consumers do not re-initialize components - `app-focus-trap.js` — shared focus-trap + refcounted scroll-lock, used by both filter-drawer and detail-drawer - `getHtml()` in `app-http.js` + `SessionExpiredError`; drawer reloads the page on auth redirect instead of rendering the login form in the panel - `DetailDrawerFragmentContractTest` enforces that every `initDetailDrawer` consumer ships matching `*-fragment($id).php` + `*-fragment(none).phtml` Address book list: - Grid collapses from 9 columns to 4 (identity / context / phone / actions) with a two-line identity cell (avatar + name + email) - Tenant register tabs above the grid using the `app-list-tabs` partial; tenant filter wired via hidden toolbar field so grid.js forwards it on every data fetch - Profile body extracted to a shared partial so the full-page view and the new drawer fragment share the same markup - New i18n keys for the drawer/list labels Also refactors `app-filter-drawer` to reuse the shared focus-trap and scroll-lock instead of maintaining its own copy, and documents the detail-drawer convention in CLAUDE.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
143
modules/addressbook/web/css/pages/address-book-index.css
Normal file
143
modules/addressbook/web/css/pages/address-book-index.css
Normal file
@@ -0,0 +1,143 @@
|
||||
/* Address book list — dense identity layout */
|
||||
|
||||
#address-book-grid .gridjs-tr {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.addr-identity-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.addr-identity-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.addr-identity-text {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.addr-identity-name {
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
color: var(--app-color-text, inherit);
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 28ch;
|
||||
}
|
||||
|
||||
.addr-identity-name:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.addr-identity-email {
|
||||
color: var(--app-color-muted, #6b7280);
|
||||
font-size: 0.82rem;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 28ch;
|
||||
}
|
||||
|
||||
.addr-context-cell {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
min-width: 0;
|
||||
color: var(--app-color-text, inherit);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.addr-context-primary {
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 20ch;
|
||||
}
|
||||
|
||||
.addr-context-secondary {
|
||||
color: var(--app-color-muted, #6b7280);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 20ch;
|
||||
}
|
||||
|
||||
.addr-context-separator {
|
||||
color: var(--app-color-muted, #9ca3af);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.addr-context-extra {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 1.75rem;
|
||||
height: 1.25rem;
|
||||
padding: 0 0.375rem;
|
||||
border-radius: 9999px;
|
||||
background: var(--app-color-subtle, #f3f4f6);
|
||||
color: var(--app-color-muted, #6b7280);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
margin-left: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.addr-context-empty,
|
||||
.addr-phone-empty {
|
||||
color: var(--app-color-muted, #9ca3af);
|
||||
}
|
||||
|
||||
.addr-phone-cell {
|
||||
color: var(--app-color-text, inherit);
|
||||
text-decoration: none;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
|
||||
.addr-phone-cell:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.addr-action-cell {
|
||||
display: inline-flex;
|
||||
gap: 0.25rem;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.addr-action-button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border-radius: 0.375rem;
|
||||
color: var(--app-color-muted, #6b7280);
|
||||
text-decoration: none;
|
||||
transition: background 0.12s, color 0.12s;
|
||||
}
|
||||
|
||||
.addr-action-button:hover {
|
||||
background: var(--app-color-subtle, #f3f4f6);
|
||||
color: var(--app-color-text, #111827);
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.addr-identity-name,
|
||||
.addr-identity-email,
|
||||
.addr-context-primary,
|
||||
.addr-context-secondary {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user