@brytdesigns/web-component-gnav-header 1.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
- const Name = "gnav-header-section";
2
- const Component = () => {
3
- };
4
-
1
+ //#region src/components/gnav-header-section.ts
2
+ var Name = "gnav-header-section";
3
+ var Component = () => {};
4
+ //#endregion
5
5
  export { Component, Name };
6
- //# sourceMappingURL=gnav-header-section.js.map
6
+
7
7
  //# sourceMappingURL=gnav-header-section.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/gnav-header-section.ts"],"names":[],"mappings":"AAQO,MAAM,IAAO,GAAA;AAEb,MAAM,YAAyC,MAAM;AAAC","file":"gnav-header-section.js","sourcesContent":["import type { CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\n\nexport type Props = {\n shouldTrigger: boolean;\n position: number;\n hideOnTrigger: boolean;\n};\n\nexport const Name = \"gnav-header-section\";\n\nexport const Component: CorrectComponentType<Props> = () => {};\n"]}
1
+ {"version":3,"file":"gnav-header-section.js","names":[],"sources":["../../../src/components/gnav-header-section.ts"],"sourcesContent":["import type { CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\n\nexport type Props = {\n\tshouldTrigger: boolean;\n\tposition: number;\n\thideOnTrigger: boolean;\n};\n\nexport const Name = \"gnav-header-section\";\n\nexport const Component: CorrectComponentType<Props> = () => {};\n"],"mappings":";AAQA,IAAa,OAAO;AAEpB,IAAa,kBAA+C,CAAC"}
@@ -1,192 +1,173 @@
1
- import '@brytdesigns/web-component-core/utils';
2
- import { awaitAllAnimations, getTransitionConfig } from '@brytdesigns/web-component-core/animation';
3
- import { debounce } from '@solid-primitives/scheduled';
4
- import { createSignal, onCleanup, createEffect, on } from 'solid-js';
5
- import { animate } from 'motion';
6
- import { updateCssVar, getCssVarValue, groupBy } from '../utils.js';
7
-
8
- const Name = `gnav-header`;
9
- const SECTION_TAG = "gnav-header-section";
1
+ import { getCssVarValue, groupBy, updateCssVar } from "../utils.js";
2
+ import { createGuardedEffect } from "@brytdesigns/web-component-core/utils";
3
+ import { awaitAllAnimations, getTransitionConfig, shouldReduceMotion } from "@brytdesigns/web-component-core/animation";
4
+ import { debounce } from "@solid-primitives/scheduled";
5
+ import { animate } from "motion";
6
+ import { createSignal, onSettled, untrack } from "solid-js";
7
+ //#region src/components/gnav-header.ts
8
+ var Name = `gnav-header`;
9
+ var SECTION_TAG = "gnav-header-section";
10
10
  function awaitInFlightSectionAnimations(sections) {
11
- return Promise.allSettled(
12
- sections.flatMap(
13
- (section) => section.getAnimations().map((animation) => animation.finished)
14
- )
15
- );
11
+ return Promise.allSettled(sections.flatMap((section) => section.getAnimations().map((animation) => animation.finished)));
16
12
  }
17
13
  function nodesContainSection(nodes) {
18
- return Array.from(nodes).some(
19
- (node) => node instanceof Element && (node.tagName.toLowerCase() === SECTION_TAG || node.querySelector(SECTION_TAG) !== null)
20
- );
14
+ return Array.from(nodes).some((node) => node instanceof Element && (node.tagName.toLowerCase() === SECTION_TAG || node.querySelector(SECTION_TAG) !== null));
21
15
  }
22
16
  function getTransition(host) {
23
- const fromCss = getTransitionConfig(getComputedStyle(host));
24
- const cleaned = Object.fromEntries(
25
- Object.entries(fromCss).filter(([, v]) => v !== void 0)
26
- );
27
- return { duration: 0.3, ease: "easeOut", ...cleaned };
17
+ const fromCss = getTransitionConfig(getComputedStyle(host));
18
+ const transition = {
19
+ duration: .3,
20
+ ease: "easeOut",
21
+ ...Object.fromEntries(Object.entries(fromCss).filter(([, v]) => v !== void 0))
22
+ };
23
+ if (shouldReduceMotion()) return {
24
+ ...transition,
25
+ duration: 0
26
+ };
27
+ return transition;
28
28
  }
