@ulu/frontend-vue 0.5.10 → 0.5.12

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.
@@ -5,13 +5,8 @@ type __VLS_WithTemplateSlots<T, S> = T & (new () => {
5
5
  });
6
6
  declare const __VLS_component: import('vue').DefineComponent<{}, {
7
7
  $emit: (event: "section-change", ...args: any[]) => void;
8
- observerOptions: Record<string, any>;
9
- firstItemActive: boolean;
10
- debug: boolean;
11
8
  $props: {
12
- readonly observerOptions?: Record<string, any> | undefined;
13
- readonly firstItemActive?: boolean | undefined;
14
- readonly debug?: boolean | undefined;
9
+ readonly [x: string]: any;
15
10
  };
16
11
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {
17
12
  componentEl: HTMLDivElement;
@@ -1 +1 @@
1
- {"version":3,"file":"UluScrollAnchors.vue.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/scroll-anchors/UluScrollAnchors.vue"],"names":[],"mappings":"AAKA;wBAiKqB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;;6BAEtE,CAAC,EAAE,CAAC;;;AAbjC;;;;;;;;;;;;mBAUG"}
1
+ {"version":3,"file":"UluScrollAnchors.vue.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/scroll-anchors/UluScrollAnchors.vue"],"names":[],"mappings":"AAKA;wBAqLqB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;;6BAEtE,CAAC,EAAE,CAAC;;;AAbjC;;;;;;;mBAUG"}
@@ -1,4 +1,4 @@
1
- import { ref as n, provide as r, computed as a, createElementBlock as m, openBlock as d, renderSlot as f } from "vue";
1
+ import { ref as s, provide as n, computed as p, createElementBlock as f, openBlock as m, renderSlot as d } from "vue";
2
2
  import { useScrollAnchors as h } from "./useScrollAnchors.js";
