Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 58 additions & 36 deletions site/lib/_sass/components/_sidenav.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ $sidenav-wide-layout: 1024px;
margin-top: 0.5rem;
}

a.nav-link, button.nav-link {
a, button {
background: none;
border: none;
text-wrap: pretty;
Expand All @@ -106,52 +106,55 @@ $sidenav-wide-layout: 1024px;
outline: 2px solid var(--site-primary-color);
}

> div {
display: inline-block;
&.nav-link {

.material-symbols {
font-size: 0.825rem;
margin-left: 0.125rem;
color: var(--site-base-fgColor-alt);
}
}
> div {
display: inline-block;

span {
vertical-align: middle;
}
.material-symbols {
font-size: 0.825rem;
margin-left: 0.125rem;
color: var(--site-base-fgColor-alt);
}
}

.expander {
color: var(--site-base-fgColor-lighter);
transition: transform .3s ease-in-out;
}
span {
vertical-align: middle;
}

+ ul {
display: none;
}
.expander {
color: var(--site-base-fgColor-lighter);
transition: transform .3s ease-in-out;
}

&:hover {
@include mixins.interaction-style(3%);
}
+ ul {
display: none;
}

&:active {
@include mixins.interaction-style(5%);
}
&:hover {
@include mixins.interaction-style(3%);
}

&:not(.collapsed) {
.expander {
transform: rotate(180deg);
&:active {
@include mixins.interaction-style(5%);
}

+ ul {
display: block;
&:not(.collapsed) {
.expander {
transform: rotate(180deg);
}

+ ul {
display: block;
}
}
}

&.active {
background-color: rgb(var(--site-interaction-base-values) / 4%);
&.active {
background-color: rgb(var(--site-interaction-base-values) / 4%);

&:not(.collapsible) {
color: var(--site-link-fgColor);
&:not(.collapsible) {
color: var(--site-link-fgColor);
}
}
}
}
Expand All @@ -171,11 +174,30 @@ $sidenav-wide-layout: 1024px;
.navbar-nav {
display: block;

a.nav-link {
a.nav-button {
font-size: 1.125rem;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
padding: 0.375rem 0.6rem;
justify-content: flex-start;
gap: .5rem;

&.active {
background-color: var(--site-primary-color);
color: var(--site-onPrimary-color-lightest);
}

&:hover {
@include mixins.interaction-style(5%);
}

&:active {
@include mixins.interaction-style(10%);
}
}

.material-symbols {
font-size: 1.25rem;
}

// Hide items from top navbar in wide layout to avoid duplication.
Expand Down
56 changes: 7 additions & 49 deletions site/lib/src/components/layout/header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:collection/collection.dart';
import 'package:jaspr/jaspr.dart';
import 'package:jaspr_content/jaspr_content.dart';

import '../../util.dart';
import '../../utils/active_nav.dart';
import '../common/material_icon.dart';
import 'menu_toggle.dart';
import 'site_switcher.dart';
Expand All @@ -20,7 +20,7 @@ class DashHeader extends StatelessComponent {
Component build(BuildContext context) {
final pageUrlPath = context.page.url;
final layout = context.page.data.page['layout'];
final activeEntry = _activeNavEntry(pageUrlPath);
final activeEntry = activeNavEntry(pageUrlPath);

return header(id: 'site-header', classes: 'always-dark-mode', [
nav(classes: 'navbar', [
Expand Down Expand Up @@ -52,7 +52,7 @@ class DashHeader extends StatelessComponent {
href: '/overview',
classes: [
'nav-link',
if (activeEntry == _ActiveNavEntry.overview) 'active',
if (activeEntry == ActiveNavEntry.overview) 'active',
].toClasses,
[text('Overview')],
),
Expand All @@ -62,7 +62,7 @@ class DashHeader extends StatelessComponent {
href: '/docs',
classes: [
'nav-link',
if (activeEntry == _ActiveNavEntry.docs) 'active',
if (activeEntry == ActiveNavEntry.docs) 'active',
].toClasses,
[
span([text('Docs')]),
Expand All @@ -74,7 +74,6 @@ class DashHeader extends StatelessComponent {
href: 'https://blog.dart.dev',
classes: [
'nav-link',
if (activeEntry == _ActiveNavEntry.blog) 'active',
].toClasses,
[text('Blog')],
),
Expand All @@ -84,13 +83,13 @@ class DashHeader extends StatelessComponent {
href: '/community',
classes: [
'nav-link',
if (activeEntry == _ActiveNavEntry.community) 'active',
if (activeEntry == ActiveNavEntry.community) 'active',
].toClasses,
[text('Community')],
),
]),
li([
if (activeEntry == _ActiveNavEntry.learn)
if (activeEntry == ActiveNavEntry.learn)
a(href: '/get-started', classes: 'nav-link active', [
text('Learn'),
])
Expand All @@ -104,7 +103,7 @@ class DashHeader extends StatelessComponent {
href: '/get-dart',
classes: [
'nav-link',
if (activeEntry == _ActiveNavEntry.getDart) 'active',
if (activeEntry == ActiveNavEntry.getDart) 'active',
].toClasses,
[text('Get Dart')],
),
Expand Down Expand Up @@ -152,44 +151,3 @@ class DashHeader extends StatelessComponent {
]);
}
}

_ActiveNavEntry? _activeNavEntry(String pageUrlPath) {
final firstFragment = pageUrlPath
.split('/')
.where((fragment) => fragment.isNotEmpty)
.firstOrNull
?.trim()
.toLowerCase();

return switch (firstFragment) {
'overview' => _ActiveNavEntry.overview,
'blog' => _ActiveNavEntry.blog,
'community' => _ActiveNavEntry.community,
'get-started' => _ActiveNavEntry.learn,
'get-dart' => _ActiveNavEntry.getDart,
'deprecated' ||
'docs' ||
'effective-dart' ||
'get-started' ||
'interop' ||
'language' ||
'libraries' ||
'null-safety' ||
'resources' ||
'server' ||
'tools' ||
'tutorials' ||
'web' ||
'multiplatform-apps' => _ActiveNavEntry.docs,
_ => null,
};
}

enum _ActiveNavEntry {
overview,
blog,
community,
getDart,
docs,
learn,
}
119 changes: 84 additions & 35 deletions site/lib/src/components/layout/sidenav.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
// BSD-style license that can be found in the LICENSE file.

import 'package:jaspr/jaspr.dart';
import 'package:jaspr_content/jaspr_content.dart';

import '../../models/sidenav_model.dart';
import '../../util.dart';
import '../../utils/active_nav.dart';
import '../common/material_icon.dart';

/// The site-wide side navigation menu,
Expand All @@ -23,52 +25,99 @@ final class DashSideNav extends StatelessComponent {
final String currentPageUrl;

@override
Component build(BuildContext _) => div(id: 'sidenav', [
form(action: '/search/', classes: 'site-header-search form-inline', [
input(
classes: 'site-header-searchfield search-field',
type: InputType.search,
name: 'q',
id: 'search-side',
attributes: {
'autocomplete': 'off',
'placeholder': 'Search',
'aria-label': 'Search',
},
),
]),
ul(classes: 'navbar-nav', const [
_SideNavDivider(),
_TopNavItem(href: '/overview', label: 'Overview'),
_TopNavItem(href: '/community', label: 'Community'),
_TopNavItem(href: 'https://dartpad.dev', label: 'Try Dart'),
_TopNavItem(href: '/get-dart', label: 'Get Dart'),
_TopNavItem(href: '/docs', label: 'Docs'),
_SideNavDivider(),
]),
_SideNavLevel(
entries: navEntries,
parentId: 'docs',
currentLevel: 0,
possiblyActive: true,
activePath: _ActiveNavigationPath.findActive(
Component build(BuildContext context) {
final activeEntry = activeNavEntry(context.page.url);

return div(id: 'sidenav', [
form(action: '/search/', classes: 'site-header-search form-inline', [
input(
classes: 'site-header-searchfield search-field',
type: InputType.search,
name: 'q',
id: 'search-side',
attributes: {
'autocomplete': 'off',
'placeholder': 'Search',
'aria-label': 'Search',
},
),
]),
ul(classes: 'navbar-nav', [
const _SideNavDivider(),
_TopNavItem(
href: '/overview',
label: 'Overview',
iconId: 'asterisk',
active: activeEntry == ActiveNavEntry.overview,
),
_TopNavItem(
href: '/docs',
label: 'Docs',
iconId: 'docs',
active: activeEntry == ActiveNavEntry.docs,
),
const _TopNavItem(
href: 'https://blog.dart.dev',
label: 'Blog',
iconId: 'newsmode',
),
_TopNavItem(
href: '/community',
label: 'Community',
iconId: 'public',
active: activeEntry == ActiveNavEntry.community,
),
const _TopNavItem(
href: 'https://dart.dev',
label: 'Try Dart',
iconId: 'code_blocks',
),
_TopNavItem(
href: '/get-dart',
label: 'Get Dart',
iconId: 'download',
active: activeEntry == ActiveNavEntry.getDart,
),
const _SideNavDivider(),
]),
_SideNavLevel(
entries: navEntries,
currentPageUrl: currentPageUrl,
parentId: 'docs',
currentLevel: 0,
possiblyActive: true,
activePath: _ActiveNavigationPath.findActive(
entries: navEntries,
currentPageUrl: currentPageUrl,
),
classes: 'nav',
),
classes: 'nav',
),
]);
]);
}
}

class _TopNavItem extends StatelessComponent {
const _TopNavItem({required this.href, required this.label});
const _TopNavItem({
required this.href,
required this.label,
required this.iconId,
this.active = false,
});

final String href;
final String label;
final String iconId;
final bool active;

@override
Component build(BuildContext _) => li(classes: 'nav-item', [
a(href: href, classes: 'nav-link', [text(label)]),
a(
href: href,
classes: ['nav-button', if (active) 'active'].toClasses,
[
MaterialIcon(iconId),
text(label),
],
),
]);
}

Expand Down
Loading