docyard 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -1
- data/CHANGELOG.md +20 -1
- data/lib/docyard/build/asset_bundler.rb +22 -7
- data/lib/docyard/build/file_copier.rb +49 -27
- data/lib/docyard/build/sitemap_generator.rb +6 -6
- data/lib/docyard/build/static_generator.rb +85 -12
- data/lib/docyard/builder.rb +6 -6
- data/lib/docyard/config/branding_resolver.rb +126 -17
- data/lib/docyard/config/constants.rb +6 -4
- data/lib/docyard/config/validator.rb +122 -99
- data/lib/docyard/config.rb +36 -43
- data/lib/docyard/initializer.rb +15 -76
- data/lib/docyard/navigation/breadcrumb_builder.rb +133 -0
- data/lib/docyard/navigation/prev_next_builder.rb +4 -1
- data/lib/docyard/navigation/sidebar/children_discoverer.rb +51 -0
- data/lib/docyard/navigation/sidebar/config_parser.rb +136 -108
- data/lib/docyard/navigation/sidebar/file_resolver.rb +78 -0
- data/lib/docyard/navigation/sidebar/file_system_scanner.rb +2 -1
- data/lib/docyard/navigation/sidebar/item.rb +45 -7
- data/lib/docyard/navigation/sidebar/local_config_loader.rb +51 -0
- data/lib/docyard/navigation/sidebar/metadata_extractor.rb +69 -0
- data/lib/docyard/navigation/sidebar/metadata_reader.rb +47 -0
- data/lib/docyard/navigation/sidebar/path_prefixer.rb +34 -0
- data/lib/docyard/navigation/sidebar/renderer.rb +55 -37
- data/lib/docyard/navigation/sidebar/sorter.rb +21 -0
- data/lib/docyard/navigation/sidebar/tree_builder.rb +99 -26
- data/lib/docyard/navigation/sidebar/tree_filter.rb +55 -0
- data/lib/docyard/navigation/sidebar_builder.rb +105 -36
- data/lib/docyard/rendering/icon_helpers.rb +13 -0
- data/lib/docyard/rendering/icons/phosphor.rb +23 -1
- data/lib/docyard/rendering/markdown.rb +5 -0
- data/lib/docyard/rendering/renderer.rb +74 -34
- data/lib/docyard/rendering/template_resolver.rb +172 -0
- data/lib/docyard/routing/fallback_resolver.rb +92 -0
- data/lib/docyard/search/build_indexer.rb +1 -1
- data/lib/docyard/search/dev_indexer.rb +51 -6
- data/lib/docyard/search/pagefind_support.rb +2 -0
- data/lib/docyard/server/asset_handler.rb +24 -19
- data/lib/docyard/server/pagefind_handler.rb +63 -0
- data/lib/docyard/server/preview_server.rb +1 -1
- data/lib/docyard/server/rack_application.rb +81 -64
- data/lib/docyard/templates/assets/css/code.css +18 -51
- data/lib/docyard/templates/assets/css/components/breadcrumbs.css +143 -0
- data/lib/docyard/templates/assets/css/components/callout.css +67 -67
- data/lib/docyard/templates/assets/css/components/code-block.css +180 -282
- data/lib/docyard/templates/assets/css/components/heading-anchor.css +28 -15
- data/lib/docyard/templates/assets/css/components/icon.css +0 -1
- data/lib/docyard/templates/assets/css/components/logo.css +0 -2
- data/lib/docyard/templates/assets/css/components/nav-menu.css +237 -0
- data/lib/docyard/templates/assets/css/components/navigation.css +186 -167
- data/lib/docyard/templates/assets/css/components/prev-next.css +76 -47
- data/lib/docyard/templates/assets/css/components/search.css +186 -174
- data/lib/docyard/templates/assets/css/components/tab-bar.css +163 -0
- data/lib/docyard/templates/assets/css/components/table-of-contents.css +127 -114
- data/lib/docyard/templates/assets/css/components/tabs.css +119 -160
- data/lib/docyard/templates/assets/css/components/theme-toggle.css +48 -44
- data/lib/docyard/templates/assets/css/landing.css +815 -0
- data/lib/docyard/templates/assets/css/layout.css +489 -87
- data/lib/docyard/templates/assets/css/main.css +1 -3
- data/lib/docyard/templates/assets/css/markdown.css +111 -93
- data/lib/docyard/templates/assets/css/reset.css +0 -3
- data/lib/docyard/templates/assets/css/typography.css +43 -41
- data/lib/docyard/templates/assets/css/variables.css +268 -208
- data/lib/docyard/templates/assets/favicon.svg +7 -8
- data/lib/docyard/templates/assets/fonts/Inter-Variable.ttf +0 -0
- data/lib/docyard/templates/assets/js/components/code-block.js +24 -42
- data/lib/docyard/templates/assets/js/components/heading-anchor.js +26 -24
- data/lib/docyard/templates/assets/js/components/navigation.js +181 -70
- data/lib/docyard/templates/assets/js/components/search.js +0 -75
- data/lib/docyard/templates/assets/js/components/sidebar-toggle.js +29 -0
- data/lib/docyard/templates/assets/js/components/tab-navigation.js +145 -0
- data/lib/docyard/templates/assets/js/components/table-of-contents.js +153 -66
- data/lib/docyard/templates/assets/js/components/tabs.js +31 -69
- data/lib/docyard/templates/assets/js/theme.js +0 -3
- data/lib/docyard/templates/assets/logo-dark.svg +8 -2
- data/lib/docyard/templates/assets/logo.svg +7 -4
- data/lib/docyard/templates/config/docyard.yml.erb +37 -34
- data/lib/docyard/templates/errors/404.html.erb +1 -1
- data/lib/docyard/templates/errors/500.html.erb +1 -1
- data/lib/docyard/templates/layouts/default.html.erb +18 -67
- data/lib/docyard/templates/layouts/splash.html.erb +176 -0
- data/lib/docyard/templates/partials/_breadcrumbs.html.erb +24 -0
- data/lib/docyard/templates/partials/_code_block.html.erb +5 -3
- data/lib/docyard/templates/partials/_doc_footer.html.erb +25 -0
- data/lib/docyard/templates/partials/_features.html.erb +15 -0
- data/lib/docyard/templates/partials/_footer.html.erb +42 -0
- data/lib/docyard/templates/partials/_head.html.erb +22 -0
- data/lib/docyard/templates/partials/_header.html.erb +49 -0
- data/lib/docyard/templates/partials/_heading_anchor.html.erb +3 -1
- data/lib/docyard/templates/partials/_hero.html.erb +27 -0
- data/lib/docyard/templates/partials/_nav_group.html.erb +25 -11
- data/lib/docyard/templates/partials/_nav_leaf.html.erb +1 -1
- data/lib/docyard/templates/partials/_nav_menu.html.erb +42 -0
- data/lib/docyard/templates/partials/_nav_nested_section.html.erb +11 -0
- data/lib/docyard/templates/partials/_nav_section.html.erb +1 -1
- data/lib/docyard/templates/partials/_prev_next.html.erb +8 -2
- data/lib/docyard/templates/partials/_scripts.html.erb +7 -0
- data/lib/docyard/templates/partials/_search_modal.html.erb +2 -6
- data/lib/docyard/templates/partials/_search_trigger.html.erb +2 -6
- data/lib/docyard/templates/partials/_sidebar.html.erb +21 -4
- data/lib/docyard/templates/partials/_tab_bar.html.erb +25 -0
- data/lib/docyard/templates/partials/_table_of_contents.html.erb +12 -12
- data/lib/docyard/templates/partials/_table_of_contents_toggle.html.erb +1 -3
- data/lib/docyard/templates/partials/_tabs.html.erb +2 -2
- data/lib/docyard/templates/partials/_theme_toggle.html.erb +2 -11
- data/lib/docyard/version.rb +1 -1
- metadata +33 -5
- data/lib/docyard/templates/markdown/getting-started/installation.md.erb +0 -77
- data/lib/docyard/templates/markdown/guides/configuration.md.erb +0 -202
- data/lib/docyard/templates/markdown/guides/markdown-features.md.erb +0 -247
- data/lib/docyard/templates/markdown/index.md.erb +0 -82
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TableOfContentsManager handles TOC interactions and scroll tracking
|
|
3
|
-
* Features: scroll spy, active highlighting, smooth scrolling, mobile toggle
|
|
4
|
-
*/
|
|
5
1
|
class TableOfContentsManager {
|
|
6
2
|
constructor() {
|
|
7
3
|
this.toc = document.querySelector('.docyard-toc');
|
|
@@ -20,51 +16,80 @@ class TableOfContentsManager {
|
|
|
20
16
|
init() {
|
|
21
17
|
if (this.headings.length === 0) return;
|
|
22
18
|
|
|
19
|
+
this.aside = document.querySelector('.doc-aside');
|
|
20
|
+
this.createIndicator();
|
|
23
21
|
this.setupScrollSpy();
|
|
24
22
|
this.setupSmoothScrolling();
|
|
25
23
|
this.setupMobileToggle();
|
|
26
24
|
this.setupKeyboardNavigation();
|
|
25
|
+
this.setupScrollFadeIndicators();
|
|
26
|
+
this.setupAsideOverflowDetection();
|
|
27
27
|
this.handleInitialHash();
|
|
28
|
+
|
|
29
|
+
requestAnimationFrame(() => {
|
|
30
|
+
this.updateIndicator();
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
createIndicator() {
|
|
36
|
+
const list = this.toc.querySelector('.docyard-toc__list');
|
|
37
|
+
if (!list) return;
|
|
38
|
+
|
|
39
|
+
this.indicator = document.createElement('div');
|
|
40
|
+
this.indicator.className = 'docyard-toc__indicator';
|
|
41
|
+
this.indicator.style.opacity = '0';
|
|
42
|
+
list.appendChild(this.indicator);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
updateIndicator() {
|
|
47
|
+
if (!this.indicator || !this.activeLink) return;
|
|
48
|
+
|
|
49
|
+
const list = this.toc.querySelector('.docyard-toc__list');
|
|
50
|
+
if (!list) return;
|
|
51
|
+
|
|
52
|
+
const listRect = list.getBoundingClientRect();
|
|
53
|
+
const linkRect = this.activeLink.getBoundingClientRect();
|
|
54
|
+
|
|
55
|
+
const top = linkRect.top - listRect.top;
|
|
56
|
+
const height = linkRect.height;
|
|
57
|
+
|
|
58
|
+
this.indicator.style.top = `${top}px`;
|
|
59
|
+
this.indicator.style.height = `${height}px`;
|
|
60
|
+
this.indicator.style.opacity = '1';
|
|
28
61
|
}
|
|
29
62
|
|
|
30
|
-
|
|
31
|
-
* Check if viewport is mobile/tablet (TOC in secondary header)
|
|
32
|
-
* @returns {boolean}
|
|
33
|
-
*/
|
|
63
|
+
|
|
34
64
|
isMobile() {
|
|
35
65
|
return window.innerWidth <= 1280;
|
|
36
66
|
}
|
|
37
67
|
|
|
38
|
-
|
|
39
|
-
* Check if viewport is tablet (both headers visible)
|
|
40
|
-
* @returns {boolean}
|
|
41
|
-
*/
|
|
68
|
+
|
|
42
69
|
isTablet() {
|
|
43
70
|
return window.innerWidth > 1024 && window.innerWidth <= 1280;
|
|
44
71
|
}
|
|
45
72
|
|
|
46
|
-
|
|
47
|
-
* Get scroll offset based on viewport and header state
|
|
48
|
-
* @returns {number}
|
|
49
|
-
*/
|
|
73
|
+
|
|
50
74
|
getScrollOffset() {
|
|
75
|
+
const hasTabs = document.body.classList.contains('has-tabs');
|
|
76
|
+
const headerHeight = 64;
|
|
77
|
+
const tabBarHeight = hasTabs ? 48 : 0;
|
|
78
|
+
const buffer = 24;
|
|
79
|
+
|
|
51
80
|
if (this.isTablet()) {
|
|
52
|
-
return
|
|
81
|
+
return headerHeight + 48 + buffer;
|
|
53
82
|
}
|
|
54
83
|
|
|
55
84
|
if (window.innerWidth <= 1024) {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
return isHeaderHidden ? 128 : 64;
|
|
85
|
+
const secondaryHeaderHeight = 48;
|
|
86
|
+
return headerHeight + secondaryHeaderHeight + buffer;
|
|
59
87
|
}
|
|
60
88
|
|
|
61
|
-
return
|
|
89
|
+
return headerHeight + tabBarHeight + buffer;
|
|
62
90
|
}
|
|
63
91
|
|
|
64
|
-
|
|
65
|
-
* Get all headings referenced in TOC
|
|
66
|
-
* @returns {Array<HTMLElement>}
|
|
67
|
-
*/
|
|
92
|
+
|
|
68
93
|
getHeadings() {
|
|
69
94
|
return this.links
|
|
70
95
|
.map(link => {
|
|
@@ -74,9 +99,7 @@ class TableOfContentsManager {
|
|
|
74
99
|
.filter(Boolean);
|
|
75
100
|
}
|
|
76
101
|
|
|
77
|
-
|
|
78
|
-
* Setup Intersection Observer for scroll spy
|
|
79
|
-
*/
|
|
102
|
+
|
|
80
103
|
setupScrollSpy() {
|
|
81
104
|
const options = {
|
|
82
105
|
root: null,
|
|
@@ -97,10 +120,7 @@ class TableOfContentsManager {
|
|
|
97
120
|
});
|
|
98
121
|
}
|
|
99
122
|
|
|
100
|
-
|
|
101
|
-
* Set active link in TOC
|
|
102
|
-
* @param {string} id - Heading ID
|
|
103
|
-
*/
|
|
123
|
+
|
|
104
124
|
setActiveLink(id) {
|
|
105
125
|
const link = this.links.find(l => l.dataset.headingId === id);
|
|
106
126
|
if (!link || link === this.activeLink) return;
|
|
@@ -112,22 +132,23 @@ class TableOfContentsManager {
|
|
|
112
132
|
link.classList.add('is-active');
|
|
113
133
|
this.activeLink = link;
|
|
114
134
|
|
|
135
|
+
this.updateIndicator();
|
|
115
136
|
this.scrollLinkIntoView(link);
|
|
116
137
|
}
|
|
117
138
|
|
|
118
|
-
|
|
119
|
-
* Scroll TOC to keep active link visible
|
|
120
|
-
* @param {HTMLElement} link - Active link element
|
|
121
|
-
*/
|
|
139
|
+
|
|
122
140
|
scrollLinkIntoView(link) {
|
|
123
141
|
if (this.isMobile()) return;
|
|
124
142
|
|
|
143
|
+
const scrollContainer = this.toc.querySelector('.docyard-toc__scroll');
|
|
144
|
+
if (!scrollContainer) return;
|
|
145
|
+
|
|
125
146
|
if (!this.ticking) {
|
|
126
147
|
requestAnimationFrame(() => {
|
|
127
|
-
const
|
|
148
|
+
const containerRect = scrollContainer.getBoundingClientRect();
|
|
128
149
|
const linkRect = link.getBoundingClientRect();
|
|
129
150
|
|
|
130
|
-
if (linkRect.top <
|
|
151
|
+
if (linkRect.top < containerRect.top || linkRect.bottom > containerRect.bottom) {
|
|
131
152
|
link.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
132
153
|
}
|
|
133
154
|
|
|
@@ -137,9 +158,7 @@ class TableOfContentsManager {
|
|
|
137
158
|
}
|
|
138
159
|
}
|
|
139
160
|
|
|
140
|
-
|
|
141
|
-
* Setup smooth scrolling for TOC links
|
|
142
|
-
*/
|
|
161
|
+
|
|
143
162
|
setupSmoothScrolling() {
|
|
144
163
|
this.links.forEach(link => {
|
|
145
164
|
link.addEventListener('click', (e) => {
|
|
@@ -160,7 +179,6 @@ class TableOfContentsManager {
|
|
|
160
179
|
|
|
161
180
|
heading.focus({ preventScroll: true });
|
|
162
181
|
|
|
163
|
-
// Close mobile menu after clicking a link
|
|
164
182
|
if (this.isMobile()) {
|
|
165
183
|
this.collapseMobile();
|
|
166
184
|
}
|
|
@@ -169,9 +187,7 @@ class TableOfContentsManager {
|
|
|
169
187
|
});
|
|
170
188
|
}
|
|
171
189
|
|
|
172
|
-
|
|
173
|
-
* Setup mobile toggle functionality
|
|
174
|
-
*/
|
|
190
|
+
|
|
175
191
|
setupMobileToggle() {
|
|
176
192
|
const secondaryToggle = document.querySelector('.secondary-header-toc-toggle');
|
|
177
193
|
|
|
@@ -186,12 +202,12 @@ class TableOfContentsManager {
|
|
|
186
202
|
|
|
187
203
|
if (!isExpanded) {
|
|
188
204
|
document.body.style.overflow = 'hidden';
|
|
205
|
+
requestAnimationFrame(() => this.updateIndicator());
|
|
189
206
|
} else {
|
|
190
207
|
document.body.style.overflow = '';
|
|
191
208
|
}
|
|
192
209
|
});
|
|
193
210
|
|
|
194
|
-
// Close menu when clicking outside (mobile only)
|
|
195
211
|
document.addEventListener('click', (e) => {
|
|
196
212
|
if (!this.isMobile()) return;
|
|
197
213
|
|
|
@@ -205,11 +221,19 @@ class TableOfContentsManager {
|
|
|
205
221
|
this.collapseMobile();
|
|
206
222
|
}
|
|
207
223
|
});
|
|
224
|
+
|
|
225
|
+
document.addEventListener('keydown', (e) => {
|
|
226
|
+
if (e.key === 'Escape') {
|
|
227
|
+
const isExpanded = secondaryToggle.getAttribute('aria-expanded') === 'true';
|
|
228
|
+
if (isExpanded) {
|
|
229
|
+
this.collapseMobile();
|
|
230
|
+
secondaryToggle.focus();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
});
|
|
208
234
|
}
|
|
209
235
|
|
|
210
|
-
|
|
211
|
-
* Collapse mobile TOC
|
|
212
|
-
*/
|
|
236
|
+
|
|
213
237
|
collapseMobile() {
|
|
214
238
|
const secondaryToggle = document.querySelector('.secondary-header-toc-toggle');
|
|
215
239
|
if (secondaryToggle) {
|
|
@@ -221,9 +245,7 @@ class TableOfContentsManager {
|
|
|
221
245
|
}
|
|
222
246
|
}
|
|
223
247
|
|
|
224
|
-
|
|
225
|
-
* Setup keyboard navigation
|
|
226
|
-
*/
|
|
248
|
+
|
|
227
249
|
setupKeyboardNavigation() {
|
|
228
250
|
this.links.forEach((link, index) => {
|
|
229
251
|
link.addEventListener('keydown', (e) => {
|
|
@@ -257,9 +279,78 @@ class TableOfContentsManager {
|
|
|
257
279
|
});
|
|
258
280
|
}
|
|
259
281
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
282
|
+
|
|
283
|
+
setupAsideOverflowDetection() {
|
|
284
|
+
if (!this.aside) return;
|
|
285
|
+
|
|
286
|
+
const checkOverflow = () => {
|
|
287
|
+
if (this.isMobile()) {
|
|
288
|
+
this.aside.classList.remove('has-overflow');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this.aside.classList.remove('has-overflow');
|
|
293
|
+
|
|
294
|
+
const footer = this.aside.querySelector('.doc-footer-desktop');
|
|
295
|
+
const tocScroll = this.toc.querySelector('.docyard-toc__scroll');
|
|
296
|
+
if (!footer || !tocScroll) return;
|
|
297
|
+
|
|
298
|
+
requestAnimationFrame(() => {
|
|
299
|
+
const asideHeight = this.aside.clientHeight;
|
|
300
|
+
const tocNaturalHeight = this.toc.offsetHeight;
|
|
301
|
+
const footerHeight = footer.offsetHeight;
|
|
302
|
+
const totalContentHeight = tocNaturalHeight + footerHeight;
|
|
303
|
+
|
|
304
|
+
if (totalContentHeight > asideHeight) {
|
|
305
|
+
this.aside.classList.add('has-overflow');
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
checkOverflow();
|
|
311
|
+
window.addEventListener('resize', checkOverflow);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
setupScrollFadeIndicators() {
|
|
315
|
+
const scrollContainer = this.toc.querySelector('.docyard-toc__scroll');
|
|
316
|
+
if (!scrollContainer) return;
|
|
317
|
+
|
|
318
|
+
const updateFadeIndicators = () => {
|
|
319
|
+
if (this.isMobile()) {
|
|
320
|
+
this.toc.classList.remove('can-scroll-top', 'can-scroll-bottom');
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const scrollTop = scrollContainer.scrollTop;
|
|
325
|
+
const scrollHeight = scrollContainer.scrollHeight;
|
|
326
|
+
const clientHeight = scrollContainer.clientHeight;
|
|
327
|
+
const threshold = 10;
|
|
328
|
+
|
|
329
|
+
if (scrollTop > threshold) {
|
|
330
|
+
this.toc.classList.add('can-scroll-top');
|
|
331
|
+
} else {
|
|
332
|
+
this.toc.classList.remove('can-scroll-top');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (scrollTop + clientHeight < scrollHeight - threshold) {
|
|
336
|
+
this.toc.classList.add('can-scroll-bottom');
|
|
337
|
+
} else {
|
|
338
|
+
this.toc.classList.remove('can-scroll-bottom');
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
updateFadeIndicators();
|
|
343
|
+
scrollContainer.addEventListener('scroll', () => {
|
|
344
|
+
updateFadeIndicators();
|
|
345
|
+
this.updateIndicator();
|
|
346
|
+
});
|
|
347
|
+
window.addEventListener('resize', () => {
|
|
348
|
+
updateFadeIndicators();
|
|
349
|
+
this.updateIndicator();
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
263
354
|
handleInitialHash() {
|
|
264
355
|
const hash = window.location.hash.slice(1);
|
|
265
356
|
if (!hash) return;
|
|
@@ -267,20 +358,16 @@ class TableOfContentsManager {
|
|
|
267
358
|
const heading = document.getElementById(hash);
|
|
268
359
|
if (!heading) return;
|
|
269
360
|
|
|
270
|
-
|
|
271
|
-
this.setActiveLink(hash);
|
|
361
|
+
this.setActiveLink(hash);
|
|
272
362
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}, 100);
|
|
363
|
+
const offsetTop = heading.getBoundingClientRect().top + window.pageYOffset - this.getScrollOffset();
|
|
364
|
+
window.scrollTo({
|
|
365
|
+
top: offsetTop,
|
|
366
|
+
behavior: 'instant'
|
|
367
|
+
});
|
|
279
368
|
}
|
|
280
369
|
|
|
281
|
-
|
|
282
|
-
* Cleanup and destroy
|
|
283
|
-
*/
|
|
370
|
+
|
|
284
371
|
destroy() {
|
|
285
372
|
if (this.observer) {
|
|
286
373
|
this.observer.disconnect();
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TabsManager - Manages tab component interactions
|
|
3
|
-
*
|
|
4
|
-
* @class TabsManager
|
|
5
|
-
*/
|
|
6
1
|
class TabsManager {
|
|
7
|
-
|
|
8
|
-
* Create a TabsManager instance
|
|
9
|
-
* @param {HTMLElement} container - The .docyard-tabs container element
|
|
10
|
-
*/
|
|
2
|
+
|
|
11
3
|
constructor(container) {
|
|
12
4
|
if (!container) return;
|
|
13
5
|
|
|
@@ -28,9 +20,7 @@ class TabsManager {
|
|
|
28
20
|
this.init();
|
|
29
21
|
}
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
* Initialize the tabs component
|
|
33
|
-
*/
|
|
23
|
+
|
|
34
24
|
init() {
|
|
35
25
|
if (!this.tabList || this.tabs.length === 0 || this.panels.length === 0) {
|
|
36
26
|
return;
|
|
@@ -41,12 +31,15 @@ class TabsManager {
|
|
|
41
31
|
this.attachEventListeners();
|
|
42
32
|
this.activateTab(this.activeIndex, false);
|
|
43
33
|
this.updateIndicator();
|
|
44
|
-
|
|
34
|
+
|
|
35
|
+
requestAnimationFrame(() => {
|
|
36
|
+
requestAnimationFrame(() => {
|
|
37
|
+
this.updateScrollIndicators();
|
|
38
|
+
});
|
|
39
|
+
});
|
|
45
40
|
}
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
* Create scroll indicator elements
|
|
49
|
-
*/
|
|
42
|
+
|
|
50
43
|
createScrollIndicators() {
|
|
51
44
|
if (!this.tabListWrapper || !this.tabList) return;
|
|
52
45
|
|
|
@@ -59,9 +52,7 @@ class TabsManager {
|
|
|
59
52
|
this.tabListWrapper.appendChild(this.rightIndicator);
|
|
60
53
|
}
|
|
61
54
|
|
|
62
|
-
|
|
63
|
-
* Attach all event listeners
|
|
64
|
-
*/
|
|
55
|
+
|
|
65
56
|
attachEventListeners() {
|
|
66
57
|
this.tabs.forEach((tab, index) => {
|
|
67
58
|
tab.addEventListener('click', () => this.handleTabClick(index));
|
|
@@ -74,10 +65,7 @@ class TabsManager {
|
|
|
74
65
|
window.addEventListener('resize', this.handleResize);
|
|
75
66
|
}
|
|
76
67
|
|
|
77
|
-
|
|
78
|
-
* Handle tab click
|
|
79
|
-
* @param {number} index - Index of clicked tab
|
|
80
|
-
*/
|
|
68
|
+
|
|
81
69
|
handleTabClick(index) {
|
|
82
70
|
if (index === this.activeIndex) return;
|
|
83
71
|
|
|
@@ -85,10 +73,7 @@ class TabsManager {
|
|
|
85
73
|
this.savePreference(index);
|
|
86
74
|
}
|
|
87
75
|
|
|
88
|
-
|
|
89
|
-
* Handle keyboard navigation
|
|
90
|
-
* @param {KeyboardEvent} event - Keyboard event
|
|
91
|
-
*/
|
|
76
|
+
|
|
92
77
|
handleKeyDown(event) {
|
|
93
78
|
const { key } = event;
|
|
94
79
|
|
|
@@ -118,9 +103,7 @@ class TabsManager {
|
|
|
118
103
|
}
|
|
119
104
|
}
|
|
120
105
|
|
|
121
|
-
|
|
122
|
-
* Handle scroll event - update scroll indicators
|
|
123
|
-
*/
|
|
106
|
+
|
|
124
107
|
handleScroll() {
|
|
125
108
|
if (this.scrollTimeout) {
|
|
126
109
|
cancelAnimationFrame(this.scrollTimeout);
|
|
@@ -131,9 +114,7 @@ class TabsManager {
|
|
|
131
114
|
});
|
|
132
115
|
}
|
|
133
116
|
|
|
134
|
-
|
|
135
|
-
* Handle window resize - update indicator and scroll indicators
|
|
136
|
-
*/
|
|
117
|
+
|
|
137
118
|
handleResize() {
|
|
138
119
|
if (this.resizeTimeout) {
|
|
139
120
|
cancelAnimationFrame(this.resizeTimeout);
|
|
@@ -145,17 +126,15 @@ class TabsManager {
|
|
|
145
126
|
});
|
|
146
127
|
}
|
|
147
128
|
|
|
148
|
-
|
|
149
|
-
* Activate a specific tab
|
|
150
|
-
* @param {number} index - Index of tab to activate
|
|
151
|
-
* @param {boolean} animate - Whether to animate the transition
|
|
152
|
-
*/
|
|
129
|
+
|
|
153
130
|
activateTab(index, animate = true) {
|
|
154
131
|
if (index < 0 || index >= this.tabs.length) return;
|
|
155
132
|
|
|
156
133
|
const previousIndex = this.activeIndex;
|
|
157
134
|
this.activeIndex = index;
|
|
158
135
|
|
|
136
|
+
const direction = index > previousIndex ? 'right' : 'left';
|
|
137
|
+
|
|
159
138
|
this.tabs.forEach((tab, i) => {
|
|
160
139
|
const isActive = i === index;
|
|
161
140
|
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
|
@@ -165,6 +144,12 @@ class TabsManager {
|
|
|
165
144
|
this.panels.forEach((panel, i) => {
|
|
166
145
|
const isActive = i === index;
|
|
167
146
|
panel.setAttribute('aria-hidden', isActive ? 'false' : 'true');
|
|
147
|
+
|
|
148
|
+
if (isActive && animate && previousIndex !== index) {
|
|
149
|
+
panel.setAttribute('data-direction', direction);
|
|
150
|
+
} else {
|
|
151
|
+
panel.removeAttribute('data-direction');
|
|
152
|
+
}
|
|
168
153
|
});
|
|
169
154
|
|
|
170
155
|
this.updateIndicator(animate);
|
|
@@ -174,26 +159,19 @@ class TabsManager {
|
|
|
174
159
|
}
|
|
175
160
|
}
|
|
176
161
|
|
|
177
|
-
|
|
178
|
-
* Activate the next tab (wraps around)
|
|
179
|
-
*/
|
|
162
|
+
|
|
180
163
|
activateNextTab() {
|
|
181
164
|
const nextIndex = (this.activeIndex + 1) % this.tabs.length;
|
|
182
165
|
this.activateTab(nextIndex, true);
|
|
183
166
|
}
|
|
184
167
|
|
|
185
|
-
|
|
186
|
-
* Activate the previous tab (wraps around)
|
|
187
|
-
*/
|
|
168
|
+
|
|
188
169
|
activatePreviousTab() {
|
|
189
170
|
const prevIndex = (this.activeIndex - 1 + this.tabs.length) % this.tabs.length;
|
|
190
171
|
this.activateTab(prevIndex, true);
|
|
191
172
|
}
|
|
192
173
|
|
|
193
|
-
|
|
194
|
-
* Update the visual indicator position
|
|
195
|
-
* @param {boolean} animate - Whether to animate the transition
|
|
196
|
-
*/
|
|
174
|
+
|
|
197
175
|
updateIndicator(animate = true) {
|
|
198
176
|
if (!this.indicator || !this.tabs[this.activeIndex]) return;
|
|
199
177
|
|
|
@@ -214,9 +192,7 @@ class TabsManager {
|
|
|
214
192
|
}
|
|
215
193
|
}
|
|
216
194
|
|
|
217
|
-
|
|
218
|
-
* Update scroll indicators visibility based on scroll position
|
|
219
|
-
*/
|
|
195
|
+
|
|
220
196
|
updateScrollIndicators() {
|
|
221
197
|
if (!this.tabList || !this.leftIndicator || !this.rightIndicator) return;
|
|
222
198
|
|
|
@@ -244,9 +220,7 @@ class TabsManager {
|
|
|
244
220
|
}
|
|
245
221
|
}
|
|
246
222
|
|
|
247
|
-
|
|
248
|
-
* Load user preference from localStorage
|
|
249
|
-
*/
|
|
223
|
+
|
|
250
224
|
loadPreference() {
|
|
251
225
|
try {
|
|
252
226
|
const preferredTab = localStorage.getItem('docyard-preferred-pm');
|
|
@@ -264,10 +238,7 @@ class TabsManager {
|
|
|
264
238
|
}
|
|
265
239
|
}
|
|
266
240
|
|
|
267
|
-
|
|
268
|
-
* Save user preference to localStorage
|
|
269
|
-
* @param {number} index - Index of active tab
|
|
270
|
-
*/
|
|
241
|
+
|
|
271
242
|
savePreference(index) {
|
|
272
243
|
if (index < 0 || index >= this.tabs.length) return;
|
|
273
244
|
|
|
@@ -279,10 +250,7 @@ class TabsManager {
|
|
|
279
250
|
}
|
|
280
251
|
}
|
|
281
252
|
|
|
282
|
-
|
|
283
|
-
* Activate tab by name
|
|
284
|
-
* @param {string} name - Name of tab to activate
|
|
285
|
-
*/
|
|
253
|
+
|
|
286
254
|
activateTabByName(name) {
|
|
287
255
|
const index = this.tabs.findIndex(tab =>
|
|
288
256
|
tab.textContent.trim().toLowerCase() === name.toLowerCase()
|
|
@@ -293,9 +261,7 @@ class TabsManager {
|
|
|
293
261
|
}
|
|
294
262
|
}
|
|
295
263
|
|
|
296
|
-
|
|
297
|
-
* Cleanup - remove event listeners
|
|
298
|
-
*/
|
|
264
|
+
|
|
299
265
|
destroy() {
|
|
300
266
|
this.tabs.forEach((tab, index) => {
|
|
301
267
|
tab.removeEventListener('click', () => this.handleTabClick(index));
|
|
@@ -315,9 +281,6 @@ class TabsManager {
|
|
|
315
281
|
}
|
|
316
282
|
}
|
|
317
283
|
|
|
318
|
-
/**
|
|
319
|
-
* Auto-initialize all tabs on page load
|
|
320
|
-
*/
|
|
321
284
|
function initializeTabs() {
|
|
322
285
|
const tabsContainers = document.querySelectorAll('.docyard-tabs');
|
|
323
286
|
|
|
@@ -326,7 +289,6 @@ function initializeTabs() {
|
|
|
326
289
|
});
|
|
327
290
|
}
|
|
328
291
|
|
|
329
|
-
// Initialize on DOM ready
|
|
330
292
|
if (document.readyState === 'loading') {
|
|
331
293
|
document.addEventListener('DOMContentLoaded', initializeTabs);
|
|
332
294
|
} else {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
<svg width="531" height="769" viewBox="0 0 531 769" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
-
<
|
|
3
|
-
<
|
|
2
|
+
<defs>
|
|
3
|
+
<style>
|
|
4
|
+
.logo-accent { fill: #22D3EE; }
|
|
5
|
+
.logo-dark { fill: #FFFFFF; }
|
|
6
|
+
</style>
|
|
7
|
+
</defs>
|
|
8
|
+
<path class="logo-accent" d="M359.643 59.1798C402.213 89.4398 449.713 123.6 502.063 160.99C510.793 167.23 515.873 170.31 519.293 178.05C523.253 187.02 521.733 198.11 515.883 205.77C513.77 208.536 510.93 211.2 507.363 213.76C379.643 305.353 309.413 355.73 296.673 364.89C287.987 371.136 282.07 374.8 278.923 375.88C269.703 379.026 260.263 378.636 250.603 374.71C248.243 373.75 244.497 371.416 239.363 367.71C199.963 339.29 177.32 322.99 171.433 318.81C128.863 288.54 81.3733 254.39 29.0233 216.99C20.2833 210.75 15.2033 207.67 11.7833 199.93C7.82332 190.96 9.34332 179.87 15.1933 172.21C17.3067 169.443 20.1467 166.78 23.7133 164.22C151.433 72.6264 221.663 22.2498 234.403 13.0898C243.09 6.84309 249.007 3.17976 252.153 2.09976C261.373 -1.04691 270.813 -0.656912 280.473 3.26976C282.833 4.22976 286.58 6.56309 291.713 10.2698C331.113 38.6898 353.757 54.9931 359.643 59.1798Z"/>
|
|
9
|
+
<path class="logo-dark" d="M467.383 298.01C483.943 286.23 505.033 289.93 519.063 303.51C524.457 308.723 528.033 314.713 529.793 321.48C530.433 323.92 530.733 330.946 530.693 342.56C530.647 356.206 530.657 427.233 530.723 555.64C530.723 566.633 530.513 573 530.093 574.74C527.033 587.29 518.333 592.61 506.693 601.06C504.313 602.786 430.877 656.346 286.383 761.74C275.623 769.59 261.793 770.79 250.113 764.36C249.18 763.846 245.86 761.513 240.153 757.36C150.56 692.066 74.8667 637.046 13.0733 592.3C6.70001 587.68 2.65667 581.73 0.943337 574.45C0.316671 571.783 0.00333476 564.803 0.00333476 553.51C-0.00333191 421.323 -4.06895e-06 348.98 0.0133293 336.48C0.0133293 332.84 -0.0766665 327.18 0.783334 323.18C4.59333 305.51 20.1033 293.29 37.4533 291.15C42.9467 290.476 48.8667 291.276 55.2133 293.55C58.28 294.643 63.3533 297.8 70.4333 303.02C75.98 307.113 82.4433 311.78 89.8233 317.02C128.563 344.526 178.703 380.303 240.243 424.35C242.73 426.13 245.853 428.246 249.613 430.7C257.443 435.8 268.453 436.24 277.213 433.14C279.8 432.22 284.54 429.283 291.433 424.33C394.46 350.276 453.11 308.17 467.383 298.01Z"/>
|
|
4
10
|
</svg>
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
<svg width="531" height="769" viewBox="0 0 531 769" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
2
|
<defs>
|
|
3
3
|
<style>
|
|
4
|
+
.logo-accent { fill: #06B6D4; }
|
|
5
|
+
.logo-dark { fill: #18181B; }
|
|
6
|
+
|
|
4
7
|
@media (prefers-color-scheme: dark) {
|
|
5
|
-
|
|
6
|
-
|
|
8
|
+
.logo-accent { fill: #22D3EE; }
|
|
9
|
+
.logo-dark { fill: #E4E4E7; }
|
|
7
10
|
}
|
|
8
11
|
</style>
|
|
9
12
|
</defs>
|
|
10
|
-
<path d="M359.643 59.1798C402.213 89.4398 449.713 123.6 502.063 160.99C510.793 167.23 515.873 170.31 519.293 178.05C523.253 187.02 521.733 198.11 515.883 205.77C513.77 208.536 510.93 211.2 507.363 213.76C379.643 305.353 309.413 355.73 296.673 364.89C287.987 371.136 282.07 374.8 278.923 375.88C269.703 379.026 260.263 378.636 250.603 374.71C248.243 373.75 244.497 371.416 239.363 367.71C199.963 339.29 177.32 322.99 171.433 318.81C128.863 288.54 81.3733 254.39 29.0233 216.99C20.2833 210.75 15.2033 207.67 11.7833 199.93C7.82332 190.96 9.34332 179.87 15.1933 172.21C17.3067 169.443 20.1467 166.78 23.7133 164.22C151.433 72.6264 221.663 22.2498 234.403 13.0898C243.09 6.84309 249.007 3.17976 252.153 2.09976C261.373 -1.04691 270.813 -0.656912 280.473 3.26976C282.833 4.22976 286.58 6.56309 291.713 10.2698C331.113 38.6898 353.757 54.9931 359.643 59.1798Z"
|
|
11
|
-
<path d="M467.383 298.01C483.943 286.23 505.033 289.93 519.063 303.51C524.457 308.723 528.033 314.713 529.793 321.48C530.433 323.92 530.733 330.946 530.693 342.56C530.647 356.206 530.657 427.233 530.723 555.64C530.723 566.633 530.513 573 530.093 574.74C527.033 587.29 518.333 592.61 506.693 601.06C504.313 602.786 430.877 656.346 286.383 761.74C275.623 769.59 261.793 770.79 250.113 764.36C249.18 763.846 245.86 761.513 240.153 757.36C150.56 692.066 74.8667 637.046 13.0733 592.3C6.70001 587.68 2.65667 581.73 0.943337 574.45C0.316671 571.783 0.00333476 564.803 0.00333476 553.51C-0.00333191 421.323 -4.06895e-06 348.98 0.0133293 336.48C0.0133293 332.84 -0.0766665 327.18 0.783334 323.18C4.59333 305.51 20.1033 293.29 37.4533 291.15C42.9467 290.476 48.8667 291.276 55.2133 293.55C58.28 294.643 63.3533 297.8 70.4333 303.02C75.98 307.113 82.4433 311.78 89.8233 317.02C128.563 344.526 178.703 380.303 240.243 424.35C242.73 426.13 245.853 428.246 249.613 430.7C257.443 435.8 268.453 436.24 277.213 433.14C279.8 432.22 284.54 429.283 291.433 424.33C394.46 350.276 453.11 308.17 467.383 298.01Z"
|
|
13
|
+
<path class="logo-accent" d="M359.643 59.1798C402.213 89.4398 449.713 123.6 502.063 160.99C510.793 167.23 515.873 170.31 519.293 178.05C523.253 187.02 521.733 198.11 515.883 205.77C513.77 208.536 510.93 211.2 507.363 213.76C379.643 305.353 309.413 355.73 296.673 364.89C287.987 371.136 282.07 374.8 278.923 375.88C269.703 379.026 260.263 378.636 250.603 374.71C248.243 373.75 244.497 371.416 239.363 367.71C199.963 339.29 177.32 322.99 171.433 318.81C128.863 288.54 81.3733 254.39 29.0233 216.99C20.2833 210.75 15.2033 207.67 11.7833 199.93C7.82332 190.96 9.34332 179.87 15.1933 172.21C17.3067 169.443 20.1467 166.78 23.7133 164.22C151.433 72.6264 221.663 22.2498 234.403 13.0898C243.09 6.84309 249.007 3.17976 252.153 2.09976C261.373 -1.04691 270.813 -0.656912 280.473 3.26976C282.833 4.22976 286.58 6.56309 291.713 10.2698C331.113 38.6898 353.757 54.9931 359.643 59.1798Z"/>
|
|
14
|
+
<path class="logo-dark" d="M467.383 298.01C483.943 286.23 505.033 289.93 519.063 303.51C524.457 308.723 528.033 314.713 529.793 321.48C530.433 323.92 530.733 330.946 530.693 342.56C530.647 356.206 530.657 427.233 530.723 555.64C530.723 566.633 530.513 573 530.093 574.74C527.033 587.29 518.333 592.61 506.693 601.06C504.313 602.786 430.877 656.346 286.383 761.74C275.623 769.59 261.793 770.79 250.113 764.36C249.18 763.846 245.86 761.513 240.153 757.36C150.56 692.066 74.8667 637.046 13.0733 592.3C6.70001 587.68 2.65667 581.73 0.943337 574.45C0.316671 571.783 0.00333476 564.803 0.00333476 553.51C-0.00333191 421.323 -4.06895e-06 348.98 0.0133293 336.48C0.0133293 332.84 -0.0766665 327.18 0.783334 323.18C4.59333 305.51 20.1033 293.29 37.4533 291.15C42.9467 290.476 48.8667 291.276 55.2133 293.55C58.28 294.643 63.3533 297.8 70.4333 303.02C75.98 307.113 82.4433 311.78 89.8233 317.02C128.563 344.526 178.703 380.303 240.243 424.35C242.73 426.13 245.853 428.246 249.613 430.7C257.443 435.8 268.453 436.24 277.213 433.14C279.8 432.22 284.54 429.283 291.433 424.33C394.46 350.276 453.11 308.17 467.383 298.01Z"/>
|
|
12
15
|
</svg>
|