PageSourceSearch

https://smartinnovationnorway.com/wp-content/themes/wp-theme-sin-n…/blocks/audience-wizard/view.js?ver=7.1.2

js smartinnovationnorway.com collected 2026-09-26 00:45:34 UTC 3,639 bytes, 134 lines download raw bytes

1/**
2 * sin/audience-wizard — Interactivity API store.
3 *
4 * State machine: picker → result, with reset.
5 * Search: debounced (250ms) fetch against context.searchUrl
6 * (rest_url('sin/v1/wizard-search')), with AbortController
7 * to drop stale responses while typing fast.
8 *
9 * Note: getContext() only works inside the call stack of a directive
10 * invocation. Async timers must capture ctx via closure outside the
11 * timer callback.
12 */
13import { store, getContext } from '@wordpress/interactivity';
14
15const DEBOUNCE_MS = 250;
16const MIN_QUERY = 2;
17
18let debounceTimer = null;
19let activeAbort = null;
20
21const { state } = store( 'sin/audienceWizard', {
22	state: {
23		// Derived flags read by data-wp-class--is-* directives.
24		get isPicker() {
25			return getContext().step === 'picker';
26		},
27		get isResult() {
28			return getContext().step === 'result';
29		},
30		get isStartup() {
31			const ctx = getContext();
32			return ctx.step === 'result' && ctx.selectedAudience === 'startup';
33		},
34		get isBedrift() {
35			const ctx = getContext();
36			return ctx.step === 'result' && ctx.selectedAudience === 'bedrift';
37		},
38		get isOffentlig() {
39			const ctx = getContext();
40			return ctx.step === 'result' && ctx.selectedAudience === 'offentlig';
41		},
42		get isForskning() {
43			const ctx = getContext();
44			return ctx.step === 'result' && ctx.selectedAudience === 'forskning';
45		},
46		get showEmpty() {
47			const ctx = getContext();
48			return (
49				ctx.showResults &&
50				! ctx.isLoading &&
51				ctx.query.length >= MIN_QUERY &&
52				Array.isArray( ctx.results ) &&
53				ctx.results.length === 0
54			);
55		},
56	},
57
58	actions: {
59		selectAudience: ( event ) => {
60			const audience = event.currentTarget.getAttribute( 'data-audience' );
61			if ( ! audience ) {
62				return;
63			}
64			const ctx = getContext();
65			ctx.selectedAudience = audience;
66			ctx.step = 'result';
67		},
68
69		reset: () => {
70			const ctx = getContext();
71			ctx.selectedAudience = '';
72			ctx.step = 'picker';
73		},
74
75		handleSearchInput: ( event ) => {
76			const ctx = getContext();
77			const value = event.target.value || '';
78			ctx.query = value;
79
80			if ( debounceTimer ) {
81				clearTimeout( debounceTimer );
82			}
83
84			const trimmed = value.trim();
85			if ( trimmed.length < MIN_QUERY ) {
86				ctx.results = [];
87				ctx.showResults = false;
88				ctx.isLoading = false;
89				if ( activeAbort ) {
90					activeAbort.abort();
91					activeAbort = null;
92				}
93				return;
94			}
95
96			// Capture references via closure — getContext() does NOT work
97			// inside the setTimeout/fetch callbacks because the directive
98			// frame has unwound by the time they fire.
99			const capturedCtx = ctx;
100			const searchUrl = ctx.searchUrl;
101
102			debounceTimer = setTimeout( async () => {
103				capturedCtx.isLoading = true;
104				capturedCtx.showResults = true;
105
106				if ( activeAbort ) {
107					activeAbort.abort();
108				}
109				activeAbort = new AbortController();
110				const thisAbort = activeAbort;
111
112				try {
113					const url = `${ searchUrl }?q=${ encodeURIComponent( trimmed ) }`;
114					const response = await fetch( url, { signal: thisAbort.signal } );
115					if ( ! response.ok ) {
116						throw new Error( `HTTP ${ response.status }` );
117					}
118					const data = await response.json();
119					// Only commit if this query is still what's in the box.
120					if ( capturedCtx.query.trim() === trimmed ) {
121						capturedCtx.results = Array.isArray( data.results ) ? data.results : [];
122						capturedCtx.isLoading = false;
123					}
124				} catch ( err ) {
125					if ( err && err.name === 'AbortError' ) {
126						return; // Superseded by a newer request.
127					}
128					capturedCtx.results = [];
129					capturedCtx.isLoading = false;
130				}
131			}, DEBOUNCE_MS );
132		},
133	},
134} );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.