@wrrdev/fenix-ui 1.1.12 → 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 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.12**, acesso público.
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.12):
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;;;;;;;GAOG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAsDpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;CAUlC;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
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":";;;;AAaO,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;AAAA,EACH;AACF;AAtEE,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;AAyEA,SAAS,eAA8B;AAC5C,SAAO,cAAc,WAAW,MAAM;AACxC;"}
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;;;;;;;;;;;;;;GAcG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,SA+JpB;IAIF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAIjD;IAED,OAAO,CAAC,QAAQ,CAAC,CAAmB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAqB;IAEzC,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;IAM/C,OAAO,CAAC,MAAM;IASd,qEAAqE;IACrE,OAAO,CAAC,gBAAgB;cAiBL,MAAM,IAAI,IAAI;CAqGlC;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
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.removeAttribute("open");
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.removeAttribute("open");
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.toggleAttribute("open");
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.toggleAttribute("open");
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.removeAttribute("open");
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.removeAttribute("open");
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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"}
@@ -1 +1 @@
1
- {"version":3,"file":"card.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/card.doc.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,OAAO,EAAE,YA4BrB,CAAC"}
1
+ {"version":3,"file":"card.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/card.doc.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C,eAAO,MAAM,OAAO,EAAE,YA8CrB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"select.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/select.doc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,eAAO,MAAM,SAAS,EAAE,YAqIvB,CAAC"}
1
+ {"version":3,"file":"select.doc.d.ts","sourceRoot":"","sources":["../../../src/docs/componentes/select.doc.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,eAAO,MAAM,SAAS,EAAE,YA8JvB,CAAC"}