@keenmate/pure-admin-core 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +11 -6
  2. package/dist/css/main.css +47 -130
  3. package/package.json +1 -1
  4. package/snippets/AUDIT.md +94 -0
  5. package/snippets/alerts.html +264 -89
  6. package/snippets/badges.html +193 -61
  7. package/snippets/buttons.html +178 -0
  8. package/snippets/callouts.html +210 -129
  9. package/snippets/cards.html +383 -200
  10. package/snippets/checkbox-lists.html +199 -65
  11. package/snippets/code.html +55 -11
  12. package/snippets/command-palette.html +401 -111
  13. package/snippets/comparison.html +144 -93
  14. package/snippets/customization.html +311 -104
  15. package/snippets/data-display.html +584 -0
  16. package/snippets/detail-panel.html +470 -138
  17. package/snippets/filter-card.html +246 -0
  18. package/snippets/forms.html +408 -308
  19. package/snippets/grid.html +253 -141
  20. package/snippets/layout.html +379 -480
  21. package/snippets/lists.html +144 -47
  22. package/snippets/loaders.html +64 -39
  23. package/snippets/manifest.json +330 -280
  24. package/snippets/modal-dialogs.html +137 -64
  25. package/snippets/modals.html +221 -151
  26. package/snippets/notifications.html +285 -0
  27. package/snippets/popconfirm.html +213 -19
  28. package/snippets/profile.html +290 -330
  29. package/snippets/statistics.html +247 -0
  30. package/snippets/tables.html +359 -150
  31. package/snippets/tabs.html +129 -45
  32. package/snippets/timeline.html +123 -56
  33. package/snippets/toasts.html +179 -31
  34. package/snippets/tooltips.html +199 -81
  35. package/snippets/typography.html +183 -58
  36. package/snippets/utilities.html +511 -415
  37. package/snippets/virtual-scroll.html +201 -75
  38. package/snippets/web-daterangepicker.html +369 -189
  39. package/snippets/web-multiselect.html +360 -124
  40. package/src/scss/core-components/_alerts.scss +51 -12
  41. package/src/scss/core-components/_pagers.scss +1 -1
  42. package/src/scss/core-components/_popconfirm.scss +35 -13
  43. package/src/scss/core-components/_tables.scss +2 -134
  44. package/src/scss/variables/_components.scss +17 -2
