@netlify/spark-ui 0.0.23 → 0.0.25

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.
Files changed (25) hide show
  1. package/dist/chunks/{compat.module.BPxt-oTe.js → compat.module.AxtqoZMv.js} +1 -1
  2. package/dist/chunks/{hooks.module.ExpuRnbo.js → hooks.module.BFfOesdf.js} +1 -0
  3. package/dist/components/preact/Modal/Modal.js +1 -1
  4. package/dist/components/preact/SearchModal/components/SearchInput.js +1 -1
  5. package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +63 -54
  6. package/dist/components/preact/SearchModal/components/SearchToggle/SearchToggle.js +1 -1
  7. package/dist/components/preact/SearchModal/components/SearchWindow.js +28 -20
  8. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.d.ts +1 -1
  9. package/dist/components/preact/SearchModal/hooks/useKeyboardNav.js +13 -12
  10. package/dist/components/preact/SearchModal/hooks/useSearch.js +25 -18
  11. package/dist/components/preact/SearchModal/hooks/useSearchHotkeys.d.ts +1 -1
  12. package/dist/components/preact/SearchModal/hooks/useSearchHotkeys.js +9 -8
  13. package/dist/components/preact/SearchModal/hooks/useSearchParams.d.ts +1 -1
  14. package/dist/components/preact/SearchModal/hooks/useSearchParams.js +8 -8
  15. package/dist/components/preact/SearchModal/hooks/useSearchToggles.d.ts +1 -1
  16. package/dist/components/preact/SearchModal/hooks/useSearchToggles.js +11 -10
  17. package/dist/components/preact/index.js +1 -1
  18. package/package.json +1 -1
  19. package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +27 -7
  20. package/packages/components/preact/SearchModal/components/SearchWindow.tsx +10 -5
  21. package/packages/components/preact/SearchModal/hooks/useKeyboardNav.ts +3 -1
  22. package/packages/components/preact/SearchModal/hooks/useSearch.ts +12 -3
  23. package/packages/components/preact/SearchModal/hooks/useSearchHotkeys.ts +4 -2
  24. package/packages/components/preact/SearchModal/hooks/useSearchParams.ts +3 -3
  25. package/packages/components/preact/SearchModal/hooks/useSearchToggles.ts +3 -2
@@ -1,5 +1,5 @@
1
1
  import { Component as f, options as a, createElement as d, Fragment as h, toChildArray as v } from "preact";