29
- const Component = (props, { element }) => {
30
- const [sections, setSections] = createSignal([]);
31
- let runId = 0;
32
- function setupSection(section, position) {
33
- section.setAttribute("position", String(position));
34
- if (!section.style.position) section.style.position = "relative";
35
- if (section.hasAttribute("hide-on-trigger")) section.style.zIndex = "99";
36
- else section.style.zIndex = "100";
37
- }
38
- function refreshSections() {
39
- const found = Array.from(
40
- document.querySelectorAll(SECTION_TAG)
41
- );
42
- found.forEach((section, i) => setupSection(section, i + 1));
43
- setSections(found);
44
- }
45
- refreshSections();
46
- if (!sections().length) {
47
- console.warn("No gnav-header-section found!");
48
- }
49
- updateCssVar(element, "headerHeight", element.offsetHeight);
50
- updateCssVar(element, "collapsedHeaderHeight", getCollapsedHeaderHeight());
51
- const observer = new MutationObserver((mutations) => {
52
- const relevant = mutations.some(
53
- (m) => nodesContainSection(m.addedNodes) || nodesContainSection(m.removedNodes)
54
- );
55
- if (relevant) refreshSections();
56
- });
57
- observer.observe(document.body, { childList: true, subtree: true });
58
- onCleanup(() => observer.disconnect());
59
- createEffect(
60
- on(
61
- () => props.scrollThreshold,
62
- (scrollThreshold) => {
63
- const controller = new AbortController();
64
- let previousScroll = window.scrollY;
65
- function handleScroll() {
66
- const delta = window.scrollY - previousScroll;
67
- if (Math.abs(delta) < scrollThreshold) return;
68
- element.setAttribute("collapsed", String(delta > 0));
69
- previousScroll = Math.max(0, window.scrollY);
70
- }
71
- const handleResize = debounce(function() {
72
- updateCssVar(element, "headerHeight", element.offsetHeight);
73
- updateCssVar(
74
- element,
75
- "collapsedHeaderHeight",
76
- getCollapsedHeaderHeight()
77
- );
78
- if (props.collapsed) handleScrollDownAnimation();
79
- else handleScrollUpAnimation();
80
- }, 200);
81
- window.addEventListener("scroll", handleScroll, {
82
- capture: true,
83
- signal: controller.signal
84
- });
85
- window.addEventListener("resize", handleResize, {
86
- capture: true,
87
- signal: controller.signal
88
- });
89
- onCleanup(() => {
90
- controller.abort();
91
- handleResize.clear();
92
- });
93
- }
94
- )
95
- );
96
- createEffect(
97
- on(
98
- () => props.collapsed,
99
- (collapsed) => {
100
- if (collapsed) {
101
- return handleScrollDownAnimation();
102
- }
103
- return handleScrollUpAnimation();
104
- }
105
- )
106
- );
107
- function shouldHideSectionOnTrigger(section) {
108
- return section.hasAttribute("hide-on-trigger");
109
- }
110
- function getCollapsedHeaderHeight() {
111
- return sections().reduce((total, section) => {
112
- if (shouldHideSectionOnTrigger(section)) return total;
113
- return total + section.getBoundingClientRect().height;
114
- }, 0);
115
- }
116
- function getGroupedSections() {
117
- return groupBy(
118
- sections(),
119
- (section) => shouldHideSectionOnTrigger(section) ? "hidden" : "visible"
120
- );
121
- }
122
- async function handleScrollDownAnimation() {
123
- const id = ++runId;
124
- const currentSections = sections();
125
- if (!currentSections.length) return;
126
- await awaitInFlightSectionAnimations(currentSections);
127
- if (id !== runId) return;
128
- const transition = getTransition(element);
129
- const animations = [];
130
- const groupedSections = getGroupedSections();
131
- if (!groupedSections.hidden) groupedSections.hidden = [];
132
- if (!groupedSections.visible) groupedSections.visible = [];
133
- let offset = groupedSections.hidden.reduce((total, el) => {
134
- return total + el.getBoundingClientRect().height;
135
- }, 0);
136
- groupedSections.hidden.forEach((section) => {
137
- const bounding = section.getBoundingClientRect();
138
- const animation = animate(
139
- section,
140
- { y: `${(section.offsetTop + offset) * -1}px` },
141
- transition
142
- );
143
- offset -= bounding.height;
144
- animations.push(animation);
145
- });
146
- offset = 0;
147
- groupedSections.visible.forEach((section) => {
148
- const bounding = section.getBoundingClientRect();
149
- const animation = animate(
150
- section,
151
- { y: `${offset - section.offsetTop}px` },
152
- transition
153
- );
154
- offset += bounding.height;
155
- animations.push(animation);
156
- });
157
- updateCssVar(element, "stickyHeaderHeight", offset);
158
- updateCssVar(element, "collapsedHeaderHeight", offset);
159
- animations.push(animateStickyHeaderTop());
160
- return awaitAllAnimations(animations);
161
- }
162
- async function handleScrollUpAnimation() {
163
- const id = ++runId;
164
- const currentSections = sections();
165
- if (!currentSections.length) return;
166
- await awaitInFlightSectionAnimations(currentSections);
167
- if (id !== runId) return;
168
- updateCssVar(
169
- element,
170
- "stickyHeaderHeight",
171
- element.getBoundingClientRect().height
172
- );
173
- return awaitAllAnimations([
174
- animate(currentSections, { y: "0px" }, getTransition(element)),
175
- animateStickyHeaderTop(false)
176
- ]);
177
- }
178
- function animateStickyHeaderTop(isScrollingDown = true) {
179
- let from = getCssVarValue(element, "stickyHeaderTop");
180
- if (!from)
181
- from = isScrollingDown ? element.offsetHeight : getCollapsedHeaderHeight();
182
- const to = isScrollingDown ? getCollapsedHeaderHeight() : element.getBoundingClientRect().height;
183
- return animate(from, to, {
184
- ...getTransition(element),
185
- onUpdate: (latest) => updateCssVar(element, "stickyHeaderTop", latest)
186
- });
187
- }
29
+ var Component = (props, { element }) => {
30
+ const [sections, setSections] = createSignal([], { ownedWrite: true });
31
+ let runId = 0;
32
+ function setupSection(section, position) {
33
+ section.setAttribute("position", String(position));
34
+ if (!section.style.position) section.style.position = "relative";
35
+ if (section.hasAttribute("hide-on-trigger")) section.style.zIndex = "99";
36
+ else section.style.zIndex = "100";
37
+ }
38
+ /** Returns what it found, because the signal write does not land yet. */
39
+ function refreshSections() {
40
+ const found = Array.from(document.querySelectorAll(SECTION_TAG));
41
+ found.forEach((section, i) => {
42
+ setupSection(section, i + 1);
43
+ });
44
+ setSections(found);
45
+ return found;
46
+ }
47
+ const initialSections = refreshSections();
48
+ if (!initialSections.length) console.warn("No gnav-header-section found!");
49
+ updateCssVar("headerHeight", element.offsetHeight);
50
+ updateCssVar("collapsedHeaderHeight", getCollapsedHeaderHeight(initialSections));
51
+ const observer = new MutationObserver((mutations) => {
52
+ if (mutations.some((m) => nodesContainSection(m.addedNodes) || nodesContainSection(m.removedNodes))) refreshSections();
53
+ });
54
+ observer.observe(document.body, {
55
+ childList: true,
56
+ subtree: true
57
+ });
58
+ onSettled(() => () => observer.disconnect());
59
+ createGuardedEffect(() => props.scrollThreshold, (scrollThreshold) => {
60
+ const controller = new AbortController();
61
+ let previousScroll = window.scrollY;
62
+ function handleScroll() {
63
+ const delta = window.scrollY - previousScroll;
64
+ if (Math.abs(delta) < scrollThreshold) return;
65
+ element.setAttribute("collapsed", String(delta > 0));
66
+ previousScroll = Math.max(0, window.scrollY);
67
+ }
68
+ const handleResize = debounce(() => {
69
+ updateCssVar("headerHeight", element.offsetHeight);
70
+ updateCssVar("collapsedHeaderHeight", getCollapsedHeaderHeight());
71
+ if (props.collapsed) handleScrollDownAnimation();
72
+ else handleScrollUpAnimation();
73
+ }, 200);
74
+ window.addEventListener("scroll", handleScroll, {
75
+ capture: true,
76
+ signal: controller.signal
77
+ });
78
+ window.addEventListener("resize", handleResize, {
79
+ capture: true,
80
+ signal: controller.signal
81
+ });
82
+ return () => {
83
+ controller.abort();
84
+ handleResize.clear();
85
+ };
86
+ });
87
+ createGuardedEffect(() => props.collapsed, (collapsed) => {
88
+ if (collapsed) handleScrollDownAnimation();
89
+ else handleScrollUpAnimation();
90
+ });
91
+ /**
92
+ * The sections as they stand right now, as a declared snapshot.
93
+ *
94
+ * Everything below runs from an effect's apply phase or from an async
95
+ * animation handler it started — neither is a tracking scope, so a bare
96
+ * `sections()` there is an untracked read that Solid reports
97
+ * (STRICT_READ_UNTRACKED). Reading the current list at animation time is
98
+ * exactly right: the header measures and moves the sections that are on
99
+ * the page when the animation runs, not the ones that were there when the
100
+ * effect last re-ran. `untrack` states that, and keeps the remaining
101
+ * reports meaningful.
102
+ */
103
+ const snapshotSections = () => untrack(sections);
104
+ function shouldHideSectionOnTrigger(section) {
105
+ return section.hasAttribute("hide-on-trigger");
106
+ }
107
+ function getCollapsedHeaderHeight(list = snapshotSections()) {
108
+ return list.reduce((total, section) => {
109
+ if (shouldHideSectionOnTrigger(section)) return total;
110
+ return total + section.getBoundingClientRect().height;
111
+ }, 0);
112
+ }
113
+ function getGroupedSections() {
114
+ return groupBy(snapshotSections(), (section) => shouldHideSectionOnTrigger(section) ? "hidden" : "visible");
115
+ }
116
+ async function handleScrollDownAnimation() {
117
+ const id = ++runId;
118
+ const currentSections = snapshotSections();
119
+ if (!currentSections.length) return;
120
+ await awaitInFlightSectionAnimations(currentSections);
121
+ if (id !== runId) return;
122
+ const transition = getTransition(element);
123
+ const animations = [];
124
+ const groupedSections = getGroupedSections();
125
+ if (!groupedSections.hidden) groupedSections.hidden = [];
126
+ if (!groupedSections.visible) groupedSections.visible = [];
127
+ let offset = groupedSections.hidden.reduce((total, el) => {
128
+ return total + el.getBoundingClientRect().height;
129
+ }, 0);
130
+ groupedSections.hidden.forEach((section) => {
131
+ const bounding = section.getBoundingClientRect();
132
+ const animation = animate(section, { y: `${(section.offsetTop + offset) * -1}px` }, transition);
133
+ offset -= bounding.height;
134
+ animations.push(animation);
135
+ });
136
+ offset = 0;
137
+ groupedSections.visible.forEach((section) => {
138
+ const bounding = section.getBoundingClientRect();
139
+ const animation = animate(section, { y: `${offset - section.offsetTop}px` }, transition);
140
+ offset += bounding.height;
141
+ animations.push(animation);
142
+ });
143
+ updateCssVar("stickyHeaderHeight", offset);
144
+ updateCssVar("collapsedHeaderHeight", offset);
145
+ animations.push(animateStickyHeaderTop());
146
+ await awaitAllAnimations(animations);
147
+ if (id !== runId) return;
148
+ for (const section of groupedSections.hidden) section.setAttribute("inert", "");
149
+ }
150
+ async function handleScrollUpAnimation() {
151
+ const id = ++runId;
152
+ const currentSections = snapshotSections();
153
+ if (!currentSections.length) return;
154
+ await awaitInFlightSectionAnimations(currentSections);
155
+ if (id !== runId) return;
156
+ for (const section of currentSections) section.removeAttribute("inert");
157
+ updateCssVar("stickyHeaderHeight", element.getBoundingClientRect().height);
158
+ return awaitAllAnimations([animate(currentSections, { y: "0px" }, getTransition(element)), animateStickyHeaderTop(false)]);
159
+ }
160
+ function animateStickyHeaderTop(isScrollingDown = true) {
161
+ let from = getCssVarValue("stickyHeaderTop");
162
+ if (!from) from = isScrollingDown ? element.offsetHeight : getCollapsedHeaderHeight();
163
+ const to = isScrollingDown ? getCollapsedHeaderHeight() : element.getBoundingClientRect().height;
164
+ return animate(from, to, {
165
+ ...getTransition(element),
166
+ onUpdate: (latest) => updateCssVar("stickyHeaderTop", latest)
167
+ });
168
+ }
188
169
  };
189
-
170
+ //#endregion
190
171
  export { Component, Name };
191
- //# sourceMappingURL=gnav-header.js.map
172
+
192
173
  //# sourceMappingURL=gnav-header.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/gnav-header.ts"],"names":[],"mappings":"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","file":"gnav-header.js","sourcesContent":["import { type CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\nimport {\n awaitAllAnimations,\n getTransitionConfig,\n} from \"@brytdesigns/web-component-core/animation\";\nimport { debounce } from \"@solid-primitives/scheduled\";\nimport { onCleanup, createEffect, createSignal, on } from \"solid-js\";\nimport { animate } from \"motion\";\n\nimport { groupBy, updateCssVar, getCssVarValue } from \"../utils.js\";\n\nexport type Props = {\n scrollThreshold: number;\n collapsed: boolean;\n};\n\nexport const Name = `gnav-header`;\n\nconst SECTION_TAG = \"gnav-header-section\";\n\nfunction awaitInFlightSectionAnimations(sections: HTMLElement[]) {\n return Promise.allSettled(\n sections.flatMap((section) =>\n section.getAnimations().map((animation) => animation.finished),\n ),\n );\n}\n\nfunction nodesContainSection(nodes: NodeList): boolean {\n return Array.from(nodes).some(\n (node) =>\n node instanceof Element &&\n (node.tagName.toLowerCase() === SECTION_TAG ||\n node.querySelector(SECTION_TAG) !== null),\n );\n}\n\nfunction getTransition(host: HTMLElement) {\n const fromCss = getTransitionConfig(getComputedStyle(host));\n const cleaned = Object.fromEntries(\n Object.entries(fromCss).filter(([, v]) => v !== undefined),\n );\n return { duration: 0.3, ease: \"easeOut\" as const, ...cleaned };\n}\n\nexport const Component: CorrectComponentType<Props> = (props, { element }) => {\n const [sections, setSections] = createSignal<HTMLElement[]>([]);\n let runId = 0;\n\n function setupSection(section: HTMLElement, position: number) {\n section.setAttribute(\"position\", String(position));\n if (!section.style.position) section.style.position = \"relative\";\n if (section.hasAttribute(\"hide-on-trigger\")) section.style.zIndex = \"99\";\n else section.style.zIndex = \"100\";\n }\n\n function refreshSections() {\n const found = Array.from(\n document.querySelectorAll<HTMLElement>(SECTION_TAG),\n );\n found.forEach((section, i) => setupSection(section, i + 1));\n setSections(found);\n }\n\n refreshSections();\n\n if (!sections().length) {\n console.warn(\"No gnav-header-section found!\");\n }\n\n updateCssVar(element, \"headerHeight\", element.offsetHeight);\n updateCssVar(element, \"collapsedHeaderHeight\", getCollapsedHeaderHeight());\n\n const observer = new MutationObserver((mutations) => {\n const relevant = mutations.some(\n (m) =>\n nodesContainSection(m.addedNodes) ||\n nodesContainSection(m.removedNodes),\n );\n if (relevant) refreshSections();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n onCleanup(() => observer.disconnect());\n\n createEffect(\n on(\n () => props.scrollThreshold,\n (scrollThreshold) => {\n const controller = new AbortController();\n let previousScroll = window.scrollY;\n\n function handleScroll() {\n const delta = window.scrollY - previousScroll;\n if (Math.abs(delta) < scrollThreshold) return;\n element.setAttribute(\"collapsed\", String(delta > 0));\n previousScroll = Math.max(0, window.scrollY);\n }\n\n const handleResize = debounce(function () {\n updateCssVar(element, \"headerHeight\", element.offsetHeight);\n updateCssVar(\n element,\n \"collapsedHeaderHeight\",\n getCollapsedHeaderHeight(),\n );\n if (props.collapsed) handleScrollDownAnimation();\n else handleScrollUpAnimation();\n }, 200);\n\n window.addEventListener(\"scroll\", handleScroll, {\n capture: true,\n signal: controller.signal,\n });\n window.addEventListener(\"resize\", handleResize, {\n capture: true,\n signal: controller.signal,\n });\n\n onCleanup(() => {\n controller.abort();\n handleResize.clear();\n });\n },\n ),\n );\n\n createEffect(\n on(\n () => props.collapsed,\n (collapsed) => {\n if (collapsed) {\n return handleScrollDownAnimation();\n }\n return handleScrollUpAnimation();\n },\n ),\n );\n\n function shouldHideSectionOnTrigger(section: HTMLElement) {\n return section.hasAttribute(\"hide-on-trigger\");\n }\n\n function getCollapsedHeaderHeight() {\n return sections().reduce((total, section) => {\n if (shouldHideSectionOnTrigger(section)) return total;\n return total + section.getBoundingClientRect().height;\n }, 0);\n }\n\n function getGroupedSections() {\n return groupBy(sections(), (section) =>\n shouldHideSectionOnTrigger(section) ? \"hidden\" : \"visible\",\n );\n }\n\n async function handleScrollDownAnimation() {\n const id = ++runId;\n const currentSections = sections();\n if (!currentSections.length) return;\n\n await awaitInFlightSectionAnimations(currentSections);\n if (id !== runId) return;\n\n const transition = getTransition(element);\n const animations = [];\n const groupedSections = getGroupedSections();\n\n if (!groupedSections.hidden) groupedSections.hidden = [];\n if (!groupedSections.visible) groupedSections.visible = [];\n\n let offset = groupedSections.hidden.reduce((total, el) => {\n return total + el.getBoundingClientRect().height;\n }, 0);\n\n groupedSections.hidden.forEach((section) => {\n const bounding = section.getBoundingClientRect();\n const animation = animate(\n section,\n { y: `${(section.offsetTop + offset) * -1}px` },\n transition,\n );\n offset -= bounding.height;\n animations.push(animation);\n });\n\n offset = 0;\n\n groupedSections.visible.forEach((section) => {\n const bounding = section.getBoundingClientRect();\n const animation = animate(\n section,\n { y: `${offset - section.offsetTop}px` },\n transition,\n );\n offset += bounding.height;\n animations.push(animation);\n });\n\n updateCssVar(element, \"stickyHeaderHeight\", offset);\n updateCssVar(element, \"collapsedHeaderHeight\", offset);\n\n animations.push(animateStickyHeaderTop());\n\n return awaitAllAnimations(animations);\n }\n\n async function handleScrollUpAnimation() {\n const id = ++runId;\n const currentSections = sections();\n if (!currentSections.length) return;\n\n await awaitInFlightSectionAnimations(currentSections);\n if (id !== runId) return;\n\n updateCssVar(\n element,\n \"stickyHeaderHeight\",\n element.getBoundingClientRect().height,\n );\n return awaitAllAnimations([\n animate(currentSections, { y: \"0px\" }, getTransition(element)),\n animateStickyHeaderTop(false),\n ]);\n }\n\n function animateStickyHeaderTop(isScrollingDown = true) {\n let from = getCssVarValue(element, \"stickyHeaderTop\");\n\n if (!from)\n from = isScrollingDown ? element.offsetHeight : getCollapsedHeaderHeight();\n\n const to = isScrollingDown\n ? getCollapsedHeaderHeight()\n : element.getBoundingClientRect().height;\n\n return animate(from, to, {\n ...getTransition(element),\n onUpdate: (latest) => updateCssVar(element, \"stickyHeaderTop\", latest),\n });\n }\n};\n"]}
1
+ {"version":3,"file":"gnav-header.js","names":[],"sources":["../../../src/components/gnav-header.ts"],"sourcesContent":["import {\n\tawaitAllAnimations,\n\tgetTransitionConfig,\n\tshouldReduceMotion,\n} from \"@brytdesigns/web-component-core/animation\";\nimport type { CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\nimport { createGuardedEffect } from \"@brytdesigns/web-component-core/utils\";\nimport { debounce } from \"@solid-primitives/scheduled\";\nimport { animate } from \"motion\";\nimport { createSignal, onSettled, untrack } from \"solid-js\";\n\nimport { getCssVarValue, groupBy, updateCssVar } from \"../utils.js\";\n\nexport type Props = {\n\tscrollThreshold: number;\n\tcollapsed: boolean;\n};\n\nexport const Name = `gnav-header`;\n\nconst SECTION_TAG = \"gnav-header-section\";\n\nfunction awaitInFlightSectionAnimations(sections: HTMLElement[]) {\n\treturn Promise.allSettled(\n\t\tsections.flatMap((section) =>\n\t\t\tsection.getAnimations().map((animation) => animation.finished),\n\t\t),\n\t);\n}\n\nfunction nodesContainSection(nodes: NodeList): boolean {\n\treturn Array.from(nodes).some(\n\t\t(node) =>\n\t\t\tnode instanceof Element &&\n\t\t\t(node.tagName.toLowerCase() === SECTION_TAG ||\n\t\t\t\tnode.querySelector(SECTION_TAG) !== null),\n\t);\n}\n\nfunction getTransition(host: HTMLElement) {\n\tconst fromCss = getTransitionConfig(getComputedStyle(host));\n\tconst cleaned = Object.fromEntries(\n\t\tObject.entries(fromCss).filter(([, v]) => v !== undefined),\n\t);\n\tconst transition = { duration: 0.3, ease: \"easeOut\" as const, ...cleaned };\n\n\t// The header still collapses under reduced motion — it arrives instead of\n\t// travelling. Duration 0 rather than skipping the animation keeps every\n\t// offset calculation and CSS variable identical.\n\tif (shouldReduceMotion()) return { ...transition, duration: 0 };\n\treturn transition;\n}\n\nexport const Component: CorrectComponentType<Props> = (props, { element }) => {\n\t// `refreshSections()` runs synchronously in this component body — an owned\n\t// scope — and again from a MutationObserver callback. v2 forbids a reactive\n\t// write from an owned scope by default, and this threw\n\t// `REACTIVE_WRITE_IN_OWNED_SCOPE` at mount, halting the reactive system\n\t// before a single CSS variable was published. This is component-internal\n\t// state, which is exactly what `ownedWrite` is the opt-in for.\n\tconst [sections, setSections] = createSignal<HTMLElement[]>([], {\n\t\townedWrite: true,\n\t});\n\tlet runId = 0;\n\n\tfunction setupSection(section: HTMLElement, position: number) {\n\t\tsection.setAttribute(\"position\", String(position));\n\t\tif (!section.style.position) section.style.position = \"relative\";\n\t\tif (section.hasAttribute(\"hide-on-trigger\")) section.style.zIndex = \"99\";\n\t\telse section.style.zIndex = \"100\";\n\t}\n\n\t/** Returns what it found, because the signal write does not land yet. */\n\tfunction refreshSections() {\n\t\tconst found = Array.from(\n\t\t\tdocument.querySelectorAll<HTMLElement>(SECTION_TAG),\n\t\t);\n\t\tfound.forEach((section, i) => {\n\t\t\tsetupSection(section, i + 1);\n\t\t});\n\t\tsetSections(found);\n\t\treturn found;\n\t}\n\n\t// The returned array, not `sections()`. In v2 writes stage and flush\n\t// together, so a read in the same synchronous block still sees the old\n\t// value — here the initial `[]`. That made the header warn \"No\n\t// gnav-header-section found!\" on a page that had two, and publish\n\t// `--collapsed-header-height: 0px` instead of the height of the rows that\n\t// survive a collapse.\n\tconst initialSections = refreshSections();\n\n\tif (!initialSections.length) {\n\t\tconsole.warn(\"No gnav-header-section found!\");\n\t}\n\n\tupdateCssVar(\"headerHeight\", element.offsetHeight);\n\tupdateCssVar(\n\t\t\"collapsedHeaderHeight\",\n\t\tgetCollapsedHeaderHeight(initialSections),\n\t);\n\n\tconst observer = new MutationObserver((mutations) => {\n\t\tconst relevant = mutations.some(\n\t\t\t(m) =>\n\t\t\t\tnodesContainSection(m.addedNodes) ||\n\t\t\t\tnodesContainSection(m.removedNodes),\n\t\t);\n\t\tif (relevant) refreshSections();\n\t});\n\tobserver.observe(document.body, { childList: true, subtree: true });\n\tonSettled(() => () => observer.disconnect());\n\n\tcreateGuardedEffect(\n\t\t() => props.scrollThreshold,\n\t\t(scrollThreshold) => {\n\t\t\tconst controller = new AbortController();\n\t\t\tlet previousScroll = window.scrollY;\n\n\t\t\tfunction handleScroll() {\n\t\t\t\tconst delta = window.scrollY - previousScroll;\n\t\t\t\tif (Math.abs(delta) < scrollThreshold) return;\n\t\t\t\telement.setAttribute(\"collapsed\", String(delta > 0));\n\t\t\t\tpreviousScroll = Math.max(0, window.scrollY);\n\t\t\t}\n\n\t\t\tconst handleResize = debounce(() => {\n\t\t\t\tupdateCssVar(\"headerHeight\", element.offsetHeight);\n\t\t\t\tupdateCssVar(\"collapsedHeaderHeight\", getCollapsedHeaderHeight());\n\t\t\t\tif (props.collapsed) handleScrollDownAnimation();\n\t\t\t\telse handleScrollUpAnimation();\n\t\t\t}, 200);\n\n\t\t\twindow.addEventListener(\"scroll\", handleScroll, {\n\t\t\t\tcapture: true,\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\t\t\twindow.addEventListener(\"resize\", handleResize, {\n\t\t\t\tcapture: true,\n\t\t\t\tsignal: controller.signal,\n\t\t\t});\n\n\t\t\treturn () => {\n\t\t\t\tcontroller.abort();\n\t\t\t\thandleResize.clear();\n\t\t\t};\n\t\t},\n\t);\n\n\tcreateGuardedEffect(\n\t\t() => props.collapsed,\n\t\t(collapsed) => {\n\t\t\t// Deliberately not returning these. In v2 the apply phase's return\n\t\t\t// value IS the cleanup function, so forwarding an async handler's\n\t\t\t// promise here would be wrong — under `on()` the return was merely\n\t\t\t// passed to the next invocation and ignored.\n\t\t\tif (collapsed) handleScrollDownAnimation();\n\t\t\telse handleScrollUpAnimation();\n\t\t},\n\t);\n\n\t/**\n\t * The sections as they stand right now, as a declared snapshot.\n\t *\n\t * Everything below runs from an effect's apply phase or from an async\n\t * animation handler it started — neither is a tracking scope, so a bare\n\t * `sections()` there is an untracked read that Solid reports\n\t * (STRICT_READ_UNTRACKED). Reading the current list at animation time is\n\t * exactly right: the header measures and moves the sections that are on\n\t * the page when the animation runs, not the ones that were there when the\n\t * effect last re-ran. `untrack` states that, and keeps the remaining\n\t * reports meaningful.\n\t */\n\tconst snapshotSections = () => untrack(sections);\n\n\tfunction shouldHideSectionOnTrigger(section: HTMLElement) {\n\t\treturn section.hasAttribute(\"hide-on-trigger\");\n\t}\n\n\tfunction getCollapsedHeaderHeight(list: HTMLElement[] = snapshotSections()) {\n\t\treturn list.reduce((total, section) => {\n\t\t\tif (shouldHideSectionOnTrigger(section)) return total;\n\t\t\treturn total + section.getBoundingClientRect().height;\n\t\t}, 0);\n\t}\n\n\tfunction getGroupedSections() {\n\t\treturn groupBy(snapshotSections(), (section) =>\n\t\t\tshouldHideSectionOnTrigger(section) ? \"hidden\" : \"visible\",\n\t\t);\n\t}\n\n\tasync function handleScrollDownAnimation() {\n\t\tconst id = ++runId;\n\t\tconst currentSections = snapshotSections();\n\t\tif (!currentSections.length) return;\n\n\t\tawait awaitInFlightSectionAnimations(currentSections);\n\t\tif (id !== runId) return;\n\n\t\tconst transition = getTransition(element);\n\t\tconst animations = [];\n\t\tconst groupedSections = getGroupedSections();\n\n\t\tif (!groupedSections.hidden) groupedSections.hidden = [];\n\t\tif (!groupedSections.visible) groupedSections.visible = [];\n\n\t\tlet offset = groupedSections.hidden.reduce((total, el) => {\n\t\t\treturn total + el.getBoundingClientRect().height;\n\t\t}, 0);\n\n\t\tgroupedSections.hidden.forEach((section) => {\n\t\t\tconst bounding = section.getBoundingClientRect();\n\t\t\tconst animation = animate(\n\t\t\t\tsection,\n\t\t\t\t{ y: `${(section.offsetTop + offset) * -1}px` },\n\t\t\t\ttransition,\n\t\t\t);\n\t\t\toffset -= bounding.height;\n\t\t\tanimations.push(animation);\n\t\t});\n\n\t\toffset = 0;\n\n\t\tgroupedSections.visible.forEach((section) => {\n\t\t\tconst bounding = section.getBoundingClientRect();\n\t\t\tconst animation = animate(\n\t\t\t\tsection,\n\t\t\t\t{ y: `${offset - section.offsetTop}px` },\n\t\t\t\ttransition,\n\t\t\t);\n\t\t\toffset += bounding.height;\n\t\t\tanimations.push(animation);\n\t\t});\n\n\t\tupdateCssVar(\"stickyHeaderHeight\", offset);\n\t\tupdateCssVar(\"collapsedHeaderHeight\", offset);\n\n\t\tanimations.push(animateStickyHeaderTop());\n\n\t\tawait awaitAllAnimations(animations);\n\t\tif (id !== runId) return;\n\n\t\t// Sections are moved out of view with a transform, which removes nothing\n\t\t// from the tab order — so a keyboard user tabbed into links scrolled\n\t\t// above the viewport and watched the focus ring disappear off-screen.\n\t\t// `inert` is right here, where these are the component's own elements,\n\t\t// in a way it would not be against arbitrary theme markup.\n\t\tfor (const section of groupedSections.hidden) {\n\t\t\tsection.setAttribute(\"inert\", \"\");\n\t\t}\n\t}\n\n\tasync function handleScrollUpAnimation() {\n\t\tconst id = ++runId;\n\t\tconst currentSections = snapshotSections();\n\t\tif (!currentSections.length) return;\n\n\t\tawait awaitInFlightSectionAnimations(currentSections);\n\t\tif (id !== runId) return;\n\n\t\t// Operable again before they move, not after: the animation is what the\n\t\t// user sees, but the tab order should not lag behind it.\n\t\tfor (const section of currentSections) section.removeAttribute(\"inert\");\n\n\t\tupdateCssVar(\"stickyHeaderHeight\", element.getBoundingClientRect().height);\n\t\treturn awaitAllAnimations([\n\t\t\tanimate(currentSections, { y: \"0px\" }, getTransition(element)),\n\t\t\tanimateStickyHeaderTop(false),\n\t\t]);\n\t}\n\n\tfunction animateStickyHeaderTop(isScrollingDown = true) {\n\t\tlet from = getCssVarValue(\"stickyHeaderTop\");\n\n\t\tif (!from)\n\t\t\tfrom = isScrollingDown\n\t\t\t\t? element.offsetHeight\n\t\t\t\t: getCollapsedHeaderHeight();\n\n\t\tconst to = isScrollingDown\n\t\t\t? getCollapsedHeaderHeight()\n\t\t\t: element.getBoundingClientRect().height;\n\n\t\treturn animate(from, to, {\n\t\t\t...getTransition(element),\n\t\t\tonUpdate: (latest) => updateCssVar(\"stickyHeaderTop\", latest),\n\t\t});\n\t}\n};\n"],"mappings":"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"}
@@ -1,6 +1 @@
1
- import * as gnavHeader_js from './gnav-header.js';
2
- export { gnavHeader_js as GnavHeader };
3
- import * as gnavHeaderSection_js from './gnav-header-section.js';
4
- export { gnavHeaderSection_js as GnavHeaderSection };
5
- //# sourceMappingURL=index.js.map
6
- //# sourceMappingURL=index.js.map
1
+ import "./gnav-header.js";
@@ -2,6 +2,11 @@
2
2
 
3
3
  import { ICustomElement } from 'component-register';
4
4
 
5
+ /**
6
+ * `component-register` types `options.element` as a bare `ICustomElement`, but
7
+ * at runtime it is always also an `HTMLElement`. Every component in this repo
8
+ * needs the DOM half.
9
+ */
5
10
  export type CorrectComponentOptions = {
6
11
  element: HTMLElement & ICustomElement;
7
12
  };
@@ -1,18 +1,17 @@
1
- import { customShadowlessElement, correctElementType } from '@brytdesigns/web-component-core/utils';
2
- import { GnavHeader, GnavHeaderSection } from './components/index.js';
1
+ import { Component, Name } from "./components/gnav-header.js";
2
+ import { Component as Component$1, Name as Name$1 } from "./components/gnav-header-section.js";
3
+ import "./components/index.js";
4
+ import { customShadowlessElement } from "@brytdesigns/web-component-core/utils";
5
+ //#region src/index.ts
6
+ customShadowlessElement(Name, {
7
+ scrollThreshold: 20,
8
+ collapsed: false
9
+ }, Component);
10
+ customShadowlessElement(Name$1, {
11
+ hideOnTrigger: false,
12
+ position: 0,
13
+ shouldTrigger: false
14
+ }, Component$1);
15
+ //#endregion
3
16
 
4
- customShadowlessElement(
5
- GnavHeader.Name,
6
- {
7
- scrollThreshold: 20,
8
- collapsed: false
9
- },
10
- correctElementType(GnavHeader.Component)
11
- );
12
- customShadowlessElement(GnavHeaderSection.Name, {
13
- hideOnTrigger: false,
14
- position: 0,
15
- shouldTrigger: false
16
- }, correctElementType(GnavHeaderSection.Component));
17
- //# sourceMappingURL=index.js.map
18
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/index.ts"],"names":[],"mappings":";;;AAUA,uBAAA;AAAA,EACC,UAAW,CAAA,IAAA;AAAA,EACX;AAAA,IACC,eAAiB,EAAA,EAAA;AAAA,IACjB,SAAW,EAAA;AAAA,GACZ;AAAA,EACA,kBAAA,CAAmB,WAAW,SAAS;AACxC,CAAA;AAEA,uBAAA,CAAwB,kBAAkB,IAAM,EAAA;AAAA,EAC/C,aAAe,EAAA,KAAA;AAAA,EACf,QAAU,EAAA,CAAA;AAAA,EACV,aAAe,EAAA;AAChB,CAAG,EAAA,kBAAA,CAAmB,iBAAkB,CAAA,SAAS,CAAC,CAAA","file":"index.js","sourcesContent":["import {\n\tcorrectElementType,\n\tcustomShadowlessElement,\n} from \"@brytdesigns/web-component-core/utils\";\n\nimport { GnavHeader, GnavHeaderSection } from \"./components/index.js\";\n\nexport type GnavHeaderProps = GnavHeader.Props;\nexport type GnavHeaderSectionProps = GnavHeaderSection.Props;\n\ncustomShadowlessElement(\n\tGnavHeader.Name,\n\t{\n\t\tscrollThreshold: 20,\n\t\tcollapsed: false\n\t},\n\tcorrectElementType(GnavHeader.Component),\n);\n\ncustomShadowlessElement(GnavHeaderSection.Name, {\n\thideOnTrigger: false,\n\tposition: 0,\n\tshouldTrigger: false\n}, correctElementType(GnavHeaderSection.Component))"]}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import { customShadowlessElement } from \"@brytdesigns/web-component-core/utils\";\n\nimport { GnavHeader, GnavHeaderSection } from \"./components/index.js\";\n\nexport type GnavHeaderProps = GnavHeader.Props;\nexport type GnavHeaderSectionProps = GnavHeaderSection.Props;\n\ncustomShadowlessElement(\n\tGnavHeader.Name,\n\t{\n\t\tscrollThreshold: 20,\n\t\tcollapsed: false,\n\t},\n\tGnavHeader.Component,\n);\n\ncustomShadowlessElement(\n\tGnavHeaderSection.Name,\n\t{\n\t\thideOnTrigger: false,\n\t\tposition: 0,\n\t\tshouldTrigger: false,\n\t},\n\tGnavHeaderSection.Component,\n);\n"],"mappings":";;;;;AAOA,wBACC,MACA;CACC,iBAAiB;CACjB,WAAW;AACZ,GACA,SACD;AAEA,wBACC,QACA;CACC,eAAe;CACf,UAAU;CACV,eAAe;AAChB,GACA,WACD"}
@@ -1,26 +1,24 @@
1
- const groupBy = (list, getKey) => list.reduce(
2
- (previous, currentItem, index) => {
3
- const group = getKey(currentItem, index);
4
- if (!previous[group]) previous[group] = [];
5
- previous[group].push(currentItem);
6
- return previous;
7
- },
8
- {}
9
- );
10
- const CSSVars = {
11
- headerHeight: "--header-height",
12
- collapsedHeaderHeight: "--collapsed-header-height",
13
- stickyHeaderHeight: "--sticky-header-height",
14
- stickyHeaderTop: "--sticky-header-top"
1
+ //#region src/utils.ts
2
+ var groupBy = (list, getKey) => list.reduce((previous, currentItem, index) => {
3
+ const group = getKey(currentItem, index);
4
+ if (!previous[group]) previous[group] = [];
5
+ previous[group].push(currentItem);
6
+ return previous;
7
+ }, {});
8
+ var CSSVars = {
9
+ headerHeight: "--header-height",
10
+ collapsedHeaderHeight: "--collapsed-header-height",
11
+ stickyHeaderHeight: "--sticky-header-height",
12
+ stickyHeaderTop: "--sticky-header-top"
15
13
  };
16
- function updateCssVar(host, varName, amount) {
17
- host.style.setProperty(CSSVars[varName], `${amount}px`);
14
+ function updateCssVar(varName, amount) {
15
+ document.documentElement.style.setProperty(CSSVars[varName], `${amount}px`);
18
16
  }
19
- function getCssVarValue(host, varName) {
20
- const raw = getComputedStyle(host).getPropertyValue(CSSVars[varName]);
21
- return parseFloat(raw) || 0;
17
+ function getCssVarValue(varName) {
18
+ const raw = getComputedStyle(document.documentElement).getPropertyValue(CSSVars[varName]);
19
+ return parseFloat(raw) || 0;
22
20
  }
23
-
21
+ //#endregion
24
22
  export { CSSVars, getCssVarValue, groupBy, updateCssVar };
25
- //# sourceMappingURL=utils.js.map
23
+
26
24
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils.ts"],"names":[],"mappings":"AAAO,MAAM,OAAU,GAAA,CACrB,IACA,EAAA,MAAA,KAEA,IAAK,CAAA,MAAA;AAAA,EACH,CAAC,QAAU,EAAA,WAAA,EAAa,KAAU,KAAA;AAChC,IAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,WAAA,EAAa,KAAK,CAAA;AACvC,IAAA,IAAI,CAAC,QAAS,CAAA,KAAK,GAAY,QAAA,CAAA,KAAK,IAAI,EAAC;AACzC,IAAS,QAAA,CAAA,KAAK,CAAE,CAAA,IAAA,CAAK,WAAW,CAAA;AAChC,IAAO,OAAA,QAAA;AAAA,GACT;AAAA,EACA;AACF;AAEK,MAAM,OAAU,GAAA;AAAA,EACrB,YAAc,EAAA,iBAAA;AAAA,EACd,qBAAuB,EAAA,2BAAA;AAAA,EACvB,kBAAoB,EAAA,wBAAA;AAAA,EACpB,eAAiB,EAAA;AACnB;AAEO,SAAS,YAAA,CACd,IACA,EAAA,OAAA,EACA,MACA,EAAA;AACA,EAAA,IAAA,CAAK,MAAM,WAAY,CAAA,OAAA,CAAQ,OAAO,CAAG,EAAA,CAAA,EAAG,MAAM,CAAI,EAAA,CAAA,CAAA;AACxD;AAEO,SAAS,cAAA,CACd,MACA,OACQ,EAAA;AACR,EAAA,MAAM,MAAM,gBAAiB,CAAA,IAAI,EAAE,gBAAiB,CAAA,OAAA,CAAQ,OAAO,CAAC,CAAA;AACpE,EAAO,OAAA,UAAA,CAAW,GAAG,CAAK,IAAA,CAAA;AAC5B","file":"utils.js","sourcesContent":["export const groupBy = <T, K extends PropertyKey>(\n list: T[],\n getKey: (item: T, index: number) => K\n) =>\n list.reduce(\n (previous, currentItem, index) => {\n const group = getKey(currentItem, index);\n if (!previous[group]) previous[group] = [];\n previous[group].push(currentItem);\n return previous;\n },\n {} as Record<K, T[]>\n );\n\nexport const CSSVars = {\n headerHeight: \"--header-height\",\n collapsedHeaderHeight: \"--collapsed-header-height\",\n stickyHeaderHeight: \"--sticky-header-height\",\n stickyHeaderTop: \"--sticky-header-top\",\n} as const;\n\nexport function updateCssVar(\n host: HTMLElement,\n varName: keyof typeof CSSVars,\n amount: number\n) {\n host.style.setProperty(CSSVars[varName], `${amount}px`);\n}\n\nexport function getCssVarValue(\n host: HTMLElement,\n varName: keyof typeof CSSVars\n): number {\n const raw = getComputedStyle(host).getPropertyValue(CSSVars[varName]);\n return parseFloat(raw) || 0;\n}\n"]}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/utils.ts"],"sourcesContent":["export const groupBy = <T, K extends PropertyKey>(\n\tlist: T[],\n\tgetKey: (item: T, index: number) => K,\n) =>\n\tlist.reduce(\n\t\t(previous, currentItem, index) => {\n\t\t\tconst group = getKey(currentItem, index);\n\t\t\tif (!previous[group]) previous[group] = [];\n\t\t\tprevious[group].push(currentItem);\n\t\t\treturn previous;\n\t\t},\n\t\t{} as Record<K, T[]>,\n\t);\n\nexport const CSSVars = {\n\theaderHeight: \"--header-height\",\n\tcollapsedHeaderHeight: \"--collapsed-header-height\",\n\tstickyHeaderHeight: \"--sticky-header-height\",\n\tstickyHeaderTop: \"--sticky-header-top\",\n} as const;\n\nexport function updateCssVar(varName: keyof typeof CSSVars, amount: number) {\n\tdocument.documentElement.style.setProperty(CSSVars[varName], `${amount}px`);\n}\n\nexport function getCssVarValue(varName: keyof typeof CSSVars): number {\n\tconst raw = getComputedStyle(document.documentElement).getPropertyValue(\n\t\tCSSVars[varName],\n\t);\n\treturn parseFloat(raw) || 0;\n}\n"],"mappings":";AAAA,IAAa,WACZ,MACA,WAEA,KAAK,QACH,UAAU,aAAa,UAAU;CACjC,MAAM,QAAQ,OAAO,aAAa,KAAK;CACvC,IAAI,CAAC,SAAS,QAAQ,SAAS,SAAS,CAAC;CACzC,SAAS,MAAM,CAAC,KAAK,WAAW;CAChC,OAAO;AACR,GACA,CAAC,CACF;AAED,IAAa,UAAU;CACtB,cAAc;CACd,uBAAuB;CACvB,oBAAoB;CACpB,iBAAiB;AAClB;AAEA,SAAgB,aAAa,SAA+B,QAAgB;CAC3E,SAAS,gBAAgB,MAAM,YAAY,QAAQ,UAAU,GAAG,OAAO,GAAG;AAC3E;AAEA,SAAgB,eAAe,SAAuC;CACrE,MAAM,MAAM,iBAAiB,SAAS,eAAe,CAAC,CAAC,iBACtD,QAAQ,QACT;CACA,OAAO,WAAW,GAAG,KAAK;AAC3B"}
@@ -0,0 +1,23 @@
1
+ /*
2
+ * WCAG 2.4.11 Focus Not Obscured (Minimum), which is a 2.2 AA criterion.
3
+ *
4
+ * A sticky header covers whatever the browser scrolls to, so tabbing to a link
5
+ * below it lands on something hidden behind the bar. The component already
6
+ * measures itself and publishes the height; this is what consumes it.
7
+ *
8
+ * `--sticky-header-height` is written by gnav-header on every scroll and
9
+ * resize, so this tracks the collapsed and expanded heights without the theme
10
+ * hard-coding either.
11
+ */
12
+ :root {
13
+ scroll-padding-top: var(--sticky-header-height, 0px);
14
+ }
15
+
16
+ /*
17
+ * Collapsed sections are moved out of view with a transform, so they still
18
+ * occupy space and still paint. `inert` is applied to them in script; this
19
+ * hides them from the pointer too, which a transform alone does not do.
20
+ */
21
+ gnav-header-section[inert] {
22
+ pointer-events: none;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brytdesigns/web-component-gnav-header",
3
- "version": "1.0.1",
3
+ "version": "3.0.0",
4
4
  "description": "Web component wrapper for the gnav-header navigation element",
5
5
  "main": "./dist/main/index.js",
6
6
  "module": "./dist/main/index.js",
@@ -9,8 +9,9 @@
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/main/index.d.ts",
12
- "module": "./dist/main/index.js"
13
- }
12
+ "default": "./dist/main/index.js"
13
+ },
14
+ "./styles.css": "./dist/styles.css"
14
15
  },