@@ -1,138 +1,470 @@
1
- <!-- =============================================
2
- Detail Panel - Inline Split-View Mode
3
- ============================================= -->
4
-
5
- <!-- Inline split-view: table + side panel -->
6
- <div class="pa-detail-view">
7
- <!-- Left side: table -->
8
- <div class="pa-detail-view__main">
9
- <table class="pa-table pa-table--hover pa-table--striped">
10
- <thead>
11
- <tr>
12
- <th>Name</th>
13
- <th>Email</th>
14
- <th>Role</th>
15
- </tr>
16
- </thead>
17
- <tbody>
18
- <tr class="is-selected" onclick="openInlinePanel(1)">
19
- <td>John Doe</td>
20
- <td>john@example.com</td>
21
- <td>Admin</td>
22
- </tr>
23
- <tr onclick="openInlinePanel(2)">
24
- <td>Jane Smith</td>
25
- <td>jane@example.com</td>
26
- <td>Editor</td>
27
- </tr>
28
- </tbody>
29
- </table>
30
- </div>
31
-
32
- <!-- Right side: detail panel -->
33
- <div class="pa-detail-view__panel pa-detail-view__panel--open">
34
- <div class="pa-detail-panel__content">
35
- <div class="pa-detail-panel-resize"></div>
36
- <div class="pa-detail-panel__header">
37
- <h4 class="pa-detail-panel__title">User Details</h4>
38
- <button class="pa-detail-panel__close" onclick="closeInlinePanel()">
39
- <i class="fas fa-times"></i>
40
- </button>
41
- </div>
42
- <div class="pa-detail-panel__body">
43
- <p>Detail content goes here...</p>
44
- </div>
45
- <div class="pa-detail-panel__footer">
46
- <button class="pa-btn pa-btn--primary pa-btn--sm">Edit</button>
47
- <button class="pa-btn pa-btn--secondary pa-btn--sm">Delete</button>
48
- </div>
49
- </div>
50
- </div>
51
- </div>
52
-
53
-
54
- <!-- =============================================
55
- Detail Panel - Card Overlay Mode
56
- ============================================= -->
57
-
58
- <!-- Card overlay: panel floats over the table within the card -->
59
- <div class="pa-detail-view pa-detail-view--overlay">
60
- <!-- Backdrop (click to close) -->
61
- <div class="pa-detail-view__overlay" onclick="closeCardOverlayPanel()"></div>
62
-
63
- <!-- Table (stays full width) -->
64
- <div class="pa-detail-view__main">
65
- <table class="pa-table pa-table--hover pa-table--striped">
66
- <thead>
67
- <tr>
68
- <th>Name</th>
69
- <th>Email</th>
70
- <th>Role</th>
71
- </tr>
72
- </thead>
73
- <tbody>
74
- <tr class="is-selected" onclick="openCardOverlayPanel(1)">
75
- <td>John Doe</td>
76
- <td>john@example.com</td>
77
- <td>Admin</td>
78
- </tr>
79
- <tr onclick="openCardOverlayPanel(2)">
80
- <td>Jane Smith</td>
81
- <td>jane@example.com</td>
82
- <td>Editor</td>
83
- </tr>
84
- </tbody>
85
- </table>
86
- </div>
87
-
88
- <!-- Detail panel (overlays the table) -->
89
- <div class="pa-detail-view__panel pa-detail-view__panel--open">
90
- <div class="pa-detail-panel__content">
91
- <div class="pa-detail-panel-resize"></div>
92
- <div class="pa-detail-panel__header">
93
- <h4 class="pa-detail-panel__title">User Details</h4>
94
- <button class="pa-detail-panel__close" onclick="closeCardOverlayPanel()">
95
- <i class="fas fa-times"></i>
96
- </button>
97
- </div>
98
- <div class="pa-detail-panel__body">
99
- <p>Detail content goes here...</p>
100
- </div>
101
- <div class="pa-detail-panel__footer">
102
- <button class="pa-btn pa-btn--primary pa-btn--sm">Edit</button>
103
- <button class="pa-btn pa-btn--secondary pa-btn--sm">Delete</button>
104
- </div>
105
- </div>
106
- </div>
107
- </div>
108
-
109
-
110
- <!-- =============================================
111
- Detail Panel - Overlay Mode
112
- ============================================= -->
113
-
114
- <!-- Trigger button (typically a table row click) -->
115
- <button class="pa-btn pa-btn--primary" onclick="openOverlayPanel(1)">
116
- Open Detail Panel
117
- </button>
118
-
119
- <!-- Overlay panel (fixed position, like profile panel) -->
120
- <div class="pa-detail-panel--overlay" id="detailPanelOverlay">
121
- <div class="pa-detail-panel__overlay" onclick="closeOverlayPanel()"></div>
122
- <div class="pa-detail-panel__content">
123
- <div class="pa-detail-panel-resize"></div>
124
- <div class="pa-detail-panel__header">
125
- <h4 class="pa-detail-panel__title">User Details</h4>
126
- <button class="pa-detail-panel__close" onclick="closeOverlayPanel()">
127
- <i class="fas fa-times"></i>
128
- </button>
129
- </div>
130
- <div class="pa-detail-panel__body">
131
- <p>Detail content goes here...</p>
132
- </div>
133
- <div class="pa-detail-panel__footer">
134
- <button class="pa-btn pa-btn--primary pa-btn--sm">Edit</button>
135
- <button class="pa-btn pa-btn--secondary pa-btn--sm">Delete</button>
136
- </div>
137
- </div>
138
- </div>
1
+ <!-- ================================
2
+ DETAIL PANEL SNIPPETS
3
+ Pure Admin Visual Framework
4
+
5
+ Three display modes, all sharing the same inner __content /
6
+ __header / __title / __close / __body / __footer structure:
7
+
8
+ 1. Inline split-view .pa-detail-view (wraps main + panel)
9
+ 2. Card overlay .pa-detail-view.pa-detail-view--overlay
10
+ (panel floats over the main area, inside
11
+ a card)
12
+ 3. Fixed overlay .pa-detail-panel--overlay (full-viewport
13
+ sibling — same layout as profile panel)
14
+
15
+ Width is driven by two CSS custom properties (:root +
16
+ :where(...) for low specificity), so utility classes on
17
+ .pa-detail-view__panel override the defaults cleanly.
18
+ ================================ -->
19
+
20
+
21
+ <!-- ================================
22
+ 1) INLINE SPLIT-VIEW MODE
23
+ Table on the start side, panel on the end. Panel has width: 0
24
+ when closed, expands to --pa-local-detail-panel-width when --open.
25
+ ================================ -->
26
+
27
+ <div class="pa-detail-view">
28
+ <!-- Main area (table / content; takes the remaining flex space) -->
29
+ <div class="pa-detail-view__main">
30
+ <table class="pa-table pa-table--hover pa-table--striped">
31
+ <thead>
32
+ <tr><th>Name</th><th>Email</th><th>Role</th></tr>
33
+ </thead>
34
+ <tbody>
35
+ <tr class="is-selected" onclick="openInlinePanel(1)">
36
+ <td>John Doe</td><td>john@example.com</td><td>Admin</td>
37
+ </tr>
38
+ <tr onclick="openInlinePanel(2)">
39
+ <td>Jane Smith</td><td>jane@example.com</td><td>Editor</td>
40
+ </tr>
41
+ </tbody>
42
+ </table>
43
+ </div>
44
+
45
+ <!-- Detail panel (inline-end side). --open expands it to the configured width. -->
46
+ <div class="pa-detail-view__panel pa-detail-view__panel--open" id="inlinePanel">
47
+ <div class="pa-detail-panel__content">
48
+ <div class="pa-detail-panel-resize"></div>
49
+ <div class="pa-detail-panel__header">
50
+ <h4 class="pa-detail-panel__title">User Details</h4>
51
+ <button class="pa-detail-panel__close" onclick="closeInlinePanel()" aria-label="Close">
52
+ <i class="fa-solid fa-xmark"></i>
53
+ </button>
54
+ </div>
55
+ <div class="pa-detail-panel__body">
56
+ <p>Detail content for the selected row.</p>
57
+ </div>
58
+ <div class="pa-detail-panel__footer">
59
+ <button class="pa-btn pa-btn--primary pa-btn--sm">Edit</button>
60
+ <button class="pa-btn pa-btn--secondary pa-btn--sm">Delete</button>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+
67
+ <!-- ================================
68
+ 2) CARD OVERLAY MODE
69
+ Add --overlay on the root. The panel uses position: absolute and
70
+ sits over the main area; the backdrop (__view__overlay) dims the
71
+ whole card when --visible.
72
+ Scope stays within the card / container (z-index 3500/3501 — below
73
+ the framework header at 4000).
74
+ ================================ -->
75
+
76
+ <div class="pa-detail-view pa-detail-view--overlay">
77
+ <!-- Backdrop — click to close -->
78
+ <div class="pa-detail-view__overlay pa-detail-view__overlay--visible"
79
+ onclick="closeCardOverlayPanel()"></div>
80
+
81
+ <div class="pa-detail-view__main">
82
+ <table class="pa-table pa-table--hover">
83
+ <thead>
84
+ <tr><th>Name</th><th>Role</th></tr>
85
+ </thead>
86
+ <tbody>
87
+ <tr class="is-selected" onclick="openCardOverlayPanel(1)">
88
+ <td>John Doe</td><td>Admin</td>
89
+ </tr>
90
+ </tbody>
91
+ </table>
92
+ </div>
93
+
94
+ <div class="pa-detail-view__panel pa-detail-view__panel--open">
95
+ <div class="pa-detail-panel__content">
96
+ <div class="pa-detail-panel-resize"></div>
97
+ <div class="pa-detail-panel__header">
98
+ <h4 class="pa-detail-panel__title">User Details</h4>
99
+ <button class="pa-detail-panel__close" onclick="closeCardOverlayPanel()" aria-label="Close">
100
+ <i class="fa-solid fa-xmark"></i>
101
+ </button>
102
+ </div>
103
+ <div class="pa-detail-panel__body">
104
+ <p>Overlays the card's main area. The backdrop dims the full card.</p>
105
+ </div>
106
+ <div class="pa-detail-panel__footer">
107
+ <button class="pa-btn pa-btn--primary pa-btn--sm">Save</button>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+
113
+
114
+ <!-- ================================
115
+ 3) FIXED OVERLAY MODE
116
+ Behaves like the profile panel — fixed to the viewport, slides in
117
+ from the inline-end side (automatic in RTL). Separate from the
118
+ inline split-view; does NOT require .pa-detail-view wrapping.
119
+ ================================ -->
120
+
121
+ <button class="pa-btn pa-btn--primary" onclick="openOverlayPanel(1)">
122
+ Open Detail Panel
123
+ </button>
124
+
125
+ <div class="pa-detail-panel--overlay pa-detail-panel--open" id="detailPanelOverlay">
126
+ <div class="pa-detail-panel__overlay" onclick="closeOverlayPanel()"></div>
127
+ <div class="pa-detail-panel__content">
128
+ <div class="pa-detail-panel__header">
129
+ <h4 class="pa-detail-panel__title">User Details</h4>
130
+ <button class="pa-detail-panel__close" onclick="closeOverlayPanel()" aria-label="Close">
131
+ <i class="fa-solid fa-xmark"></i>
132
+ </button>
133
+ </div>
134
+ <div class="pa-detail-panel__body">
135
+ <p>Fixed-viewport overlay. Toggle .pa-detail-panel--open to show/hide.</p>
136
+ </div>
137
+ <div class="pa-detail-panel__footer">
138
+ <button class="pa-btn pa-btn--primary pa-btn--sm">Save</button>
139
+ </div>
140
+ </div>
141
+ </div>
142
+
143
+
144
+ <!-- ================================
145
+ PANEL WITH TABS
146
+ Use .pa-detail-panel__tabs between __header and __body to host the
147
+ shared .pa-tabs component (see tabs.html). The detail-panel SCSS
148
+ lightly themes __tabs__item to match the panel chrome.
149
+ ================================ -->
150
+
151
+ <div class="pa-detail-panel__content">
152
+ <div class="pa-detail-panel__header">
153
+ <h4 class="pa-detail-panel__title">User Details</h4>
154
+ <button class="pa-detail-panel__close" aria-label="Close">
155
+ <i class="fa-solid fa-xmark"></i>
156
+ </button>
157
+ </div>
158
+
159
+ <div class="pa-detail-panel__tabs">
160
+ <div class="pa-tabs pa-tabs--full">
161
+ <button class="pa-tabs__item pa-tabs__item--active" data-detail-tab="profile">
162
+ <i class="fa-solid fa-user"></i>
163
+ <span>Profile</span>
164
+ </button>
165
+ <button class="pa-tabs__item" data-detail-tab="activity">
166
+ <i class="fa-solid fa-clock-rotate-left"></i>
167
+ <span>Activity</span>
168
+ </button>
169
+ </div>
170
+ </div>
171
+
172
+ <div class="pa-detail-panel__body">
173
+ <div class="pa-tabs__panel pa-tabs__panel--active" data-detail-panel="profile">
174
+ <p>Profile content…</p>
175
+ </div>
176
+ <div class="pa-tabs__panel" data-detail-panel="activity">
177
+ <p>Activity feed…</p>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+
183
+ <!-- ================================
184
+ BORDERED CONTENT MODIFIER
185
+ Adds top + bottom borders on the content wrapper — useful when
186
+ the panel sits flush against a table without its own border.
187
+ ================================ -->
188
+
189
+ <div class="pa-detail-view__panel pa-detail-view__panel--open">
190
+ <div class="pa-detail-panel__content pa-detail-panel__content--bordered">
191
+ <!-- header / body / footer as usual -->
192
+ </div>
193
+ </div>
194
+
195
+
196
+ <!-- ================================
197
+ WIDTH CONTROL
198
+ Two CSS custom properties, applied via a :where() rule on
199
+ .pa-detail-view__panel so utility classes win without !important.
200
+
201
+ :root {
202
+ --pa-local-detail-panel-width: $detail-panel-width;
203
+ --pa-local-detail-panel-max-width: $detail-panel-max-width;
204
+ }
205
+ :where(.pa-detail-view__panel) {
206
+ width: var(--pa-local-detail-panel-width);
207
+ max-width: var(--pa-local-detail-panel-max-width);
208
+ }
209
+
210
+ Override strategies (ordered by scope):
211
+ 1. Utility class on the panel .wr-{n}, .minwr-{n}, .maxwr-{n}
212
+ 2. Inline style style="width: 32rem"
213
+ 3. Root CSS variable change the default app-wide
214
+ ================================ -->
215
+
216
+ <!-- Fixed-width panel: 32rem (320px). Utility class wins because
217
+ :where() has 0 specificity. -->
218
+ <div class="pa-detail-view__panel pa-detail-view__panel--open wr-32">
219
+ <!-- … -->
220
+ </div>
221
+
222
+ <!-- Clamp between floor and ceiling (good when combined with the
223
+ resize handle — the drag JS writes to --pa-local-detail-panel-width,
224
+ and this clamps how far it can go). -->
225
+ <div class="pa-detail-view__panel pa-detail-view__panel--open minwr-24 maxwr-60">
226
+ <!-- … -->
227
+ </div>
228
+
229
+ <!-- App-wide default override -->
230
+ <style>
231
+ :root {
232
+ --pa-local-detail-panel-width: 40rem;
233
+ --pa-local-detail-panel-max-width: 70rem;
234
+ }
235
+ </style>
236
+
237
+
238
+ <!-- ================================
239
+ MOBILE OVERLAY VARIANT
240
+ Inline split-view auto-hides the panel below $mobile-breakpoint
241
+ (display: none). To keep the detail pane reachable on phones,
242
+ render the SAME content inside a .pa-detail-panel--mobile-overlay
243
+ container too — it's display: none on desktop, switches on for
244
+ mobile only, and behaves like the fixed overlay (slides in from
245
+ inline-end, full-height).
246
+
247
+ Typical pattern: render both markup blocks; JS toggles --open on
248
+ whichever one is currently visible.
249
+ ================================ -->
250
+
251
+ <div class="pa-detail-panel--mobile-overlay" id="detailPanelMobile">
252
+ <div class="pa-detail-panel__overlay" onclick="closeMobileOverlay()"></div>
253
+ <div class="pa-detail-panel__content">
254
+ <div class="pa-detail-panel__header">
255
+ <h4 class="pa-detail-panel__title">User Details</h4>
256
+ <button class="pa-detail-panel__close" onclick="closeMobileOverlay()" aria-label="Close">
257
+ <i class="fa-solid fa-xmark"></i>
258
+ </button>
259
+ </div>
260
+ <div class="pa-detail-panel__body">
261
+ <p>Same markup as the overlay panel — visible only at mobile widths.</p>
262
+ </div>
263
+ </div>
264
+ </div>
265
+
266
+
267
+ <!-- ================================
268
+ JAVASCRIPT — minimal toggle contracts
269
+ All three modes boil down to toggling a class. Real apps will
270
+ also populate the panel body with data on open.
271
+ ================================ -->
272
+
273
+ <script>
274
+ // Mode 1: inline split-view — flip --open on the panel element
275
+ function openInlinePanel(itemId) {
276
+ document.getElementById('inlinePanel').classList.add('pa-detail-view__panel--open');
277
+ // …populate content based on itemId
278
+ }
279
+ function closeInlinePanel() {
280
+ document.getElementById('inlinePanel').classList.remove('pa-detail-view__panel--open');
281
+ }
282
+
283
+ // Mode 2: card overlay — flip --open on the panel AND --visible on the backdrop
284
+ function openCardOverlayPanel(itemId) {
285
+ const view = document.querySelector('.pa-detail-view--overlay');
286
+ view.querySelector('.pa-detail-view__panel').classList.add('pa-detail-view__panel--open');
287
+ view.querySelector('.pa-detail-view__overlay').classList.add('pa-detail-view__overlay--visible');
288
+ }
289
+ function closeCardOverlayPanel() {
290
+ const view = document.querySelector('.pa-detail-view--overlay');
291
+ view.querySelector('.pa-detail-view__panel').classList.remove('pa-detail-view__panel--open');
292
+ view.querySelector('.pa-detail-view__overlay').classList.remove('pa-detail-view__overlay--visible');
293
+ }
294
+
295
+ // Mode 3: fixed overlay — flip --open on the overlay root
296
+ function openOverlayPanel(itemId) {
297
+ document.getElementById('detailPanelOverlay').classList.add('pa-detail-panel--open');
298
+ }
299
+ function closeOverlayPanel() {
300
+ document.getElementById('detailPanelOverlay').classList.remove('pa-detail-panel--open');
301
+ }
302
+
303
+ function closeMobileOverlay() {
304
+ document.getElementById('detailPanelMobile').classList.remove('pa-detail-panel--open');
305
+ }
306
+
307
+ // Resize handle — wire up on page load if the panel is resizable.
308
+ // The contract: write to --pa-local-detail-panel-width at :root (or on
309
+ // the panel) during drag, add .pa-detail-panel-resizing on <body> to
310
+ // disable text selection, remove on mouseup.
311
+ (function setupDetailPanelResize() {
312
+ const handle = document.querySelector('.pa-detail-panel-resize');
313
+ if (!handle) return;
314
+
315
+ let startX = 0, startWidth = 0;
316
+ const panel = handle.closest('.pa-detail-view__panel') || handle.closest('.pa-detail-panel__content');
317
+
318
+ handle.addEventListener('mousedown', (e) => {
319
+ startX = e.clientX;
320
+ startWidth = panel.getBoundingClientRect().width;
321
+ document.body.classList.add('pa-detail-panel-resizing');
322
+ handle.classList.add('pa-detail-panel-resize--active');
323
+
324
+ const onMove = (ev) => {
325
+ // Drag toward inline-start = widen panel on inline-end; invert in RTL.
326
+ const dir = document.documentElement.dir === 'rtl' ? +1 : -1;
327
+ const next = Math.max(200, startWidth + dir * (ev.clientX - startX));
328
+ document.documentElement.style.setProperty('--pa-local-detail-panel-width', next + 'px');
329
+ };
330
+ const onUp = () => {
331
+ document.body.classList.remove('pa-detail-panel-resizing');
332
+ handle.classList.remove('pa-detail-panel-resize--active');
333
+ document.removeEventListener('mousemove', onMove);
334
+ document.removeEventListener('mouseup', onUp);
335
+ };
336
+ document.addEventListener('mousemove', onMove);
337
+ document.addEventListener('mouseup', onUp);
338
+ });
339
+ })();
340
+ </script>
341
+
342
+
343
+ <!-- ================================
344
+ COMPONENT REFERENCE
345
+ ================================ -->
346
+
347
+ <!--
348
+ THREE DISPLAY MODES:
349
+
350
+ 1. INLINE SPLIT-VIEW (.pa-detail-view)
351
+ - .pa-detail-view flex row: main + panel
352
+ - .pa-detail-view__main flex: 1; overflow: auto
353
+ - .pa-detail-view__panel width: 0 when closed
354
+ - .pa-detail-view__panel--open expands to width + shows border-inline-start
355
+
356
+ Below $mobile-breakpoint (768px), .pa-detail-view__panel hides
357
+ with display: none. Use --mobile-overlay to keep the panel
358
+ reachable on phones (see mode 4).
359
+
360
+ 2. CARD OVERLAY (.pa-detail-view--overlay)
361
+ - Add --overlay on the root; panel uses position: absolute.
362
+ - .pa-detail-view__overlay backdrop inside the card;
363
+ - .pa-detail-view__overlay--visible fade-in trigger
364
+ - z-index 3500/3501 — below the framework header (4000),
365
+ so the panel stays inside its card even when the page scrolls.
366
+
367
+ 3. FIXED OVERLAY (.pa-detail-panel--overlay)
368
+ - position: fixed; covers the viewport.
369
+ - .pa-detail-panel--overlay root (hidden by default)
370
+ - .pa-detail-panel--open visible state
371
+ - .pa-detail-panel__overlay backdrop (child element)
372
+ - Slides content in from inline-end; [dir="rtl"] flips to
373
+ inline-start automatically.
374
+
375
+ 4. MOBILE OVERLAY (.pa-detail-panel--mobile-overlay)
376
+ - Like mode 3, but display: none above $mobile-breakpoint.
377
+ - Pairs with mode 1 for responsive apps: inline panel on desktop,
378
+ mobile overlay on phones. Render both blocks; toggle --open on
379
+ whichever is currently visible.
380
+
381
+
382
+ SHARED PANEL STRUCTURE (inside __panel / inside --overlay root):
383
+
384
+ - .pa-detail-panel__content flex column (header + body + footer)
385
+ - .pa-detail-panel__content--bordered adds top + bottom borders
386
+ - .pa-detail-panel__header flex row, flex-shrink: 0
387
+ - .pa-detail-panel__title <h4>-weight; ellipsis on overflow
388
+ - .pa-detail-panel__close 32×32 close button
389
+ - .pa-detail-panel__tabs optional tab strip between header
390
+ and body (hosts .pa-tabs — see
391
+ tabs.html for full tab docs)
392
+ - .pa-detail-panel__body flex: 1; overflow-y: auto;
393
+ overscroll-behavior: contain
394
+ - .pa-detail-panel__footer flex row, flex-shrink: 0
395
+
396
+
397
+ RESIZE HANDLE:
398
+
399
+ - .pa-detail-panel-resize absolute handle on the
400
+ inline-start edge (RTL-aware).
401
+ Cursor: col-resize.
402
+ - .pa-detail-panel-resize--active Add during drag (visual feedback).
403
+ - body.pa-detail-panel-resizing Added during drag to disable
404
+ text selection + force
405
+ col-resize cursor.
406
+
407
+ Drag contract: write to --pa-local-detail-panel-width at :root or on
408
+ the panel element while dragging; remove the body class on mouseup.
409
+
410
+
411
+ WIDTH CONTROL (CSS variables — override without specificity battles):
412
+
413
+ --pa-local-detail-panel-width runtime width (rewrite on resize)
414
+ --pa-local-detail-panel-max-width upper bound
415
+
416
+ Applied via :where(.pa-detail-view__panel) { width / max-width } so
417
+ .wr-{n} / .minwr-{n} / .maxwr-{n} utility classes beat the default.
418
+
419
+ Override paths:
420
+ 1. .wr-* / .minwr-* / .maxwr-* on the panel
421
+ 2. inline style="width: …"
422
+ 3. :root redefinition for app-wide changes
423
+
424
+
425
+ TABLE ROW SELECTION:
426
+
427
+ - .pa-table tbody tr.is-selected SCSS colours the row with
428
+ --pa-detail-panel-selected-bg
429
+ and keeps the colour on hover
430
+ so "which row am I viewing?"
431
+ stays obvious when the detail
432
+ panel is open.
433
+
434
+
435
+ STRUCTURE PATTERNS:
436
+
437
+ Inline split view:
438
+ <div class="pa-detail-view">
439
+ <div class="pa-detail-view__main">…</div>
440
+ <div class="pa-detail-view__panel pa-detail-view__panel--open">
441
+ <div class="pa-detail-panel__content">
442
+ <div class="pa-detail-panel__header">…</div>
443
+ <div class="pa-detail-panel__body">…</div>
444
+ <div class="pa-detail-panel__footer">…</div>
445
+ </div>
446
+ </div>
447
+ </div>
448
+
449
+ Fixed overlay (sibling of the layout):
450
+ <div class="pa-detail-panel--overlay" id="p">
451
+ <div class="pa-detail-panel__overlay"></div>
452
+ <div class="pa-detail-panel__content">…</div>
453
+ </div>
454
+ // open: document.getElementById('p').classList.add('pa-detail-panel--open');
455
+ // close: document.getElementById('p').classList.remove('pa-detail-panel--open');
456
+
457
+ Panel with tabs:
458
+ <div class="pa-detail-panel__content">
459
+ <div class="pa-detail-panel__header">…</div>
460
+ <div class="pa-detail-panel__tabs">
461
+ <div class="pa-tabs pa-tabs--full">…</div>
462
+ </div>
463
+ <div class="pa-detail-panel__body">
464
+ <div class="pa-tabs__panel pa-tabs__panel--active">…</div>
465
+ </div>
466
+ </div>
467
+
468
+ Width override via utility:
469
+ <div class="pa-detail-view__panel pa-detail-view__panel--open wr-32">…</div>
470
+ -->