// Scrollspy -- a table of contents that follows the reader.
//
//   <Scrollspy items='[{"label":"Overview","href":"#overview"}]' />
//
// Each href points at an element on the page. The runtime observes those
// elements and moves aria-current to the link for whichever one is in view.
//
// The runtime writes the marker straight onto the links rather than into
// component state. An IntersectionObserver callback fires long after the
// client function that registered it has returned, and a state write made
// there is dropped -- so the DOM is the only place the answer can live.
//
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
// and silently swallows the rule that follows it.
component Scrollspy {
  outputs {
    change(payload: { href: string; label: string })
  }

  props {
    color: string = "primary"
    size: string = "default"
    items: unknown[] = []
    active: string = ""
    label: string = "On this page"
    heading: string = ""
    class: string = ""
  }

  functions {
    shared function itemList() {
      return Array.isArray(items) ? items : []
    }

    shared function isActive(item) {
      return Boolean(item.href) && item.href === active
    }
  }

  view {
    <nav
      {...attrs}
      data-ui-component="Scrollspy"
      class='wire-scrollspy {class}'
      data-color='{color}'
      data-size='{size}'
      data-wrn-scrollspy="true"
      role="navigation"
      aria-label='{label}'
    >
      <p class="wire-scrollspy__heading" data-show="heading">{heading}</p>

      <ul class="wire-scrollspy__list">
        {#each itemList() as item}
          <li class="wire-scrollspy__item">
            <a
              class="wire-scrollspy__link"
              href='{item.href || "#"}'
              data-active='{isActive(item)}'
              aria-current='{isActive(item) ? "location" : "false"}'
            >
              <span class="wire-scrollspy__label">{item.label}</span>
            </a>
          </li>
        {/each}
      </ul>

      <slot />
    </nav>
  }

  style {
    .wire-scrollspy {
      --scrollspy-accent: var(--wire-color-primary);
      max-width: 100%;
    }

    .wire-scrollspy[data-color="secondary"] {
      --scrollspy-accent: var(--wire-color-secondary);
    }

    .wire-scrollspy[data-color="success"] {
      --scrollspy-accent: var(--wire-color-success);
    }

    .wire-scrollspy[data-color="danger"] {
      --scrollspy-accent: var(--wire-color-danger);
    }

    .wire-scrollspy[data-color="info"] {
      --scrollspy-accent: var(--wire-color-info);
    }

    .wire-scrollspy[data-size="sm"] {
      font-size: 0.82rem;
    }

    .wire-scrollspy[data-size="lg"] {
      font-size: 1rem;
    }

    .wire-scrollspy__heading {
      margin: 0 0 0.5rem;
      color: var(--wire-color-text-muted);
      font-size: 0.72rem;
      font-weight: 700;
      letter-spacing: 0.08em;
      text-transform: uppercase;
    }

    .wire-scrollspy__list {
      display: grid;
      gap: 0.1rem;
      margin: 0;
      padding: 0;
      list-style: none;
      border-left: 1px solid var(--wire-color-border);
    }

    .wire-scrollspy__link {
      display: block;
      padding: 0.3rem 0.75rem;
      margin-left: -1px;
      border-left: 2px solid transparent;
      color: var(--wire-color-text-muted);
      font-size: 0.85rem;
      text-decoration: none;
    }

    .wire-scrollspy__link:hover {
      color: var(--wire-color-text);
    }

    .wire-scrollspy__link:focus-visible {
      outline: 2px solid var(--scrollspy-accent);
      outline-offset: -2px;
    }

    .wire-scrollspy__link[data-active="true"] {
      border-left-color: var(--scrollspy-accent);
      color: var(--scrollspy-accent);
      font-weight: 600;
    }

    /*
     * A table of contents is a sidebar affordance. On a phone it stops being
     * a rail and becomes a horizontal strip that scrolls, so it costs one
     * line rather than a screenful.
     */
    @media (max-width: 767px) {
      .wire-scrollspy__list {
        grid-auto-flow: column;
        grid-auto-columns: max-content;
        overflow-x: auto;
        border-left: 0;
        border-bottom: 1px solid var(--wire-color-border);
      }

      .wire-scrollspy__link {
        margin-left: 0;
        border-left: 0;
        border-bottom: 2px solid transparent;
        white-space: nowrap;
      }

      .wire-scrollspy__link[data-active="true"] {
        border-left-color: transparent;
        border-bottom-color: var(--scrollspy-accent);
      }
    }
  }
}
