@erplora/outfitkit
Advanced tools
| import './ok-chart.js'; |
| import { LitElement } from 'lit'; | ||
| import '../ok-gauge/ok-gauge.js'; | ||
| import '../ok-chart/ok-chart.js'; | ||
| /** Health status computed by the server for the metric. */ | ||
| export type OkResourceStatus = 'ok' | 'warning' | 'critical' | 'unknown'; | ||
| /** Server-computed metric: current reading + history + status. */ | ||
| export interface OkResourceMetric { | ||
| /** False when the metric could not be read — renders the unreadable state. */ | ||
| known: boolean; | ||
| /** Current reading (0-100 scale; values above 100 clamp visually). */ | ||
| current: number | null; | ||
| /** History as [epochSeconds, value] pairs, oldest first. */ | ||
| points: Array<[number, number]>; | ||
| /** Status decided by the server; drives every color in the panel. */ | ||
| status: OkResourceStatus; | ||
| /** Optional server message shown in the status band / unreadable state. */ | ||
| message: string | null; | ||
| } | ||
| /** Thresholds received from the server; only used for the gauge zone gradient. */ | ||
| export interface OkResourceThresholds { | ||
| warning: number; | ||
| critical: number; | ||
| } | ||
| /** Optional upgrade call-to-action. */ | ||
| export interface OkResourceUpgrade { | ||
| show: boolean; | ||
| message: string; | ||
| url: string; | ||
| } | ||
| export declare class OkResourceUsage extends LitElement { | ||
| static styles: import("lit").CSSResult; | ||
| /** Server-computed metric (reading + history + status). */ | ||
| metric: OkResourceMetric; | ||
| /** Server thresholds; only feed the gauge zone gradient. */ | ||
| thresholds: OkResourceThresholds; | ||
| /** Optional upgrade CTA; the link renders only when `show` is true. */ | ||
| upgrade: OkResourceUpgrade | null; | ||
| /** Panel heading (e.g. 'RAM'). */ | ||
| label: string; | ||
| /** Value suffix (default '%'). */ | ||
| unit: string; | ||
| /** Small chip describing the history range (e.g. '3d'). */ | ||
| rangeLabel: string; | ||
| /** Text of the not-measured state; the consumer passes its translation. */ | ||
| unreadableLabel: string; | ||
| private get statusColor(); | ||
| private get gaugeThresholds(); | ||
| private renderHead; | ||
| private renderUnreadable; | ||
| private renderBody; | ||
| private renderUpgrade; | ||
| render(): unknown; | ||
| } | ||
| declare global { | ||
| interface HTMLElementTagNameMap { | ||
| 'ok-resource-usage': OkResourceUsage; | ||
| } | ||
| } |
| import './ok-resource-usage.js'; |
| import { LitElement, css, nothing, html } from "lit"; | ||
| import { property } from "lit/decorators.js"; | ||
| import { define } from "./define.js"; | ||
| import { z as iconAlertCircleOutline } from "./shared/icons.js"; | ||
| import "./ok-gauge.js"; | ||
| import "./ok-chart.js"; | ||
| var __defProp = Object.defineProperty; | ||
| var __decorateClass = (decorators, target, key, kind) => { | ||
| var result = void 0; | ||
| for (var i = decorators.length - 1, decorator; i >= 0; i--) | ||
| if (decorator = decorators[i]) | ||
| result = decorator(target, key, result) || result; | ||
| if (result) __defProp(target, key, result); | ||
| return result; | ||
| }; | ||
| const STATUS_COLOR = { | ||
| ok: "var(--ion-color-success, #2dd36f)", | ||
| warning: "var(--ion-color-warning, #ffc409)", | ||
| critical: "var(--ion-color-danger, #eb445a)", | ||
| unknown: "var(--ion-color-medium, #92949c)" | ||
| }; | ||
| class OkResourceUsage extends LitElement { | ||
| constructor() { | ||
| super(...arguments); | ||
| this.metric = { | ||
| known: false, | ||
| current: null, | ||
| points: [], | ||
| status: "unknown", | ||
| message: null | ||
| }; | ||
| this.thresholds = { | ||
| warning: 70, | ||
| critical: 80 | ||
| }; | ||
| this.upgrade = null; | ||
| this.label = ""; | ||
| this.unit = "%"; | ||
| this.rangeLabel = ""; | ||
| this.unreadableLabel = "Could not read it"; | ||
| } | ||
| static { | ||
| this.styles = css` | ||
| :host { | ||
| display: block; | ||
| width: 100%; | ||
| box-sizing: border-box; | ||
| --ink: var(--ok-text-color, var(--ion-text-color, #1f2933)); | ||
| --ink-muted: var(--ok-color-medium, var(--ion-color-medium, #92949c)); | ||
| --line: var(--ok-border-color, var(--ion-border-color, #e0e0e0)); | ||
| --track: var(--ok-track-color, var(--ion-color-light, #f4f5f8)); | ||
| } | ||
| .panel { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 0.75rem; | ||
| width: 100%; | ||
| box-sizing: border-box; | ||
| } | ||
| .head { | ||
| display: flex; | ||
| align-items: baseline; | ||
| justify-content: space-between; | ||
| gap: 0.5rem; | ||
| } | ||
| .label { | ||
| font-size: 0.6875rem; | ||
| font-weight: 600; | ||
| color: var(--ink-muted); | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.06em; | ||
| } | ||
| .range { | ||
| font-size: 0.6875rem; | ||
| color: var(--ink-muted); | ||
| font-variant-numeric: tabular-nums; | ||
| border: 1px solid var(--line); | ||
| border-radius: 999px; | ||
| padding: 1px 8px; | ||
| } | ||
| .body { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 1rem; | ||
| } | ||
| .dial { | ||
| flex: 0 0 auto; | ||
| } | ||
| .trend { | ||
| flex: 1 1 auto; | ||
| min-width: 0; | ||
| } | ||
| .status { | ||
| font-size: 0.8125rem; | ||
| border-radius: 8px; | ||
| padding: 6px 10px; | ||
| min-height: 1.25rem; | ||
| box-sizing: border-box; | ||
| } | ||
| .unreadable { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 0.625rem; | ||
| padding: 1rem; | ||
| border: 1px dashed var(--line); | ||
| border-radius: 8px; | ||
| color: var(--ink-muted); | ||
| background: var(--track); | ||
| } | ||
| .unreadable ion-icon { | ||
| font-size: 1.5rem; | ||
| flex-shrink: 0; | ||
| } | ||
| .unreadable-text { | ||
| font-weight: 600; | ||
| color: var(--ink); | ||
| } | ||
| .unreadable-message { | ||
| font-size: 0.8125rem; | ||
| } | ||
| a.upgrade { | ||
| align-self: flex-start; | ||
| font-size: 0.8125rem; | ||
| font-weight: 600; | ||
| text-decoration: none; | ||
| color: var(--ok-primary, var(--ion-color-primary, #3880ff)); | ||
| } | ||
| a.upgrade:hover { | ||
| text-decoration: underline; | ||
| } | ||
| `; | ||
| } | ||
| // Every color in the panel derives from the received status (the server decides). | ||
| get statusColor() { | ||
| return STATUS_COLOR[this.metric?.status] ?? STATUS_COLOR.unknown; | ||
| } | ||
| // Gauge zones from the received thresholds (the one place they are used). | ||
| get gaugeThresholds() { | ||
| const t = this.thresholds ?? { warning: 70, critical: 80 }; | ||
| return [ | ||
| { to: t.warning, color: STATUS_COLOR.ok }, | ||
| { to: t.critical, color: STATUS_COLOR.warning }, | ||
| { to: 100, color: STATUS_COLOR.critical } | ||
| ]; | ||
| } | ||
| renderHead() { | ||
| if (!this.label && !this.rangeLabel) return nothing; | ||
| return html`<div class="head"> | ||
| ${this.label ? html`<span class="label">${this.label}</span>` : nothing} | ||
| ${this.rangeLabel ? html`<span class="range">${this.rangeLabel}</span>` : nothing} | ||
| </div>`; | ||
| } | ||
| // Not-measured state: icon + label, NEVER a gauge or a chart at zero. | ||
| renderUnreadable() { | ||
| return html`<div class="unreadable" role="status"> | ||
| <ion-icon .icon=${iconAlertCircleOutline} aria-hidden="true"></ion-icon> | ||
| <span class="unreadable-text">${this.unreadableLabel}</span> | ||
| ${this.metric?.message ? html`<span class="unreadable-message">${this.metric.message}</span>` : nothing} | ||
| </div>`; | ||
| } | ||
| renderBody() { | ||
| const m = this.metric; | ||
| const real = m.current; | ||
| const shown = real == null ? null : Math.min(real, 100); | ||
| const points = m.points ?? []; | ||
| return html`<div class="body"> | ||
| ${shown != null ? html`<div class="dial" title=${`${real}${this.unit}`}> | ||
| <ok-gauge | ||
| type="ring" | ||
| size="110" | ||
| unit=${this.unit} | ||
| .value=${shown} | ||
| .thresholds=${this.gaugeThresholds} | ||
| .color=${this.statusColor} | ||
| ></ok-gauge> | ||
| </div>` : nothing} | ||
| ${points.length ? html`<div class="trend"> | ||
| <ok-chart | ||
| type="area" | ||
| height="96" | ||
| min="0" | ||
| max="100" | ||
| .series=${[{ color: this.statusColor, data: points.map(([, v]) => v) }]} | ||
| ></ok-chart> | ||
| </div>` : nothing} | ||
| </div> | ||
| <div | ||
| class=${`status status--${m.status}`} | ||
| style=${`background:color-mix(in srgb, ${this.statusColor} 15%, transparent);color:${this.statusColor};`} | ||
| > | ||
| ${m.message ?? nothing} | ||
| </div>`; | ||
| } | ||
| renderUpgrade() { | ||
| if (!this.upgrade?.show) return nothing; | ||
| return html`<a class="upgrade" href=${this.upgrade.url}>${this.upgrade.message}</a>`; | ||
| } | ||
| render() { | ||
| return html`<div class="panel"> | ||
| ${this.renderHead()} | ||
| ${this.metric?.known ? this.renderBody() : this.renderUnreadable()} | ||
| ${this.renderUpgrade()} | ||
| </div>`; | ||
| } | ||
| } | ||
| __decorateClass([ | ||
| property({ attribute: false }) | ||
| ], OkResourceUsage.prototype, "metric"); | ||
| __decorateClass([ | ||
| property({ attribute: false }) | ||
| ], OkResourceUsage.prototype, "thresholds"); | ||
| __decorateClass([ | ||
| property({ attribute: false }) | ||
| ], OkResourceUsage.prototype, "upgrade"); | ||
| __decorateClass([ | ||
| property() | ||
| ], OkResourceUsage.prototype, "label"); | ||
| __decorateClass([ | ||
| property() | ||
| ], OkResourceUsage.prototype, "unit"); | ||
| __decorateClass([ | ||
| property({ attribute: "range-label" }) | ||
| ], OkResourceUsage.prototype, "rangeLabel"); | ||
| __decorateClass([ | ||
| property({ attribute: "unreadable-label" }) | ||
| ], OkResourceUsage.prototype, "unreadableLabel"); | ||
| define("ok-resource-usage", OkResourceUsage); | ||
| export { | ||
| OkResourceUsage | ||
| }; |
+1
-0
@@ -62,2 +62,3 @@ import './components/ok-store/ok-store.js'; | ||
| import './components/ok-chart/ok-chart.js'; | ||
| import './components/ok-resource-usage/ok-resource-usage.js'; | ||
| import './components/ok-donut/ok-donut.js'; | ||
@@ -64,0 +65,0 @@ import './components/ok-heatmap/ok-heatmap.js'; |
@@ -32,2 +32,6 @@ import { LitElement } from 'lit'; | ||
| endpoint: boolean; | ||
| /** Mínimo explícito del eje de valor; si se omite, autoescala al mínimo de los datos. */ | ||
| min?: number; | ||
| /** Máximo explícito del eje de valor; si se omite, autoescala al máximo de los datos. */ | ||
| max?: number; | ||
| /** Texto de la etiqueta del endpoint; si se omite usa el último dato. */ | ||
@@ -34,0 +38,0 @@ endpointLabel: string; |
+2
-0
@@ -91,2 +91,4 @@ export { createStore, store } from './store/store.js'; | ||
| export type { OkChartSeries, OkChartType } from './components/ok-chart/ok-chart.js'; | ||
| export { OkResourceUsage } from './components/ok-resource-usage/ok-resource-usage.js'; | ||
| export type { OkResourceMetric, OkResourceStatus, OkResourceThresholds, OkResourceUpgrade, } from './components/ok-resource-usage/ok-resource-usage.js'; | ||
| export { OkDonut } from './components/ok-donut/ok-donut.js'; | ||
@@ -93,0 +95,0 @@ export type { OkDonutSlice } from './components/ok-donut/ok-donut.js'; |
+2
-0
@@ -64,2 +64,3 @@ import { createStore, store } from "./store.js"; | ||
| import { OkChart } from "./ok-chart.js"; | ||
| import { OkResourceUsage } from "./ok-resource-usage.js"; | ||
| import { OkDonut } from "./ok-donut.js"; | ||
@@ -169,2 +170,3 @@ import { OkHeatmap } from "./ok-heatmap.js"; | ||
| OkReceipt, | ||
| OkResourceUsage, | ||
| OkReveal, | ||
@@ -171,0 +173,0 @@ OkRichText, |
+21
-5
@@ -119,10 +119,20 @@ import { LitElement, css, svg, html } from "lit"; | ||
| } | ||
| // Mínimo/máximo global de todas las series (las barras crecen desde 0). | ||
| // Mínimo/máximo del eje: los props explícitos `min`/`max` mandan; sin ellos, | ||
| // autoescala al mínimo/máximo global de las series (las barras crecen desde 0). | ||
| get bounds() { | ||
| const all = []; | ||
| for (const s of this.series ?? []) for (const v of s.data ?? []) all.push(v); | ||
| if (!all.length) return { min: 0, max: 1 }; | ||
| let min = Math.min(...all); | ||
| let max = Math.max(...all); | ||
| if (this.type !== "line") min = Math.min(min, 0); | ||
| let min; | ||
| let max; | ||
| if (!all.length) { | ||
| min = 0; | ||
| max = 1; | ||
| } else { | ||
| min = Math.min(...all); | ||
| max = Math.max(...all); | ||
| if (this.type !== "line") min = Math.min(min, 0); | ||
| if (min === max) max = min + 1; | ||
| } | ||
| if (this.min != null && Number.isFinite(this.min)) min = this.min; | ||
| if (this.max != null && Number.isFinite(this.max)) max = this.max; | ||
| if (min === max) max = min + 1; | ||
@@ -321,2 +331,8 @@ return { min, max }; | ||
| __decorateClass([ | ||
| property({ type: Number }) | ||
| ], OkChart.prototype, "min"); | ||
| __decorateClass([ | ||
| property({ type: Number }) | ||
| ], OkChart.prototype, "max"); | ||
| __decorateClass([ | ||
| property() | ||
@@ -323,0 +339,0 @@ ], OkChart.prototype, "endpointLabel"); |
@@ -62,2 +62,3 @@ import "./ok-store.js"; | ||
| import "./ok-chart.js"; | ||
| import "./ok-resource-usage.js"; | ||
| import "./ok-donut.js"; | ||
@@ -64,0 +65,0 @@ import "./ok-heatmap.js"; |
+2
-1
| { | ||
| "name": "@erplora/outfitkit", | ||
| "version": "0.1.37", | ||
| "version": "0.1.38", | ||
| "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-*).", | ||
@@ -94,2 +94,3 @@ "type": "module", | ||
| "./ok-chart": "./dist/ok-chart.js", | ||
| "./ok-resource-usage": "./dist/ok-resource-usage.js", | ||
| "./ok-donut": "./dist/ok-donut.js", | ||
@@ -96,0 +97,0 @@ "./ok-heatmap": "./dist/ok-heatmap.js", |
+1
-0
@@ -122,2 +122,3 @@ # OutfitKit (`@erplora/outfitkit`) | ||
| | `ok-chart` | Gráfico SVG inline (línea/área/barras) sin librerías externas. | — | | ||
| | `ok-resource-usage` | Panel de recurso 0-100 % (gauge + histórico + banda de estado + CTA de upgrade); todo precalculado por el servidor. | — | | ||
| | `ok-donut` | Donut/pie proporcional con leyenda opcional. | — | | ||
@@ -124,0 +125,0 @@ | `ok-heatmap` | Heatmap de calendario (estilo GitHub) o anual, con cuantiles e intensidad. | — | |
Sorry, the diff of this file is too big to display
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
2131119
0.79%295
1.37%61598
0.86%460
0.22%177
1.14%