@wrrdev/fenix-ui 1.1.10 → 1.1.11
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 +20 -2
- package/dist/assets/{font-CF0TXsAP.js → font-DPeWTz-n.js} +58 -13
- package/dist/assets/font-DPeWTz-n.js.map +1 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts +4 -0
- package/dist/components/breadcrumb/breadcrumb.d.ts.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js +12 -2
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/table/table.d.ts +16 -0
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +28 -1
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/toast/toast.d.ts +8 -0
- package/dist/components/toast/toast.d.ts.map +1 -1
- package/dist/components/toast/toast.js +13 -0
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/tooltip/directive.d.ts +8 -0
- package/dist/components/tooltip/directive.d.ts.map +1 -1
- package/dist/components/tooltip/directive.js +59 -18
- package/dist/components/tooltip/directive.js.map +1 -1
- package/dist/docs/app.d.ts.map +1 -1
- package/dist/fenix-ui.umd.min.js +142 -142
- package/dist/fenix-ui3.esm.js +8 -13
- package/dist/fenix-ui3.esm.js.map +1 -1
- package/dist/fenix-ui4.esm.js +184 -93
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/dist/icons/font.d.ts +23 -0
- package/dist/icons/font.d.ts.map +1 -1
- package/dist/icons/font.js +54 -8
- package/dist/icons/font.js.map +1 -1
- package/dist/icons/index.d.ts +7 -2
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/index.js +6 -11
- package/dist/icons/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/font-CF0TXsAP.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"mappings":";;;;;;;;AAiBA,MAAM,aAAwC;AAAA,EAC5C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AACX;AAGA,MAAM,YAAuC;AAAA,EAC3C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AACR;AAEA,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4GV,MAAM,gBAAgB,YAAY;AAAA,EAgEvC,cAAc;AACZ;AAjEG;AA8DL,SAAQ,QAA8C;AAIpD,SAAK,aAAa,EAAE,MAAM,QAAQ;AAAA,EACpC;AAAA,EAlEA,WAAW,qBAAqB;AAC9B,WAAO,CAAC,QAAQ,SAAS,WAAW,YAAY,QAAQ,QAAQ,YAAY,iBAAiB;AAAA,EAC/F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,IAAI,WAAsD;AACxD,QAAI,CAAC,KAAK,aAAa,UAAU,EAAG,QAAO;AAC3C,UAAM,IAAI,KAAK,aAAa,UAAU;AACtC,WAAO,MAAM,WAAW,MAAM,WAAW,IAAI;AAAA,EAC/C;AAAA,EACA,IAAI,SAAS,OAAkD;AAC7D,QAAI,MAAO,MAAK,aAAa,YAAY,KAAK;AAAA,QACzC,MAAK,gBAAgB,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,IAAI,iBAAmC;AACrC,WAAO,KAAK,aAAa,iBAAiB,MAAM,UAAU,UAAU;AAAA,EACtE;AAAA,EACA,IAAI,eAAe,OAAyB;AAC1C,SAAK,aAAa,mBAAmB,KAAK;AAAA,EAC5C;AAAA;AAAA,EAGA,IAAY,eAAuB;AACjC,UAAM,MAAM,OAAO,KAAK,aAAa,UAAU,KAAK,MAAM;AAC1D,QAAI,QAAQ,EAAG,QAAO;AACtB,WAAO,KAAK,IAAI,OAAO,MAAM,GAAG,IAAI,MAAO,KAAK,GAAI;AAAA,EACtD;AAAA;AAAA,EAGA,IAAI,OAAqC;AACvC,UAAM,IAAI,KAAK,aAAa,MAAM;AAClC,WAAO,MAAM,WAAW,MAAM,SAAS,IAAI;AAAA,EAC7C;AAAA,EACA,IAAI,KAAK,OAAqC;AAC5C,QAAI,MAAO,MAAK,aAAa,QAAQ,KAAK;AAAA,QACrC,MAAK,gBAAgB,MAAM;AAAA,EAClC;AAAA,EASA,oBAAoB;AAClB,QAAI,CAAC,KAAK,WAAY,kCAAiB;AAGvC,UAAM,OAAO,KAAK;AAClB,QAAI,OAAO,KAAK,CAAC,KAAK,OAAO;AAC3B,WAAK,QAAQ,WAAW,MAAM,KAAK,WAAW,IAAI;AAAA,IACpD;AACA,0BAAK,qCAAL;AAAA,EACF;AAAA,EAEA,uBAAuB;AACrB,QAAI,KAAK,MAAO,cAAa,KAAK,KAAK;AACvC,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,yBAAyB,MAAc;AAGrC,QAAI,SAAS,cAAc,SAAS,cAAc,SAAS,qBAAqB,SAAS,QAAQ;AAC/F,4BAAK,qCAAL;AAAA,IACF;AAAA,EACF;AAAA,EAuCA,UAAU;AACR,QAAI,KAAK,MAAO,cAAa,KAAK,KAAK;AACvC,SAAK,QAAQ;AACb,UAAM,OAAO,KAAK,WAAY,cAAc,OAAO;AACnD,QAAI,MAAM;AACR,WAAK,UAAU,IAAI,SAAS;AAC5B,iBAAW,MAAM,KAAK,UAAU,GAAG;AAAA,IACrC,OAAO;AACL,WAAK;AAAA,IACP;AAAA,EACF;AA2BF;AAvKO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoGL,6BAAsB;AACpB,QAAM,OAAO,KAAK,YAAY,cAA2B,OAAO;AAChE,MAAI,CAAC,KAAM;AACX,QAAM,MAAM,KAAK,WAAY,cAA2B,WAAW;AACnE,QAAM,OAAO,KAAK;AAClB,QAAM,MAAM,KAAK;AACjB,QAAM,aAAa,CAAC,CAAC,QAAQ,MAAM;AAEnC,MAAI,CAAC,YAAY;AACf,SAAK;AACL,SAAK,UAAU,OAAO,UAAU;AAChC;AAAA,EACF;AACA,MAAI,CAAC,KAAK;AACR,SAAK,WAAY,cAAc,OAAO,EAAG,mBAAmB,aAAa,mEAAmE;AAAA,EAC9I;AACA,QAAM,KAAK,KAAK,WAAY,cAA2B,WAAW;AAClE,KAAG,MAAM,YAAY,oBAAoB,GAAG,GAAG,IAAI;AAEnD,KAAG,MAAM,YAAY,2BAA2B,GAAG,KAAK,cAAc,SAAS;AAC/E,KAAG,UAAU,OAAO,KAAK;AAGzB,MAAI,SAAS,SAAU,IAAG,UAAU,IAAI,KAAK;AAG7C,OAAK,UAAU,IAAI,UAAU;AAC7B,wBAAsB,MAAM,sBAAsB,MAAM,KAAK,UAAU,OAAO,UAAU,CAAC,CAAC;AAC5F;AAcA,uBAAU;AACR,QAAM,OAAQ,KAAK,aAAa,MAAM,KAAK;AAC3C,QAAM,QAAQ,KAAK,aAAa,OAAO,KAAK;AAC5C,QAAM,MAAM,KAAK,aAAa,SAAS,KAAK;AAC5C,QAAM,QAAQ,WAAW,IAAI,KAAK,WAAW;AAC7C,QAAM,WAAW,KAAK,aAAa,MAAM;AACzC,QAAM,WAAW,CAAC,CAAC,YAAY,oBAAoB,KAAK,QAAQ;AAChE,MAAI,SAAU,0BAAK,OAAO,sBAAa;AACvC,QAAM,cAAc,WAAW,WAAW,UAAU,IAAI,KAAK,UAAU;AACvE,OAAK,WAAY,YAAY;AAAA,eAClB,QAAQ;AAAA,wCACiB,KAAK;AAAA,2BAClB,WAAW,aAAa,EAAE,KAAK,WAAW;AAAA;AAAA,YAEzD,QAAQ,8BAA8B,EAAE;AAAA,YACxC,MAAM,4BAA4B,EAAE;AAAA;AAAA;AAAA;AAI5C,QAAM,IAAI,KAAK,WAAY,cAA2B,QAAQ;AAC9D,QAAM,IAAI,KAAK,WAAY,cAA2B,MAAM;AAC5D,MAAI,KAAK,cAAc;AACvB,MAAI,KAAK,cAAc;AACvB,OAAK,WAAY,cAAc,QAAQ,EAAG,iBAAiB,SAAS,MAAM,KAAK,SAAS;AAC1F;AAIF,SAAS,UAAU,UAAsC;AACvD,MAAI,SAAS,SAAS,cAA2B,0BAA0B,QAAQ,IAAI;AACvF,MAAI,CAAC,QAAQ;AACX,aAAS,SAAS,cAAc,KAAK;AACrC,WAAO,aAAa,wBAAwB,QAAQ;AACpD,UAAM,CAAC,UAAU,UAAU,IAAI,SAAS,MAAM,GAAG;AACjD,UAAM,QAAQ,OAAO;AACrB,UAAM,WAAW;AACjB,UAAM,QAAQ,IAAI;AAClB,QAAI,eAAe,UAAU;AAC3B,YAAM,OAAO;AACb,YAAM,YAAY;AAAA,IACpB,OAAO;AACL,YAAM,UAAU,IAAI;AAAA,IACtB;AACA,UAAM,UAAU;AAChB,UAAM,gBAAgB;AACtB,UAAM,MAAM;AACZ,UAAM,SAAS;AACf,UAAM,gBAAgB;AAGtB,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAyBA,MAAM,SAAS;AAAA,EAAf;AACE,SAAQ,MAAM;AACd,SAAQ,0BAAU;AA4BlB,mBAAU,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,WAAW,GAAG,GAAG,CAAC;AACnF,iBAAQ,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,SAAS,GAAG,GAAG,CAAC;AAC/E,gBAAO,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,QAAQ,GAAG,GAAG,CAAC;AAC7E,mBAAU,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,WAAW,GAAG,GAAG,CAAC;AAAA;AAAA,EA7BnF,KAAK,MAAiB,OAAe,eAAuC,MAA6B;AACvG,UAAM,KAAK,EAAE,KAAK;AAIlB,UAAM,UAAU,OAAO,kBAAkB,WAAW,gBAAgB,eAAe;AACnF,UAAM,IAAkB,EAAE,GAAI,OAAO,kBAAkB,YAAY,gBAAgB,gBAAgB,QAAY,GAAG;AAClH,UAAM,KAAK,SAAS,cAAc,UAAU;AAC5C,OAAG,aAAa,QAAQ,IAAI;AAC5B,OAAG,aAAa,SAAS,KAAK;AAC9B,QAAI,QAAS,IAAG,aAAa,WAAW,OAAO;AAC/C,OAAG,aAAa,YAAY,EAAE,YAAY,WAAW;AACrD,OAAG,aAAa,YAAY,OAAO,EAAE,YAAY,GAAI,CAAC;AACtD,QAAI,EAAE,KAAM,IAAG,aAAa,QAAQ,EAAE,IAAI;AAC1C,QAAI,EAAE,SAAU,IAAG,aAAa,YAAY,EAAE,QAAQ;AACtD,QAAI,EAAE,eAAgB,IAAG,aAAa,mBAAmB,EAAE,cAAc;AACzE,cAAU,EAAE,YAAY,WAAW,EAAE,YAAY,EAAE;AACnD,SAAK,IAAI,IAAI,IAAI,EAAE;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAY;AAChB,SAAK,IAAI,IAAI,EAAE,GAAG;AAClB,SAAK,IAAI,OAAO,EAAE;AAAA,EACpB;AAMF;AAEO,MAAM,aAAa,IAAI;AAE7B,WAAkD,aAAa;AAEzD,SAAS,gBAAgB;AAC9B,MAAI,CAAC,eAAe,IAAI,UAAU,EAAG,gBAAe,OAAO,YAAY,OAAO;AAChF","names":[],"ignoreList":[],"sources":["../../../src/components/toast/toast.ts"],"sourcesContent":["/**\n * <fx-toast> — notificações imperativas com posição e duração configuráveis.\n *\n * Atributos: kind (success|error|info|warning), title, message,\n * position (top-left|top-center|top-right|bottom-left|bottom-center|bottom-right),\n * duration (ms; 0 = fixo até fechar), mode ('light'|'dark' — força o esquema de\n * cores do card independentemente do tema global), progress (booleano ou\n * 'always'|'hover'|'paused' — barra de contagem regressiva na cor do kind),\n * progress-origin (left|right — lado pelo qual a barra esvazia; padrão left).\n */\nconst POSITIONS = [\n 'top-left', 'top-center', 'top-right',\n 'bottom-left', 'bottom-center', 'bottom-right',\n] as const;\nexport type ToastPosition = (typeof POSITIONS)[number];\ntype ToastKind = 'success' | 'error' | 'info' | 'warning';\n\nconst KIND_COLOR: Record<ToastKind, string> = {\n success: 'var(--fx-color-success, #10b981)',\n error: 'var(--fx-color-danger, #f43f5e)',\n info: 'var(--fx-color-info, #0ea5e9)',\n warning: 'var(--fx-color-warning, #f59e0b)',\n};\n\n/** Ícone exibido no lugar da bolinha, conforme a variante. */\nconst KIND_ICON: Record<ToastKind, string> = {\n success: '✓',\n error: '✕',\n warning: '!',\n info: 'i',\n};\n\nconst CARD_CSS = `\n:host { display: contents; pointer-events: auto; }\n/* Modo explícito: sobrepõe os tokens herdados do tema global SOMENTE neste card. */\n:host([mode='dark']) {\n --fx-surface-background: #1e293b;\n --fx-text-default: #f1f5f9;\n --fx-text-muted: #94a3b8;\n --fx-border-default: #334155;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .5);\n --fx-surface-surface-hover: rgba(255, 255, 255, .08);\n}\n:host([mode='light']) {\n --fx-surface-background: #ffffff;\n --fx-text-default: #1e293b;\n --fx-text-muted: #64748b;\n --fx-border-default: #e2e8f0;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .14);\n --fx-surface-surface-hover: rgba(0, 0, 0, .05);\n}\n.fx-icon {\n font-family: var(--fx-icon-font, 'Fenix Icons');\n font-weight: normal;\n font-style: normal;\n line-height: 1;\n letter-spacing: normal;\n display: inline-block;\n white-space: nowrap;\n direction: ltr;\n font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24;\n -webkit-font-smoothing: antialiased;\n user-select: none;\n}\n.card {\n display: flex; align-items: flex-start; gap: 10px;\n min-width: 260px; max-width: 360px;\n background: var(--fx-surface-background, #fff);\n color: var(--fx-text-default, #1e293b);\n border: 1px solid var(--fx-border-default, #e2e8f0);\n border-left: 4px solid var(--kind);\n border-radius: var(--fx-radius-md, 8px);\n box-shadow: var(--fx-shadow-lg, 0 10px 30px rgba(0,0,0,.14));\n padding: var(--fx-space-sm, 8px) var(--fx-space-md, 12px);\n font-family: inherit;\n animation: fx-toast-in .25s ease;\n}\n@keyframes fx-toast-in { from { opacity: 0; transform: translateY(-6px); } }\n.card.leaving { opacity: 0; transition: opacity .2s ease; }\n.icon {\n width: 22px; height: 22px; border-radius: 50%;\n background: var(--kind);\n color: #fff;\n display: flex; align-items: center; justify-content: center;\n font-size: 12px; font-weight: 700;\n flex: none; margin-top: 2px;\n user-select: none;\n}\n.body { flex: 1; min-width: 0; }\n.title { font-weight: 600; font-size: 14px; }\n.msg { font-size: calc(var(--fx-font-size, 14px) - 2px); color: var(--fx-text-muted, #64748b); margin-top: 2px; word-break: break-word; white-space: pre-line; }\n.close {\n all: unset; cursor: pointer; flex: none; line-height: 1;\n color: var(--fx-text-muted, #64748b); font-size: 15px; padding: 2px 4px; border-radius: 4px;\n}\n.close:hover { color: var(--fx-color-danger, #f43f5e); background: var(--fx-surface-surface-hover, rgba(0,0,0,.05)); }\n\n/* --- Barra de contagem regressiva (atributo progress) ---\n Fica na base do card e esvazia durante a duração, na cor do kind (--kind) —\n ou seja, acompanha success/error/warning/info sem configuração extra.\n\n O lado é configurável pelo atributo progress-origin (padrão: left):\n left → a barra drena da direita para a esquerda (ancorada à esquerda)\n right → a barra drena da esquerda para a direita (ancorada à direita)\n A origem entra como custom property, então os keyframes são compartilhados.\n\n Usa escalaX em vez de width: a animação escreve só na compositor, sem\n reflow por frame. */\n.card { position: relative; overflow: hidden; }\n.progress {\n position: absolute;\n left: 0;\n bottom: 0;\n width: 100%;\n height: 3px;\n background: var(--kind);\n transform-origin: var(--toast-progress-origin, left center);\n transform: scaleX(1);\n border-radius: 0 0 var(--fx-radius-md, 8px) var(--fx-radius-md, 8px);\n pointer-events: none;\n will-change: transform;\n}\n.progress.run { animation: fx-toast-drain var(--toast-duration, 4000ms) linear forwards; }\n/* No modo hover a animação fica parada até o cursor/foco entrar no card. */\n.card:hover .progress.run,\n.card:focus-within .progress.run { animation-play-state: running; }\n@keyframes fx-toast-drain {\n from { transform: scaleX(1); }\n to { transform: scaleX(0); }\n}\n/* A barra só começa a correr no 2º frame: no 1º ela já apareceria zerada\n (o elemento acabou de ser inserido e a animação aplica o estado final). */\n.card.entering .progress { animation: none; }\n/* Ao dispensar, congela a barra: ela some junto com o card em .2s. */\n.card.leaving .progress { animation-play-state: paused; }\n@media (prefers-reduced-motion: reduce) {\n .progress.run { animation-duration: 0.01ms; }\n}\n`;\n\nexport class FxToast extends HTMLElement {\n static get observedAttributes() {\n return ['kind', 'title', 'message', 'duration', 'icon', 'mode', 'progress', 'progress-origin'];\n }\n\n /**\n * Exibe a barra de contagem regressiva na base do card.\n *\n * A barra esvazia da esquerda para a direita durante exatamente `duration`\n * e usa a cor do `kind`. Com `duration=\"0\"` (toast fixo) a barra é\n * omitida — não há tempo restante a representar.\n *\n * - `'always'` (ou atributo vazio): barra sempre correndo.\n * - `'hover'`: a barra só corre enquanto o cursor estiver sobre o toast;\n * ao sair, a animação pausa, então dá tempo de ler a mensagem.\n * - `'paused'`: barra visível, porém parada.\n */\n get progress(): 'always' | 'hover' | 'paused' | undefined {\n if (!this.hasAttribute('progress')) return undefined;\n const v = this.getAttribute('progress');\n return v === 'hover' || v === 'paused' ? v : 'always';\n }\n set progress(value: 'always' | 'hover' | 'paused' | undefined) {\n if (value) this.setAttribute('progress', value);\n else this.removeAttribute('progress');\n }\n\n /**\n * Lado a partir do qual a barra esvazia.\n *\n * - `'left'` (padrão): a barra fica ancorada à esquerda e drena da direita\n * para a esquerda.\n * - `'right'`: a barra fica ancorada à direita e drena da esquerda para a\n * direita (espelhado).\n *\n * Só tem efeito junto com `progress`; qualquer valor fora da lista cai em\n * `'left'`.\n */\n get progressOrigin(): 'left' | 'right' {\n return this.getAttribute('progress-origin') === 'right' ? 'right' : 'left';\n }\n set progressOrigin(value: 'left' | 'right') {\n this.setAttribute('progress-origin', value);\n }\n\n /** Duração efetiva em ms (0 = fixo). Mesma regra usada no timer. */\n private get safeDuration(): number {\n const dur = Number(this.getAttribute('duration') ?? '4000');\n if (dur === 0) return 0;\n return Math.max(Number.isNaN(dur) ? 4000 : dur, 1000);\n }\n\n /** Esquema de cores forçado do card: 'light' | 'dark' | undefined (segue o tema). */\n get mode(): 'light' | 'dark' | undefined {\n const m = this.getAttribute('mode');\n return m === 'light' || m === 'dark' ? m : undefined;\n }\n set mode(value: 'light' | 'dark' | undefined) {\n if (value) this.setAttribute('mode', value);\n else this.removeAttribute('mode');\n }\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n if (!this.shadowRoot!.firstChild) this.#render();\n // Durações mínimas: valores muito baixos (ex.: digitação parcial no\n // playground) são elevados para 1s; 0 permanece fixo até fechar.\n const safe = this.safeDuration;\n if (safe > 0 && !this.timer) {\n this.timer = setTimeout(() => this.dismiss(), safe);\n }\n this.#syncProgress();\n }\n\n disconnectedCallback() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n }\n\n attributeChangedCallback(name: string) {\n // A duração é lida em dois lugares (timer + barra): se mudar com o toast\n // na tela, ambos precisam se realinhar.\n if (name === 'duration' || name === 'progress' || name === 'progress-origin' || name === 'kind') {\n this.#syncProgress();\n }\n }\n\n /**\n * Aplica (ou remove) a barra de contagem regressiva.\n *\n * A barra só existe com `progress` e `duration > 0`. O tempo da animação\n * vem de `--toast-duration`, sempre igual ao do `setTimeout` que fecha o\n * toast — assim a barra e o desaparecimento nunca desalinham.\n */\n #syncProgress(): void {\n const card = this.shadowRoot?.querySelector<HTMLElement>('.card');\n if (!card) return;\n const bar = this.shadowRoot!.querySelector<HTMLElement>('.progress');\n const mode = this.progress;\n const dur = this.safeDuration;\n const shouldShow = !!mode && dur > 0;\n\n if (!shouldShow) {\n bar?.remove();\n card.classList.remove('entering');\n return;\n }\n if (!bar) {\n this.shadowRoot!.querySelector('.card')!.insertAdjacentHTML('beforeend', '<span class=\"progress\" part=\"progress\" aria-hidden=\"true\"></span>');\n }\n const el = this.shadowRoot!.querySelector<HTMLElement>('.progress')!;\n el.style.setProperty('--toast-duration', `${dur}ms`);\n // `left` (padrão) => origem à esquerda; `right` espelha a drenagem.\n el.style.setProperty('--toast-progress-origin', `${this.progressOrigin} center`);\n el.classList.remove('run');\n // `paused` mantém a barra cheia e imóvel; `always` e `hover` deixam a\n // animação correr (no `hover`, o CSS só a dispara sob :hover do host).\n if (mode !== 'paused') el.classList.add('run');\n // 1 frame sem animação para o browser registrar o estado inicial cheio\n // antes de a barra começar a esvaziar.\n card.classList.add('entering');\n requestAnimationFrame(() => requestAnimationFrame(() => card.classList.remove('entering')));\n }\n\n dismiss() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n const card = this.shadowRoot!.querySelector('.card');\n if (card) {\n card.classList.add('leaving');\n setTimeout(() => this.remove(), 200);\n } else {\n this.remove();\n }\n }\n\n #render() {\n const kind = (this.getAttribute('kind') || 'info') as ToastKind;\n const title = this.getAttribute('title') ?? '';\n const msg = this.getAttribute('message') ?? '';\n const color = KIND_COLOR[kind] ?? KIND_COLOR.info;\n const iconName = this.getAttribute('icon');\n const isFxIcon = !!iconName && /^[a-z][a-z0-9_]*$/.test(iconName);\n if (isFxIcon) void import('../../icons');\n const iconContent = isFxIcon ? iconName : KIND_ICON[kind] ?? KIND_ICON.info;\n this.shadowRoot!.innerHTML = `\n <style>${CARD_CSS}</style>\n <div class=\"card\" style=\"--kind:${color}\" role=\"status\" part=\"card\">\n <span class=\"icon${isFxIcon ? ' fx-icon' : ''}\">${iconContent}</span>\n <div class=\"body\">\n ${title ? '<div class=\"title\"></div>' : ''}\n ${msg ? '<div class=\"msg\"></div>' : ''}\n </div>\n <button class=\"close\" aria-label=\"Fechar\">✕</button>\n </div>`;\n const t = this.shadowRoot!.querySelector<HTMLElement>('.title');\n const m = this.shadowRoot!.querySelector<HTMLElement>('.msg');\n if (t) t.textContent = title;\n if (m) m.textContent = msg;\n this.shadowRoot!.querySelector('.close')!.addEventListener('click', () => this.dismiss());\n }\n}\n\n/* ---------- Regiões por posição na tela ---------- */\nfunction regionFor(position: ToastPosition): HTMLElement {\n let region = document.querySelector<HTMLElement>(`[data-fx-toast-region=\"${position}\"]`);\n if (!region) {\n region = document.createElement('div');\n region.setAttribute('data-fx-toast-region', position);\n const [vertical, horizontal] = position.split('-') as [string, string];\n const style = region.style as CSSStyleDeclaration & Record<string, string>;\n style.position = 'fixed';\n style[vertical] = '16px';\n if (horizontal === 'center') {\n style.left = '50%';\n style.transform = 'translateX(-50%)';\n } else {\n style[horizontal] = '16px';\n }\n style.display = 'flex';\n style.flexDirection = 'column';\n style.gap = '8px';\n style.zIndex = '1100';\n style.pointerEvents = 'none';\n // Os cards reabilitam pointer-events via `:host { pointer-events: auto }` (herança),\n // sem necessidade de MutationObserver.\n document.body.appendChild(region);\n }\n return region;\n}\n\nexport interface ToastOptions {\n /** Texto secundário (também aceito como 2º argumento dos métodos). */\n message?: string;\n position?: ToastPosition;\n /** Tempo em ms até sumir. 0 = fixo até fechar. Padrão: 4000 */\n duration?: number;\n /**\n * Barra de contagem regressiva na base do card, na cor do `kind`.\n * `'always'` (padrão) corre sempre; `'hover'` só enquanto o cursor estiver\n * sobre o toast; `'paused'` exibe a barra cheia, porém imóvel.\n * Sem efeito com `duration: 0` (não há tempo restante a representar).\n */\n progress?: 'always' | 'hover' | 'paused';\n /**\n * Lado a partir do qual a barra esvazia. `'left'` (padrão) deixa a barra\n * ancorada à esquerda; `'right'` a ancora à direita (espelhado).\n * Só tem efeito junto com `progress`.\n */\n progressOrigin?: 'left' | 'right';\n /** Força o esquema de cores do card, independentemente do tema global. */\n mode?: 'light' | 'dark';\n}\n\nclass ToastApi {\n private seq = 0;\n private map = new Map<number, FxToast>();\n\n push(kind: ToastKind, title: string, messageOrOpts?: string | ToastOptions, opts?: ToastOptions): number {\n const id = ++this.seq;\n // Assinaturas suportadas:\n // push(kind, title, options)\n // push(kind, title, message, options) <- usada pela doc e mais intuitiva\n const message = typeof messageOrOpts === 'string' ? messageOrOpts : messageOrOpts?.message;\n const o: ToastOptions = { ...(typeof messageOrOpts === 'object' && messageOrOpts ? messageOrOpts : undefined), ...opts };\n const el = document.createElement('fx-toast') as FxToast;\n el.setAttribute('kind', kind);\n el.setAttribute('title', title);\n if (message) el.setAttribute('message', message);\n el.setAttribute('position', o.position ?? 'top-right');\n el.setAttribute('duration', String(o.duration ?? 4000));\n if (o.mode) el.setAttribute('mode', o.mode);\n if (o.progress) el.setAttribute('progress', o.progress);\n if (o.progressOrigin) el.setAttribute('progress-origin', o.progressOrigin);\n regionFor(o.position ?? 'top-right').appendChild(el);\n this.map.set(id, el);\n return id;\n }\n\n close(id: number) {\n this.map.get(id)?.dismiss();\n this.map.delete(id);\n }\n\n success = (t: string, m?: string, o?: ToastOptions) => this.push('success', t, m, o);\n error = (t: string, m?: string, o?: ToastOptions) => this.push('error', t, m, o);\n info = (t: string, m?: string, o?: ToastOptions) => this.push('info', t, m, o);\n warning = (t: string, m?: string, o?: ToastOptions) => this.push('warning', t, m, o);\n}\n\nexport const FenixToast = new ToastApi();\n// Exposto globalmente para uso em HTML puro (onclick=\"FenixToast.success(...)\")\n(globalThis as unknown as Record<string, unknown>).FenixToast = FenixToast;\n\nexport function defineFxToast() {\n if (!customElements.get('fx-toast')) customElements.define('fx-toast', FxToast);\n}\n\n"],"file":"components/toast/toast.js"}
|
|
1
|
+
{"version":3,"mappings":";;;;;;;;AAsBA,MAAM,gBAAgB;AAEtB,MAAM,aAAwC;AAAA,EAC5C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,MAAM;AAAA,EACN,SAAS;AACX;AAGA,MAAM,YAAuC;AAAA,EAC3C,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AACR;AAEA,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4GV,MAAM,gBAAgB,YAAY;AAAA,EAgEvC,cAAc;AACZ;AAjEG;AA8DL,SAAQ,QAA8C;AAIpD,SAAK,aAAa,EAAE,MAAM,QAAQ;AAAA,EACpC;AAAA,EAlEA,WAAW,qBAAqB;AAC9B,WAAO,CAAC,QAAQ,SAAS,WAAW,YAAY,QAAQ,QAAQ,YAAY,iBAAiB;AAAA,EAC/F;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,IAAI,WAAsD;AACxD,QAAI,CAAC,KAAK,aAAa,UAAU,EAAG,QAAO;AAC3C,UAAM,IAAI,KAAK,aAAa,UAAU;AACtC,WAAO,MAAM,WAAW,MAAM,WAAW,IAAI;AAAA,EAC/C;AAAA,EACA,IAAI,SAAS,OAAkD;AAC7D,QAAI,MAAO,MAAK,aAAa,YAAY,KAAK;AAAA,QACzC,MAAK,gBAAgB,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,IAAI,iBAAmC;AACrC,WAAO,KAAK,aAAa,iBAAiB,MAAM,UAAU,UAAU;AAAA,EACtE;AAAA,EACA,IAAI,eAAe,OAAyB;AAC1C,SAAK,aAAa,mBAAmB,KAAK;AAAA,EAC5C;AAAA;AAAA,EAGA,IAAY,eAAuB;AACjC,UAAM,MAAM,OAAO,KAAK,aAAa,UAAU,KAAK,MAAM;AAC1D,QAAI,QAAQ,EAAG,QAAO;AACtB,WAAO,KAAK,IAAI,OAAO,MAAM,GAAG,IAAI,MAAO,KAAK,GAAI;AAAA,EACtD;AAAA;AAAA,EAGA,IAAI,OAAqC;AACvC,UAAM,IAAI,KAAK,aAAa,MAAM;AAClC,WAAO,MAAM,WAAW,MAAM,SAAS,IAAI;AAAA,EAC7C;AAAA,EACA,IAAI,KAAK,OAAqC;AAC5C,QAAI,MAAO,MAAK,aAAa,QAAQ,KAAK;AAAA,QACrC,MAAK,gBAAgB,MAAM;AAAA,EAClC;AAAA,EASA,oBAAoB;AAClB,QAAI,CAAC,KAAK,WAAY,kCAAiB;AAGvC,UAAM,OAAO,KAAK;AAClB,QAAI,OAAO,KAAK,CAAC,KAAK,OAAO;AAC3B,WAAK,QAAQ,WAAW,MAAM,KAAK,WAAW,IAAI;AAAA,IACpD;AACA,0BAAK,qCAAL;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,uBAAuB;AACrB,QAAI,KAAK,MAAO,cAAa,KAAK,KAAK;AACvC,SAAK,QAAQ;AACb,SAAK,cAAc,IAAI,YAAY,eAAe,EAAE,SAAS,OAAO,UAAU,OAAO,CAAC;AAAA,EACxF;AAAA,EAEA,yBAAyB,MAAc;AAGrC,QAAI,SAAS,cAAc,SAAS,cAAc,SAAS,qBAAqB,SAAS,QAAQ;AAC/F,4BAAK,qCAAL;AAAA,IACF;AAAA,EACF;AAAA,EAuCA,UAAU;AACR,QAAI,KAAK,MAAO,cAAa,KAAK,KAAK;AACvC,SAAK,QAAQ;AACb,UAAM,OAAO,KAAK,WAAY,cAAc,OAAO;AACnD,QAAI,MAAM;AACR,WAAK,UAAU,IAAI,SAAS;AAC5B,iBAAW,MAAM,KAAK,UAAU,GAAG;AAAA,IACrC,OAAO;AACL,WAAK;AAAA,IACP;AAAA,EACF;AA2BF;AAhLO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6GL,6BAAsB;AACpB,QAAM,OAAO,KAAK,YAAY,cAA2B,OAAO;AAChE,MAAI,CAAC,KAAM;AACX,QAAM,MAAM,KAAK,WAAY,cAA2B,WAAW;AACnE,QAAM,OAAO,KAAK;AAClB,QAAM,MAAM,KAAK;AACjB,QAAM,aAAa,CAAC,CAAC,QAAQ,MAAM;AAEnC,MAAI,CAAC,YAAY;AACf,SAAK;AACL,SAAK,UAAU,OAAO,UAAU;AAChC;AAAA,EACF;AACA,MAAI,CAAC,KAAK;AACR,SAAK,WAAY,cAAc,OAAO,EAAG,mBAAmB,aAAa,mEAAmE;AAAA,EAC9I;AACA,QAAM,KAAK,KAAK,WAAY,cAA2B,WAAW;AAClE,KAAG,MAAM,YAAY,oBAAoB,GAAG,GAAG,IAAI;AAEnD,KAAG,MAAM,YAAY,2BAA2B,GAAG,KAAK,cAAc,SAAS;AAC/E,KAAG,UAAU,OAAO,KAAK;AAGzB,MAAI,SAAS,SAAU,IAAG,UAAU,IAAI,KAAK;AAG7C,OAAK,UAAU,IAAI,UAAU;AAC7B,wBAAsB,MAAM,sBAAsB,MAAM,KAAK,UAAU,OAAO,UAAU,CAAC,CAAC;AAC5F;AAcA,uBAAU;AACR,QAAM,OAAQ,KAAK,aAAa,MAAM,KAAK;AAC3C,QAAM,QAAQ,KAAK,aAAa,OAAO,KAAK;AAC5C,QAAM,MAAM,KAAK,aAAa,SAAS,KAAK;AAC5C,QAAM,QAAQ,WAAW,IAAI,KAAK,WAAW;AAC7C,QAAM,WAAW,KAAK,aAAa,MAAM;AACzC,QAAM,WAAW,CAAC,CAAC,YAAY,oBAAoB,KAAK,QAAQ;AAChE,MAAI,SAAU,0BAAK,OAAO,sBAAa;AACvC,QAAM,cAAc,WAAW,WAAW,UAAU,IAAI,KAAK,UAAU;AACvE,OAAK,WAAY,YAAY;AAAA,eAClB,QAAQ;AAAA,wCACiB,KAAK;AAAA,2BAClB,WAAW,aAAa,EAAE,KAAK,WAAW;AAAA;AAAA,YAEzD,QAAQ,8BAA8B,EAAE;AAAA,YACxC,MAAM,4BAA4B,EAAE;AAAA;AAAA;AAAA;AAI5C,QAAM,IAAI,KAAK,WAAY,cAA2B,QAAQ;AAC9D,QAAM,IAAI,KAAK,WAAY,cAA2B,MAAM;AAC5D,MAAI,KAAK,cAAc;AACvB,MAAI,KAAK,cAAc;AACvB,OAAK,WAAY,cAAc,QAAQ,EAAG,iBAAiB,SAAS,MAAM,KAAK,SAAS;AAC1F;AAIF,SAAS,UAAU,UAAsC;AACvD,MAAI,SAAS,SAAS,cAA2B,0BAA0B,QAAQ,IAAI;AACvF,MAAI,CAAC,QAAQ;AACX,aAAS,SAAS,cAAc,KAAK;AACrC,WAAO,aAAa,wBAAwB,QAAQ;AACpD,UAAM,CAAC,UAAU,UAAU,IAAI,SAAS,MAAM,GAAG;AACjD,UAAM,QAAQ,OAAO;AACrB,UAAM,WAAW;AACjB,UAAM,QAAQ,IAAI;AAClB,QAAI,eAAe,UAAU;AAC3B,YAAM,OAAO;AACb,YAAM,YAAY;AAAA,IACpB,OAAO;AACL,YAAM,UAAU,IAAI;AAAA,IACtB;AACA,UAAM,UAAU;AAChB,UAAM,gBAAgB;AACtB,UAAM,MAAM;AACZ,UAAM,SAAS;AACf,UAAM,gBAAgB;AAGtB,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC;AACA,SAAO;AACT;AAyBA,MAAM,SAAS;AAAA,EAAf;AACE,SAAQ,MAAM;AACd,SAAQ,0BAAU;AAiClB,mBAAU,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,WAAW,GAAG,GAAG,CAAC;AACnF,iBAAQ,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,SAAS,GAAG,GAAG,CAAC;AAC/E,gBAAO,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,QAAQ,GAAG,GAAG,CAAC;AAC7E,mBAAU,CAAC,GAAW,GAAY,MAAqB,KAAK,KAAK,WAAW,GAAG,GAAG,CAAC;AAAA;AAAA,EAlCnF,KAAK,MAAiB,OAAe,eAAuC,MAA6B;AACvG,UAAM,KAAK,EAAE,KAAK;AAIlB,UAAM,UAAU,OAAO,kBAAkB,WAAW,gBAAgB,eAAe;AACnF,UAAM,IAAkB,EAAE,GAAI,OAAO,kBAAkB,YAAY,gBAAgB,gBAAgB,QAAY,GAAG;AAClH,UAAM,KAAK,SAAS,cAAc,UAAU;AAC5C,OAAG,aAAa,QAAQ,IAAI;AAC5B,OAAG,aAAa,SAAS,KAAK;AAC9B,QAAI,QAAS,IAAG,aAAa,WAAW,OAAO;AAC/C,OAAG,aAAa,YAAY,EAAE,YAAY,WAAW;AACrD,OAAG,aAAa,YAAY,OAAO,EAAE,YAAY,GAAI,CAAC;AACtD,QAAI,EAAE,KAAM,IAAG,aAAa,QAAQ,EAAE,IAAI;AAC1C,QAAI,EAAE,SAAU,IAAG,aAAa,YAAY,EAAE,QAAQ;AACtD,QAAI,EAAE,eAAgB,IAAG,aAAa,mBAAmB,EAAE,cAAc;AACzE,cAAU,EAAE,YAAY,WAAW,EAAE,YAAY,EAAE;AAGnD,OAAG,iBAAiB,eAAe,MAAM;AACvC,UAAI,KAAK,IAAI,IAAI,EAAE,MAAM,GAAI,MAAK,IAAI,OAAO,EAAE;AAAA,IACjD,CAAC;AACD,SAAK,IAAI,IAAI,IAAI,EAAE;AACnB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAY;AAChB,SAAK,IAAI,IAAI,EAAE,GAAG;AAClB,SAAK,IAAI,OAAO,EAAE;AAAA,EACpB;AAMF;AAEO,MAAM,aAAa,IAAI;AAE7B,WAAkD,aAAa;AAEzD,SAAS,gBAAgB;AAC9B,MAAI,CAAC,eAAe,IAAI,UAAU,EAAG,gBAAe,OAAO,YAAY,OAAO;AAChF","names":[],"ignoreList":[],"sources":["../../../src/components/toast/toast.ts"],"sourcesContent":["/**\n * <fx-toast> — notificações imperativas com posição e duração configuráveis.\n *\n * Atributos: kind (success|error|info|warning), title, message,\n * position (top-left|top-center|top-right|bottom-left|bottom-center|bottom-right),\n * duration (ms; 0 = fixo até fechar), mode ('light'|'dark' — força o esquema de\n * cores do card independentemente do tema global), progress (booleano ou\n * 'always'|'hover'|'paused' — barra de contagem regressiva na cor do kind),\n * progress-origin (left|right — lado pelo qual a barra esvazia; padrão left).\n */\nconst POSITIONS = [\n 'top-left', 'top-center', 'top-right',\n 'bottom-left', 'bottom-center', 'bottom-right',\n] as const;\nexport type ToastPosition = (typeof POSITIONS)[number];\ntype ToastKind = 'success' | 'error' | 'info' | 'warning';\n\n/**\n * Evento interno disparado quando o <fx-toast> sai do DOM.\n * A API `FenixToast` o escuta para remover a entrada do mapa interno —\n * é o que impede o acúmulo de toasts já dispensados.\n */\nconst REMOVED_EVENT = 'fx-toast:removed';\n\nconst KIND_COLOR: Record<ToastKind, string> = {\n success: 'var(--fx-color-success, #10b981)',\n error: 'var(--fx-color-danger, #f43f5e)',\n info: 'var(--fx-color-info, #0ea5e9)',\n warning: 'var(--fx-color-warning, #f59e0b)',\n};\n\n/** Ícone exibido no lugar da bolinha, conforme a variante. */\nconst KIND_ICON: Record<ToastKind, string> = {\n success: '✓',\n error: '✕',\n warning: '!',\n info: 'i',\n};\n\nconst CARD_CSS = `\n:host { display: contents; pointer-events: auto; }\n/* Modo explícito: sobrepõe os tokens herdados do tema global SOMENTE neste card. */\n:host([mode='dark']) {\n --fx-surface-background: #1e293b;\n --fx-text-default: #f1f5f9;\n --fx-text-muted: #94a3b8;\n --fx-border-default: #334155;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .5);\n --fx-surface-surface-hover: rgba(255, 255, 255, .08);\n}\n:host([mode='light']) {\n --fx-surface-background: #ffffff;\n --fx-text-default: #1e293b;\n --fx-text-muted: #64748b;\n --fx-border-default: #e2e8f0;\n --fx-shadow-lg: 0 10px 30px rgba(0, 0, 0, .14);\n --fx-surface-surface-hover: rgba(0, 0, 0, .05);\n}\n.fx-icon {\n font-family: var(--fx-icon-font, 'Fenix Icons');\n font-weight: normal;\n font-style: normal;\n line-height: 1;\n letter-spacing: normal;\n display: inline-block;\n white-space: nowrap;\n direction: ltr;\n font-variation-settings: 'FILL' 0, 'wght' 500, 'GRAD' 0, 'opsz' 24;\n -webkit-font-smoothing: antialiased;\n user-select: none;\n}\n.card {\n display: flex; align-items: flex-start; gap: 10px;\n min-width: 260px; max-width: 360px;\n background: var(--fx-surface-background, #fff);\n color: var(--fx-text-default, #1e293b);\n border: 1px solid var(--fx-border-default, #e2e8f0);\n border-left: 4px solid var(--kind);\n border-radius: var(--fx-radius-md, 8px);\n box-shadow: var(--fx-shadow-lg, 0 10px 30px rgba(0,0,0,.14));\n padding: var(--fx-space-sm, 8px) var(--fx-space-md, 12px);\n font-family: inherit;\n animation: fx-toast-in .25s ease;\n}\n@keyframes fx-toast-in { from { opacity: 0; transform: translateY(-6px); } }\n.card.leaving { opacity: 0; transition: opacity .2s ease; }\n.icon {\n width: 22px; height: 22px; border-radius: 50%;\n background: var(--kind);\n color: #fff;\n display: flex; align-items: center; justify-content: center;\n font-size: 12px; font-weight: 700;\n flex: none; margin-top: 2px;\n user-select: none;\n}\n.body { flex: 1; min-width: 0; }\n.title { font-weight: 600; font-size: 14px; }\n.msg { font-size: calc(var(--fx-font-size, 14px) - 2px); color: var(--fx-text-muted, #64748b); margin-top: 2px; word-break: break-word; white-space: pre-line; }\n.close {\n all: unset; cursor: pointer; flex: none; line-height: 1;\n color: var(--fx-text-muted, #64748b); font-size: 15px; padding: 2px 4px; border-radius: 4px;\n}\n.close:hover { color: var(--fx-color-danger, #f43f5e); background: var(--fx-surface-surface-hover, rgba(0,0,0,.05)); }\n\n/* --- Barra de contagem regressiva (atributo progress) ---\n Fica na base do card e esvazia durante a duração, na cor do kind (--kind) —\n ou seja, acompanha success/error/warning/info sem configuração extra.\n\n O lado é configurável pelo atributo progress-origin (padrão: left):\n left → a barra drena da direita para a esquerda (ancorada à esquerda)\n right → a barra drena da esquerda para a direita (ancorada à direita)\n A origem entra como custom property, então os keyframes são compartilhados.\n\n Usa escalaX em vez de width: a animação escreve só na compositor, sem\n reflow por frame. */\n.card { position: relative; overflow: hidden; }\n.progress {\n position: absolute;\n left: 0;\n bottom: 0;\n width: 100%;\n height: 3px;\n background: var(--kind);\n transform-origin: var(--toast-progress-origin, left center);\n transform: scaleX(1);\n border-radius: 0 0 var(--fx-radius-md, 8px) var(--fx-radius-md, 8px);\n pointer-events: none;\n will-change: transform;\n}\n.progress.run { animation: fx-toast-drain var(--toast-duration, 4000ms) linear forwards; }\n/* No modo hover a animação fica parada até o cursor/foco entrar no card. */\n.card:hover .progress.run,\n.card:focus-within .progress.run { animation-play-state: running; }\n@keyframes fx-toast-drain {\n from { transform: scaleX(1); }\n to { transform: scaleX(0); }\n}\n/* A barra só começa a correr no 2º frame: no 1º ela já apareceria zerada\n (o elemento acabou de ser inserido e a animação aplica o estado final). */\n.card.entering .progress { animation: none; }\n/* Ao dispensar, congela a barra: ela some junto com o card em .2s. */\n.card.leaving .progress { animation-play-state: paused; }\n@media (prefers-reduced-motion: reduce) {\n .progress.run { animation-duration: 0.01ms; }\n}\n`;\n\nexport class FxToast extends HTMLElement {\n static get observedAttributes() {\n return ['kind', 'title', 'message', 'duration', 'icon', 'mode', 'progress', 'progress-origin'];\n }\n\n /**\n * Exibe a barra de contagem regressiva na base do card.\n *\n * A barra esvazia da esquerda para a direita durante exatamente `duration`\n * e usa a cor do `kind`. Com `duration=\"0\"` (toast fixo) a barra é\n * omitida — não há tempo restante a representar.\n *\n * - `'always'` (ou atributo vazio): barra sempre correndo.\n * - `'hover'`: a barra só corre enquanto o cursor estiver sobre o toast;\n * ao sair, a animação pausa, então dá tempo de ler a mensagem.\n * - `'paused'`: barra visível, porém parada.\n */\n get progress(): 'always' | 'hover' | 'paused' | undefined {\n if (!this.hasAttribute('progress')) return undefined;\n const v = this.getAttribute('progress');\n return v === 'hover' || v === 'paused' ? v : 'always';\n }\n set progress(value: 'always' | 'hover' | 'paused' | undefined) {\n if (value) this.setAttribute('progress', value);\n else this.removeAttribute('progress');\n }\n\n /**\n * Lado a partir do qual a barra esvazia.\n *\n * - `'left'` (padrão): a barra fica ancorada à esquerda e drena da direita\n * para a esquerda.\n * - `'right'`: a barra fica ancorada à direita e drena da esquerda para a\n * direita (espelhado).\n *\n * Só tem efeito junto com `progress`; qualquer valor fora da lista cai em\n * `'left'`.\n */\n get progressOrigin(): 'left' | 'right' {\n return this.getAttribute('progress-origin') === 'right' ? 'right' : 'left';\n }\n set progressOrigin(value: 'left' | 'right') {\n this.setAttribute('progress-origin', value);\n }\n\n /** Duração efetiva em ms (0 = fixo). Mesma regra usada no timer. */\n private get safeDuration(): number {\n const dur = Number(this.getAttribute('duration') ?? '4000');\n if (dur === 0) return 0;\n return Math.max(Number.isNaN(dur) ? 4000 : dur, 1000);\n }\n\n /** Esquema de cores forçado do card: 'light' | 'dark' | undefined (segue o tema). */\n get mode(): 'light' | 'dark' | undefined {\n const m = this.getAttribute('mode');\n return m === 'light' || m === 'dark' ? m : undefined;\n }\n set mode(value: 'light' | 'dark' | undefined) {\n if (value) this.setAttribute('mode', value);\n else this.removeAttribute('mode');\n }\n\n private timer: ReturnType<typeof setTimeout> | null = null;\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n }\n\n connectedCallback() {\n if (!this.shadowRoot!.firstChild) this.#render();\n // Durações mínimas: valores muito baixos (ex.: digitação parcial no\n // playground) são elevados para 1s; 0 permanece fixo até fechar.\n const safe = this.safeDuration;\n if (safe > 0 && !this.timer) {\n this.timer = setTimeout(() => this.dismiss(), safe);\n }\n this.#syncProgress();\n }\n\n /**\n * Limpa recursos ao sair do DOM e avisa a API imperativa (`FenixToast`).\n *\n * O aviso é essencial: `dismiss()` (timer ou botão de fechar) tira o\n * elemento do DOM mas, sem isto, `ToastApi.map` continuaria segurando uma\n * referência forte para cada toast já dispensado — crescimento de memória\n * sem limite em uso prolongado.\n */\n disconnectedCallback() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n this.dispatchEvent(new CustomEvent(REMOVED_EVENT, { bubbles: false, composed: false }));\n }\n\n attributeChangedCallback(name: string) {\n // A duração é lida em dois lugares (timer + barra): se mudar com o toast\n // na tela, ambos precisam se realinhar.\n if (name === 'duration' || name === 'progress' || name === 'progress-origin' || name === 'kind') {\n this.#syncProgress();\n }\n }\n\n /**\n * Aplica (ou remove) a barra de contagem regressiva.\n *\n * A barra só existe com `progress` e `duration > 0`. O tempo da animação\n * vem de `--toast-duration`, sempre igual ao do `setTimeout` que fecha o\n * toast — assim a barra e o desaparecimento nunca desalinham.\n */\n #syncProgress(): void {\n const card = this.shadowRoot?.querySelector<HTMLElement>('.card');\n if (!card) return;\n const bar = this.shadowRoot!.querySelector<HTMLElement>('.progress');\n const mode = this.progress;\n const dur = this.safeDuration;\n const shouldShow = !!mode && dur > 0;\n\n if (!shouldShow) {\n bar?.remove();\n card.classList.remove('entering');\n return;\n }\n if (!bar) {\n this.shadowRoot!.querySelector('.card')!.insertAdjacentHTML('beforeend', '<span class=\"progress\" part=\"progress\" aria-hidden=\"true\"></span>');\n }\n const el = this.shadowRoot!.querySelector<HTMLElement>('.progress')!;\n el.style.setProperty('--toast-duration', `${dur}ms`);\n // `left` (padrão) => origem à esquerda; `right` espelha a drenagem.\n el.style.setProperty('--toast-progress-origin', `${this.progressOrigin} center`);\n el.classList.remove('run');\n // `paused` mantém a barra cheia e imóvel; `always` e `hover` deixam a\n // animação correr (no `hover`, o CSS só a dispara sob :hover do host).\n if (mode !== 'paused') el.classList.add('run');\n // 1 frame sem animação para o browser registrar o estado inicial cheio\n // antes de a barra começar a esvaziar.\n card.classList.add('entering');\n requestAnimationFrame(() => requestAnimationFrame(() => card.classList.remove('entering')));\n }\n\n dismiss() {\n if (this.timer) clearTimeout(this.timer);\n this.timer = null;\n const card = this.shadowRoot!.querySelector('.card');\n if (card) {\n card.classList.add('leaving');\n setTimeout(() => this.remove(), 200);\n } else {\n this.remove();\n }\n }\n\n #render() {\n const kind = (this.getAttribute('kind') || 'info') as ToastKind;\n const title = this.getAttribute('title') ?? '';\n const msg = this.getAttribute('message') ?? '';\n const color = KIND_COLOR[kind] ?? KIND_COLOR.info;\n const iconName = this.getAttribute('icon');\n const isFxIcon = !!iconName && /^[a-z][a-z0-9_]*$/.test(iconName);\n if (isFxIcon) void import('../../icons');\n const iconContent = isFxIcon ? iconName : KIND_ICON[kind] ?? KIND_ICON.info;\n this.shadowRoot!.innerHTML = `\n <style>${CARD_CSS}</style>\n <div class=\"card\" style=\"--kind:${color}\" role=\"status\" part=\"card\">\n <span class=\"icon${isFxIcon ? ' fx-icon' : ''}\">${iconContent}</span>\n <div class=\"body\">\n ${title ? '<div class=\"title\"></div>' : ''}\n ${msg ? '<div class=\"msg\"></div>' : ''}\n </div>\n <button class=\"close\" aria-label=\"Fechar\">✕</button>\n </div>`;\n const t = this.shadowRoot!.querySelector<HTMLElement>('.title');\n const m = this.shadowRoot!.querySelector<HTMLElement>('.msg');\n if (t) t.textContent = title;\n if (m) m.textContent = msg;\n this.shadowRoot!.querySelector('.close')!.addEventListener('click', () => this.dismiss());\n }\n}\n\n/* ---------- Regiões por posição na tela ---------- */\nfunction regionFor(position: ToastPosition): HTMLElement {\n let region = document.querySelector<HTMLElement>(`[data-fx-toast-region=\"${position}\"]`);\n if (!region) {\n region = document.createElement('div');\n region.setAttribute('data-fx-toast-region', position);\n const [vertical, horizontal] = position.split('-') as [string, string];\n const style = region.style as CSSStyleDeclaration & Record<string, string>;\n style.position = 'fixed';\n style[vertical] = '16px';\n if (horizontal === 'center') {\n style.left = '50%';\n style.transform = 'translateX(-50%)';\n } else {\n style[horizontal] = '16px';\n }\n style.display = 'flex';\n style.flexDirection = 'column';\n style.gap = '8px';\n style.zIndex = '1100';\n style.pointerEvents = 'none';\n // Os cards reabilitam pointer-events via `:host { pointer-events: auto }` (herança),\n // sem necessidade de MutationObserver.\n document.body.appendChild(region);\n }\n return region;\n}\n\nexport interface ToastOptions {\n /** Texto secundário (também aceito como 2º argumento dos métodos). */\n message?: string;\n position?: ToastPosition;\n /** Tempo em ms até sumir. 0 = fixo até fechar. Padrão: 4000 */\n duration?: number;\n /**\n * Barra de contagem regressiva na base do card, na cor do `kind`.\n * `'always'` (padrão) corre sempre; `'hover'` só enquanto o cursor estiver\n * sobre o toast; `'paused'` exibe a barra cheia, porém imóvel.\n * Sem efeito com `duration: 0` (não há tempo restante a representar).\n */\n progress?: 'always' | 'hover' | 'paused';\n /**\n * Lado a partir do qual a barra esvazia. `'left'` (padrão) deixa a barra\n * ancorada à esquerda; `'right'` a ancora à direita (espelhado).\n * Só tem efeito junto com `progress`.\n */\n progressOrigin?: 'left' | 'right';\n /** Força o esquema de cores do card, independentemente do tema global. */\n mode?: 'light' | 'dark';\n}\n\nclass ToastApi {\n private seq = 0;\n private map = new Map<number, FxToast>();\n\n push(kind: ToastKind, title: string, messageOrOpts?: string | ToastOptions, opts?: ToastOptions): number {\n const id = ++this.seq;\n // Assinaturas suportadas:\n // push(kind, title, options)\n // push(kind, title, message, options) <- usada pela doc e mais intuitiva\n const message = typeof messageOrOpts === 'string' ? messageOrOpts : messageOrOpts?.message;\n const o: ToastOptions = { ...(typeof messageOrOpts === 'object' && messageOrOpts ? messageOrOpts : undefined), ...opts };\n const el = document.createElement('fx-toast') as FxToast;\n el.setAttribute('kind', kind);\n el.setAttribute('title', title);\n if (message) el.setAttribute('message', message);\n el.setAttribute('position', o.position ?? 'top-right');\n el.setAttribute('duration', String(o.duration ?? 4000));\n if (o.mode) el.setAttribute('mode', o.mode);\n if (o.progress) el.setAttribute('progress', o.progress);\n if (o.progressOrigin) el.setAttribute('progress-origin', o.progressOrigin);\n regionFor(o.position ?? 'top-right').appendChild(el);\n // O toast avisa quando sai do DOM (timer esgotado, botão de fechar ou\n // remoção externa). Sem este sync o mapa retém cada toast da sessão.\n el.addEventListener(REMOVED_EVENT, () => {\n if (this.map.get(id) === el) this.map.delete(id);\n });\n this.map.set(id, el);\n return id;\n }\n\n close(id: number) {\n this.map.get(id)?.dismiss();\n this.map.delete(id);\n }\n\n success = (t: string, m?: string, o?: ToastOptions) => this.push('success', t, m, o);\n error = (t: string, m?: string, o?: ToastOptions) => this.push('error', t, m, o);\n info = (t: string, m?: string, o?: ToastOptions) => this.push('info', t, m, o);\n warning = (t: string, m?: string, o?: ToastOptions) => this.push('warning', t, m, o);\n}\n\nexport const FenixToast = new ToastApi();\n// Exposto globalmente para uso em HTML puro (onclick=\"FenixToast.success(...)\")\n(globalThis as unknown as Record<string, unknown>).FenixToast = FenixToast;\n\nexport function defineFxToast() {\n if (!customElements.get('fx-toast')) customElements.define('fx-toast', FxToast);\n}\n\n"],"file":"components/toast/toast.js"}
|
|
@@ -24,6 +24,14 @@ export interface FxTooltipConfig {
|
|
|
24
24
|
* defineFxTooltipDirective();
|
|
25
25
|
*/
|
|
26
26
|
export declare function defineFxTooltipDirective(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Quantos tooltips a diretiva mantém vivos agora.
|
|
29
|
+
*
|
|
30
|
+
* Deve igualar o nº de elementos `[fx-tooltip]` no documento. Se crescer após
|
|
31
|
+
* trocar de tela, há comportamento retido (vazamento). Pensado para diagnóstico
|
|
32
|
+
* em produção e para os testes de regressão.
|
|
33
|
+
*/
|
|
34
|
+
export declare function fxTooltipActiveCount(): number;
|
|
27
35
|
/**
|
|
28
36
|
* Destrói a diretiva fx-tooltip e limpa todos os recursos.
|
|
29
37
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directive.d.ts","sourceRoot":"","sources":["../../../src/components/tooltip/directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,IAAI,EAAE,OAAO,CAAC;CACf;
|
|
1
|
+
{"version":3,"file":"directive.d.ts","sourceRoot":"","sources":["../../../src/components/tooltip/directive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;IAC9C,IAAI,EAAE,OAAO,CAAC;CACf;AAkXD;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAS/C;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -90,6 +90,8 @@ const _FxTooltipManager = class _FxTooltipManager {
|
|
|
90
90
|
this.observer = null;
|
|
91
91
|
this.behaviors = /* @__PURE__ */ new Map();
|
|
92
92
|
this.injectedStyles = null;
|
|
93
|
+
this._pending = { added: [] };
|
|
94
|
+
this._flushScheduled = false;
|
|
93
95
|
this.injectStyles();
|
|
94
96
|
this.observe();
|
|
95
97
|
this.processExistingElements();
|
|
@@ -100,6 +102,20 @@ const _FxTooltipManager = class _FxTooltipManager {
|
|
|
100
102
|
}
|
|
101
103
|
return _FxTooltipManager.instance;
|
|
102
104
|
}
|
|
105
|
+
/** Quantos tooltips estão ativos agora (0 se a diretiva nunca foi iniciada). */
|
|
106
|
+
static peekActiveCount() {
|
|
107
|
+
return _FxTooltipManager.instance?.behaviors.size ?? 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Quantos tooltips estão ativamente gerenciados.
|
|
111
|
+
*
|
|
112
|
+
* Deve acompanhar o número de elementos `[fx-tooltip]` no documento: um
|
|
113
|
+
* valor crescente depois de trocar de tela significa comportamento retido
|
|
114
|
+
* (vazamento). Usado nos testes de regressão e no diagnóstico em produção.
|
|
115
|
+
*/
|
|
116
|
+
get activeCount() {
|
|
117
|
+
return this.behaviors.size;
|
|
118
|
+
}
|
|
103
119
|
injectStyles() {
|
|
104
120
|
if (document.getElementById("fx-tooltip-directive-styles")) return;
|
|
105
121
|
this.injectedStyles = document.createElement("style");
|
|
@@ -109,32 +125,55 @@ const _FxTooltipManager = class _FxTooltipManager {
|
|
|
109
125
|
}
|
|
110
126
|
observe() {
|
|
111
127
|
this.observer = new MutationObserver((mutations) => {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (target.hasAttribute("fx-tooltip")) {
|
|
124
|
-
this.processElement(target);
|
|
125
|
-
} else if (this.behaviors.has(target)) {
|
|
126
|
-
this.removeBehavior(target);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
128
|
+
this._pending.added.push(...mutations);
|
|
129
|
+
if (this._flushScheduled) return;
|
|
130
|
+
this._flushScheduled = true;
|
|
131
|
+
const run = () => {
|
|
132
|
+
this._flushScheduled = false;
|
|
133
|
+
const batch = this._pending;
|
|
134
|
+
this._pending = { added: [] };
|
|
135
|
+
this._flush(batch.added);
|
|
136
|
+
};
|
|
137
|
+
if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
|
|
138
|
+
else setTimeout(run, 0);
|
|
130
139
|
});
|
|
131
140
|
this.observer.observe(document.body, {
|
|
132
141
|
childList: true,
|
|
133
142
|
subtree: true,
|
|
134
143
|
attributes: true,
|
|
144
|
+
// `attributeFilter` é o que mantém o custo baixo: o browser só notifica
|
|
145
|
+
// mudanças nestes 3 atributos, e não em qualquer atributo da página.
|
|
135
146
|
attributeFilter: ["fx-tooltip", "fx-tooltip-position", "fx-tooltip-html"]
|
|
136
147
|
});
|
|
137
148
|
}
|
|
149
|
+
/** Um passe de verificação sobre todos os registros acumulados. */
|
|
150
|
+
_flush(records) {
|
|
151
|
+
const toProcess = /* @__PURE__ */ new Set();
|
|
152
|
+
const toRemove = /* @__PURE__ */ new Set();
|
|
153
|
+
for (const mutation of records) {
|
|
154
|
+
if (mutation.type === "childList") {
|
|
155
|
+
mutation.addedNodes.forEach((node) => {
|
|
156
|
+
if (node instanceof HTMLElement) {
|
|
157
|
+
toProcess.add(node);
|
|
158
|
+
node.querySelectorAll("[fx-tooltip]").forEach((el) => toProcess.add(el));
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
mutation.removedNodes.forEach((node) => {
|
|
162
|
+
if (node instanceof HTMLElement) {
|
|
163
|
+
toRemove.add(node);
|
|
164
|
+
node.querySelectorAll("[fx-tooltip]").forEach((el) => toRemove.add(el));
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
if (mutation.type === "attributes" && mutation.target instanceof HTMLElement) {
|
|
169
|
+
const target = mutation.target;
|
|
170
|
+
if (target.hasAttribute("fx-tooltip")) toProcess.add(target);
|
|
171
|
+
else toRemove.add(target);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
toRemove.forEach((el) => this.removeBehavior(el));
|
|
175
|
+
toProcess.forEach((el) => this.processElement(el));
|
|
176
|
+
}
|
|
138
177
|
processExistingElements() {
|
|
139
178
|
document.querySelectorAll("[fx-tooltip]").forEach((el) => this.processElement(el));
|
|
140
179
|
}
|
|
@@ -157,6 +196,8 @@ const _FxTooltipManager = class _FxTooltipManager {
|
|
|
157
196
|
}
|
|
158
197
|
}
|
|
159
198
|
destroy() {
|
|
199
|
+
this._pending.added = [];
|
|
200
|
+
this._flushScheduled = false;
|
|
160
201
|
if (this.observer) {
|
|
161
202
|
this.observer.disconnect();
|
|
162
203
|
this.observer = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directive.js","sources":["../../../src/components/tooltip/directive.ts"],"sourcesContent":["/**\n * Diretiva fx-tooltip — Aplica tooltip em qualquer elemento HTML.\n *\n * Uso:\n * <div fx-tooltip=\"Texto simples\">Div com tooltip</div>\n * <button fx-tooltip=\"Clique\" fx-tooltip-position=\"bottom\">Botão</button>\n * <span fx-tooltip=\"<strong>HTML</strong>\" fx-tooltip-html>Texto</span>\n *\n * Atributos:\n * fx-tooltip — Conteúdo do tooltip (texto ou HTML)\n * fx-tooltip-position — Posição: top | bottom | left | right (padrão: top)\n * fx-tooltip-html — Se presente, trata o conteúdo como HTML\n */\n\nexport interface FxTooltipConfig {\n position: 'top' | 'bottom' | 'left' | 'right';\n html: boolean;\n}\n\nconst DEFAULT_CONFIG: FxTooltipConfig = {\n position: 'top',\n html: false,\n};\n\n/**\n * Classe que gerencia o comportamento de tooltip em um elemento.\n */\nclass TooltipBehavior {\n private element: HTMLElement;\n private tooltipElement: HTMLSpanElement | null = null;\n private config: FxTooltipConfig;\n private showTimeout: number | null = null;\n private hideTimeout: number | null = null;\n\n constructor(element: HTMLElement) {\n this.element = element;\n this.config = this.parseConfig();\n this.init();\n }\n\n private parseConfig(): FxTooltipConfig {\n const position = this.element.getAttribute('fx-tooltip-position') as FxTooltipConfig['position'];\n return {\n position: position && ['top', 'bottom', 'left', 'right'].includes(position) ? position : DEFAULT_CONFIG.position,\n html: this.element.hasAttribute('fx-tooltip-html'),\n };\n }\n\n private init(): void {\n const computedStyle = window.getComputedStyle(this.element);\n if (computedStyle.position === 'static') {\n this.element.style.position = 'relative';\n }\n this.createTooltipElement();\n this.element.addEventListener('mouseenter', this.show);\n this.element.addEventListener('mouseleave', this.hide);\n this.element.addEventListener('focus', this.show);\n this.element.addEventListener('blur', this.hide);\n }\n\n private createTooltipElement(): void {\n const content = this.element.getAttribute('fx-tooltip');\n if (!content) return;\n this.tooltipElement = document.createElement('span');\n this.tooltipElement.setAttribute('role', 'tooltip');\n this.tooltipElement.setAttribute('data-fx-tooltip', '');\n this.tooltipElement.className = `fx-tooltip-bubble fx-tooltip-${this.config.position}`;\n if (this.config.html) {\n this.tooltipElement.innerHTML = content;\n } else {\n this.tooltipElement.textContent = content;\n }\n this.element.appendChild(this.tooltipElement);\n }\n\n private show = (): void => {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout);\n this.hideTimeout = null;\n }\n this.showTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.add('fx-tooltip-visible');\n }\n }, 100);\n };\n\n private hide = (): void => {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout);\n this.showTimeout = null;\n }\n this.hideTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.remove('fx-tooltip-visible');\n }\n }, 50);\n };\n\n public update(): void {\n this.config = this.parseConfig();\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n this.createTooltipElement();\n }\n\n public destroy(): void {\n this.element.removeEventListener('mouseenter', this.show);\n this.element.removeEventListener('mouseleave', this.hide);\n this.element.removeEventListener('focus', this.show);\n this.element.removeEventListener('blur', this.hide);\n if (this.showTimeout) clearTimeout(this.showTimeout);\n if (this.hideTimeout) clearTimeout(this.hideTimeout);\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n }\n}\n\n/**\n * Gerencia todas as instâncias de tooltip no documento.\n */\nclass FxTooltipManager {\n private static instance: FxTooltipManager | null = null;\n private observer: MutationObserver | null = null;\n private behaviors: Map<HTMLElement, TooltipBehavior> = new Map();\n private injectedStyles: HTMLStyleElement | null = null;\n\n private constructor() {\n this.injectStyles();\n this.observe();\n this.processExistingElements();\n }\n\n public static getInstance(): FxTooltipManager {\n if (!FxTooltipManager.instance) {\n FxTooltipManager.instance = new FxTooltipManager();\n }\n return FxTooltipManager.instance;\n }\n\n private injectStyles(): void {\n if (document.getElementById('fx-tooltip-directive-styles')) return;\n this.injectedStyles = document.createElement('style');\n this.injectedStyles.id = 'fx-tooltip-directive-styles';\n this.injectedStyles.textContent = getTooltipStyles();\n document.head.appendChild(this.injectedStyles);\n }\n\n private observe(): void {\n this.observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n this.processElement(node);\n node.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => this.processElement(el));\n }\n });\n }\n if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {\n const target = mutation.target;\n if (target.hasAttribute('fx-tooltip')) {\n this.processElement(target);\n } else if (this.behaviors.has(target)) {\n this.removeBehavior(target);\n }\n }\n }\n });\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['fx-tooltip', 'fx-tooltip-position', 'fx-tooltip-html'],\n });\n }\n\n private processExistingElements(): void {\n document.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => this.processElement(el));\n }\n\n private processElement(element: HTMLElement): void {\n if (!element.hasAttribute('fx-tooltip')) {\n this.removeBehavior(element);\n return;\n }\n if (this.behaviors.has(element)) {\n this.behaviors.get(element)!.update();\n } else {\n this.behaviors.set(element, new TooltipBehavior(element));\n }\n }\n\n private removeBehavior(element: HTMLElement): void {\n const behavior = this.behaviors.get(element);\n if (behavior) {\n behavior.destroy();\n this.behaviors.delete(element);\n }\n }\n\n public destroy(): void {\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n this.behaviors.forEach((behavior) => behavior.destroy());\n this.behaviors.clear();\n if (this.injectedStyles) {\n this.injectedStyles.remove();\n this.injectedStyles = null;\n }\n FxTooltipManager.instance = null;\n }\n}\n\n/**\n * Retorna os estilos CSS para os tooltips.\n */\nfunction getTooltipStyles(): string {\n return /* css */ `\n [fx-tooltip] {\n position: relative;\n }\n\n .fx-tooltip-bubble {\n position: absolute;\n background: var(--fx-text-default, #1e293b);\n color: var(--fx-surface-background, #fff);\n font-family: var(--fx-font-family, system-ui, -apple-system, sans-serif);\n font-size: calc(var(--fx-font-size, 14px) - 2px);\n line-height: 1.45;\n padding: var(--fx-space-xs, 4px) var(--fx-space-md, 12px);\n border-radius: var(--fx-radius-sm, 4px);\n box-shadow: var(--fx-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));\n width: max-content;\n min-width: 60px;\n max-width: var(--fx-tooltip-max-width, 320px);\n white-space: normal;\n text-align: center;\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--fx-motion-duration-fast, 150ms) var(--fx-motion-easing, ease);\n z-index: var(--fx-z-tooltip, 1050);\n pointer-events: none;\n }\n\n .fx-tooltip-bubble.fx-tooltip-visible {\n opacity: 1;\n visibility: visible;\n }\n\n .fx-tooltip-top {\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n }\n .fx-tooltip-top::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-bottom {\n top: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n bottom: auto;\n }\n .fx-tooltip-bottom::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-bottom-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-left {\n right: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n left: auto;\n bottom: auto;\n }\n .fx-tooltip-left::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-left-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-right {\n left: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n right: auto;\n bottom: auto;\n }\n .fx-tooltip-right::after {\n content: '';\n position: absolute;\n top: 50%;\n right: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-right-color: var(--fx-text-default, #1e293b);\n }\n `;\n}\n\n/**\n * Inicializa a diretiva fx-tooltip.\n * Deve ser chamado uma vez na aplicação.\n *\n * @example\n * import { defineFxTooltipDirective } from '@wrrdev/fenix-ui/tooltip';\n * defineFxTooltipDirective();\n */\nexport function defineFxTooltipDirective(): void {\n if (typeof window === 'undefined') return;\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n FxTooltipManager.getInstance();\n });\n } else {\n FxTooltipManager.getInstance();\n }\n}\n\n/**\n * Destrói a diretiva fx-tooltip e limpa todos os recursos.\n */\nexport function destroyFxTooltipDirective(): void {\n FxTooltipManager.getInstance().destroy();\n}\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"directive.js","sources":["../../../src/components/tooltip/directive.ts"],"sourcesContent":["/**\n * Diretiva fx-tooltip — Aplica tooltip em qualquer elemento HTML.\n *\n * Uso:\n * <div fx-tooltip=\"Texto simples\">Div com tooltip</div>\n * <button fx-tooltip=\"Clique\" fx-tooltip-position=\"bottom\">Botão</button>\n * <span fx-tooltip=\"<strong>HTML</strong>\" fx-tooltip-html>Texto</span>\n *\n * Atributos:\n * fx-tooltip — Conteúdo do tooltip (texto ou HTML)\n * fx-tooltip-position — Posição: top | bottom | left | right (padrão: top)\n * fx-tooltip-html — Se presente, trata o conteúdo como HTML\n */\n\nexport interface FxTooltipConfig {\n position: 'top' | 'bottom' | 'left' | 'right';\n html: boolean;\n}\n\nconst DEFAULT_CONFIG: FxTooltipConfig = {\n position: 'top',\n html: false,\n};\n\n/**\n * Classe que gerencia o comportamento de tooltip em um elemento.\n */\nclass TooltipBehavior {\n private element: HTMLElement;\n private tooltipElement: HTMLSpanElement | null = null;\n private config: FxTooltipConfig;\n private showTimeout: number | null = null;\n private hideTimeout: number | null = null;\n\n constructor(element: HTMLElement) {\n this.element = element;\n this.config = this.parseConfig();\n this.init();\n }\n\n private parseConfig(): FxTooltipConfig {\n const position = this.element.getAttribute('fx-tooltip-position') as FxTooltipConfig['position'];\n return {\n position: position && ['top', 'bottom', 'left', 'right'].includes(position) ? position : DEFAULT_CONFIG.position,\n html: this.element.hasAttribute('fx-tooltip-html'),\n };\n }\n\n private init(): void {\n const computedStyle = window.getComputedStyle(this.element);\n if (computedStyle.position === 'static') {\n this.element.style.position = 'relative';\n }\n this.createTooltipElement();\n this.element.addEventListener('mouseenter', this.show);\n this.element.addEventListener('mouseleave', this.hide);\n this.element.addEventListener('focus', this.show);\n this.element.addEventListener('blur', this.hide);\n }\n\n private createTooltipElement(): void {\n const content = this.element.getAttribute('fx-tooltip');\n if (!content) return;\n this.tooltipElement = document.createElement('span');\n this.tooltipElement.setAttribute('role', 'tooltip');\n this.tooltipElement.setAttribute('data-fx-tooltip', '');\n this.tooltipElement.className = `fx-tooltip-bubble fx-tooltip-${this.config.position}`;\n if (this.config.html) {\n this.tooltipElement.innerHTML = content;\n } else {\n this.tooltipElement.textContent = content;\n }\n this.element.appendChild(this.tooltipElement);\n }\n\n private show = (): void => {\n if (this.hideTimeout) {\n clearTimeout(this.hideTimeout);\n this.hideTimeout = null;\n }\n this.showTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.add('fx-tooltip-visible');\n }\n }, 100);\n };\n\n private hide = (): void => {\n if (this.showTimeout) {\n clearTimeout(this.showTimeout);\n this.showTimeout = null;\n }\n this.hideTimeout = window.setTimeout(() => {\n if (this.tooltipElement) {\n this.tooltipElement.classList.remove('fx-tooltip-visible');\n }\n }, 50);\n };\n\n public update(): void {\n this.config = this.parseConfig();\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n this.createTooltipElement();\n }\n\n public destroy(): void {\n this.element.removeEventListener('mouseenter', this.show);\n this.element.removeEventListener('mouseleave', this.hide);\n this.element.removeEventListener('focus', this.show);\n this.element.removeEventListener('blur', this.hide);\n if (this.showTimeout) clearTimeout(this.showTimeout);\n if (this.hideTimeout) clearTimeout(this.hideTimeout);\n if (this.tooltipElement) {\n this.tooltipElement.remove();\n this.tooltipElement = null;\n }\n }\n}\n\n/**\n * Gerencia todas as instâncias de tooltip no documento.\n */\nclass FxTooltipManager {\n private static instance: FxTooltipManager | null = null;\n private observer: MutationObserver | null = null;\n private behaviors: Map<HTMLElement, TooltipBehavior> = new Map();\n private injectedStyles: HTMLStyleElement | null = null;\n\n private constructor() {\n this.injectStyles();\n this.observe();\n this.processExistingElements();\n }\n\n public static getInstance(): FxTooltipManager {\n if (!FxTooltipManager.instance) {\n FxTooltipManager.instance = new FxTooltipManager();\n }\n return FxTooltipManager.instance;\n }\n\n /** Quantos tooltips estão ativos agora (0 se a diretiva nunca foi iniciada). */\n public static peekActiveCount(): number {\n return FxTooltipManager.instance?.behaviors.size ?? 0;\n }\n\n /**\n * Quantos tooltips estão ativamente gerenciados.\n *\n * Deve acompanhar o número de elementos `[fx-tooltip]` no documento: um\n * valor crescente depois de trocar de tela significa comportamento retido\n * (vazamento). Usado nos testes de regressão e no diagnóstico em produção.\n */\n public get activeCount(): number {\n return this.behaviors.size;\n }\n\n private injectStyles(): void {\n if (document.getElementById('fx-tooltip-directive-styles')) return;\n this.injectedStyles = document.createElement('style');\n this.injectedStyles.id = 'fx-tooltip-directive-styles';\n this.injectedStyles.textContent = getTooltipStyles();\n document.head.appendChild(this.injectedStyles);\n }\n\n private observe(): void {\n this.observer = new MutationObserver((mutations) => {\n // NÃO processa aqui: enfileira e faz UM passe por frame.\n //\n // O observer escuta `childList + subtree` no <body> inteiro, então numa\n // tela grande (tabela, lista virtualizada) um único gesto do usuário pode\n // gerar dezenas de registros. Processar cada record na hora repete\n // `querySelectorAll` sobre a mesma subárvore várias vezes e trava o\n // main thread. O lote garante no máximo 1 varredura por frame.\n this._pending.added.push(...mutations);\n if (this._flushScheduled) return;\n this._flushScheduled = true;\n const run = () => {\n this._flushScheduled = false;\n const batch = this._pending;\n this._pending = { added: [] };\n this._flush(batch.added);\n };\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(run);\n else setTimeout(run, 0);\n });\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n attributes: true,\n // `attributeFilter` é o que mantém o custo baixo: o browser só notifica\n // mudanças nestes 3 atributos, e não em qualquer atributo da página.\n attributeFilter: ['fx-tooltip', 'fx-tooltip-position', 'fx-tooltip-html'],\n });\n }\n\n private _pending: { added: MutationRecord[] } = { added: [] };\n private _flushScheduled = false;\n\n /** Um passe de verificação sobre todos os registros acumulados. */\n private _flush(records: MutationRecord[]): void {\n // Sets evitam processar o mesmo elemento várias vezes quando ele aparece\n // em mais de um registro (comum com inserções aninhadas).\n const toProcess = new Set<HTMLElement>();\n const toRemove = new Set<HTMLElement>();\n\n for (const mutation of records) {\n if (mutation.type === 'childList') {\n mutation.addedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n toProcess.add(node);\n node.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => toProcess.add(el));\n }\n });\n // SEMPRE trate as remoções: sem isso, cada elemento que já teve\n // `fx-tooltip` fica preso no Map (elemento + listeners + bubble)\n // mesmo depois de desmontado. Em app grande/uso prolongado isso\n // cresce sem limite.\n mutation.removedNodes.forEach((node) => {\n if (node instanceof HTMLElement) {\n toRemove.add(node);\n node.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => toRemove.add(el));\n }\n });\n }\n if (mutation.type === 'attributes' && mutation.target instanceof HTMLElement) {\n const target = mutation.target;\n if (target.hasAttribute('fx-tooltip')) toProcess.add(target);\n else toRemove.add(target);\n }\n }\n\n // Remoções primeiro: um elemento que saiu e voltou no mesmo frame deve\n // acabar com o comportamento novo, não ser destruído por engano.\n toRemove.forEach((el) => this.removeBehavior(el));\n toProcess.forEach((el) => this.processElement(el));\n }\n\n private processExistingElements(): void {\n document.querySelectorAll<HTMLElement>('[fx-tooltip]').forEach((el) => this.processElement(el));\n }\n\n private processElement(element: HTMLElement): void {\n if (!element.hasAttribute('fx-tooltip')) {\n this.removeBehavior(element);\n return;\n }\n if (this.behaviors.has(element)) {\n this.behaviors.get(element)!.update();\n } else {\n this.behaviors.set(element, new TooltipBehavior(element));\n }\n }\n\n private removeBehavior(element: HTMLElement): void {\n const behavior = this.behaviors.get(element);\n if (behavior) {\n behavior.destroy();\n this.behaviors.delete(element);\n }\n }\n\n public destroy(): void {\n // Descarta o lote pendente: um flush depois do destroy recriaria\n // comportamentos em elementos já desmontados.\n this._pending.added = [];\n this._flushScheduled = false;\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n this.behaviors.forEach((behavior) => behavior.destroy());\n this.behaviors.clear();\n if (this.injectedStyles) {\n this.injectedStyles.remove();\n this.injectedStyles = null;\n }\n FxTooltipManager.instance = null;\n }\n}\n\n/**\n * Retorna os estilos CSS para os tooltips.\n */\nfunction getTooltipStyles(): string {\n return /* css */ `\n [fx-tooltip] {\n position: relative;\n }\n\n .fx-tooltip-bubble {\n position: absolute;\n background: var(--fx-text-default, #1e293b);\n color: var(--fx-surface-background, #fff);\n font-family: var(--fx-font-family, system-ui, -apple-system, sans-serif);\n font-size: calc(var(--fx-font-size, 14px) - 2px);\n line-height: 1.45;\n padding: var(--fx-space-xs, 4px) var(--fx-space-md, 12px);\n border-radius: var(--fx-radius-sm, 4px);\n box-shadow: var(--fx-shadow-md, 0 4px 6px -1px rgba(0, 0, 0, 0.1));\n width: max-content;\n min-width: 60px;\n max-width: var(--fx-tooltip-max-width, 320px);\n white-space: normal;\n text-align: center;\n opacity: 0;\n visibility: hidden;\n transition: opacity var(--fx-motion-duration-fast, 150ms) var(--fx-motion-easing, ease);\n z-index: var(--fx-z-tooltip, 1050);\n pointer-events: none;\n }\n\n .fx-tooltip-bubble.fx-tooltip-visible {\n opacity: 1;\n visibility: visible;\n }\n\n .fx-tooltip-top {\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n }\n .fx-tooltip-top::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-top-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-bottom {\n top: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n bottom: auto;\n }\n .fx-tooltip-bottom::after {\n content: '';\n position: absolute;\n bottom: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 5px solid transparent;\n border-bottom-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-left {\n right: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n left: auto;\n bottom: auto;\n }\n .fx-tooltip-left::after {\n content: '';\n position: absolute;\n top: 50%;\n left: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-left-color: var(--fx-text-default, #1e293b);\n }\n\n .fx-tooltip-right {\n left: calc(100% + 6px);\n top: 50%;\n transform: translateY(-50%);\n right: auto;\n bottom: auto;\n }\n .fx-tooltip-right::after {\n content: '';\n position: absolute;\n top: 50%;\n right: 100%;\n transform: translateY(-50%);\n border: 5px solid transparent;\n border-right-color: var(--fx-text-default, #1e293b);\n }\n `;\n}\n\n/**\n * Inicializa a diretiva fx-tooltip.\n * Deve ser chamado uma vez na aplicação.\n *\n * @example\n * import { defineFxTooltipDirective } from '@wrrdev/fenix-ui/tooltip';\n * defineFxTooltipDirective();\n */\nexport function defineFxTooltipDirective(): void {\n if (typeof window === 'undefined') return;\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', () => {\n FxTooltipManager.getInstance();\n });\n } else {\n FxTooltipManager.getInstance();\n }\n}\n\n/**\n * Quantos tooltips a diretiva mantém vivos agora.\n *\n * Deve igualar o nº de elementos `[fx-tooltip]` no documento. Se crescer após\n * trocar de tela, há comportamento retido (vazamento). Pensado para diagnóstico\n * em produção e para os testes de regressão.\n */\nexport function fxTooltipActiveCount(): number {\n return FxTooltipManager.peekActiveCount();\n}\n\n/**\n * Destrói a diretiva fx-tooltip e limpa todos os recursos.\n */\nexport function destroyFxTooltipDirective(): void {\n FxTooltipManager.getInstance().destroy();\n}\n\n"],"names":[],"mappings":"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"}
|
package/dist/docs/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/docs/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAiB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAY5B,OAAO,EAAa,aAAa,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EAAE,aAAa,EAAE,CAAC;AAkEzB,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmC9C;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/docs/app.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAiB,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAY5B,OAAO,EAAa,aAAa,EAAE,MAAM,WAAW,CAAC;AAGrD,OAAO,EAAE,aAAa,EAAE,CAAC;AAkEzB,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmC9C;AA6pED,eAAO,MAAM,iBAAiB,QAAO,OAAO,CAAC,IAAI,CAAgB,CAAC"}
|