@wrrdev/fenix-ui 1.1.11 → 1.1.13
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 +2 -2
- package/dist/components/card/card.d.ts +10 -0
- package/dist/components/card/card.d.ts.map +1 -1
- package/dist/components/card/card.js +18 -0
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/select/select.d.ts +29 -1
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +98 -10
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/table/table.d.ts +2 -1
- package/dist/components/table/table.d.ts.map +1 -1
- package/dist/components/table/table.js +92 -22
- package/dist/components/table/table.js.map +1 -1
- package/dist/core/jsx.d.ts +19 -0
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- package/dist/docs/componentes/card.doc.d.ts.map +1 -1
- package/dist/docs/componentes/select.doc.d.ts.map +1 -1
- package/dist/docs/componentes/table.doc.d.ts.map +1 -1
- package/dist/fenix-ui.umd.min.js +188 -105
- package/dist/fenix-ui4.esm.js +207 -31
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ import { FenixUI } from '@wrrdev/fenix-ui'; // API de tema/tokens (índice)
|
|
|
18
18
|
FenixUI.theme('dark');
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.
|
|
21
|
+
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.13**, acesso público.
|
|
22
22
|
> Subpaths disponíveis: `./theme`, `./tokens`, `./jsx`, `./vue`, `./react`, `./button`, `./badge`, `./spinner`, `./select`, `./input`, `./switch`, `./textarea`, `./checkbox`, `./radio`, `./calendar`, `./datepicker`, `./multiselect`, `./table`, `./floatlabel`, `./dialog`, `./toast`, `./tooltip`, `./tabs`, `./progress`, `./skeleton`, `./alert`, `./drawer`, `./dropdown`, `./icon`, `./icons`, `./pagination`, `./autocomplete`, `./knob`, `./orderlist`, `./picklist`, `./auto-import` e o curinga `./components/*`.
|
|
23
23
|
|
|
24
24
|
|
|
@@ -65,7 +65,7 @@ Carregue o bundle **UMD** único: ele registra todos os componentes e expõe os
|
|
|
65
65
|
<fx-toast></fx-toast>
|
|
66
66
|
|
|
67
67
|
<!--
|
|
68
|
-
🔗 Links diretos para CDN (versão 1.1.
|
|
68
|
+
🔗 Links diretos para CDN (versão 1.1.13):
|
|
69
69
|
• https://cdn.jsdelivr.net/npm/@wrrdev/fenix-ui@latest/dist/fenix-ui.umd.min.js
|
|
70
70
|
• https://unpkg.com/@wrrdev/fenix-ui@latest/dist/fenix-ui.umd.min.js
|
|
71
71
|
-->
|
|
@@ -6,11 +6,21 @@ import { FxElement } from '../../core/base';
|
|
|
6
6
|
* radius (sm|md|lg — arredondamento; `size` segue aceito como alias legado),
|
|
7
7
|
* padded (exibe padding interno), heading (rótulo opcional no cabeçalho).
|
|
8
8
|
* Slots: `header`, padrão (conteúdo), `footer`.
|
|
9
|
+
*
|
|
10
|
+
* `header` e `footer` só são renderizados visualmente quando têm conteúdo:
|
|
11
|
+
* sem `heading`/nó no slot header e sem nó no slot footer, as seções ficam
|
|
12
|
+
* `hidden` (sem padding e sem a linha divisória).
|
|
9
13
|
*/
|
|
10
14
|
export declare class FxCard extends FxElement {
|
|
11
15
|
static styles: string;
|
|
12
16
|
static get observedAttributes(): string[];
|
|
13
17
|
protected render(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Esconde `header`/`footer` quando o respectivo slot não recebeu nenhum nó.
|
|
20
|
+
* O `<slot>` fica no shadow e os nós no light DOM, então a checagem é feita
|
|
21
|
+
* via `assignedNodes()` — cobre conteúdo adicionado dinamicamente.
|
|
22
|
+
*/
|
|
23
|
+
private _syncSlots;
|
|
14
24
|
}
|
|
15
25
|
export declare function defineFxCard(): typeof FxCard;
|
|
16
26
|
//# sourceMappingURL=card.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/components/card/card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C
|
|
1
|
+
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/components/card/card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;GAWG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAsDpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;IAmBjC;;;;OAIG;IACH,OAAO,CAAC,UAAU;CAiBnB;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
|
|
@@ -15,6 +15,24 @@ const _FxCard = class _FxCard extends FxElement {
|
|
|
15
15
|
<footer part="footer"><slot name="footer"></slot></footer>
|
|
16
16
|
</div>
|
|
17
17
|
`);
|
|
18
|
+
this._syncSlots();
|
|
19
|
+
for (const name of ["header", "footer"]) {
|
|
20
|
+
this.root.querySelector(`slot[name="${name}"]`)?.addEventListener("slotchange", () => this._syncSlots());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Esconde `header`/`footer` quando o respectivo slot não recebeu nenhum nó.
|
|
25
|
+
* O `<slot>` fica no shadow e os nós no light DOM, então a checagem é feita
|
|
26
|
+
* via `assignedNodes()` — cobre conteúdo adicionado dinamicamente.
|
|
27
|
+
*/
|
|
28
|
+
_syncSlots() {
|
|
29
|
+
for (const name of ["header", "footer"]) {
|
|
30
|
+
const slot = this.root.querySelector(`slot[name="${name}"]`);
|
|
31
|
+
const section = this.root.querySelector(name);
|
|
32
|
+
if (!slot || !section) continue;
|
|
33
|
+
const temConteudo = slot.assignedNodes({ flatten: true }).some((n) => n.nodeType === 1 || (n.textContent ?? "").trim() !== "") || name === "header" && Boolean(this.getAttr("heading"));
|
|
34
|
+
section.toggleAttribute("hidden", !temConteudo);
|
|
35
|
+
}
|
|
18
36
|
}
|
|
19
37
|
};
|
|
20
38
|
_FxCard.styles = css`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.js","sources":["../../../src/components/card/card.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-card> — Container de conteúdo com cabeçalho, corpo e rodapé.\n *\n * Atributos: variant (elevated|flat|outline|ghost, padrão elevated),\n * radius (sm|md|lg — arredondamento; `size` segue aceito como alias legado),\n * padded (exibe padding interno), heading (rótulo opcional no cabeçalho).\n * Slots: `header`, padrão (conteúdo), `footer`.\n */\nexport class FxCard extends FxElement {\n static override styles = css`\n :host {\n display: block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n box-sizing: border-box;\n }\n .card {\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-lg);\n box-shadow: var(--fx-shadow-sm);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n /* Elevado: sombra principal; Flat: sem sombra mas cor plana; Outline: sem sombra e borda sólida */\n :host([variant='elevated']) .card { box-shadow: var(--fx-shadow-md); }\n :host([variant='elevated']) .card:hover { box-shadow: var(--fx-shadow-xl); }\n :host([variant='flat']) .card { box-shadow: none; border-color: transparent; }\n :host([variant='outline']) .card { box-shadow: none; border-style: solid; border-width: 1px; }\n :host([variant='ghost']) .card { box-shadow: none; border-color: transparent; background: transparent; }\n\n /* Raio do container: radius (nome atual) — size continua aceito\n como alias legado para não quebrar quem já usava o atributo. */\n :host([radius='sm']) .card,\n :host([size='sm']) .card { border-radius: var(--fx-radius-sm); }\n :host([radius='lg']) .card,\n :host([size='lg']) .card { border-radius: var(--fx-radius-xl); }\n\n header {\n padding: var(--fx-space-md) var(--fx-space-xl);\n border-bottom: 1px solid var(--fx-border-default);\n font-weight: 600;\n color: var(--fx-text-default);\n font-size: calc(var(--fx-font-size) + 2px);\n }\n header[hidden] { display: none; }\n\n .body {\n flex: 1 1 auto;\n }\n :host([padded]) .body { padding: var(--fx-space-lg) var(--fx-space-xl); }\n :host([padded]) .body:empty { padding: 0; }\n\n footer {\n padding: var(--fx-space-md) var(--fx-space-xl);\n border-top: 1px solid var(--fx-border-default);\n display: flex;\n justify-content: flex-end;\n gap: var(--fx-space-sm);\n }\n footer[hidden] { display: none; }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'radius', 'size', 'padded', 'heading'];\n }\n\n protected override render(): void {\n const heading = this.getAttr('heading');\n this.setTemplate(`\n <div class=\"card\" part=\"card\">\n <header part=\"header\" ${heading ? '' : 'hidden'}>${esc(heading)}<slot name=\"header\"></slot></header>\n <div class=\"body\" part=\"content\"><slot></slot></div>\n <footer part=\"footer\"><slot name=\"footer\"></slot></footer>\n </div>\n `);\n }\n}\n\nexport function defineFxCard(): typeof FxCard {\n return defineElement('fx-card', FxCard);\n}\n"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"card.js","sources":["../../../src/components/card/card.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-card> — Container de conteúdo com cabeçalho, corpo e rodapé.\n *\n * Atributos: variant (elevated|flat|outline|ghost, padrão elevated),\n * radius (sm|md|lg — arredondamento; `size` segue aceito como alias legado),\n * padded (exibe padding interno), heading (rótulo opcional no cabeçalho).\n * Slots: `header`, padrão (conteúdo), `footer`.\n *\n * `header` e `footer` só são renderizados visualmente quando têm conteúdo:\n * sem `heading`/nó no slot header e sem nó no slot footer, as seções ficam\n * `hidden` (sem padding e sem a linha divisória).\n */\nexport class FxCard extends FxElement {\n static override styles = css`\n :host {\n display: block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n box-sizing: border-box;\n }\n .card {\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-lg);\n box-shadow: var(--fx-shadow-sm);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n /* Elevado: sombra principal; Flat: sem sombra mas cor plana; Outline: sem sombra e borda sólida */\n :host([variant='elevated']) .card { box-shadow: var(--fx-shadow-md); }\n :host([variant='elevated']) .card:hover { box-shadow: var(--fx-shadow-xl); }\n :host([variant='flat']) .card { box-shadow: none; border-color: transparent; }\n :host([variant='outline']) .card { box-shadow: none; border-style: solid; border-width: 1px; }\n :host([variant='ghost']) .card { box-shadow: none; border-color: transparent; background: transparent; }\n\n /* Raio do container: radius (nome atual) — size continua aceito\n como alias legado para não quebrar quem já usava o atributo. */\n :host([radius='sm']) .card,\n :host([size='sm']) .card { border-radius: var(--fx-radius-sm); }\n :host([radius='lg']) .card,\n :host([size='lg']) .card { border-radius: var(--fx-radius-xl); }\n\n header {\n padding: var(--fx-space-md) var(--fx-space-xl);\n border-bottom: 1px solid var(--fx-border-default);\n font-weight: 600;\n color: var(--fx-text-default);\n font-size: calc(var(--fx-font-size) + 2px);\n }\n header[hidden] { display: none; }\n\n .body {\n flex: 1 1 auto;\n }\n :host([padded]) .body { padding: var(--fx-space-lg) var(--fx-space-xl); }\n :host([padded]) .body:empty { padding: 0; }\n\n footer {\n padding: var(--fx-space-md) var(--fx-space-xl);\n border-top: 1px solid var(--fx-border-default);\n display: flex;\n justify-content: flex-end;\n gap: var(--fx-space-sm);\n }\n footer[hidden] { display: none; }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'radius', 'size', 'padded', 'heading'];\n }\n\n protected override render(): void {\n const heading = this.getAttr('heading');\n this.setTemplate(`\n <div class=\"card\" part=\"card\">\n <header part=\"header\" ${heading ? '' : 'hidden'}>${esc(heading)}<slot name=\"header\"></slot></header>\n <div class=\"body\" part=\"content\"><slot></slot></div>\n <footer part=\"footer\"><slot name=\"footer\"></slot></footer>\n </div>\n `);\n // Header e footer só existem visualmente se houver conteúdo: sem isso o\n // rodapé vazio ainda pintava padding + linha divisória no card.\n this._syncSlots();\n for (const name of ['header', 'footer']) {\n this.root\n .querySelector(`slot[name=\"${name}\"]`)\n ?.addEventListener('slotchange', () => this._syncSlots());\n }\n }\n\n /**\n * Esconde `header`/`footer` quando o respectivo slot não recebeu nenhum nó.\n * O `<slot>` fica no shadow e os nós no light DOM, então a checagem é feita\n * via `assignedNodes()` — cobre conteúdo adicionado dinamicamente.\n */\n private _syncSlots(): void {\n for (const name of ['header', 'footer'] as const) {\n const slot = this.root.querySelector<HTMLSlotElement>(`slot[name=\"${name}\"]`);\n const section = this.root.querySelector<HTMLElement>(name);\n if (!slot || !section) continue;\n // `flatten: true` atravessa slots aninhados; nós em branco (whitespace)\n // não contam como conteúdo. O atributo `heading` alimenta SÓ o header —\n // contá-lo também no footer faria todo card com `heading` exibir a linha\n // divisória do rodapé mesmo sem nada no slot `footer`.\n const temConteudo =\n slot\n .assignedNodes({ flatten: true })\n .some((n) => n.nodeType === 1 || (n.textContent ?? '').trim() !== '') ||\n (name === 'header' && Boolean(this.getAttr('heading')));\n section.toggleAttribute('hidden', !temConteudo);\n }\n }\n}\n\nexport function defineFxCard(): typeof FxCard {\n return defineElement('fx-card', FxCard);\n}\n"],"names":[],"mappings":";;;;AAiBO,MAAM,UAAN,MAAM,gBAAe,UAAU;AAAA,EAyDpC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,UAAU,QAAQ,UAAU,SAAS;AAAA,EAC1D;AAAA,EAEmB,SAAe;AAChC,UAAM,UAAU,KAAK,QAAQ,SAAS;AACtC,SAAK,YAAY;AAAA;AAAA,gCAEW,UAAU,KAAK,QAAQ,IAAI,IAAI,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA,KAIlE;AAGD,SAAK,WAAA;AACL,eAAW,QAAQ,CAAC,UAAU,QAAQ,GAAG;AACvC,WAAK,KACF,cAAc,cAAc,IAAI,IAAI,GACnC,iBAAiB,cAAc,MAAM,KAAK,WAAA,CAAY;AAAA,IAC5D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,aAAmB;AACzB,eAAW,QAAQ,CAAC,UAAU,QAAQ,GAAY;AAChD,YAAM,OAAO,KAAK,KAAK,cAA+B,cAAc,IAAI,IAAI;AAC5E,YAAM,UAAU,KAAK,KAAK,cAA2B,IAAI;AACzD,UAAI,CAAC,QAAQ,CAAC,QAAS;AAKvB,YAAM,cACJ,KACG,cAAc,EAAE,SAAS,MAAM,EAC/B,KAAK,CAAC,MAAM,EAAE,aAAa,MAAM,EAAE,eAAe,IAAI,KAAA,MAAW,EAAE,KACrE,SAAS,YAAY,QAAQ,KAAK,QAAQ,SAAS,CAAC;AACvD,cAAQ,gBAAgB,UAAU,CAAC,WAAW;AAAA,IAChD;AAAA,EACF;AACF;AArGE,QAAgB,SAAS;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;AADpB,IAAM,SAAN;AAwGA,SAAS,eAA8B;AAC5C,SAAO,cAAc,WAAW,MAAM;AACxC;"}
|
|
@@ -11,14 +11,24 @@ import { FxElement } from '../../core/base';
|
|
|
11
11
|
* </fx-select>
|
|
12
12
|
*
|
|
13
13
|
* Atributos: value, size (sm|md|lg), disabled, placeholder,
|
|
14
|
-
* searchable, clearable, search-placeholder, no-results
|
|
14
|
+
* searchable, clearable, search-placeholder, no-results,
|
|
15
|
+
* placement (auto|top|bottom — direção do painel; `auto` inverte quando
|
|
16
|
+
* não cabe na viewport).
|
|
15
17
|
* Evento: `change` (composed, detail: { value }).
|
|
16
18
|
*/
|
|
17
19
|
export declare class FxSelect extends FxElement {
|
|
18
20
|
static styles: string;
|
|
19
21
|
static get observedAttributes(): string[];
|
|
22
|
+
/** Espaço (px) entre o campo e o painel — espelha `--fx-select-panel-offset`. */
|
|
23
|
+
private static readonly GAP;
|
|
24
|
+
/** Margem (px) mantida entre o painel e as bordas da viewport. */
|
|
25
|
+
private static readonly MARGEM;
|
|
26
|
+
/** Altura mínima do painel ao ser limitado pelo espaço disponível. */
|
|
27
|
+
private static readonly MIN_ALTURA;
|
|
20
28
|
private observer?;
|
|
21
29
|
private docListener?;
|
|
30
|
+
/** Reposiciona o painel enquanto ele estiver aberto (bound em `_abrir`). */
|
|
31
|
+
private onReflow?;
|
|
22
32
|
/** Tamanho do campo. Padrão: `'md'`. */
|
|
23
33
|
get size(): string;
|
|
24
34
|
set size(value: string);
|
|
@@ -29,6 +39,24 @@ export declare class FxSelect extends FxElement {
|
|
|
29
39
|
private get options();
|
|
30
40
|
protected connectedCallback(): void;
|
|
31
41
|
protected disconnectedCallback(): void;
|
|
42
|
+
/** Abre o dropdown, posiciona o painel e passa a reposicioná-lo no reflow. */
|
|
43
|
+
private _abrir;
|
|
44
|
+
/** Fecha o dropdown e libera os listeners de resize/scroll. */
|
|
45
|
+
private _fechar;
|
|
46
|
+
/** Liga o reposicionamento (resize + scroll interno, em capture). */
|
|
47
|
+
private _bindReflow;
|
|
48
|
+
private _unbindReflow;
|
|
49
|
+
/**
|
|
50
|
+
* Decide a posição vertical do painel medindo o espaço real da viewport.
|
|
51
|
+
*
|
|
52
|
+
* `placement="top"`/`"bottom"` são respeitados literalmente; o padrão
|
|
53
|
+
* (`auto`) só inverte quando o painel não cabe embaixo E couber melhor
|
|
54
|
+
* acima. Também limita a altura ao espaço disponível e evita overflow
|
|
55
|
+
* horizontal próximo à borda direita.
|
|
56
|
+
*/
|
|
57
|
+
protected ajustarPosicao(): void;
|
|
58
|
+
/** Altura máxima do painel a partir de `--fx-select-panel-max-height`. */
|
|
59
|
+
private static _maxAltura;
|
|
32
60
|
private select;
|
|
33
61
|
/** Navegação por teclado entre as opções do listbox (WCAG 2.1.1). */
|
|
34
62
|
private _navigateOptions;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/components/select/select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../../src/components/select/select.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,SA+KpB;IAIF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAIjD;IAED,iFAAiF;IACjF,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAK;IAChC,kEAAkE;IAClE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAK;IACnC,sEAAsE;IACtE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAM;IAExC,OAAO,CAAC,QAAQ,CAAC,CAAmB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAqB;IACzC,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,CAAa;IAE9B,wCAAwC;IACxC,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAEtB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,OAAO,KAAK,OAAO,GAKlB;cAEkB,iBAAiB,IAAI,IAAI;cAezB,oBAAoB,IAAI,IAAI;IAO/C,8EAA8E;IAC9E,OAAO,CAAC,MAAM;IAKd,+DAA+D;IAC/D,OAAO,CAAC,OAAO;IAQf,qEAAqE;IACrE,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,aAAa;IAOrB;;;;;;;OAOG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAuChC,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,UAAU;IAMzB,OAAO,CAAC,MAAM;IASd,qEAAqE;IACrE,OAAO,CAAC,gBAAgB;cAiBL,MAAM,IAAI,IAAI;CAwGlC;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
|
|
@@ -6,7 +6,7 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
6
6
|
// `value` fica FORA da observação: refleti-lo no change não pode
|
|
7
7
|
// re-renderizar o template e fechar o dropdown.
|
|
8
8
|
static get observedAttributes() {
|
|
9
|
-
return ["size", "disabled", "placeholder", "searchable", "clearable", "error", "invalid", "success", "valid"];
|
|
9
|
+
return ["size", "disabled", "placeholder", "searchable", "clearable", "error", "invalid", "success", "valid", "placement"];
|
|
10
10
|
}
|
|
11
11
|
/** Tamanho do campo. Padrão: `'md'`. */
|
|
12
12
|
get size() {
|
|
@@ -41,7 +41,7 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
41
41
|
this.docListener = (e) => {
|
|
42
42
|
if (!this.hasAttr("open")) return;
|
|
43
43
|
if (e.composedPath().includes(this)) return;
|
|
44
|
-
this.
|
|
44
|
+
this._fechar();
|
|
45
45
|
this.render();
|
|
46
46
|
};
|
|
47
47
|
document.addEventListener("click", this.docListener);
|
|
@@ -50,10 +50,77 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
50
50
|
super.disconnectedCallback();
|
|
51
51
|
this.observer?.disconnect();
|
|
52
52
|
if (this.docListener) document.removeEventListener("click", this.docListener);
|
|
53
|
+
this._unbindReflow();
|
|
54
|
+
}
|
|
55
|
+
/** Abre o dropdown, posiciona o painel e passa a reposicioná-lo no reflow. */
|
|
56
|
+
_abrir() {
|
|
57
|
+
this.setAttribute("open", "");
|
|
58
|
+
this._bindReflow();
|
|
59
|
+
}
|
|
60
|
+
/** Fecha o dropdown e libera os listeners de resize/scroll. */
|
|
61
|
+
_fechar() {
|
|
62
|
+
this.removeAttribute("open");
|
|
63
|
+
this.removeAttribute("data-placement");
|
|
64
|
+
this._unbindReflow();
|
|
65
|
+
}
|
|
66
|
+
/** Liga o reposicionamento (resize + scroll interno, em capture). */
|
|
67
|
+
_bindReflow() {
|
|
68
|
+
if (this.onReflow) return;
|
|
69
|
+
this.onReflow = () => {
|
|
70
|
+
if (this.hasAttr("open")) this.ajustarPosicao();
|
|
71
|
+
};
|
|
72
|
+
window.addEventListener("resize", this.onReflow);
|
|
73
|
+
window.addEventListener("scroll", this.onReflow, true);
|
|
74
|
+
}
|
|
75
|
+
_unbindReflow() {
|
|
76
|
+
if (!this.onReflow) return;
|
|
77
|
+
window.removeEventListener("resize", this.onReflow);
|
|
78
|
+
window.removeEventListener("scroll", this.onReflow, true);
|
|
79
|
+
this.onReflow = void 0;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Decide a posição vertical do painel medindo o espaço real da viewport.
|
|
83
|
+
*
|
|
84
|
+
* `placement="top"`/`"bottom"` são respeitados literalmente; o padrão
|
|
85
|
+
* (`auto`) só inverte quando o painel não cabe embaixo E couber melhor
|
|
86
|
+
* acima. Também limita a altura ao espaço disponível e evita overflow
|
|
87
|
+
* horizontal próximo à borda direita.
|
|
88
|
+
*/
|
|
89
|
+
ajustarPosicao() {
|
|
90
|
+
const painel = this.root.querySelector(".panel");
|
|
91
|
+
const trigger = this.root.querySelector(".trigger");
|
|
92
|
+
if (!painel || !trigger) return;
|
|
93
|
+
const r = trigger.getBoundingClientRect();
|
|
94
|
+
const abaixo = window.innerHeight - r.bottom;
|
|
95
|
+
const acima = r.top;
|
|
96
|
+
const desejado = painel.scrollHeight;
|
|
97
|
+
const maxAltura = _FxSelect._maxAltura(this);
|
|
98
|
+
const pedido = this.getAttr("placement", "auto");
|
|
99
|
+
const paraCima = pedido === "top" || pedido !== "bottom" && abaixo < Math.min(desejado, maxAltura) && acima > abaixo;
|
|
100
|
+
if (paraCima) this.setAttribute("data-placement", "top");
|
|
101
|
+
else this.removeAttribute("data-placement");
|
|
102
|
+
const disponivel = Math.max(
|
|
103
|
+
_FxSelect.MIN_ALTURA,
|
|
104
|
+
(paraCima ? acima : abaixo) - _FxSelect.GAP
|
|
105
|
+
);
|
|
106
|
+
painel.style.maxHeight = `${Math.min(desejado || disponivel, disponivel, maxAltura)}px`;
|
|
107
|
+
painel.style.left = "";
|
|
108
|
+
const largura = painel.offsetWidth;
|
|
109
|
+
const excesso = r.left + largura - (window.innerWidth - _FxSelect.MARGEM);
|
|
110
|
+
if (excesso > 0) {
|
|
111
|
+
painel.style.left = `${-excesso}px`;
|
|
112
|
+
painel.style.right = "auto";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Altura máxima do painel a partir de `--fx-select-panel-max-height`. */
|
|
116
|
+
static _maxAltura(host) {
|
|
117
|
+
const raw = getComputedStyle(host).getPropertyValue("--fx-select-panel-max-height");
|
|
118
|
+
const n = parseInt(raw, 10);
|
|
119
|
+
return Number.isFinite(n) && n > 0 ? n : 260;
|
|
53
120
|
}
|
|
54
121
|
select(value) {
|
|
55
122
|
this.value = value;
|
|
56
|
-
this.
|
|
123
|
+
this._fechar();
|
|
57
124
|
this.render();
|
|
58
125
|
this.dispatchEvent(
|
|
59
126
|
new CustomEvent("change", { bubbles: true, composed: true, detail: { value } })
|
|
@@ -63,7 +130,7 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
63
130
|
_navigateOptions(e) {
|
|
64
131
|
e.preventDefault();
|
|
65
132
|
if (!this.hasAttr("open")) {
|
|
66
|
-
this.
|
|
133
|
+
this._abrir();
|
|
67
134
|
this.render();
|
|
68
135
|
}
|
|
69
136
|
const opts = Array.from(this.root.querySelectorAll(".opt"));
|
|
@@ -108,6 +175,7 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
108
175
|
</div>
|
|
109
176
|
`);
|
|
110
177
|
if (prevOpen) this.setAttribute("open", "");
|
|
178
|
+
if (this.hasAttr("open")) this.ajustarPosicao();
|
|
111
179
|
const searchInput = this.root.querySelector(".search");
|
|
112
180
|
if (searchInput) {
|
|
113
181
|
searchInput.value = search;
|
|
@@ -127,7 +195,8 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
127
195
|
trigger.addEventListener("click", (e) => {
|
|
128
196
|
if (this.disabled) return;
|
|
129
197
|
if (e.target.closest(".clear")) return;
|
|
130
|
-
this.
|
|
198
|
+
if (this.hasAttr("open")) this._fechar();
|
|
199
|
+
else this._abrir();
|
|
131
200
|
this.render();
|
|
132
201
|
this.root.querySelector(".search")?.focus();
|
|
133
202
|
});
|
|
@@ -140,7 +209,7 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
140
209
|
}
|
|
141
210
|
if (e.key === "Escape") {
|
|
142
211
|
if (this.hasAttr("open")) {
|
|
143
|
-
this.
|
|
212
|
+
this._fechar();
|
|
144
213
|
this.render();
|
|
145
214
|
this.root.querySelector(".trigger")?.focus();
|
|
146
215
|
}
|
|
@@ -153,7 +222,7 @@ const _FxSelect = class _FxSelect extends FxElement {
|
|
|
153
222
|
this.root.querySelector(".clear")?.addEventListener("click", (e) => {
|
|
154
223
|
e.stopPropagation();
|
|
155
224
|
this.value = "";
|
|
156
|
-
this.
|
|
225
|
+
this._fechar();
|
|
157
226
|
this.render();
|
|
158
227
|
this.dispatchEvent(
|
|
159
228
|
new CustomEvent("change", { bubbles: true, composed: true, detail: { value: "" } })
|
|
@@ -175,6 +244,11 @@ _FxSelect.styles = css`
|
|
|
175
244
|
e style inline no elemento definem a largura sem precisar do full. */
|
|
176
245
|
width: var(--fx-select-width, max-content);
|
|
177
246
|
min-width: var(--fx-select-min-width, 200px);
|
|
247
|
+
/* Espaço entre o campo e o painel + altura máxima do painel. Expostos
|
|
248
|
+
como custom property porque ::part() do lado do consumidor tem
|
|
249
|
+
prioridade MENOR que a folha do shadow root e não vence o top. */
|
|
250
|
+
--fx-select-panel-offset: 4px;
|
|
251
|
+
--fx-select-panel-max-height: 260px;
|
|
178
252
|
}
|
|
179
253
|
.trigger {
|
|
180
254
|
display: inline-flex;
|
|
@@ -262,14 +336,15 @@ _FxSelect.styles = css`
|
|
|
262
336
|
}
|
|
263
337
|
.caret { font-size: calc(var(--fx-font-size) - 3px); color: var(--fx-text-muted); pointer-events: none; }
|
|
264
338
|
|
|
265
|
-
/* Painel do dropdown
|
|
339
|
+
/* Painel do dropdown — a POSIÇÃO vertical é definida pelas regras
|
|
340
|
+
[data-placement] abaixo (o JS de ajustarPosicao decide), por isso
|
|
341
|
+
top/bottom não ficam aqui. */
|
|
266
342
|
.panel {
|
|
267
343
|
position: absolute;
|
|
268
|
-
top: calc(100% + 4px);
|
|
269
344
|
left: 0;
|
|
270
345
|
z-index: var(--fx-z-dropdown, 1000);
|
|
271
346
|
width: max(100%, 220px);
|
|
272
|
-
max-height: 260px;
|
|
347
|
+
max-height: var(--fx-select-panel-max-height, 260px);
|
|
273
348
|
overflow-y: auto;
|
|
274
349
|
display: none;
|
|
275
350
|
background: var(--fx-surface-background);
|
|
@@ -277,6 +352,16 @@ _FxSelect.styles = css`
|
|
|
277
352
|
border-radius: var(--fx-radius-md);
|
|
278
353
|
box-shadow: var(--fx-shadow-lg);
|
|
279
354
|
}
|
|
355
|
+
/* Padrão: abre para baixo. */
|
|
356
|
+
:host([open]:not([data-placement='top'])) .panel {
|
|
357
|
+
top: calc(100% + var(--fx-select-panel-offset, 4px));
|
|
358
|
+
}
|
|
359
|
+
/* placement="top" explícito ou auto-flip: abre para cima. */
|
|
360
|
+
:host([open][placement='top']) .panel,
|
|
361
|
+
:host([open][data-placement='top']) .panel {
|
|
362
|
+
top: auto;
|
|
363
|
+
bottom: calc(100% + var(--fx-select-panel-offset, 4px));
|
|
364
|
+
}
|
|
280
365
|
:host([open]) .panel { display: block; }
|
|
281
366
|
.search {
|
|
282
367
|
position: sticky;
|
|
@@ -324,6 +409,9 @@ _FxSelect.styles = css`
|
|
|
324
409
|
.clear:hover { color: var(--fx-color-danger); }
|
|
325
410
|
.clear[hidden] { display: none; }
|
|
326
411
|
`;
|
|
412
|
+
_FxSelect.GAP = 4;
|
|
413
|
+
_FxSelect.MARGEM = 8;
|
|
414
|
+
_FxSelect.MIN_ALTURA = 80;
|
|
327
415
|
let FxSelect = _FxSelect;
|
|
328
416
|
function defineFxSelect() {
|
|
329
417
|
return defineElement("fx-select", FxSelect);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","sources":["../../../src/components/select/select.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-select> — Campo de seleção com dropdown customizado.\n *\n * Escreva os filhos como `<option>` nativos no light DOM; eles são\n * espelhados automaticamente (MutationObserver):\n *\n * <fx-select value=\"b\" searchable clearable>\n * <option value=\"a\">Opção A</option>\n * <option value=\"b\">Opção B</option>\n * </fx-select>\n *\n * Atributos: value, size (sm|md|lg), disabled, placeholder,\n * searchable, clearable, search-placeholder, no-results.\n * Evento: `change` (composed, detail: { value }).\n */\nexport class FxSelect extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n /* Largura no HOST (padrão: conteúdo com mín. 200px). CSS externo, classes\n e style inline no elemento definem a largura sem precisar do full. */\n width: var(--fx-select-width, max-content);\n min-width: var(--fx-select-min-width, 200px);\n }\n .trigger {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-space-sm);\n box-sizing: border-box;\n min-height: var(--fx-size-md);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n font: inherit;\n /* Altura/linha determinísticas: a caixa fica no token --fx-size-*\n (o line-height fixo impede que a herança da página estoure o min-height). */\n line-height: var(--fx-font-line-height);\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\n text-align: left;\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n padding: var(--fx-space-xs) var(--fx-space-md);\n cursor: pointer;\n transition:\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .trigger:hover { border-color: var(--fx-border-hover); }\n .trigger:focus-visible,\n :host([open]) .trigger {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([size='sm']) { min-width: var(--fx-select-min-width-sm, 180px); }\n :host([size='sm']) .trigger { min-height: var(--fx-size-sm); }\n :host([size='lg']) .trigger { min-height: var(--fx-size-lg); font-size: calc(var(--fx-font-size) + 4px); }\n /* Full width: o host estica até o pai e o trigger acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) { min-width: 0; }\n :host([full]) .trigger { width: 100%; min-width: 0; }\n :host([full]) .panel { width: 100%; }\n /* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o\n brilho segue --fx-effect-error-ring, que acompanha effect.focus-ring. */\n :host([error]) .trigger,\n :host([invalid]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([error]) .trigger:focus-visible,\n :host([invalid]) .trigger:focus-visible,\n :host([error][open]) .trigger,\n :host([invalid][open]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .trigger,\n :host([valid]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([success]) .trigger:focus-visible,\n :host([valid]) .trigger:focus-visible,\n :host([success][open]) .trigger,\n :host([valid][open]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([disabled]) .trigger,\n .trigger[aria-disabled='true'] { opacity: 0.55; cursor: not-allowed; background-color: var(--fx-surface-surface-hover); }\n /* O rótulo encolhe (ellipsis) e os ícones NUNCA saem do campo. */\n .label,\n .placeholder {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .placeholder { color: var(--fx-text-muted); }\n .actions {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs, 8px);\n flex-shrink: 0;\n }\n .caret { font-size: calc(var(--fx-font-size) - 3px); color: var(--fx-text-muted); pointer-events: none; }\n\n /* Painel do dropdown */\n .panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n width: max(100%, 220px);\n max-height: 260px;\n overflow-y: auto;\n display: none;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n box-shadow: var(--fx-shadow-lg);\n }\n :host([open]) .panel { display: block; }\n .search {\n position: sticky;\n top: 0;\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: none;\n border-bottom: 1px solid var(--fx-border-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n outline: none;\n }\n .opt {\n display: block;\n width: 100%;\n text-align: left;\n font: inherit;\n color: var(--fx-text-default);\n background: none;\n border: none;\n padding: var(--fx-space-sm) var(--fx-space-md);\n cursor: pointer;\n }\n /* Hover e selecionado com a cor primária do tema (igual ao multiselect). */\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, transparent); }\n .opt[aria-selected='true'] {\n background: color-mix(in srgb, var(--fx-color-primary) 18%, transparent);\n color: var(--fx-color-primary);\n font-weight: var(--fx-font-weight);\n }\n .empty { padding: var(--fx-space-sm) var(--fx-space-md); color: var(--fx-text-muted); }\n\n /* Clearable */\n .clear {\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n cursor: pointer;\n padding: 0;\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n `;\n\n // `value` fica FORA da observação: refleti-lo no change não pode\n // re-renderizar o template e fechar o dropdown.\n static override get observedAttributes(): string[] {\n // `invalid`/`valid` são aliases aceitos no CSS; sem observá-los o\n // setAttribute no submit não re-renderiza.\n return ['size', 'disabled', 'placeholder', 'searchable', 'clearable', 'error', 'invalid', 'success', 'valid'];\n }\n\n private observer?: MutationObserver;\n private docListener?: (e: Event) => void;\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string) {\n this.setAttribute('value', value);\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n private get options(): { value: string; label: string }[] {\n return [...this.querySelectorAll('option')].map((o) => ({\n value: o.getAttribute('value') ?? o.textContent?.trim() ?? '',\n label: o.textContent?.trim() ?? '',\n }));\n }\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n // Espelha mudanças nas <option> do light DOM.\n this.observer = new MutationObserver(() => this.render());\n this.observer.observe(this, { childList: true, subtree: true, characterData: true });\n // Fecha o dropdown ao clicar fora.\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this.removeAttribute('open');\n this.render();\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.observer?.disconnect();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n private select(value: string): void {\n this.value = value;\n this.removeAttribute('open');\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value } }),\n );\n }\n\n /** Navegação por teclado entre as opções do listbox (WCAG 2.1.1). */\n private _navigateOptions(e: KeyboardEvent): void {\n e.preventDefault();\n if (!this.hasAttr('open')) {\n this.toggleAttribute('open');\n this.render();\n }\n const opts = Array.from(this.root.querySelectorAll<HTMLButtonElement>('.opt'));\n if (!opts.length) return;\n const current = opts.indexOf(this.root.activeElement as HTMLButtonElement);\n let next = 0;\n if (e.key === 'ArrowDown') next = current === -1 ? 0 : Math.min(current + 1, opts.length - 1);\n else if (e.key === 'ArrowUp') next = current === -1 ? opts.length - 1 : Math.max(current - 1, 0);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = opts.length - 1;\n opts[next]?.focus();\n }\n\n protected override render(): void {\n const prevOpen = this.hasAttr('open');\n const prevSearch = this.root.querySelector<HTMLInputElement>('.search');\n // Estado do campo de busca antes de re-renderizar (valor, foco e cursor).\n const search = prevSearch?.value ?? '';\n const searchFocused =\n prevSearch != null && this.root.activeElement === prevSearch;\n const caretPos = searchFocused ? prevSearch!.selectionStart : null;\n\n const opts = this.options;\n let current = this.getAttr('value');\n if (!current) {\n const explicit = this.querySelector('option[selected]');\n current = explicit?.getAttribute('value') ?? explicit?.textContent?.trim() ?? '';\n }\n const selectedLabel = opts.find((o) => o.value === current)?.label ?? '';\n const placeholder = this.getAttr('placeholder', 'Selecione…');\n\n const filtered = search\n ? opts.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))\n : opts;\n\n this.setTemplate(`\n <span class=\"trigger\" part=\"trigger\" role=\"button\" tabindex=\"${this.disabled ? -1 : 0}\" aria-haspopup=\"listbox\" aria-expanded=\"${prevOpen}\">\n <span class=\"${selectedLabel ? 'label' : 'placeholder'}\">${esc(selectedLabel) || esc(placeholder)}</span>\n <span class=\"actions\">\n ${this.hasAttr('clearable') && current ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\">×</button>' : ''}\n <span class=\"caret\">▼</span>\n </span>\n </span>\n <div class=\"panel\" part=\"panel\" role=\"listbox\">\n ${this.hasAttr('searchable') ? `<input class=\"search\" part=\"search\" type=\"text\" placeholder=\"${esc(this.getAttr('search-placeholder', 'Pesquisar…'))}\">` : ''}\n ${filtered.map((o) => `\n <button type=\"button\" class=\"opt\" role=\"option\" data-value=\"${esc(o.value)}\"\n aria-selected=\"${o.value === current}\">${esc(o.label)}</button>`).join('')}\n ${filtered.length === 0 ? `<div class=\"empty\">${esc(this.getAttr('no-results', 'Nenhum resultado'))}</div>` : ''}\n </div>\n `);\n\n if (prevOpen) this.setAttribute('open', '');\n const searchInput = this.root.querySelector<HTMLInputElement>('.search');\n if (searchInput) {\n searchInput.value = search;\n searchInput.addEventListener('input', () => this.render());\n searchInput.addEventListener('click', (e) => e.stopPropagation());\n // Re-render não pode roubar o foco nem perder a posição do cursor.\n if (searchFocused) {\n searchInput.focus();\n try { searchInput.setSelectionRange(caretPos!, caretPos!); } catch { /* type=text sempre suporta */ }\n }\n }\n\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\n if (!trigger) return;\n if (this.disabled) trigger.setAttribute('aria-disabled', 'true');\n\n trigger.addEventListener('click', (e) => {\n if (this.disabled) return;\n if ((e.target as HTMLElement).closest('.clear')) return;\n this.toggleAttribute('open');\n this.render();\n this.root.querySelector<HTMLInputElement>('.search')?.focus();\n });\n\n // Teclado: Enter/Espaço abrem o dropdown; setas/Home/End/Escape navegam.\n trigger.addEventListener('keydown', (e) => {\n if (this.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n trigger.click();\n return;\n }\n if (e.key === 'Escape') {\n if (this.hasAttr('open')) {\n this.removeAttribute('open');\n this.render();\n this.root.querySelector<HTMLElement>('.trigger')?.focus();\n }\n return;\n }\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n this._navigateOptions(e);\n }\n });\n\n // Limpar.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.value = '';\n this.removeAttribute('open');\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value: '' } }),\n );\n });\n\n // Escolher opção.\n this.root.querySelectorAll<HTMLElement>('.opt').forEach((opt) => {\n opt.addEventListener('click', () => this.select(opt.dataset.value!));\n });\n }\n}\n\nexport function defineFxSelect(): typeof FxSelect {\n return defineElement('fx-select', FxSelect);\n}\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"select.js","sources":["../../../src/components/select/select.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-select> — Campo de seleção com dropdown customizado.\n *\n * Escreva os filhos como `<option>` nativos no light DOM; eles são\n * espelhados automaticamente (MutationObserver):\n *\n * <fx-select value=\"b\" searchable clearable>\n * <option value=\"a\">Opção A</option>\n * <option value=\"b\">Opção B</option>\n * </fx-select>\n *\n * Atributos: value, size (sm|md|lg), disabled, placeholder,\n * searchable, clearable, search-placeholder, no-results,\n * placement (auto|top|bottom — direção do painel; `auto` inverte quando\n * não cabe na viewport).\n * Evento: `change` (composed, detail: { value }).\n */\nexport class FxSelect extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n /* Largura no HOST (padrão: conteúdo com mín. 200px). CSS externo, classes\n e style inline no elemento definem a largura sem precisar do full. */\n width: var(--fx-select-width, max-content);\n min-width: var(--fx-select-min-width, 200px);\n /* Espaço entre o campo e o painel + altura máxima do painel. Expostos\n como custom property porque ::part() do lado do consumidor tem\n prioridade MENOR que a folha do shadow root e não vence o top. */\n --fx-select-panel-offset: 4px;\n --fx-select-panel-max-height: 260px;\n }\n .trigger {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-space-sm);\n box-sizing: border-box;\n min-height: var(--fx-size-md);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n font: inherit;\n /* Altura/linha determinísticas: a caixa fica no token --fx-size-*\n (o line-height fixo impede que a herança da página estoure o min-height). */\n line-height: var(--fx-font-line-height);\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\n text-align: left;\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n padding: var(--fx-space-xs) var(--fx-space-md);\n cursor: pointer;\n transition:\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .trigger:hover { border-color: var(--fx-border-hover); }\n .trigger:focus-visible,\n :host([open]) .trigger {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([size='sm']) { min-width: var(--fx-select-min-width-sm, 180px); }\n :host([size='sm']) .trigger { min-height: var(--fx-size-sm); }\n :host([size='lg']) .trigger { min-height: var(--fx-size-lg); font-size: calc(var(--fx-font-size) + 4px); }\n /* Full width: o host estica até o pai e o trigger acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) { min-width: 0; }\n :host([full]) .trigger { width: 100%; min-width: 0; }\n :host([full]) .panel { width: 100%; }\n /* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o\n brilho segue --fx-effect-error-ring, que acompanha effect.focus-ring. */\n :host([error]) .trigger,\n :host([invalid]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([error]) .trigger:focus-visible,\n :host([invalid]) .trigger:focus-visible,\n :host([error][open]) .trigger,\n :host([invalid][open]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .trigger,\n :host([valid]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([success]) .trigger:focus-visible,\n :host([valid]) .trigger:focus-visible,\n :host([success][open]) .trigger,\n :host([valid][open]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([disabled]) .trigger,\n .trigger[aria-disabled='true'] { opacity: 0.55; cursor: not-allowed; background-color: var(--fx-surface-surface-hover); }\n /* O rótulo encolhe (ellipsis) e os ícones NUNCA saem do campo. */\n .label,\n .placeholder {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .placeholder { color: var(--fx-text-muted); }\n .actions {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs, 8px);\n flex-shrink: 0;\n }\n .caret { font-size: calc(var(--fx-font-size) - 3px); color: var(--fx-text-muted); pointer-events: none; }\n\n /* Painel do dropdown — a POSIÇÃO vertical é definida pelas regras\n [data-placement] abaixo (o JS de ajustarPosicao decide), por isso\n top/bottom não ficam aqui. */\n .panel {\n position: absolute;\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n width: max(100%, 220px);\n max-height: var(--fx-select-panel-max-height, 260px);\n overflow-y: auto;\n display: none;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n box-shadow: var(--fx-shadow-lg);\n }\n /* Padrão: abre para baixo. */\n :host([open]:not([data-placement='top'])) .panel {\n top: calc(100% + var(--fx-select-panel-offset, 4px));\n }\n /* placement=\"top\" explícito ou auto-flip: abre para cima. */\n :host([open][placement='top']) .panel,\n :host([open][data-placement='top']) .panel {\n top: auto;\n bottom: calc(100% + var(--fx-select-panel-offset, 4px));\n }\n :host([open]) .panel { display: block; }\n .search {\n position: sticky;\n top: 0;\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: none;\n border-bottom: 1px solid var(--fx-border-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n outline: none;\n }\n .opt {\n display: block;\n width: 100%;\n text-align: left;\n font: inherit;\n color: var(--fx-text-default);\n background: none;\n border: none;\n padding: var(--fx-space-sm) var(--fx-space-md);\n cursor: pointer;\n }\n /* Hover e selecionado com a cor primária do tema (igual ao multiselect). */\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, transparent); }\n .opt[aria-selected='true'] {\n background: color-mix(in srgb, var(--fx-color-primary) 18%, transparent);\n color: var(--fx-color-primary);\n font-weight: var(--fx-font-weight);\n }\n .empty { padding: var(--fx-space-sm) var(--fx-space-md); color: var(--fx-text-muted); }\n\n /* Clearable */\n .clear {\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n cursor: pointer;\n padding: 0;\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n `;\n\n // `value` fica FORA da observação: refleti-lo no change não pode\n // re-renderizar o template e fechar o dropdown.\n static override get observedAttributes(): string[] {\n // `invalid`/`valid` são aliases aceitos no CSS; sem observá-los o\n // setAttribute no submit não re-renderiza.\n return ['size', 'disabled', 'placeholder', 'searchable', 'clearable', 'error', 'invalid', 'success', 'valid', 'placement'];\n }\n\n /** Espaço (px) entre o campo e o painel — espelha `--fx-select-panel-offset`. */\n private static readonly GAP = 4;\n /** Margem (px) mantida entre o painel e as bordas da viewport. */\n private static readonly MARGEM = 8;\n /** Altura mínima do painel ao ser limitado pelo espaço disponível. */\n private static readonly MIN_ALTURA = 80;\n\n private observer?: MutationObserver;\n private docListener?: (e: Event) => void;\n /** Reposiciona o painel enquanto ele estiver aberto (bound em `_abrir`). */\n private onReflow?: () => void;\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string) {\n this.setAttribute('value', value);\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n private get options(): { value: string; label: string }[] {\n return [...this.querySelectorAll('option')].map((o) => ({\n value: o.getAttribute('value') ?? o.textContent?.trim() ?? '',\n label: o.textContent?.trim() ?? '',\n }));\n }\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n // Espelha mudanças nas <option> do light DOM.\n this.observer = new MutationObserver(() => this.render());\n this.observer.observe(this, { childList: true, subtree: true, characterData: true });\n // Fecha o dropdown ao clicar fora.\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this._fechar();\n this.render();\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.observer?.disconnect();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n this._unbindReflow();\n }\n\n /** Abre o dropdown, posiciona o painel e passa a reposicioná-lo no reflow. */\n private _abrir(): void {\n this.setAttribute('open', '');\n this._bindReflow();\n }\n\n /** Fecha o dropdown e libera os listeners de resize/scroll. */\n private _fechar(): void {\n this.removeAttribute('open');\n // Sem isto o `data-placement` fica preso no host e a próxima abertura\n // mostraria o painel na posição invertida antes do ajuste rodar.\n this.removeAttribute('data-placement');\n this._unbindReflow();\n }\n\n /** Liga o reposicionamento (resize + scroll interno, em capture). */\n private _bindReflow(): void {\n if (this.onReflow) return;\n this.onReflow = () => {\n if (this.hasAttr('open')) this.ajustarPosicao();\n };\n window.addEventListener('resize', this.onReflow);\n window.addEventListener('scroll', this.onReflow, true);\n }\n\n private _unbindReflow(): void {\n if (!this.onReflow) return;\n window.removeEventListener('resize', this.onReflow);\n window.removeEventListener('scroll', this.onReflow, true);\n this.onReflow = undefined;\n }\n\n /**\n * Decide a posição vertical do painel medindo o espaço real da viewport.\n *\n * `placement=\"top\"`/`\"bottom\"` são respeitados literalmente; o padrão\n * (`auto`) só inverte quando o painel não cabe embaixo E couber melhor\n * acima. Também limita a altura ao espaço disponível e evita overflow\n * horizontal próximo à borda direita.\n */\n protected ajustarPosicao(): void {\n const painel = this.root.querySelector<HTMLElement>('.panel');\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\n if (!painel || !trigger) return;\n\n const r = trigger.getBoundingClientRect();\n const abaixo = window.innerHeight - r.bottom; // espaço livre embaixo\n const acima = r.top; // espaço livre acima\n const desejado = painel.scrollHeight; // altura já com a busca\n const maxAltura = FxSelect._maxAltura(this);\n\n const pedido = this.getAttr('placement', 'auto');\n const paraCima =\n pedido === 'top' ||\n (pedido !== 'bottom' && abaixo < Math.min(desejado, maxAltura) && acima > abaixo);\n\n // O atributo placement do host tem precedência no CSS; data-placement\n // é o canal interno do auto-flip e usa a mesma regra de estilo.\n if (paraCima) this.setAttribute('data-placement', 'top');\n else this.removeAttribute('data-placement');\n\n // Limita a altura ao espaço real disponível (evita o painel vazar da tela).\n const disponivel = Math.max(\n FxSelect.MIN_ALTURA,\n (paraCima ? acima : abaixo) - FxSelect.GAP,\n );\n painel.style.maxHeight = `${Math.min(desejado || disponivel, disponivel, maxAltura)}px`;\n\n // Reset do deslocamento horizontal antes de remedir (o painel é recriado\n // a cada render, mas o ajuste pode rodar várias vezes com o mesmo nó).\n painel.style.left = '';\n const largura = painel.offsetWidth;\n const excesso = r.left + largura - (window.innerWidth - FxSelect.MARGEM);\n if (excesso > 0) {\n painel.style.left = `${-excesso}px`;\n painel.style.right = 'auto';\n }\n }\n\n /** Altura máxima do painel a partir de `--fx-select-panel-max-height`. */\n private static _maxAltura(host: HTMLElement): number {\n const raw = getComputedStyle(host).getPropertyValue('--fx-select-panel-max-height');\n const n = parseInt(raw, 10);\n return Number.isFinite(n) && n > 0 ? n : 260;\n }\n\n private select(value: string): void {\n this.value = value;\n this._fechar();\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value } }),\n );\n }\n\n /** Navegação por teclado entre as opções do listbox (WCAG 2.1.1). */\n private _navigateOptions(e: KeyboardEvent): void {\n e.preventDefault();\n if (!this.hasAttr('open')) {\n this._abrir();\n this.render();\n }\n const opts = Array.from(this.root.querySelectorAll<HTMLButtonElement>('.opt'));\n if (!opts.length) return;\n const current = opts.indexOf(this.root.activeElement as HTMLButtonElement);\n let next = 0;\n if (e.key === 'ArrowDown') next = current === -1 ? 0 : Math.min(current + 1, opts.length - 1);\n else if (e.key === 'ArrowUp') next = current === -1 ? opts.length - 1 : Math.max(current - 1, 0);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = opts.length - 1;\n opts[next]?.focus();\n }\n\n protected override render(): void {\n const prevOpen = this.hasAttr('open');\n const prevSearch = this.root.querySelector<HTMLInputElement>('.search');\n // Estado do campo de busca antes de re-renderizar (valor, foco e cursor).\n const search = prevSearch?.value ?? '';\n const searchFocused =\n prevSearch != null && this.root.activeElement === prevSearch;\n const caretPos = searchFocused ? prevSearch!.selectionStart : null;\n\n const opts = this.options;\n let current = this.getAttr('value');\n if (!current) {\n const explicit = this.querySelector('option[selected]');\n current = explicit?.getAttribute('value') ?? explicit?.textContent?.trim() ?? '';\n }\n const selectedLabel = opts.find((o) => o.value === current)?.label ?? '';\n const placeholder = this.getAttr('placeholder', 'Selecione…');\n\n const filtered = search\n ? opts.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))\n : opts;\n\n this.setTemplate(`\n <span class=\"trigger\" part=\"trigger\" role=\"button\" tabindex=\"${this.disabled ? -1 : 0}\" aria-haspopup=\"listbox\" aria-expanded=\"${prevOpen}\">\n <span class=\"${selectedLabel ? 'label' : 'placeholder'}\">${esc(selectedLabel) || esc(placeholder)}</span>\n <span class=\"actions\">\n ${this.hasAttr('clearable') && current ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\">×</button>' : ''}\n <span class=\"caret\">▼</span>\n </span>\n </span>\n <div class=\"panel\" part=\"panel\" role=\"listbox\">\n ${this.hasAttr('searchable') ? `<input class=\"search\" part=\"search\" type=\"text\" placeholder=\"${esc(this.getAttr('search-placeholder', 'Pesquisar…'))}\">` : ''}\n ${filtered.map((o) => `\n <button type=\"button\" class=\"opt\" role=\"option\" data-value=\"${esc(o.value)}\"\n aria-selected=\"${o.value === current}\">${esc(o.label)}</button>`).join('')}\n ${filtered.length === 0 ? `<div class=\"empty\">${esc(this.getAttr('no-results', 'Nenhum resultado'))}</div>` : ''}\n </div>\n `);\n\n if (prevOpen) this.setAttribute('open', '');\n // A medição só é válida com o painel já `display: block` (open aplicado).\n if (this.hasAttr('open')) this.ajustarPosicao();\n const searchInput = this.root.querySelector<HTMLInputElement>('.search');\n if (searchInput) {\n searchInput.value = search;\n searchInput.addEventListener('input', () => this.render());\n searchInput.addEventListener('click', (e) => e.stopPropagation());\n // Re-render não pode roubar o foco nem perder a posição do cursor.\n if (searchFocused) {\n searchInput.focus();\n try { searchInput.setSelectionRange(caretPos!, caretPos!); } catch { /* type=text sempre suporta */ }\n }\n }\n\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\n if (!trigger) return;\n if (this.disabled) trigger.setAttribute('aria-disabled', 'true');\n\n trigger.addEventListener('click', (e) => {\n if (this.disabled) return;\n if ((e.target as HTMLElement).closest('.clear')) return;\n if (this.hasAttr('open')) this._fechar();\n else this._abrir();\n this.render();\n this.root.querySelector<HTMLInputElement>('.search')?.focus();\n });\n\n // Teclado: Enter/Espaço abrem o dropdown; setas/Home/End/Escape navegam.\n trigger.addEventListener('keydown', (e) => {\n if (this.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n trigger.click();\n return;\n }\n if (e.key === 'Escape') {\n if (this.hasAttr('open')) {\n this._fechar();\n this.render();\n this.root.querySelector<HTMLElement>('.trigger')?.focus();\n }\n return;\n }\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n this._navigateOptions(e);\n }\n });\n\n // Limpar.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.value = '';\n this._fechar();\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value: '' } }),\n );\n });\n\n // Escolher opção.\n this.root.querySelectorAll<HTMLElement>('.opt').forEach((opt) => {\n opt.addEventListener('click', () => this.select(opt.dataset.value!));\n });\n }\n}\n\nexport function defineFxSelect(): typeof FxSelect {\n return defineElement('fx-select', FxSelect);\n}\n\n"],"names":[],"mappings":"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"}
|
|
@@ -45,7 +45,8 @@ import "../select";
|
|
|
45
45
|
*
|
|
46
46
|
* Atributos: pagination, rows, rows-options, pagination-position (left|center|right),
|
|
47
47
|
* striped, hover, empty-message, lazy, total, loading, loading-message,
|
|
48
|
-
* sort-field, sort-order
|
|
48
|
+
* sort-field, sort-order, rounded (none|sm|md|lg, padrão md; `radius` é alias),
|
|
49
|
+
* no-borders-x (remove apenas as bordas laterais, mantendo topo e base).
|
|
49
50
|
* Eventos: page-change, sort-change, row-click, filter-change (todos composed).
|
|
50
51
|
*/
|
|
51
52
|
export declare class FxTable extends FxElement {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C,OAAO,WAAW,CAAC;AAEnB
|
|
1
|
+
{"version":3,"file":"table.d.ts","sourceRoot":"","sources":["../../../src/components/table/table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C,OAAO,WAAW,CAAC;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAkRpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAoBjD;IAED,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,eAAe,CAAC,CAAmB;IAE3C,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAEpC;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAGxC;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;IAED,IAAI,WAAW,IAAI,MAAM,CAGxB;IACD,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAE5B;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IAED,OAAO,KAAK,OAAO,GAuBlB;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,CAAC,oBAAoB;IAW5B,OAAO,KAAK,eAAe,GAU1B;IAED;;;;OAIG;IACH,OAAO,CAAC,WAAW;cAeA,iBAAiB,IAAI,IAAI;cAWzB,oBAAoB,IAAI,IAAI;IAK/C,+EAA+E;IAC/E,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,WAAW;IAiDnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,QAAQ;IAahB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS,CAAM;cAEJ,MAAM,IAAI,IAAI;IAkOjC;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAmBtB,OAAO,CAAC,qBAAqB;CAM7B;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
|