Skip to content

Commit 1c8de9f

Browse files
author
Dobromir Hristov
authored
Merge branch 'main' into dhristov/r97716047-support-links-topicsSectionStyle
2 parents 7ff9ecd + bd518f5 commit 1c8de9f

File tree

12 files changed

+361
-29
lines changed

12 files changed

+361
-29
lines changed

src/components/ContentNode.vue

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import Small from './ContentNode/Small.vue';
2525
import BlockVideo from './ContentNode/BlockVideo.vue';
2626
import Column from './ContentNode/Column.vue';
2727
import Row from './ContentNode/Row.vue';
28+
import TabNavigator from './ContentNode/TabNavigator.vue';
2829
2930
const BlockType = {
3031
aside: 'aside',
@@ -40,6 +41,7 @@ const BlockType = {
4041
small: 'small',
4142
video: 'video',
4243
row: 'row',
44+
tabNavigator: 'tabNavigator',
4345
};
4446
4547
const InlineType = {
@@ -93,6 +95,9 @@ const TableHeaderStyle = {
9395
row: 'row',
9496
};
9597
98+
// The point after which a TabNavigator turns to vertical mode.
99+
const TabNavigatorVerticalThreshold = 5;
100+
96101
// Recursively call the passed `createElement` function for each content node
97102
// and any of its children by mapping each node `type` to a given Vue component
98103
//
@@ -311,6 +316,22 @@ function renderNode(createElement, references) {
311316
)),
312317
);
313318
}
319+
case BlockType.tabNavigator: {
320+
// If the tabs count is more than the threshold, use vertical tabs instead.
321+
const vertical = node.tabs.length > TabNavigatorVerticalThreshold;
322+
const titles = node.tabs.map(tab => tab.title);
323+
const scopedSlots = node.tabs.reduce((slots, tab) => ({
324+
...slots,
325+
[tab.title]: () => renderChildren(tab.content),
326+
}), {});
327+
return createElement(TabNavigator, {
328+
props: {
329+
titles,
330+
vertical,
331+
},
332+
scopedSlots,
333+
});
334+
}
314335
case InlineType.codeVoice:
315336
return createElement(CodeVoice, {}, (
316337
node.code
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<!--
2+
This source file is part of the Swift.org open source project
3+
4+
Copyright (c) 2022 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
13+
class="TabNavigator"
14+
:class="[{ 'tabs--vertical': vertical }]"
15+
>
16+
<Tabnav v-model="currentTitle" v-bind="{ position, vertical }">
17+
<TabnavItem v-for="title in titles" :key="title" :value="title">
18+
{{ title }}
19+
</TabnavItem>
20+
</Tabnav>
21+
<div class="tabs-content">
22+
<template v-for="title in titles">
23+
<slot v-if="title === currentTitle" :name="title" />
24+
</template>
25+
</div>
26+
</div>
27+
</template>
28+
29+
<script>
30+
import Tabnav from 'docc-render/components/Tabnav.vue';
31+
import TabnavItem from 'docc-render/components/TabnavItem.vue';
32+
33+
/**
34+
* Tab navigation component, that renders `ContentNode`,
35+
* with ability to align the tabs horizontally and vertically to the start/center/end of the line.
36+
* It can also flip the navigation, so it renders content first, navigation second,
37+
* in both horizontal and vertical mode.
38+
*/
39+
export default {
40+
name: 'TabNavigator',
41+
components: {
42+
TabnavItem,
43+
Tabnav,
44+
},
45+
props: {
46+
vertical: {
47+
type: Boolean,
48+
default: false,
49+
},
50+
position: {
51+
type: String,
52+
default: 'start',
53+
validator: v => new Set(['start', 'center', 'end']).has(v),
54+
},
55+
titles: {
56+
type: Array,
57+
required: true,
58+
default: () => [],
59+
},
60+
},
61+
data() {
62+
return {
63+
currentTitle: this.titles[0],
64+
};
65+
},
66+
};
67+
</script>
68+
69+
<style scoped lang='scss'>
70+
@import 'docc-render/styles/_core.scss';
71+
72+
.TabNavigator {
73+
margin-bottom: $stacked-margin-large;
74+
75+
.tabnav {
76+
overflow: auto;
77+
white-space: nowrap;
78+
}
79+
}
80+
81+
.tabs--vertical {
82+
display: flex;
83+
flex-flow: row-reverse;
84+
85+
@include breakpoint(small) {
86+
flex-flow: column-reverse;
87+
}
88+
89+
.tabnav {
90+
width: 20%;
91+
flex: 0 0 auto;
92+
white-space: normal;
93+
margin: 0;
94+
@include breakpoint(small) {
95+
width: 100%;
96+
}
97+
}
98+
99+
.tabs-content {
100+
flex: 1 1 auto;
101+
min-width: 0;
102+
padding-right: $stacked-margin-large;
103+
@include breakpoint(small) {
104+
padding-right: 0;
105+
padding-bottom: $stacked-margin-large;
106+
}
107+
}
108+
}
109+
</style>

src/components/NavMenuLink.vue

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,23 @@
1818
>
1919
<slot />
2020
</span>
21-
<router-link
21+
<Reference
2222
v-else
2323
class="nav-menu-link"
24-
:to="url"
24+
:url="url"
2525
tabindex="0"
2626
>
2727
<slot />
28-
</router-link>
28+
</Reference>
2929
</template>
3030

3131
<script>
3232
import { buildUrl } from 'docc-render/utils/url-helper';
33+
import Reference from 'docc-render/components/ContentNode/Reference.vue';
3334
3435
export default {
3536
name: 'NavMenuLink',
37+
components: { Reference },
3638
computed: {
3739
isCurrent: ({ $route, url }) => ((typeof url === 'string') ? (
3840
// Use the `buildUrl` to compare urls, as we dont want to compare unnecessary query params

src/components/Tabnav.vue

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
-->
1010

1111
<template>
12-
<nav class="tabnav">
12+
<nav
13+
class="tabnav"
14+
:class="{ [`tabnav--${position}`]: position, 'tabnav--vertical': vertical }"
15+
>
1316
<ul class="tabnav-items">
1417
<slot />
1518
</ul>
@@ -35,8 +38,17 @@ export default {
3538
};
3639
},
3740
props: {
38-
value: {
41+
position: {
3942
type: String,
43+
required: false,
44+
validator: v => new Set(['start', 'center', 'end']).has(v),
45+
},
46+
vertical: {
47+
type: Boolean,
48+
default: false,
49+
},
50+
value: {
51+
type: [String, Number],
4052
required: true,
4153
},
4254
},
@@ -56,10 +68,32 @@ $-tabnav-bottom-margin: rem(25px);
5668
5769
.tabnav {
5870
margin: $-tabnav-top-margin 0 $-tabnav-bottom-margin 0;
71+
display: flex;
72+
73+
&--center {
74+
justify-content: center;
75+
}
76+
77+
&--end {
78+
justify-content: flex-end;
79+
}
80+
81+
&--vertical {
82+
flex-flow: column wrap;
83+
84+
.tabnav-items {
85+
flex-flow: column;
86+
overflow: hidden;
87+
}
88+
89+
/deep/ .tabnav-item {
90+
padding-left: 0;
91+
}
92+
}
5993
}
6094
6195
.tabnav-items {
62-
display: inline-block;
96+
display: flex;
6397
margin: 0;
6498
text-align: center;
6599
}

src/components/TabnavItem.vue

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
href="#"
1515
class="tabnav-link"
1616
:class="{ 'active': isActive }"
17+
:aria-current="isActive ? 'true' : 'false'"
1718
@click.prevent="tabnavData.selectTab(value)"
1819
>
1920
<slot />
@@ -22,6 +23,7 @@
2223
</template>
2324

2425
<script>
26+
2527
export default {
2628
name: 'TabnavItem',
2729
inject: {
@@ -31,8 +33,8 @@ export default {
3133
},
3234
props: {
3335
value: {
34-
type: String,
35-
default: '',
36+
type: [String, Number],
37+
default: null,
3638
},
3739
},
3840
computed: {
@@ -46,15 +48,15 @@ export default {
4648
<style lang='scss' scoped>
4749
@import 'docc-render/styles/_core.scss';
4850
49-
$tabnav-padding: 11px !default;
50-
$tabnav-bottom-border-margin: 4px !default;
51+
$tabnav-padding: 6px !default;
52+
$tabnav-margin: 4px !default;
5153
$tabnav-item-gutter: rem(30px);
5254
5355
.tabnav-item {
5456
border-bottom: 1px solid;
5557
border-color: var(--colors-tabnav-item-border-color, var(--color-tabnav-item-border-color));
5658
57-
display: inline-block;
59+
display: flex;
5860
list-style: none;
5961
padding-left: $tabnav-item-gutter;
6062
margin: 0;
@@ -71,19 +73,17 @@ $tabnav-item-gutter: rem(30px);
7173
}
7274
7375
.tabnav-link {
74-
$-tabnav-padding-focus-offset: 2px;
75-
$-tabnav-padding-top: $tabnav-padding - $-tabnav-padding-focus-offset;
76-
7776
color: var(--colors-secondary-label, var(--color-secondary-label));
7877
@include font-styles(tabnav-link);
79-
padding: $-tabnav-padding-top 0 $tabnav-padding;
80-
margin-top: $-tabnav-padding-focus-offset;
81-
margin-bottom: $tabnav-bottom-border-margin;
78+
padding: $tabnav-padding 0;
79+
margin-top: $tabnav-margin;
80+
margin-bottom: $tabnav-margin;
8281
text-align: left;
8382
text-decoration: none;
8483
display: block;
8584
position: relative;
8685
z-index: 0;
86+
width: 100%;
8787
8888
&:hover {
8989
text-decoration: none;
@@ -96,7 +96,7 @@ $tabnav-item-gutter: rem(30px);
9696
&:after {
9797
content: '';
9898
position: absolute;
99-
bottom: -1 * ($tabnav-bottom-border-margin + 1);
99+
bottom: -1 * ($tabnav-margin + 1);
100100
left: 0;
101101
width: 100%;
102102
border: 1px solid transparent;

src/styles/core/typography/_font-attributes.scss

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@ $font-attributes: (
7171
// menu
7272
nav_12_12: (size: 12, line-height: 12),
7373
nav_14_14: (size: 14, line-height: 14),
74-
// tabnav
75-
tabnav_link_17_17: (size: 17, line-height: 17),
7674
// base dropdown
7775
form-default-text_17_21: (size: 17, line-height: 21),
7876
form-textbox-label-small_12_21: (size: 12, line-height: 21)

src/styles/core/typography/_font-styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ $font-styles: (
172172
large: 17_25
173173
),
174174
tabnav-link: (
175-
large: tabnav_link_17_17
175+
large: nav_14_14
176176
),
177177
badge: (
178178
large: 12_16

0 commit comments

Comments
 (0)