@erplora/outfitkit 0.1.16 → 0.1.18
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/dist/cdn.d.ts +1 -0
- package/dist/components/ok-receipt/ok-receipt.d.ts +7 -0
- package/dist/components/ok-receipt/ok-receipt.test.d.ts +1 -0
- package/dist/components/ok-spotlight-search/ok-spotlight-search.d.ts +27 -0
- package/dist/components/ok-spotlight-search/ok-spotlight-search.test.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/ok-receipt.js +11 -0
- package/dist/ok-spotlight-search.js +211 -0
- package/dist/outfitkit.bundle.js +1775 -1583
- package/dist/outfitkit.js +1 -0
- package/package.json +2 -1
package/dist/cdn.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ import './components/ok-invoice/ok-invoice.js';
|
|
|
38
38
|
import './components/ok-timeline/ok-timeline.js';
|
|
39
39
|
import './components/ok-qty-stepper/ok-qty-stepper.js';
|
|
40
40
|
import './components/ok-command-palette/ok-command-palette.js';
|
|
41
|
+
import './components/ok-spotlight-search/ok-spotlight-search.js';
|
|
41
42
|
import './components/ok-color-picker/ok-color-picker.js';
|
|
42
43
|
import './components/ok-avatar/ok-avatar.js';
|
|
43
44
|
import './components/ok-status-pill/ok-status-pill.js';
|
|
@@ -77,6 +77,11 @@ export interface ReceiptData {
|
|
|
77
77
|
qr?: string;
|
|
78
78
|
/** Leyenda bajo el QR. */
|
|
79
79
|
qr_note?: string;
|
|
80
|
+
/** QR promocional del negocio (reseñas Google, redes, web). Va SIEMPRE al final,
|
|
81
|
+
* después del fiscal (que es el legal) y más pequeño. Si vacío, no deja rastro. */
|
|
82
|
+
promo_qr?: string;
|
|
83
|
+
/** Leyenda sobre el QR promocional (p.ej. «Escanea y déjanos una reseña»). */
|
|
84
|
+
promo_note?: string;
|
|
80
85
|
}
|
|
81
86
|
export declare class OkReceipt extends LitElement {
|
|
82
87
|
static styles: import("lit").CSSResult;
|
|
@@ -95,6 +100,8 @@ export declare class OkReceipt extends LitElement {
|
|
|
95
100
|
private renderLines;
|
|
96
101
|
private renderTotals;
|
|
97
102
|
private renderQr;
|
|
103
|
+
/** QR promocional (reseñas/redes): al final del papel y más pequeño que el fiscal. */
|
|
104
|
+
private renderPromo;
|
|
98
105
|
}
|
|
99
106
|
declare global {
|
|
100
107
|
interface HTMLElementTagNameMap {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './ok-receipt.js';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
export declare class OkSpotlightSearch extends LitElement {
|
|
3
|
+
static styles: import("lit").CSSResult;
|
|
4
|
+
/** Abierto/cerrado (reflejado para CSS externo). */
|
|
5
|
+
open: boolean;
|
|
6
|
+
/** Texto guía del input de búsqueda. */
|
|
7
|
+
placeholder: string;
|
|
8
|
+
/** Texto de búsqueda actual (el consumidor puede resetearlo). */
|
|
9
|
+
value: string;
|
|
10
|
+
/** Icono (Iconify/ionicon) del botón-trigger propio; si vacío, no se pinta trigger. */
|
|
11
|
+
triggerIcon: string;
|
|
12
|
+
/** Nombre accesible del botón-trigger (aria-label). */
|
|
13
|
+
triggerLabel: string;
|
|
14
|
+
private input?;
|
|
15
|
+
openSearch(): void;
|
|
16
|
+
close(): void;
|
|
17
|
+
toggle(): void;
|
|
18
|
+
private emitOpen;
|
|
19
|
+
private onInput;
|
|
20
|
+
protected updated(): void;
|
|
21
|
+
render(): unknown;
|
|
22
|
+
}
|
|
23
|
+
declare global {
|
|
24
|
+
interface HTMLElementTagNameMap {
|
|
25
|
+
'ok-spotlight-search': OkSpotlightSearch;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './ok-spotlight-search.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -59,6 +59,7 @@ export type { OkTimelineItem } from './components/ok-timeline/ok-timeline.js';
|
|
|
59
59
|
export { OkQtyStepper } from './components/ok-qty-stepper/ok-qty-stepper.js';
|
|
60
60
|
export { OkCommandPalette } from './components/ok-command-palette/ok-command-palette.js';
|
|
61
61
|
export type { OkCommand } from './components/ok-command-palette/ok-command-palette.js';
|
|
62
|
+
export { OkSpotlightSearch } from './components/ok-spotlight-search/ok-spotlight-search.js';
|
|
62
63
|
export { OkColorPicker } from './components/ok-color-picker/ok-color-picker.js';
|
|
63
64
|
export type { OkRgb } from './components/ok-color-picker/ok-color-picker.js';
|
|
64
65
|
export { OkAvatar } from './components/ok-avatar/ok-avatar.js';
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import { OkInvoice } from "./ok-invoice.js";
|
|
|
39
39
|
import { OkTimeline } from "./ok-timeline.js";
|
|
40
40
|
import { OkQtyStepper } from "./ok-qty-stepper.js";
|
|
41
41
|
import { OkCommandPalette } from "./ok-command-palette.js";
|
|
42
|
+
import { OkSpotlightSearch } from "./ok-spotlight-search.js";
|
|
42
43
|
import { OkColorPicker } from "./ok-color-picker.js";
|
|
43
44
|
import { OkAvatar } from "./ok-avatar.js";
|
|
44
45
|
import { OkStatusPill } from "./ok-status-pill.js";
|
|
@@ -180,6 +181,7 @@ export {
|
|
|
180
181
|
OkSparkline,
|
|
181
182
|
OkSplitButton,
|
|
182
183
|
OkSplitter,
|
|
184
|
+
OkSpotlightSearch,
|
|
183
185
|
OkStat,
|
|
184
186
|
OkStatusDot,
|
|
185
187
|
OkStatusPill,
|
package/dist/ok-receipt.js
CHANGED
|
@@ -76,6 +76,8 @@ class OkReceipt extends LitElement {
|
|
|
76
76
|
.footer { font-size: 10px; white-space: pre-line; }
|
|
77
77
|
.qr-wrap { display: flex; flex-direction: column; align-items: center; gap: 1mm; margin-top: 2mm; }
|
|
78
78
|
.qr-note { font-size: 8px; text-align: center; word-break: break-word; }
|
|
79
|
+
.promo-wrap { display: flex; flex-direction: column; align-items: center; gap: 1mm; margin-top: 2mm; }
|
|
80
|
+
.promo-note { font-size: 9px; text-align: center; word-break: break-word; }
|
|
79
81
|
.empty { padding: 4mm; text-align: center; color: #888; font-style: italic; }
|
|
80
82
|
`;
|
|
81
83
|
}
|
|
@@ -101,6 +103,7 @@ class OkReceipt extends LitElement {
|
|
|
101
103
|
${this.renderTotals(r)}
|
|
102
104
|
${r.footer ? html`<hr class="sep" /><div class="center footer">${r.footer}</div>` : nothing}
|
|
103
105
|
${this.renderQr(r)}
|
|
106
|
+
${this.renderPromo(r)}
|
|
104
107
|
</div>`;
|
|
105
108
|
}
|
|
106
109
|
renderHeader(r) {
|
|
@@ -167,6 +170,14 @@ class OkReceipt extends LitElement {
|
|
|
167
170
|
${r.qr_note ? html`<div class="qr-note">${r.qr_note}</div>` : nothing}
|
|
168
171
|
</div>`;
|
|
169
172
|
}
|
|
173
|
+
/** QR promocional (reseñas/redes): al final del papel y más pequeño que el fiscal. */
|
|
174
|
+
renderPromo(r) {
|
|
175
|
+
if (!r.promo_qr) return nothing;
|
|
176
|
+
return html`<div class="promo-wrap">
|
|
177
|
+
${r.promo_note ? html`<div class="promo-note">${r.promo_note}</div>` : nothing}
|
|
178
|
+
<ok-qr .value=${r.promo_qr} .size=${Math.round(this.qrSize * 0.7)} ec="M" color="#000" background="#fff"></ok-qr>
|
|
179
|
+
</div>`;
|
|
180
|
+
}
|
|
170
181
|
}
|
|
171
182
|
__decorateClass([
|
|
172
183
|
property({ attribute: false })
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { LitElement, css, nothing, html } from "lit";
|
|
2
|
+
import { property, query } from "lit/decorators.js";
|
|
3
|
+
import { define } from "./define.js";
|
|
4
|
+
import { L as iconSearchOutline, v as iconCloseOutline, o as okIcon } from "./shared/icons.js";
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
7
|
+
var result = void 0;
|
|
8
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
9
|
+
if (decorator = decorators[i])
|
|
10
|
+
result = decorator(target, key, result) || result;
|
|
11
|
+
if (result) __defProp(target, key, result);
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
class OkSpotlightSearch extends LitElement {
|
|
15
|
+
constructor() {
|
|
16
|
+
super(...arguments);
|
|
17
|
+
this.open = false;
|
|
18
|
+
this.placeholder = "";
|
|
19
|
+
this.value = "";
|
|
20
|
+
this.triggerIcon = "";
|
|
21
|
+
this.triggerLabel = "";
|
|
22
|
+
}
|
|
23
|
+
static {
|
|
24
|
+
this.styles = css`
|
|
25
|
+
:host {
|
|
26
|
+
--color: var(--ok-text, var(--ion-text-color, #1c1b17));
|
|
27
|
+
--color-muted: var(--ok-text-muted, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.6));
|
|
28
|
+
--panel-bg: var(--ok-surface, var(--ion-background-color, #ffffff));
|
|
29
|
+
--scrim-bg: var(--ok-scrim, rgba(0, 0, 0, 0.28));
|
|
30
|
+
--border-soft: var(--ok-border-soft, rgba(var(--ion-text-color-rgb, 28, 27, 23), 0.1));
|
|
31
|
+
--radius: var(--ok-radius, 16px);
|
|
32
|
+
--shadow: var(--ok-shadow, 0 24px 80px rgba(0, 0, 0, 0.35));
|
|
33
|
+
--font: var(--ok-font, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif);
|
|
34
|
+
display: contents;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Botón-trigger opcional (icon-only). */
|
|
38
|
+
button.trigger {
|
|
39
|
+
display: inline-flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
width: 2.4rem;
|
|
43
|
+
height: 2.4rem;
|
|
44
|
+
padding: 0;
|
|
45
|
+
border: 0;
|
|
46
|
+
border-radius: 10px;
|
|
47
|
+
background: none;
|
|
48
|
+
color: var(--color-muted);
|
|
49
|
+
cursor: pointer;
|
|
50
|
+
}
|
|
51
|
+
button.trigger[data-assigned] { color: var(--ok-primary, var(--ion-color-primary, #3880ff)); }
|
|
52
|
+
button.trigger ion-icon { font-size: 1.35rem; }
|
|
53
|
+
|
|
54
|
+
/* El <dialog> flota arriba-centro, translúcido con blur (Spotlight). El top layer lo saca de
|
|
55
|
+
cualquier containing block. */
|
|
56
|
+
dialog {
|
|
57
|
+
margin: 10vh auto auto;
|
|
58
|
+
width: min(92vw, 36rem);
|
|
59
|
+
max-height: 72vh;
|
|
60
|
+
padding: 0;
|
|
61
|
+
border: none;
|
|
62
|
+
border-radius: var(--radius);
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
color: var(--color);
|
|
65
|
+
font-family: var(--font);
|
|
66
|
+
background: color-mix(in srgb, var(--panel-bg) 80%, transparent);
|
|
67
|
+
-webkit-backdrop-filter: blur(22px) saturate(180%);
|
|
68
|
+
backdrop-filter: blur(22px) saturate(180%);
|
|
69
|
+
box-shadow: var(--shadow), 0 0 0 1px rgba(128, 128, 128, 0.18);
|
|
70
|
+
}
|
|
71
|
+
dialog::backdrop {
|
|
72
|
+
background: var(--scrim-bg);
|
|
73
|
+
-webkit-backdrop-filter: blur(3px);
|
|
74
|
+
backdrop-filter: blur(3px);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/* Fila del input hero + cierre. */
|
|
78
|
+
.top {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: 0.5rem;
|
|
82
|
+
padding: 0.7rem 0.85rem;
|
|
83
|
+
border-bottom: 1px solid var(--border-soft);
|
|
84
|
+
}
|
|
85
|
+
.top .lupa { flex: 0 0 auto; font-size: 1.25rem; color: var(--color-muted); }
|
|
86
|
+
.top input {
|
|
87
|
+
flex: 1 1 auto;
|
|
88
|
+
min-width: 0;
|
|
89
|
+
border: 0;
|
|
90
|
+
outline: none;
|
|
91
|
+
background: none;
|
|
92
|
+
color: inherit;
|
|
93
|
+
font: inherit;
|
|
94
|
+
font-size: 1.05rem;
|
|
95
|
+
}
|
|
96
|
+
.top input::placeholder { color: var(--color-muted); }
|
|
97
|
+
.top .close {
|
|
98
|
+
flex: 0 0 auto;
|
|
99
|
+
display: inline-flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
justify-content: center;
|
|
102
|
+
width: 2rem;
|
|
103
|
+
height: 2rem;
|
|
104
|
+
padding: 0;
|
|
105
|
+
border: 0;
|
|
106
|
+
border-radius: 8px;
|
|
107
|
+
background: none;
|
|
108
|
+
color: var(--color-muted);
|
|
109
|
+
cursor: pointer;
|
|
110
|
+
}
|
|
111
|
+
.top .close ion-icon { font-size: 1.2rem; }
|
|
112
|
+
|
|
113
|
+
/* Cuerpo scrollable: aquí caen los resultados del consumidor (slot por defecto). */
|
|
114
|
+
.results { max-height: 56vh; overflow-y: auto; padding: 0.35rem; }
|
|
115
|
+
.footer:not(:empty) { border-top: 1px solid var(--border-soft); padding: 0.3rem 0.6rem; }
|
|
116
|
+
|
|
117
|
+
@media (max-width: 560px) {
|
|
118
|
+
dialog { margin: 0 auto auto; width: 100vw; max-width: 100vw; max-height: 100vh; height: auto; border-radius: 0; }
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
// ── API pública ──────────────────────────────────────────────────────────
|
|
123
|
+
openSearch() {
|
|
124
|
+
if (this.open) return;
|
|
125
|
+
this.open = true;
|
|
126
|
+
this.emitOpen(true);
|
|
127
|
+
}
|
|
128
|
+
close() {
|
|
129
|
+
if (!this.open) return;
|
|
130
|
+
this.open = false;
|
|
131
|
+
this.emitOpen(false);
|
|
132
|
+
}
|
|
133
|
+
toggle() {
|
|
134
|
+
this.open ? this.close() : this.openSearch();
|
|
135
|
+
}
|
|
136
|
+
emitOpen(open) {
|
|
137
|
+
this.dispatchEvent(new CustomEvent("ok-open", { detail: { open }, bubbles: true, composed: true }));
|
|
138
|
+
}
|
|
139
|
+
onInput(e) {
|
|
140
|
+
this.value = e.target.value;
|
|
141
|
+
this.dispatchEvent(new CustomEvent("ok-input", { detail: { value: this.value }, bubbles: true, composed: true }));
|
|
142
|
+
}
|
|
143
|
+
// Sincroniza `open` ↔ el <dialog> nativo (top layer). try/catch porque happy-dom (tests) no
|
|
144
|
+
// implementa showModal/close; ahí `open` sigue siendo la verdad.
|
|
145
|
+
updated() {
|
|
146
|
+
const d = this.renderRoot.querySelector("dialog");
|
|
147
|
+
if (!d) return;
|
|
148
|
+
try {
|
|
149
|
+
if (this.open && !d.open) {
|
|
150
|
+
d.showModal();
|
|
151
|
+
this.input?.focus();
|
|
152
|
+
} else if (!this.open && d.open) {
|
|
153
|
+
d.close();
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
render() {
|
|
159
|
+
return html`
|
|
160
|
+
${this.triggerIcon ? html`<button class="trigger" ?data-assigned=${this.open} aria-label=${this.triggerLabel || this.placeholder}
|
|
161
|
+
title=${this.triggerLabel || this.placeholder} @click=${() => this.openSearch()}>
|
|
162
|
+
<ion-icon .icon=${okIcon(this.triggerIcon)}></ion-icon>
|
|
163
|
+
</button>` : nothing}
|
|
164
|
+
|
|
165
|
+
<dialog aria-label=${this.triggerLabel || this.placeholder}
|
|
166
|
+
@close=${() => {
|
|
167
|
+
if (this.open) {
|
|
168
|
+
this.open = false;
|
|
169
|
+
this.emitOpen(false);
|
|
170
|
+
}
|
|
171
|
+
}}
|
|
172
|
+
@click=${(e) => {
|
|
173
|
+
if (e.target === e.currentTarget) this.close();
|
|
174
|
+
}}>
|
|
175
|
+
<div class="top">
|
|
176
|
+
<ion-icon class="lupa" .icon=${iconSearchOutline}></ion-icon>
|
|
177
|
+
<input type="text" .value=${this.value} placeholder=${this.placeholder}
|
|
178
|
+
aria-label=${this.placeholder} autocomplete="off" spellcheck="false"
|
|
179
|
+
@input=${(e) => this.onInput(e)} />
|
|
180
|
+
<button class="close" aria-label="Cerrar" @click=${() => this.close()}>
|
|
181
|
+
<ion-icon .icon=${iconCloseOutline}></ion-icon>
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
<div class="results"><slot></slot></div>
|
|
185
|
+
<div class="footer"><slot name="footer"></slot></div>
|
|
186
|
+
</dialog>
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
__decorateClass([
|
|
191
|
+
property({ type: Boolean, reflect: true })
|
|
192
|
+
], OkSpotlightSearch.prototype, "open");
|
|
193
|
+
__decorateClass([
|
|
194
|
+
property()
|
|
195
|
+
], OkSpotlightSearch.prototype, "placeholder");
|
|
196
|
+
__decorateClass([
|
|
197
|
+
property()
|
|
198
|
+
], OkSpotlightSearch.prototype, "value");
|
|
199
|
+
__decorateClass([
|
|
200
|
+
property({ attribute: "trigger-icon" })
|
|
201
|
+
], OkSpotlightSearch.prototype, "triggerIcon");
|
|
202
|
+
__decorateClass([
|
|
203
|
+
property({ attribute: "trigger-label" })
|
|
204
|
+
], OkSpotlightSearch.prototype, "triggerLabel");
|
|
205
|
+
__decorateClass([
|
|
206
|
+
query(".top input")
|
|
207
|
+
], OkSpotlightSearch.prototype, "input");
|
|
208
|
+
define("ok-spotlight-search", OkSpotlightSearch);
|
|
209
|
+
export {
|
|
210
|
+
OkSpotlightSearch
|
|
211
|
+
};
|