Skip to content

Commit 21fcef2

Browse files
committed
add page nav breadcrumbs and fix styling
1 parent 56e6529 commit 21fcef2

File tree

9 files changed

+109
-72
lines changed

9 files changed

+109
-72
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: 4 additions & 2 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 {

site/lib/_sass/components/_pagenav.scss

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pagenav {
22
flex-grow: 1;
33
min-width: 0;
4+
max-width: 100%;
45

56
>button.dropdown-button {
67
display: flex;
@@ -25,27 +26,36 @@
2526
}
2627
}
2728

28-
.toc-intro {
29+
.toc-breadcrumb {
30+
flex-shrink: 2;
2931
white-space: nowrap;
32+
overflow: hidden;
3033

31-
.material-symbols {
34+
&:first-child .material-symbols {
3235
margin-right: 0.25rem;
3336
}
34-
}
3537

36-
.toc-current {
37-
display: none;
38-
39-
@media (min-width: 320px) {
40-
display: flex;
38+
span:last-child {
39+
overflow: hidden;
40+
text-overflow: ellipsis;
4141
}
42+
}
4243

43-
flex-wrap: nowrap;
44+
.toc-current {
45+
flex-shrink: 1;
4446
white-space: nowrap;
4547
overflow: hidden;
46-
text-overflow: ellipsis;
4748

4849
color: var(--site-base-fgColor-alt);
50+
51+
@media (max-width: 320px) {
52+
display: none !important;
53+
}
54+
55+
span:last-child {
56+
overflow: hidden;
57+
text-overflow: ellipsis;
58+
}
4959
}
5060

5161
#pagenav-content {
@@ -137,7 +147,7 @@
137147
color: var(--site-primary-color);
138148
}
139149

140-
~ nav {
150+
~nav {
141151
padding: 0;
142152
}
143153
}

site/lib/jaspr_options.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ Map<String, dynamic> _prefix8DartPadInjector(prefix8.DartPadInjector c) => {
170170
'runAutomatically': c.runAutomatically,
171171
};
172172
Map<String, dynamic> _prefix9PageNav(prefix9.PageNav c) => {
173-
'title': c.title,
173+
'breadcrumbs': c.breadcrumbs,
174+
'initialHeading': c.initialHeading,
174175
'content': c.content.toId(),
175176
};
176177
Map<String, dynamic> _prefix13ArchiveTable(prefix13.ArchiveTable c) => {

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) ...[

site/lib/src/layouts/tutorial_layout.dart

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,39 @@ class TutorialLayout extends DocLayout {
1515

1616
@override
1717
Component buildBody(Page page, Component child) {
18-
page.apply(
19-
data: {
20-
'page': {
21-
'showBanner': false,
22-
//TODO(schultek): Extract the real pages in some way.
23-
'navigationEntries': [
24-
{'type': 'divider', 'title': 'Introdution to Flutter UI'},
25-
{'title': 'Create a Flutter app', 'path': '/fwe0'},
26-
{'title': 'Widget fundamentals', 'path': '/fwe1'},
27-
{'title': 'Layout widgets on a screen', 'path': '/fwe2'},
28-
{'title': 'FWE Testing Page', 'path': '/fwe'},
29-
{'title': 'Devtools', 'path': '/fwe3'},
30-
{'title': 'Handle user input', 'path': '/fwe4'},
31-
{'type': 'divider', 'title': 'State in Flutter apps'},
32-
{'title': 'Set up a new project', 'path': '/fwe5'},
33-
{'title': 'Make Http Requests', 'path': '/fwe6'},
34-
{
35-
'title': 'Use ChangeNotifier to update app state',
36-
'path': '/fwe7',
37-
},
38-
{
39-
'title': 'Use ListenableBuilder to update app UI',
40-
'path': '/fwe8',
41-
},
42-
{'type': 'divider', 'title': 'Flutter UI 102'},
43-
{'title': 'Set up your project', 'path': '/fwe9'},
44-
{'title': 'LayoutBuilder and adaptive layouts', 'path': '/fwe10'},
45-
{'title': 'Scrolling and slivers', 'path': '/fwe11'},
46-
{'title': 'Stack based navigation', 'path': '/fwe12'},
47-
],
18+
//TODO(schultek): Extract the real pages in some way.
19+
const navigationEntries = [
20+
{'type': 'divider', 'title': 'Introdution to Flutter UI'},
21+
{'title': 'Create a Flutter app', 'path': '/fwe0'},
22+
{'title': 'Widget fundamentals', 'path': '/fwe1'},
23+
{'title': 'Layout widgets on a screen', 'path': '/fwe2'},
24+
{'title': 'FWE Testing Page', 'path': '/fwe'},
25+
{'title': 'Devtools', 'path': '/fwe3'},
26+
{'title': 'Handle user input', 'path': '/fwe4'},
27+
{'type': 'divider', 'title': 'State in Flutter apps'},
28+
{'title': 'Set up a new project', 'path': '/fwe5'},
29+
{'title': 'Make Http Requests', 'path': '/fwe6'},
30+
{'title': 'Use ChangeNotifier to update app state', 'path': '/fwe7'},
31+
{'title': 'Use ListenableBuilder to update app UI', 'path': '/fwe8'},
32+
{'type': 'divider', 'title': 'Flutter UI 102'},
33+
{'title': 'Set up your project', 'path': '/fwe9'},
34+
{'title': 'LayoutBuilder and adaptive layouts', 'path': '/fwe10'},
35+
{'title': 'Scrolling and slivers', 'path': '/fwe11'},
36+
{'title': 'Stack based navigation', 'path': '/fwe12'},
37+
];
38+
39+
return super.buildBody(
40+
page..apply(
41+
data: {
42+
'page': {
43+
'showBanner': false,
44+
'navigationCollectionTitle': 'Flutter Fundamentals',
45+
'navigationEntries': navigationEntries,
46+
},
47+
'sidenav': null,
4848
},
49-
'sidenav': null,
50-
},
49+
),
50+
child,
5151
);
52-
return super.buildBody(page, child);
5352
}
5453
}

site/lib/src/models/page_navigation_model.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ extension GetPageNavigationData on Page {
2020
maxLevel: pageData['maxTocDepth'] as int? ?? 3,
2121
);
2222

23+
final parentTitle = pageData['navigationCollectionTitle'] as String?;
24+
2325
final pageEntries = <PageNavigationEntry>[];
2426
if (pageData['navigationEntries'] case final List<Object?> entries) {
2527
for (final entry in entries) {
@@ -31,10 +33,10 @@ extension GetPageNavigationData on Page {
3133

3234
// If there are less than 2 top-level entries, hide the toc.
3335
if (tocData.topLevelEntries.length < 2) {
34-
return PageNavigationData(null, pageEntries);
36+
return PageNavigationData(null, pageEntries, parentTitle);
3537
}
3638

37-
return PageNavigationData(tocData, pageEntries);
39+
return PageNavigationData(tocData, pageEntries, parentTitle);
3840
}
3941

4042
TocNavigationData _getTocData(
@@ -91,13 +93,14 @@ extension GetPageNavigationData on Page {
9193
}
9294

9395
final class PageNavigationData {
94-
PageNavigationData(this.toc, this.pageEntries);
96+
PageNavigationData(this.toc, this.pageEntries, this.parentTitle);
9597

9698
final TocNavigationData? toc;
9799
final List<PageNavigationEntry> pageEntries;
100+
final String? parentTitle;
98101
}
99102

100-
class TocNavigationData {
103+
final class TocNavigationData {
101104
TocNavigationData(this.topLevelEntries);
102105

103106
final List<TocNavigationEntry> topLevelEntries;

site/lib/src/style_hash.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
// dart format off
33

44
/// The generated hash of the `main.css` file.
5-
const generatedStylesHash = 'Nx/ufc4onkpD';
5+
const generatedStylesHash = 'udYDN8P9KB1z';

0 commit comments

Comments
 (0)