Skip to content

Commit

Permalink
Adds helper for graph search filters
Browse files Browse the repository at this point in the history
  • Loading branch information
d13 committed Oct 7, 2022
1 parent fe0b6b0 commit 858f5b8
Showing 1 changed file with 141 additions and 16 deletions.
157 changes: 141 additions & 16 deletions src/webviews/apps/shared/components/search/search-input.ts
Expand Up @@ -5,8 +5,13 @@ import '../codicon';
// match case is disabled unless regex is true
const template = html<SearchInput>`
<template role="search">
<label htmlFor="search">
<label
for="search"
class="${x => (x.showHelp ? 'has-helper' : '')}"
@click="${(x, c) => x.handleShowHelper(c.event)}"
>
<code-icon icon="search" aria-label="${x => x.label}" title="${x => x.label}"></code-icon>
<code-icon class="icon-small" icon="chevron-down" aria-hidden="true"></code-icon>
</label>
<div class="field">
<input
Expand All @@ -21,31 +26,36 @@ const template = html<SearchInput>`
aria-describedby="${x => (!x.errorMessage ? '' : 'error')}"
@input="${(x, c) => x.handleInput(c.event)}"
@keydown="${(x, c) => x.handleShortcutKeys(c.event as KeyboardEvent)}"
@focus="${(x, c) => x.handleFocus(c.event)}"
/>
<div class="message" id="error" aria-live="polite">${x => x.errorMessage}</div>
</div>
<div class="controls">
<button
class="clear-button${x => (x.value ? '' : ' clear-button__hidden')}"
class="control${x => (x.value ? '' : ' is-hidden')}"
type="button"
role="button"
aria-label="Clear"
title="Clear"
@click="${(x, c) => x.handleClear(c.event)}"
@focus="${(x, c) => x.handleFocus(c.event)}"
>
<code-icon icon="close"></code-icon>
</button>
<button
class="control"
type="button"
role="checkbox"
aria-label="Match All"
title="Match All"
aria-checked="${x => x.matchAll}"
@click="${(x, c) => x.handleMatchAll(c.event)}"
@focus="${(x, c) => x.handleFocus(c.event)}"
>
<code-icon icon="whole-word"></code-icon>
</button>
<button
class="control"
type="button"
role="checkbox"
aria-label="Match Case${x =>
Expand All @@ -55,20 +65,40 @@ const template = html<SearchInput>`
?disabled="${x => !x.matchRegex}"
aria-checked="${x => x.matchCaseOverride}"
@click="${(x, c) => x.handleMatchCase(c.event)}"
@focus="${(x, c) => x.handleFocus(c.event)}"
>
<code-icon icon="case-sensitive"></code-icon>
</button>
<button
class="control"
type="button"
role="checkbox"
aria-label="Use Regular Expression"
title="Use Regular Expression"
aria-checked="${x => x.matchRegex}"
@click="${(x, c) => x.handleMatchRegex(c.event)}"
@focus="${(x, c) => x.handleFocus(c.event)}"
>
<code-icon icon="regex"></code-icon>
</button>
</div>
<div class="helper" tabindex="-1" ${ref('helper')}>
<button class="helper-button" type="button" @click="${(x, c) => x.handleInsertToken('message:')}">
Search by Message <small>message: or =:</small>
</button>
<button class="helper-button" type="button" @click="${(x, c) => x.handleInsertToken('author:')}">
Search by Author <small>author: or @:</small>
</button>
<button class="helper-button" type="button" @click="${(x, c) => x.handleInsertToken('sha:')}">
Search by Commit SHA <small>sha: or #:</small>
</button>
<button class="helper-button" type="button" @click="${(x, c) => x.handleInsertToken('file:')}">
Search by File <small>file: or ?:</small>
</button>
<button class="helper-button" type="button" @click="${(x, c) => x.handleInsertToken('change:')}">
Search by Changes <small>change: or ~:</small>
</button>
</div>
</template>
`;

Expand All @@ -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 {
Expand Down Expand Up @@ -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({
Expand All @@ -203,6 +287,9 @@ const styles = css`
styles: styles,
})
export class SearchInput extends FASTElement {
@observable
showHelp = false;

@observable
errorMessage = '';

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 858f5b8

Please sign in to comment.