@lit-roa/mini-carrito 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 +64 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/mini-carrito.d.ts +43 -0
- package/dist/mini-carrito.d.ts.map +1 -0
- package/dist/mini-carrito.js +273 -0
- package/dist/mini-carrito.js.map +1 -0
- package/package.json +32 -4
- package/src/index.ts +1 -0
- package/src/mini-carrito.ts +256 -0
package/README.md
CHANGED
|
@@ -1,3 +1,65 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @lit-roa/mini-carrito
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Botón de carrito con contador (salta cuando sube) y panel lateral con lo
|
|
4
|
+
que pongas adentro: normalmente `<lr-linea-carrito compacta>` y, en el
|
|
5
|
+
pie, el subtotal y el botón de pagar. En celular el panel ocupa toda la
|
|
6
|
+
pantalla. Se cierra con la X, Esc o tocando el fondo.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @lit-roa/mini-carrito @lit-roa/linea-carrito
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```html
|
|
13
|
+
<lr-mini-carrito cantidad="3">
|
|
14
|
+
<lr-linea-carrito compacta id-producto="cafe" nombre="Café" precio="189" cantidad="2"></lr-linea-carrito>
|
|
15
|
+
<div slot="pie">
|
|
16
|
+
<p>Subtotal: $378.00</p>
|
|
17
|
+
<lr-boton bloque>Ir a pagar</lr-boton>
|
|
18
|
+
</div>
|
|
19
|
+
</lr-mini-carrito>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Si prefieres llevar a una página de carrito en lugar del panel, usa
|
|
23
|
+
`sin-panel` y escucha `abrir`.
|
|
24
|
+
|
|
25
|
+
## Propiedades y métodos
|
|
26
|
+
|
|
27
|
+
| Atributo | Tipo | Por omisión |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `cantidad` | `number` (piezas; 0 muestra el mensaje de vacío) | `0` |
|
|
30
|
+
| `abierto` | `boolean` | `false` |
|
|
31
|
+
| `titulo` | `string` | `'Tu carrito'` |
|
|
32
|
+
| `icono` | `bolsa` · `carrito` | `bolsa` |
|
|
33
|
+
| `sin-panel` | `boolean` | `false` |
|
|
34
|
+
| `texto-vacio` | `string` | `'Tu carrito está vacío.'` |
|
|
35
|
+
|
|
36
|
+
`abrir()` y `cerrar()`.
|
|
37
|
+
|
|
38
|
+
## Eventos
|
|
39
|
+
|
|
40
|
+
| Evento | Uso |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `abrir` | Al tocar el botón. Cancelable: `preventDefault()` no abre el panel. |
|
|
43
|
+
| `cerrar` | Al cerrar el panel. |
|
|
44
|
+
|
|
45
|
+
Los eventos de las líneas (`cambio-cantidad`, `quitar`…) también suben
|
|
46
|
+
hasta aquí.
|
|
47
|
+
|
|
48
|
+
## Slots
|
|
49
|
+
|
|
50
|
+
| Slot | Uso |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| (por omisión) | Contenido del panel |
|
|
53
|
+
| `pie` | Parte fija de abajo |
|
|
54
|
+
| `vacio` | Reemplaza el mensaje de carrito vacío |
|
|
55
|
+
| `icono` | Reemplaza el ícono del botón |
|
|
56
|
+
|
|
57
|
+
Estilos: `--lr-mini-carrito-ancho`, `--lr-mini-carrito-contador`,
|
|
58
|
+
`--lr-mini-carrito-pie`, `--lr-mini-carrito-hover`, y `::part(boton)`,
|
|
59
|
+
`::part(contador)`, `::part(panel)`.
|
|
60
|
+
|
|
61
|
+
## Desarrollo
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run dev -w @lit-roa/mini-carrito
|
|
65
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { LitElement, type PropertyValues } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Botón de carrito con contador y panel lateral. El panel muestra lo que
|
|
4
|
+
* pongas en el slot (por ejemplo `<lr-linea-carrito>`) y un pie fijo para el
|
|
5
|
+
* resumen y el botón de pagar.
|
|
6
|
+
*
|
|
7
|
+
* El botón abre el panel solo; si prefieres abrir tu propia página de
|
|
8
|
+
* carrito usa `sin-panel` y escucha `abrir`.
|
|
9
|
+
*
|
|
10
|
+
* @fires abrir - Al tocar el botón (cancelable: `preventDefault()` no abre el panel).
|
|
11
|
+
* @fires cerrar - Al cerrar el panel.
|
|
12
|
+
* @slot - Contenido del panel (líneas del carrito).
|
|
13
|
+
* @slot pie - Parte fija de abajo: resumen y botones.
|
|
14
|
+
* @slot vacio - Se muestra cuando `cantidad` es 0.
|
|
15
|
+
* @slot icono - Reemplaza el ícono del botón.
|
|
16
|
+
*/
|
|
17
|
+
export declare class LrMiniCarrito extends LitElement {
|
|
18
|
+
static styles: import("lit").CSSResult[];
|
|
19
|
+
/** Número de piezas en el carrito (se muestra en el contador). */
|
|
20
|
+
cantidad: number;
|
|
21
|
+
abierto: boolean;
|
|
22
|
+
titulo: string;
|
|
23
|
+
/** El botón solo lanza `abrir`; no hay panel. */
|
|
24
|
+
sinPanel: boolean;
|
|
25
|
+
textoVacio: string;
|
|
26
|
+
/** Ícono del botón: `bolsa` o `carrito`. */
|
|
27
|
+
icono: 'bolsa' | 'carrito';
|
|
28
|
+
private dialogo?;
|
|
29
|
+
private contador?;
|
|
30
|
+
abrir(): void;
|
|
31
|
+
cerrar(): void;
|
|
32
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
33
|
+
protected updated(cambios: PropertyValues<this>): void;
|
|
34
|
+
private alTocar;
|
|
35
|
+
private alCancelar;
|
|
36
|
+
private alClicFondo;
|
|
37
|
+
}
|
|
38
|
+
declare global {
|
|
39
|
+
interface HTMLElementTagNameMap {
|
|
40
|
+
'lr-mini-carrito': LrMiniCarrito;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=mini-carrito.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mini-carrito.d.ts","sourceRoot":"","sources":["../src/mini-carrito.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAI1E;;;;;;;;;;;;;;GAcG;AACH,qBACa,aAAc,SAAQ,UAAU;IAC3C,OAAgB,MAAM,4BA6IpB;IAEF,kEAAkE;IACtC,QAAQ,SAAK;IAEG,OAAO,UAAS;IAEhD,MAAM,SAAgB;IAElC,iDAAiD;IACI,QAAQ,UAAS;IAE9B,UAAU,SAA4B;IAE9E,4CAA4C;IAChC,KAAK,EAAE,OAAO,GAAG,SAAS,CAAW;IAEhC,OAAO,CAAC,OAAO,CAAC,CAAoB;IAEjC,OAAO,CAAC,QAAQ,CAAC,CAAc;IAEnD,KAAK;IAIL,MAAM;IAIG,MAAM;cA4BI,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAiBxD,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,WAAW;CAGpB;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,iBAAiB,EAAE,aAAa,CAAC;KAClC;CACF"}
|
|
@@ -0,0 +1,273 @@
|
|
|
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, nothing } from 'lit';
|
|
8
|
+
import { customElement, property, query } from 'lit/decorators.js';
|
|
9
|
+
import { emitir, estilosBase, iconoShoppingBag, iconoShoppingCart, iconoX, tokens } from '@lit-roa/base';
|
|
10
|
+
/**
|
|
11
|
+
* Botón de carrito con contador y panel lateral. El panel muestra lo que
|
|
12
|
+
* pongas en el slot (por ejemplo `<lr-linea-carrito>`) y un pie fijo para el
|
|
13
|
+
* resumen y el botón de pagar.
|
|
14
|
+
*
|
|
15
|
+
* El botón abre el panel solo; si prefieres abrir tu propia página de
|
|
16
|
+
* carrito usa `sin-panel` y escucha `abrir`.
|
|
17
|
+
*
|
|
18
|
+
* @fires abrir - Al tocar el botón (cancelable: `preventDefault()` no abre el panel).
|
|
19
|
+
* @fires cerrar - Al cerrar el panel.
|
|
20
|
+
* @slot - Contenido del panel (líneas del carrito).
|
|
21
|
+
* @slot pie - Parte fija de abajo: resumen y botones.
|
|
22
|
+
* @slot vacio - Se muestra cuando `cantidad` es 0.
|
|
23
|
+
* @slot icono - Reemplaza el ícono del botón.
|
|
24
|
+
*/
|
|
25
|
+
let LrMiniCarrito = class LrMiniCarrito extends LitElement {
|
|
26
|
+
constructor() {
|
|
27
|
+
super(...arguments);
|
|
28
|
+
/** Número de piezas en el carrito (se muestra en el contador). */
|
|
29
|
+
this.cantidad = 0;
|
|
30
|
+
this.abierto = false;
|
|
31
|
+
this.titulo = 'Tu carrito';
|
|
32
|
+
/** El botón solo lanza `abrir`; no hay panel. */
|
|
33
|
+
this.sinPanel = false;
|
|
34
|
+
this.textoVacio = 'Tu carrito está vacío.';
|
|
35
|
+
/** Ícono del botón: `bolsa` o `carrito`. */
|
|
36
|
+
this.icono = 'bolsa';
|
|
37
|
+
}
|
|
38
|
+
static { this.styles = [
|
|
39
|
+
tokens,
|
|
40
|
+
estilosBase,
|
|
41
|
+
css `
|
|
42
|
+
:host {
|
|
43
|
+
display: inline-block;
|
|
44
|
+
}
|
|
45
|
+
.boton {
|
|
46
|
+
all: unset;
|
|
47
|
+
position: relative;
|
|
48
|
+
display: grid;
|
|
49
|
+
place-items: center;
|
|
50
|
+
width: 2.75rem;
|
|
51
|
+
height: 2.75rem;
|
|
52
|
+
border-radius: 999px;
|
|
53
|
+
color: inherit;
|
|
54
|
+
font-size: 1.5rem;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
}
|
|
57
|
+
.boton:hover {
|
|
58
|
+
background: var(--lr-mini-carrito-hover, rgb(0 0 0 / 0.06));
|
|
59
|
+
}
|
|
60
|
+
.boton:focus-visible {
|
|
61
|
+
outline: 2px solid var(--_color-primario);
|
|
62
|
+
}
|
|
63
|
+
.contador {
|
|
64
|
+
position: absolute;
|
|
65
|
+
top: 0.125rem;
|
|
66
|
+
right: 0.125rem;
|
|
67
|
+
min-width: 1.25rem;
|
|
68
|
+
height: 1.25rem;
|
|
69
|
+
padding: 0 0.3125rem;
|
|
70
|
+
border-radius: 999px;
|
|
71
|
+
background: var(--lr-mini-carrito-contador, var(--_color-primario));
|
|
72
|
+
color: var(--_color-sobre-primario);
|
|
73
|
+
font-size: 0.6875rem;
|
|
74
|
+
font-weight: 700;
|
|
75
|
+
line-height: 1.25rem;
|
|
76
|
+
text-align: center;
|
|
77
|
+
}
|
|
78
|
+
.contador.salta {
|
|
79
|
+
animation: saltar 0.35s ease-out;
|
|
80
|
+
}
|
|
81
|
+
@keyframes saltar {
|
|
82
|
+
40% {
|
|
83
|
+
transform: scale(1.35);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
dialog {
|
|
88
|
+
position: fixed;
|
|
89
|
+
inset: 0 0 0 auto;
|
|
90
|
+
width: min(var(--lr-mini-carrito-ancho, 26rem), 100vw);
|
|
91
|
+
max-width: 100vw;
|
|
92
|
+
height: 100dvh;
|
|
93
|
+
max-height: 100dvh;
|
|
94
|
+
margin: 0;
|
|
95
|
+
padding: 0;
|
|
96
|
+
border: 0;
|
|
97
|
+
background: var(--_color-fondo);
|
|
98
|
+
color: var(--_color-texto);
|
|
99
|
+
font-family: var(--_fuente);
|
|
100
|
+
box-shadow: -10px 0 40px rgb(0 0 0 / 0.18);
|
|
101
|
+
}
|
|
102
|
+
dialog[open] {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
animation: deslizar 0.22s ease-out;
|
|
106
|
+
}
|
|
107
|
+
dialog::backdrop {
|
|
108
|
+
background: rgb(17 24 39 / 0.5);
|
|
109
|
+
}
|
|
110
|
+
@keyframes deslizar {
|
|
111
|
+
from {
|
|
112
|
+
transform: translateX(100%);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
@media (prefers-reduced-motion: reduce) {
|
|
116
|
+
dialog[open],
|
|
117
|
+
.contador.salta {
|
|
118
|
+
animation: none;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
header {
|
|
122
|
+
display: flex;
|
|
123
|
+
align-items: center;
|
|
124
|
+
justify-content: space-between;
|
|
125
|
+
padding: 1rem 1.25rem;
|
|
126
|
+
border-bottom: 1px solid var(--_color-borde);
|
|
127
|
+
}
|
|
128
|
+
h2 {
|
|
129
|
+
margin: 0;
|
|
130
|
+
font-size: 1.125rem;
|
|
131
|
+
}
|
|
132
|
+
h2 span {
|
|
133
|
+
color: var(--_color-texto-suave);
|
|
134
|
+
font-weight: 500;
|
|
135
|
+
}
|
|
136
|
+
.cerrar {
|
|
137
|
+
all: unset;
|
|
138
|
+
display: grid;
|
|
139
|
+
place-items: center;
|
|
140
|
+
width: 2.25rem;
|
|
141
|
+
height: 2.25rem;
|
|
142
|
+
border-radius: 999px;
|
|
143
|
+
font-size: 1.25rem;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
}
|
|
146
|
+
.cerrar:hover {
|
|
147
|
+
background: var(--_color-borde);
|
|
148
|
+
}
|
|
149
|
+
.cerrar:focus-visible {
|
|
150
|
+
outline: 2px solid var(--_color-primario);
|
|
151
|
+
}
|
|
152
|
+
.cuerpo {
|
|
153
|
+
flex: 1;
|
|
154
|
+
overflow: auto;
|
|
155
|
+
padding: 0.5rem 1.25rem;
|
|
156
|
+
}
|
|
157
|
+
.vacio {
|
|
158
|
+
display: grid;
|
|
159
|
+
place-items: center;
|
|
160
|
+
gap: 0.75rem;
|
|
161
|
+
height: 100%;
|
|
162
|
+
min-height: 12rem;
|
|
163
|
+
text-align: center;
|
|
164
|
+
color: var(--_color-texto-suave);
|
|
165
|
+
}
|
|
166
|
+
.vacio .icono {
|
|
167
|
+
font-size: 3rem;
|
|
168
|
+
color: var(--_color-borde);
|
|
169
|
+
}
|
|
170
|
+
footer {
|
|
171
|
+
padding: 1rem 1.25rem 1.25rem;
|
|
172
|
+
border-top: 1px solid var(--_color-borde);
|
|
173
|
+
background: var(--lr-mini-carrito-pie, #f9fafb);
|
|
174
|
+
}
|
|
175
|
+
footer[hidden] {
|
|
176
|
+
display: none;
|
|
177
|
+
}
|
|
178
|
+
`,
|
|
179
|
+
]; }
|
|
180
|
+
abrir() {
|
|
181
|
+
this.abierto = true;
|
|
182
|
+
}
|
|
183
|
+
cerrar() {
|
|
184
|
+
this.abierto = false;
|
|
185
|
+
}
|
|
186
|
+
render() {
|
|
187
|
+
const etiqueta = this.cantidad === 1 ? '1 producto' : `${this.cantidad} productos`;
|
|
188
|
+
return html `
|
|
189
|
+
<button class="boton" part="boton" aria-label="${this.titulo}, ${etiqueta}" aria-haspopup="dialog" @click=${this.alTocar}>
|
|
190
|
+
<slot name="icono">${this.icono === 'carrito' ? iconoShoppingCart : iconoShoppingBag}</slot>
|
|
191
|
+
${this.cantidad > 0
|
|
192
|
+
? html `<span class="contador" part="contador" aria-hidden="true">${this.cantidad > 99 ? '99+' : this.cantidad}</span>`
|
|
193
|
+
: nothing}
|
|
194
|
+
</button>
|
|
195
|
+
${this.sinPanel
|
|
196
|
+
? nothing
|
|
197
|
+
: html `<dialog part="panel" aria-labelledby="titulo" @cancel=${this.alCancelar} @click=${this.alClicFondo}>
|
|
198
|
+
<header>
|
|
199
|
+
<h2 id="titulo">${this.titulo} ${this.cantidad ? html `<span>(${this.cantidad})</span>` : nothing}</h2>
|
|
200
|
+
<button class="cerrar" aria-label="Cerrar" @click=${this.cerrar}>${iconoX}</button>
|
|
201
|
+
</header>
|
|
202
|
+
<div class="cuerpo">
|
|
203
|
+
${this.cantidad > 0
|
|
204
|
+
? html `<slot></slot>`
|
|
205
|
+
: html `<div class="vacio">
|
|
206
|
+
<slot name="vacio"><div><div class="icono">${iconoShoppingBag}</div>${this.textoVacio}</div></slot>
|
|
207
|
+
</div>`}
|
|
208
|
+
</div>
|
|
209
|
+
<footer ?hidden=${this.cantidad === 0}><slot name="pie"></slot></footer>
|
|
210
|
+
</dialog>`}
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
updated(cambios) {
|
|
214
|
+
if (cambios.has('cantidad') && cambios.get('cantidad') !== undefined && this.cantidad > (cambios.get('cantidad') ?? 0)) {
|
|
215
|
+
this.contador?.classList.remove('salta');
|
|
216
|
+
void this.contador?.offsetWidth;
|
|
217
|
+
this.contador?.classList.add('salta');
|
|
218
|
+
}
|
|
219
|
+
if (!cambios.has('abierto') || !this.dialogo)
|
|
220
|
+
return;
|
|
221
|
+
if (this.abierto && !this.dialogo.open) {
|
|
222
|
+
this.dialogo.showModal();
|
|
223
|
+
document.documentElement.style.overflow = 'hidden';
|
|
224
|
+
}
|
|
225
|
+
else if (!this.abierto && this.dialogo.open) {
|
|
226
|
+
this.dialogo.close();
|
|
227
|
+
document.documentElement.style.overflow = '';
|
|
228
|
+
emitir(this, 'cerrar');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
alTocar() {
|
|
232
|
+
const sigue = emitir(this, 'abrir');
|
|
233
|
+
if (sigue && !this.sinPanel)
|
|
234
|
+
this.abrir();
|
|
235
|
+
}
|
|
236
|
+
alCancelar(evento) {
|
|
237
|
+
evento.preventDefault();
|
|
238
|
+
this.cerrar();
|
|
239
|
+
}
|
|
240
|
+
alClicFondo(evento) {
|
|
241
|
+
if (evento.target === this.dialogo)
|
|
242
|
+
this.cerrar();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
__decorate([
|
|
246
|
+
property({ type: Number })
|
|
247
|
+
], LrMiniCarrito.prototype, "cantidad", void 0);
|
|
248
|
+
__decorate([
|
|
249
|
+
property({ type: Boolean, reflect: true })
|
|
250
|
+
], LrMiniCarrito.prototype, "abierto", void 0);
|
|
251
|
+
__decorate([
|
|
252
|
+
property()
|
|
253
|
+
], LrMiniCarrito.prototype, "titulo", void 0);
|
|
254
|
+
__decorate([
|
|
255
|
+
property({ type: Boolean, attribute: 'sin-panel' })
|
|
256
|
+
], LrMiniCarrito.prototype, "sinPanel", void 0);
|
|
257
|
+
__decorate([
|
|
258
|
+
property({ attribute: 'texto-vacio' })
|
|
259
|
+
], LrMiniCarrito.prototype, "textoVacio", void 0);
|
|
260
|
+
__decorate([
|
|
261
|
+
property()
|
|
262
|
+
], LrMiniCarrito.prototype, "icono", void 0);
|
|
263
|
+
__decorate([
|
|
264
|
+
query('dialog')
|
|
265
|
+
], LrMiniCarrito.prototype, "dialogo", void 0);
|
|
266
|
+
__decorate([
|
|
267
|
+
query('.contador')
|
|
268
|
+
], LrMiniCarrito.prototype, "contador", void 0);
|
|
269
|
+
LrMiniCarrito = __decorate([
|
|
270
|
+
customElement('lr-mini-carrito')
|
|
271
|
+
], LrMiniCarrito);
|
|
272
|
+
export { LrMiniCarrito };
|
|
273
|
+
//# sourceMappingURL=mini-carrito.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mini-carrito.js","sourceRoot":"","sources":["../src/mini-carrito.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lit-roa/mini-carrito",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Botón de carrito con contador y panel lateral.",
|
|
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
|
+
"devDependencies": {
|
|
31
|
+
"@lit-roa/boton": "^0.1.3",
|
|
32
|
+
"@lit-roa/linea-carrito": "^0.1.2"
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LrMiniCarrito } from './mini-carrito.js';
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { LitElement, css, html, nothing, type PropertyValues } from 'lit';
|
|
2
|
+
import { customElement, property, query } from 'lit/decorators.js';
|
|
3
|
+
import { emitir, estilosBase, iconoShoppingBag, iconoShoppingCart, iconoX, tokens } from '@lit-roa/base';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Botón de carrito con contador y panel lateral. El panel muestra lo que
|
|
7
|
+
* pongas en el slot (por ejemplo `<lr-linea-carrito>`) y un pie fijo para el
|
|
8
|
+
* resumen y el botón de pagar.
|
|
9
|
+
*
|
|
10
|
+
* El botón abre el panel solo; si prefieres abrir tu propia página de
|
|
11
|
+
* carrito usa `sin-panel` y escucha `abrir`.
|
|
12
|
+
*
|
|
13
|
+
* @fires abrir - Al tocar el botón (cancelable: `preventDefault()` no abre el panel).
|
|
14
|
+
* @fires cerrar - Al cerrar el panel.
|
|
15
|
+
* @slot - Contenido del panel (líneas del carrito).
|
|
16
|
+
* @slot pie - Parte fija de abajo: resumen y botones.
|
|
17
|
+
* @slot vacio - Se muestra cuando `cantidad` es 0.
|
|
18
|
+
* @slot icono - Reemplaza el ícono del botón.
|
|
19
|
+
*/
|
|
20
|
+
@customElement('lr-mini-carrito')
|
|
21
|
+
export class LrMiniCarrito extends LitElement {
|
|
22
|
+
static override styles = [
|
|
23
|
+
tokens,
|
|
24
|
+
estilosBase,
|
|
25
|
+
css`
|
|
26
|
+
:host {
|
|
27
|
+
display: inline-block;
|
|
28
|
+
}
|
|
29
|
+
.boton {
|
|
30
|
+
all: unset;
|
|
31
|
+
position: relative;
|
|
32
|
+
display: grid;
|
|
33
|
+
place-items: center;
|
|
34
|
+
width: 2.75rem;
|
|
35
|
+
height: 2.75rem;
|
|
36
|
+
border-radius: 999px;
|
|
37
|
+
color: inherit;
|
|
38
|
+
font-size: 1.5rem;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
}
|
|
41
|
+
.boton:hover {
|
|
42
|
+
background: var(--lr-mini-carrito-hover, rgb(0 0 0 / 0.06));
|
|
43
|
+
}
|
|
44
|
+
.boton:focus-visible {
|
|
45
|
+
outline: 2px solid var(--_color-primario);
|
|
46
|
+
}
|
|
47
|
+
.contador {
|
|
48
|
+
position: absolute;
|
|
49
|
+
top: 0.125rem;
|
|
50
|
+
right: 0.125rem;
|
|
51
|
+
min-width: 1.25rem;
|
|
52
|
+
height: 1.25rem;
|
|
53
|
+
padding: 0 0.3125rem;
|
|
54
|
+
border-radius: 999px;
|
|
55
|
+
background: var(--lr-mini-carrito-contador, var(--_color-primario));
|
|
56
|
+
color: var(--_color-sobre-primario);
|
|
57
|
+
font-size: 0.6875rem;
|
|
58
|
+
font-weight: 700;
|
|
59
|
+
line-height: 1.25rem;
|
|
60
|
+
text-align: center;
|
|
61
|
+
}
|
|
62
|
+
.contador.salta {
|
|
63
|
+
animation: saltar 0.35s ease-out;
|
|
64
|
+
}
|
|
65
|
+
@keyframes saltar {
|
|
66
|
+
40% {
|
|
67
|
+
transform: scale(1.35);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
dialog {
|
|
72
|
+
position: fixed;
|
|
73
|
+
inset: 0 0 0 auto;
|
|
74
|
+
width: min(var(--lr-mini-carrito-ancho, 26rem), 100vw);
|
|
75
|
+
max-width: 100vw;
|
|
76
|
+
height: 100dvh;
|
|
77
|
+
max-height: 100dvh;
|
|
78
|
+
margin: 0;
|
|
79
|
+
padding: 0;
|
|
80
|
+
border: 0;
|
|
81
|
+
background: var(--_color-fondo);
|
|
82
|
+
color: var(--_color-texto);
|
|
83
|
+
font-family: var(--_fuente);
|
|
84
|
+
box-shadow: -10px 0 40px rgb(0 0 0 / 0.18);
|
|
85
|
+
}
|
|
86
|
+
dialog[open] {
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
animation: deslizar 0.22s ease-out;
|
|
90
|
+
}
|
|
91
|
+
dialog::backdrop {
|
|
92
|
+
background: rgb(17 24 39 / 0.5);
|
|
93
|
+
}
|
|
94
|
+
@keyframes deslizar {
|
|
95
|
+
from {
|
|
96
|
+
transform: translateX(100%);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
@media (prefers-reduced-motion: reduce) {
|
|
100
|
+
dialog[open],
|
|
101
|
+
.contador.salta {
|
|
102
|
+
animation: none;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
header {
|
|
106
|
+
display: flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
padding: 1rem 1.25rem;
|
|
110
|
+
border-bottom: 1px solid var(--_color-borde);
|
|
111
|
+
}
|
|
112
|
+
h2 {
|
|
113
|
+
margin: 0;
|
|
114
|
+
font-size: 1.125rem;
|
|
115
|
+
}
|
|
116
|
+
h2 span {
|
|
117
|
+
color: var(--_color-texto-suave);
|
|
118
|
+
font-weight: 500;
|
|
119
|
+
}
|
|
120
|
+
.cerrar {
|
|
121
|
+
all: unset;
|
|
122
|
+
display: grid;
|
|
123
|
+
place-items: center;
|
|
124
|
+
width: 2.25rem;
|
|
125
|
+
height: 2.25rem;
|
|
126
|
+
border-radius: 999px;
|
|
127
|
+
font-size: 1.25rem;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
}
|
|
130
|
+
.cerrar:hover {
|
|
131
|
+
background: var(--_color-borde);
|
|
132
|
+
}
|
|
133
|
+
.cerrar:focus-visible {
|
|
134
|
+
outline: 2px solid var(--_color-primario);
|
|
135
|
+
}
|
|
136
|
+
.cuerpo {
|
|
137
|
+
flex: 1;
|
|
138
|
+
overflow: auto;
|
|
139
|
+
padding: 0.5rem 1.25rem;
|
|
140
|
+
}
|
|
141
|
+
.vacio {
|
|
142
|
+
display: grid;
|
|
143
|
+
place-items: center;
|
|
144
|
+
gap: 0.75rem;
|
|
145
|
+
height: 100%;
|
|
146
|
+
min-height: 12rem;
|
|
147
|
+
text-align: center;
|
|
148
|
+
color: var(--_color-texto-suave);
|
|
149
|
+
}
|
|
150
|
+
.vacio .icono {
|
|
151
|
+
font-size: 3rem;
|
|
152
|
+
color: var(--_color-borde);
|
|
153
|
+
}
|
|
154
|
+
footer {
|
|
155
|
+
padding: 1rem 1.25rem 1.25rem;
|
|
156
|
+
border-top: 1px solid var(--_color-borde);
|
|
157
|
+
background: var(--lr-mini-carrito-pie, #f9fafb);
|
|
158
|
+
}
|
|
159
|
+
footer[hidden] {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
`,
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
/** Número de piezas en el carrito (se muestra en el contador). */
|
|
166
|
+
@property({ type: Number }) cantidad = 0;
|
|
167
|
+
|
|
168
|
+
@property({ type: Boolean, reflect: true }) abierto = false;
|
|
169
|
+
|
|
170
|
+
@property() titulo = 'Tu carrito';
|
|
171
|
+
|
|
172
|
+
/** El botón solo lanza `abrir`; no hay panel. */
|
|
173
|
+
@property({ type: Boolean, attribute: 'sin-panel' }) sinPanel = false;
|
|
174
|
+
|
|
175
|
+
@property({ attribute: 'texto-vacio' }) textoVacio = 'Tu carrito está vacío.';
|
|
176
|
+
|
|
177
|
+
/** Ícono del botón: `bolsa` o `carrito`. */
|
|
178
|
+
@property() icono: 'bolsa' | 'carrito' = 'bolsa';
|
|
179
|
+
|
|
180
|
+
@query('dialog') private dialogo?: HTMLDialogElement;
|
|
181
|
+
|
|
182
|
+
@query('.contador') private contador?: HTMLElement;
|
|
183
|
+
|
|
184
|
+
abrir() {
|
|
185
|
+
this.abierto = true;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
cerrar() {
|
|
189
|
+
this.abierto = false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
override render() {
|
|
193
|
+
const etiqueta = this.cantidad === 1 ? '1 producto' : `${this.cantidad} productos`;
|
|
194
|
+
return html`
|
|
195
|
+
<button class="boton" part="boton" aria-label="${this.titulo}, ${etiqueta}" aria-haspopup="dialog" @click=${this.alTocar}>
|
|
196
|
+
<slot name="icono">${this.icono === 'carrito' ? iconoShoppingCart : iconoShoppingBag}</slot>
|
|
197
|
+
${this.cantidad > 0
|
|
198
|
+
? html`<span class="contador" part="contador" aria-hidden="true">${this.cantidad > 99 ? '99+' : this.cantidad}</span>`
|
|
199
|
+
: nothing}
|
|
200
|
+
</button>
|
|
201
|
+
${this.sinPanel
|
|
202
|
+
? nothing
|
|
203
|
+
: html`<dialog part="panel" aria-labelledby="titulo" @cancel=${this.alCancelar} @click=${this.alClicFondo}>
|
|
204
|
+
<header>
|
|
205
|
+
<h2 id="titulo">${this.titulo} ${this.cantidad ? html`<span>(${this.cantidad})</span>` : nothing}</h2>
|
|
206
|
+
<button class="cerrar" aria-label="Cerrar" @click=${this.cerrar}>${iconoX}</button>
|
|
207
|
+
</header>
|
|
208
|
+
<div class="cuerpo">
|
|
209
|
+
${this.cantidad > 0
|
|
210
|
+
? html`<slot></slot>`
|
|
211
|
+
: html`<div class="vacio">
|
|
212
|
+
<slot name="vacio"><div><div class="icono">${iconoShoppingBag}</div>${this.textoVacio}</div></slot>
|
|
213
|
+
</div>`}
|
|
214
|
+
</div>
|
|
215
|
+
<footer ?hidden=${this.cantidad === 0}><slot name="pie"></slot></footer>
|
|
216
|
+
</dialog>`}
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
protected override updated(cambios: PropertyValues<this>) {
|
|
221
|
+
if (cambios.has('cantidad') && cambios.get('cantidad') !== undefined && this.cantidad > (cambios.get('cantidad') ?? 0)) {
|
|
222
|
+
this.contador?.classList.remove('salta');
|
|
223
|
+
void this.contador?.offsetWidth;
|
|
224
|
+
this.contador?.classList.add('salta');
|
|
225
|
+
}
|
|
226
|
+
if (!cambios.has('abierto') || !this.dialogo) return;
|
|
227
|
+
if (this.abierto && !this.dialogo.open) {
|
|
228
|
+
this.dialogo.showModal();
|
|
229
|
+
document.documentElement.style.overflow = 'hidden';
|
|
230
|
+
} else if (!this.abierto && this.dialogo.open) {
|
|
231
|
+
this.dialogo.close();
|
|
232
|
+
document.documentElement.style.overflow = '';
|
|
233
|
+
emitir(this, 'cerrar');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private alTocar() {
|
|
238
|
+
const sigue = emitir(this, 'abrir');
|
|
239
|
+
if (sigue && !this.sinPanel) this.abrir();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private alCancelar(evento: Event) {
|
|
243
|
+
evento.preventDefault();
|
|
244
|
+
this.cerrar();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private alClicFondo(evento: MouseEvent) {
|
|
248
|
+
if (evento.target === this.dialogo) this.cerrar();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
declare global {
|
|
253
|
+
interface HTMLElementTagNameMap {
|
|
254
|
+
'lr-mini-carrito': LrMiniCarrito;
|
|
255
|
+
}
|
|
256
|
+
}
|