@lit-roa/cantidad 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,35 @@
1
- # Temporary Holding Version
1
+ # @lit-roa/cantidad
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Selector de cantidad con botones − y +. También se puede escribir el
4
+ número o usar las flechas ↑ ↓. Respeta `min` y `max` (por ejemplo, las
5
+ existencias) y funciona dentro de un `<form>` con `name`.
6
+
7
+ ```bash
8
+ npm install @lit-roa/cantidad
9
+ ```
10
+
11
+ ```html
12
+ <lr-cantidad valor="1" max="5"></lr-cantidad>
13
+ <script>
14
+ document.querySelector('lr-cantidad').addEventListener('cambio', (e) => console.log(e.detail.valor));
15
+ </script>
16
+ ```
17
+
18
+ | Atributo | Tipo | Por omisión |
19
+ | --- | --- | --- |
20
+ | `valor` | `number` | `1` |
21
+ | `min` | `number` | `1` |
22
+ | `max` | `number` | sin límite |
23
+ | `paso` | `number` | `1` |
24
+ | `name` | `string` | `''` |
25
+ | `deshabilitado` | `boolean` | `false` |
26
+ | `tamano` | `chico` · `mediano` | `mediano` |
27
+ | `etiqueta` | `string` (texto accesible) | `'Cantidad'` |
28
+
29
+ Evento `cambio` con `{ valor }`. Estilo: `--lr-cantidad-borde`.
30
+
31
+ ## Desarrollo
32
+
33
+ ```bash
34
+ npm run dev -w @lit-roa/cantidad
35
+ ```
@@ -0,0 +1,37 @@
1
+ import { LitElement } from 'lit';
2
+ export interface DetalleCambioCantidad {
3
+ valor: number;
4
+ }
5
+ /**
6
+ * Selector de cantidad con botones − y +. También se puede escribir el
7
+ * número. Participa en formularios con `name`.
8
+ *
9
+ * @fires cambio - Cuando cambia la cantidad. `detail`: `{ valor }`.
10
+ */
11
+ export declare class LrCantidad extends LitElement {
12
+ static formAssociated: boolean;
13
+ static styles: import("lit").CSSResult[];
14
+ valor: number;
15
+ min: number;
16
+ /** Máximo (por ejemplo, las existencias). Sin límite si no se indica. */
17
+ max?: number;
18
+ paso: number;
19
+ name: string;
20
+ deshabilitado: boolean;
21
+ tamano: 'chico' | 'mediano';
22
+ /** Texto accesible del campo. */
23
+ etiqueta: string;
24
+ private internos;
25
+ render(): import("lit-html").TemplateResult<1>;
26
+ protected updated(): void;
27
+ private limitar;
28
+ private fijar;
29
+ private alEscribir;
30
+ private alTeclear;
31
+ }
32
+ declare global {
33
+ interface HTMLElementTagNameMap {
34
+ 'lr-cantidad': LrCantidad;
35
+ }
36
+ }
37
+ //# sourceMappingURL=cantidad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cantidad.d.ts","sourceRoot":"","sources":["../src/cantidad.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,qBACa,UAAW,SAAQ,UAAU;IACxC,MAAM,CAAC,cAAc,UAAQ;IAE7B,OAAgB,MAAM,4BA+DpB;IAE0B,KAAK,SAAK;IAEV,GAAG,SAAK;IAEpC,yEAAyE;IAC7C,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,IAAI,SAAK;IAEzB,IAAI,SAAM;IAEsB,aAAa,UAAS;IAErC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAa;IAErE,iCAAiC;IACrB,QAAQ,SAAc;IAElC,OAAO,CAAC,QAAQ,CAA0B;IAEjC,MAAM;cAmCI,OAAO;IAI1B,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,KAAK;IAUb,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,SAAS;CAMlB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,aAAa,EAAE,UAAU,CAAC;KAC3B;CACF"}
@@ -0,0 +1,186 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { LitElement, css, html } from 'lit';
8
+ import { customElement, property } from 'lit/decorators.js';
9
+ import { emitir, estilosBase, iconoMinus, iconoPlus, tokens } from '@lit-roa/base';
10
+ /**
11
+ * Selector de cantidad con botones − y +. También se puede escribir el
12
+ * número. Participa en formularios con `name`.
13
+ *
14
+ * @fires cambio - Cuando cambia la cantidad. `detail`: `{ valor }`.
15
+ */
16
+ let LrCantidad = class LrCantidad extends LitElement {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.valor = 1;
20
+ this.min = 1;
21
+ this.paso = 1;
22
+ this.name = '';
23
+ this.deshabilitado = false;
24
+ this.tamano = 'mediano';
25
+ /** Texto accesible del campo. */
26
+ this.etiqueta = 'Cantidad';
27
+ this.internos = this.attachInternals();
28
+ }
29
+ static { this.formAssociated = true; }
30
+ static { this.styles = [
31
+ tokens,
32
+ estilosBase,
33
+ css `
34
+ :host {
35
+ display: inline-flex;
36
+ --_alto: 2.5rem;
37
+ }
38
+ :host([tamano='chico']) {
39
+ --_alto: 2rem;
40
+ }
41
+ .caja {
42
+ display: inline-flex;
43
+ align-items: stretch;
44
+ height: var(--_alto);
45
+ border: 1.5px solid var(--lr-cantidad-borde, #d1d5db);
46
+ border-radius: calc(var(--_radio) / 1.5);
47
+ background: var(--_color-fondo);
48
+ overflow: hidden;
49
+ }
50
+ .caja:focus-within {
51
+ border-color: var(--_color-primario);
52
+ }
53
+ button {
54
+ all: unset;
55
+ display: grid;
56
+ place-items: center;
57
+ width: var(--_alto);
58
+ font-size: 1rem;
59
+ color: var(--_color-texto);
60
+ cursor: pointer;
61
+ }
62
+ button:hover:not(:disabled) {
63
+ background: #f3f4f6;
64
+ }
65
+ button:disabled {
66
+ color: #d1d5db;
67
+ cursor: not-allowed;
68
+ }
69
+ button:focus-visible {
70
+ outline: 2px solid var(--_color-primario);
71
+ outline-offset: -2px;
72
+ }
73
+ input {
74
+ width: 2.75rem;
75
+ border: 0;
76
+ outline: 0;
77
+ background: transparent;
78
+ color: inherit;
79
+ font: inherit;
80
+ font-weight: 600;
81
+ text-align: center;
82
+ -moz-appearance: textfield;
83
+ }
84
+ input::-webkit-inner-spin-button,
85
+ input::-webkit-outer-spin-button {
86
+ -webkit-appearance: none;
87
+ margin: 0;
88
+ }
89
+ :host([deshabilitado]) .caja {
90
+ opacity: 0.55;
91
+ }
92
+ `,
93
+ ]; }
94
+ render() {
95
+ const enMinimo = this.valor <= this.min;
96
+ const enMaximo = this.max !== undefined && this.valor >= this.max;
97
+ return html `<div class="caja" role="group" aria-label=${this.etiqueta}>
98
+ <button
99
+ type="button"
100
+ aria-label="Quitar uno"
101
+ ?disabled=${this.deshabilitado || enMinimo}
102
+ @click=${() => this.fijar(this.valor - this.paso)}
103
+ >
104
+ ${iconoMinus}
105
+ </button>
106
+ <input
107
+ type="number"
108
+ inputmode="numeric"
109
+ aria-label=${this.etiqueta}
110
+ .value=${String(this.valor)}
111
+ min=${this.min}
112
+ max=${this.max ?? ''}
113
+ step=${this.paso}
114
+ ?disabled=${this.deshabilitado}
115
+ @change=${this.alEscribir}
116
+ @keydown=${this.alTeclear}
117
+ />
118
+ <button
119
+ type="button"
120
+ aria-label="Agregar uno"
121
+ ?disabled=${this.deshabilitado || enMaximo}
122
+ @click=${() => this.fijar(this.valor + this.paso)}
123
+ >
124
+ ${iconoPlus}
125
+ </button>
126
+ </div>`;
127
+ }
128
+ updated() {
129
+ this.internos.setFormValue(String(this.valor));
130
+ }
131
+ limitar(n) {
132
+ if (Number.isNaN(n))
133
+ return this.min;
134
+ const conMax = this.max !== undefined ? Math.min(this.max, n) : n;
135
+ return Math.max(this.min, Math.round(conMax));
136
+ }
137
+ fijar(n) {
138
+ const valor = this.limitar(n);
139
+ if (valor === this.valor) {
140
+ this.requestUpdate(); // regresa el input al valor válido
141
+ return;
142
+ }
143
+ this.valor = valor;
144
+ emitir(this, 'cambio', { valor });
145
+ }
146
+ alEscribir(evento) {
147
+ const input = evento.target;
148
+ this.fijar(Number(input.value));
149
+ input.value = String(this.valor);
150
+ }
151
+ alTeclear(evento) {
152
+ if (evento.key === 'ArrowUp' || evento.key === 'ArrowDown') {
153
+ evento.preventDefault();
154
+ this.fijar(this.valor + (evento.key === 'ArrowUp' ? this.paso : -this.paso));
155
+ }
156
+ }
157
+ };
158
+ __decorate([
159
+ property({ type: Number })
160
+ ], LrCantidad.prototype, "valor", void 0);
161
+ __decorate([
162
+ property({ type: Number })
163
+ ], LrCantidad.prototype, "min", void 0);
164
+ __decorate([
165
+ property({ type: Number })
166
+ ], LrCantidad.prototype, "max", void 0);
167
+ __decorate([
168
+ property({ type: Number })
169
+ ], LrCantidad.prototype, "paso", void 0);
170
+ __decorate([
171
+ property()
172
+ ], LrCantidad.prototype, "name", void 0);
173
+ __decorate([
174
+ property({ type: Boolean, reflect: true })
175
+ ], LrCantidad.prototype, "deshabilitado", void 0);
176
+ __decorate([
177
+ property({ reflect: true })
178
+ ], LrCantidad.prototype, "tamano", void 0);
179
+ __decorate([
180
+ property()
181
+ ], LrCantidad.prototype, "etiqueta", void 0);
182
+ LrCantidad = __decorate([
183
+ customElement('lr-cantidad')
184
+ ], LrCantidad);
185
+ export { LrCantidad };
186
+ //# sourceMappingURL=cantidad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cantidad.js","sourceRoot":"","sources":["../src/cantidad.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export { LrCantidad, type DetalleCambioCantidad } from './cantidad.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { LrCantidad } from './cantidad.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA8B,MAAM,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,30 @@
1
1
  {
2
2
  "name": "@lit-roa/cantidad",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "Selector de cantidad con botones − y +.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
18
+ "sideEffects": [
19
+ "./dist/index.js"
20
+ ],
21
+ "scripts": {
22
+ "dev": "vite",
23
+ "build": "tsc -b"
24
+ },
25
+ "dependencies": {
26
+ "@lit-roa/base": "^0.5.0",
27
+ "lit": "^3.3.3"
28
+ },
29
+ "license": "MIT"
30
+ }
@@ -0,0 +1,177 @@
1
+ import { LitElement, css, html } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { emitir, estilosBase, iconoMinus, iconoPlus, tokens } from '@lit-roa/base';
4
+
5
+ export interface DetalleCambioCantidad {
6
+ valor: number;
7
+ }
8
+
9
+ /**
10
+ * Selector de cantidad con botones − y +. También se puede escribir el
11
+ * número. Participa en formularios con `name`.
12
+ *
13
+ * @fires cambio - Cuando cambia la cantidad. `detail`: `{ valor }`.
14
+ */
15
+ @customElement('lr-cantidad')
16
+ export class LrCantidad extends LitElement {
17
+ static formAssociated = true;
18
+
19
+ static override styles = [
20
+ tokens,
21
+ estilosBase,
22
+ css`
23
+ :host {
24
+ display: inline-flex;
25
+ --_alto: 2.5rem;
26
+ }
27
+ :host([tamano='chico']) {
28
+ --_alto: 2rem;
29
+ }
30
+ .caja {
31
+ display: inline-flex;
32
+ align-items: stretch;
33
+ height: var(--_alto);
34
+ border: 1.5px solid var(--lr-cantidad-borde, #d1d5db);
35
+ border-radius: calc(var(--_radio) / 1.5);
36
+ background: var(--_color-fondo);
37
+ overflow: hidden;
38
+ }
39
+ .caja:focus-within {
40
+ border-color: var(--_color-primario);
41
+ }
42
+ button {
43
+ all: unset;
44
+ display: grid;
45
+ place-items: center;
46
+ width: var(--_alto);
47
+ font-size: 1rem;
48
+ color: var(--_color-texto);
49
+ cursor: pointer;
50
+ }
51
+ button:hover:not(:disabled) {
52
+ background: #f3f4f6;
53
+ }
54
+ button:disabled {
55
+ color: #d1d5db;
56
+ cursor: not-allowed;
57
+ }
58
+ button:focus-visible {
59
+ outline: 2px solid var(--_color-primario);
60
+ outline-offset: -2px;
61
+ }
62
+ input {
63
+ width: 2.75rem;
64
+ border: 0;
65
+ outline: 0;
66
+ background: transparent;
67
+ color: inherit;
68
+ font: inherit;
69
+ font-weight: 600;
70
+ text-align: center;
71
+ -moz-appearance: textfield;
72
+ }
73
+ input::-webkit-inner-spin-button,
74
+ input::-webkit-outer-spin-button {
75
+ -webkit-appearance: none;
76
+ margin: 0;
77
+ }
78
+ :host([deshabilitado]) .caja {
79
+ opacity: 0.55;
80
+ }
81
+ `,
82
+ ];
83
+
84
+ @property({ type: Number }) valor = 1;
85
+
86
+ @property({ type: Number }) min = 1;
87
+
88
+ /** Máximo (por ejemplo, las existencias). Sin límite si no se indica. */
89
+ @property({ type: Number }) max?: number;
90
+
91
+ @property({ type: Number }) paso = 1;
92
+
93
+ @property() name = '';
94
+
95
+ @property({ type: Boolean, reflect: true }) deshabilitado = false;
96
+
97
+ @property({ reflect: true }) tamano: 'chico' | 'mediano' = 'mediano';
98
+
99
+ /** Texto accesible del campo. */
100
+ @property() etiqueta = 'Cantidad';
101
+
102
+ private internos = this.attachInternals();
103
+
104
+ override render() {
105
+ const enMinimo = this.valor <= this.min;
106
+ const enMaximo = this.max !== undefined && this.valor >= this.max;
107
+ return html`<div class="caja" role="group" aria-label=${this.etiqueta}>
108
+ <button
109
+ type="button"
110
+ aria-label="Quitar uno"
111
+ ?disabled=${this.deshabilitado || enMinimo}
112
+ @click=${() => this.fijar(this.valor - this.paso)}
113
+ >
114
+ ${iconoMinus}
115
+ </button>
116
+ <input
117
+ type="number"
118
+ inputmode="numeric"
119
+ aria-label=${this.etiqueta}
120
+ .value=${String(this.valor)}
121
+ min=${this.min}
122
+ max=${this.max ?? ''}
123
+ step=${this.paso}
124
+ ?disabled=${this.deshabilitado}
125
+ @change=${this.alEscribir}
126
+ @keydown=${this.alTeclear}
127
+ />
128
+ <button
129
+ type="button"
130
+ aria-label="Agregar uno"
131
+ ?disabled=${this.deshabilitado || enMaximo}
132
+ @click=${() => this.fijar(this.valor + this.paso)}
133
+ >
134
+ ${iconoPlus}
135
+ </button>
136
+ </div>`;
137
+ }
138
+
139
+ protected override updated() {
140
+ this.internos.setFormValue(String(this.valor));
141
+ }
142
+
143
+ private limitar(n: number) {
144
+ if (Number.isNaN(n)) return this.min;
145
+ const conMax = this.max !== undefined ? Math.min(this.max, n) : n;
146
+ return Math.max(this.min, Math.round(conMax));
147
+ }
148
+
149
+ private fijar(n: number) {
150
+ const valor = this.limitar(n);
151
+ if (valor === this.valor) {
152
+ this.requestUpdate(); // regresa el input al valor válido
153
+ return;
154
+ }
155
+ this.valor = valor;
156
+ emitir<DetalleCambioCantidad>(this, 'cambio', { valor });
157
+ }
158
+
159
+ private alEscribir(evento: Event) {
160
+ const input = evento.target as HTMLInputElement;
161
+ this.fijar(Number(input.value));
162
+ input.value = String(this.valor);
163
+ }
164
+
165
+ private alTeclear(evento: KeyboardEvent) {
166
+ if (evento.key === 'ArrowUp' || evento.key === 'ArrowDown') {
167
+ evento.preventDefault();
168
+ this.fijar(this.valor + (evento.key === 'ArrowUp' ? this.paso : -this.paso));
169
+ }
170
+ }
171
+ }
172
+
173
+ declare global {
174
+ interface HTMLElementTagNameMap {
175
+ 'lr-cantidad': LrCantidad;
176
+ }
177
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { LrCantidad, type DetalleCambioCantidad } from './cantidad.js';