UNPKG

js-draw

Version:

Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript.

132 lines (131 loc) 4.99 kB
// Displays a find dialog that allows the user to search for and focus text. // // @packageDocumentation import TextComponent from '../components/TextComponent.mjs'; import ImageComponent from '../components/ImageComponent.mjs'; import BaseTool from './BaseTool.mjs'; import { toggleFindVisibleShortcutId } from './keybindings.mjs'; const cssPrefix = 'find-tool'; export default class FindTool extends BaseTool { constructor(editor) { super(editor.notifier, editor.localization.findLabel); this.editor = editor; this.currentMatchIdx = 0; this.overlay = document.createElement('div'); this.fillOverlay(); editor.createHTMLOverlay(this.overlay); this.overlay.style.display = 'none'; this.overlay.classList.add(`${cssPrefix}-overlay`); } canReceiveInputInReadOnlyEditor() { return true; } getMatches(searchFor) { const lowerSearchFor = searchFor.toLocaleLowerCase(); const matchingComponents = this.editor.image.getAllComponents().filter((component) => { let text = ''; if (component instanceof TextComponent) { text = component.getText(); } else if (component instanceof ImageComponent) { text = component.getAltText() ?? ''; } else { return false; } const hasLowercaseMatch = text.toLocaleLowerCase().indexOf(lowerSearchFor) !== -1; const hasSameCaseMatch = text.indexOf(searchFor) !== -1; return hasLowercaseMatch || hasSameCaseMatch; }); return matchingComponents.map((match) => match.getBBox()); } focusCurrentMatch() { const matches = this.getMatches(this.searchInput.value); let matchIdx = this.currentMatchIdx % matches.length; if (matchIdx < 0) { matchIdx = matches.length + matchIdx; } if (matchIdx < matches.length) { const undoable = false; void this.editor.dispatch(this.editor.viewport.zoomTo(matches[matchIdx], true, true), undoable); this.editor.announceForAccessibility(this.editor.localization.focusedFoundText(matchIdx + 1, matches.length)); } } toNextMatch() { this.currentMatchIdx++; this.focusCurrentMatch(); } toPrevMatch() { this.currentMatchIdx--; this.focusCurrentMatch(); } fillOverlay() { const label = document.createElement('label'); this.searchInput = document.createElement('input'); const nextBtn = document.createElement('button'); const closeBtn = document.createElement('button'); // Math.random() ensures that the ID is unique (to allow us to refer to it // with an htmlFor). this.searchInput.setAttribute('id', `${cssPrefix}-searchInput-${Math.random()}`); label.htmlFor = this.searchInput.getAttribute('id'); label.innerText = this.editor.localization.findLabel; nextBtn.innerText = this.editor.localization.toNextMatch; closeBtn.innerText = this.editor.localization.closeDialog; this.searchInput.onkeydown = (ev) => { if (ev.key === 'Enter') { if (ev.shiftKey) { this.toPrevMatch(); } else { this.toNextMatch(); } } else if (ev.key === 'Escape') { this.setVisible(false); } else if (this.editor.shortcuts.matchesShortcut(toggleFindVisibleShortcutId, ev)) { ev.preventDefault(); this.toggleVisible(); } }; nextBtn.onclick = () => { this.toNextMatch(); }; closeBtn.onclick = () => { this.setVisible(false); }; this.overlay.replaceChildren(label, this.searchInput, nextBtn, closeBtn); } isVisible() { return this.overlay.style.display !== 'none'; } setVisible(visible) { if (visible !== this.isVisible()) { this.overlay.style.display = visible ? 'block' : 'none'; if (visible) { this.searchInput.focus(); this.editor.announceForAccessibility(this.editor.localization.findDialogShown); } else { this.editor.focus(); this.editor.announceForAccessibility(this.editor.localization.findDialogHidden); } } } toggleVisible() { this.setVisible(!this.isVisible()); } onKeyPress(event) { if (this.editor.shortcuts.matchesShortcut(toggleFindVisibleShortcutId, event)) { this.toggleVisible(); return true; } return false; } setEnabled(enabled) { super.setEnabled(enabled); if (this.isEnabled()) { this.setVisible(false); } } }