Skip to content

Commit 14923e5

Browse files
authored
Refactor DocumentationTopic view to be able to reuse the navigator for other views (#792)
* Create BaseNavigatorView * Change background color for documentation nav * Move onPageLoadScrollToFragment to BaseNavigatorView * Wrap baseViewProps * Delete StaticContentWidth + Rename DocumentationLayout * Fix technology default value * Fix enableNavigator * Move CodeTheme to DocumentationTopic * Enabling isTargetIDE in DocumentationTopic * Add resetting the store to DocumentationTopic * Delete DocumentationTopicStore from DocumentationLayout * Rename aside in DocumentationLayout * overwrite the NavBase background color only for documentation pages * Rename documentation-layout-aside * Delete unnecessary template at DocumentationTopic * It renders slot for content * Refactor color background for DocumentationNav * Rename nav-title slot * Fix alignment in nav-title * Hide actions in NavBase if there is no menu items * Add medium-content-viewport to Pager * Add SAFE_SPACE_GUTTER to mediumContentViewport * Fix when Pager should collapse its controllers * Replace `collapsed-controllers` with `with-compact-controls` * Update license headers
1 parent e07c2d1 commit 14923e5

24 files changed

+1071
-867
lines changed

src/components/AdjustableSidebarWidth.vue

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<!--
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2022 Apple Inc. and the Swift project authors
4+
Copyright (c) 2022-2024 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -13,10 +13,11 @@
1313
class="adjustable-sidebar-width"
1414
:class="{
1515
dragging: isDragging,
16-
'sidebar-hidden': hiddenOnLarge
16+
'sidebar-hidden': !enableNavigator || hiddenOnLarge
1717
}"
1818
>
1919
<div
20+
v-if="enableNavigator"
2021
ref="sidebar"
2122
class="sidebar"
2223
>
@@ -46,7 +47,11 @@
4647
<div class="content" ref="content">
4748
<slot />
4849
</div>
49-
<BreakpointEmitter :scope="BreakpointScopes.nav" @change="breakpoint = $event" />
50+
<BreakpointEmitter
51+
v-if="enableNavigator"
52+
:scope="BreakpointScopes.nav"
53+
@change="breakpoint = $event"
54+
/>
5055
</div>
5156
</template>
5257

