This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/freemarker-docgen.git
commit ade43f68e8da006395fea910b89c20b45f6e7076 Author: ddekany <[email protected]> AuthorDate: Sat Jun 21 20:53:28 2025 +0200 Added Pagefind support (and some other *.less cleanup/fixing). More work is needed to improve the pagefind index content. --- .../freemarker/docgen/core/ExceptionUtils.java} | 53 +++----- .../org/freemarker/docgen/core/PageFindRunner.java | 150 +++++++++++++++++++++ .../java/org/freemarker/docgen/core/Transform.java | 30 ++++- .../org/freemarker/docgen/core/less/lib/base.less | 2 +- .../core/less/lib/components/search-form.less | 6 +- .../docgen/core/less/lib/layout/content.less | 4 +- .../docgen/core/less/lib/layout/header.less | 23 ++-- .../freemarker/docgen/core/less/lib/pagefind.less | 69 ++++++++++ .../freemarker/docgen/core/less/lib/utilities.less | 4 +- .../freemarker/docgen/core/less/lib/variables.less | 7 + .../org/freemarker/docgen/core/less/styles.less | 3 + .../freemarker/docgen/core/statics/js/pagefind.js | 110 +++++++++++++++ .../freemarker/docgen/core/templates/header.ftlh | 42 +++--- .../org/freemarker/docgen/core/templates/page.ftlh | 6 + 14 files changed, 439 insertions(+), 70 deletions(-) diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less b/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/ExceptionUtils.java similarity index 53% copy from freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less copy to freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/ExceptionUtils.java index 308b12c..f751133 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less +++ b/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/ExceptionUtils.java @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -16,40 +16,25 @@ * specific language governing permissions and limitations * under the License. */ -// determines the max-width of the site at various resolutions -.site-width { - padding-left: @gutter-width / 4 * 3; - padding-right: @gutter-width / 4 * 3; - @media (min-width: @screen-sm-min) { - padding-left: @gutter-width; - padding-right: @gutter-width; - } +package org.freemarker.docgen.core; - @media (min-width: @screen-lg-min) { - margin-left: auto; - margin-right: auto; - width: @container-lg; - } -} - -// Only display content to screen readers -// See: http://a11yproject.com/posts/how-to-hide-content/ -.sr-only { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0,0,0,0); - border: 0; -} +final class ExceptionUtils { + private ExceptionUtils() { + throw new AssertionError(); + } -.center-img { - text-align: center; + static String toCauseTrace(Throwable e) { + StringBuilder sb = new StringBuilder(); + Throwable currentE = e; + while (currentE != null) { + if (sb.length() != 0) { + sb.append("\nCaused by: "); + } + sb.append(currentE); - img { - max-width: 100%; - } + currentE = currentE.getCause(); + } + return sb.toString(); + } } diff --git a/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/PageFindRunner.java b/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/PageFindRunner.java new file mode 100644 index 0000000..3d8a4bd --- /dev/null +++ b/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/PageFindRunner.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.freemarker.docgen.core; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility class to install Pagefind and run its indexing process + * as part of a Java build or site generation. + */ +final class PageFindRunner { + // See latest version here: https://www.npmjs.com/package/pagefind + private static final String PAGEFIND_VERSION = "1.3.0"; + + private final Path indexedDir; + + /** + * Constructs a PageFindRunner instance. + * + * @param indexedDir The path to the directory containing the fully generated static HTML site + * that needs to be indexed by Pagefind. + * @param workDir The path to a working directory where Node.js (npm) will install Pagefind. + * This directory will contain `node_modules` and potentially `package.json`. + */ + private PageFindRunner(Path indexedDir) { + this.indexedDir = indexedDir.toAbsolutePath().normalize(); + } + + /** + * Static method to initiate the Pagefind installation and indexing process. + * + * @param indexedDir The path to the directory containing the fully generated static HTML site. + * @param workDir The path to a working directory for Pagefind installation. + * @throws RuntimeException If an IOException, InterruptedException, or an error during + * Pagefind command execution occurs. + */ + static void run(Path indexedDir) { + try { + new PageFindRunner(indexedDir).run(); + } catch (IOException | InterruptedException e) { + // Wrap checked exceptions in a RuntimeException for simpler API usage + throw new RuntimeException("Pagefind execution failed (see cause exception)", e); + } + } + + /** + * Executes the Pagefind installation and indexing. + */ + private void run() throws IOException, InterruptedException { + if (!Files.isDirectory(indexedDir)) { + throw new IllegalArgumentException("Indexed directory must be a valid directory: " + indexedDir); + } + + System.out.println("Invoking Pagefind on indexed directory: " + indexedDir); + + int exitCode = runNodeProcess(); + if (exitCode != 0) { + throw new RuntimeException("Pagefind indexing command failed with exit code: " + exitCode); + } + + System.out.println("Pagefind indexing completed successfully."); + } + + private int runNodeProcess() throws IOException, InterruptedException { + String npxCommand = findNpxCommand(); + + ProcessBuilder processBuilder = new ProcessBuilder( + npxCommand, + "pagefind@" + PAGEFIND_VERSION, + "--site", + indexedDir.toString() + ); + + // Just to have a consistent work directory... + processBuilder.directory(indexedDir.toFile()); + + // Redirect standard output and standard error to the Java application's console + processBuilder.inheritIO(); + + Process process = processBuilder.start(); + + return process.waitFor(); + } + + private String findNpxCommand() throws IOException, InterruptedException { + Map<String, Exception> failedAttempts = new LinkedHashMap<>(); + for (String npxCommand : List.of("npx", "npx.cmd", "npx.ps1")) { + ProcessBuilder processBuilder = new ProcessBuilder( + npxCommand, "--version" + ); + + // Just to have a consistent work directory... + processBuilder.directory(indexedDir.toFile()); + try { + Process process = processBuilder.start(); + String output = new String(process.getInputStream().readAllBytes()); + process.waitFor(); + if (process.exitValue() != 0) { + throw new IllegalStateException(npxCommand + " --version command failed with exit code " + process.exitValue()); + } + System.out.println(npxCommand + " --version output:\n" + output); + return npxCommand; + } catch (IOException e) { + failedAttempts.put(npxCommand, e); + } + } + + StringBuilder sb = new StringBuilder("Can't find a working npx command. " + + "Ensure that Node.js is installed, and it's in the system path!\n"); + for (Map.Entry<String, Exception> entry : failedAttempts.entrySet()) { + StringWriter stackTrace = new StringWriter(); + sb.append("\nAttempted command: ").append(entry.getKey()).append("\n") + .append("Failed with: ").append(ExceptionUtils.toCauseTrace(entry.getValue())) + .append("\n"); + } + throw new IOException(sb.toString()); + } + + // For testing purposes only! + public static void main(String[] args) { + if (args.length != 1) { + throw new IllegalArgumentException("Command-line arguments should be: <indexedDir>"); + } + PageFindRunner.run(Path.of(args[0])); + } +} diff --git a/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/Transform.java b/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/Transform.java index a7f6f61..7c45efd 100644 --- a/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/Transform.java +++ b/freemarker-docgen-core/src/main/java/org/freemarker/docgen/core/Transform.java @@ -142,6 +142,7 @@ public final class Transform { static final String SETTING_GENERATE_ECLIPSE_TOC = "generateEclipseTOC"; static final String SETTING_SHOW_XXE_LOGO = "showXXELogo"; static final String SETTING_SEARCH_KEY = "searchKey"; + static final String SETTING_PAGEFIND_BASED_SEARCH = "pagefindBasedSearch"; static final String SETTING_DISABLE_JAVASCRIPT = "disableJavaScript"; static final String SETTING_TIME_ZONE = "timeZone"; static final String SETTING_LOCALE = "locale"; @@ -226,6 +227,10 @@ public final class Transform { = SETTING_SHOW_XXE_LOGO; private static final String VAR_SEARCH_KEY = SETTING_SEARCH_KEY; + private static final String VAR_PAGEFIND_BASED_SEARCH + = SETTING_PAGEFIND_BASED_SEARCH; + private static final String VAR_SHOW_SEARCH_FORM + = "hasSearch"; private static final String VAR_DISABLE_JAVASCRIPT = SETTING_DISABLE_JAVASCRIPT; private static final String VAR_ECLIPSE_LINK_TO = SETTING_ECLIPSE_LINK_TO; @@ -426,8 +431,12 @@ public final class Transform { private String searchKey; + private boolean hasSearch; + private boolean disableJavaScript; + private boolean pagefindBasedSearch; + private boolean validate = true; private Locale locale = Locale.US; @@ -561,6 +570,7 @@ public final class Transform { if (!offline && searchKey != null) { generateSearchResultsHtmlFile(doc); htmlFileCounter++; + pagefindBasedSearch = true; } copyStandardStatics(); @@ -571,6 +581,12 @@ public final class Transform { generateEclipseTOC(doc); } + // Unfortunately, Pagefind doesn't work offline, as it uses ES6 modules. + if (!offline && pagefindBasedSearch) { + PageFindRunner.run(destDir.toPath()); + pagefindBasedSearch = true; + } + logger.info( "Done: " + htmlFileCounter + " HTML-s + " @@ -832,6 +848,8 @@ public final class Transform { showXXELogo = castSetting(settingName, settingValue, Boolean.class); } else if (topSettingName.equals(SETTING_SEARCH_KEY)) { searchKey = castSetting(settingName, settingValue, String.class); + }else if (topSettingName.equals(SETTING_PAGEFIND_BASED_SEARCH)) { + pagefindBasedSearch = castSetting(settingName, settingValue, Boolean.class); }else if (topSettingName.equals(SETTING_DISABLE_JAVASCRIPT)) { disableJavaScript = castSetting(settingName, settingValue, Boolean.class); } else if (topSettingName.equals(SETTING_CONTENT_DIRECTORY)) { @@ -953,7 +971,7 @@ public final class Transform { TemplateLoader templateLoader = new ClassTemplateLoader( Transform.class, "templates"); File templatesDir = new File(srcDir, DIR_TEMPLATES); - if (!templatesDir.exists()) { + if (templatesDir.exists()) { templateLoader = new MultiTemplateLoader( new TemplateLoader[] { new FileTemplateLoader(templatesDir), templateLoader }); } @@ -1120,6 +1138,8 @@ public final class Transform { fmConfig.setSharedVariable(VAR_SHOW_EDITORAL_NOTES, showEditoralNotes); fmConfig.setSharedVariable(VAR_SHOW_XXE_LOGO, showXXELogo); fmConfig.setSharedVariable(VAR_SEARCH_KEY, searchKey); + fmConfig.setSharedVariable(VAR_PAGEFIND_BASED_SEARCH, pagefindBasedSearch); + fmConfig.setSharedVariable(VAR_SHOW_SEARCH_FORM, pagefindBasedSearch); fmConfig.setSharedVariable(VAR_DISABLE_JAVASCRIPT, disableJavaScript); fmConfig.setSharedVariable(VAR_OLINKS, olinks); fmConfig.setSharedVariable(VAR_NUMBERED_SECTIONS, numberedSections); @@ -1286,6 +1306,9 @@ public final class Transform { } if (!disableJavaScript) { copyCommonStatic("main.min.js"); + if (pagefindBasedSearch) { + copyCommonStatic("js/pagefind.js"); + } } } @@ -1348,8 +1371,9 @@ public final class Transform { } Path destSubdir = destDir.toPath().resolve("docgen-resources"); - Files.createDirectories(destSubdir); - Files.write(destSubdir.resolve(staticFileName), content.getBytes(fileCharset)); + Path destFile = destSubdir.resolve(staticFileName); + Files.createDirectories(destFile.getParent()); + Files.write(destFile, content.getBytes(fileCharset)); } else { FileUtil.copyResourceIntoFile( Transform.class, "statics", staticFileName, diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/base.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/base.less index 8fdd6be..e33d032 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/base.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/base.less @@ -22,7 +22,7 @@ html { color: @text-color; line-height: 1.5; font-weight: normal; - background-color: #fff; + background-color: @background-color; text-size-adjust: 100%; height: 100%; } diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/components/search-form.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/components/search-form.less index c3ed0b1..d068a81 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/components/search-form.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/components/search-form.less @@ -42,7 +42,7 @@ color: @brand-color; line-height: 1; - background-color: #fff; + background-color: @background-color; background-image: none; border: 0; @@ -89,7 +89,7 @@ font-size: 16px; // prevent zoom in on mobile line-height: 21px; - border: 1px solid #aec0d6; + border: 1px solid @input-border-color; border-radius: 0; box-sizing: border-box; @@ -101,7 +101,7 @@ &:focus { outline: 0; - border-color: @link-color; + border-color: @input-border-color-focus; } } } diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/content.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/content.less index 3c2e087..2c3a741 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/content.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/content.less @@ -57,7 +57,7 @@ left: 0px; top: @hamburger-icon-top + @hamburger-icon-height; z-index: 100; - background: #fff; // TODO variable? + background: @background-color; border: 1px solid #000; // TODO variable? box-shadow: 5px 5px 5px 0px rgba(0,0,0,0.33); @@ -74,7 +74,7 @@ // For xs width: flex: 0 0 160px; max-width: 160px; // set max-width to prevent flicker - margin-right: @gutter-width / 4 * 3; + margin-right: (@gutter-width / 4 * 3); } @media (min-width: @screen-sm-min) { diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/header.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/header.less index 6941237..a09adee 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/header.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/layout/header.less @@ -58,15 +58,21 @@ z-index: 1; } -.search-row { - padding-top: 12px; +.navigation-header-font-size() { + font-size: 18px; @media (min-width: @screen-xs-min) { - display: flex; - justify-content: space-between; - flex-wrap: wrap; - align-items: flex-end; + font-size: 27px; } +} + +.search-row { + padding-top: @header-panel-top-padding; + + display: flex; + justify-content: space-between; + flex-wrap: wrap; + align-items: flex-end; .navigation-header, .search-form { @@ -76,13 +82,10 @@ .navigation-header { display: block; - font-size: 18px; line-height: 1; flex: 1 1 auto; - @media (min-width: @screen-xs-min) { - font-size: 27px; - } + .navigation-header-font-size(); @media (min-width: @screen-sm-min) { flex-grow: 0; diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/pagefind.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/pagefind.less new file mode 100644 index 0000000..9db5e78 --- /dev/null +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/pagefind.less @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#pagefind-panel { + padding-top: @header-panel-top-padding; + display: none; + + --pagefind-ui-border-width: 0; + --pagefind-ui-border-radius: 0; + --pagefind-ui-text: @text-color; + --pagefind-ui-background: @background-color; + --pagefind-ui-font: @font-sans-serif; + + input { + --pagefind-ui-border: @input-border-color; + --pagefind-ui-border-width: 1px; + + &:focus { + outline: 0; + border-color: @input-border-color-focus; + } + } +} + +#pagefind-panel-toggler { + .search-btn { + color: @brand-color; + + background: none; + + border: 0; + border-radius: 0; + + padding: 0; + + &:hover, + &:focus { + color: lighten(@link-color, 30%); + cursor: pointer; + outline: 0; + } + + &::before { + .icon(); + .icon-search(); + + box-sizing: border-box; + display: block; + + .navigation-header-font-size(); + } + } +} diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less index 308b12c..d1a1af6 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/utilities.less @@ -18,8 +18,8 @@ */ // determines the max-width of the site at various resolutions .site-width { - padding-left: @gutter-width / 4 * 3; - padding-right: @gutter-width / 4 * 3; + padding-left: (@gutter-width / 4 * 3); + padding-right: (@gutter-width / 4 * 3); @media (min-width: @screen-sm-min) { padding-left: @gutter-width; diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/variables.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/variables.less index 64bbcdf..37c7283 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/variables.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/lib/variables.less @@ -19,6 +19,8 @@ // fonts @font-sans-serif: "Roboto", "Helvetica Neue", Arial, sans-serif; +@background-color: #fff; + // colors @brand-color: #0050b2; @@ -29,6 +31,9 @@ @link-hover: #0973f5; @link-visited: #AF09AF; //used for toc +// form inputs +@input-border-color: #aec0d6; +@input-border-color-focus: @link-color; // headers @header-color: @brand-color; @@ -64,3 +69,5 @@ @breadcrumb-row-font-size-xs: 11px; @breadcrumb-row-font-size-sm: 13px; @breadcrumb-row-padding-top: 6px; + +@header-panel-top-padding: 12px; diff --git a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/styles.less b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/styles.less index 613ad1b..df32f82 100644 --- a/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/styles.less +++ b/freemarker-docgen-core/src/main/resources-gulp/org/freemarker/docgen/core/less/styles.less @@ -49,3 +49,6 @@ // generic utilities @import "lib/utilities.less"; + +// pagefind +@import "lib/pagefind.less"; diff --git a/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/statics/js/pagefind.js b/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/statics/js/pagefind.js new file mode 100644 index 0000000..2ba281b --- /dev/null +++ b/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/statics/js/pagefind.js @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* jshint esversion: 6 */ +/* jshint browser: true */ + +'use strict'; + +var memoizedPagefindPanel = null; +function getPagefindPanel() { + if (memoizedPagefindPanel == null) { + const pagefindPanel = document.getElementById('pagefind-panel'); + if (!pagefindPanel) { + window.alert('Pagefind panel not found!'); + throw new Error('Pagefind panel not found!'); + } + + memoizedPagefindPanel = pagefindPanel; + } + + return memoizedPagefindPanel; +} + +var memoizedPagefindInput = null; +function getPagefindInput() { + if (memoizedPagefindInput == null) { + const pagefindInput = getPagefindPanel().querySelector('input[type="text"]'); + if (!pagefindInput) { + window.alert('Search input not found in the Pagefind panel!'); + throw new Error('Search input not found in the Pagefind panel!'); + } + + memoizedPagefindInput = pagefindInput; + } + return memoizedPagefindInput; +} + +var memoizedPagefindPanelToggleButton = null; +function getPagefindPanelToggleButton() { + if (memoizedPagefindPanelToggleButton == null) { + const button = document.getElementById('pagefind-panel-toggle-button'); + if (!button) { + window.alert('Search toggle button not found!'); + throw new Error('Search toggle button not found!'); + } + + memoizedPagefindPanelToggleButton = button; + } + return memoizedPagefindPanelToggleButton; +} + +function hidePagefindPanel() { + getPagefindPanel().style.display = 'none'; + getPagefindInput().blur(); +} + +function showPagefindPanel() { + getPagefindPanel().style.display = 'block'; + getPagefindInput().focus(); +} + +function togglePagefindPanel() { + const pagefindPanel = getPagefindPanel(); + if (pagefindPanel.style.display === 'none' || pagefindPanel.style.display === '') { + showPagefindPanel(); + } else { + hidePagefindPanel(); + } +} + +function addDocgenPagefindCustomizations() { + const searchInput = getPagefindInput(); + + // Input auto-hiding: + searchInput.addEventListener('blur', () => { + if (searchInput.value === '') { + hidePagefindPanel(); + } + }); + searchInput.addEventListener('keydown', (event) => { + if (event.key === 'Escape') { + hidePagefindPanel(); + } + }); + + hidePagefindPanel(); +} + +window.addEventListener('DOMContentLoaded', (event) => { + /*jshint -W117 */ + new PagefindUI({ element: '#pagefind-panel', showSubResults: true }); + /*jshint -W117 */ + addDocgenPagefindCustomizations(); +}); diff --git a/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/header.ftlh b/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/header.ftlh index ad5e96b..fb514de 100644 --- a/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/header.ftlh +++ b/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/header.ftlh @@ -60,7 +60,10 @@ </#macro> <#macro navigationHeader> - <div class="header-bottom-bg"><#t> + <div class="header-bottom-bg"> + <#if hasSearch && pagefindBasedSearch> + <div class="site-width" id="pagefind-panel"></div> + </#if> <div class="site-width search-row"><#t> <#local book = .node?root.*> <a href="${CreateLinkFromNode(book)}" class="navigation-header"><#t> @@ -68,24 +71,33 @@ </a><#t> <#-- empty div to maintain layout --> <div class="navigation-header"></div><#t> - <@searchForm /><#t> - </div><#t> + <@searchAccessor /><#t> + </div> <div class="site-width breadcrumb-row"><#t> <@nav.breadcrumb /><#t> <@nav.bookmarks /><#t> - </div><#t> - </div><#t> + </div> + </div> </#macro> -<#macro searchForm> - <#if searchKey?? && !offline> - <form method="get" class="search-form<#if offline> offline</#if>" action="search-results.html"><#t> - <fieldset><#t> - <legend class="sr-only">Search form</legend><#t> - <label for="search-field" class="sr-only">Search query</label><#t> - <input id="search-field" name="q" type="search" class="search-input" placeholder="Search" spellcheck="false" autocorrect="off" autocomplete="off"><#t> - <button type="submit" class="search-btn"><span class="sr-only">Search</span></button><#t> - </fieldset><#t> - </form><#t> +<#macro searchAccessor> + <#if hasSearch> + <#if pagefindBasedSearch> + <div id="pagefind-panel-toggler"><#t> + <button onclick="togglePagefindPanel()" id="pagefind-panel-toggle-button" class="search-btn"><span class="sr-only">Search</span></button> + </div> + <#else> + <div class="search-form"><#t> + <div id="search-field"></div> + <form method="get" action="search-results.html"><#t> + <fieldset><#t> + <legend class="sr-only">Search form</legend><#t> + <label for="search-field" class="sr-only">Search query</label><#t> + <input id="search-field" name="q" type="search" class="search-input" placeholder="Search" spellcheck="false" autocorrect="off" autocomplete="off"><#t> + <button type="submit" class="search-btn"><span class="sr-only">Search</span></button><#t> + </fieldset><#t> + </form><#t> + </div> + </#if> </#if> </#macro> diff --git a/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/page.ftlh b/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/page.ftlh index 60c7a84..c5be7b2 100644 --- a/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/page.ftlh +++ b/freemarker-docgen-core/src/main/resources/org/freemarker/docgen/core/templates/page.ftlh @@ -80,6 +80,12 @@ <#if !offline && cookieConsentScriptURL??> <script type="text/javascript" src="${cookieConsentScriptURL}"></script> </#if> + + <#if hasSearch && pagefindBasedSearch> + <link href="pagefind/pagefind-ui.css" rel="stylesheet"> + <script src="pagefind/pagefind-ui.js"></script> + <script src="docgen-resources/js/pagefind.js"></script> + </#if> </head> </#compress> </#macro>
