@use "/shell" as *; @use "quark:list" as list; #release-notice { /* opens once: sheet re-runs restart a delay, so gate it on the fact it writes */ &:not([data-did-open]) { @delay 7000 { is-open: ""; data-did-open: ""; } } /* prevent clicks from bubbling to the sheet */ @on mouseup, click (stop-propagation); } provider-fetch[api-url*="package-metas/index.json"][is-success] { $package-indices: prop("provision").body; /* `excom.navGroup` moves a package out of its type's list, into that group */ $ungrouped: list.reject($package-indices.packages, "navGroup"); .package-links { [bind-elements] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "kit-element")); } [bind-element-bases] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "element-base")); } [bind-tools] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "tool")); } /* the standalone libraries: same rows, rendered flat (shell.css) */ [bind-libraries] ul:not(ul ul) { content: iterate(getPackagesByType($ungrouped, "library")); } [bind-library-group] ul:not(ul ul) { content: iterate(list.filter($package-indices.packages, "navGroup", "libraries")); } ul:not(ul ul) > li { $pkg: item.shortName; > spa-a { route-href: "/packages/#{item.shortName}"; } } /* one `content` rule per link: a second one would rewrite the first's text on every pass */ ul:not(ul ul, [bind-libraries] ul) > li > spa-a { content: item.shortName; } [bind-libraries] ul:not(ul ul) > li > spa-a { content: displayName(item.shortName); } /* packages whose docs span several pages: one collapsible group per section */ [bind-sections] { content: iterate(item.docSections); summary { content: item.title; } details > ul { content: iterate(item.docs); spa-a { route-href: "/packages/#{$pkg}/#{item.name}"; content: item.title; } } details:not(:has(spa-a[is-active])) { open: none; } } } /* page titles: the page, then the site. The docs home keeps the title in the document head; the other routes carry theirs in markup */ spa-route[is-active] { /* a site guide */ &[route-href$=":name"] { $title-guide: list.find($package-indices.docs, "name", $route.params.name); document-title: "#{$title-guide.title or $route.params.name} · Nucleus · docs"; } /* a package README */ &[route-href$=":packageName"] { document-title: "#{displayName($route.params.packageName)} · Nucleus · docs"; } /* one page of a package's docs */ &[route-href$=":docName"] { $title-package: list.find($package-indices.packages, "shortName", $route.params.packageName); document-title: "#{docTitle($title-package, $route.params.docName)} · #{displayName($route.params.packageName)} · Nucleus · docs"; } } /* the page's markdown file, for the footer link: the docs home and each guide the index lists, and a package's page whose index entry says `markdown` (build-docs-index sets it; the dev server's index has none, so no link there). siteDocHref and the path check say which URL is the page's own: the 404 and a package's doc pages have none */ > spa-manager[active-url] { $page-path: attr("active-url").split("#").at(0).split("?").at(0); $page-name: if($page-path == SITE_HOME: SITE_HOME_DOC; else: $page-path.split("/").at(-1)); $page-guide: list.find($package-indices.docs, "name", $page-name); $page-package: list.find($package-indices.packages, "shortName", $page-name); $page-markdown: if($page-guide and siteDocHref($page-guide.name) == $page-path: "/docs/#{$page-guide.name}.md"; $page-package.markdown and "#{SITE_BASE}/packages/#{$page-package.shortName}" == $page-path: "/#{$page-package.shortName}.md"); footer [bind-page-markdown] { href: $page-markdown; content: ternary($page-markdown, "Markdown version of this page"); } } } spa-route { $route: prop("provision"); /* paramless routes name their guide in markup (the docs home, `/`); /docs/:name gets it from params */ $route-doc-name: attr("data-doc-name"); } /* the desktop aside and the mobile sheet stamp the same nav template */ [data-site-nav] { details:has(spa-a[is-active]) { /* not kosher */ open: ""; } spa-a[is-active] { aria-current: ""; } spa-a:not([is-active]) { aria-current: none; } } /* mobile sheet: Escape / back gesture while open; a tapped link closes it */ #site-menu { &[is-open] dismiss-watcher { is-active: ""; } &:not([is-open]) dismiss-watcher { is-active: none; } @on click (target: "spa-a") { is-open: none; } } #site-menu-gesture { &:has(> #site-menu[is-open]) { progress-offset: 1; } &:not(:has(> #site-menu[is-open])) { progress-offset: 0; } @on gesture-handler-start { #site-menu { is-scrubbing: ""; } } @on gesture-handler-end { #site-menu { is-open: event.detail.snap == 1; is-scrubbing: none; } } } main { @on copy-source (handle: copySource); } #search-dialog[open] { > include-content { is-active: ""; /* for some reason this is necessary on first render */ @on include-content-did-render (handle: focusInput); } input[type="search"] { /* works on all subsequent opens */ autofocus: ""; } } #search-dialog:not([open]) input[type="search"] { autofocus: none; }

