@netlify/spark-ui 1.13.2 → 1.14.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.
- package/dist/chunks/{AskNetlify.CNYCiNSp.js → AskNetlify.hLTCaFLd.js} +222 -213
- package/dist/components/preact/AskNetlify/AskNetlify.js +1 -1
- package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +9 -9
- package/dist/components/preact/SearchModal/hooks/useSearchParams.d.ts +15 -6
- package/dist/components/preact/SearchModal/hooks/useSearchParams.js +19 -13
- package/dist/components/preact/index.js +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/package.json +1 -1
- package/packages/components/ActionArrow.astro +42 -0
- package/packages/components/ActionLink.astro +6 -40
- package/packages/components/Card.astro +60 -3
- package/packages/components/preact/AskNetlify/AskNetlify.tsx +26 -5
- package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +2 -2
- package/packages/components/preact/SearchModal/hooks/useSearchParams.ts +37 -10
- package/packages/utils/index.ts +1 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
|
|
2
2
|
import "../../../chunks/hooks.module.DGkdD5n9.js";
|
|
3
|
-
import { A as d } from "../../../chunks/AskNetlify.
|
|
3
|
+
import { A as d } from "../../../chunks/AskNetlify.hLTCaFLd.js";
|
|
4
4
|
import "./components/QuestionList/questions.js";
|
|
5
5
|
import "./components/QuestionList/QuestionList.js";
|
|
6
6
|
import "./components/BackButton.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { u as e } from "../../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
|
|
2
2
|
import { A as g, y as h, d as k } from "../../../../../chunks/hooks.module.DGkdD5n9.js";
|
|
3
|
-
import { A as w, K as b } from "../../../../../chunks/AskNetlify.
|
|
3
|
+
import { A as w, K as b } from "../../../../../chunks/AskNetlify.hLTCaFLd.js";
|
|
4
4
|
import K from "../../../Modal/Modal.js";
|
|
5
5
|
import d from "../../../KeyboardKey/KeyboardKey.js";
|
|
6
6
|
import L from "../SearchWindow.js";
|
|
@@ -26,8 +26,8 @@ import '../../../../../assets/SearchModal.css';function D(t) {
|
|
|
26
26
|
}, [t]), n;
|
|
27
27
|
}
|
|
28
28
|
function re(t = { className: "", children: void 0, debug: !1, kapaIntegrationId: void 0 }) {
|
|
29
|
-
const { children: n, className: o, debug: m, ...T } = t, [
|
|
30
|
-
q(l,
|
|
29
|
+
const { children: n, className: o, debug: m, ...T } = t, [c, s] = k({ current: "search" }), [i, f] = k(!1), l = () => f(!0), u = () => f(!1), a = g(null), v = D(u), { state: r, actions: p } = C();
|
|
30
|
+
q(l, s), R(l, p.setSearchTerm, s), P(i, l, u), Q(a, i, u), E(r, m);
|
|
31
31
|
const { handleNewQuery: A, handleAnswer: I } = x();
|
|
32
32
|
return h(() => {
|
|
33
33
|
a.current && (i ? a.current.showModal() : a.current.close());
|
|
@@ -35,7 +35,7 @@ function re(t = { className: "", children: void 0, debug: !1, kapaIntegrationId:
|
|
|
35
35
|
var y;
|
|
36
36
|
const S = (y = a.current) == null ? void 0 : y.querySelector("input");
|
|
37
37
|
S && S.focus();
|
|
38
|
-
}, [
|
|
38
|
+
}, [c.current]), /* @__PURE__ */ e(
|
|
39
39
|
b,
|
|
40
40
|
{
|
|
41
41
|
integrationId: t.kapaIntegrationId || "",
|
|
@@ -50,21 +50,21 @@ function re(t = { className: "", children: void 0, debug: !1, kapaIntegrationId:
|
|
|
50
50
|
{
|
|
51
51
|
ref: a,
|
|
52
52
|
className: `n-search-modal ${o || ""}`,
|
|
53
|
-
"data-state":
|
|
53
|
+
"data-state": c.current,
|
|
54
54
|
...T,
|
|
55
55
|
children: /* @__PURE__ */ e("div", { ref: v, className: "n-search-container", children: [
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
c.current === "ask" && /* @__PURE__ */ e(w, { setModalState: s, initialQuery: r.searchTerm }),
|
|
57
|
+
c.current === "search" && /* @__PURE__ */ e(N, { children: [
|
|
58
58
|
/* @__PURE__ */ e(M, { setSearchTerm: p.setSearchTerm, initialValue: r.searchTerm }),
|
|
59
59
|
/* @__PURE__ */ e(
|
|
60
60
|
L,
|
|
61
61
|
{
|
|
62
|
-
setModalState:
|
|
62
|
+
setModalState: s,
|
|
63
63
|
searchLoading: r.isLoading,
|
|
64
64
|
searchTerm: r.searchTerm,
|
|
65
65
|
results: r.results,
|
|
66
66
|
children: /* @__PURE__ */ e(N, { children: [
|
|
67
|
-
t.kapaIntegrationId && /* @__PURE__ */ e(O, { searchTerm: r.searchTerm, setModalState:
|
|
67
|
+
t.kapaIntegrationId && /* @__PURE__ */ e(O, { searchTerm: r.searchTerm, setModalState: s }),
|
|
68
68
|
n
|
|
69
69
|
] })
|
|
70
70
|
}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Custom hook that checks the current URL for
|
|
3
|
-
*
|
|
2
|
+
* Custom hook that checks the current URL for query parameters and updates
|
|
3
|
+
* search state or modal state accordingly:
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* - If `?ask` is present (optionally with `?msg` or `?message`), it opens the modal and
|
|
6
|
+
* optionally pre-fills the search term.
|
|
7
|
+
* - If only `?msg` or `?message` is present, it behaves similarly, opening the modal
|
|
8
|
+
* and pre-filling the search term.
|
|
9
|
+
* - If only `?search` is present, it pre-fills the search input and opens the search view.
|
|
10
|
+
*
|
|
11
|
+
* The hook also waits for `window.pagefind` to load before evaluating parameters,
|
|
12
|
+
* ensuring deep linking doesn't interfere with pagefind initialization.
|
|
7
13
|
*
|
|
8
|
-
* @param setSearchTerm - Function to update the search input state.
|
|
9
14
|
* @param onOpen - Function to trigger opening of the search interface (e.g., modal or drawer).
|
|
15
|
+
* @param setSearchTerm - Function to update the search input state.
|
|
16
|
+
* @param setModalState - (Optional) Function to set modal state, useful for distinguishing modal views.
|
|
10
17
|
*/
|
|
11
|
-
export declare function useSearchParams(
|
|
18
|
+
export declare function useSearchParams(onOpen: () => void, setSearchTerm: (term: string) => void, setModalState?: (e: {
|
|
19
|
+
current: string;
|
|
20
|
+
}) => void): void;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
|
-
import { d as
|
|
2
|
-
function
|
|
3
|
-
const [
|
|
4
|
-
|
|
1
|
+
import { d as u, y as t } from "../../../../chunks/hooks.module.DGkdD5n9.js";
|
|
2
|
+
function l(s, a, r) {
|
|
3
|
+
const [c, d] = u(!1);
|
|
4
|
+
t(() => {
|
|
5
5
|
let e;
|
|
6
|
-
const
|
|
7
|
-
window.pagefind && (
|
|
6
|
+
const n = () => {
|
|
7
|
+
window.pagefind && (d(!0), e && clearInterval(e));
|
|
8
8
|
};
|
|
9
|
-
return
|
|
10
|
-
}, []),
|
|
11
|
-
if (typeof location > "u" ||
|
|
12
|
-
const e = (location
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
return n(), e = setInterval(n, 100), () => clearInterval(e);
|
|
10
|
+
}, []), t(() => {
|
|
11
|
+
if (typeof location > "u" || c) return;
|
|
12
|
+
const e = new URLSearchParams(location.search), n = e.has("ask"), i = e.get("msg") || e.get("message"), o = e.get("search"), g = (f) => {
|
|
13
|
+
f && a(f), r == null || r({ current: "ask" }), s();
|
|
14
|
+
};
|
|
15
|
+
if (n || i) {
|
|
16
|
+
g(i);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
o && (a(o), s());
|
|
20
|
+
}, [c]);
|
|
15
21
|
}
|
|
16
22
|
export {
|
|
17
|
-
|
|
23
|
+
l as useSearchParams
|
|
18
24
|
};
|
|
@@ -4,7 +4,7 @@ import { default as m } from "./Icon/index.js";
|
|
|
4
4
|
import { default as s } from "./SearchModal/components/SearchLink/SearchLink.js";
|
|
5
5
|
import { default as u } from "./SearchModal/components/SearchHeading/SearchHeading.js";
|
|
6
6
|
import { default as i } from "./SearchModal/components/SearchToggle/SearchToggle.js";
|
|
7
|
-
import { A as S } from "../../chunks/AskNetlify.
|
|
7
|
+
import { A as S } from "../../chunks/AskNetlify.hLTCaFLd.js";
|
|
8
8
|
import "preact";
|
|
9
9
|
import "../../chunks/hooks.module.DGkdD5n9.js";
|
|
10
10
|
import { default as n } from "./SearchModal/components/SearchModal/SearchModal.js";
|
package/dist/utils/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<div class="n-action-arrow">
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 14 14">
|
|
3
|
+
<g stroke="currentColor" stroke-linecap="square" stroke-width="1.564" clip-path="url(#a)">
|
|
4
|
+
<path stroke-linejoin="round" d="M3.417 10.556 9.4 4.572"></path>
|
|
5
|
+
<path d="M9.88 9.381V4.093H4.59"></path>
|
|
6
|
+
</g>
|
|
7
|
+
</svg>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<style>
|
|
11
|
+
.n-action-arrow {
|
|
12
|
+
display: inline-flex;
|
|
13
|
+
overflow: hidden;
|
|
14
|
+
|
|
15
|
+
svg {
|
|
16
|
+
color: var(--color-icon);
|
|
17
|
+
transition: translate 0.1s ease-out;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@media (any-hover: hover) {
|
|
22
|
+
:global(
|
|
23
|
+
:is(.n-card-clickable, .n-action-link):where(:hover, :focus-visible) .n-action-arrow svg
|
|
24
|
+
) {
|
|
25
|
+
--_offset: 100%;
|
|
26
|
+
animation: n-action-link-hover 350ms cubic-bezier(0.85, 0, 0.15, 1) forwards;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@keyframes n-action-link-hover {
|
|
30
|
+
0%,
|
|
31
|
+
100% {
|
|
32
|
+
translate: 0 0;
|
|
33
|
+
}
|
|
34
|
+
30% {
|
|
35
|
+
translate: calc(var(--_offset) * 2) calc(var(--_offset) * -2);
|
|
36
|
+
}
|
|
37
|
+
31% {
|
|
38
|
+
translate: calc(var(--_offset) * -1) var(--_offset);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import ActionArrow from './ActionArrow.astro';
|
|
3
|
+
|
|
2
4
|
interface Props {
|
|
3
5
|
id?: string;
|
|
4
6
|
href: string;
|
|
@@ -10,19 +12,14 @@ const { id, href, text, class: className, ...rest } = Astro.props;
|
|
|
10
12
|
---
|
|
11
13
|
|
|
12
14
|
<a id={id} class:list={['n-action-link', className]} href={href} {...rest}>
|
|
13
|
-
<span>{text ? text : <slot />}</span><
|
|
14
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="none" viewBox="0 0 14 14">
|
|
15
|
-
<g stroke="currentColor" stroke-linecap="square" stroke-width="1.564" clip-path="url(#a)">
|
|
16
|
-
<path stroke-linejoin="round" d="M3.417 10.556 9.4 4.572"></path>
|
|
17
|
-
<path d="M9.88 9.381V4.093H4.59"></path>
|
|
18
|
-
</g>
|
|
19
|
-
</svg>
|
|
20
|
-
</div>
|
|
15
|
+
<span>{text ? text : <slot />}</span><ActionArrow />
|
|
21
16
|
</a>
|
|
22
17
|
|
|
23
18
|
<style>
|
|
24
19
|
.n-action-link {
|
|
25
|
-
display: inline-
|
|
20
|
+
display: inline-flex;
|
|
21
|
+
gap: var(--space-3xs);
|
|
22
|
+
align-items: center;
|
|
26
23
|
font-weight: var(--font-weight-medium);
|
|
27
24
|
font-size: var(--font-size-text-md);
|
|
28
25
|
text-wrap: pretty; /** Prevents orphan icon */
|
|
@@ -35,35 +32,4 @@ const { id, href, text, class: className, ...rest } = Astro.props;
|
|
|
35
32
|
text-underline-offset: 0.1em;
|
|
36
33
|
}
|
|
37
34
|
}
|
|
38
|
-
|
|
39
|
-
.n-action-link-icon {
|
|
40
|
-
display: inline-flex;
|
|
41
|
-
overflow: hidden;
|
|
42
|
-
margin-inline-start: 0.1em;
|
|
43
|
-
|
|
44
|
-
svg {
|
|
45
|
-
color: var(--color-icon);
|
|
46
|
-
transition: translate 0.1s ease-out;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
@media (any-hover: hover) {
|
|
51
|
-
.n-action-link:where(:hover, :focus-visible) .n-action-link-icon svg {
|
|
52
|
-
--_offset: 100%;
|
|
53
|
-
animation: n-action-link-hover 350ms cubic-bezier(0.85, 0, 0.15, 1) forwards;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
@keyframes n-action-link-hover {
|
|
57
|
-
0%,
|
|
58
|
-
100% {
|
|
59
|
-
translate: 0 0;
|
|
60
|
-
}
|
|
61
|
-
30% {
|
|
62
|
-
translate: calc(var(--_offset) * 2) calc(var(--_offset) * -2);
|
|
63
|
-
}
|
|
64
|
-
31% {
|
|
65
|
-
translate: calc(var(--_offset) * -1) var(--_offset);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
35
|
</style>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
+
import ActionArrow from './ActionArrow.astro';
|
|
2
3
|
import ActionLink from './ActionLink.astro';
|
|
3
4
|
import Eyebrow from './Eyebrow.astro';
|
|
4
5
|
|
|
@@ -25,9 +26,19 @@ const {
|
|
|
25
26
|
class: className,
|
|
26
27
|
...rest
|
|
27
28
|
} = Astro.props;
|
|
29
|
+
|
|
30
|
+
const isAnchorHeadline = isCardClickable && headline && !link?.text && link?.href;
|
|
28
31
|
---
|
|
29
32
|
|
|
30
|
-
<Element
|
|
33
|
+
<Element
|
|
34
|
+
class:list={[
|
|
35
|
+
'n-card n-flow',
|
|
36
|
+
isCardClickable && 'n-card-clickable',
|
|
37
|
+
isAnchorHeadline && 'n-card-anchor-heaadline',
|
|
38
|
+
className,
|
|
39
|
+
]}
|
|
40
|
+
{...rest}
|
|
41
|
+
>
|
|
31
42
|
{
|
|
32
43
|
Astro.slots.has('icon') && (
|
|
33
44
|
<div class="n-card-icon">
|
|
@@ -43,16 +54,25 @@ const {
|
|
|
43
54
|
)
|
|
44
55
|
}
|
|
45
56
|
{eyebrow && <Eyebrow class="n-card-eyebrow">{eyebrow}</Eyebrow>}
|
|
46
|
-
{
|
|
57
|
+
{
|
|
58
|
+
isAnchorHeadline ? (
|
|
59
|
+
<a class="n-card-link n-card-link-headline" href={link?.href} id={link.id}>
|
|
60
|
+
<div class="n-card-headline" set:html={headline} />
|
|
61
|
+
</a>
|
|
62
|
+
) : (
|
|
63
|
+
<div class="n-card-headline" set:html={headline} />
|
|
64
|
+
)
|
|
65
|
+
}
|
|
47
66
|
{paragraph && <div class="n-card-paragraph" set:html={paragraph} />}
|
|
48
67
|
<slot />
|
|
49
68
|
{
|
|
50
|
-
link && (
|
|
69
|
+
link && link.text && (
|
|
51
70
|
<ActionLink class="n-card-link" id={link.id} href={link.href}>
|
|
52
71
|
{link.text}
|
|
53
72
|
</ActionLink>
|
|
54
73
|
)
|
|
55
74
|
}
|
|
75
|
+
{isAnchorHeadline && <ActionArrow />}
|
|
56
76
|
</Element>
|
|
57
77
|
|
|
58
78
|
<style>
|
|
@@ -72,6 +92,10 @@ const {
|
|
|
72
92
|
--n-flow-space: var(--space-s);
|
|
73
93
|
}
|
|
74
94
|
|
|
95
|
+
.n-card-link:has(> .n-card-headline) {
|
|
96
|
+
text-decoration: none;
|
|
97
|
+
}
|
|
98
|
+
|
|
75
99
|
.n-card-headline {
|
|
76
100
|
font-size: var(--font-size-display-sm);
|
|
77
101
|
font-family: var(--font-family-display);
|
|
@@ -80,6 +104,29 @@ const {
|
|
|
80
104
|
color: var(--heading-text-color);
|
|
81
105
|
}
|
|
82
106
|
|
|
107
|
+
.n-card-headline:has(+ .n-action-link) {
|
|
108
|
+
margin-block-end: auto;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.n-card-link-headline {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
justify-content: space-between;
|
|
115
|
+
gap: var(--space-s);
|
|
116
|
+
width: 100%;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.n-card-link-headline .n-card-headline {
|
|
120
|
+
flex: 1 1 auto;
|
|
121
|
+
min-width: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.n-card :global(.n-action-arrow) {
|
|
125
|
+
flex: 0 0 auto;
|
|
126
|
+
align-self: center;
|
|
127
|
+
margin-inline-start: auto;
|
|
128
|
+
}
|
|
129
|
+
|
|
83
130
|
.n-card-paragraph {
|
|
84
131
|
line-height: var(--line-height-text);
|
|
85
132
|
}
|
|
@@ -119,4 +166,14 @@ const {
|
|
|
119
166
|
inset: 0;
|
|
120
167
|
}
|
|
121
168
|
}
|
|
169
|
+
|
|
170
|
+
/* Focus for Anchor Headline */
|
|
171
|
+
.n-card-anchor-heaadline:focus-within {
|
|
172
|
+
box-shadow: 0 0 0 3px var(--color-focus-ring, Highlight);
|
|
173
|
+
border-radius: var(--radius-s, 0.25rem);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.n-card-anchor-heaadline a:focus {
|
|
177
|
+
outline: none;
|
|
178
|
+
}
|
|
122
179
|
</style>
|
|
@@ -19,6 +19,7 @@ export default function AskNetlify({ setModalState, initialQuery }: Props) {
|
|
|
19
19
|
const [newMessageReady, setNewMessageReady] = useState(false);
|
|
20
20
|
const chatRef = useRef<HTMLDivElement>(null);
|
|
21
21
|
const menuRef = useRef<HTMLUListElement>(null);
|
|
22
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
22
23
|
const lastMessageRef = useRef<HTMLDivElement | null>(null);
|
|
23
24
|
|
|
24
25
|
const { conversation, submitQuery, isGeneratingAnswer, isPreparingAnswer, stopGeneration } =
|
|
@@ -50,19 +51,38 @@ export default function AskNetlify({ setModalState, initialQuery }: Props) {
|
|
|
50
51
|
}, [newMessageReady]);
|
|
51
52
|
|
|
52
53
|
useEffect(() => {
|
|
53
|
-
if (initialQuery)
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
if (!initialQuery) return;
|
|
55
|
+
|
|
56
|
+
const trimmedQuery = initialQuery.trim();
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* When reCAPTCHA is ready, we know this was triggered by a user action
|
|
60
|
+
* (like clicking a button), so we can safely submit the query right away.
|
|
61
|
+
* If reCAPTCHA isn't ready, the modal was likely opened from an outside source,
|
|
62
|
+
* in that case, we populate the input and focus it, requiring manual submission
|
|
63
|
+
* to ensure a user action triggers reCAPTCHA.
|
|
64
|
+
*/
|
|
65
|
+
if (window.grecaptcha) {
|
|
66
|
+
window.grecaptcha.enterprise.ready(() => {
|
|
67
|
+
submitQuery(trimmedQuery);
|
|
68
|
+
});
|
|
69
|
+
} else {
|
|
70
|
+
setMessage(trimmedQuery);
|
|
71
|
+
inputRef?.current?.focus();
|
|
56
72
|
}
|
|
73
|
+
|
|
74
|
+
setNewMessageReady(true);
|
|
57
75
|
}, [initialQuery]);
|
|
58
76
|
|
|
59
77
|
return (
|
|
60
78
|
<div className="n-ask-chat-container">
|
|
61
79
|
<BackButton onClick={() => setModalState?.({ current: 'search' })} />
|
|
62
80
|
<div ref={chatRef} className="n-ask-conversation n-search-window">
|
|
63
|
-
{!conversation.length
|
|
81
|
+
{!conversation.length && !initialQuery && (
|
|
64
82
|
<QuestionList onClick={handleSubmit} menuRef={menuRef} />
|
|
65
|
-
)
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
{conversation.length > 0 && (
|
|
66
86
|
<ChatMessages
|
|
67
87
|
isPreparingAnswer={isPreparingAnswer}
|
|
68
88
|
conversation={conversation}
|
|
@@ -73,6 +93,7 @@ export default function AskNetlify({ setModalState, initialQuery }: Props) {
|
|
|
73
93
|
|
|
74
94
|
<form onSubmit={handleSubmit} className="n-ask-form n-search-input-wrapper">
|
|
75
95
|
<input
|
|
96
|
+
ref={inputRef}
|
|
76
97
|
className="n-ask-input n-search-input"
|
|
77
98
|
type="text"
|
|
78
99
|
value={message}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ComponentChildren } from 'preact';
|
|
2
|
-
import { useState, useEffect, useRef } from 'preact/hooks';
|
|
2
|
+
import { useState, useEffect, useRef, useMemo } from 'preact/hooks';
|
|
3
3
|
|
|
4
4
|
import { KapaProvider } from '@kapaai/react-sdk';
|
|
5
5
|
|
|
@@ -45,7 +45,7 @@ function SearchModal(
|
|
|
45
45
|
|
|
46
46
|
const { state, actions } = useSearch();
|
|
47
47
|
useSearchToggles(openSearch, setModalState);
|
|
48
|
-
useSearchParams(actions.setSearchTerm,
|
|
48
|
+
useSearchParams(openSearch, actions.setSearchTerm, setModalState);
|
|
49
49
|
useSearchHotkeys(searchOpen, openSearch, closeSearch);
|
|
50
50
|
useKeyboardNav(modalRef, searchOpen, closeSearch);
|
|
51
51
|
useSearchAnalytics(state, debug);
|
|
@@ -1,17 +1,28 @@
|
|
|
1
1
|
import { useEffect, useState } from 'preact/hooks';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Custom hook that checks the current URL for
|
|
5
|
-
*
|
|
4
|
+
* Custom hook that checks the current URL for query parameters and updates
|
|
5
|
+
* search state or modal state accordingly:
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* - If `?ask` is present (optionally with `?msg` or `?message`), it opens the modal and
|
|
8
|
+
* optionally pre-fills the search term.
|
|
9
|
+
* - If only `?msg` or `?message` is present, it behaves similarly, opening the modal
|
|
10
|
+
* and pre-filling the search term.
|
|
11
|
+
* - If only `?search` is present, it pre-fills the search input and opens the search view.
|
|
12
|
+
*
|
|
13
|
+
* The hook also waits for `window.pagefind` to load before evaluating parameters,
|
|
14
|
+
* ensuring deep linking doesn't interfere with pagefind initialization.
|
|
9
15
|
*
|
|
10
|
-
* @param setSearchTerm - Function to update the search input state.
|
|
11
16
|
* @param onOpen - Function to trigger opening of the search interface (e.g., modal or drawer).
|
|
17
|
+
* @param setSearchTerm - Function to update the search input state.
|
|
18
|
+
* @param setModalState - (Optional) Function to set modal state, useful for distinguishing modal views.
|
|
12
19
|
*/
|
|
13
20
|
|
|
14
|
-
export function useSearchParams(
|
|
21
|
+
export function useSearchParams(
|
|
22
|
+
onOpen: () => void,
|
|
23
|
+
setSearchTerm: (term: string) => void,
|
|
24
|
+
setModalState?: (e: { current: string }) => void
|
|
25
|
+
) {
|
|
15
26
|
const [isPagefindLoaded, setIsPagefindLoaded] = useState(false);
|
|
16
27
|
|
|
17
28
|
// Poll for the existence of window.pagefind.
|
|
@@ -34,11 +45,27 @@ export function useSearchParams(setSearchTerm: (term: string) => void, onOpen: (
|
|
|
34
45
|
}, []);
|
|
35
46
|
|
|
36
47
|
useEffect(() => {
|
|
37
|
-
// Cleanup the interval when the component unmounts.
|
|
38
48
|
if (typeof location === 'undefined' || isPagefindLoaded) return;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
|
|
50
|
+
const params = new URLSearchParams(location.search);
|
|
51
|
+
|
|
52
|
+
const hasAsk = params.has('ask');
|
|
53
|
+
const msg = params.get('msg') || params.get('message');
|
|
54
|
+
const search = params.get('search');
|
|
55
|
+
|
|
56
|
+
const openAskModal = (term?: string | null) => {
|
|
57
|
+
if (term) setSearchTerm(term);
|
|
58
|
+
setModalState?.({ current: 'ask' });
|
|
59
|
+
onOpen();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (hasAsk || msg) {
|
|
63
|
+
openAskModal(msg);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (search) {
|
|
68
|
+
setSearchTerm(search);
|
|
42
69
|
onOpen();
|
|
43
70
|
}
|
|
44
71
|
}, [isPagefindLoaded]);
|
package/packages/utils/index.ts
CHANGED