@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.
- package/README.md +11 -6
- package/dist/css/main.css +47 -130
- package/package.json +1 -1
- package/snippets/AUDIT.md +94 -0
- package/snippets/alerts.html +264 -89
- package/snippets/badges.html +193 -61
- package/snippets/buttons.html +178 -0
- package/snippets/callouts.html +210 -129
- package/snippets/cards.html +383 -200
- package/snippets/checkbox-lists.html +199 -65
- package/snippets/code.html +55 -11
- package/snippets/command-palette.html +401 -111
- package/snippets/comparison.html +144 -93
- package/snippets/customization.html +311 -104
- package/snippets/data-display.html +584 -0
- package/snippets/detail-panel.html +470 -138
- package/snippets/filter-card.html +246 -0
- package/snippets/forms.html +408 -308
- package/snippets/grid.html +253 -141
- package/snippets/layout.html +379 -480
- package/snippets/lists.html +144 -47
- package/snippets/loaders.html +64 -39
- package/snippets/manifest.json +330 -280
- package/snippets/modal-dialogs.html +137 -64
- package/snippets/modals.html +221 -151
- package/snippets/notifications.html +285 -0
- package/snippets/popconfirm.html +213 -19
- package/snippets/profile.html +290 -330
- package/snippets/statistics.html +247 -0
- package/snippets/tables.html +359 -150
- package/snippets/tabs.html +129 -45
- package/snippets/timeline.html +123 -56
- package/snippets/toasts.html +179 -31
- package/snippets/tooltips.html +199 -81
- package/snippets/typography.html +183 -58
- package/snippets/utilities.html +511 -415
- package/snippets/virtual-scroll.html +201 -75
- package/snippets/web-daterangepicker.html +369 -189
- package/snippets/web-multiselect.html +360 -124
- package/src/scss/core-components/_alerts.scss +51 -12
- package/src/scss/core-components/_pagers.scss +1 -1
- package/src/scss/core-components/_popconfirm.scss +35 -13
- package/src/scss/core-components/_tables.scss +2 -134
- package/src/scss/variables/_components.scss +17 -2
|
@@ -1,138 +1,470 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
<
|
|
39
|
-
<
|
|
40
|
-
</
|
|
41
|
-
</
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
</
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
<!--
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
<
|
|
136
|
-
</div>
|
|
137
|
-
|
|
138
|
-
</
|
|
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
|
+
-->
|