@canopy-iiif/app 0.8.2 → 0.8.4
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/lib/AGENTS.md +1 -1
- package/lib/build/build.js +10 -0
- package/lib/build/dev.js +87 -40
- package/lib/build/iiif.js +124 -43
- package/lib/build/mdx.js +14 -4
- package/lib/build/pages.js +23 -9
- package/lib/build/runtimes.js +6 -6
- package/lib/build/search.js +7 -10
- package/lib/build/verify.js +9 -9
- package/lib/components/featured.js +17 -3
- package/lib/components/navigation.js +308 -0
- package/lib/page-context.js +14 -0
- package/lib/search/search-app.jsx +2 -2
- package/lib/search/{command-runtime.js → search-form-runtime.js} +9 -9
- package/lib/search/search.js +2 -2
- package/package.json +1 -1
- package/ui/dist/index.mjs +76 -63
- package/ui/dist/index.mjs.map +3 -3
- package/ui/dist/server.mjs +170 -67
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/base/_common.scss +19 -6
- package/ui/styles/base/_heading.scss +17 -0
- package/ui/styles/base/index.scss +2 -1
- package/ui/styles/components/_sub-navigation.scss +76 -0
- package/ui/styles/components/header/_header.scss +13 -0
- package/ui/styles/components/header/_logo.scss +20 -0
- package/ui/styles/components/header/_navbar.scss +15 -0
- package/ui/styles/components/header/index.scss +3 -0
- package/ui/styles/components/index.scss +5 -4
- package/ui/styles/components/search/_filters.scss +265 -0
- package/ui/styles/components/search/_form.scss +171 -0
- package/ui/styles/components/search/_results.scss +158 -0
- package/ui/styles/components/search/index.scss +3 -0
- package/ui/styles/index.css +584 -71
- package/ui/styles/variables.scss +15 -5
- package/ui/styles/components/_command.scss +0 -164
- package/ui/styles/components/_header.scss +0 -0
package/lib/build/verify.js
CHANGED
|
@@ -21,10 +21,10 @@ function verifyHomepageElements(outDir) {
|
|
|
21
21
|
const idx = path.join(outDir, 'index.html');
|
|
22
22
|
const html = readFileSafe(idx);
|
|
23
23
|
const okHero = /class=\"[^\"]*canopy-hero/.test(html) || /<div[^>]+canopy-hero/.test(html);
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
return { okHero,
|
|
24
|
+
const okSearchForm = /data-canopy-search-form=/.test(html);
|
|
25
|
+
const okSearchFormTrigger = /data-canopy-search-form-trigger/.test(html);
|
|
26
|
+
const okSearchFormScriptRef = /<script[^>]+canopy-search-form\.js/.test(html);
|
|
27
|
+
return { okHero, okSearchForm, okSearchFormTrigger, okSearchFormScriptRef, htmlPath: idx };
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
function verifyBuildOutput(options = {}) {
|
|
@@ -34,7 +34,7 @@ function verifyBuildOutput(options = {}) {
|
|
|
34
34
|
const okAny = total > 0;
|
|
35
35
|
const indexPath = path.join(outDir, 'index.html');
|
|
36
36
|
const hasIndex = fs.existsSync(indexPath) && fs.statSync(indexPath).size > 0;
|
|
37
|
-
const { okHero,
|
|
37
|
+
const { okHero, okSearchForm, okSearchFormTrigger, okSearchFormScriptRef } = verifyHomepageElements(outDir);
|
|
38
38
|
|
|
39
39
|
const ck = (label, ok, extra) => {
|
|
40
40
|
const status = ok ? '✓' : '✗';
|
|
@@ -44,12 +44,12 @@ function verifyBuildOutput(options = {}) {
|
|
|
44
44
|
ck('HTML pages exist', okAny, okAny ? `(${total})` : '');
|
|
45
45
|
ck('homepage exists', hasIndex, hasIndex ? `(${indexPath})` : '');
|
|
46
46
|
ck('homepage: Hero present', okHero);
|
|
47
|
-
ck('homepage:
|
|
48
|
-
ck('homepage:
|
|
49
|
-
ck('homepage:
|
|
47
|
+
ck('homepage: Search form present', okSearchForm);
|
|
48
|
+
ck('homepage: Search form trigger present', okSearchFormTrigger);
|
|
49
|
+
ck('homepage: Search form script referenced', okSearchFormScriptRef);
|
|
50
50
|
|
|
51
51
|
// Do not fail build on missing SSR trigger; the client runtime injects a default.
|
|
52
|
-
const ok = okAny && hasIndex && okHero &&
|
|
52
|
+
const ok = okAny && hasIndex && okHero && okSearchForm && okSearchFormScriptRef;
|
|
53
53
|
if (!ok) {
|
|
54
54
|
const err = new Error('Build verification failed');
|
|
55
55
|
err.outDir = outDir;
|
|
@@ -105,9 +105,23 @@ function readFeaturedFromCacheSync() {
|
|
|
105
105
|
href: rootRelativeHref(path.join('works', slug + '.html').split(path.sep).join('/')),
|
|
106
106
|
type: 'work',
|
|
107
107
|
};
|
|
108
|
-
if (entry && entry.
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
if (entry && entry.heroThumbnail) {
|
|
109
|
+
rec.thumbnail = String(entry.heroThumbnail);
|
|
110
|
+
if (typeof entry.heroThumbnailWidth === 'number') {
|
|
111
|
+
rec.thumbnailWidth = entry.heroThumbnailWidth;
|
|
112
|
+
} else if (typeof entry.thumbnailWidth === 'number') {
|
|
113
|
+
rec.thumbnailWidth = entry.thumbnailWidth;
|
|
114
|
+
}
|
|
115
|
+
if (typeof entry.heroThumbnailHeight === 'number') {
|
|
116
|
+
rec.thumbnailHeight = entry.heroThumbnailHeight;
|
|
117
|
+
} else if (typeof entry.thumbnailHeight === 'number') {
|
|
118
|
+
rec.thumbnailHeight = entry.thumbnailHeight;
|
|
119
|
+
}
|
|
120
|
+
} else {
|
|
121
|
+
if (entry && entry.thumbnail) rec.thumbnail = String(entry.thumbnail);
|
|
122
|
+
if (entry && typeof entry.thumbnailWidth === 'number') rec.thumbnailWidth = entry.thumbnailWidth;
|
|
123
|
+
if (entry && typeof entry.thumbnailHeight === 'number') rec.thumbnailHeight = entry.thumbnailHeight;
|
|
124
|
+
}
|
|
111
125
|
if (!rec.thumbnail) {
|
|
112
126
|
try {
|
|
113
127
|
const t = m && m.thumbnail;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
const {fs, path, CONTENT_DIR, rootRelativeHref} = require("../common");
|
|
2
|
+
const mdx = require("../build/mdx.js");
|
|
3
|
+
const {getPageContext} = require("../page-context");
|
|
4
|
+
|
|
5
|
+
const EXCLUDED_ROOTS = new Set(["works", "search"]);
|
|
6
|
+
|
|
7
|
+
let NAV_CACHE = null;
|
|
8
|
+
|
|
9
|
+
function normalizeRelativePath(rel) {
|
|
10
|
+
if (!rel) return "";
|
|
11
|
+
let normalized = String(rel).replace(/\\+/g, "/");
|
|
12
|
+
while (normalized.startsWith("./")) normalized = normalized.slice(2);
|
|
13
|
+
while (normalized.startsWith("../")) normalized = normalized.slice(3);
|
|
14
|
+
if (normalized.startsWith("/")) {
|
|
15
|
+
normalized = normalized.replace(/^\/+/, "");
|
|
16
|
+
}
|
|
17
|
+
return normalized;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function humanizeSegment(seg) {
|
|
21
|
+
if (!seg) return "";
|
|
22
|
+
const cleaned = String(seg).replace(/[-_]+/g, " ");
|
|
23
|
+
return cleaned.replace(/(^|\s)([a-z])/g, (match) => match.toUpperCase());
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function slugFromRelative(relativePath) {
|
|
27
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
28
|
+
if (!normalized) {
|
|
29
|
+
return {slug: "", segments: [], isIndex: false};
|
|
30
|
+
}
|
|
31
|
+
const parts = normalized.split("/");
|
|
32
|
+
const fileName = parts.pop() || "";
|
|
33
|
+
const baseName = fileName.replace(/\.mdx$/i, "");
|
|
34
|
+
const isIndex = baseName.toLowerCase() === "index";
|
|
35
|
+
const dirSegments = parts.filter(Boolean);
|
|
36
|
+
const segments = isIndex ? dirSegments : dirSegments.concat(baseName);
|
|
37
|
+
const slug = segments.join("/");
|
|
38
|
+
return {slug, segments, isIndex};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function pageSortKey(relativePath) {
|
|
42
|
+
const normalized = normalizeRelativePath(relativePath).toLowerCase();
|
|
43
|
+
if (!normalized) return "";
|
|
44
|
+
return normalized.replace(/(^|\/)index\.mdx$/i, "$1-index.mdx");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function extractTitleSafe(raw) {
|
|
48
|
+
try {
|
|
49
|
+
return mdx.extractTitle(raw);
|
|
50
|
+
} catch (_) {
|
|
51
|
+
return "Untitled";
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function collectPagesSync() {
|
|
56
|
+
const pages = [];
|
|
57
|
+
|
|
58
|
+
function walk(dir) {
|
|
59
|
+
let entries = [];
|
|
60
|
+
try {
|
|
61
|
+
entries = fs.readdirSync(dir, {withFileTypes: true});
|
|
62
|
+
} catch (_) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
if (!entry) continue;
|
|
67
|
+
const name = entry.name || "";
|
|
68
|
+
if (!name) continue;
|
|
69
|
+
if (name.startsWith(".")) continue;
|
|
70
|
+
const absPath = path.join(dir, name);
|
|
71
|
+
const relPath = path.relative(CONTENT_DIR, absPath);
|
|
72
|
+
const normalizedRel = normalizeRelativePath(relPath);
|
|
73
|
+
if (!normalizedRel) continue;
|
|
74
|
+
const segments = normalizedRel.split("/");
|
|
75
|
+
const firstRaw = segments[0] || "";
|
|
76
|
+
const firstSegment = firstRaw.replace(/\.mdx$/i, "");
|
|
77
|
+
if (EXCLUDED_ROOTS.has(firstSegment)) continue;
|
|
78
|
+
if (segments.some((segment) => segment.startsWith("_"))) continue;
|
|
79
|
+
if (entry.isDirectory()) {
|
|
80
|
+
walk(absPath);
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (!entry.isFile()) continue;
|
|
84
|
+
if (!/\.mdx$/i.test(name)) continue;
|
|
85
|
+
let raw = "";
|
|
86
|
+
try {
|
|
87
|
+
raw = fs.readFileSync(absPath, "utf8");
|
|
88
|
+
} catch (_) {
|
|
89
|
+
raw = "";
|
|
90
|
+
}
|
|
91
|
+
const {
|
|
92
|
+
slug,
|
|
93
|
+
segments: slugSegments,
|
|
94
|
+
isIndex,
|
|
95
|
+
} = slugFromRelative(normalizedRel);
|
|
96
|
+
const titleRaw = extractTitleSafe(raw);
|
|
97
|
+
const fallbackTitle = humanizeSegment(
|
|
98
|
+
slugSegments.slice(-1)[0] || firstSegment || ""
|
|
99
|
+
);
|
|
100
|
+
const title =
|
|
101
|
+
titleRaw && titleRaw !== "Untitled"
|
|
102
|
+
? titleRaw
|
|
103
|
+
: fallbackTitle || titleRaw;
|
|
104
|
+
const htmlRel = normalizedRel.replace(/\.mdx$/i, ".html");
|
|
105
|
+
const href = rootRelativeHref(htmlRel);
|
|
106
|
+
const page = {
|
|
107
|
+
filePath: absPath,
|
|
108
|
+
relativePath: normalizedRel,
|
|
109
|
+
slug,
|
|
110
|
+
segments: slugSegments,
|
|
111
|
+
isIndex,
|
|
112
|
+
href,
|
|
113
|
+
title,
|
|
114
|
+
fallbackTitle,
|
|
115
|
+
sortKey: pageSortKey(normalizedRel),
|
|
116
|
+
topSegment: slugSegments[0] || firstSegment || "",
|
|
117
|
+
};
|
|
118
|
+
pages.push(page);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
walk(CONTENT_DIR);
|
|
123
|
+
pages.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
124
|
+
return pages;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function createNode(slug) {
|
|
128
|
+
const segments = slug ? slug.split("/") : [];
|
|
129
|
+
const name = segments.slice(-1)[0] || "";
|
|
130
|
+
return {
|
|
131
|
+
slug,
|
|
132
|
+
segments,
|
|
133
|
+
name,
|
|
134
|
+
title: humanizeSegment(name),
|
|
135
|
+
href: null,
|
|
136
|
+
hasContent: false,
|
|
137
|
+
relativePath: null,
|
|
138
|
+
sortKey: slug || name,
|
|
139
|
+
sourcePage: null,
|
|
140
|
+
children: [],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getNavigationCache() {
|
|
145
|
+
if (NAV_CACHE) return NAV_CACHE;
|
|
146
|
+
const pages = collectPagesSync();
|
|
147
|
+
const pagesByRelative = new Map();
|
|
148
|
+
const nodes = new Map();
|
|
149
|
+
|
|
150
|
+
for (const page of pages) {
|
|
151
|
+
const {slug, segments} = page;
|
|
152
|
+
pagesByRelative.set(page.relativePath, page);
|
|
153
|
+
if (!segments.length) continue;
|
|
154
|
+
for (let i = 0; i < segments.length; i += 1) {
|
|
155
|
+
const key = segments.slice(0, i + 1).join("/");
|
|
156
|
+
if (key && !nodes.has(key)) {
|
|
157
|
+
nodes.set(key, createNode(key));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const page of pages) {
|
|
163
|
+
if (!page.slug) continue;
|
|
164
|
+
const node = nodes.get(page.slug);
|
|
165
|
+
if (!node) continue;
|
|
166
|
+
if (
|
|
167
|
+
!node.sourcePage ||
|
|
168
|
+
(node.sourcePage && node.sourcePage.isIndex && !page.isIndex)
|
|
169
|
+
) {
|
|
170
|
+
node.sourcePage = page;
|
|
171
|
+
node.title = page.title || node.title;
|
|
172
|
+
node.href = page.href || node.href;
|
|
173
|
+
node.relativePath = page.relativePath;
|
|
174
|
+
node.sortKey = page.sortKey || node.sortKey;
|
|
175
|
+
node.hasContent = true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const node of nodes.values()) {
|
|
180
|
+
const {segments} = node;
|
|
181
|
+
if (!segments.length) continue;
|
|
182
|
+
const parentSlug = segments.slice(0, -1).join("/");
|
|
183
|
+
if (!parentSlug) continue;
|
|
184
|
+
const parent = nodes.get(parentSlug);
|
|
185
|
+
if (!parent) continue;
|
|
186
|
+
if (!parent.children.some((child) => child.slug === node.slug)) {
|
|
187
|
+
parent.children.push(node);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const sortChildren = (node) => {
|
|
192
|
+
node.children.sort((a, b) => a.sortKey.localeCompare(b.sortKey));
|
|
193
|
+
for (const child of node.children) sortChildren(child);
|
|
194
|
+
};
|
|
195
|
+
for (const node of nodes.values()) {
|
|
196
|
+
sortChildren(node);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const roots = new Map();
|
|
200
|
+
for (const node of nodes.values()) {
|
|
201
|
+
if (node.segments.length === 1) {
|
|
202
|
+
roots.set(node.slug, node);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
NAV_CACHE = {
|
|
207
|
+
pages,
|
|
208
|
+
pagesByRelative,
|
|
209
|
+
nodes,
|
|
210
|
+
roots,
|
|
211
|
+
};
|
|
212
|
+
return NAV_CACHE;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function cloneNode(node, currentSlug) {
|
|
216
|
+
if (!node) return null;
|
|
217
|
+
const slug = node.slug;
|
|
218
|
+
const isActive = currentSlug && slug === currentSlug;
|
|
219
|
+
const isAncestor = !!(
|
|
220
|
+
currentSlug &&
|
|
221
|
+
slug &&
|
|
222
|
+
slug.length < currentSlug.length &&
|
|
223
|
+
currentSlug.startsWith(slug + "/")
|
|
224
|
+
);
|
|
225
|
+
const children = node.children
|
|
226
|
+
.map((child) => cloneNode(child, currentSlug))
|
|
227
|
+
.filter(Boolean);
|
|
228
|
+
if (!node.hasContent && !children.length) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
slug,
|
|
233
|
+
title: node.title,
|
|
234
|
+
href: node.href,
|
|
235
|
+
segments: node.segments.slice(),
|
|
236
|
+
depth: Math.max(0, node.segments.length - 1),
|
|
237
|
+
isActive: !!isActive,
|
|
238
|
+
isAncestor,
|
|
239
|
+
isExpanded: !!(isActive || isAncestor),
|
|
240
|
+
hasContent: node.hasContent,
|
|
241
|
+
relativePath: node.relativePath,
|
|
242
|
+
children,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function getPageInfo(relativePath) {
|
|
247
|
+
const cache = getNavigationCache();
|
|
248
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
249
|
+
const page = cache.pagesByRelative.get(normalized);
|
|
250
|
+
if (page) {
|
|
251
|
+
return {
|
|
252
|
+
title: page.title,
|
|
253
|
+
href: page.href,
|
|
254
|
+
slug: page.slug,
|
|
255
|
+
segments: page.segments.slice(),
|
|
256
|
+
relativePath: page.relativePath,
|
|
257
|
+
rootSegment: page.segments[0] || "",
|
|
258
|
+
isIndex: page.isIndex,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const {slug, segments} = slugFromRelative(normalized);
|
|
262
|
+
const htmlRel = normalized.replace(/\.mdx$/i, ".html");
|
|
263
|
+
return {
|
|
264
|
+
title: humanizeSegment(segments.slice(-1)[0] || slug || ""),
|
|
265
|
+
href: rootRelativeHref(htmlRel),
|
|
266
|
+
slug,
|
|
267
|
+
segments,
|
|
268
|
+
relativePath: normalized,
|
|
269
|
+
rootSegment: segments[0] || "",
|
|
270
|
+
isIndex: false,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function buildNavigationForFile(relativePath) {
|
|
275
|
+
const cache = getNavigationCache();
|
|
276
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
277
|
+
const page = cache.pagesByRelative.get(normalized);
|
|
278
|
+
const fallback = slugFromRelative(normalized);
|
|
279
|
+
const slug = page ? page.slug : fallback.slug;
|
|
280
|
+
const rootSegment = page
|
|
281
|
+
? page.segments[0] || ""
|
|
282
|
+
: fallback.segments[0] || "";
|
|
283
|
+
if (!slug || !rootSegment || EXCLUDED_ROOTS.has(rootSegment)) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
const rootNode = cache.roots.get(rootSegment);
|
|
287
|
+
if (!rootNode) return null;
|
|
288
|
+
const cloned = cloneNode(rootNode, slug);
|
|
289
|
+
if (!cloned) return null;
|
|
290
|
+
return {
|
|
291
|
+
rootSegment,
|
|
292
|
+
currentSlug: slug,
|
|
293
|
+
root: cloned,
|
|
294
|
+
title: cloned.title,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function resetNavigationCache() {
|
|
299
|
+
NAV_CACHE = null;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
module.exports = {
|
|
303
|
+
normalizeRelativePath,
|
|
304
|
+
getPageInfo,
|
|
305
|
+
buildNavigationForFile,
|
|
306
|
+
resetNavigationCache,
|
|
307
|
+
getPageContext,
|
|
308
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const React = require('react');
|
|
2
|
+
|
|
3
|
+
let PageContext = null;
|
|
4
|
+
|
|
5
|
+
function getPageContext() {
|
|
6
|
+
if (!PageContext) {
|
|
7
|
+
PageContext = React.createContext({ navigation: null, page: null });
|
|
8
|
+
}
|
|
9
|
+
return PageContext;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
getPageContext,
|
|
14
|
+
};
|
|
@@ -681,7 +681,7 @@ function parseProps(el) {
|
|
|
681
681
|
function bindSearchInputToStore() {
|
|
682
682
|
if (!store || typeof document === "undefined") return;
|
|
683
683
|
try {
|
|
684
|
-
const input = document.querySelector("[data-canopy-
|
|
684
|
+
const input = document.querySelector("[data-canopy-search-form-input]");
|
|
685
685
|
if (!input || input.dataset.canopySearchSync === "1") return;
|
|
686
686
|
input.dataset.canopySearchSync = "1";
|
|
687
687
|
|
|
@@ -764,7 +764,7 @@ if (typeof document !== "undefined") {
|
|
|
764
764
|
const q =
|
|
765
765
|
ev && ev.detail && typeof ev.detail.query === "string"
|
|
766
766
|
? ev.detail.query
|
|
767
|
-
: document.querySelector("[data-canopy-
|
|
767
|
+
: document.querySelector("[data-canopy-search-form-input]")?.value ||
|
|
768
768
|
"";
|
|
769
769
|
if (typeof q === "string") store.setQuery(q);
|
|
770
770
|
} catch (_) {}
|
|
@@ -217,7 +217,7 @@ function bindKeyboardNavigation({ input, list, panel }) {
|
|
|
217
217
|
});
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
async function
|
|
220
|
+
async function attachSearchForm(host) {
|
|
221
221
|
const config = parseProps(host) || {};
|
|
222
222
|
const maxResults = Number(config.maxResults || 8) || 8;
|
|
223
223
|
const groupOrder = Array.isArray(config.groupOrder) ? config.groupOrder : ['work', 'page'];
|
|
@@ -225,14 +225,14 @@ async function attachCommand(host) {
|
|
|
225
225
|
const onSearchPage = isOnSearchPage();
|
|
226
226
|
|
|
227
227
|
const panel = (() => {
|
|
228
|
-
try { return host.querySelector('[data-canopy-
|
|
228
|
+
try { return host.querySelector('[data-canopy-search-form-panel]'); } catch (_) { return null; }
|
|
229
229
|
})();
|
|
230
230
|
if (!panel) return;
|
|
231
231
|
|
|
232
232
|
if (!onSearchPage) {
|
|
233
233
|
try {
|
|
234
234
|
const wrapper = host.querySelector('.relative');
|
|
235
|
-
if (wrapper) wrapper.setAttribute('data-canopy-
|
|
235
|
+
if (wrapper) wrapper.setAttribute('data-canopy-search-form-auto', '1');
|
|
236
236
|
} catch (_) {}
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -244,7 +244,7 @@ async function attachCommand(host) {
|
|
|
244
244
|
if (!list) return;
|
|
245
245
|
|
|
246
246
|
const input = (() => {
|
|
247
|
-
try { return host.querySelector('[data-canopy-
|
|
247
|
+
try { return host.querySelector('[data-canopy-search-form-input]'); } catch (_) { return null; }
|
|
248
248
|
})();
|
|
249
249
|
if (!input) return;
|
|
250
250
|
|
|
@@ -345,9 +345,9 @@ async function attachCommand(host) {
|
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
host.addEventListener('click', (event) => {
|
|
348
|
-
const trigger = event.target && event.target.closest && event.target.closest('[data-canopy-
|
|
348
|
+
const trigger = event.target && event.target.closest && event.target.closest('[data-canopy-search-form-trigger]');
|
|
349
349
|
if (!trigger) return;
|
|
350
|
-
const mode = (trigger.dataset && trigger.dataset.
|
|
350
|
+
const mode = (trigger.dataset && trigger.dataset.canopySearchFormTrigger) || '';
|
|
351
351
|
if (mode === 'submit' || mode === 'form') return;
|
|
352
352
|
event.preventDefault();
|
|
353
353
|
openPanel();
|
|
@@ -359,12 +359,12 @@ async function attachCommand(host) {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
ready(() => {
|
|
362
|
-
const hosts = Array.from(document.querySelectorAll('[data-canopy-
|
|
362
|
+
const hosts = Array.from(document.querySelectorAll('[data-canopy-search-form]'));
|
|
363
363
|
if (!hosts.length) return;
|
|
364
364
|
hosts.forEach((host) => {
|
|
365
|
-
|
|
365
|
+
attachSearchForm(host).catch((err) => {
|
|
366
366
|
try {
|
|
367
|
-
console.warn('[canopy][
|
|
367
|
+
console.warn('[canopy][search-form] failed to initialise', err && (err.message || err));
|
|
368
368
|
} catch (_) {}
|
|
369
369
|
});
|
|
370
370
|
});
|
package/lib/search/search.js
CHANGED
|
@@ -158,13 +158,13 @@ async function buildSearchPage() {
|
|
|
158
158
|
// Include react-globals vendor shim before search.js to provide window.React globals
|
|
159
159
|
const vendorReactAbs = path.join(OUT_DIR, 'scripts', 'react-globals.js');
|
|
160
160
|
const vendorFlexAbs = path.join(OUT_DIR, 'scripts', 'flexsearch-globals.js');
|
|
161
|
-
const
|
|
161
|
+
const vendorSearchFormAbs = path.join(OUT_DIR, 'scripts', 'canopy-search-form.js');
|
|
162
162
|
function verRel(abs) {
|
|
163
163
|
let rel = path.relative(path.dirname(outPath), abs).split(path.sep).join('/');
|
|
164
164
|
try { const st = require('fs').statSync(abs); rel += `?v=${Math.floor(st.mtimeMs || Date.now())}`; } catch (_) {}
|
|
165
165
|
return rel;
|
|
166
166
|
}
|
|
167
|
-
const vendorTags = `<script src="${verRel(vendorReactAbs)}"></script><script src="${verRel(vendorFlexAbs)}"></script><script src="${verRel(
|
|
167
|
+
const vendorTags = `<script src="${verRel(vendorReactAbs)}"></script><script src="${verRel(vendorFlexAbs)}"></script><script src="${verRel(vendorSearchFormAbs)}"></script>`;
|
|
168
168
|
let headExtra = vendorTags + head + importMap;
|
|
169
169
|
try {
|
|
170
170
|
const { BASE_PATH } = require('../common');
|