diff --git a/src/webviews/apps/shared/components/search/search-input.ts b/src/webviews/apps/shared/components/search/search-input.ts index 2ea0b32..58c5a86 100644 --- a/src/webviews/apps/shared/components/search/search-input.ts +++ b/src/webviews/apps/shared/components/search/search-input.ts @@ -5,8 +5,13 @@ import '../codicon'; // match case is disabled unless regex is true const template = html` `; @@ -81,14 +111,25 @@ const styles = css` display: inline-flex; flex-direction: row; align-items: center; - gap: 0.8rem; + gap: 0.4rem; position: relative; flex: auto 1 1; } label { + display: flex; + justify-content: center; + align-items: center; + gap: 0.2rem; + width: 3.2rem; + height: 2.4rem; color: var(--vscode-input-foreground); + cursor: pointer; + } + + .icon-small { + font-size: 1rem; } .field { @@ -158,43 +199,86 @@ const styles = css` } button { - display: inline-flex; - justify-content: center; - align-items: center; - width: 2rem; - height: 2rem; padding: 0; color: var(--vscode-input-foreground); border: 1px solid transparent; background: none; - text-align: center; - border-radius: 0.25rem; } - button[role='checkbox']:focus:not([disabled]) { + button:focus:not([disabled]) { outline: 1px solid var(--vscode-focusBorder); outline-offset: -1px; } button:not([disabled]) { cursor: pointer; } - button:hover:not([disabled]):not([aria-checked='true']) { + + .control { + display: inline-flex; + justify-content: center; + align-items: center; + width: 2rem; + height: 2rem; + text-align: center; + border-radius: 0.25rem; + } + .control:hover:not([disabled]):not([aria-checked='true']) { background-color: var(--vscode-inputOption-hoverBackground); } - button[disabled] { + .control[disabled] { opacity: 0.5; } - button[disabled][aria-checked='true'] { + .control[disabled][aria-checked='true'] { opacity: 0.8; } - button[aria-checked='true'] { + .control[aria-checked='true'] { background-color: var(--vscode-inputOption-activeBackground); color: var(--vscode-inputOption-activeForeground); border-color: var(--vscode-inputOption-activeBorder); } - .clear-button__hidden { + .control.is-hidden { display: none; } + + .has-helper { + background-color: var(--vscode-input-background); + border-radius: 0.3rem 0.3rem 0 0; + } + + .helper { + display: none; + position: absolute; + top: 100%; + left: 0; + z-index: 5000; + width: fit-content; + background-color: var(--vscode-input-background); + border-radius: 0 0.3rem 0.3rem 0.3rem; + outline: none; + } + .has-helper ~ .helper { + display: block; + } + + .helper-button { + display: block; + width: 100%; + padding: 0.3rem 0.6rem; + text-align: left; + } + .helper-button:hover { + background-color: var(--vscode-inputOption-hoverBackground); + } + .helper-button:first-child { + border-top-right-radius: 0.3rem; + } + .helper-button:last-child { + border-bottom-left-radius: 0.3rem; + border-bottom-right-radius: 0.3rem; + } + .helper-button small { + opacity: 0.5; + } `; @customElement({ @@ -204,6 +288,9 @@ const styles = css` }) export class SearchInput extends FASTElement { @observable + showHelp = false; + + @observable errorMessage = ''; @attr @@ -230,11 +317,35 @@ export class SearchInput extends FASTElement { } input!: HTMLInputElement; + helper!: HTMLElement; + + override connectedCallback() { + super.connectedCallback(); + document.addEventListener('click', this.handleDocumentClick.bind(this)); + } + + override disconnectedCallback() { + super.disconnectedCallback(); + document.removeEventListener('click', this.handleDocumentClick.bind(this)); + } override focus(options?: FocusOptions): void { this.input.focus(options); } + handleDocumentClick(e: MouseEvent) { + if (this.showHelp === false) return; + + const composedPath = e.composedPath(); + if (!composedPath.includes(this)) { + this.showHelp = false; + } + } + + handleFocus(_e: Event) { + this.showHelp = false; + } + handleClear(_e: Event) { this.value = ''; this.emitSearch(); @@ -273,6 +384,20 @@ export class SearchInput extends FASTElement { return false; } + handleShowHelper(_e: Event) { + this.showHelp = !this.showHelp; + if (this.showHelp) { + window.requestAnimationFrame(() => { + this.helper.focus(); + }); + } + } + + handleInsertToken(token: string) { + this.value += `${this.value.length > 0 ? ' ' : ''}${token}`; + this.input.focus(); + } + private emitSearch() { const search: SearchQuery = { query: this.value,