3
3
  const A = {
4
4
  __name: "UluScrollAnchors",
@@ -8,17 +8,27 @@ const A = {
8
8
  */
9
9
  firstItemActive: Boolean,
10
10
  /**
11
- * IntersectionObserver options
12
- * - Defaults: { root: null, threshold: 0, rootMargin: "-25% 0px -55% 0px" }
11
+ * Custom IntersectionObserver options to completely override internal defaults.
12
+ * Defaults: { root: null, threshold: 0, rootMargin: "-25% 0px -55% 0px" }
13
13
  * See: https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver
14
+ * @type {Object|null}
14
15
  */
15
16
  observerOptions: {
16
17
  type: Object,
17
- default: () => ({
18
- root: null,
19
- threshold: 0,
20
- rootMargin: "-25% 0px -55% 0px"
21
- })
18
+ default: null
19
+ },
20
+ /**
21
+ * Creates a strict 1% horizontal observation line to trigger active states.
22
+ * - Accepts a number representing the percentage down from the top of the screen (e.g., 20 for 20%).
23
+ * - If you pass true it will default to 20%
24
+ * - Optional not enabled by default
25
+ * - You can control this yourself with observerOptions
26
+ * @type {Number|Boolean}
27
+ * @default false
28
+ */
29
+ snapOffset: {
30
+ type: [Number, Boolean],
31
+ default: !1
22
32
  },
23
33
  /**
24
34
  * Enable debug logging for the IntersectionObserver
@@ -26,19 +36,19 @@ const A = {
26
36
  debug: Boolean
27
37
  },
28
38
  emits: ["section-change"],
29
- setup(c, { emit: s }) {
30
- const u = c, i = s, e = n([]), t = n(null);
31
- return h({ sections: e, props: u, emit: i, componentElRef: t }), r("uluScrollAnchorsSections", a(() => e.value)), r("uluScrollAnchorsRegister", (o) => {
39
+ setup(r, { emit: c }) {
40
+ const u = r, a = c, e = s([]), t = s(null);
41
+ return h({ sections: e, props: u, emit: a, componentElRef: t }), n("uluScrollAnchorsSections", p(() => e.value)), n("uluScrollAnchorsRegister", (o) => {
32
42
  e.value.push(o);
33
- }), r("uluScrollAnchorsUnregister", (o) => {
34
- const l = e.value.findIndex((p) => p.id === o);
43
+ }), n("uluScrollAnchorsUnregister", (o) => {
44
+ const l = e.value.findIndex((i) => i.id === o);
35
45
  l > -1 && e.value.splice(l, 1);
36
- }), (o, l) => (d(), m("div", {
46
+ }), (o, l) => (m(), f("div", {
37
47
  class: "scroll-anchors",
38
48
  ref_key: "componentEl",
39
49
  ref: t
40
50
  }, [
41
- f(o.$slots, "default")
51
+ d(o.$slots, "default")
42
52
  ], 512));
43
53
  }
44
54
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useScrollAnchors.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/scroll-anchors/useScrollAnchors.js"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,4EAFW;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,WAAW;IAAC,cAAc,EAAE,MAAM,CAAA;CAAC,QAqJnF"}
1
+ {"version":3,"file":"useScrollAnchors.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/scroll-anchors/useScrollAnchors.js"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,4EAFW;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,WAAW;IAAC,cAAc,EAAE,MAAM,CAAA;CAAC,QA0KnF"}
@@ -1,44 +1,44 @@
1
- import { onMounted as y, onUnmounted as T, watch as Y, nextTick as m } from "vue";
1
+ import { onMounted as T, onUnmounted as Y, watch as F, nextTick as O } from "vue";
2
2
  import { getScrollParent as C } from "@ulu/utils/browser/dom.js";
3
3
  function D({ sections: i, props: e, emit: h, componentElRef: I }) {
4
4
  let r = null;
5
5
  function v(t) {
6
6
  return i.value.findIndex(({ element: l }) => t === l);
7
7
  }
8
- function O(t = null, l = "down") {
8
+ function w(t = null, l = "down") {
9
9
  i.value.forEach((c) => {
10
10
  c !== t && (c.active && (c.inactiveFrom = l === "down" ? "forward" : "reverse", c.activeFrom = null), c.active = !1);
11
11
  });
12
12
  }
13
- function E() {
13
+ function A() {
14
14
  let t = 0, l = !0;
15
- const c = (d) => {
16
- const { root: A } = r, f = A ? A.scrollTop : document.documentElement.scrollTop || window.scrollY;
17
- if (e.debug && (console.group("useScrollAnchors: onObserve"), console.log("Observer:", r), console.log("Last/Current Y:", `${t}/${f}`), console.log("Entries:", d.map((n) => ({ el: n.target, is: n.isIntersecting })))), l && e.firstItemActive) {
18
- e.debug && console.log("Initial observation, respecting `firstItemActive`."), l = !1, t = f, e.debug && console.groupEnd();
15
+ const c = (a) => {
16
+ const { root: S } = r, d = S ? S.scrollTop : document.documentElement.scrollTop || window.scrollY;
17
+ if (e.debug && (console.group("useScrollAnchors: onObserve"), console.log("Observer:", r), console.log("Last/Current Y:", `${t}/${d}`), console.log("Entries:", a.map((n) => ({ el: n.target, is: n.isIntersecting })))), l && e.firstItemActive) {
18
+ e.debug && console.log("Initial observation, respecting `firstItemActive`."), l = !1, t = d, e.debug && console.groupEnd();
19
19
  return;
20
20
  }
21
21
  l = !1;
22
- const s = f > t ? "down" : "up";
23
- t = f, e.debug && console.log(`Scroll direction: ${s}`);
24
- const a = d.filter((n) => n.isIntersecting);
25
- if (e.debug && console.log("Intersecting entries:", a.map((n) => n.target)), a.length > 0) {
26
- a.sort((u, b) => v(u.target) - v(b.target));
27
- const n = s === "down" ? a[a.length - 1] : a[0];
22
+ const s = d > t ? "down" : "up";
23
+ t = d, e.debug && console.log(`Scroll direction: ${s}`);
24
+ const f = a.filter((n) => n.isIntersecting);
25
+ if (e.debug && console.log("Intersecting entries:", f.map((n) => n.target)), f.length > 0) {
26
+ f.sort((u, m) => v(u.target) - v(m.target));
27
+ const n = s === "down" ? f[f.length - 1] : f[0];
28
28
  e.debug && console.log("Chosen target entry:", n.target);
29
29
  const o = i.value[v(n.target)];
30
- o && !o.active && (e.debug && console.log("Activating section:", o.title), m(() => {
31
- O(o, s), o.active = !0, o.inactiveFrom = null, o.activeFrom = s === "down" ? "forward" : "reverse", h("section-change", { section: o, sections: i.value, active: !0 });
30
+ o && !o.active && (e.debug && console.log("Activating section:", o.title), O(() => {
31
+ w(o, s), o.active = !0, o.inactiveFrom = null, o.activeFrom = s === "down" ? "forward" : "reverse", h("section-change", { section: o, sections: i.value, active: !0 });
32
32
  }));
33
33
  } else {
34
34
  e.debug && console.log("No intersecting entries. Checking edge cases.");
35
35
  const n = i.value.find((o) => o.active);
36
36
  if (n) {
37
- const o = d.find((u) => u.target === n.element);
37
+ const o = a.find((u) => u.target === n.element);
38
38
  if (o && !o.isIntersecting) {
39
- const u = v(o.target), b = u === 0, x = u === i.value.length - 1;
40
- (b && s === "up" && !e.firstItemActive || x && s === "down") && (e.debug && console.log("Deactivating section at edge:", n.title), m(() => {
41
- O(null, s), h("section-change", { section: n, sections: i.value, active: !1 });
39
+ const u = v(o.target), m = u === 0, y = u === i.value.length - 1;
40
+ (m && s === "up" && !e.firstItemActive || y && s === "down") && (e.debug && console.log("Deactivating section at edge:", n.title), O(() => {
41
+ w(null, s), h("section-change", { section: n, sections: i.value, active: !1 });
42
42
  }));
43
43
  }
44
44
  }
@@ -46,34 +46,49 @@ function D({ sections: i, props: e, emit: h, componentElRef: I }) {
46
46
  e.debug && console.groupEnd();
47
47
  };
48
48
  let g = null;
49
- e.observerOptions && e.observerOptions.root ? g = e.observerOptions.root : I.value && (g = C(I.value), g === document.scrollingElement && (g = null));
50
- const F = {
51
- ...e.observerOptions,
49
+ e.observerOptions && e.observerOptions.root !== void 0 ? g = e.observerOptions.root : I.value && (g = C(I.value), g === document.scrollingElement && (g = null));
50
+ let E = {
51
+ rootMargin: "-25% 0px -55% 0px",
52
+ threshold: 0
53
+ };
54
+ if (e.snapOffset !== !1 && e.snapOffset !== void 0) {
55
+ const a = e.snapOffset === !0 ? 20 : Number(e.snapOffset);
56
+ E.rootMargin = `-${a}% 0px -${99 - a}% 0px`;
57
+ }
58
+ const $ = {
59
+ ...E,
60
+ ...e.observerOptions || {},
52
61
  root: g
53
62
  };
54
- r = new IntersectionObserver(c, F);
63
+ r = new IntersectionObserver(c, $);
55
64
  }
56
- function w() {
65
+ function b() {
57
66
  r && (r.disconnect(), i.value.forEach(({ element: t }) => {
58
67
  t && r.observe(t);
59
68
  }));
60
69
  }
61
- function S() {
70
+ function x() {
62
71
  r && (r.disconnect(), r = null);
63
72
  }
64
- y(() => {
73
+ T(() => {
65
74
  if (e.firstItemActive && i.value.length > 0) {
66
75
  const t = i.value[0];
67
76
  t && (t.active = !0);
68
77
  }
69
- E(), w();
70
- }), T(() => {
71
- S();
72
- }), Y(() => i.value.length, () => {
73
- m(() => {
74
- w();
78
+ A(), b();
79
+ }), Y(() => {
80
+ x();
81
+ }), F(() => i.value.length, () => {
82
+ O(() => {
83
+ b();
75
84
  });
76
- });
85
+ }), F(
86
+ () => [e.snapOffset, e.observerOptions],
87
+ () => {
88
+ x(), A(), b();
89
+ },
90
+ { deep: !0 }
91
+ );
77
92
  }
78
93
  export {
79
94
  D as useScrollAnchors
@@ -1 +1 @@
1
- {"version":3,"file":"useWindowResize.d.ts","sourceRoot":"","sources":["../../lib/composables/useWindowResize.js"],"names":[],"mappings":"AAiDA;;;;;;;;;;;;;GAaG;AACH,mCATa;IACR,QAAQ,EAAE,OAAO,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrC,aAAa,EAAE,CAAC,QAAQ,UAAU,aAAa,CAAC;IAChD,WAAW,EAAE,CAAC,QAAQ,UAAU,aAAa,CAAA;CAC9C,CAeH"}
1
+ {"version":3,"file":"useWindowResize.d.ts","sourceRoot":"","sources":["../../lib/composables/useWindowResize.js"],"names":[],"mappings":"AAkDA;;;;;;;;;;;;;GAaG;AACH,mCATa;IACR,QAAQ,EAAE,OAAO,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IACrC,aAAa,EAAE,CAAC,QAAQ,UAAU,aAAa,CAAC;IAChD,WAAW,EAAE,CAAC,QAAQ,UAAU,aAAa,CAAA;CAC9C,CAeH"}
@@ -1,23 +1,24 @@
1
- import { ref as u } from "vue";
2
- import { debounce as f } from "@ulu/utils/performance.js";
3
- const t = u(!1), n = {
1
+ import { ref as f } from "vue";
2
+ import { debounce as u } from "@ulu/utils/performance.js";
3
+ import { isBrowser as c } from "@ulu/utils/browser/dom.js";
4
+ const t = f(!1), n = {
4
5
  start: [],
5
6
  end: []
6
7
  };
7
8
  function r() {
8
9
  window.removeEventListener("resize", r), t.value = !0, n.start.forEach((e) => e());
9
10
  }
10
- function c() {
11
+ function a() {
11
12
  t.value = !1, n.end.forEach((e) => e()), window.addEventListener("resize", r);
12
13
  }
13
- window.addEventListener("resize", r), window.addEventListener("resize", f(c, 300));
14
+ c() && (window.addEventListener("resize", r), window.addEventListener("resize", u(a, 300)));
14
15
  function s(e, i) {
15
16
  return e.push(i), () => {
16
17
  const o = e.findIndex((d) => d === i);
17
18
  o > -1 && e.splice(o);
18
19
  };
19
20
  }
20
- function z() {
21
+ function m() {
21
22
  return {
22
23
  resizing: t,
23
24
  onResizeStart(e) {
@@ -29,5 +30,5 @@ function z() {
29
30
  };
30
31
  }
31
32
  export {
32
- z as useWindowResize
33
+ m as useWindowResize
33
34
  };
@@ -14,17 +14,27 @@
14
14
  */
15
15
  firstItemActive: Boolean,
16
16
  /**
17
- * IntersectionObserver options
18
- * - Defaults: { root: null, threshold: 0, rootMargin: "-25% 0px -55% 0px" }
17
+ * Custom IntersectionObserver options to completely override internal defaults.
18
+ * Defaults: { root: null, threshold: 0, rootMargin: "-25% 0px -55% 0px" }
19
19
  * See: https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver
20
+ * @type {Object|null}
20
21
  */
21
22
  observerOptions: {
22
23
  type: Object,
23
- default: () => ({
24
- root: null,
25
- threshold: 0,
26
- rootMargin: "-25% 0px -55% 0px"
27
- })
24
+ default: null
25
+ },
26
+ /**
27
+ * Creates a strict 1% horizontal observation line to trigger active states.
28
+ * - Accepts a number representing the percentage down from the top of the screen (e.g., 20 for 20%).
29
+ * - If you pass true it will default to 20%
30
+ * - Optional not enabled by default
31
+ * - You can control this yourself with observerOptions
32
+ * @type {Number|Boolean}
33
+ * @default false
34
+ */
35
+ snapOffset: {
36
+ type: [Number, Boolean],
37
+ default: false
28
38
  },
29
39
  /**
30
40
  * Enable debug logging for the IntersectionObserver
@@ -101,7 +101,7 @@ export function useScrollAnchors({ sections, props, emit, componentElRef }) {
101
101
  };
102
102
 
103
103
  let root = null;
104
- if (props.observerOptions && props.observerOptions.root) {
104
+ if (props.observerOptions && props.observerOptions.root !== undefined) {
105
105
  root = props.observerOptions.root;
106
106
  } else if (componentElRef.value) {
107
107
  root = getScrollParent(componentElRef.value);
@@ -110,11 +110,22 @@ export function useScrollAnchors({ sections, props, emit, componentElRef }) {
110
110
  }
111
111
  }
112
112
 
113
+ let defaultOptions = {
114
+ rootMargin: "-25% 0px -55% 0px",
115
+ threshold: 0
116
+ };
117
+
118
+ if (props.snapOffset !== false && props.snapOffset !== undefined) {
119
+ const offset = props.snapOffset === true ? 20 : Number(props.snapOffset);
120
+ defaultOptions.rootMargin = `-${offset}% 0px -${99 - offset}% 0px`;
121
+ }
122
+
113
123
  const finalObserverOptions = {
114
- ...props.observerOptions,
124
+ ...defaultOptions,
125
+ ...(props.observerOptions || {}),
115
126
  root
116
127
  };
117
-
128
+
118
129
  observer = new IntersectionObserver(onObserve, finalObserverOptions);
119
130
  }
120
131
 
@@ -155,4 +166,14 @@ export function useScrollAnchors({ sections, props, emit, componentElRef }) {
155
166
  observeItems();
156
167
  });
157
168
  });
169
+
170
+ watch(
171
+ () => [props.snapOffset, props.observerOptions],
172
+ () => {
173
+ destroyObserver();
174
+ createObserver();
175
+ observeItems();
176
+ },
177
+ { deep: true }
178
+ );
158
179
  }
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { ref } from "vue";
7
7
  import { debounce } from "@ulu/utils/performance.js";
8
+ import { isBrowser } from "@ulu/utils/browser/dom.js";
8
9
  const resizing = ref(false);
9
10
  const callbacks = {
10
11
  start: [],
@@ -26,7 +27,7 @@ function onEnd() {
26
27
  }
27
28
 
28
29
  // Only allow in browser contexts
29
- if (!import.meta.env.SSR) {
30
+ if (isBrowser()) {
30
31
  window.addEventListener("resize", onStart);
31
32
  window.addEventListener("resize", debounce(onEnd, 300));
32
33
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulu/frontend-vue",
3
- "version": "0.5.10",
3
+ "version": "0.5.12",
4
4
  "description": "A modular, tree-shakeable Vue 3 component library for the Ulu Frontend theming system, plus general utilities for Vue development",
5
5
  "type": "module",
6
6
  "files": [
@@ -65,7 +65,7 @@
65
65
  "@fortawesome/vue-fontawesome": "^3.0.8",
66
66
  "@headlessui/vue": "^1.7.23",
67
67
  "@portabletext/vue": "^1.0.14",
68
- "@ulu/frontend": "^0.4.6",
68
+ "@ulu/frontend": "^0.4.9",
69
69
  "@ulu/utils": "^0.0.34",
70
70
  "@unhead/vue": "^2.0.11",
71
71
  "fuse.js": "^6.6.2",