service-worker

Observes navigator.serviceWorker and optionally relays its events — zero app JS required.

Features

  • Observation only Reports on an existing Service Worker; never registers one
  • Support detection is-supported reflects API availability
  • Ready state is-ready reflects once an active worker controls the page
  • Event relay relay-events forwards message / messageerror / controllerchange as plain DOM events
  • Bindable state .provision is { isSupported, isReady, hasController, scope } — kept current on connect, ready, and every controllerchange; read it from Quark with prop("provision")

Installation

This package is available in the NucleusKit. Or it can be used by itself:

CDN Package Manager
<script src="https://unpkg.com/@excom/kit-utils@0.3.0/dist/index.umd.min.js"></script>
<script src="https://unpkg.com/@excom/neutron@0.2.0/dist/index.umd.min.js"></script>
<script src="https://unpkg.com/@excom/service-worker@0.1.4/dist/index.umd.min.js"></script>
npm install @excom/service-worker
HTML Imports JS / CSS Imports
<!-- import path to `node_modules` will depend on your build setup -->
<script type="module" src="/node_modules/@excom/service-worker"></script>
<link rel="stylesheet" href="/node_modules/@excom/service-worker">
import "@excom/service-worker";
Peer dependencies (0)

Packages a consumer must install alongside this one. Workspace deps are bundled.

Package Version
View Dist Files

All exports:

.
  • types: ./dist/index.d.ts
  • import: ./dist/index.js
  • default: ./dist/index.js
./index
  • types: ./dist/index.d.ts
  • import: ./dist/index.js
  • default: ./dist/index.js
./index.js
  • types: ./dist/index.d.ts
  • import: ./dist/index.js
  • default: ./dist/index.js
./index.min
  • types: ./dist/index.d.ts
  • import: ./dist/index.min.js
  • default: ./dist/index.min.js
./index.min.js
  • types: ./dist/index.d.ts
  • import: ./dist/index.min.js
  • default: ./dist/index.min.js
./index.umd.min
  • types: ./dist/index.d.ts
  • default: ./dist/index.umd.min.js
./index.umd.min.js
  • types: ./dist/index.d.ts
  • default: ./dist/index.umd.min.js
./service-worker
  • types: ./dist/service-worker.d.ts
  • import: ./dist/service-worker.js
  • default: ./dist/service-worker.js
./service-worker.js
  • types: ./dist/service-worker.d.ts
  • import: ./dist/service-worker.js
  • default: ./dist/service-worker.js
./service-worker.min
  • types: ./dist/service-worker.d.ts
  • import: ./dist/service-worker.min.js
  • default: ./dist/service-worker.min.js
./service-worker.min.js
  • types: ./dist/service-worker.d.ts
  • import: ./dist/service-worker.min.js
  • default: ./dist/service-worker.min.js
./service-worker.umd.min
  • types: ./dist/service-worker.d.ts
  • default: ./dist/service-worker.umd.min.js
./service-worker.umd.min.js
  • types: ./dist/service-worker.d.ts
  • default: ./dist/service-worker.umd.min.js

