Skip to content

test merge #2859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
0644b57
Add css variables for dimensions, and use svh units
phoneticallySAARTHaK Jan 18, 2025
32f46d9
Fix excess/absent margins and padding.
phoneticallySAARTHaK Jan 18, 2025
aaff25e
Fix tab order of toolbar and remove unused selectors. **Breaks search**
phoneticallySAARTHaK Jan 19, 2025
99ae7be
Add more search related 118n strings, and inject more strings in js s…
phoneticallySAARTHaK Jan 19, 2025
876b81e
Implement search component as combobox, with minimal functionality
phoneticallySAARTHaK Jan 19, 2025
ae8388b
Add global keyboard listeners for opening modal
phoneticallySAARTHaK Jan 19, 2025
35f5be6
rename `setCurrentResult` to `setNextResult`
phoneticallySAARTHaK Jan 19, 2025
10ede76
Use semantic element `mark` for highlighting search matches
phoneticallySAARTHaK Jan 19, 2025
06e5b4d
Add exit animation for modals, using custom overlay
phoneticallySAARTHaK Jan 19, 2025
af61556
Remove unused keyframes. Merge `body` selectors
phoneticallySAARTHaK Jan 19, 2025
573cd44
Adjust max-height with virtual keyboard, in mobiles
phoneticallySAARTHaK Jan 19, 2025
523e097
fix placeholder text color on focus search input
phoneticallySAARTHaK Jan 19, 2025
f27f150
Remove min-height from search dialog and set it to `.state`
phoneticallySAARTHaK Jan 20, 2025
8986d06
fix single element edge case in `setNextResult`
phoneticallySAARTHaK Jan 20, 2025
d3fd2e1
Show recent searches when the search query is empty
phoneticallySAARTHaK Jan 20, 2025
d25d3a0
Completely revert "recent searches" feature.
phoneticallySAARTHaK Jan 25, 2025
0a64f24
Fix typo and refactor search.ts code
phoneticallySAARTHaK Jan 25, 2025
1a68fbf
Fix invalid search message implementation
phoneticallySAARTHaK Jan 26, 2025
f425f95
Add placeholder for no_results i18n string
phoneticallySAARTHaK Jan 26, 2025
67d03bb
remove global button styles and add it to tsd-widget
phoneticallySAARTHaK Jan 26, 2025
f4ebea6
Improve text contrast, satisfying WCAG level AA
phoneticallySAARTHaK Jan 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/lib/internationalization/locales/en.cts
Original file line number Diff line number Diff line change
@@ -509,7 +509,6 @@ export = {
theme_generated_using_typedoc: "Generated using TypeDoc", // If this includes "TypeDoc", theme will insert a link at that location.
// Search
theme_preparing_search_index: "Preparing search index...",
theme_search_index_not_available: "The search index is not available",
// Left nav bar
theme_loading: "Loading...",
// Right nav bar
@@ -537,4 +536,7 @@ export = {
"This member is normally hidden due to your filter settings.",
theme_hierarchy_expand: "Expand",
theme_hierarchy_collapse: "Collapse",
theme_search_index_not_available: "The search index is not available",
theme_search_no_results_found_for_0: "No results found for {0}",
theme_search_placeholder: "Search the docs",
} as const;
6 changes: 6 additions & 0 deletions src/lib/output/plugins/AssetsPlugin.ts
Original file line number Diff line number Diff line change
@@ -41,6 +41,12 @@ export class AssetsPlugin extends RendererComponent {
hierarchy_expand: i18n.theme_hierarchy_expand(),
hierarchy_collapse: i18n.theme_hierarchy_collapse(),
folder: i18n.theme_folder(),
theme_search_index_not_available:
this.application.i18n.theme_search_index_not_available(),
theme_search_no_results_found_for_0:
this.application.i18n.theme_search_no_results_found_for_0(
"{0}",
),
};

for (const key of getEnumKeys(ReflectionKind)) {
5 changes: 5 additions & 0 deletions src/lib/output/themes/default/assets/bootstrap.ts
Original file line number Diff line number Diff line change
@@ -26,3 +26,8 @@ Object.defineProperty(window, "app", { value: app });
initSearch();
initNav();
initHierarchy();

if ("virtualKeyboard" in navigator) {
// @ts-ignore
navigator.virtualKeyboard.overlaysContent = true;
}
4 changes: 4 additions & 0 deletions src/lib/output/themes/default/assets/typedoc/Application.ts
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ declare global {
// Kind strings for icons
folder: string;
[k: `kind_${number}`]: string;
theme_search_index_not_available: string;
theme_search_no_results_found_for_0: string;
};
}
}
@@ -50,6 +52,8 @@ window.translations ||= {
kind_2097152: "Type Alias",
kind_4194304: "Reference",
kind_8388608: "Document",
theme_search_index_not_available: "The search index is not available",
theme_search_no_results_found_for_0: "No results found for {0}",
};

/**
333 changes: 197 additions & 136 deletions src/lib/output/themes/default/assets/typedoc/components/Search.ts

Large diffs are not rendered by default.

119 changes: 119 additions & 0 deletions src/lib/output/themes/default/assets/typedoc/utils/modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* @module
*
* Browsers allow scrolling of page with native dialog, which is a UX issue.
*
* `@starting-style` and `overlay` aren't well supported in FF, and only available in latest versions of chromium,
* hence, a custom overlay workaround is required.
*
* Workaround:
*
* - Append a custom overlay element (a div) to `document.body`,
* this does **NOT** handle nested modals,
* as the overlay div cannot be in the top layer, which wouldn't overshadow the parent modal.
*
* - Add exit animation on dialog and overlay, without actually closing them
* - Listen for `animationend` event, and close the modal immediately
*
* @see
* - The "[right](https://frontendmasters.com/blog/animating-dialog/)" way to animate modals
* - [Workaround](https://github.com/whatwg/html/issues/7732#issuecomment-2437820350) to prevent background scrolling
*/

