AlexxNB / tinro

Highly declarative, tiny, dependency free router for Svelte's web applications.
MIT License
669 stars 30 forks source link

I'd like to make a Breadcrumbs component that listens to router.meta() and updates itself #113

Closed michahell closed 2 years ago

michahell commented 2 years ago

Basically, using IBM's Carbon Components, I'd like to create this:

import { meta } from 'tinro'

  // I can't import TinroRouteMeta or TinroBreadcrumb because it's not exported :(
  export let breadcrumbs: { name: string; path: string }[] = []

  meta().subscribe((currentRoute: any) => {
    console.log('tinro current route: ', currentRoute)
    breadcrumbs = currentRoute.breadcrumbs
  })
<section class="breadcrumbs">
  <Breadcrumb>
    {#each breadcrumbs as breadcrumb}
      <BreadcrumbItem href="/{breadcrumb.path}">{breadcrumb.name}</BreadcrumbItem>
    {/each}
  </Breadcrumb>
</section>

There is probably a more svelte way to do this but I'm still picking up svelte. However, I don't see currentRoute containing any breadcrumbs. I feel that the idea is to recreate breadcrumbs on every page that sits within a Route, but I find that a bit... verbose.

Instead of this:

<Route path="/apage" let:meta>
  <Breadcrumbs breadcrumbs={meta.breadcrumbs}/>
</Route>
<Route path="/anotherpage" let:meta>
  <Breadcrumbs  breadcrumbs={meta.breadcrumbs}/>
</Route>

I want to do this:

<Header>
  <Navigation />
  <Breadcrumbs /> // listens to router.meta() and renders breadcrumb items reactively
</Header>

<Route path="/apage" breadcrumb="apage">
  <!-- apage content -->
</Route>
<Route path="/anotherpage"  breadcrumb="anotherpage">
  <!-- anotherpage content -->
</Route>

Can this be done?

michahell commented 2 years ago

I realise this is indeed not how breadcrumbs were meant to be used. I made my own tiny breadcrumb implementation:

type BreadCrumb = { name: string; url: string }

let breadcrumbs: BreadCrumb[] = []

function buildBreadCrumbs(crumbs: string[]): BreadCrumb[] {
  return crumbs.map((crumb, index) => {
    if (index === 0) {
      return {
        name: crumb,
        url: `/${crumb}`,
      }
    } else {
      const url = crumbs.reduce((acc, currCrumb, currIndex) => {
        if (currIndex <= index) {
          return acc + `${currCrumb}/`
        } else {
          return acc
        }
      }, '/')
      return {
        name: crumb,
        url: url,
      }
    }
  })
}

router.subscribe((currentRoute: TinroRoute) => {
  const crumbs = currentRoute.path.split('/').filter(Boolean)
  breadcrumbs = buildBreadCrumbs(crumbs)
})
<section class="breadcrumbs">
  <Breadcrumb>
    <BreadcrumbItem href="/">home</BreadcrumbItem>
    {#each breadcrumbs as breadcrumb}
      <BreadcrumbItem href={breadcrumb.url}>{breadcrumb.name}</BreadcrumbItem>
    {/each}
  </Breadcrumb>
</section>

Even nicer is to prepend <BreadcrumbItem href="/">home</BreadcrumbItem>

within the script tag but I'm lazy