@erplora/outfitkit 0.1.13 → 0.1.14
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.
|
@@ -39,6 +39,13 @@ export interface DataTableAction {
|
|
|
39
39
|
icon?: string;
|
|
40
40
|
/** Color Ionic del botón (p.ej. 'danger', 'primary'). */
|
|
41
41
|
color?: string;
|
|
42
|
+
/** (opcional, por fila) Si devuelve `true` para una fila, el botón de esa acción se renderiza
|
|
43
|
+
* deshabilitado (`?disabled` + `aria-disabled="true"`). */
|
|
44
|
+
disabled?: (row: Record<string, unknown>) => boolean;
|
|
45
|
+
/** (opcional, por fila) Si devuelve `true` para una fila, el botón muestra un
|
|
46
|
+
* `<ion-spinner name="dots">` en lugar del icono/label Y queda deshabilitado
|
|
47
|
+
* (una acción en curso no debe ser re-clicable). */
|
|
48
|
+
loading?: (row: Record<string, unknown>) => boolean;
|
|
42
49
|
}
|
|
43
50
|
/** Acción primaria de la topbar (botón destacado). Emite el evento `primaryAction`. */
|
|
44
51
|
export interface DataTablePrimaryAction {
|
package/dist/ok-data-table.js
CHANGED
|
@@ -304,6 +304,10 @@ class OkDataTable extends LitElement {
|
|
|
304
304
|
.empty .empty-ic { display: grid; place-items: center; width: 3.25rem; height: 3.25rem; border-radius: 999px; background: var(--header-background); font-size: 26px; }
|
|
305
305
|
|
|
306
306
|
.actions { display: flex; gap: 0.25rem; justify-content: flex-end; }
|
|
307
|
+
/* Spinner de acción en curso (loading): contenido dentro del ion-button small (Ionic lo fija
|
|
308
|
+
* a 28px en el :host, por eso width/height y no font-size). Cubre tabla y tarjetas: los
|
|
309
|
+
* botones de fila siempre van dentro de .actions. */
|
|
310
|
+
.actions ion-spinner { width: 18px; height: 18px; }
|
|
307
311
|
|
|
308
312
|
/* ── Pie: contador + paginación ──────────────────────────────────────────────────────── */
|
|
309
313
|
.pager { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.55rem 1rem; border-top: 1px solid var(--border-color); background: var(--header-background); font-size: 12.5px; color: var(--color-muted); }
|
|
@@ -813,16 +817,22 @@ class OkDataTable extends LitElement {
|
|
|
813
817
|
return html`
|
|
814
818
|
<div class="actions">
|
|
815
819
|
${this.actions.map(
|
|
816
|
-
(a) =>
|
|
820
|
+
(a) => {
|
|
821
|
+
const loading = a.loading?.(row) === true;
|
|
822
|
+
const disabled = loading || a.disabled?.(row) === true;
|
|
823
|
+
return html`
|
|
817
824
|
<ion-button
|
|
818
825
|
size="small"
|
|
819
826
|
fill="clear"
|
|
820
827
|
color=${a.color ?? "medium"}
|
|
828
|
+
?disabled=${disabled}
|
|
829
|
+
aria-disabled=${disabled ? "true" : nothing}
|
|
821
830
|
@click=${() => this.emit("rowAction", { actionId: a.id, row })}
|
|
822
831
|
>
|
|
823
|
-
${a.icon ? html`<ion-icon slot="icon-only" name=${a.icon}></ion-icon>` : a.label}
|
|
832
|
+
${loading ? html`<ion-spinner slot="icon-only" name="dots"></ion-spinner>` : a.icon ? html`<ion-icon slot="icon-only" name=${a.icon}></ion-icon>` : a.label}
|
|
824
833
|
</ion-button>
|
|
825
|
-
|
|
834
|
+
`;
|
|
835
|
+
}
|
|
826
836
|
)}
|
|
827
837
|
</div>
|
|
828
838
|
`;
|
package/dist/outfitkit.bundle.js
CHANGED
|
@@ -1229,6 +1229,10 @@ class _ extends g {
|
|
|
1229
1229
|
.empty .empty-ic { display: grid; place-items: center; width: 3.25rem; height: 3.25rem; border-radius: 999px; background: var(--header-background); font-size: 26px; }
|
|
1230
1230
|
|
|
1231
1231
|
.actions { display: flex; gap: 0.25rem; justify-content: flex-end; }
|
|
1232
|
+
/* Spinner de acción en curso (loading): contenido dentro del ion-button small (Ionic lo fija
|
|
1233
|
+
* a 28px en el :host, por eso width/height y no font-size). Cubre tabla y tarjetas: los
|
|
1234
|
+
* botones de fila siempre van dentro de .actions. */
|
|
1235
|
+
.actions ion-spinner { width: 18px; height: 18px; }
|
|
1232
1236
|
|
|
1233
1237
|
/* ── Pie: contador + paginación ──────────────────────────────────────────────────────── */
|
|
1234
1238
|
.pager { display: flex; align-items: center; justify-content: space-between; gap: 0.75rem; padding: 0.55rem 1rem; border-top: 1px solid var(--border-color); background: var(--header-background); font-size: 12.5px; color: var(--color-muted); }
|
|
@@ -1636,16 +1640,21 @@ class _ extends g {
|
|
|
1636
1640
|
return this.actions.length ? s`
|
|
1637
1641
|
<div class="actions">
|
|
1638
1642
|
${this.actions.map(
|
|
1639
|
-
(t) =>
|
|
1643
|
+
(t) => {
|
|
1644
|
+
const r = t.loading?.(e) === !0, o = r || t.disabled?.(e) === !0;
|
|
1645
|
+
return s`
|
|
1640
1646
|
<ion-button
|
|
1641
1647
|
size="small"
|
|
1642
1648
|
fill="clear"
|
|
1643
1649
|
color=${t.color ?? "medium"}
|
|
1650
|
+
?disabled=${o}
|
|
1651
|
+
aria-disabled=${o ? "true" : m}
|
|
1644
1652
|
@click=${() => this.emit("rowAction", { actionId: t.id, row: e })}
|
|
1645
1653
|
>
|
|
1646
|
-
${t.icon ? s`<ion-icon slot="icon-only" name=${t.icon}></ion-icon>` : t.label}
|
|
1654
|
+
${r ? s`<ion-spinner slot="icon-only" name="dots"></ion-spinner>` : t.icon ? s`<ion-icon slot="icon-only" name=${t.icon}></ion-icon>` : t.label}
|
|
1647
1655
|
</ion-button>
|
|
1648
|
-
|
|
1656
|
+
`;
|
|
1657
|
+
}
|
|
1649
1658
|
)}
|
|
1650
1659
|
</div>
|
|
1651
1660
|
` : m;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@erplora/outfitkit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "OutfitKit — librería de Web Components (Lit) que CONSTRUYE lo que Ionic no tiene (tree, data-table rica, inline-feedback, kpi/stat, stepper/wizard, calendar, kanban…) sobre primitivos de Ionic. Ionic es la base; OutfitKit cubre los huecos. npm + CDN, imports individuales, CSP-safe. Tema vía tokens --ok-* (fallback a --ion-*).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|