Skip to content

Commit fb7857d

Browse files
committed
add page nav breadcrumbs and fix styling
1 parent 3a5eb9c commit fb7857d

File tree

9 files changed

+160
-101
lines changed

9 files changed

+160
-101
lines changed

site/lib/_sass/base/_base.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ body {
88
color: var(--site-base-fgColor);
99

1010
// The top TOC is not shown on narrow screens.
11-
&:not(:has(#toc-top.show-always)) {
11+
&:not(:has(#site-subheader.show-always)) {
1212
@media (min-width: 1200px) {
1313
--site-subheader-height: 0rem;
1414
}

site/lib/_sass/components/_header.scss

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
border-bottom: 0.1rem solid var(--site-outline-variant);
88

99
@media (min-width: 1200px) {
10-
box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
11-
border-bottom: none;
10+
&:not(:has(~* #site-subheader.show-always)) {
11+
box-shadow: 0 2px 4px rgba(0, 0, 0, .05);
12+
border-bottom: none;
13+
}
1214
}
1315

1416
.navbar {
@@ -191,4 +193,4 @@ body.open_menu #menu-toggle span.material-symbols {
191193
display: none;
192194
}
193195
}
194-
}
196+
}

site/lib/_sass/components/_pagenav.scss

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pagenav {
2+
max-width: 100%;
23

34
>button.dropdown-button {
45
display: flex;
@@ -23,27 +24,36 @@
2324
}
2425
}
2526

26-
.toc-intro {
27+
.toc-breadcrumb {
28+
flex-shrink: 2;
2729
white-space: nowrap;
30+
overflow: hidden;
2831

29-
.material-symbols {
32+
&:first-child .material-symbols {
3033
margin-right: 0.25rem;
3134
}
32-
}
3335

34-
.toc-current {
35-
display: none;
36-
37-
@media (min-width: 320px) {
38-
display: flex;
36+
span:last-child {
37+
overflow: hidden;
38+
text-overflow: ellipsis;
3939
}
40+
}
4041

41-
flex-wrap: nowrap;
42+
.toc-current {
43+
flex-shrink: 1;
4244
white-space: nowrap;
4345
overflow: hidden;
44-
text-overflow: ellipsis;
4546

4647
color: var(--site-base-fgColor-alt);
48+
49+
@media (max-width: 320px) {
50+
display: none !important;
51+
}
52+
53+
span:last-child {
54+
overflow: hidden;
55+
text-overflow: ellipsis;
56+
}
4757
}
4858

4959
#pagenav-content {
@@ -135,7 +145,7 @@
135145
color: var(--site-primary-color);
136146
}
137147

138-
~ nav {
148+
~nav {
139149
padding: 0;
140150
}
141151
}

site/lib/jaspr_options.dart

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,29 @@ import 'package:docs_flutter_dev_site/src/components/common/client/on_this_page_
1919
as prefix5;
2020
import 'package:docs_flutter_dev_site/src/components/common/client/os_selector.dart'
2121
as prefix6;
22-
import 'package:docs_flutter_dev_site/src/components/dartpad/dartpad_injector.dart'
22+
import 'package:docs_flutter_dev_site/src/components/common/client/simple_tooltip.dart'
2323
as prefix7;
24-
import 'package:docs_flutter_dev_site/src/components/layout/menu_toggle.dart'
24+
import 'package:docs_flutter_dev_site/src/components/dartpad/dartpad_injector.dart'
2525
as prefix8;
26-
import 'package:docs_flutter_dev_site/src/components/layout/site_switcher.dart'
26+
import 'package:docs_flutter_dev_site/src/components/layout/client/pagenav.dart'
2727
as prefix9;
28-
import 'package:docs_flutter_dev_site/src/components/layout/theme_switcher.dart'
28+
import 'package:docs_flutter_dev_site/src/components/layout/menu_toggle.dart'
2929
as prefix10;
30-
import 'package:docs_flutter_dev_site/src/components/pages/archive_table.dart'
30+
import 'package:docs_flutter_dev_site/src/components/layout/site_switcher.dart'
3131
as prefix11;
32-
import 'package:docs_flutter_dev_site/src/components/pages/glossary_search_section.dart'
32+
import 'package:docs_flutter_dev_site/src/components/layout/theme_switcher.dart'
3333
as prefix12;
34-
import 'package:docs_flutter_dev_site/src/components/pages/learning_resource_filters.dart'
34+
import 'package:docs_flutter_dev_site/src/components/pages/archive_table.dart'
3535
as prefix13;
36-
import 'package:docs_flutter_dev_site/src/components/pages/learning_resource_filters_sidebar.dart'
36+
import 'package:docs_flutter_dev_site/src/components/pages/glossary_search_section.dart'
3737
as prefix14;
38-
import 'package:docs_flutter_dev_site/src/components/tutorial/client/quiz.dart'
38+
import 'package:docs_flutter_dev_site/src/components/pages/learning_resource_filters.dart'
3939
as prefix15;
40-
import 'package:jaspr_content/components/file_tree.dart' as prefix16;
40+
import 'package:docs_flutter_dev_site/src/components/pages/learning_resource_filters_sidebar.dart'
41+
as prefix16;
42+
import 'package:docs_flutter_dev_site/src/components/tutorial/client/quiz.dart'
43+
as prefix17;
44+
import 'package:jaspr_content/components/file_tree.dart' as prefix18;
4145

4246
/// Default [JasprOptions] for use with your jaspr project.
4347
///
@@ -88,49 +92,59 @@ JasprOptions get defaultJasprOptions => JasprOptions(
8892
'src/components/common/client/os_selector',
8993
),
9094

91-
prefix7.DartPadInjector: ClientTarget<prefix7.DartPadInjector>(
95+
prefix7.SimpleTooltip: ClientTarget<prefix7.SimpleTooltip>(
96+
'src/components/common/client/simple_tooltip',
97+
params: _prefix7SimpleTooltip,
98+
),
99+
100+
prefix8.DartPadInjector: ClientTarget<prefix8.DartPadInjector>(
92101
'src/components/dartpad/dartpad_injector',
93-
params: _prefix7DartPadInjector,
102+
params: _prefix8DartPadInjector,
103+
),
104+
105+
prefix9.PageNav: ClientTarget<prefix9.PageNav>(
106+
'src/components/layout/client/pagenav',
107+
params: _prefix9PageNav,
94108
),
95109

96-
prefix8.MenuToggle: ClientTarget<prefix8.MenuToggle>(
110+
prefix10.MenuToggle: ClientTarget<prefix10.MenuToggle>(
97111
'src/components/layout/menu_toggle',
98112
),
99113

100-
prefix9.SiteSwitcher: ClientTarget<prefix9.SiteSwitcher>(
114+
prefix11.SiteSwitcher: ClientTarget<prefix11.SiteSwitcher>(
101115
'src/components/layout/site_switcher',
102116
),
103117

104-
prefix10.ThemeSwitcher: ClientTarget<prefix10.ThemeSwitcher>(
118+
prefix12.ThemeSwitcher: ClientTarget<prefix12.ThemeSwitcher>(
105119
'src/components/layout/theme_switcher',
106120
),
107121

108-
prefix11.ArchiveTable: ClientTarget<prefix11.ArchiveTable>(
122+
prefix13.ArchiveTable: ClientTarget<prefix13.ArchiveTable>(
109123
'src/components/pages/archive_table',
110-
params: _prefix11ArchiveTable,
124+
params: _prefix13ArchiveTable,
111125
),
112126

113-
prefix12.GlossarySearchSection:
114-
ClientTarget<prefix12.GlossarySearchSection>(
127+
prefix14.GlossarySearchSection:
128+
ClientTarget<prefix14.GlossarySearchSection>(
115129
'src/components/pages/glossary_search_section',
116130
),
117131

118-
prefix13.LearningResourceFilters:
119-
ClientTarget<prefix13.LearningResourceFilters>(
132+
prefix15.LearningResourceFilters:
133+
ClientTarget<prefix15.LearningResourceFilters>(
120134
'src/components/pages/learning_resource_filters',
121135
),
122136

123-
prefix14.LearningResourceFiltersSidebar:
124-
ClientTarget<prefix14.LearningResourceFiltersSidebar>(
137+
prefix16.LearningResourceFiltersSidebar:
138+
ClientTarget<prefix16.LearningResourceFiltersSidebar>(
125139
'src/components/pages/learning_resource_filters_sidebar',
126140
),
127141

128-
prefix15.InteractiveQuiz: ClientTarget<prefix15.InteractiveQuiz>(
142+
prefix17.InteractiveQuiz: ClientTarget<prefix17.InteractiveQuiz>(
129143
'src/components/tutorial/client/quiz',
130-
params: _prefix15InteractiveQuiz,
144+
params: _prefix17InteractiveQuiz,
131145
),
132146
},
133-
styles: () => [...prefix16.FileTree.styles],
147+
styles: () => [...prefix18.FileTree.styles],
134148
);
135149

136150
Map<String, dynamic> _prefix2CopyButton(prefix2.CopyButton c) => {
@@ -145,17 +159,26 @@ Map<String, dynamic> _prefix3DownloadLatestButton(
145159
Map<String, dynamic> _prefix4FeedbackComponent(prefix4.FeedbackComponent c) => {
146160
'issueUrl': c.issueUrl,
147161
};
148-
Map<String, dynamic> _prefix7DartPadInjector(prefix7.DartPadInjector c) => {
162+
Map<String, dynamic> _prefix7SimpleTooltip(prefix7.SimpleTooltip c) => {
163+
'target': c.target.toId(),
164+
'content': c.content.toId(),
165+
};
166+
Map<String, dynamic> _prefix8DartPadInjector(prefix8.DartPadInjector c) => {
149167
'title': c.title,
150168
'theme': c.theme,
151169
'height': c.height,
152170
'runAutomatically': c.runAutomatically,
153171
};
154-
Map<String, dynamic> _prefix11ArchiveTable(prefix11.ArchiveTable c) => {
172+
Map<String, dynamic> _prefix9PageNav(prefix9.PageNav c) => {
173+
'breadcrumbs': c.breadcrumbs,
174+
'initialHeading': c.initialHeading,
175+
'content': c.content.toId(),
176+
};
177+
Map<String, dynamic> _prefix13ArchiveTable(prefix13.ArchiveTable c) => {
155178
'os': c.os,
156179
'channel': c.channel,
157180
};
158-
Map<String, dynamic> _prefix15InteractiveQuiz(prefix15.InteractiveQuiz c) => {
181+
Map<String, dynamic> _prefix17InteractiveQuiz(prefix17.InteractiveQuiz c) => {
159182
'title': c.title,
160183
'questions': c.questions.map((i) => i.toJson()).toList(),
161184
};

site/lib/src/components/layout/client/pagenav.dart

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import '../../util/component_ref.dart';
1414
@client
1515
class PageNav extends StatefulComponent {
1616
const PageNav({
17-
this.label,
18-
required this.title,
17+
this.breadcrumbs = const [],
18+
required this.initialHeading,
1919
required this.content,
2020
super.key,
2121
});
2222

23-
final String? label;
24-
final String title;
23+
final List<String> breadcrumbs;
24+
final String initialHeading;
2525
final ComponentRef content;
2626

2727
@override
@@ -65,21 +65,34 @@ class _PageNavState extends State<PageNav> {
6565
'aria-label': 'Toggle the table of contents dropdown',
6666
},
6767
[
68-
span(classes: 'toc-intro', [
69-
const MaterialIcon('list'),
70-
span(
71-
attributes: {'aria-label': component.label ?? 'On this page'},
72-
[
73-
text(component.label ?? 'On this page'),
74-
],
75-
),
76-
]),
68+
if (component.breadcrumbs.isEmpty)
69+
span(classes: 'toc-intro', [
70+
const MaterialIcon('list'),
71+
span(
72+
attributes: {'aria-label': 'On this page'},
73+
[text('On this page')],
74+
),
75+
])
76+
else ...[
77+
for (final (index, crumb) in component.breadcrumbs.indexed) ...[
78+
span(classes: 'toc-breadcrumb', [
79+
if (index == 0)
80+
const MaterialIcon('list')
81+
else
82+
const MaterialIcon('chevron_right'),
83+
span([
84+
text(crumb),
85+
]),
86+
]),
87+
],
88+
],
89+
7790
span(classes: 'toc-current', [
7891
const MaterialIcon('chevron_right'),
7992
ValueListenableBuilder(
8093
listenable: currentPageHeading,
8194
builder: (context, value) {
82-
return span([text(value ?? component.title)]);
95+
return span([text(value ?? component.initialHeading)]);
8396
},
8497
),
8598
]),

site/lib/src/components/layout/toc.dart

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,27 @@ final class PageNavBar extends StatelessComponent {
3333

3434
@override
3535
Component build(BuildContext context) {
36-
final currentLinkedPage = data.pageEntries
37-
.where((page) => page.url == context.page.url)
38-
.firstOrNull;
36+
PageNavigationEntry? currentLinkedPage;
37+
String? currentDivider;
38+
39+
for (final page in data.pageEntries) {
40+
if (page.url == context.page.url) {
41+
currentLinkedPage = page;
42+
break;
43+
}
44+
if (page.isDivider) {
45+
currentDivider = page.title;
46+
}
47+
}
3948

4049
final linkedPageTitle = currentLinkedPage?.title;
4150
final currentTitle = context.page.data.page['title'] as String;
4251

4352
var pageEntryNumber = 1;
4453

4554
return PageNav(
46-
label: linkedPageTitle,
47-
title: currentTitle,
55+
breadcrumbs: [?data.parentTitle, ?currentDivider, ?linkedPageTitle],
56+
initialHeading: currentTitle,
4857
content: context.ref(
4958
div([
5059
if (data.pageEntries.isEmpty) ...[

0 commit comments

Comments
 (0)