wired-elements
Version:
Collection of hand-drawn sketchy web components
160 lines (141 loc) • 4.79 kB
text/typescript
import { WiredBase, BaseCSS, Point } from './wired-base';
import { rectangle, line, svgNode, ellipse } from './wired-lib';
import { css, TemplateResult, html, CSSResultArray } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
('wired-search-input')
export class WiredSearchInput extends WiredBase {
({ type: Boolean, reflect: true }) disabled = false;
({ type: String }) placeholder = '';
({ type: String }) autocomplete = '';
({ type: String }) autocorrect = '';
({ type: Boolean }) autofocus = false;
('input') private textInput?: HTMLInputElement;
private pendingValue?: string;
private searchIcon?: SVGElement;
private closeIcon?: SVGElement;
static get styles(): CSSResultArray {
return [
BaseCSS,
css`
:host {
display: inline-block;
position: relative;
padding: 10px 40px 10px 5px;
font-family: sans-serif;
width: 180px;
outline: none;
}
:host([disabled]) {
opacity: 0.6 ;
cursor: default;
pointer-events: none;
}
:host([disabled]) svg {
background: rgba(0, 0, 0, 0.07);
}
input {
display: block;
width: 100%;
box-sizing: border-box;
outline: none;
border: none;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
color: inherit;
padding: 6px;
}
input[type=search]::-ms-clear { display: none; width : 0; height: 0; }
input[type=search]::-ms-reveal { display: none; width : 0; height: 0; }
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button,
input[type="search"]::-webkit-search-results-decoration {
display: none;
}
.thicker path {
stroke-width: 1.5;
}
button {
position: absolute;
top: 0;
right: 2px;
width: 32px;
height: 100%;
box-sizing: border-box;
background: none;
border: none;
cursor: pointer;
outline: none;
opacity: 0;
}
`
];
}
render(): TemplateResult {
return html`
<input type="search" placeholder="${this.placeholder}" ?disabled="${this.disabled}"
autocomplete="${this.autocomplete}" ?autofocus="${this.autofocus}"
autocapitalize="${this.autocapitalize}" autocorrect="${this.autocorrect}"
@change="${this.refire}" @input="${this.refire}">
<div id="overlay">
<svg></svg>
</div>
<button @click="${() => this.value = ''}"></button>
`;
}
get input(): HTMLInputElement | undefined {
return this.textInput;
}
get value(): string {
const input = this.input;
return (input && input.value) || '';
}
set value(v: string) {
if (this.shadowRoot) {
const input = this.input;
if (input) {
input.value = v;
}
this.refreshIconState();
} else {
this.pendingValue = v;
}
}
wiredRender(force = false) {
super.wiredRender(force);
this.refreshIconState();
}
firstUpdated() {
this.value = this.pendingValue || this.value || this.getAttribute('value') || '';
delete this.pendingValue;
}
protected canvasSize(): Point {
const s = this.getBoundingClientRect();
return [s.width, s.height];
}
protected draw(svg: SVGSVGElement, size: Point) {
rectangle(svg, 2, 2, size[0] - 2, size[1] - 2, this.seed);
this.searchIcon = svgNode('g');
this.searchIcon.classList.add('thicker');
svg.appendChild(this.searchIcon);
ellipse(this.searchIcon, size[0] - 30, (size[1] - 30) / 2 + 10, 20, 20, this.seed);
line(this.searchIcon, size[0] - 10, (size[1] - 30) / 2 + 30, size[0] - 25, (size[1] - 30) / 2 + 15, this.seed);
this.closeIcon = svgNode('g');
this.closeIcon.classList.add('thicker');
svg.appendChild(this.closeIcon);
line(this.closeIcon, size[0] - 33, (size[1] - 30) / 2 + 2, size[0] - 7, (size[1] - 30) / 2 + 28, this.seed);
line(this.closeIcon, size[0] - 7, (size[1] - 30) / 2 + 2, size[0] - 33, (size[1] - 30) / 2 + 28, this.seed);
}
private refreshIconState() {
if (this.searchIcon && this.closeIcon) {
this.searchIcon.style.display = this.value.trim() ? 'none' : '';
this.closeIcon.style.display = this.value.trim() ? '' : 'none';
}
}
private refire(event: Event) {
this.refreshIconState();
event.stopPropagation();
this.fire(event.type, { sourceEvent: event });
}
}