2
- import "./hooks.module.ExpuRnbo.js";
2
+ import "./hooks.module.BFfOesdf.js";
3
3
  function C(e, t) {
4
4
  for (var r in t) e[r] = t[r];
5
5
  return e;
@@ -126,6 +126,7 @@ function W(_, t) {
126
126
  }
127
127
  export {
128
128
  I as A,
129
+ z as T,
129
130
  S as d,
130
131
  G as y
131
132
  };
@@ -1,5 +1,5 @@
1
1
  import { u as l } from "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { D as d } from "../../../chunks/compat.module.BPxt-oTe.js";
2
+ import { D as d } from "../../../chunks/compat.module.AxtqoZMv.js";
3
3
  const u = d(function({ children: o, className: r, ...a }, t) {
4
4
  return /* @__PURE__ */ l("dialog", { ref: t, className: r, role: "dialog", "aria-modal": "true", ...a, children: o });
5
5
  });
@@ -1,5 +1,5 @@
1
1
  import { u as e } from "../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { d as i, y as s } from "../../../../chunks/hooks.module.ExpuRnbo.js";
2
+ import { d as i, y as s } from "../../../../chunks/hooks.module.BFfOesdf.js";
3
3
  import h from "../../Icon/index.js";
4
4
  const m = (a) => {
5
5
  const [t, l] = i(""), c = (r) => {
@@ -1,98 +1,107 @@
1
- import { u as s } from "../../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { A as h, y as S, d as v } from "../../../../../chunks/hooks.module.ExpuRnbo.js";
3
- import N from "../../../Modal/Modal.js";
4
- import f from "../../../KeyboardKey/KeyboardKey.js";
5
- import O from "../SearchWindow.js";
6
- import T from "../SearchInput.js";
7
- import { useSearch as b } from "../../hooks/useSearch.js";
8
- import { useKeyboardNav as w } from "../../hooks/useKeyboardNav.js";
1
+ import { u as n } from "../../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
+ import { A as O, y as v, d as N } from "../../../../../chunks/hooks.module.BFfOesdf.js";
3
+ import T from "../../../Modal/Modal.js";
4
+ import p from "../../../KeyboardKey/KeyboardKey.js";
5
+ import b from "../SearchWindow.js";
6
+ import I from "../SearchInput.js";
7
+ import { useSearch as L } from "../../hooks/useSearch.js";
8
+ import { useKeyboardNav as P } from "../../hooks/useKeyboardNav.js";
9
9
  import { useSearchHotkeys as E } from "../../hooks/useSearchHotkeys.js";
10
- import { useSearchParams as g } from "../../hooks/useSearchParams.js";
11
- import { useSearchToggles as j } from "../../hooks/useSearchToggles.js";
10
+ import { useSearchParams as j } from "../../hooks/useSearchParams.js";
11
+ import { useSearchToggles as k } from "../../hooks/useSearchToggles.js";
12
12
  import '../../../../../assets/SearchModal.css';function x(t) {
13
13
  return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t;
14
14
  }
15
- var p = { exports: {} };
15
+ var h = { exports: {} };
16
16
  /*!
17
17
  Copyright (c) 2018 Jed Watson.
18
18
  Licensed under the MIT License (MIT), see
19
19
  http://jedwatson.github.io/classnames
20
20
  */
21
- var d;
21
+ var w;
22
22
  function C() {
23
- return d || (d = 1, function(t) {
23
+ return w || (w = 1, function(t) {
24
24
  (function() {
25
- var a = {}.hasOwnProperty;
26
- function n() {
25
+ var s = {}.hasOwnProperty;
26
+ function o() {
27
27
  for (var e = "", r = 0; r < arguments.length; r++) {
28
- var o = arguments[r];
29
- o && (e = c(e, i(o)));
28
+ var a = arguments[r];
29
+ a && (e = l(e, c(a)));
30
30
  }
31
31
  return e;
32
32
  }
33
- function i(e) {
33
+ function c(e) {
34
34
  if (typeof e == "string" || typeof e == "number")
35
35
  return e;
36
36
  if (typeof e != "object")
37
37
  return "";
38
38
  if (Array.isArray(e))
39
- return n.apply(null, e);
39
+ return o.apply(null, e);
40
40
  if (e.toString !== Object.prototype.toString && !e.toString.toString().includes("[native code]"))
41
41
  return e.toString();
42
42
  var r = "";
43
- for (var o in e)
44
- a.call(e, o) && e[o] && (r = c(r, o));
43
+ for (var a in e)
44
+ s.call(e, a) && e[a] && (r = l(r, a));
45
45
  return r;
46
46
  }
47
- function c(e, r) {
47
+ function l(e, r) {
48
48
  return r ? e ? e + " " + r : e + r : e;
49
49
  }
50
- t.exports ? (n.default = n, t.exports = n) : window.classNames = n;
50
+ t.exports ? (o.default = o, t.exports = o) : window.classNames = o;
51
51
  })();
52
- }(p)), p.exports;
52
+ }(h)), h.exports;
53
53
  }
54
- var L = C();
55
- const M = /* @__PURE__ */ x(L);
56
- function k(t) {
57
- const a = h(null);
58
- return S(() => {
59
- function n(i) {
60
- a.current && !a.current.contains(i.target) && t();
54
+ var M = C();
55
+ const A = /* @__PURE__ */ x(M);
56
+ function K(t) {
57
+ const s = O(null);
58
+ return v(() => {
59
+ function o(c) {
60
+ s.current && !s.current.contains(c.target) && t();
61
61
  }
62
- return document.addEventListener("mousedown", n), () => {
63
- document.removeEventListener("mousedown", n);
62
+ return document.addEventListener("mousedown", o), () => {
63
+ document.removeEventListener("mousedown", o);
64
64
  };
65
- }, [t]), a;
65
+ }, [t]), s;
66
66
  }
67
- function W(t = { className: "", children: null }) {
68
- const { children: a, className: n, ...i } = t, { state: c, actions: e } = b(), [r, o] = v(!1), l = () => o(!0), m = () => o(!1), u = h(null), y = k(m);
69
- return j(l), g(e.setSearchTerm, l), E(r, l, m), w(u, r, m), S(() => {
70
- u.current && (r ? u.current.showModal() : u.current.close());
71
- }, [r]), /* @__PURE__ */ s(N, { ref: u, className: M(n, "n-search-modal"), ...i, children: /* @__PURE__ */ s("div", { ref: y, className: "n-search-container", children: [
72
- /* @__PURE__ */ s("form", { className: "n-search-bar", children: /* @__PURE__ */ s(T, { setSearchTerm: e.setSearchTerm, initialValue: c.searchTerm }) }),
73
- /* @__PURE__ */ s(
74
- O,
67
+ function J(t = { className: "", children: null }) {
68
+ const [s, o] = N(!1);
69
+ v(() => {
70
+ const y = () => {
71
+ window.pagefind && (o(!0), d && clearInterval(d));
72
+ };
73
+ y();
74
+ const d = setInterval(y, 100);
75
+ return () => clearInterval(d);
76
+ }, []);
77
+ const { children: c, className: l, ...e } = t, { state: r, actions: a } = L(), [u, S] = N(!1), f = () => S(!0), m = () => S(!1), i = O(null), g = K(m);
78
+ return k(s, f), j(s, a.setSearchTerm, f), E(s, u, f, m), P(s, i, u, m), v(() => {
79
+ i.current && (u ? i.current.showModal() : i.current.close());
80
+ }, [u]), /* @__PURE__ */ n(T, { ref: i, className: A(l, "n-search-modal"), ...e, children: /* @__PURE__ */ n("div", { ref: g, className: "n-search-container", children: [
81
+ /* @__PURE__ */ n("form", { className: "n-search-bar", children: /* @__PURE__ */ n(I, { setSearchTerm: a.setSearchTerm, initialValue: r.searchTerm }) }),
82
+ /* @__PURE__ */ n(
83
+ b,
75
84
  {
76
- searchLoading: c.isLoading,
77
- searchTerm: c.searchTerm,
78
- results: c.results,
79
- children: a
85
+ searchLoading: r.isLoading,
86
+ searchTerm: r.searchTerm,
87
+ results: r.results,
88
+ children: c
80
89
  }
81
90
  ),
82
- /* @__PURE__ */ s("div", { className: "n-search-footer", children: [
83
- /* @__PURE__ */ s("p", { className: "n-search-footer-tip", children: [
84
- /* @__PURE__ */ s(f, { keyNames: ["Esc"] }),
91
+ /* @__PURE__ */ n("div", { className: "n-search-footer", children: [
92
+ /* @__PURE__ */ n("p", { className: "n-search-footer-tip", children: [
93
+ /* @__PURE__ */ n(p, { keyNames: ["Esc"] }),
85
94
  "to close"
86
95
  ] }),
87
- /* @__PURE__ */ s("p", { className: "n-search-footer-tip", children: [
88
- /* @__PURE__ */ s(f, { keyNames: ["Tab"] }),
89
- /* @__PURE__ */ s("span", { children: "or" }),
90
- /* @__PURE__ */ s(f, { keyNames: ["↑", "↓"] }),
96
+ /* @__PURE__ */ n("p", { className: "n-search-footer-tip", children: [
97
+ /* @__PURE__ */ n(p, { keyNames: ["Tab"] }),
98
+ /* @__PURE__ */ n("span", { children: "or" }),
99
+ /* @__PURE__ */ n(p, { keyNames: ["↑", "↓"] }),
91
100
  "to navigate"
92
101
  ] })
93
102
  ] })
94
103
  ] }) });
95
104
  }
96
105
  export {
97
- W as default
106
+ J as default
98
107
  };
@@ -1,5 +1,5 @@
1
1
  import { u as e } from "../../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import { D as t } from "../../../../../chunks/compat.module.BPxt-oTe.js";
2
+ import { D as t } from "../../../../../chunks/compat.module.AxtqoZMv.js";
3
3
  import o from "../../../Icon/index.js";
4
4
  import '../../../../../assets/SearchToggle.css';const s = t(function(r, a) {
5
5
  return /* @__PURE__ */ e("button", { id: r.id, ref: a, class: "n-search-toggle", disabled: !0, "data-ntl-search-toggle": !0, children: [
@@ -1,33 +1,41 @@
1
1
  import { u as e } from "../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
2
- import a from "./SearchHeading/SearchHeading.js";
3
- import c from "./SearchLink/SearchLink.js";
4
- const t = ({
5
- searchLoading: s,
6
- searchTerm: l,
2
+ import { T as o } from "../../../../chunks/hooks.module.BFfOesdf.js";
3
+ import t from "./SearchHeading/SearchHeading.js";
4
+ import a from "./SearchLink/SearchLink.js";
5
+ const h = ({
6
+ searchLoading: i,
7
+ searchTerm: n,
7
8
  results: r
8
- }) => s ? /* @__PURE__ */ e(a, { children: [
9
- "Searching for ",
10
- /* @__PURE__ */ e("span", { className: "n-search-term", children: l }),
11
- " ..."
12
- ] }) : !r || r.length === 0 ? /* @__PURE__ */ e(a, { children: [
13
- "0 results for ",
14
- /* @__PURE__ */ e("span", { className: "n-search-term", children: l })
15
- ] }) : /* @__PURE__ */ e("ul", { className: "n-results-list", role: "list", children: r.slice(0, 15).map((i, n) => /* @__PURE__ */ e("li", { className: "n-results-list-item", children: /* @__PURE__ */ e(c, { ...i }) }, n)) });
16
- function m({
17
- searchLoading: s,
18
- searchTerm: l,
9
+ }) => {
10
+ if (i)
11
+ return /* @__PURE__ */ e(t, { children: [
12
+ "Searching for ",
13
+ /* @__PURE__ */ e("span", { className: "n-search-term", children: n }),
14
+ " ..."
15
+ ] });
16
+ if (!r || r.length === 0)
17
+ return /* @__PURE__ */ e(t, { children: [
18
+ "0 results for ",
19
+ /* @__PURE__ */ e("span", { className: "n-search-term", children: n })
20
+ ] });
21
+ const l = o(() => r.map((s, c) => /* @__PURE__ */ e("li", { className: "n-results-list-item", children: /* @__PURE__ */ e(a, { ...s }) }, c)), [r]);
22
+ return /* @__PURE__ */ e("ul", { className: "n-results-list", role: "list", children: l });
23
+ };
24
+ function N({
25
+ searchLoading: i,
26
+ searchTerm: n,
19
27
  results: r,
20
- children: i
28
+ children: l
21
29
  }) {
22
30
  return /* @__PURE__ */ e("div", { className: "n-search-window", children: /* @__PURE__ */ e("div", { className: "search-links", children: [
23
- /* @__PURE__ */ e(c, { ...{
31
+ /* @__PURE__ */ e(a, { ...{
24
32
  icon: "askNetlify",
25
33
  label: "Ask Netlify AI",
26
34
  url: "https://docs.netlify.com/ask-netlify/"
27
35
  } }),
28
- l ? /* @__PURE__ */ e(t, { searchLoading: s, searchTerm: l, results: r }) : i || null
36
+ n ? /* @__PURE__ */ e(h, { searchLoading: i, searchTerm: n, results: r }) : l || null
29
37
  ] }) });
30
38
  }
31
39
  export {
32
- m as default
40
+ N as default
33
41
  };
@@ -11,4 +11,4 @@ import { RefObject } from 'preact';
11
11
  * @param isOpen - Boolean indicating whether the navigation context is active
12
12
  * @param onClose - Callback to close the navigation UI when `Escape` is pressed
13
13
  */
14
- export declare function useKeyboardNav(containerRef: RefObject<HTMLElement>, isOpen: boolean, onClose: () => void): void;
14
+ export declare function useKeyboardNav(isPagefindLoaded: boolean, containerRef: RefObject<HTMLElement>, isOpen: boolean, onClose: () => void): void;
@@ -1,24 +1,25 @@
1
- import { y as a } from "../../../../chunks/hooks.module.ExpuRnbo.js";
2
- function l(r, t, o) {
3
- a(() => {
4
- const c = (e) => {
5
- if (t) {
1
+ import { y as l } from "../../../../chunks/hooks.module.BFfOesdf.js";
2
+ function s(r, t, o, c) {
3
+ l(() => {
4
+ if (!r) return;
5
+ const u = (e) => {
6
+ if (o) {
6
7
  if (e.key === "Escape") {
7
- o();
8
+ c();
8
9
  return;
9
10
  }
10
11
  if (e.key === "ArrowUp" || e.key === "ArrowDown") {
11
12
  e.preventDefault();
12
- const d = r.current;
13
+ const d = t.current;
13
14
  if (!d) return;
14
- const n = Array.from(d.querySelectorAll("a")), u = n.findIndex((i) => i === document.activeElement), y = e.key === "ArrowDown" ? (u + 1) % n.length : (u - 1 + n.length) % n.length;
15
- n[y].focus();
15
+ const n = Array.from(d.querySelectorAll("a")), y = n.findIndex((i) => i === document.activeElement), f = e.key === "ArrowDown" ? (y + 1) % n.length : (y - 1 + n.length) % n.length;
16
+ n[f].focus();
16
17
  }
17
18
  }
18
19
  };
19
- return document.addEventListener("keydown", c), () => document.removeEventListener("keydown", c);
20
- }, [r, t, o]);
20
+ return document.addEventListener("keydown", u), () => document.removeEventListener("keydown", u);
21
+ }, [r, t, o, c]);
21
22
  }
22
23
  export {
23
- l as useKeyboardNav
24
+ s as useKeyboardNav
24
25
  };
@@ -1,36 +1,43 @@
1
- import { A as h, y as p, d as g } from "../../../../chunks/hooks.module.ExpuRnbo.js";
1
+ import { A as u, y as p, d as w } from "../../../../chunks/hooks.module.BFfOesdf.js";
2
2
  import { getSiteIcon as S } from "../utils/index.js";
3
- function w(s, r) {
4
- const t = h(null), c = h("");
3
+ function T(s, r) {
4
+ const t = u(null), c = u("");
5
5
  return p(() => () => {
6
6
  t.current && clearTimeout(t.current);
7
7
  }, []), { reset: () => {
8
8
  t.current && clearTimeout(t.current);
9
- }, run: (e) => {
10
- c.current = e, t.current = setTimeout(
9
+ }, run: (h) => {
10
+ c.current = h, t.current = setTimeout(
11
11
  () => s(c.current),
12
12
  r
13
13
  );
14
14
  } };
15
15
  }
16
- function y() {
17
- const [s, r] = g({
16
+ function R() {
17
+ const [s, r] = w({
18
18
  searchTerm: "",
19
19
  results: [],
20
20
  isLoading: !1
21
- }), { reset: t, run: c } = w((e) => {
21
+ }), t = u(/* @__PURE__ */ new Map()), { reset: c, run: l } = T((e) => {
22
22
  (e.length > 2 || e.length === 0) && o(e);
23
23
  }, 150), o = async (e) => {
24
24
  if (!(window != null && window.pagefind)) return;
25
- r((n) => ({ ...n, isLoading: !0 }));
26
- const i = await window.pagefind.search(e), d = await Promise.all(
27
- i.results.map(async (n) => {
28
- var u, l, m;
25
+ if (r((n) => ({ ...n, isLoading: !0 })), t.current.has(e)) {
26
+ r((n) => ({
27
+ ...n,
28
+ results: t.current.get(e),
29
+ isLoading: !1
30
+ }));
31
+ return;
32
+ }
33
+ const i = await window.pagefind.search(e), g = await Promise.all(
34
+ i.results.slice(0, 15).map(async (n) => {
35
+ var m, f, d;
29
36
  const a = await n.data();
30
37
  return {
31
- icon: S((u = a.meta) == null ? void 0 : u.content_type),
32
- content_type: (l = a.meta) == null ? void 0 : l.content_type,
33
- label: (m = a.meta) == null ? void 0 : m.title,
38
+ icon: S((m = a.meta) == null ? void 0 : m.content_type),
39
+ content_type: (f = a.meta) == null ? void 0 : f.content_type,
40
+ label: (d = a.meta) == null ? void 0 : d.title,
34
41
  url: a.url,
35
42
  excerpt: a.excerpt
36
43
  };
@@ -38,7 +45,7 @@ function y() {
38
45
  );
39
46
  r((n) => ({
40
47
  ...n,
41
- results: d,
48
+ results: g,
42
49
  isLoading: !1
43
50
  }));
44
51
  };
@@ -46,12 +53,12 @@ function y() {
46
53
  state: s,
47
54
  actions: {
48
55
  setSearchTerm: (e) => {
49
- r((i) => ({ ...i, searchTerm: e })), t(), c(e);
56
+ r((i) => ({ ...i, searchTerm: e })), c(), l(e);
50
57
  },
51
58
  submitSearch: o
52
59
  }
53
60
  };
54
61
  }
55
62
  export {
56
- y as useSearch
63
+ R as useSearch
57
64
  };
@@ -10,4 +10,4 @@
10
10
  * @param onOpen - Callback to open the search interface.
11
11
  * @param onClose - Callback to close the search interface.
12
12
  */
13
- export declare function useSearchHotkeys(isOpen: boolean, onOpen: () => void, onClose: () => void): void;
13
+ export declare function useSearchHotkeys(isPagefindLoaded: boolean, isOpen: boolean, onOpen: () => void, onClose: () => void): void;
@@ -1,12 +1,13 @@
1
- import { y as d } from "../../../../chunks/hooks.module.ExpuRnbo.js";
2
- function m(t, n, o) {
3
- d(() => {
4
- const r = (e) => {
5
- e.metaKey && e.key === "k" && (e.preventDefault(), e.stopImmediatePropagation(), t ? o() : n());
1
+ import { y as u } from "../../../../chunks/hooks.module.BFfOesdf.js";
2
+ function a(t, r, n, o) {
3
+ u(() => {
4
+ if (!t) return;
5
+ const m = (e) => {
6
+ e.metaKey && e.key === "k" && (e.preventDefault(), e.stopImmediatePropagation(), r ? o() : n());
6
7
  };
7
- return document.addEventListener("keydown", r), () => document.removeEventListener("keydown", r);
8
- }, [t, n, o]);
8
+ return document.addEventListener("keydown", m), () => document.removeEventListener("keydown", m);
9
+ }, [t, r, n, o]);
9
10
  }
10
11
  export {
11
- m as useSearchHotkeys
12
+ a as useSearchHotkeys
12
13
  };
@@ -8,4 +8,4 @@
8
8
  * @param setSearchTerm - Function to update the search input state.
9
9
  * @param onOpen - Function to trigger opening of the search interface (e.g., modal or drawer).
10
10
  */
11
- export declare function useSearchParams(setSearchTerm: (term: string) => void, onOpen: () => void): void;
11
+ export declare function useSearchParams(isPagefindLoaded: boolean, setSearchTerm: (term: string) => void, onOpen: () => void): void;
@@ -1,11 +1,11 @@
1
- import { y as e } from "../../../../chunks/hooks.module.ExpuRnbo.js";
2
- function t(a, o) {
3
- e(() => {
4
- if (typeof location > "u") return;
5
- const r = (location == null ? void 0 : location.search) && new URLSearchParams(location.search).get("search");
6
- r && (a(r), o());
7
- }, [a, o]);
1
+ import { y as c } from "../../../../chunks/hooks.module.BFfOesdf.js";
2
+ function n(r, o, a) {
3
+ c(() => {
4
+ if (typeof location > "u" || r) return;
5
+ const e = (location == null ? void 0 : location.search) && new URLSearchParams(location.search).get("search");
6
+ e && (o(e), a());
7
+ }, [r]);
8
8
  }
9
9
  export {
10
- t as useSearchParams
10
+ n as useSearchParams
11
11
  };
@@ -9,4 +9,4 @@
9
9
  *
10
10
  * @param onOpen - A function to be called when a toggle element is clicked.
11
11
  */
12
- export declare function useSearchToggles(onOpen: () => void): void;
12
+ export declare function useSearchToggles(isPagefindLoaded: boolean, onOpen: () => void): void;
@@ -1,15 +1,16 @@
1
- import { y as c } from "../../../../chunks/hooks.module.ExpuRnbo.js";
2
- function l(t) {
3
- c(() => {
4
- const r = document.querySelectorAll("[data-ntl-search-toggle]");
5
- if (r.length)
6
- return r.forEach((e) => {
7
- e.removeAttribute("disabled"), e.addEventListener("click", t);
1
+ import { y as l } from "../../../../chunks/hooks.module.BFfOesdf.js";
2
+ function i(c, r) {
3
+ l(() => {
4
+ if (!c) return;
5
+ const t = document.querySelectorAll("[data-ntl-search-toggle]");
6
+ if (t.length)
7
+ return t.forEach((e) => {
8
+ e.removeAttribute("disabled"), e.addEventListener("click", r);
8
9
  }), () => {
9
- r.forEach((e) => e.removeEventListener("click", t));
10
+ t.forEach((e) => e.removeEventListener("click", r));
10
11
  };
11
- }, [t]);
12
+ }, [c, r]);
12
13
  }
13
14
  export {
14
- l as useSearchToggles
15
+ i as useSearchToggles
15
16
  };
@@ -5,7 +5,7 @@ import { default as s } from "./SearchModal/components/SearchLink/SearchLink.js"
5
5
  import { default as x } from "./SearchModal/components/SearchHeading/SearchHeading.js";
6
6
  import { default as h } from "./SearchModal/components/SearchToggle/SearchToggle.js";
7
7
  import "preact";
8
- import "../../chunks/hooks.module.ExpuRnbo.js";
8
+ import "../../chunks/hooks.module.BFfOesdf.js";
9
9
  import { default as S } from "./SearchModal/components/SearchModal/SearchModal.js";
10
10
  export {
11
11
  m as Icon,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/spark-ui",
3
3
  "description": "Assets, design tokens, components, and utilities",
4
- "version": "0.0.23",
4
+ "version": "0.0.25",
5
5
  "type": "module",
6
6
  "main": "dist/components/preact/index.js",
7
7
  "types": "dist/components/preact/index.d.ts",
@@ -2,11 +2,11 @@ import { type ComponentChildren } from 'preact';
2
2
  import { useState, useEffect, useRef } from 'preact/hooks';
3
3
  import cn from "classnames"
4
4
 
5
- import Modal from '@components/preact/Modal/Modal';
6
- import KeyboardKey from '@components/preact/KeyboardKey/KeyboardKey';
5
+ import Modal from '../../../../preact/Modal/Modal';
6
+ import KeyboardKey from '../../../../preact/KeyboardKey/KeyboardKey';
7
7
  import SearchWindow from '../../components/SearchWindow';
8
8
  import SearchInput from '../../components/SearchInput';
9
- import { useClickOutside } from '@utils/useClickOutside';
9
+ import { useClickOutside } from '../../../../../utils/useClickOutside';
10
10
  import {
11
11
  useSearch,
12
12
  useKeyboardNav,
@@ -23,6 +23,26 @@ interface Props {
23
23
  }
24
24
 
25
25
  function SearchModal(props: Props = {className: '', children: null}) {
26
+ const [isPagefindLoaded, setIsPagefindLoaded] = useState(false);
27
+
28
+ // Poll for the existence of window.pagefind.
29
+ useEffect(() => {
30
+ const checkForPagefind = () => {
31
+ if (window.pagefind) {
32
+ setIsPagefindLoaded(true);
33
+ if (intervalId) clearInterval(intervalId);
34
+ }
35
+ };
36
+
37
+ // Check immediately and then start polling every 100ms.
38
+ checkForPagefind();
39
+ const intervalId = setInterval(checkForPagefind, 100);
40
+
41
+ // Cleanup the interval when the component unmounts.
42
+ return () => clearInterval(intervalId);
43
+ }, []);
44
+
45
+
26
46
  const { children, className, ...rest } = props;
27
47
  const { state, actions } = useSearch();
28
48
  const [searchOpen, setSearchOpen] = useState(false);
@@ -33,10 +53,10 @@ function SearchModal(props: Props = {className: '', children: null}) {
33
53
  const modalRef = useRef<HTMLDialogElement>(null);
34
54
  const containerRef = useClickOutside<HTMLDivElement>(closeSearch);
35
55
 
36
- useSearchToggles(openSearch);
37
- useSearchParams(actions.setSearchTerm, openSearch);
38
- useSearchHotkeys(searchOpen, openSearch, closeSearch);
39
- useKeyboardNav(modalRef, searchOpen, closeSearch);
56
+ useSearchToggles(isPagefindLoaded, openSearch);
57
+ useSearchParams(isPagefindLoaded, actions.setSearchTerm, openSearch);
58
+ useSearchHotkeys(isPagefindLoaded, searchOpen, openSearch, closeSearch);
59
+ useKeyboardNav(isPagefindLoaded, modalRef, searchOpen, closeSearch);
40
60
 
41
61
  useEffect(() => {
42
62
  if (!modalRef.current) return;
@@ -1,5 +1,6 @@
1
1
  import type { FunctionComponent } from 'preact';
2
2
  import type { SearchLinkProps, SearchResultsProps, SearchWindowProps } from '../types/search.types';
3
+ import { useMemo } from 'preact/hooks';
3
4
  import SearchHeading from './SearchHeading/SearchHeading';
4
5
  import SearchLink from './SearchLink/SearchLink';
5
6
 
@@ -25,13 +26,17 @@ const SearchResults: FunctionComponent<SearchResultsProps> = ({
25
26
  );
26
27
  }
27
28
 
29
+ const renderedResults = useMemo(() => {
30
+ return results.map((result, i) => (
31
+ <li key={i} className="n-results-list-item">
32
+ <SearchLink {...result} />
33
+ </li>
34
+ ));
35
+ }, [results]);
36
+
28
37
  return (
29
38
  <ul className="n-results-list" role="list">
30
- {results.slice(0, 15).map((result, idx) => (
31
- <li key={idx} className="n-results-list-item">
32
- <SearchLink {...result} />
33
- </li>
34
- ))}
39
+ {renderedResults}
35
40
  </ul>
36
41
  );
37
42
  };
@@ -15,11 +15,13 @@ import { useEffect } from 'preact/hooks';
15
15
  */
16
16
 
17
17
  export function useKeyboardNav(
18
+ isPagefindLoaded: boolean,
18
19
  containerRef: RefObject<HTMLElement>,
19
20
  isOpen: boolean,
20
21
  onClose: () => void
21
22
  ) {
22
23
  useEffect(() => {
24
+ if (!isPagefindLoaded) return
23
25
  const handleKeydown = (e: KeyboardEvent) => {
24
26
  if (!isOpen) return;
25
27
 
@@ -46,5 +48,5 @@ export function useKeyboardNav(
46
48
 
47
49
  document.addEventListener('keydown', handleKeydown);
48
50
  return () => document.removeEventListener('keydown', handleKeydown);
49
- }, [containerRef, isOpen, onClose]);
51
+ }, [isPagefindLoaded, containerRef, isOpen, onClose]);
50
52
  }
@@ -1,4 +1,4 @@
1
- import { useState } from 'preact/hooks';
1
+ import { useState, useRef } from 'preact/hooks';
2
2
  import type { SearchState } from '../types/search.types';
3
3
  import { useTimeout } from '../../../../utils/useTimeout';
4
4
  import { getSiteIcon } from '../utils';
@@ -19,6 +19,7 @@ export function useSearch() {
19
19
  results: [],
20
20
  isLoading: false,
21
21
  });
22
+ const cacheRef = useRef(new Map());
22
23
 
23
24
  const { reset, run } = useTimeout((query: string) => {
24
25
  if (query.length > 2 || query.length === 0) {
@@ -30,10 +31,18 @@ export function useSearch() {
30
31
  if (!window?.pagefind) return;
31
32
 
32
33
  setSearchState(prev => ({ ...prev, isLoading: true }));
33
-
34
+
35
+ if (cacheRef.current.has(query)) {
36
+ setSearchState(prev => ({
37
+ ...prev,
38
+ results: cacheRef.current.get(query),
39
+ isLoading: false,
40
+ }));
41
+ return;
42
+ }
34
43
  const search = await window.pagefind.search(query);
35
44
  const results = await Promise.all(
36
- search.results.map(async (result: any) => {
45
+ search.results.slice(0, 15).map(async (result: any) => {
37
46
  const data = await result.data();
38
47
  return {
39
48
  icon: getSiteIcon(data.meta?.content_type),
@@ -13,8 +13,10 @@ import { useEffect } from 'preact/hooks';
13
13
  * @param onClose - Callback to close the search interface.
14
14
  */
15
15
 
16
- export function useSearchHotkeys(isOpen: boolean, onOpen: () => void, onClose: () => void) {
16
+ export function useSearchHotkeys(isPagefindLoaded: boolean, isOpen: boolean, onOpen: () => void, onClose: () => void) {
17
17
  useEffect(() => {
18
+ if (!isPagefindLoaded) return;
19
+
18
20
  const handleKeydown = (e: KeyboardEvent) => {
19
21
  if (e.metaKey && e.key === 'k') {
20
22
  e.preventDefault();
@@ -25,5 +27,5 @@ export function useSearchHotkeys(isOpen: boolean, onOpen: () => void, onClose: (
25
27
 
26
28
  document.addEventListener('keydown', handleKeydown);
27
29
  return () => document.removeEventListener('keydown', handleKeydown);
28
- }, [isOpen, onOpen, onClose]);
30
+ }, [isPagefindLoaded, isOpen, onOpen, onClose]);
29
31
  }
@@ -11,13 +11,13 @@ import { useEffect } from 'preact/hooks';
11
11
  * @param onOpen - Function to trigger opening of the search interface (e.g., modal or drawer).
12
12
  */
13
13
 
14
- export function useSearchParams(setSearchTerm: (term: string) => void, onOpen: () => void) {
14
+ export function useSearchParams(isPagefindLoaded: boolean, setSearchTerm: (term: string) => void, onOpen: () => void) {
15
15
  useEffect(() => {
16
- if (typeof location === 'undefined') return;
16
+ if (typeof location === 'undefined' || isPagefindLoaded) return;
17
17
  const query = location?.search && new URLSearchParams(location.search).get('search');
18
18
  if (query) {
19
19
  setSearchTerm(query);
20
20
  onOpen();
21
21
  }
22
- }, [setSearchTerm, onOpen]);
22
+ }, [isPagefindLoaded]);
23
23
  }
@@ -12,8 +12,9 @@ import { useEffect } from 'preact/hooks';
12
12
  * @param onOpen - A function to be called when a toggle element is clicked.
13
13
  */
14
14
 
15
- export function useSearchToggles(onOpen: () => void) {
15
+ export function useSearchToggles(isPagefindLoaded: boolean, onOpen: () => void) {
16
16
  useEffect(() => {
17
+ if (!isPagefindLoaded) return
17
18
  const toggles = document.querySelectorAll('[data-ntl-search-toggle]');
18
19
 
19
20
  if (!toggles.length) return;
@@ -26,5 +27,5 @@ export function useSearchToggles(onOpen: () => void) {
26
27
  return () => {
27
28
  toggles.forEach(t => t.removeEventListener('click', onOpen));
28
29
  };
29
- }, [onOpen]);
30
+ }, [isPagefindLoaded, onOpen]);
30
31
  }