Active

Add doors.Active to the navigation links:

dashboard.gox

elem (d dashboard) unitNav(days int) {
	<ul>
		~(for _, units := range []driver.Units{driver.Metric, driver.Imperial} {
			<li>
				<a
					class="secondary"
					(doors.ALink{
                        // Active link setup.
                        Active: doors.Active{
                            // Indicate activity with an attribute.
							Indicator: doors.IndicateAttr("aria-current", "true"),
                            // Additional parameters for changing matching logic:
                            // PathMatcher: ...,
                            // QueryMatcher: ...,
						},
						Model: Path{
							Route: Dashboard,
							CityID: d.cityID,
							Days: daysQuery(days),
							Units: unitsQuery(units),
						},
					})>
					~(units.String())
				</a>
			</li>
		})
	</ul>
}

Let's refactor the link into a separate component, so we can reuse the same setup in both navs:

type link struct {
	city int
	days int
	units driver.Units
	text any
}

elem (l link) Main() {
	<a
		class="secondary"
		(doors.ALink{
			Model: Path{
				Route: Dashboard,
				CityID: l.city,
				Days: daysQuery(l.days),
				Units: unitsQuery(l.units),
			},
		})>
		~(l.text)
	</a>
}

Render it in the navs:

elem (d dashboard) daysNav(units driver.Units) {
	<ul>
		~(for i := range 7 {
			~{
				days := i + 1
			}
			<li>
				~navLink{
					city: d.cityID,
					days: days,
					units: units,
					text: <>
						~(days)
						~(if days == 1 {
							 day
						} else {
							 days
						})
					</>,
				}
			</li>
		})
	</ul>
}

elem (d dashboard) unitNav(days int) {
	<ul>
		~(for _, units := range []driver.Units{driver.Metric, driver.Imperial} {
			<li>
				~navLink{
					city: d.cityID,
					days: days,
					units: units,
					text: units,
				}
			</li>
		})
	</ul>
}

The active link API reuses the indication API and lets you configure rules for path and query matching. See the Navigation documentation for more details.

After it is applied to both navigation groups:

Active link highlighting for days and units


Next: Charts

Code

dashboard.gox

package main

import (
	"github.com/doors-dev/doors"
	"github.com/doors-dev/gox"
	"github.com/doors-dev/tutorial/driver"
)

func WeatherDashboard(path doors.Source[Path]) gox.Comp {
	city := doors.DeriveBeam(path, func(p Path) int {
		return p.CityID
	})
	days := doors.DeriveBeam(path, func(p Path) int {
		return p.days()
	})
	units := doors.DeriveBeam(path, func(p Path) driver.Units {
		return p.units()
	})
	return city.Bind(elem(cityID int) {
		~dashboard{
			cityID: cityID,
			days: days,
			units: units,
		}
	})
}

type dashboard struct {
	cityID int
	days doors.Beam[int]
	units doors.Beam[driver.Units]
}

elem (d dashboard) Main() {
	~{
		city, _ := driver.Locations.CitiesGet(d.cityID)
	}
	~(if !city.IsValid() {
		~(doors.Status(404))
		<title>Not Found</title>
		<h1>City Not Found</h1>
	} else {
		<title>~(city.Name) Weather</title>
		<h1>Weather in ~(city.Name, ", ", city.Country.Name)</h1>
	})
	~(d.change())
	~(d.menu())
}

elem (d dashboard) change() {
	~>(new(doors.Door)) <>
		~{
			days, _ := d.days.Effect(ctx)
			units, ok := d.units.Effect(ctx)
		}
		~(if ok {
			<a
				class="secondary"
				(doors.ALink{
					Model: Path{
						Route: Selector,
						Days: daysQuery(days),
						Units: unitsQuery(units),
					},
				})
				role="button">
				Change
			</a>
		})
	</>
}

elem (d dashboard) menu() {
	<nav>
		~(d.units.Bind(d.daysNav))
		~(d.days.Bind(d.unitNav))
	</nav>
}

elem (d dashboard) daysNav(units driver.Units) {
	<ul>
		~(for i := range 7 {
			~{
				days := i + 1
			}
			<li>
				~navLink{
					city: d.cityID,
					days: days,
					units: units,
					text: <>
						~(days)
						~(if days == 1 {
							 day
						} else {
							 days
						})
					</>,
				}
			</li>
		})
	</ul>
}

elem (d dashboard) unitNav(days int) {
	<ul>
		~(for _, units := range []driver.Units{driver.Metric, driver.Imperial} {
			<li>
				~navLink{
					city: d.cityID,
					days: days,
					units: units,
					text: units,
				}
			</li>
		})
	</ul>
}

type navLink struct {
	city int
	days int
	units driver.Units
	text any
}

elem (l navLink) Main() {
	<a
		class="secondary"
		(doors.ALink{
			Model: Path{
				Route: Dashboard,
				CityID: l.city,
				Days: daysQuery(l.days),
				Units: unitsQuery(l.units),
			},
		})>
		~(l.text)
	</a>
}