Skip to content

Commit bcce368

Browse files
committed
Add Number component
1 parent 06185f1 commit bcce368

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

src/components/Number.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { css, html, LitElement } from "lit";
2+
import { customElement, property } from "lit/decorators.js";
3+
import { ifDefined } from "lit/directives/if-defined.js";
4+
import { ref } from "lit/directives/ref.js";
5+
6+
import { LocalizedMessage, localizedMessagePropertyOptions } from "../core/";
7+
8+
import { Focusable, Input, Persisted } from "../mixins";
9+
10+
@customElement("sdpi-number")
11+
export class NumberElement extends Persisted(Focusable(Input<typeof LitElement, number>(LitElement))) {
12+
/** @inheritdoc */
13+
public static get styles() {
14+
return [
15+
...super.styles,
16+
css`
17+
input {
18+
background-color: var(--input-bg-color);
19+
padding: calc(var(--spacer) + 3px) var(--spacer);
20+
flex: 1;
21+
min-width: unset;
22+
max-width: unset;
23+
}
24+
25+
::-webkit-inner-spin-button {
26+
-webkit-appearance: none;
27+
}
28+
29+
input:disabled {
30+
opacity: var(--opacity-disabled);
31+
}
32+
33+
.number-container {
34+
display: flex;
35+
width: 100%;
36+
}
37+
`
38+
];
39+
}
40+
41+
/**
42+
* The maximum value.
43+
*/
44+
@property({ type: Number })
45+
public max?: number;
46+
47+
/**
48+
* The minimum value.
49+
*/
50+
@property({ type: Number })
51+
public min?: number;
52+
53+
/**
54+
* Specifies the granularity that the value must adhere to.
55+
*/
56+
@property({ type: Number })
57+
public step?: number;
58+
59+
/**
60+
* Specifies the placeholder
61+
*/
62+
@property(localizedMessagePropertyOptions)
63+
public placeholder?: LocalizedMessage;
64+
65+
/**
66+
* When specified, the user input will be clamped to the maximum and maximum values provided
67+
*/
68+
@property({
69+
attribute: "clamp",
70+
type: Boolean,
71+
})
72+
public clamp = false;
73+
74+
/** @inheritdoc */
75+
protected delaySave = true;
76+
77+
/** @inheritdoc */
78+
protected render() {
79+
const value = this.value?.toString() || this.defaultValue?.toString() || "";
80+
return html`
81+
<label class="number-container">
82+
<input
83+
${ref(this.focusElement)}
84+
type="number"
85+
max=${ifDefined(this.max)}
86+
min=${ifDefined(this.min)}
87+
step=${ifDefined(this.step)}
88+
placeholder=${ifDefined(this.placeholder)}
89+
.disabled=${this.disabled}
90+
.value=${value}
91+
@change=${(ev: HTMLInputEvent<HTMLInputElement>) => (this.setValue(ev))}
92+
/>
93+
<slot name="suffix"></slot>
94+
</label>`;
95+
}
96+
97+
private setValue(ev: HTMLInputEvent<HTMLInputElement>): void {
98+
let value = ev.target.valueAsNumber;
99+
if (Number.isNaN(value)) {
100+
// No value provided
101+
this.value = undefined;
102+
return
103+
}
104+
105+
// Constrain value to min and max if provided
106+
const min = this.clamp ? this.min : undefined;
107+
const max = this.clamp ? this.max : undefined;
108+
if (max != undefined) {
109+
value = Math.min(value, max);
110+
}
111+
if (min != undefined) {
112+
value = Math.max(value, min);
113+
}
114+
115+
// Force step size
116+
if (this.step != undefined) {
117+
// This matches the native step size on the number type input
118+
const stepStart = this.min ?? 0.0;
119+
value = Math.round((value - stepStart) / this.step) * this.step + stepStart;
120+
}
121+
122+
this.value = value;
123+
ev.target.value = String(this.value);
124+
}
125+
}
126+
127+
declare global {
128+
interface HTMLElementTagNameMap {
129+
"sdpi-number": NumberElement;
130+
}
131+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import "./components/color";
77
import "./components/delegate";
88
import "./components/file";
99
import "./components/i18n";
10+
import "./components/number";
1011
import "./components/password";
1112
import "./components/radio";
1213
import "./components/range";

0 commit comments

Comments
 (0)