15
16
  "files": [
16
17
  "dist"
@@ -19,19 +20,18 @@
19
20
  "author": "Bryt Designs",
20
21
  "license": "ISC",
21
22
  "dependencies": {
22
- "@solid-primitives/scheduled": "^1.5.3",
23
+ "@brytdesigns/web-component-core": "3.0.0",
24
+ "@solid-primitives/scheduled": "^2.0.0-next.2",
23
25
  "component-register": "0.8.7",
24
- "motion": "^12.40.0",
25
- "solid-element": "^1.9.1",
26
- "solid-js": "^1.9.9",
27
- "@brytdesigns/web-component-core": "1.1.7"
26
+ "motion": "^12.43.0",
27
+ "solid-js": "2.0.0-rc.14"
28
28
  },
29
29
  "devDependencies": {
30
- "dts-bundle-generator": "^9.5.1",
31
- "tsup": "^8.4.0"
30
+ "@repo/vite-config": "0.0.0",
31
+ "vite": "^8.2.2"
32
32
  },
33
33
  "scripts": {
34
- "build": "tsup --clean",
34
+ "build": "vite build",
35
35
  "typecheck": "tsc --noEmit"
36
36
  }
37
37
  }
package/dist/esm/index.js DELETED
@@ -1,252 +0,0 @@
1
- import { customShadowlessElement, correctElementType } from '@brytdesigns/web-component-core/utils';
2
- import { awaitAllAnimations, getTransitionConfig } from '@brytdesigns/web-component-core/animation';
3
- import { debounce } from '@solid-primitives/scheduled';
4
- import { createSignal, onCleanup, createEffect, on } from 'solid-js';
5
- import { animate } from 'motion';
6
-
7
- var __defProp = Object.defineProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
-
13
- // src/components/gnav-header.ts
14
- var gnav_header_exports = {};
15
- __export(gnav_header_exports, {
16
- Component: () => Component,
17
- Name: () => Name
18
- });
19
-
20
- // src/utils.ts
21
- var groupBy = (list, getKey) => list.reduce(
22
- (previous, currentItem, index) => {
23
- const group = getKey(currentItem, index);
24
- if (!previous[group]) previous[group] = [];
25
- previous[group].push(currentItem);
26
- return previous;
27
- },
28
- {}
29
- );
30
- var CSSVars = {
31
- headerHeight: "--header-height",
32
- collapsedHeaderHeight: "--collapsed-header-height",
33
- stickyHeaderHeight: "--sticky-header-height",
34
- stickyHeaderTop: "--sticky-header-top"
35
- };
36
- function updateCssVar(host, varName, amount) {
37
- host.style.setProperty(CSSVars[varName], `${amount}px`);
38
- }
39
- function getCssVarValue(host, varName) {
40
- const raw = getComputedStyle(host).getPropertyValue(CSSVars[varName]);
41
- return parseFloat(raw) || 0;
42
- }
43
-
44
- // src/components/gnav-header.ts
45
- var Name = `gnav-header`;
46
- var SECTION_TAG = "gnav-header-section";
47
- function awaitInFlightSectionAnimations(sections) {
48
- return Promise.allSettled(
49
- sections.flatMap(
50
- (section) => section.getAnimations().map((animation) => animation.finished)
51
- )
52
- );
53
- }
54
- function nodesContainSection(nodes) {
55
- return Array.from(nodes).some(
56
- (node) => node instanceof Element && (node.tagName.toLowerCase() === SECTION_TAG || node.querySelector(SECTION_TAG) !== null)
57
- );
58
- }
59
- function getTransition(host) {
60
- const fromCss = getTransitionConfig(getComputedStyle(host));
61
- const cleaned = Object.fromEntries(
62
- Object.entries(fromCss).filter(([, v]) => v !== void 0)
63
- );
64
- return { duration: 0.3, ease: "easeOut", ...cleaned };
65
- }
66
- var Component = (props, { element }) => {
67
- const [sections, setSections] = createSignal([]);
68
- let runId = 0;
69
- function setupSection(section, position) {
70
- section.setAttribute("position", String(position));
71
- if (!section.style.position) section.style.position = "relative";
72
- if (section.hasAttribute("hide-on-trigger")) section.style.zIndex = "99";
73
- else section.style.zIndex = "100";
74
- }
75
- function refreshSections() {
76
- const found = Array.from(
77
- document.querySelectorAll(SECTION_TAG)
78
- );
79
- found.forEach((section, i) => setupSection(section, i + 1));
80
- setSections(found);
81
- }
82
- refreshSections();
83
- if (!sections().length) {
84
- console.warn("No gnav-header-section found!");
85
- }
86
- updateCssVar(element, "headerHeight", element.offsetHeight);
87
- updateCssVar(element, "collapsedHeaderHeight", getCollapsedHeaderHeight());
88
- const observer = new MutationObserver((mutations) => {
89
- const relevant = mutations.some(
90
- (m) => nodesContainSection(m.addedNodes) || nodesContainSection(m.removedNodes)
91
- );
92
- if (relevant) refreshSections();
93
- });
94
- observer.observe(document.body, { childList: true, subtree: true });
95
- onCleanup(() => observer.disconnect());
96
- createEffect(
97
- on(
98
- () => props.scrollThreshold,
99
- (scrollThreshold) => {
100
- const controller = new AbortController();
101
- let previousScroll = window.scrollY;
102
- function handleScroll() {
103
- const delta = window.scrollY - previousScroll;
104
- if (Math.abs(delta) < scrollThreshold) return;
105
- element.setAttribute("collapsed", String(delta > 0));
106
- previousScroll = Math.max(0, window.scrollY);
107
- }
108
- const handleResize = debounce(function() {
109
- updateCssVar(element, "headerHeight", element.offsetHeight);
110
- updateCssVar(
111
- element,
112
- "collapsedHeaderHeight",
113
- getCollapsedHeaderHeight()
114
- );
115
- if (props.collapsed) handleScrollDownAnimation();
116
- else handleScrollUpAnimation();
117
- }, 200);
118
- window.addEventListener("scroll", handleScroll, {
119
- capture: true,
120
- signal: controller.signal
121
- });
122
- window.addEventListener("resize", handleResize, {
123
- capture: true,
124
- signal: controller.signal
125
- });
126
- onCleanup(() => {
127
- controller.abort();
128
- handleResize.clear();
129
- });
130
- }
131
- )
132
- );
133
- createEffect(
134
- on(
135
- () => props.collapsed,
136
- (collapsed) => {
137
- if (collapsed) {
138
- return handleScrollDownAnimation();
139
- }
140
- return handleScrollUpAnimation();
141
- }
142
- )
143
- );
144
- function shouldHideSectionOnTrigger(section) {
145
- return section.hasAttribute("hide-on-trigger");
146
- }
147
- function getCollapsedHeaderHeight() {
148
- return sections().reduce((total, section) => {
149
- if (shouldHideSectionOnTrigger(section)) return total;
150
- return total + section.getBoundingClientRect().height;
151
- }, 0);
152
- }
153
- function getGroupedSections() {
154
- return groupBy(
155
- sections(),
156
- (section) => shouldHideSectionOnTrigger(section) ? "hidden" : "visible"
157
- );
158
- }
159
- async function handleScrollDownAnimation() {
160
- const id = ++runId;
161
- const currentSections = sections();
162
- if (!currentSections.length) return;
163
- await awaitInFlightSectionAnimations(currentSections);
164
- if (id !== runId) return;
165
- const transition = getTransition(element);
166
- const animations = [];
167
- const groupedSections = getGroupedSections();
168
- if (!groupedSections.hidden) groupedSections.hidden = [];
169
- if (!groupedSections.visible) groupedSections.visible = [];
170
- let offset = groupedSections.hidden.reduce((total, el) => {
171
- return total + el.getBoundingClientRect().height;
172
- }, 0);
173
- groupedSections.hidden.forEach((section) => {
174
- const bounding = section.getBoundingClientRect();
175
- const animation = animate(
176
- section,
177
- { y: `${(section.offsetTop + offset) * -1}px` },
178
- transition
179
- );
180
- offset -= bounding.height;
181
- animations.push(animation);
182
- });
183
- offset = 0;
184
- groupedSections.visible.forEach((section) => {
185
- const bounding = section.getBoundingClientRect();
186
- const animation = animate(
187
- section,
188
- { y: `${offset - section.offsetTop}px` },
189
- transition
190
- );
191
- offset += bounding.height;
192
- animations.push(animation);
193
- });
194
- updateCssVar(element, "stickyHeaderHeight", offset);
195
- updateCssVar(element, "collapsedHeaderHeight", offset);
196
- animations.push(animateStickyHeaderTop());
197
- return awaitAllAnimations(animations);
198
- }
199
- async function handleScrollUpAnimation() {
200
- const id = ++runId;
201
- const currentSections = sections();
202
- if (!currentSections.length) return;
203
- await awaitInFlightSectionAnimations(currentSections);
204
- if (id !== runId) return;
205
- updateCssVar(
206
- element,
207
- "stickyHeaderHeight",
208
- element.getBoundingClientRect().height
209
- );
210
- return awaitAllAnimations([
211
- animate(currentSections, { y: "0px" }, getTransition(element)),
212
- animateStickyHeaderTop(false)
213
- ]);
214
- }
215
- function animateStickyHeaderTop(isScrollingDown = true) {
216
- let from = getCssVarValue(element, "stickyHeaderTop");
217
- if (!from)
218
- from = isScrollingDown ? element.offsetHeight : getCollapsedHeaderHeight();
219
- const to = isScrollingDown ? getCollapsedHeaderHeight() : element.getBoundingClientRect().height;
220
- return animate(from, to, {
221
- ...getTransition(element),
222
- onUpdate: (latest) => updateCssVar(element, "stickyHeaderTop", latest)
223
- });
224
- }
225
- };
226
-
227
- // src/components/gnav-header-section.ts
228
- var gnav_header_section_exports = {};
229
- __export(gnav_header_section_exports, {
230
- Component: () => Component2,
231
- Name: () => Name2
232
- });
233
- var Name2 = "gnav-header-section";
234
- var Component2 = () => {
235
- };
236
-
237
- // src/index.ts
238
- customShadowlessElement(
239
- gnav_header_exports.Name,
240
- {
241
- scrollThreshold: 20,
242
- collapsed: false
243
- },
244
- correctElementType(gnav_header_exports.Component)
245
- );
246
- customShadowlessElement(gnav_header_section_exports.Name, {
247
- hideOnTrigger: false,
248
- position: 0,
249
- shouldTrigger: false
250
- }, correctElementType(gnav_header_section_exports.Component));
251
- //# sourceMappingURL=index.js.map
252
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/components/gnav-header.ts","../../src/utils.ts","../../src/components/gnav-header-section.ts","../../src/index.ts"],"names":["Component","Name"],"mappings":"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","file":"index.js","sourcesContent":["import { type CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\nimport {\n awaitAllAnimations,\n getTransitionConfig,\n} from \"@brytdesigns/web-component-core/animation\";\nimport { debounce } from \"@solid-primitives/scheduled\";\nimport { onCleanup, createEffect, createSignal, on } from \"solid-js\";\nimport { animate } from \"motion\";\n\nimport { groupBy, updateCssVar, getCssVarValue } from \"../utils.js\";\n\nexport type Props = {\n scrollThreshold: number;\n collapsed: boolean;\n};\n\nexport const Name = `gnav-header`;\n\nconst SECTION_TAG = \"gnav-header-section\";\n\nfunction awaitInFlightSectionAnimations(sections: HTMLElement[]) {\n return Promise.allSettled(\n sections.flatMap((section) =>\n section.getAnimations().map((animation) => animation.finished),\n ),\n );\n}\n\nfunction nodesContainSection(nodes: NodeList): boolean {\n return Array.from(nodes).some(\n (node) =>\n node instanceof Element &&\n (node.tagName.toLowerCase() === SECTION_TAG ||\n node.querySelector(SECTION_TAG) !== null),\n );\n}\n\nfunction getTransition(host: HTMLElement) {\n const fromCss = getTransitionConfig(getComputedStyle(host));\n const cleaned = Object.fromEntries(\n Object.entries(fromCss).filter(([, v]) => v !== undefined),\n );\n return { duration: 0.3, ease: \"easeOut\" as const, ...cleaned };\n}\n\nexport const Component: CorrectComponentType<Props> = (props, { element }) => {\n const [sections, setSections] = createSignal<HTMLElement[]>([]);\n let runId = 0;\n\n function setupSection(section: HTMLElement, position: number) {\n section.setAttribute(\"position\", String(position));\n if (!section.style.position) section.style.position = \"relative\";\n if (section.hasAttribute(\"hide-on-trigger\")) section.style.zIndex = \"99\";\n else section.style.zIndex = \"100\";\n }\n\n function refreshSections() {\n const found = Array.from(\n document.querySelectorAll<HTMLElement>(SECTION_TAG),\n );\n found.forEach((section, i) => setupSection(section, i + 1));\n setSections(found);\n }\n\n refreshSections();\n\n if (!sections().length) {\n console.warn(\"No gnav-header-section found!\");\n }\n\n updateCssVar(element, \"headerHeight\", element.offsetHeight);\n updateCssVar(element, \"collapsedHeaderHeight\", getCollapsedHeaderHeight());\n\n const observer = new MutationObserver((mutations) => {\n const relevant = mutations.some(\n (m) =>\n nodesContainSection(m.addedNodes) ||\n nodesContainSection(m.removedNodes),\n );\n if (relevant) refreshSections();\n });\n observer.observe(document.body, { childList: true, subtree: true });\n onCleanup(() => observer.disconnect());\n\n createEffect(\n on(\n () => props.scrollThreshold,\n (scrollThreshold) => {\n const controller = new AbortController();\n let previousScroll = window.scrollY;\n\n function handleScroll() {\n const delta = window.scrollY - previousScroll;\n if (Math.abs(delta) < scrollThreshold) return;\n element.setAttribute(\"collapsed\", String(delta > 0));\n previousScroll = Math.max(0, window.scrollY);\n }\n\n const handleResize = debounce(function () {\n updateCssVar(element, \"headerHeight\", element.offsetHeight);\n updateCssVar(\n element,\n \"collapsedHeaderHeight\",\n getCollapsedHeaderHeight(),\n );\n if (props.collapsed) handleScrollDownAnimation();\n else handleScrollUpAnimation();\n }, 200);\n\n window.addEventListener(\"scroll\", handleScroll, {\n capture: true,\n signal: controller.signal,\n });\n window.addEventListener(\"resize\", handleResize, {\n capture: true,\n signal: controller.signal,\n });\n\n onCleanup(() => {\n controller.abort();\n handleResize.clear();\n });\n },\n ),\n );\n\n createEffect(\n on(\n () => props.collapsed,\n (collapsed) => {\n if (collapsed) {\n return handleScrollDownAnimation();\n }\n return handleScrollUpAnimation();\n },\n ),\n );\n\n function shouldHideSectionOnTrigger(section: HTMLElement) {\n return section.hasAttribute(\"hide-on-trigger\");\n }\n\n function getCollapsedHeaderHeight() {\n return sections().reduce((total, section) => {\n if (shouldHideSectionOnTrigger(section)) return total;\n return total + section.getBoundingClientRect().height;\n }, 0);\n }\n\n function getGroupedSections() {\n return groupBy(sections(), (section) =>\n shouldHideSectionOnTrigger(section) ? \"hidden\" : \"visible\",\n );\n }\n\n async function handleScrollDownAnimation() {\n const id = ++runId;\n const currentSections = sections();\n if (!currentSections.length) return;\n\n await awaitInFlightSectionAnimations(currentSections);\n if (id !== runId) return;\n\n const transition = getTransition(element);\n const animations = [];\n const groupedSections = getGroupedSections();\n\n if (!groupedSections.hidden) groupedSections.hidden = [];\n if (!groupedSections.visible) groupedSections.visible = [];\n\n let offset = groupedSections.hidden.reduce((total, el) => {\n return total + el.getBoundingClientRect().height;\n }, 0);\n\n groupedSections.hidden.forEach((section) => {\n const bounding = section.getBoundingClientRect();\n const animation = animate(\n section,\n { y: `${(section.offsetTop + offset) * -1}px` },\n transition,\n );\n offset -= bounding.height;\n animations.push(animation);\n });\n\n offset = 0;\n\n groupedSections.visible.forEach((section) => {\n const bounding = section.getBoundingClientRect();\n const animation = animate(\n section,\n { y: `${offset - section.offsetTop}px` },\n transition,\n );\n offset += bounding.height;\n animations.push(animation);\n });\n\n updateCssVar(element, \"stickyHeaderHeight\", offset);\n updateCssVar(element, \"collapsedHeaderHeight\", offset);\n\n animations.push(animateStickyHeaderTop());\n\n return awaitAllAnimations(animations);\n }\n\n async function handleScrollUpAnimation() {\n const id = ++runId;\n const currentSections = sections();\n if (!currentSections.length) return;\n\n await awaitInFlightSectionAnimations(currentSections);\n if (id !== runId) return;\n\n updateCssVar(\n element,\n \"stickyHeaderHeight\",\n element.getBoundingClientRect().height,\n );\n return awaitAllAnimations([\n animate(currentSections, { y: \"0px\" }, getTransition(element)),\n animateStickyHeaderTop(false),\n ]);\n }\n\n function animateStickyHeaderTop(isScrollingDown = true) {\n let from = getCssVarValue(element, \"stickyHeaderTop\");\n\n if (!from)\n from = isScrollingDown ? element.offsetHeight : getCollapsedHeaderHeight();\n\n const to = isScrollingDown\n ? getCollapsedHeaderHeight()\n : element.getBoundingClientRect().height;\n\n return animate(from, to, {\n ...getTransition(element),\n onUpdate: (latest) => updateCssVar(element, \"stickyHeaderTop\", latest),\n });\n }\n};\n","export const groupBy = <T, K extends PropertyKey>(\n list: T[],\n getKey: (item: T, index: number) => K\n) =>\n list.reduce(\n (previous, currentItem, index) => {\n const group = getKey(currentItem, index);\n if (!previous[group]) previous[group] = [];\n previous[group].push(currentItem);\n return previous;\n },\n {} as Record<K, T[]>\n );\n\nexport const CSSVars = {\n headerHeight: \"--header-height\",\n collapsedHeaderHeight: \"--collapsed-header-height\",\n stickyHeaderHeight: \"--sticky-header-height\",\n stickyHeaderTop: \"--sticky-header-top\",\n} as const;\n\nexport function updateCssVar(\n host: HTMLElement,\n varName: keyof typeof CSSVars,\n amount: number\n) {\n host.style.setProperty(CSSVars[varName], `${amount}px`);\n}\n\nexport function getCssVarValue(\n host: HTMLElement,\n varName: keyof typeof CSSVars\n): number {\n const raw = getComputedStyle(host).getPropertyValue(CSSVars[varName]);\n return parseFloat(raw) || 0;\n}\n","import type { CorrectComponentType } from \"@brytdesigns/web-component-core/utils\";\n\nexport type Props = {\n shouldTrigger: boolean;\n position: number;\n hideOnTrigger: boolean;\n};\n\nexport const Name = \"gnav-header-section\";\n\nexport const Component: CorrectComponentType<Props> = () => {};\n","import {\n\tcorrectElementType,\n\tcustomShadowlessElement,\n} from \"@brytdesigns/web-component-core/utils\";\n\nimport { GnavHeader, GnavHeaderSection } from \"./components/index.js\";\n\nexport type GnavHeaderProps = GnavHeader.Props;\nexport type GnavHeaderSectionProps = GnavHeaderSection.Props;\n\ncustomShadowlessElement(\n\tGnavHeader.Name,\n\t{\n\t\tscrollThreshold: 20,\n\t\tcollapsed: false\n\t},\n\tcorrectElementType(GnavHeader.Component),\n);\n\ncustomShadowlessElement(GnavHeaderSection.Name, {\n\thideOnTrigger: false,\n\tposition: 0,\n\tshouldTrigger: false\n}, correctElementType(GnavHeaderSection.Component))"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js","sourcesContent":[]}
@@ -1,3 +0,0 @@
1
-
2
- //# sourceMappingURL=index.js.map
3
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}