Usage

This element only observes an already-registered Service Worker — it does not call navigator.serviceWorker.register(...) itself. Register your Service Worker separately (in app code, or your build tool), then drop this element anywhere to expose its state as attributes and, optionally, relay its events.

<event-handler listen-for="message" fire-event="sw-message-received">
  <service-worker relay-events="message"></service-worker>
</event-handler>

Relayed events (message, messageerror, controllerchange) are dispatched with their original names — they are not prefixed with service-worker-.

API Reference

service-worker

Attributes (3) Role — option is configurable, state is managed by the element (read-only), or hybrid which is both. Role Attribute Type Values Prop Default option relay-events
Space-separated navigator.serviceWorker events to relay onto this element. Bare attribute (no value) relays all three. Event names are relayed as-is — not prefixed with the tag. relayEvents
tokenlist "message" | "messageerror" | "controllerchange" null
state is-ready
navigator.serviceWorker.ready has resolved — an active worker is controlling the page. isReady
boolean false
state is-supported
The Service Worker API is available ('serviceWorker' in navigator). isSupported
boolean false
Provision (1) This provision property lives on the DOM node. Read / watch it with Quark’s prop("provision"), or listen for the neutron-provision event from app JS. Property Type provision
{ isSupported, isReady, hasController, scope } — set on connect, when ready resolves (scope comes from the registration), and on every controllerchange. Not reflected as an attribute. provision
ServiceWorkerProvision
Events (3)
Dispatches — events that service-worker fires and their default actions. Dispatch Default Action
A "default action" is subsequent logic executed by the element if e.preventDefault() is not synchronously called on the event.
Name controllerchange
Relayed verbatim from navigator.serviceWorker's controllerchange event when controllerchange is included in relay-events. Not tag-prefixed.

Type ServiceWorkerControllerChangeEvent
Name message
Relayed verbatim from navigator.serviceWorker's message event when message is included in relay-events. Not tag-prefixed. event.detail is event.data from the original message.

Type ServiceWorkerMessageEvent
Name messageerror
Relayed verbatim from navigator.serviceWorker's messageerror event when messageerror is included in relay-events. Not tag-prefixed.

Type ServiceWorkerMessageErrorEvent
Listeners — events that service-worker listens for which will trigger subsequent actions. Listener Type Action
Commands — verbs service-worker accepts as a command event (HTML Command API): from a <button command commandfor>, an <event-handler command-name>, or a CommandEvent. Command Action
Recognized Elements (0) Child or descendant elements are recognized by service-worker and relevant to its functionality. Element Relationship Required
Styles (0)
Classes — optional classes that change the appearance of service-worker. Class Description
Variables — public CSS variables for theming service-worker. Opt-out:
If you wish to opt-out of these styles on a case-to-case basis, use property all: revert-layer. Valence.css ships this as .unstyled and .unstyled-all for the entire tree.
Variable Syntax Default
Aliases — @custom-selector synonyms so custom elements can share semantic styles. Element aliases match the host tag / .tag-* class; state aliases match attributes (nested under the element alias). Alias Kind Matches
Release notes (1)

0.1.1

  • Ship only dist (and declared extras) in the npm tarball; drop build logs, tests and sources
View Source

Examples

Support / mount / ready state

is-supported, is-mounted (reflected by default), and is-ready are all plain attributes — style or branch on them with CSS. is-ready needs an app-registered Service Worker to ever resolve, and this docs site registers one, so it is set here.

Relay messages from your Service Worker

Relaying message / messageerror / controllerchange requires a Service Worker that your app has already registered and that is actively posting messages — this is not runnable in this docs site, but works like so once wired up:

<service-worker relay-events="message"></service-worker>
<script>
  document
    .querySelector("service-worker")
    .addEventListener("message", (e) => console.log(e.detail));
</script>

Beta. The Nucleus Stack is in beta for a few weeks until features are stabilized and optimized.

Thanks — we'll email you when it ships.

Something went wrong. Please try again.