187 lines
5.5 KiB
TypeScript
187 lines
5.5 KiB
TypeScript
import { customElement, property, html, type TemplateResult, css, cssManager } from '@design.estate/dees-element';
|
||
import * as domtools from '@design.estate/dees-domtools';
|
||
import { DeesInputBase } from './dees-input-base.js';
|
||
import { demoFunc } from './dees-input-quantityselector.demo.js';
|
||
|
||
declare global {
|
||
interface HTMLElementTagNameMap {
|
||
'dees-input-quantityselector': DeesInputQuantitySelector;
|
||
}
|
||
}
|
||
|
||
@customElement('dees-input-quantityselector')
|
||
export class DeesInputQuantitySelector extends DeesInputBase<DeesInputQuantitySelector> {
|
||
public static demo = demoFunc;
|
||
|
||
// INSTANCE
|
||
|
||
@property({
|
||
type: Number
|
||
})
|
||
public value: number = 1;
|
||
|
||
|
||
|
||
public static styles = [
|
||
...DeesInputBase.baseStyles,
|
||
cssManager.defaultStyles,
|
||
css`
|
||
:host {
|
||
width: auto;
|
||
user-select: none;
|
||
}
|
||
|
||
.quantity-container {
|
||
transition: all 0.15s ease;
|
||
font-size: 14px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
background: transparent;
|
||
height: 40px;
|
||
padding: 0;
|
||
min-width: 120px;
|
||
color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')};
|
||
border: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.quantity-container.disabled {
|
||
background: ${cssManager.bdTheme('hsl(0 0% 95.1%)', 'hsl(0 0% 14.9%)')};
|
||
border-color: ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||
opacity: 0.5;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.quantity-container:hover:not(.disabled) {
|
||
border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
|
||
}
|
||
|
||
.quantity-container:focus-within {
|
||
border-color: ${cssManager.bdTheme('hsl(222.2 47.4% 51.2%)', 'hsl(217.2 91.2% 59.8%)')};
|
||
box-shadow: 0 0 0 3px ${cssManager.bdTheme('hsl(222.2 47.4% 51.2% / 0.1)', 'hsl(217.2 91.2% 59.8% / 0.1)')};
|
||
}
|
||
|
||
.selector {
|
||
flex: 0 0 40px;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.15s ease;
|
||
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
|
||
position: relative;
|
||
}
|
||
|
||
.selector:hover {
|
||
background: ${cssManager.bdTheme('hsl(0 0% 95.1%)', 'hsl(0 0% 14.9%)')};
|
||
color: ${cssManager.bdTheme('hsl(0 0% 9%)', 'hsl(0 0% 95%)')};
|
||
}
|
||
|
||
.selector:active {
|
||
background: ${cssManager.bdTheme('hsl(0 0% 91%)', 'hsl(0 0% 11%)')};
|
||
}
|
||
|
||
.selector.minus {
|
||
border-right: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||
}
|
||
|
||
.selector.plus {
|
||
border-left: 1px solid ${cssManager.bdTheme('hsl(0 0% 89.8%)', 'hsl(0 0% 14.9%)')};
|
||
}
|
||
|
||
.quantity {
|
||
flex: 1;
|
||
text-align: center;
|
||
font-weight: 500;
|
||
font-variant-numeric: tabular-nums;
|
||
letter-spacing: -0.006em;
|
||
}
|
||
|
||
/* Keyboard navigation focus styles */
|
||
.selector:focus {
|
||
outline: none;
|
||
background: ${cssManager.bdTheme('hsl(210 40% 96.1%)', 'hsl(215 20.2% 16.8%)')};
|
||
z-index: 1;
|
||
}
|
||
|
||
/* Min value state */
|
||
.quantity-container[data-min="true"] .selector.minus {
|
||
opacity: 0.3;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.quantity-container[data-min="true"] .selector.minus:hover {
|
||
background: transparent;
|
||
color: ${cssManager.bdTheme('hsl(215.4 16.3% 56.9%)', 'hsl(215 20.2% 55.1%)')};
|
||
}
|
||
|
||
`,
|
||
];
|
||
|
||
public render(): TemplateResult {
|
||
return html`
|
||
<div class="input-wrapper">
|
||
${this.label ? html`<dees-label .label=${this.label} .description=${this.description} .required=${this.required}></dees-label>` : ''}
|
||
<div
|
||
class="quantity-container ${this.disabled ? 'disabled' : ''}"
|
||
data-min="${this.value <= 0}"
|
||
>
|
||
<div
|
||
class="selector minus"
|
||
@click="${() => {this.decrease();}}"
|
||
tabindex="${this.disabled ? '-1' : '0'}"
|
||
@keydown="${(e: KeyboardEvent) => {
|
||
if (e.key === 'Enter' || e.key === ' ') {
|
||
e.preventDefault();
|
||
this.decrease();
|
||
}
|
||
}}"
|
||
role="button"
|
||
aria-label="Decrease quantity"
|
||
>−</div>
|
||
<div class="quantity" aria-live="polite" aria-atomic="true">${this.value}</div>
|
||
<div
|
||
class="selector plus"
|
||
@click="${() => {this.increase();}}"
|
||
tabindex="${this.disabled ? '-1' : '0'}"
|
||
@keydown="${(e: KeyboardEvent) => {
|
||
if (e.key === 'Enter' || e.key === ' ') {
|
||
e.preventDefault();
|
||
this.increase();
|
||
}
|
||
}}"
|
||
role="button"
|
||
aria-label="Increase quantity"
|
||
>+</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
public increase() {
|
||
if (!this.disabled) {
|
||
this.value++;
|
||
this.changeSubject.next(this);
|
||
}
|
||
}
|
||
|
||
public decrease() {
|
||
if (!this.disabled && this.value > 0) {
|
||
this.value--;
|
||
this.changeSubject.next(this);
|
||
}
|
||
}
|
||
|
||
public getValue(): number {
|
||
return this.value;
|
||
}
|
||
|
||
public setValue(value: number): void {
|
||
this.value = value;
|
||
}
|
||
}
|