// Constants
const CLOSING_CLASS = "closing";
const OVERLAY_ID = "tsd-overlay";

/** Fills the gap that scrollbar occupies. Call when the modal is opened */
function hideScrollbar() {
// https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
// Should be computed *before* body overflow is set to hidden
const width = Math.abs(
window.innerWidth - document.documentElement.clientWidth,
);

document.body.style.overflow = "hidden";

// Give padding to element to balance the hidden scrollbar width
document.body.style.paddingRight = `${width}px`;
}

/** Resets style changes made by {@link hideScrollbar} */
function resetScrollbar() {
document.body.style.removeProperty("overflow");
document.body.style.removeProperty("padding-right");
}

/** Could be popover too */
type Modal = HTMLDialogElement;

/**
* Must be called to setup a modal element properly for entry and exit side-effects.
*
* Adds event listeners to the modal element, for the closing animation.
*
* Adds workaround to fix scrolling issues caused by default browser behavior.
*
* @param closingAnimation Name of `@keyframes` for closing animation
* @param options Configure modal behavior
* @param options.closeOnEsc Defaults to true
* @param options.closeOnClick Closes modal when clicked on overlay, defaults to false.
*/
export function setUpModal(
modal: Modal,
closingAnimation: string,
options?: {
closeOnEsc?: boolean;
closeOnClick?: boolean;
},
) {
// Event listener for closing animation
modal.addEventListener("animationend", (e) => {
if (e.animationName !== closingAnimation) return;
modal.classList.remove(CLOSING_CLASS);
document.getElementById(OVERLAY_ID)?.remove();
modal.close();
resetScrollbar();
});

// Override modal cancel behavior, hopefully all browsers have same behavior
// > When a `<dialog>` is dismissed with the `Esc` key, both the `cancel` and `close` events are fired.
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/cancel_event
modal.addEventListener("cancel", (e) => {
e.preventDefault();
closeModal(modal);
});

if (options?.closeOnClick) {
document.addEventListener(
"click",
(e) => {
if (modal.open && !modal.contains(e.target as HTMLElement)) {
closeModal(modal);
}
},
true, // Disable invoking this handler in bubbling phase
);
}
}

export function openModal(modal: Modal) {
if (modal.open) return;

const overlay = document.createElement("div");
overlay.id = OVERLAY_ID;
document.body.appendChild(overlay);

modal.showModal();
hideScrollbar();
}

/** Initiates modal closing, by adding a `closing` class that starts the closing animation */
export function closeModal(modal: Modal) {
if (!modal.open) return;
const overlay = document.getElementById(OVERLAY_ID);
if (overlay) {
overlay.classList.add(CLOSING_CLASS);
}
modal.classList.add(CLOSING_CLASS);
}
70 changes: 38 additions & 32 deletions src/lib/output/themes/default/partials/toolbar.tsx
Original file line number Diff line number Diff line change
@@ -7,42 +7,48 @@ import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js"
export const toolbar = (context: DefaultThemeRenderContext, props: PageEvent<Reflection>) => (
<header class="tsd-page-toolbar">
<div class="tsd-toolbar-contents container">
<div class="table-cell" id="tsd-search">
<div class="field">
<label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption">
{context.icons.search()}
</label>
<input type="text" id="tsd-search-field" aria-label={context.i18n.theme_search()} />
</div>
<a href={context.options.getValue("titleLink") || context.relativeURL("index.html")} class="title">
{getDisplayName(props.project)}
</a>

<div class="field">
<div id="tsd-toolbar-links">
{Object.entries(context.options.getValue("navigationLinks")).map(([label, url]) => (
<a href={url}>{label}</a>
))}
</div>
</div>
<div id="tsd-toolbar-links">
{Object.entries(context.options.getValue("navigationLinks")).map(([label, url]) => (
<a href={url}>{label}</a>
))}
</div>

<ul class="results">
<li class="state loading">{context.i18n.theme_preparing_search_index()}</li>
<li class="state failure">{context.i18n.theme_search_index_not_available()}</li>
</ul>
<button id="tsd-search-trigger" class="tsd-widget" aria-label={context.i18n.theme_search()}>
{context.icons.search()}
</button>
<dialog id="tsd-search" aria-label={context.i18n.theme_search()}>
<input
role="combobox"
id="tsd-search-input"
aria-controls="tsd-search-results"
aria-autocomplete="list"
aria-expanded="true"
spellcheck={false}
autocapitalize="off"
autocomplete="off"
placeholder={context.i18n.theme_search_placeholder()}
maxLength={100}
/>

<a href={context.options.getValue("titleLink") || context.relativeURL("index.html")} class="title">
{getDisplayName(props.project)}
</a>
</div>
<ul role="listbox" id="tsd-search-results"></ul>
<div id="tsd-search-status" aria-live="polite" aria-atomic="true">
<div>{context.i18n.theme_preparing_search_index()}</div>
</div>
</dialog>

<div class="table-cell" id="tsd-widgets">
<a
href="#"
class="tsd-widget tsd-toolbar-icon menu no-caption"
data-toggle="menu"
aria-label={context.i18n.theme_menu()}
>
{context.icons.menu()}
</a>
</div>
<a
href="#"
class="tsd-widget menu"
id="tsd-toolbar-menu-trigger"
data-toggle="menu"
aria-label={context.i18n.theme_menu()}
>
{context.icons.menu()}
</a>
</div>
</header>
);
418 changes: 218 additions & 200 deletions static/style.css

Large diffs are not rendered by default.