@docmd/ui 0.4.8 → 0.4.10
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/assets/css/docmd-main.css +19 -6
- package/assets/js/docmd-main.js +51 -39
- package/package.json +1 -1
- package/templates/navigation.ejs +1 -1
|
@@ -203,9 +203,10 @@ a:any-link {
|
|
|
203
203
|
margin: 0
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
.sidebar nav li a,
|
|
206
|
+
.sidebar nav li a,
|
|
207
|
+
.sidebar nav li .nav-label {
|
|
207
208
|
display: block;
|
|
208
|
-
padding: .5em;
|
|
209
|
+
padding: .4em .5em;
|
|
209
210
|
margin: .15em 0;
|
|
210
211
|
text-decoration: none;
|
|
211
212
|
color: var(--sidebar-text);
|
|
@@ -214,6 +215,10 @@ a:any-link {
|
|
|
214
215
|
font-size: .9em;
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
.sidebar nav li a {
|
|
219
|
+
cursor: pointer;
|
|
220
|
+
}
|
|
221
|
+
|
|
217
222
|
.sidebar nav li>a.active {
|
|
218
223
|
color: var(--link-color);
|
|
219
224
|
font-weight: 600;
|
|
@@ -237,11 +242,18 @@ a:any-link {
|
|
|
237
242
|
opacity: 1;
|
|
238
243
|
}
|
|
239
244
|
|
|
245
|
+
.sidebar nav li .nav-label {
|
|
246
|
+
cursor: default;
|
|
247
|
+
padding: 0.25em .5em;
|
|
248
|
+
color: var(--text-color);
|
|
249
|
+
font-weight: 600;
|
|
250
|
+
}
|
|
251
|
+
|
|
240
252
|
.sidebar nav .submenu {
|
|
241
253
|
display: none;
|
|
242
254
|
padding-left: .25em;
|
|
243
255
|
border-left: 1px solid var(--link-color);
|
|
244
|
-
margin-left: .
|
|
256
|
+
margin-left: .85em;
|
|
245
257
|
}
|
|
246
258
|
|
|
247
259
|
.sidebar nav li[aria-expanded="true"]>.submenu,
|
|
@@ -1604,7 +1616,7 @@ hr {
|
|
|
1604
1616
|
|
|
1605
1617
|
.footer-complete-top {
|
|
1606
1618
|
display: flex;
|
|
1607
|
-
justify-content:
|
|
1619
|
+
justify-content: center;
|
|
1608
1620
|
flex-wrap: wrap;
|
|
1609
1621
|
gap: 2rem;
|
|
1610
1622
|
max-width: 1200px;
|
|
@@ -1625,7 +1637,7 @@ hr {
|
|
|
1625
1637
|
width: fit-content;
|
|
1626
1638
|
}
|
|
1627
1639
|
|
|
1628
|
-
.footer-brand .logo-link img{
|
|
1640
|
+
.footer-brand .logo-link img {
|
|
1629
1641
|
max-height: 30px;
|
|
1630
1642
|
width: auto;
|
|
1631
1643
|
}
|
|
@@ -1725,7 +1737,8 @@ hr {
|
|
|
1725
1737
|
color: #fb3a3a
|
|
1726
1738
|
}
|
|
1727
1739
|
|
|
1728
|
-
.branding-footer a,
|
|
1740
|
+
.branding-footer a,
|
|
1741
|
+
.page-footer a {
|
|
1729
1742
|
color: var(--link-color);
|
|
1730
1743
|
text-decoration: none
|
|
1731
1744
|
}
|
package/assets/js/docmd-main.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* [docmd-source] - Please do not remove this header.
|
|
12
12
|
* --------------------------------------------------------------------
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* --------------------------------------------------------------------
|
|
16
|
+
* docmd : Client-Side Application Logic (SPA Router & UI)
|
|
17
|
+
* --------------------------------------------------------------------
|
|
18
|
+
*/
|
|
14
19
|
|
|
15
20
|
(function() {
|
|
16
21
|
// =========================================================================
|
|
@@ -138,7 +143,7 @@
|
|
|
138
143
|
});
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
// =========================================================================
|
|
146
|
+
// =========================================================================
|
|
142
147
|
// 3. TARGETED SPA ROUTER
|
|
143
148
|
// =========================================================================
|
|
144
149
|
function initializeSPA() {
|
|
@@ -148,7 +153,7 @@
|
|
|
148
153
|
let currentPath = window.location.pathname;
|
|
149
154
|
|
|
150
155
|
document.addEventListener('click', async (e) => {
|
|
151
|
-
|
|
156
|
+
// Ignore clicks on expand/collapse arrows so they don't trigger navigation
|
|
152
157
|
if (e.target.closest('.collapse-icon-wrapper')) return;
|
|
153
158
|
|
|
154
159
|
const link = e.target.closest('.sidebar-nav a, .page-navigation a');
|
|
@@ -162,19 +167,17 @@
|
|
|
162
167
|
await navigateTo(url.href);
|
|
163
168
|
});
|
|
164
169
|
|
|
165
|
-
// Handle Back/Forward browser buttons & TOC Hash clicks
|
|
166
170
|
window.addEventListener('popstate', () => {
|
|
167
|
-
|
|
168
|
-
if (window.location.pathname === currentPath) return;
|
|
169
|
-
|
|
171
|
+
if (window.location.pathname === currentPath) return; // Ignore hash-only changes
|
|
170
172
|
navigateTo(window.location.href, false);
|
|
171
173
|
});
|
|
172
174
|
|
|
173
175
|
async function navigateTo(url, pushHistory = true) {
|
|
174
|
-
const
|
|
176
|
+
const layout = document.querySelector('.content-layout');
|
|
175
177
|
|
|
176
178
|
try {
|
|
177
|
-
|
|
179
|
+
// Lock height to prevent scrollbar jitter/dragging during DOM swap
|
|
180
|
+
if (layout) layout.style.minHeight = layout.getBoundingClientRect().height + 'px';
|
|
178
181
|
|
|
179
182
|
const res = await fetch(url);
|
|
180
183
|
if (!res.ok) throw new Error('Fetch failed');
|
|
@@ -183,19 +186,17 @@
|
|
|
183
186
|
const parser = new DOMParser();
|
|
184
187
|
const doc = parser.parseFromString(html, 'text/html');
|
|
185
188
|
|
|
186
|
-
// 1. UPDATE URL FIRST
|
|
187
189
|
if (pushHistory) history.pushState({}, '', finalUrl);
|
|
188
190
|
currentPath = new URL(finalUrl).pathname;
|
|
189
191
|
document.title = doc.title;
|
|
190
192
|
|
|
191
|
-
//
|
|
193
|
+
// Sync Assets
|
|
192
194
|
const assetSelectors = 'link[rel="stylesheet"], link[rel="icon"], link[rel="shortcut icon"]';
|
|
193
195
|
const oldAssets = Array.from(document.head.querySelectorAll(assetSelectors));
|
|
194
196
|
const newAssets = Array.from(doc.head.querySelectorAll(assetSelectors));
|
|
195
197
|
|
|
196
198
|
newAssets.forEach((newAsset, index) => {
|
|
197
199
|
if (oldAssets[index]) {
|
|
198
|
-
// Only update if the relative path actually changed
|
|
199
200
|
if (oldAssets[index].getAttribute('href') !== newAsset.getAttribute('href')) {
|
|
200
201
|
oldAssets[index].setAttribute('href', newAsset.getAttribute('href'));
|
|
201
202
|
}
|
|
@@ -204,16 +205,39 @@
|
|
|
204
205
|
}
|
|
205
206
|
});
|
|
206
207
|
|
|
207
|
-
//
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
openMenus.add(el.textContent.trim());
|
|
211
|
-
});
|
|
208
|
+
// Memorize Sidebar
|
|
209
|
+
const oldLis = Array.from(document.querySelectorAll('.sidebar-nav li'));
|
|
210
|
+
const newLis = Array.from(doc.querySelectorAll('.sidebar-nav li'));
|
|
212
211
|
|
|
213
|
-
|
|
212
|
+
oldLis.forEach((oldLi, i) => {
|
|
213
|
+
const newLi = newLis[i];
|
|
214
|
+
if (newLi) {
|
|
215
|
+
// Sync active classes
|
|
216
|
+
oldLi.classList.toggle('active', newLi.classList.contains('active'));
|
|
217
|
+
oldLi.classList.toggle('active-parent', newLi.classList.contains('active-parent'));
|
|
218
|
+
|
|
219
|
+
// Add expanded class if the new page requires it, but NEVER remove it
|
|
220
|
+
if (newLi.classList.contains('expanded')) {
|
|
221
|
+
oldLi.classList.add('expanded');
|
|
222
|
+
oldLi.setAttribute('aria-expanded', 'true');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Sync relative hrefs
|
|
226
|
+
const oldA = oldLi.querySelector('a');
|
|
227
|
+
const newA = newLi.querySelector('a');
|
|
228
|
+
if (oldA && newA) {
|
|
229
|
+
oldA.setAttribute('href', newA.getAttribute('href'));
|
|
230
|
+
oldA.classList.toggle('active', newA.classList.contains('active'));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// 3. Swap Body Components (Removed .sidebar-nav from this list)
|
|
214
236
|
const selectorsToSwap =[
|
|
215
|
-
'.
|
|
216
|
-
'.page-header .header-title',
|
|
237
|
+
'.content-layout',
|
|
238
|
+
'.page-header .header-title',
|
|
239
|
+
'.page-footer',
|
|
240
|
+
'.footer-complete',
|
|
217
241
|
'.page-footer-actions'
|
|
218
242
|
];
|
|
219
243
|
|
|
@@ -223,33 +247,27 @@
|
|
|
223
247
|
if (oldEl && newEl) oldEl.innerHTML = newEl.innerHTML;
|
|
224
248
|
});
|
|
225
249
|
|
|
226
|
-
//
|
|
227
|
-
document.querySelectorAll('.sidebar-nav li.collapsible').forEach(li => {
|
|
228
|
-
const title = li.querySelector('.nav-item-title')?.textContent.trim();
|
|
229
|
-
if (openMenus.has(title)) {
|
|
230
|
-
li.classList.add('expanded');
|
|
231
|
-
li.setAttribute('aria-expanded', 'true');
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// 6. SCROLL & RE-INIT
|
|
250
|
+
// Scroll & Init
|
|
236
251
|
const hash = new URL(finalUrl).hash;
|
|
237
252
|
if (hash) {
|
|
238
253
|
document.querySelector(hash)?.scrollIntoView();
|
|
239
254
|
} else {
|
|
240
|
-
if (mainContentWrapper) mainContentWrapper.scrollTo(0, 0);
|
|
241
255
|
window.scrollTo(0, 0);
|
|
242
256
|
}
|
|
243
257
|
|
|
244
|
-
if (mainContentWrapper) mainContentWrapper.style.opacity = '1';
|
|
245
258
|
injectCopyButtons();
|
|
246
259
|
initializeScrollSpy();
|
|
247
|
-
|
|
248
260
|
const newMainContent = document.querySelector('.main-content');
|
|
249
261
|
if (newMainContent) executeScripts(newMainContent);
|
|
250
262
|
|
|
251
263
|
document.dispatchEvent(new CustomEvent('docmd:page-mounted', { detail: { url: finalUrl } }));
|
|
252
264
|
|
|
265
|
+
// Unlock height smoothly
|
|
266
|
+
setTimeout(() => {
|
|
267
|
+
const newLayout = document.querySelector('.content-layout');
|
|
268
|
+
if (newLayout) newLayout.style.minHeight = '';
|
|
269
|
+
}, 100);
|
|
270
|
+
|
|
253
271
|
} catch(e) {
|
|
254
272
|
window.location.assign(url);
|
|
255
273
|
}
|
|
@@ -271,7 +289,6 @@
|
|
|
271
289
|
document.body.setAttribute('data-theme', t);
|
|
272
290
|
localStorage.setItem('docmd-theme', t);
|
|
273
291
|
|
|
274
|
-
// Highlight.js CSS swap
|
|
275
292
|
const lightLink = document.getElementById('hljs-light');
|
|
276
293
|
const darkLink = document.getElementById('hljs-dark');
|
|
277
294
|
if (lightLink && darkLink) {
|
|
@@ -285,19 +302,14 @@
|
|
|
285
302
|
initializeScrollSpy();
|
|
286
303
|
initializeSPA();
|
|
287
304
|
|
|
288
|
-
// Auto-scroll sidebar safely
|
|
289
305
|
setTimeout(() => {
|
|
290
306
|
const activeNav = document.querySelector('.sidebar-nav a.active');
|
|
291
307
|
const sidebarNav = document.querySelector('.sidebar-nav');
|
|
292
308
|
if (activeNav && sidebarNav) {
|
|
293
|
-
// Calculate scroll top safely instead of scrollIntoView which causes page jump
|
|
294
309
|
sidebarNav.scrollTo({ top: activeNav.offsetTop - (sidebarNav.clientHeight / 2), behavior: 'instant' });
|
|
295
310
|
}
|
|
296
|
-
|
|
297
|
-
// Ensure Hash anchors work on direct link visits (New Tab)
|
|
298
311
|
if (window.location.hash) {
|
|
299
|
-
|
|
300
|
-
if (el) el.scrollIntoView();
|
|
312
|
+
document.querySelector(window.location.hash)?.scrollIntoView();
|
|
301
313
|
}
|
|
302
314
|
}, 100);
|
|
303
315
|
});
|
package/package.json
CHANGED
package/templates/navigation.ejs
CHANGED
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
%>
|
|
85
85
|
<li class="<%= liClasses.join(' ') %>" <% if(isInteractive) { %> aria-expanded="<%= isOpen %>" <% } %>>
|
|
86
86
|
<% if (isDummyLink) { %>
|
|
87
|
-
<span class="nav-label"
|
|
87
|
+
<span class="nav-label">
|
|
88
88
|
<% if (item.icon) { %> <%- renderIcon(item.icon) %> <% } %>
|
|
89
89
|
<span class="nav-item-title"><%= item.title %></span>
|
|
90
90
|
<% if (isInteractive) { %>
|