@master/internal-site 0.2.2 → 0.2.3
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/package.json +1 -1
- package/src/docs-shell/components/PageContentList.svelte +2 -3
- package/src/docs-shell/components/page-content.svelte.ts +5 -0
- package/src/docs-shell/layouts/Doc.astro +3 -7
- package/src/layouts/Document.astro +2 -0
- package/src/layouts/NavigationTransition.astro +58 -0
- package/src/styles/docs-shell/docs.css +1 -0
- package/src/styles/system/header-core.css +2 -0
- package/src/styles/system/navigation-transitions.css +61 -0
package/package.json
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
<script lang="ts">
|
|
9
9
|
import '@master/internal-site/styles/system/toc.css'
|
|
10
10
|
|
|
11
|
-
import Link from './Link.svelte'
|
|
12
11
|
import PageContentTitle from './PageContentTitle.svelte'
|
|
13
12
|
import { usePageContent, type PageContentItem } from './page-content.svelte'
|
|
14
13
|
import groupPageContent from '../utils/group-page-content'
|
|
@@ -57,9 +56,9 @@
|
|
|
57
56
|
|
|
58
57
|
<!-- The bootstrap script marks the live entry before hydration, so its attribute may differ from the first render. -->
|
|
59
58
|
{#snippet nav({ id, title, level }: PageContentItem)}
|
|
60
|
-
<
|
|
59
|
+
<a href={'#' + id} aria-current={pageContent.currentId === id ? 'location' : undefined} data-page-content-nav-id={id} data-level={level} lang={langOf(title)}>
|
|
61
60
|
<PageContentTitle {title} translate={translate ? t : undefined} />
|
|
62
|
-
</
|
|
61
|
+
</a>
|
|
63
62
|
{/snippet}
|
|
64
63
|
|
|
65
64
|
<ol bind:this={element} {lang} data-fold={fold ? '' : undefined}>
|
|
@@ -96,6 +96,10 @@ export function providePageContent(getItems: () => PageContentItem[]): PageConte
|
|
|
96
96
|
|
|
97
97
|
updateCurrentId()
|
|
98
98
|
initialUpdateTimeout = window.setTimeout(requestUpdate, 100)
|
|
99
|
+
// The rail can hydrate before a streamed article has delivered its headings.
|
|
100
|
+
if (document.readyState === 'loading') {
|
|
101
|
+
document.addEventListener('DOMContentLoaded', requestUpdate, { once: true })
|
|
102
|
+
}
|
|
99
103
|
document.addEventListener('scroll', requestUpdate, {
|
|
100
104
|
capture: true,
|
|
101
105
|
passive: true
|
|
@@ -103,6 +107,7 @@ export function providePageContent(getItems: () => PageContentItem[]): PageConte
|
|
|
103
107
|
window.addEventListener('resize', requestUpdate, { passive: true })
|
|
104
108
|
|
|
105
109
|
return () => {
|
|
110
|
+
document.removeEventListener('DOMContentLoaded', requestUpdate)
|
|
106
111
|
document.removeEventListener('scroll', requestUpdate, true)
|
|
107
112
|
window.removeEventListener('resize', requestUpdate)
|
|
108
113
|
window.clearTimeout(initialUpdateTimeout)
|
|
@@ -26,16 +26,12 @@ const pageContent = Array.isArray(toc) && toc.length > 0 ? toc : null
|
|
|
26
26
|
---
|
|
27
27
|
|
|
28
28
|
<DocMain class={props.class} footerProps={footerProps}>
|
|
29
|
+
{/* Stream the rail before the article so both columns are present when its title first paints. */}
|
|
30
|
+
{pageContent && <PageContent client:load items={pageContent.map(({ id, title, level }) => ({ id, title, level }))} metadata={{ sourcePath: metadata.sourcePath, title: metadata.title }} />}
|
|
29
31
|
<DocArticle aside={Boolean(pageContent)}>
|
|
30
32
|
<ArticleHeader {...props} locale={locale} metadata={metadata} dictionaries={dictionaries} toc={pageContent} />
|
|
31
33
|
<slot />
|
|
32
34
|
<PageNavs locale={locale} metadata={metadata} pageCategories={pageCategories} dictionaries={dictionaries} />
|
|
33
35
|
</DocArticle>
|
|
34
|
-
{pageContent &&
|
|
35
|
-
<Fragment>
|
|
36
|
-
{/* The rail's island serializes only the fields it renders. */}
|
|
37
|
-
<PageContent client:load items={pageContent.map(({ id, title, level }) => ({ id, title, level }))} metadata={{ sourcePath: metadata.sourcePath, title: metadata.title }} />
|
|
38
|
-
<PageContentBootstrap items={pageContent} />
|
|
39
|
-
</Fragment>
|
|
40
|
-
)}
|
|
36
|
+
{pageContent && <PageContentBootstrap items={pageContent} />}
|
|
41
37
|
</DocMain>
|
|
@@ -9,6 +9,7 @@ import { localizePathname } from '../astro/route-paths'
|
|
|
9
9
|
import { siteContextElementId } from '../docs-shell/site'
|
|
10
10
|
import type { Metadata } from '../docs-shell/types/Metadata'
|
|
11
11
|
import type { PageAssets } from '../astro/propagated-assets'
|
|
12
|
+
import NavigationTransition from './NavigationTransition.astro'
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
14
15
|
metadata: Partial<Metadata>
|
|
@@ -45,6 +46,7 @@ if (['dark', 'light'].includes(value)) document.documentElement.style.colorSchem
|
|
|
45
46
|
<head>
|
|
46
47
|
<meta charset="utf-8" />
|
|
47
48
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
49
|
+
{!bare && <NavigationTransition />}
|
|
48
50
|
<link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="any" />
|
|
49
51
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
50
52
|
<script is:inline blocking="render" set:html={themeScript}></script>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<style is:inline>
|
|
2
|
+
@media (prefers-reduced-motion: no-preference) and (scripting: enabled) {
|
|
3
|
+
@view-transition { navigation: auto; types: master-navigation; }
|
|
4
|
+
}
|
|
5
|
+
</style>
|
|
6
|
+
<script is:inline>
|
|
7
|
+
(() => {
|
|
8
|
+
if (!('onpagereveal' in window)) return
|
|
9
|
+
|
|
10
|
+
const hasTitle = () => Boolean(document.querySelector('main h1')?.textContent?.trim())
|
|
11
|
+
const hasFragmentTarget = () => {
|
|
12
|
+
if (!location.hash) return true
|
|
13
|
+
try {
|
|
14
|
+
return Boolean(document.getElementById(decodeURIComponent(location.hash.slice(1))))
|
|
15
|
+
} catch {
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
addEventListener('pageswap', ({ viewTransition, activation }) => {
|
|
21
|
+
if (!viewTransition) return
|
|
22
|
+
void viewTransition.ready.catch(() => {})
|
|
23
|
+
// The standalone examples own their transitions and do not opt in to the site capture.
|
|
24
|
+
const destination = activation?.entry?.url
|
|
25
|
+
if (destination && new URL(destination).pathname.startsWith('/examples/')) viewTransition.skipTransition()
|
|
26
|
+
})
|
|
27
|
+
addEventListener('pagereveal', ({ viewTransition }) => {
|
|
28
|
+
if (!viewTransition) return
|
|
29
|
+
void viewTransition.ready.catch(() => {})
|
|
30
|
+
if (!hasTitle() || !hasFragmentTarget()) viewTransition.skipTransition()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
const activation = window.navigation?.activation
|
|
34
|
+
const source = activation ? activation.from?.url : document.referrer
|
|
35
|
+
const navigation = performance.getEntriesByType('navigation')[0]
|
|
36
|
+
if (!source || new URL(source).origin !== location.origin || navigation?.type === 'reload'
|
|
37
|
+
|| document.visibilityState !== 'visible' || !matchMedia('(prefers-reduced-motion: no-preference)').matches) return
|
|
38
|
+
|
|
39
|
+
// The browser may reveal a buffered response before parsing its title. Release this short
|
|
40
|
+
// wait as soon as the title arrives. If the stream is slower, pagereveal skips the capture.
|
|
41
|
+
const blocker = document.createElement('link')
|
|
42
|
+
if (!('blocking' in blocker)) return
|
|
43
|
+
blocker.rel = 'expect'
|
|
44
|
+
blocker.href = '#master-navigation-ready'
|
|
45
|
+
blocker.setAttribute('blocking', 'render')
|
|
46
|
+
const release = () => {
|
|
47
|
+
observer.disconnect()
|
|
48
|
+
clearTimeout(timeout)
|
|
49
|
+
blocker.remove()
|
|
50
|
+
}
|
|
51
|
+
const observer = new MutationObserver(() => {
|
|
52
|
+
if (hasTitle()) release()
|
|
53
|
+
})
|
|
54
|
+
const timeout = setTimeout(release, 120)
|
|
55
|
+
observer.observe(document.documentElement, { childList: true, subtree: true, characterData: true })
|
|
56
|
+
document.head.append(blocker)
|
|
57
|
+
})()
|
|
58
|
+
</script>
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
|
|
64
64
|
/* The contents rail stands still beside the article; a long list scrolls inside it. */
|
|
65
65
|
:is(.ui-docs-body > .ui-toc, .ui-docs-body > astro-island > .ui-toc) {
|
|
66
|
+
order: 1;
|
|
66
67
|
position: sticky; top: var(--docs-header); display: flex; flex: none; flex-direction: column; align-self: start; width: var(--docs-rail); max-height: calc(100dvh - var(--docs-header));
|
|
67
68
|
padding: 3.5rem var(--ui-space-lg) var(--ui-space-xl) 0;
|
|
68
69
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/* Cross-document captures only. The homepage and blog overlay headers travel with their page. */
|
|
2
|
+
:root:active-view-transition-type(master-navigation) .ui-header:not([data-variant="stickable"]) {
|
|
3
|
+
view-transition-name: master-header;
|
|
4
|
+
view-transition-class: master-navigation-chrome;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
:root:active-view-transition-type(master-navigation) #sidebar.ui-sidebar {
|
|
8
|
+
view-transition-name: master-sidebar;
|
|
9
|
+
view-transition-class: master-navigation-chrome;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:root:active-view-transition-type(master-navigation) .ui-docs-body .ui-toc[data-rail] {
|
|
13
|
+
view-transition-name: master-contents;
|
|
14
|
+
view-transition-class: master-navigation-chrome;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
:root:active-view-transition-type(master-navigation)::view-transition {
|
|
18
|
+
pointer-events: none;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
:root:active-view-transition-type(master-navigation)::view-transition-group(root) {
|
|
22
|
+
animation-duration: var(--ui-duration-quick);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Replace matching navigation in place while the page underneath crossfades. */
|
|
26
|
+
:root:active-view-transition-type(master-navigation)::view-transition-group(.master-navigation-chrome) {
|
|
27
|
+
animation: none;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
:root:active-view-transition-type(master-navigation)::view-transition-old(.master-navigation-chrome) {
|
|
31
|
+
animation: none;
|
|
32
|
+
opacity: 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
:root:active-view-transition-type(master-navigation)::view-transition-new(.master-navigation-chrome) {
|
|
36
|
+
animation: none;
|
|
37
|
+
mix-blend-mode: normal;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* If a layout gains or loses navigation, fade that capture with the page. */
|
|
41
|
+
:root:active-view-transition-type(master-navigation)::view-transition-old(.master-navigation-chrome):only-child {
|
|
42
|
+
animation-name: master-navigation-chrome-out;
|
|
43
|
+
animation-duration: var(--ui-duration-quick);
|
|
44
|
+
animation-fill-mode: both;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
:root:active-view-transition-type(master-navigation)::view-transition-new(.master-navigation-chrome):only-child {
|
|
48
|
+
animation-name: master-navigation-chrome-in;
|
|
49
|
+
animation-duration: var(--ui-duration-quick);
|
|
50
|
+
animation-fill-mode: both;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@keyframes master-navigation-chrome-in {
|
|
54
|
+
from { opacity: 0; }
|
|
55
|
+
to { opacity: 1; }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes master-navigation-chrome-out {
|
|
59
|
+
from { opacity: 1; }
|
|
60
|
+
to { opacity: 0; }
|
|
61
|
+
}
|