2025-06-19 11:39:16 +00:00
|
|
|
|
import { customElement, property, html, type TemplateResult, css, cssManager } from '@design.estate/dees-element';
|
2023-08-07 19:13:29 +02:00
|
|
|
|
import * as domtools from '@design.estate/dees-domtools';
|
2025-06-19 11:39:16 +00:00
|
|
|
|
import { DeesInputBase } from './dees-input-base.js';
|
|
|
|
|
import { demoFunc } from './dees-input-quantityselector.demo.js';
|
2020-09-13 16:24:48 +00:00
|
|
|
|
|
2021-03-06 15:48:02 +00:00
|
|
|
|
declare global {
|
|
|
|
|
interface HTMLElementTagNameMap {
|
|
|
|
|
'dees-input-quantityselector': DeesInputQuantitySelector;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-13 16:24:48 +00:00
|
|
|
|
@customElement('dees-input-quantityselector')
|
2025-06-19 11:39:16 +00:00
|
|
|
|
export class DeesInputQuantitySelector extends DeesInputBase<DeesInputQuantitySelector> {
|
|
|
|
|
public static demo = demoFunc;
|
2020-09-13 16:24:48 +00:00
|
|
|
|
|
2021-08-20 00:25:14 +02:00
|
|
|
|
// INSTANCE
|
2020-09-13 16:24:48 +00:00
|
|
|
|
|
|
|
|
|
@property({
|
|
|
|
|
type: Number
|
|
|
|
|
})
|
|
|
|
|
public value: number = 1;
|
|
|
|
|
|
2021-08-26 21:30:35 +02:00
|
|
|
|
|
2021-08-25 13:51:55 +02:00
|
|
|
|
|
2025-06-19 11:39:16 +00:00
|
|
|
|
public static styles = [
|
|
|
|
|
...DeesInputBase.baseStyles,
|
|
|
|
|
cssManager.defaultStyles,
|
|
|
|
|
css`
|
2020-09-13 16:24:48 +00:00
|
|
|
|
:host {
|
2025-06-19 11:39:16 +00:00
|
|
|
|
width: auto;
|
2020-09-13 16:24:48 +00:00
|
|
|
|
user-select: none;
|
|
|
|
|
}
|
2025-06-19 11:39:16 +00:00
|
|
|
|
|
|
|
|
|
.quantity-container {
|
2025-06-27 16:29:19 +00:00
|
|
|
|
transition: all 0.15s ease;
|
2020-09-13 16:24:48 +00:00
|
|
|
|
font-size: 14px;
|
2025-06-27 16:29:19 +00:00
|
|
|
|
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;
|
2020-12-09 23:05:13 +00:00
|
|
|
|
}
|
2025-06-19 11:39:16 +00:00
|
|
|
|
|
|
|
|
|
.quantity-container.disabled {
|
2025-06-27 16:29:19 +00:00
|
|
|
|
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%)')};
|
2025-06-19 11:39:16 +00:00
|
|
|
|
opacity: 0.5;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
2020-12-09 23:05:13 +00:00
|
|
|
|
|
2025-06-27 16:29:19 +00:00
|
|
|
|
.quantity-container:hover:not(.disabled) {
|
|
|
|
|
border-color: ${cssManager.bdTheme('hsl(0 0% 79.8%)', 'hsl(0 0% 20.9%)')};
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-27 16:29:19 +00:00
|
|
|
|
.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)')};
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selector {
|
2025-06-27 16:29:19 +00:00
|
|
|
|
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;
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.selector:hover {
|
2025-06-27 16:29:19 +00:00
|
|
|
|
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%)')};
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.quantity {
|
2025-06-27 16:29:19 +00:00
|
|
|
|
flex: 1;
|
2020-09-13 16:24:48 +00:00
|
|
|
|
text-align: center;
|
2025-06-27 16:29:19 +00:00
|
|
|
|
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%)')};
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 11:39:16 +00:00
|
|
|
|
`,
|
|
|
|
|
];
|
2020-09-13 16:24:48 +00:00
|
|
|
|
|
2025-06-19 11:39:16 +00:00
|
|
|
|
public render(): TemplateResult {
|
|
|
|
|
return html`
|
|
|
|
|
<div class="input-wrapper">
|
2025-06-27 16:29:19 +00:00
|
|
|
|
${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>
|
2025-06-19 11:39:16 +00:00
|
|
|
|
</div>
|
2020-09-13 16:24:48 +00:00
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 11:39:16 +00:00
|
|
|
|
public increase() {
|
|
|
|
|
if (!this.disabled) {
|
|
|
|
|
this.value++;
|
|
|
|
|
this.changeSubject.next(this);
|
|
|
|
|
}
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 11:39:16 +00:00
|
|
|
|
public decrease() {
|
|
|
|
|
if (!this.disabled && this.value > 0) {
|
2020-09-13 16:24:48 +00:00
|
|
|
|
this.value--;
|
2025-06-19 11:39:16 +00:00
|
|
|
|
this.changeSubject.next(this);
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
2025-06-19 11:39:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getValue(): number {
|
|
|
|
|
return this.value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public setValue(value: number): void {
|
|
|
|
|
this.value = value;
|
2020-09-13 16:24:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|