@@ -111,6 +116,10 @@ export default {
111116
type: Boolean,
112117
default: false,
113118
},
119+
enableNavigator: {
120+
type: Boolean,
121+
default: true,
122+
},
114123
hiddenOnLarge: {
115124
type: Boolean,
116125
default: false,
+285
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
<!--
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2024 Apple Inc. and the Swift project authors
5+
Licensed under Apache License v2.0 with Runtime Library Exception
6+
7+
See https://swift.org/LICENSE.txt for license information
8+
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
9+
-->
10+
11+
<template>
12+
<div class="documentation-layout">
13+
<Nav
14+
v-if="!isTargetIDE"
15+
:diffAvailability="diffAvailability"
16+
:interfaceLanguage="interfaceLanguage"
17+
:objcPath="objcPath"
18+
:swiftPath="swiftPath"
19+
:displaySidenav="enableNavigator"
20+
@toggle-sidenav="handleToggleSidenav"
21+
>
22+
<template #title>
23+
<slot name="nav-title" />
24+
</template>
25+
</Nav>
26+
<AdjustableSidebarWidth
27+
v-bind="sidebarProps"
28+
v-on="sidebarListeners"
29+
class="full-width-container topic-wrapper"
30+
>
31+
<PortalTarget name="modal-destination" multiple />
32+
<template #aside="{ scrollLockID, breakpoint }">
33+
<NavigatorDataProvider
34+
:interface-language="interfaceLanguage"
35+
:technologyUrl="technology ? technology.url : ''"
36+
:api-changes-version="selectedAPIChangesVersion"
37+
ref="NavigatorDataProvider"
38+
>
39+
<template #default="slotProps">
40+
<div class="documentation-layout-aside">
41+
<QuickNavigationModal
42+
v-if="enableQuickNavigation"
43+
:children="slotProps.flatChildren"
44+
:showQuickNavigationModal.sync="showQuickNavigationModal"
45+
:technology="technology ? technology.title : ''"
46+
/>
47+
<transition name="delay-hiding">
48+
<Navigator
49+
v-show="sidenavVisibleOnMobile || breakpoint === BreakpointName.large"
50+
:flatChildren="slotProps.flatChildren"
51+
:parent-topic-identifiers="parentTopicIdentifiers"
52+
:technology="slotProps.technology || technology"
53+
:is-fetching="slotProps.isFetching"
54+
:error-fetching="slotProps.errorFetching"
55+
:api-changes="slotProps.apiChanges"
56+
:references="references"
57+
:navigator-references="slotProps.references"
58+
:scrollLockID="scrollLockID"
59+
:render-filter-on-top="breakpoint !== BreakpointName.large"
60+
@close="handleToggleSidenav(breakpoint)"
61+
>
62+
<template v-if="enableQuickNavigation" #filter>
63+
<QuickNavigationButton @click.native="openQuickNavigationModal" />
64+
</template>
65+
<template #navigator-head>
66+
<slot name="nav-title" />
67+
</template>
68+
</Navigator>
69+
</transition>
70+
</div>
71+
</template>
72+
</NavigatorDataProvider>
73+
</template>
74+
<slot name="content" />
75+
</AdjustableSidebarWidth>
76+
</div>
77+
</template>
78+
79+
<script>
80+
import { PortalTarget } from 'portal-vue';
81+
import QuickNavigationButton from 'docc-render/components/Navigator/QuickNavigationButton.vue';
82+
import QuickNavigationModal from 'docc-render/components/Navigator/QuickNavigationModal.vue';
83+
import AdjustableSidebarWidth from 'docc-render/components/AdjustableSidebarWidth.vue';
84+
import Navigator from 'docc-render/components/Navigator.vue';
85+
import onPageLoadScrollToFragment from 'docc-render/mixins/onPageLoadScrollToFragment';
86+
import { BreakpointName } from 'docc-render/utils/breakpoints';
87+
import { storage } from 'docc-render/utils/storage';
88+
import { getSetting } from 'docc-render/utils/theme-settings';
89+
90+
import NavigatorDataProvider from 'theme/components/Navigator/NavigatorDataProvider.vue';
91+
import DocumentationNav from 'theme/components/DocumentationTopic/DocumentationNav.vue';
92+
93+
const NAVIGATOR_HIDDEN_ON_LARGE_KEY = 'navigator-hidden-large';
94+
95+
export default {
96+
name: 'DocumentationLayout',
97+
constants: { NAVIGATOR_HIDDEN_ON_LARGE_KEY },
98+
components: {
99+
Navigator,
100+
AdjustableSidebarWidth,
101+
NavigatorDataProvider,
102+
Nav: DocumentationNav,
103+
QuickNavigationButton,
104+
QuickNavigationModal,
105+
PortalTarget,
106+
},
107+
mixins: [onPageLoadScrollToFragment],
108+
props: {
109+
enableNavigator: Boolean,
110+
diffAvailability: {
111+
type: Object,
112+
required: false,
113+
},
114+
interfaceLanguage: {
115+
type: String,
116+
required: false,
117+
},
118+
references: {
119+
type: Object,
120+
default: () => {},
121+
},
122+
objcPath: {
123+
type: String,
124+
required: false,
125+
},
126+
swiftPath: {
127+
type: String,
128+
required: false,
129+
},
130+
selectedAPIChangesVersion: {
131+
type: String,
132+
required: false,
133+
},
134+
technology: {
135+
type: Object,
136+
require: false,
137+
},
138+
parentTopicIdentifiers: {
139+
type: Array,
140+
default: () => [],
141+
},
142+
},
143+
data() {
144+
return {
145+
sidenavVisibleOnMobile: false,
146+
sidenavHiddenOnLarge: storage.get(NAVIGATOR_HIDDEN_ON_LARGE_KEY, false),
147+
showQuickNavigationModal: false,
148+
BreakpointName,
149+
};
150+
},
151+
computed: {
152+
enableQuickNavigation: ({ isTargetIDE }) => (
153+
!isTargetIDE && getSetting(['features', 'docs', 'quickNavigation', 'enable'], true)
154+
),
155+
sidebarProps: ({ sidenavVisibleOnMobile, enableNavigator, sidenavHiddenOnLarge }) => (
156+
enableNavigator
157+
? {
158+
shownOnMobile: sidenavVisibleOnMobile,
159+
hiddenOnLarge: sidenavHiddenOnLarge,
160+
}
161+
: {
162+
enableNavigator,
163+
}
164+
),
165+
sidebarListeners() {
166+
return this.enableNavigator ? ({
167+
'update:shownOnMobile': this.toggleMobileSidenav,
168+
'update:hiddenOnLarge': this.toggleLargeSidenav,
169+
}) : {};
170+
},
171+
},
172+
methods: {
173+
handleToggleSidenav(breakpoint) {
174+
if (breakpoint === BreakpointName.large) {
175+
this.toggleLargeSidenav();
176+
} else {
177+
this.toggleMobileSidenav();
178+
}
179+
},
180+
openQuickNavigationModal() {
181+
if (this.sidenavVisibleOnMobile) return;
182+
this.showQuickNavigationModal = true;
183+
},
184+
toggleLargeSidenav(value = !this.sidenavHiddenOnLarge) {
185+
this.sidenavHiddenOnLarge = value;
186+
storage.set(NAVIGATOR_HIDDEN_ON_LARGE_KEY, value);
187+
},
188+
toggleMobileSidenav(value = !this.sidenavVisibleOnMobile) {
189+
this.sidenavVisibleOnMobile = value;
190+
},
191+
onQuickNavigationKeydown(event) {
192+
// Open the modal only on `/` or `cmd+shift+o` key event
193+
if (event.key !== '/' && !(event.key === 'o' && event.shiftKey && event.metaKey)) return;
194+
// Prevent modal from opening when the navigator is disabled
195+
if (!this.enableNavigator) return;
196+
// Prevent modal from opening if the event source element is an input
197+
if (event.target.tagName.toLowerCase() === 'input') return;
198+
this.openQuickNavigationModal();
199+
event.preventDefault();
200+
},
201+
},
202+
mounted() {
203+
if (this.enableQuickNavigation) window.addEventListener('keydown', this.onQuickNavigationKeydown);
204+
},
205+
beforeDestroy() {
206+
if (this.enableQuickNavigation) window.removeEventListener('keydown', this.onQuickNavigationKeydown);
207+
},
208+
inject: {
209+
isTargetIDE: {
210+
default() {
211+
return false;
212+
},
213+
},
214+
},
215+
};
216+
</script>
217+
<style lang="scss" scoped>
218+
@import 'docc-render/styles/_core.scss';
219+
220+
:deep() {
221+
.generic-modal {
222+
overflow-y: overlay;
223+
}
224+
.modal-fullscreen > .container {
225+
background-color: transparent;
226+
height: fit-content;
227+
flex: auto;
228+
margin: rem(160px) 0;
229+
max-width: rem(800px);
230+
overflow: visible;
231+
}
232+
233+
.navigator-filter .quick-navigation-open {
234+
margin-left: var(--nav-filter-horizontal-padding);
235+
width: calc(var(--nav-filter-horizontal-padding) * 2);
236+
}
237+
}
238+
239+
.documentation-layout {
240+
--delay: 1s;
241+
display: flex;
242+
flex-flow: column;
243+
background: var(--colors-text-background, var(--color-text-background));
244+
245+
.delay-hiding-leave-active {
246+
// don't hide navigator until delay time has passed
247+
transition: display var(--delay);
248+
}
249+
}
250+
251+
.documentation-layout-aside {
252+
height: 100%;
253+
box-sizing: border-box;
254+
border-right: $generic-border-style;
255+
256+
@include breakpoint(medium, nav) {
257+
background: var(--color-fill);
258+
border-right: none;
259+
260+
.sidebar-transitioning & {
261+
border-right: $generic-border-style;
262+
}
263+
}
264+
}
265+
266+
.topic-wrapper {
267+
flex: 1 1 auto;
268+
width: 100%;
269+
270+
:root.no-js &:deep(.sidebar) {
271+
display: none;
272+
}
273+
}
274+
275+
.full-width-container {
276+
@include inTargetWeb {
277+
@include breakpoint-full-width-container();
278+
@include breakpoints-from(xlarge) {
279+
border-left: $generic-border-style;
280+
border-right: $generic-border-style;
281+
box-sizing: border-box;
282+
}
283+
}
284+
}
285+
</style>

src/components/DocumentationTopic/DocumentationNav.vue

+8-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
hasFullWidthBorder
1919
class="documentation-nav"
2020
:aria-label="$t('api-reference')"
21+
:showActions="hasMenuItems"
2122
>
2223
<template #pre-title="{ closeNav, isOpen, currentBreakpoint, className }" v-if="displaySidenav">
2324
<div :class="className">
@@ -41,10 +42,11 @@
4142
</template>
4243
<template #tray="{ closeNav }">
4344
<NavMenuItems
45+
v-if="hasMenuItems"
4446
class="nav-menu-settings"
4547
>
4648
<LanguageToggle
47-
v-if="interfaceLanguage && (swiftPath || objcPath)"
49+
v-if="hasLanguageToggle"
4850
:interfaceLanguage="interfaceLanguage"
4951
:objcPath="objcPath"
5052
:swiftPath="swiftPath"
@@ -106,6 +108,9 @@ export default {
106108
computed: {
107109
baseNavOpenSidenavButtonId: () => baseNavOpenSidenavButtonId,
108110
BreakpointName: () => BreakpointName,
111+
hasLanguageToggle: ({ interfaceLanguage, swiftPath, objcPath }) => (
112+
!!(interfaceLanguage && (swiftPath || objcPath))),
113+
hasMenuItems: ({ hasLanguageToggle, $slots }) => !!(hasLanguageToggle || $slots['menu-items']),
109114
},
110115
methods: {
111116
async handleSidenavToggle(closeNav, currentBreakpoint) {
@@ -177,6 +182,8 @@ $sidenav-icon-padding-size: 5px;
177182
}
178183

179184
.documentation-nav {
185+
--color-nav-background: var(--color-fill);
186+
180187
:deep() {
181188
.nav-title {
182189
@include font-styles(nav-title-large);

0 commit comments

Comments
 (0)