@wrrdev/fenix-ui 1.1.16 → 1.2.0

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.
Files changed (111) hide show
  1. package/README.md +12 -3
  2. package/dist/components/autocomplete/autocomplete.d.ts +3 -1
  3. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  4. package/dist/components/autocomplete/autocomplete.js +7 -1
  5. package/dist/components/autocomplete/autocomplete.js.map +1 -1
  6. package/dist/components/avatar/avatar.d.ts +1 -0
  7. package/dist/components/avatar/avatar.d.ts.map +1 -1
  8. package/dist/components/avatar/avatar.js +3 -0
  9. package/dist/components/avatar/avatar.js.map +1 -1
  10. package/dist/components/badge/badge.d.ts +4 -0
  11. package/dist/components/badge/badge.d.ts.map +1 -1
  12. package/dist/components/badge/badge.js +12 -0
  13. package/dist/components/badge/badge.js.map +1 -1
  14. package/dist/components/button/button.d.ts +2 -0
  15. package/dist/components/button/button.d.ts.map +1 -1
  16. package/dist/components/button/button.js +6 -0
  17. package/dist/components/button/button.js.map +1 -1
  18. package/dist/components/card/card.d.ts +2 -0
  19. package/dist/components/card/card.d.ts.map +1 -1
  20. package/dist/components/card/card.js +6 -0
  21. package/dist/components/card/card.js.map +1 -1
  22. package/dist/components/checkbox/checkbox.d.ts +5 -1
  23. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  24. package/dist/components/checkbox/checkbox.js +7 -8
  25. package/dist/components/checkbox/checkbox.js.map +1 -1
  26. package/dist/components/datepicker/datepicker.d.ts +17 -2
  27. package/dist/components/datepicker/datepicker.d.ts.map +1 -1
  28. package/dist/components/datepicker/datepicker.js +113 -42
  29. package/dist/components/datepicker/datepicker.js.map +1 -1
  30. package/dist/components/dialog/dialog.d.ts +4 -1
  31. package/dist/components/dialog/dialog.d.ts.map +1 -1
  32. package/dist/components/dialog/dialog.js +41 -18
  33. package/dist/components/dialog/dialog.js.map +1 -1
  34. package/dist/components/drawer/drawer.d.ts +5 -1
  35. package/dist/components/drawer/drawer.d.ts.map +1 -1
  36. package/dist/components/drawer/drawer.js +39 -17
  37. package/dist/components/drawer/drawer.js.map +1 -1
  38. package/dist/components/fileupload/fileupload.d.ts +13 -1
  39. package/dist/components/fileupload/fileupload.d.ts.map +1 -1
  40. package/dist/components/fileupload/fileupload.js +29 -1
  41. package/dist/components/fileupload/fileupload.js.map +1 -1
  42. package/dist/components/floatlabel/floatlabel.d.ts +20 -0
  43. package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
  44. package/dist/components/floatlabel/floatlabel.js +99 -26
  45. package/dist/components/floatlabel/floatlabel.js.map +1 -1
  46. package/dist/components/icon/icon.d.ts +4 -0
  47. package/dist/components/icon/icon.d.ts.map +1 -1
  48. package/dist/components/icon/icon.js +12 -0
  49. package/dist/components/icon/icon.js.map +1 -1
  50. package/dist/components/input/input.d.ts +9 -2
  51. package/dist/components/input/input.d.ts.map +1 -1
  52. package/dist/components/input/input.js +56 -8
  53. package/dist/components/input/input.js.map +1 -1
  54. package/dist/components/multiselect/multiselect.d.ts +8 -1
  55. package/dist/components/multiselect/multiselect.d.ts.map +1 -1
  56. package/dist/components/multiselect/multiselect.js +61 -9
  57. package/dist/components/multiselect/multiselect.js.map +1 -1
  58. package/dist/components/orderlist/orderlist.d.ts +7 -0
  59. package/dist/components/orderlist/orderlist.d.ts.map +1 -1
  60. package/dist/components/orderlist/orderlist.js +34 -2
  61. package/dist/components/orderlist/orderlist.js.map +1 -1
  62. package/dist/components/radio/radio.d.ts +6 -0
  63. package/dist/components/radio/radio.d.ts.map +1 -1
  64. package/dist/components/radio/radio.js +12 -6
  65. package/dist/components/radio/radio.js.map +1 -1
  66. package/dist/components/select/select.d.ts +19 -1
  67. package/dist/components/select/select.d.ts.map +1 -1
  68. package/dist/components/select/select.js +116 -10
  69. package/dist/components/select/select.js.map +1 -1
  70. package/dist/components/switch/switch.d.ts +4 -1
  71. package/dist/components/switch/switch.d.ts.map +1 -1
  72. package/dist/components/switch/switch.js +2 -1
  73. package/dist/components/switch/switch.js.map +1 -1
  74. package/dist/components/table/table.d.ts +27 -0
  75. package/dist/components/table/table.d.ts.map +1 -1
  76. package/dist/components/table/table.js +119 -21
  77. package/dist/components/table/table.js.map +1 -1
  78. package/dist/components/tabs/tabs.d.ts +6 -0
  79. package/dist/components/tabs/tabs.d.ts.map +1 -1
  80. package/dist/components/tabs/tabs.js +34 -0
  81. package/dist/components/tabs/tabs.js.map +1 -1
  82. package/dist/components/textarea/textarea.d.ts +5 -1
  83. package/dist/components/textarea/textarea.d.ts.map +1 -1
  84. package/dist/components/textarea/textarea.js +25 -6
  85. package/dist/components/textarea/textarea.js.map +1 -1
  86. package/dist/core/jsx.d.ts +11 -5
  87. package/dist/core/jsx.d.ts.map +1 -1
  88. package/dist/core/jsx.js.map +1 -1
  89. package/dist/core/modal.d.ts +31 -0
  90. package/dist/core/modal.d.ts.map +1 -0
  91. package/dist/core/modal.js +61 -0
  92. package/dist/core/modal.js.map +1 -0
  93. package/dist/core/tokens.d.ts +2 -0
  94. package/dist/core/tokens.d.ts.map +1 -1
  95. package/dist/core/tokens.js +9 -0
  96. package/dist/core/tokens.js.map +1 -1
  97. package/dist/docs/app.d.ts.map +1 -1
  98. package/dist/docs/componentes/floatlabel.doc.d.ts.map +1 -1
  99. package/dist/docs/componentes/input.doc.d.ts.map +1 -1
  100. package/dist/docs/componentes/orderlist.doc.d.ts.map +1 -1
  101. package/dist/docs/componentes/table.doc.d.ts.map +1 -1
  102. package/dist/docs/shell.d.ts +10 -1
  103. package/dist/docs/shell.d.ts.map +1 -1
  104. package/dist/docs/theme-reference.d.ts +53 -0
  105. package/dist/docs/theme-reference.d.ts.map +1 -0
  106. package/dist/docs/types.d.ts +2 -0
  107. package/dist/docs/types.d.ts.map +1 -1
  108. package/dist/fenix-ui.umd.min.js +270 -233
  109. package/dist/fenix-ui4.esm.js +890 -176
  110. package/dist/fenix-ui4.esm.js.map +1 -1
  111. 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.16**, acesso público.
21
+ > 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.2.0**, acesso público.
22
22
  > Subpaths disponíveis: `./theme`, `./tokens`, `./jsx`, `./vue`, `./react`, `./button`, `./badge`, `./spinner`, `./select`, `./input`, `./switch`, `./textarea`, `./editor`, `./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.16):
68
+ 🔗 Links diretos para CDN (versão 1.2.0):
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
  -->
@@ -197,12 +197,21 @@ FenixUI.configure({
197
197
  // error-ring/success-ring acompanham o focus-ring automaticamente:
198
198
  // desligado, a validação error/success mostra só a borda (sem brilho).
199
199
  },
200
+ table: {
201
+ text: '#0f172a', // corpo do fx-table (padrão: acompanha text.default)
202
+ 'header-weight': '600', // cabeçalho mais leve que o bold do navegador
203
+ },
200
204
  },
201
205
  });
202
206
  ```
203
207
 
204
208
  Grupos de tokens disponíveis: `color`, `surface`, `text`, `border`, `font`, `space`,
205
- `radius`, `size`, `shadow`, `motion`, `effect` e `z` (veja `@wrrdev/fenix-ui/tokens`).
209
+ `radius`, `size`, `shadow`, `motion`, `effect`, `table` e `z` (veja `@wrrdev/fenix-ui/tokens`).
210
+
211
+ > 📖 Referência completa de cada token (valores claro/escuro, o que cada um controla e
212
+ > exemplo de uso): [**Tokens de tema**](https://wesley-reis.github.io/FenixUI/#/tokens).
213
+ > Ajustes finos por componente (`--fx-input-width`, `--fx-table-text-weight`, …):
214
+ > [**Variáveis de componente**](https://wesley-reis.github.io/FenixUI/#/component-vars).
206
215
 
207
216
  ### Presets prontos (temas padrão)
208
217
 
@@ -15,8 +15,10 @@ export declare class FxAutocomplete extends FxFormElement {
15
15
  static get observedAttributes(): string[];
16
16
  get size(): string;
17
17
  set size(v: string);
18
+ get disabled(): boolean;
19
+ set disabled(v: boolean);
18
20
  get value(): string;
19
- set value(v: string);
21
+ set value(v: string | null | undefined);
20
22
  get source(): string[];
21
23
  /** Setter necessário para o Vue (patchDOMProp seta `source` como
22
24
  * propriedade, pois o getter existe no prototype). */
@@ -1 +1 @@
1
- {"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/autocomplete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD;;;;;;;;;;GAUG;AACH,qBAAa,cAAe,SAAQ,aAAa;IAC/C,OAAgB,MAAM,SAqFpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAIjD;IAED,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAAmC;IACrD,IAAI,KAAK,IAAI,MAAM,CAAkC;IACrD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,EAMlB;IAED,IAAI,MAAM,IAAI,MAAM,EAAE,CAOrB;IACD;2DACuD;IACvD,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAElC;IAED,OAAO,CAAC,WAAW,CAAC,CAAqB;cAEtB,iBAAiB,IAAI,IAAI;cAQzB,oBAAoB,IAAI,IAAI;cAM5B,MAAM,IAAI,IAAI;IA0EjC,yEAAyE;cACtD,mBAAmB,IAAI,IAAI;CAK/C;AAED,wBAAgB,oBAAoB,IAAI,OAAO,cAAc,CAE5D"}
1
+ {"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/autocomplete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAKxD;;;;;;;;;;GAUG;AACH,qBAAa,cAAe,SAAQ,aAAa;IAC/C,OAAgB,MAAM,SAqFpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAIjD;IAED,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAAmC;IAErD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,EAEtB;IACD,IAAI,KAAK,IAAI,MAAM,CAAkC;IACrD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAMrC;IAED,IAAI,MAAM,IAAI,MAAM,EAAE,CAOrB;IACD;2DACuD;IACvD,IAAI,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAElC;IAED,OAAO,CAAC,WAAW,CAAC,CAAqB;cAEtB,iBAAiB,IAAI,IAAI;cAQzB,oBAAoB,IAAI,IAAI;cAM5B,MAAM,IAAI,IAAI;IA0EjC,yEAAyE;cACtD,mBAAmB,IAAI,IAAI;CAK/C;AAED,wBAAgB,oBAAoB,IAAI,OAAO,cAAc,CAE5D"}
@@ -13,11 +13,17 @@ const _FxAutocomplete = class _FxAutocomplete extends FxFormElement {
13
13
  set size(v) {
14
14
  this.setAttribute("size", v);
15
15
  }
16
+ get disabled() {
17
+ return this.hasAttr("disabled");
18
+ }
19
+ set disabled(v) {
20
+ this.toggleAttr("disabled", Boolean(v));
21
+ }
16
22
  get value() {
17
23
  return this.getAttr("value");
18
24
  }
19
25
  set value(v) {
20
- this.setAttribute("value", v);
26
+ this.setAttribute("value", v == null ? "" : String(v));
21
27
  this.syncInternalControl();
22
28
  this.updateFormValue();
23
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"autocomplete.js","sources":["../../../src/components/autocomplete/autocomplete.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-autocomplete> — Campo de texto com sugestões filtradas.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`) e é alvo de `<label for>`/`autofocus`.\n *\n * Atributos: value, name, size, placeholder, source (JSON array de strings),\n * disabled, min-chars (padrão 1), autofocus.\n * Eventos: `select`, `input` e `valueChange` (composed, detail: { value }) —\n * `input` aciona o v-model do Vue e `valueChange` o [(value)] do Angular.\n */\nexport class FxAutocomplete extends FxFormElement {\n static override styles = css`\n :host {\n position: relative;\n display: inline-block;\n /* Largura padrão no HOST: CSS externo, classes e style inline no elemento\n vencem este default naturalmente (regras do documento > :host). */\n width: var(--fx-autocomplete-width, 260px);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n .field {\n font-family: inherit;\n font-size: inherit;\n /* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\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-sm) var(--fx-space-lg);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n min-height: var(--fx-size-md);\n box-sizing: border-box;\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 .field::placeholder { color: var(--fx-text-muted); opacity: 1; }\n .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([size='sm']) { width: var(--fx-autocomplete-width-sm, 220px); }\n :host([size='sm']) .field { min-height: var(--fx-size-sm); padding: var(--fx-space-xs) var(--fx-space-md); }\n :host([size='lg']) { width: var(--fx-autocomplete-width-lg, 300px); }\n :host([size='lg']) .field { min-height: var(--fx-size-lg); padding: var(--fx-space-sm) var(--fx-space-lg); }\n /* Full width: o host estica até o pai e o campo interno acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { width: 100%; }\n /* Validação: borda sempre; brilho (anel) via token que acompanha\n effect.focus-ring — desligado ⇒ erro só com a borda vermelha. */\n :host([error]) .field,\n :host([invalid]) .field {\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]) .field,\n :host([valid]) .field {\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 .list {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\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 z-index: var(--fx-z-dropdown, 1000);\n max-height: 220px;\n overflow-y: auto;\n display: none;\n }\n :host([open]) .list { display: block; }\n .opt {\n display: block;\n width: 100%;\n text-align: left;\n border: none;\n background: transparent;\n font-family: inherit;\n font-size: inherit;\n color: var(--fx-text-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n cursor: pointer;\n transition: background var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, var(--fx-surface-background)); color: var(--fx-color-primary); }\n .empty { color: var(--fx-text-muted); font-size: calc(var(--fx-font-size) - 2px); padding: var(--fx-space-sm) var(--fx-space-md); }\n `;\n\n static override get observedAttributes(): string[] {\n // `error`/`success` (+ aliases `invalid`/`valid`) PRECISAM ser observados:\n // é assim que o toggle no playground / setAttribute no submit re-renderiza.\n return ['size', 'placeholder', 'source', 'disabled', 'min-chars', 'error', 'invalid', 'success', 'valid'];\n }\n\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(v: string) { this.setAttribute('size', v); }\n get value(): string { return this.getAttr('value'); }\n set value(v: string) {\n this.setAttribute('value', v);\n // `value` não é observado (não pode re-renderizar por tecla): sincroniza\n // o campo interno direto, sem perder foco/cursor, e publica no form.\n this.syncInternalControl();\n this.updateFormValue();\n }\n\n get source(): string[] {\n try {\n const raw = JSON.parse(this.getAttr('source', '[]'));\n return Array.isArray(raw) ? raw.map(String) : [];\n } catch {\n return [];\n }\n }\n /** Setter necessário para o Vue (patchDOMProp seta `source` como\n * propriedade, pois o getter existe no prototype). */\n set source(value: string | string[]) {\n this.setAttribute('source', typeof value === 'string' ? value : JSON.stringify(value));\n }\n\n private docListener?: (e: Event) => void;\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n this.docListener = (e: Event) => {\n if (!this.contains(e.target as Node)) this.toggleAttr('open', false);\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n this.docListener = undefined;\n }\n\n protected override render(): void {\n const placeholder = this.getAttr('placeholder');\n\n this.setTemplate(`\n <input class=\"field\" part=\"input\" type=\"text\"\n ${placeholder ? `placeholder=\"${esc(placeholder)}\"` : ''} autocomplete=\"off\"/>\n <div class=\"list\" part=\"list\" role=\"listbox\"></div>\n `);\n\n const field = this.root.querySelector<HTMLInputElement>('.field');\n if (!field) return;\n field.value = this.value;\n if (this.hasAttr('disabled')) field.setAttribute('disabled', '');\n\n const list = this.root.querySelector<HTMLElement>('.list');\n const minChars = Number(this.getAttr('min-chars', '1')) || 1;\n\n const openList = (): void => {\n if (!list) return;\n const q = field.value.toLowerCase();\n const matches = this.source.filter((s) => s.toLowerCase().includes(q)).slice(0, 8);\n list.innerHTML = matches.length\n ? matches.map((m) => `<button type=\"button\" class=\"opt\" role=\"option\" data-v=\"${esc(m)}\">${esc(m)}</button>`).join('')\n : '<div class=\"empty\">Nenhum resultado</div>';\n list.querySelectorAll<HTMLButtonElement>('.opt').forEach((btn) => {\n btn.addEventListener('click', () => {\n field.value = btn.dataset.v ?? '';\n this.value = field.value;\n this.toggleAttr('open', false);\n this.emitValueEvents();\n this.dispatchEvent(new CustomEvent('select', {\n bubbles: true, composed: true, detail: { value: this.value },\n }));\n });\n });\n this.toggleAttr('open', true);\n };\n\n // Reabrir com foco sem re-render (evita perder digitação)\n field.addEventListener('input', () => {\n this.value = field.value;\n this.emitValueEvents();\n openList();\n });\n field.addEventListener('focus', () => { if (field.value.length >= minChars) openList(); });\n // Navegação por teclado: setas movem o foco entre as sugestões; ESC fecha.\n // Enter com a lista FECHADA submete o form (o input interno não se associa\n // ao <form> do light DOM — submissão implícita acionada manualmente).\n field.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n this.toggleAttr('open', false);\n return;\n }\n if (e.key === 'Enter' && !e.isComposing && !this.hasAttr('open') && this.form) {\n e.preventDefault();\n this.form.requestSubmit();\n return;\n }\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return;\n e.preventDefault();\n if (!this.hasAttr('open')) openList();\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 const next = e.key === 'ArrowDown'\n ? (current === -1 ? 0 : Math.min(current + 1, opts.length - 1))\n : (current === -1 ? opts.length - 1 : Math.max(current - 1, 0));\n opts[next]?.focus();\n });\n\n // Publica name/value no form (ElementInternals) a cada render.\n this.updateFormValue();\n }\n\n /** Empurra o atributo `value` para o campo interno sem re-renderizar. */\n protected override syncInternalControl(): void {\n const field = this.root.querySelector<HTMLInputElement>('.field');\n const value = this.getAttr('value');\n if (field && field.value !== value) field.value = value;\n }\n}\n\nexport function defineFxAutocomplete(): typeof FxAutocomplete {\n return defineElement('fx-autocomplete', FxAutocomplete);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"autocomplete.js","sources":["../../../src/components/autocomplete/autocomplete.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-autocomplete> — Campo de texto com sugestões filtradas.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`) e é alvo de `<label for>`/`autofocus`.\n *\n * Atributos: value, name, size, placeholder, source (JSON array de strings),\n * disabled, min-chars (padrão 1), autofocus.\n * Eventos: `select`, `input` e `valueChange` (composed, detail: { value }) —\n * `input` aciona o v-model do Vue e `valueChange` o [(value)] do Angular.\n */\nexport class FxAutocomplete extends FxFormElement {\n static override styles = css`\n :host {\n position: relative;\n display: inline-block;\n /* Largura padrão no HOST: CSS externo, classes e style inline no elemento\n vencem este default naturalmente (regras do documento > :host). */\n width: var(--fx-autocomplete-width, 260px);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n .field {\n font-family: inherit;\n font-size: inherit;\n /* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\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-sm) var(--fx-space-lg);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n min-height: var(--fx-size-md);\n box-sizing: border-box;\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 .field::placeholder { color: var(--fx-text-muted); opacity: 1; }\n .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([size='sm']) { width: var(--fx-autocomplete-width-sm, 220px); }\n :host([size='sm']) .field { min-height: var(--fx-size-sm); padding: var(--fx-space-xs) var(--fx-space-md); }\n :host([size='lg']) { width: var(--fx-autocomplete-width-lg, 300px); }\n :host([size='lg']) .field { min-height: var(--fx-size-lg); padding: var(--fx-space-sm) var(--fx-space-lg); }\n /* Full width: o host estica até o pai e o campo interno acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { width: 100%; }\n /* Validação: borda sempre; brilho (anel) via token que acompanha\n effect.focus-ring — desligado ⇒ erro só com a borda vermelha. */\n :host([error]) .field,\n :host([invalid]) .field {\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]) .field,\n :host([valid]) .field {\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 .list {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n right: 0;\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 z-index: var(--fx-z-dropdown, 1000);\n max-height: 220px;\n overflow-y: auto;\n display: none;\n }\n :host([open]) .list { display: block; }\n .opt {\n display: block;\n width: 100%;\n text-align: left;\n border: none;\n background: transparent;\n font-family: inherit;\n font-size: inherit;\n color: var(--fx-text-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n cursor: pointer;\n transition: background var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, var(--fx-surface-background)); color: var(--fx-color-primary); }\n .empty { color: var(--fx-text-muted); font-size: calc(var(--fx-font-size) - 2px); padding: var(--fx-space-sm) var(--fx-space-md); }\n `;\n\n static override get observedAttributes(): string[] {\n // `error`/`success` (+ aliases `invalid`/`valid`) PRECISAM ser observados:\n // é assim que o toggle no playground / setAttribute no submit re-renderiza.\n return ['size', 'placeholder', 'source', 'disabled', 'min-chars', 'error', 'invalid', 'success', 'valid'];\n }\n\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(v: string) { this.setAttribute('size', v); }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(v: boolean) {\n this.toggleAttr('disabled', Boolean(v));\n }\n get value(): string { return this.getAttr('value'); }\n set value(v: string | null | undefined) {\n this.setAttribute('value', v == null ? '' : String(v));\n // `value` não é observado (não pode re-renderizar por tecla): sincroniza\n // o campo interno direto, sem perder foco/cursor, e publica no form.\n this.syncInternalControl();\n this.updateFormValue();\n }\n\n get source(): string[] {\n try {\n const raw = JSON.parse(this.getAttr('source', '[]'));\n return Array.isArray(raw) ? raw.map(String) : [];\n } catch {\n return [];\n }\n }\n /** Setter necessário para o Vue (patchDOMProp seta `source` como\n * propriedade, pois o getter existe no prototype). */\n set source(value: string | string[]) {\n this.setAttribute('source', typeof value === 'string' ? value : JSON.stringify(value));\n }\n\n private docListener?: (e: Event) => void;\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n this.docListener = (e: Event) => {\n if (!this.contains(e.target as Node)) this.toggleAttr('open', false);\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n this.docListener = undefined;\n }\n\n protected override render(): void {\n const placeholder = this.getAttr('placeholder');\n\n this.setTemplate(`\n <input class=\"field\" part=\"input\" type=\"text\"\n ${placeholder ? `placeholder=\"${esc(placeholder)}\"` : ''} autocomplete=\"off\"/>\n <div class=\"list\" part=\"list\" role=\"listbox\"></div>\n `);\n\n const field = this.root.querySelector<HTMLInputElement>('.field');\n if (!field) return;\n field.value = this.value;\n if (this.hasAttr('disabled')) field.setAttribute('disabled', '');\n\n const list = this.root.querySelector<HTMLElement>('.list');\n const minChars = Number(this.getAttr('min-chars', '1')) || 1;\n\n const openList = (): void => {\n if (!list) return;\n const q = field.value.toLowerCase();\n const matches = this.source.filter((s) => s.toLowerCase().includes(q)).slice(0, 8);\n list.innerHTML = matches.length\n ? matches.map((m) => `<button type=\"button\" class=\"opt\" role=\"option\" data-v=\"${esc(m)}\">${esc(m)}</button>`).join('')\n : '<div class=\"empty\">Nenhum resultado</div>';\n list.querySelectorAll<HTMLButtonElement>('.opt').forEach((btn) => {\n btn.addEventListener('click', () => {\n field.value = btn.dataset.v ?? '';\n this.value = field.value;\n this.toggleAttr('open', false);\n this.emitValueEvents();\n this.dispatchEvent(new CustomEvent('select', {\n bubbles: true, composed: true, detail: { value: this.value },\n }));\n });\n });\n this.toggleAttr('open', true);\n };\n\n // Reabrir com foco sem re-render (evita perder digitação)\n field.addEventListener('input', () => {\n this.value = field.value;\n this.emitValueEvents();\n openList();\n });\n field.addEventListener('focus', () => { if (field.value.length >= minChars) openList(); });\n // Navegação por teclado: setas movem o foco entre as sugestões; ESC fecha.\n // Enter com a lista FECHADA submete o form (o input interno não se associa\n // ao <form> do light DOM — submissão implícita acionada manualmente).\n field.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n this.toggleAttr('open', false);\n return;\n }\n if (e.key === 'Enter' && !e.isComposing && !this.hasAttr('open') && this.form) {\n e.preventDefault();\n this.form.requestSubmit();\n return;\n }\n if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') return;\n e.preventDefault();\n if (!this.hasAttr('open')) openList();\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 const next = e.key === 'ArrowDown'\n ? (current === -1 ? 0 : Math.min(current + 1, opts.length - 1))\n : (current === -1 ? opts.length - 1 : Math.max(current - 1, 0));\n opts[next]?.focus();\n });\n\n // Publica name/value no form (ElementInternals) a cada render.\n this.updateFormValue();\n }\n\n /** Empurra o atributo `value` para o campo interno sem re-renderizar. */\n protected override syncInternalControl(): void {\n const field = this.root.querySelector<HTMLInputElement>('.field');\n const value = this.getAttr('value');\n if (field && field.value !== value) field.value = value;\n }\n}\n\nexport function defineFxAutocomplete(): typeof FxAutocomplete {\n return defineElement('fx-autocomplete', FxAutocomplete);\n}\n"],"names":[],"mappings":";;;;AAgBO,MAAM,kBAAN,MAAM,wBAAuB,cAAc;AAAA,EAwFhD,WAAoB,qBAA+B;AAGjD,WAAO,CAAC,QAAQ,eAAe,UAAU,YAAY,aAAa,SAAS,WAAW,WAAW,OAAO;AAAA,EAC1G;AAAA,EAEA,IAAI,OAAe;AACjB,UAAM,IAAI,KAAK,QAAQ,QAAQ,IAAI;AACnC,WAAO,MAAM,QAAQ,MAAM,OAAO,IAAI;AAAA,EACxC;AAAA,EACA,IAAI,KAAK,GAAW;AAAE,SAAK,aAAa,QAAQ,CAAC;AAAA,EAAG;AAAA,EAEpD,IAAI,WAAoB;AACtB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,SAAS,GAAY;AACvB,SAAK,WAAW,YAAY,QAAQ,CAAC,CAAC;AAAA,EACxC;AAAA,EACA,IAAI,QAAgB;AAAE,WAAO,KAAK,QAAQ,OAAO;AAAA,EAAG;AAAA,EACpD,IAAI,MAAM,GAA8B;AACtC,SAAK,aAAa,SAAS,KAAK,OAAO,KAAK,OAAO,CAAC,CAAC;AAGrD,SAAK,oBAAA;AACL,SAAK,gBAAA;AAAA,EACP;AAAA,EAEA,IAAI,SAAmB;AACrB,QAAI;AACF,YAAM,MAAM,KAAK,MAAM,KAAK,QAAQ,UAAU,IAAI,CAAC;AACnD,aAAO,MAAM,QAAQ,GAAG,IAAI,IAAI,IAAI,MAAM,IAAI,CAAA;AAAA,IAChD,QAAQ;AACN,aAAO,CAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA,EAGA,IAAI,OAAO,OAA0B;AACnC,SAAK,aAAa,UAAU,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK,CAAC;AAAA,EACvF;AAAA,EAImB,oBAA0B;AAC3C,UAAM,kBAAA;AACN,SAAK,cAAc,CAAC,MAAa;AAC/B,UAAI,CAAC,KAAK,SAAS,EAAE,MAAc,EAAG,MAAK,WAAW,QAAQ,KAAK;AAAA,IACrE;AACA,aAAS,iBAAiB,SAAS,KAAK,WAAW;AAAA,EACrD;AAAA,EAEmB,uBAA6B;AAC9C,UAAM,qBAAA;AACN,QAAI,KAAK,YAAa,UAAS,oBAAoB,SAAS,KAAK,WAAW;AAC5E,SAAK,cAAc;AAAA,EACrB;AAAA,EAEmB,SAAe;AAChC,UAAM,cAAc,KAAK,QAAQ,aAAa;AAE9C,SAAK,YAAY;AAAA;AAAA,UAEX,cAAc,gBAAgB,IAAI,WAAW,CAAC,MAAM,EAAE;AAAA;AAAA,KAE3D;AAED,UAAM,QAAQ,KAAK,KAAK,cAAgC,QAAQ;AAChE,QAAI,CAAC,MAAO;AACZ,UAAM,QAAQ,KAAK;AACnB,QAAI,KAAK,QAAQ,UAAU,EAAG,OAAM,aAAa,YAAY,EAAE;AAE/D,UAAM,OAAO,KAAK,KAAK,cAA2B,OAAO;AACzD,UAAM,WAAW,OAAO,KAAK,QAAQ,aAAa,GAAG,CAAC,KAAK;AAE3D,UAAM,WAAW,MAAY;AAC3B,UAAI,CAAC,KAAM;AACX,YAAM,IAAI,MAAM,MAAM,YAAA;AACtB,YAAM,UAAU,KAAK,OAAO,OAAO,CAAC,MAAM,EAAE,YAAA,EAAc,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC;AACjF,WAAK,YAAY,QAAQ,SACrB,QAAQ,IAAI,CAAC,MAAM,2DAA2D,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,EAAE,IACnH;AACJ,WAAK,iBAAoC,MAAM,EAAE,QAAQ,CAAC,QAAQ;AAChE,YAAI,iBAAiB,SAAS,MAAM;AAClC,gBAAM,QAAQ,IAAI,QAAQ,KAAK;AAC/B,eAAK,QAAQ,MAAM;AACnB,eAAK,WAAW,QAAQ,KAAK;AAC7B,eAAK,gBAAA;AACL,eAAK,cAAc,IAAI,YAAY,UAAU;AAAA,YAC3C,SAAS;AAAA,YAAM,UAAU;AAAA,YAAM,QAAQ,EAAE,OAAO,KAAK,MAAA;AAAA,UAAM,CAC5D,CAAC;AAAA,QACJ,CAAC;AAAA,MACH,CAAC;AACD,WAAK,WAAW,QAAQ,IAAI;AAAA,IAC9B;AAGA,UAAM,iBAAiB,SAAS,MAAM;AACpC,WAAK,QAAQ,MAAM;AACnB,WAAK,gBAAA;AACL,eAAA;AAAA,IACF,CAAC;AACD,UAAM,iBAAiB,SAAS,MAAM;AAAE,UAAI,MAAM,MAAM,UAAU,SAAU,UAAA;AAAA,IAAY,CAAC;AAIzF,UAAM,iBAAiB,WAAW,CAAC,MAAM;AACvC,UAAI,EAAE,QAAQ,UAAU;AACtB,aAAK,WAAW,QAAQ,KAAK;AAC7B;AAAA,MACF;AACA,UAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,eAAe,CAAC,KAAK,QAAQ,MAAM,KAAK,KAAK,MAAM;AAC7E,UAAE,eAAA;AACF,aAAK,KAAK,cAAA;AACV;AAAA,MACF;AACA,UAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,UAAW;AAClD,QAAE,eAAA;AACF,UAAI,CAAC,KAAK,QAAQ,MAAM,EAAG,UAAA;AAC3B,YAAM,OAAO,MAAM,KAAK,KAAK,KAAK,iBAAoC,MAAM,CAAC;AAC7E,UAAI,CAAC,KAAK,OAAQ;AAClB,YAAM,UAAU,KAAK,QAAQ,KAAK,KAAK,aAAkC;AACzE,YAAM,OAAO,EAAE,QAAQ,cAClB,YAAY,KAAK,IAAI,KAAK,IAAI,UAAU,GAAG,KAAK,SAAS,CAAC,IAC1D,YAAY,KAAK,KAAK,SAAS,IAAI,KAAK,IAAI,UAAU,GAAG,CAAC;AAC/D,WAAK,IAAI,GAAG,MAAA;AAAA,IACd,CAAC;AAGD,SAAK,gBAAA;AAAA,EACP;AAAA;AAAA,EAGmB,sBAA4B;AAC7C,UAAM,QAAQ,KAAK,KAAK,cAAgC,QAAQ;AAChE,UAAM,QAAQ,KAAK,QAAQ,OAAO;AAClC,QAAI,SAAS,MAAM,UAAU,aAAa,QAAQ;AAAA,EACpD;AACF;AAhOE,gBAAgB,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;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,iBAAN;AAmOA,SAAS,uBAA8C;AAC5D,SAAO,cAAc,mBAAmB,cAAc;AACxD;"}
@@ -13,6 +13,7 @@ export declare class FxAvatar extends FxElement {
13
13
  static styles: string;
14
14
  static get observedAttributes(): string[];
15
15
  get variant(): string;
16
+ set variant(v: string);
16
17
  get src(): string;
17
18
  set src(v: string);
18
19
  protected render(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../src/components/avatar/avatar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;GASG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAmDpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,OAAO,IAAI,MAAM,CAA4C;IACjE,IAAI,GAAG,IAAI,MAAM,CAAgC;IACjD,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,EAAkC;cAEhC,MAAM,IAAI,IAAI;CAiBlC;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
1
+ {"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../src/components/avatar/avatar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;GASG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAmDpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,OAAO,IAAI,MAAM,CAA4C;IACjE,IAAI,OAAO,CAAC,CAAC,EAAE,MAAM,EAAsC;IAC3D,IAAI,GAAG,IAAI,MAAM,CAAgC;IACjD,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,EAAkC;cAEhC,MAAM,IAAI,IAAI;CAiBlC;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
@@ -9,6 +9,9 @@ const _FxAvatar = class _FxAvatar extends FxElement {
9
9
  get variant() {
10
10
  return this.getAttr("variant", "text");
11
11
  }
12
+ set variant(v) {
13
+ this.setAttribute("variant", v);
14
+ }
12
15
  get src() {
13
16
  return this.getAttr("src");
14
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"avatar.js","sources":["../../../src/components/avatar/avatar.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-avatar> — Representação visual de usuário/item.\n *\n * Atributos: size (number px / sm|md|lg), shape (circle|rounded|square),\n * variant (image|text|icon), src (imagem), alt (acessibilidade).\n * Slots: `icon` (glifo/emoji quando variant=icon) e padrão (iniciais).\n *\n * Uso em grupo: posicione vários `<fx-avatar>` em um container com a classe\n * `avatar-group` (empilha com overlap automático).\n */\nexport class FxAvatar extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n --_size: var(--fx-size-md, 36px);\n --_bg: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.08));\n --_color: var(--fx-text-default, #1e293b);\n }\n :host([size='sm']) { --_size: var(--fx-size-sm, 24px); }\n :host([size='lg']) { --_size: var(--fx-size-lg, 48px); }\n\n .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--_size);\n height: var(--_size);\n min-width: var(--_size);\n box-sizing: border-box;\n background: var(--_bg);\n color: var(--_color);\n font-weight: 700;\n font-size: calc(var(--_size) / 2.2);\n line-height: 1;\n overflow: hidden;\n text-decoration: none;\n /* border-radius definido por shape abaixo */\n }\n :host([shape='square']) .avatar { border-radius: var(--fx-radius-sm); }\n :host([shape='rounded']) .avatar { border-radius: var(--fx-radius-md); }\n :host([shape='circle']) .avatar, :host([shape='']) .avatar { border-radius: var(--fx-radius-full); }\n\n .avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n }\n .avatar.text { font-size: calc(var(--_size) / 2.6); }\n .avatar .slot-icon { font-size: calc(var(--_size) / 2.4); }\n\n /* grupo empilhado */\n :host(.avatar-group) { display: inline-flex; }\n :host(.avatar-group) > .avatar {\n border: 2px solid var(--fx-surface-background);\n margin-left: calc(var(--_size) / -3);\n z-index: 1;\n }\n :host(.avatar-group) > .avatar:last-child { margin-left: 0; }\n :host(.avatar-group) > .avatar:hover { z-index: 2; }\n `;\n\n static override get observedAttributes(): string[] {\n return ['size', 'shape', 'variant', 'src', 'alt'];\n }\n\n get variant(): string { return this.getAttr('variant', 'text'); }\n get src(): string { return this.getAttr('src'); }\n set src(v: string) { this.setAttribute('src', v); }\n\n protected override render(): void {\n const variant = this.variant;\n const src = this.getAttr('src');\n const alt = this.getAttr('alt', '');\n const isIcon = variant === 'icon';\n const isImage = variant === 'image' && src;\n\n this.setTemplate(`\n <span class=\"avatar${isImage ? ' image' : isIcon ? ' icon-slot' : ' text'}\" part=\"avatar\" aria-label=\"${esc(alt)}\" role=\"img\">\n ${isImage\n ? `<img src=\"${esc(src)}\" alt=\"${esc(alt)}\">`\n : isIcon\n ? `<slot name=\"icon\" class=\"slot-icon\"></slot>`\n : `<slot></slot>`}\n </span>\n `);\n }\n}\n\nexport function defineFxAvatar(): typeof FxAvatar {\n return defineElement('fx-avatar', FxAvatar);\n}\n"],"names":[],"mappings":";;;;AAeO,MAAM,YAAN,MAAM,kBAAiB,UAAU;AAAA,EAsDtC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,QAAQ,SAAS,WAAW,OAAO,KAAK;AAAA,EAClD;AAAA,EAEA,IAAI,UAAkB;AAAE,WAAO,KAAK,QAAQ,WAAW,MAAM;AAAA,EAAG;AAAA,EAChE,IAAI,MAAc;AAAE,WAAO,KAAK,QAAQ,KAAK;AAAA,EAAG;AAAA,EAChD,IAAI,IAAI,GAAW;AAAE,SAAK,aAAa,OAAO,CAAC;AAAA,EAAG;AAAA,EAE/B,SAAe;AAChC,UAAM,UAAU,KAAK;AACrB,UAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,UAAM,MAAM,KAAK,QAAQ,OAAO,EAAE;AAClC,UAAM,SAAS,YAAY;AAC3B,UAAM,UAAU,YAAY,WAAW;AAEvC,SAAK,YAAY;AAAA,2BACM,UAAU,WAAW,SAAS,eAAe,OAAO,+BAA+B,IAAI,GAAG,CAAC;AAAA,UAC5G,UACkB,aAAa,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,OACvD,SACE,gDACA,eAAe;AAAA;AAAA,KAExB;AAAA,EACH;AACF;AA9EE,UAAgB,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;AADpB,IAAM,WAAN;AAiFA,SAAS,iBAAkC;AAChD,SAAO,cAAc,aAAa,QAAQ;AAC5C;"}
1
+ {"version":3,"file":"avatar.js","sources":["../../../src/components/avatar/avatar.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-avatar> — Representação visual de usuário/item.\n *\n * Atributos: size (number px / sm|md|lg), shape (circle|rounded|square),\n * variant (image|text|icon), src (imagem), alt (acessibilidade).\n * Slots: `icon` (glifo/emoji quando variant=icon) e padrão (iniciais).\n *\n * Uso em grupo: posicione vários `<fx-avatar>` em um container com a classe\n * `avatar-group` (empilha com overlap automático).\n */\nexport class FxAvatar extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n --_size: var(--fx-size-md, 36px);\n --_bg: var(--fx-surface-surface-hover, rgba(0, 0, 0, 0.08));\n --_color: var(--fx-text-default, #1e293b);\n }\n :host([size='sm']) { --_size: var(--fx-size-sm, 24px); }\n :host([size='lg']) { --_size: var(--fx-size-lg, 48px); }\n\n .avatar {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: var(--_size);\n height: var(--_size);\n min-width: var(--_size);\n box-sizing: border-box;\n background: var(--_bg);\n color: var(--_color);\n font-weight: 700;\n font-size: calc(var(--_size) / 2.2);\n line-height: 1;\n overflow: hidden;\n text-decoration: none;\n /* border-radius definido por shape abaixo */\n }\n :host([shape='square']) .avatar { border-radius: var(--fx-radius-sm); }\n :host([shape='rounded']) .avatar { border-radius: var(--fx-radius-md); }\n :host([shape='circle']) .avatar, :host([shape='']) .avatar { border-radius: var(--fx-radius-full); }\n\n .avatar img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n display: block;\n }\n .avatar.text { font-size: calc(var(--_size) / 2.6); }\n .avatar .slot-icon { font-size: calc(var(--_size) / 2.4); }\n\n /* grupo empilhado */\n :host(.avatar-group) { display: inline-flex; }\n :host(.avatar-group) > .avatar {\n border: 2px solid var(--fx-surface-background);\n margin-left: calc(var(--_size) / -3);\n z-index: 1;\n }\n :host(.avatar-group) > .avatar:last-child { margin-left: 0; }\n :host(.avatar-group) > .avatar:hover { z-index: 2; }\n `;\n\n static override get observedAttributes(): string[] {\n return ['size', 'shape', 'variant', 'src', 'alt'];\n }\n\n get variant(): string { return this.getAttr('variant', 'text'); }\n set variant(v: string) { this.setAttribute('variant', v); }\n get src(): string { return this.getAttr('src'); }\n set src(v: string) { this.setAttribute('src', v); }\n\n protected override render(): void {\n const variant = this.variant;\n const src = this.getAttr('src');\n const alt = this.getAttr('alt', '');\n const isIcon = variant === 'icon';\n const isImage = variant === 'image' && src;\n\n this.setTemplate(`\n <span class=\"avatar${isImage ? ' image' : isIcon ? ' icon-slot' : ' text'}\" part=\"avatar\" aria-label=\"${esc(alt)}\" role=\"img\">\n ${isImage\n ? `<img src=\"${esc(src)}\" alt=\"${esc(alt)}\">`\n : isIcon\n ? `<slot name=\"icon\" class=\"slot-icon\"></slot>`\n : `<slot></slot>`}\n </span>\n `);\n }\n}\n\nexport function defineFxAvatar(): typeof FxAvatar {\n return defineElement('fx-avatar', FxAvatar);\n}\n"],"names":[],"mappings":";;;;AAeO,MAAM,YAAN,MAAM,kBAAiB,UAAU;AAAA,EAsDtC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,QAAQ,SAAS,WAAW,OAAO,KAAK;AAAA,EAClD;AAAA,EAEA,IAAI,UAAkB;AAAE,WAAO,KAAK,QAAQ,WAAW,MAAM;AAAA,EAAG;AAAA,EAChE,IAAI,QAAQ,GAAW;AAAE,SAAK,aAAa,WAAW,CAAC;AAAA,EAAG;AAAA,EAC1D,IAAI,MAAc;AAAE,WAAO,KAAK,QAAQ,KAAK;AAAA,EAAG;AAAA,EAChD,IAAI,IAAI,GAAW;AAAE,SAAK,aAAa,OAAO,CAAC;AAAA,EAAG;AAAA,EAE/B,SAAe;AAChC,UAAM,UAAU,KAAK;AACrB,UAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,UAAM,MAAM,KAAK,QAAQ,OAAO,EAAE;AAClC,UAAM,SAAS,YAAY;AAC3B,UAAM,UAAU,YAAY,WAAW;AAEvC,SAAK,YAAY;AAAA,2BACM,UAAU,WAAW,SAAS,eAAe,OAAO,+BAA+B,IAAI,GAAG,CAAC;AAAA,UAC5G,UACkB,aAAa,IAAI,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,OACvD,SACE,gDACA,eAAe;AAAA;AAAA,KAExB;AAAA,EACH;AACF;AA/EE,UAAgB,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;AADpB,IAAM,WAAN;AAkFA,SAAS,iBAAkC;AAChD,SAAO,cAAc,aAAa,QAAQ;AAC5C;"}
@@ -8,6 +8,10 @@ import { FxElement } from '../../core/base';
8
8
  export declare class FxBadge extends FxElement {
9
9
  static styles: string;
10
10
  static get observedAttributes(): string[];
11
+ get variant(): string;
12
+ set variant(v: string);
13
+ get round(): boolean;
14
+ set round(v: boolean);
11
15
  protected render(): void;
12
16
  }
13
17
  export declare function defineFxBadge(): typeof FxBadge;
@@ -1 +1 @@
1
- {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../../src/components/badge/badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;;GAKG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACpC,OAAgB,MAAM,SAyBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;CAGlC;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
1
+ {"version":3,"file":"badge.d.ts","sourceRoot":"","sources":["../../../src/components/badge/badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;;GAKG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACpC,OAAgB,MAAM,SAyBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,OAAO,IAAI,MAAM,CAA+C;IACpE,IAAI,OAAO,CAAC,CAAC,EAAE,MAAM,EAAsC;IAE3D,IAAI,KAAK,IAAI,OAAO,CAAkC;IACtD,IAAI,KAAK,CAAC,CAAC,EAAE,OAAO,EAA2C;cAE5C,MAAM,IAAI,IAAI;CAGlC;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
@@ -5,6 +5,18 @@ const _FxBadge = class _FxBadge extends FxElement {
5
5
  static get observedAttributes() {
6
6
  return ["variant", "round"];
7
7
  }
8
+ get variant() {
9
+ return this.getAttr("variant", "default");
10
+ }
11
+ set variant(v) {
12
+ this.setAttribute("variant", v);
13
+ }
14
+ get round() {
15
+ return this.hasAttr("round");
16
+ }
17
+ set round(v) {
18
+ this.toggleAttr("round", Boolean(v));
19
+ }
8
20
  render() {
9
21
  this.setTemplate(`<span class="badge" part="badge"><slot></slot></span>`);
10
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"badge.js","sources":["../../../src/components/badge/badge.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\n\n/**\n * <fx-badge> — Rótulo/contador de status.\n *\n * Atributos: variant (default|primary|success|warning|danger|info), round.\n * Slots: default (conteúdo).\n */\nexport class FxBadge extends FxElement {\n static override styles = css`\n :host {\n display: inline-flex;\n vertical-align: middle;\n }\n .badge {\n display: inline-flex;\n align-items: center;\n font-family: var(--fx-font-family);\n font-size: calc(var(--fx-font-size) - 2px);\n font-weight: var(--fx-font-weight);\n line-height: 1;\n padding: var(--fx-space-xs) var(--fx-space-sm);\n border-radius: var(--fx-radius-full);\n border: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n color: var(--fx-text-muted);\n white-space: nowrap;\n }\n :host([variant='primary']) .badge { background: var(--fx-color-primary); color: #fff; border-color: transparent; }\n :host([variant='success']) .badge { background: var(--fx-color-success); color: #fff; border-color: transparent; }\n :host([variant='warning']) .badge { background: var(--fx-color-warning); color: #fff; border-color: transparent; }\n :host([variant='danger']) .badge { background: var(--fx-color-danger); color: #fff; border-color: transparent; }\n :host([variant='info']) .badge { background: var(--fx-color-info); color: #fff; border-color: transparent; }\n :host(:not([round])) .badge { border-radius: var(--fx-radius-sm); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'round'];\n }\n\n protected override render(): void {\n this.setTemplate(`<span class=\"badge\" part=\"badge\"><slot></slot></span>`);\n }\n}\n\nexport function defineFxBadge(): typeof FxBadge {\n return defineElement('fx-badge', FxBadge);\n}"],"names":[],"mappings":";;;AAUO,MAAM,WAAN,MAAM,iBAAgB,UAAU;AAAA,EA4BrC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,OAAO;AAAA,EAC5B;AAAA,EAEmB,SAAe;AAChC,SAAK,YAAY,uDAAuD;AAAA,EAC1E;AACF;AAlCE,SAAgB,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;AADpB,IAAM,UAAN;AAqCA,SAAS,gBAAgC;AAC9C,SAAO,cAAc,YAAY,OAAO;AAC1C;"}
1
+ {"version":3,"file":"badge.js","sources":["../../../src/components/badge/badge.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\n\n/**\n * <fx-badge> — Rótulo/contador de status.\n *\n * Atributos: variant (default|primary|success|warning|danger|info), round.\n * Slots: default (conteúdo).\n */\nexport class FxBadge extends FxElement {\n static override styles = css`\n :host {\n display: inline-flex;\n vertical-align: middle;\n }\n .badge {\n display: inline-flex;\n align-items: center;\n font-family: var(--fx-font-family);\n font-size: calc(var(--fx-font-size) - 2px);\n font-weight: var(--fx-font-weight);\n line-height: 1;\n padding: var(--fx-space-xs) var(--fx-space-sm);\n border-radius: var(--fx-radius-full);\n border: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n color: var(--fx-text-muted);\n white-space: nowrap;\n }\n :host([variant='primary']) .badge { background: var(--fx-color-primary); color: #fff; border-color: transparent; }\n :host([variant='success']) .badge { background: var(--fx-color-success); color: #fff; border-color: transparent; }\n :host([variant='warning']) .badge { background: var(--fx-color-warning); color: #fff; border-color: transparent; }\n :host([variant='danger']) .badge { background: var(--fx-color-danger); color: #fff; border-color: transparent; }\n :host([variant='info']) .badge { background: var(--fx-color-info); color: #fff; border-color: transparent; }\n :host(:not([round])) .badge { border-radius: var(--fx-radius-sm); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'round'];\n }\n\n get variant(): string { return this.getAttr('variant', 'default'); }\n set variant(v: string) { this.setAttribute('variant', v); }\n\n get round(): boolean { return this.hasAttr('round'); }\n set round(v: boolean) { this.toggleAttr('round', Boolean(v)); }\n\n protected override render(): void {\n this.setTemplate(`<span class=\"badge\" part=\"badge\"><slot></slot></span>`);\n }\n}\n\nexport function defineFxBadge(): typeof FxBadge {\n return defineElement('fx-badge', FxBadge);\n}"],"names":[],"mappings":";;;AAUO,MAAM,WAAN,MAAM,iBAAgB,UAAU;AAAA,EA4BrC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,OAAO;AAAA,EAC5B;AAAA,EAEA,IAAI,UAAkB;AAAE,WAAO,KAAK,QAAQ,WAAW,SAAS;AAAA,EAAG;AAAA,EACnE,IAAI,QAAQ,GAAW;AAAE,SAAK,aAAa,WAAW,CAAC;AAAA,EAAG;AAAA,EAE1D,IAAI,QAAiB;AAAE,WAAO,KAAK,QAAQ,OAAO;AAAA,EAAG;AAAA,EACrD,IAAI,MAAM,GAAY;AAAE,SAAK,WAAW,SAAS,QAAQ,CAAC,CAAC;AAAA,EAAG;AAAA,EAE3C,SAAe;AAChC,SAAK,YAAY,uDAAuD;AAAA,EAC1E;AACF;AAxCE,SAAgB,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;AADpB,IAAM,UAAN;AA2CA,SAAS,gBAAgC;AAC9C,SAAO,cAAc,YAAY,OAAO;AAC1C;"}
@@ -33,6 +33,8 @@ export declare class FxButton extends FxElement {
33
33
  set disabled(value: boolean);
34
34
  get loading(): boolean;
35
35
  set loading(value: boolean);
36
+ get full(): boolean;
37
+ set full(value: boolean);
36
38
  protected render(): void;
37
39
  protected connectedCallback(): void;
38
40
  protected disconnectedCallback(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/button/button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,0EAA0E;IAC1E,MAAM,CAAC,cAAc,UAAQ;IAC7B,6EAA6E;IAC7E,OAAgB,aAAa,EAAE,cAAc,CAA0C;IACvF,OAAgB,MAAM,SAmHpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;;IAUtD,qEAAqE;IACrE,IAAI,IAAI,IAAI,eAAe,GAAG,IAAI,CAIjC;IAED,0EAA0E;IAC1E,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAK7C,+BAA+B;IAC/B,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;cAEkB,MAAM,IAAI,IAAI;cA8Dd,iBAAiB,IAAI,IAAI;cAKzB,oBAAoB,IAAI,IAAI;IAK/C;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAmBjB;CACH;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
1
+ {"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/button/button.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;GAaG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,0EAA0E;IAC1E,MAAM,CAAC,cAAc,UAAQ;IAC7B,6EAA6E;IAC7E,OAAgB,aAAa,EAAE,cAAc,CAA0C;IACvF,OAAgB,MAAM,SAmHpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;;IAUtD,qEAAqE;IACrE,IAAI,IAAI,IAAI,eAAe,GAAG,IAAI,CAIjC;IAED,0EAA0E;IAC1E,oBAAoB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI;IAK7C,+BAA+B;IAC/B,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,IAAI,IAAI,OAAO,CAElB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,OAAO,EAEtB;cAEkB,MAAM,IAAI,IAAI;cA8Dd,iBAAiB,IAAI,IAAI;cAKzB,oBAAoB,IAAI,IAAI;IAK/C;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAmBjB;CACH;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
@@ -55,6 +55,12 @@ const _FxButton = class _FxButton extends FxElement {
55
55
  set loading(value) {
56
56
  this.toggleAttr("loading", Boolean(value));
57
57
  }
58
+ get full() {
59
+ return this.hasAttr("full");
60
+ }
61
+ set full(value) {
62
+ this.toggleAttr("full", Boolean(value));
63
+ }
58
64
  render() {
59
65
  const loading = this.hasAttr("loading");
60
66
  const disabled = this.hasAttr("disabled") || loading;
@@ -1 +1 @@
1
- {"version":3,"file":"button.js","sources":["../../../src/components/button/button.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\n\n/**\n * <fx-button> — Componente de ação (piloto).\n *\n * Form-Associated Custom Element: `type=\"submit\"` submete o <form> em que\n * vive e `type=\"reset\"` o limpa (o <button> interno está no Shadow DOM e não\n * se associa ao form sozinho — a ação é acionada via ElementInternals).\n * `<form disabled>`/`<fieldset disabled>` desabilitam o botão.\n *\n * Atributos: variant, size, disabled, loading, type, full (largura 100%\n * acompanhando o elemento pai), icon (nome do glifo Fenix Icons ou\n * emoji/texto livre) e icon-pos (left|right, padrão left).\n * Slots: default (rótulo), `icon` (ícone customizado — vence o atributo).\n * Evento: `click` nativo atravessa o Shadow DOM (composed) — ouça no host.\n */\nexport class FxButton extends FxElement {\n /** Participa de <form> como botão (submit/reset via ElementInternals). */\n static formAssociated = true;\n /** focus() e clique em <label for> atingem o botão interno do Shadow DOM. */\n static override shadowOptions: ShadowRootInit = { mode: 'open', delegatesFocus: true };\n static override styles = css`\n ${FENIX_ICON_BASE_CSS}\n :host {\n display: inline-block;\n vertical-align: middle;\n }\n /* Full width: o host inline-block é shrink-to-fit, então precisa virar\n block para o 100% do .btn interno ter contra o que esticar. */\n :host([full]) {\n display: block;\n width: 100%;\n max-width: 100%;\n }\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n gap: var(--fx-space-sm);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n font-weight: var(--fx-font-weight);\n line-height: var(--fx-font-line-height);\n /* Padding vertical limitado ao orçamento do token --fx-size-*: a altura\n fica EXATAMENTE em sm 32 / md 40 / lg 48 (mesma medida dos campos). */\n padding: var(--fx-space-sm) var(--fx-space-xl);\n border-radius: var(--fx-radius-md);\n border: 1px solid transparent;\n color: #fff;\n background: var(--fx-color-primary);\n cursor: pointer;\n user-select: none;\n text-decoration: none;\n transition:\n background-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n transform var(--fx-motion-duration-fast) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .btn:focus-visible {\n outline: none;\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .btn:hover { filter: brightness(0.88); }\n .btn:active { transform: translateY(1px); }\n\n /* Tamanhos — alturas vindas dos tokens (personalizáveis por preset) */\n .btn { min-height: var(--fx-size-md); }\n :host([size='sm']) .btn { padding: var(--fx-space-xs) var(--fx-space-lg); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }\n :host([size='lg']) .btn { padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }\n\n /* Full width */\n :host([full]) .btn { width: 100%; }\n\n /* Variantes */\n :host([variant='secondary']) .btn { background: var(--fx-color-secondary); }\n :host([variant='success']) .btn { background: var(--fx-color-success); }\n :host([variant='warning']) .btn { background: var(--fx-color-warning); }\n :host([variant='danger']) .btn { background: var(--fx-color-danger); }\n :host([variant='info']) .btn { background: var(--fx-color-info); }\n :host([variant='ghost']) .btn { background: transparent; color: var(--fx-text-default); }\n :host([variant='ghost']) .btn:hover { background: var(--fx-surface-surface-hover); filter: none; }\n :host([variant='outline']) .btn { background: transparent; color: var(--fx-color-primary); border-color: var(--fx-color-primary); }\n :host([variant='outline']) .btn:hover { background: var(--fx-surface-surface-hover); filter: none; }\n\n /* Ícone e spinner */\n .btn__icon { display: inline-flex; align-items: center; font-size: var(--fx-button-icon-size, calc(var(--fx-font-size) + 6px)); line-height: 1; }\n .btn__icon[hidden] { display: none; }\n .btn__label { display: inline-flex; align-items: center; justify-content: center; }\n /* Icon-only: a label some (o gap do flex some junto) e o botão vira\n quadrado com padding simétrico — ícone perfeitamente centralizado. */\n .btn--icon-only { padding-left: var(--fx-space-md); padding-right: var(--fx-space-md); }\n .btn--icon-only .btn__label { display: none; }\n .btn[hidden] { display: none; }\n .btn__spinner {\n width: 1em;\n height: 1em;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: var(--fx-radius-full);\n animation: fx-spin var(--fx-motion-duration-normal) linear infinite;\n }\n @keyframes fx-spin { to { transform: rotate(360deg); } }\n\n /* Estados */\n :host([disabled]) .btn,\n :host([loading]) .btn {\n opacity: 0.55;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* Efeito ripple (). Desative com effect.ripple: '0' no preset. */\n .btn {\n position: relative;\n overflow: hidden;\n }\n .btn__ripple {\n position: absolute;\n border-radius: var(--fx-radius-full);\n background: currentColor;\n opacity: 0.28;\n transform: scale(0);\n animation: fx-ripple 600ms var(--fx-motion-easing) forwards;\n pointer-events: none;\n }\n @keyframes fx-ripple {\n to {\n transform: scale(2.6);\n opacity: 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .btn__ripple { display: none; }\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'size', 'disabled', 'loading', 'type', 'full', 'icon', 'icon-pos'];\n }\n\n protected readonly internals: ElementInternals | null;\n\n constructor() {\n super();\n // Ambientes sem ElementInternals (ex.: jsdom nos testes) funcionam sem\n // a integração nativa de form — o getter `form` cai no closest('form').\n this.internals =\n typeof this.attachInternals === 'function' ? this.attachInternals() : null;\n }\n\n /** Form ao qual o botão está associado (null se fora de um form). */\n get form(): HTMLFormElement | null {\n return (\n this.internals?.form ?? (this.closest('form') as HTMLFormElement | null)\n );\n }\n\n /** <form disabled>/<fieldset disabled>: espelha no atributo observado. */\n formDisabledCallback(disabled: boolean): void {\n if (disabled) this.setAttribute('disabled', '');\n else this.removeAttribute('disabled');\n }\n\n /** Tamanho. 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 disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n get loading(): boolean {\n return this.hasAttr('loading');\n }\n set loading(value: boolean) {\n this.toggleAttr('loading', Boolean(value));\n }\n\n protected override render(): void {\n const loading = this.hasAttr('loading');\n const disabled = this.hasAttr('disabled') || loading;\n const btnType = this.getAttr('type', 'button');\n const icon = this.getAttr('icon');\n const iconRight = this.getAttr('icon-pos', 'left') === 'right';\n // Slot `icon` vence o atributo `icon`; sem nenhum dos dois, sem ícone.\n const slottedIcon = this.querySelector<HTMLElement>('[slot=\"icon\"]');\n const iconContent = slottedIcon\n ? '<slot name=\"icon\"></slot>'\n : (icon ? fenixIconHtml(icon) : '');\n\n this.setTemplate(`\n <button class=\"btn\" part=\"button\" type=\"${btnType}\">\n ${!iconRight ? `<span class=\"btn__icon\" part=\"icon\">${iconContent}</span>` : ''}\n <span class=\"btn__label\" part=\"label\"><slot></slot></span>\n ${iconRight ? `<span class=\"btn__icon\" part=\"icon\">${iconContent}</span>` : ''}\n ${loading ? '<span class=\"btn__spinner\" aria-hidden=\"true\"></span>' : ''}\n </button>\n `);\n\n const btn = this.root.querySelector<HTMLButtonElement>('.btn');\n if (!btn) return;\n\n // Esconde o container do ícone quando vazio, evitando espaço fantasma\n // do `gap` que descentraliza o rótulo.\n const hasIcon = iconContent.trim() !== '';\n for (const wrap of this.root.querySelectorAll<HTMLElement>('.btn__icon')) {\n wrap.toggleAttribute('hidden', !hasIcon);\n }\n\n // Icon-only: sem texto no slot default, o botão vira quadrado\n // (padding simétrico) em vez de retangular largo.\n const hasLabel = [...(this.childNodes ?? [])].some((n) =>\n n.nodeType === Node.ELEMENT_NODE\n ? (n as Element).getAttribute?.('slot') !== 'icon'\n : (n.textContent ?? '').trim() !== '',\n );\n btn.classList.toggle('btn--icon-only', !hasLabel && hasIcon);\n if (disabled) {\n btn.setAttribute('disabled', '');\n btn.setAttribute('aria-disabled', 'true');\n } else {\n btn.removeAttribute('disabled');\n btn.removeAttribute('aria-disabled');\n }\n if (loading) btn.setAttribute('aria-busy', 'true');\n else btn.removeAttribute('aria-busy');\n\n // type=\"submit\"/\"reset\": o <button> interno vive no Shadow DOM e NÃO se\n // associa ao <form> do light DOM (a fronteira do shadow corta a associação\n // por árvore) — a ação do botão é acionada manualmente via requestSubmit/\n // reset do form associado. Ouvintes de submit seguem recebendo o evento.\n if (btnType === 'submit' || btnType === 'reset') {\n btn.addEventListener('click', () => {\n if (this.hasAttr('disabled') || this.hasAttr('loading')) return;\n if (btnType === 'submit' && this.form) this.form.requestSubmit();\n else if (btnType === 'reset' && this.form) this.form.reset();\n });\n }\n }\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('pointerdown', this.spawnRipple);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('pointerdown', this.spawnRipple);\n }\n\n /**\n * Efeito ripple : cria um círculo que expande do ponto\n * de clique e desaparece. Respeita o token `effect.ripple` ('0' desativa)\n * e `prefers-reduced-motion` (via CSS).\n */\n private spawnRipple = (event: PointerEvent): void => {\n if (this.hasAttr('disabled') || this.hasAttr('loading')) return;\n const enabled = getComputedStyle(this)\n .getPropertyValue('--fx-effect-ripple')\n .trim();\n if (enabled === '0' || enabled === 'false') return;\n\n const btn = this.root.querySelector<HTMLElement>('.btn');\n if (!btn) return;\n\n const rect = btn.getBoundingClientRect();\n const size = Math.max(rect.width, rect.height);\n const ripple = document.createElement('span');\n ripple.className = 'btn__ripple';\n ripple.style.width = ripple.style.height = `${size}px`;\n ripple.style.left = `${event.clientX - rect.left - size / 2}px`;\n ripple.style.top = `${event.clientY - rect.top - size / 2}px`;\n btn.appendChild(ripple);\n ripple.addEventListener('animationend', () => ripple.remove());\n };\n}\n\nexport function defineFxButton(): typeof FxButton {\n return defineElement('fx-button', FxButton);\n}"],"names":[],"mappings":";;;;AAmBO,MAAM,YAAN,MAAM,kBAAiB,UAAU;AAAA,EAgItC,cAAc;AACZ,UAAA;AAwHF,SAAQ,cAAc,CAAC,UAA8B;AACnD,UAAI,KAAK,QAAQ,UAAU,KAAK,KAAK,QAAQ,SAAS,EAAG;AACzD,YAAM,UAAU,iBAAiB,IAAI,EAClC,iBAAiB,oBAAoB,EACrC,KAAA;AACH,UAAI,YAAY,OAAO,YAAY,QAAS;AAE5C,YAAM,MAAM,KAAK,KAAK,cAA2B,MAAM;AACvD,UAAI,CAAC,IAAK;AAEV,YAAM,OAAO,IAAI,sBAAA;AACjB,YAAM,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM;AAC7C,YAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,aAAO,YAAY;AACnB,aAAO,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAG,IAAI;AAClD,aAAO,MAAM,OAAO,GAAG,MAAM,UAAU,KAAK,OAAO,OAAO,CAAC;AAC3D,aAAO,MAAM,MAAM,GAAG,MAAM,UAAU,KAAK,MAAM,OAAO,CAAC;AACzD,UAAI,YAAY,MAAM;AACtB,aAAO,iBAAiB,gBAAgB,MAAM,OAAO,QAAQ;AAAA,IAC/D;AAxIE,SAAK,YACH,OAAO,KAAK,oBAAoB,aAAa,KAAK,oBAAoB;AAAA,EAC1E;AAAA,EAZA,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,QAAQ,YAAY,WAAW,QAAQ,QAAQ,QAAQ,UAAU;AAAA,EACtF;AAAA;AAAA,EAaA,IAAI,OAA+B;AACjC,WACE,KAAK,WAAW,QAAS,KAAK,QAAQ,MAAM;AAAA,EAEhD;AAAA;AAAA,EAGA,qBAAqB,UAAyB;AAC5C,QAAI,SAAU,MAAK,aAAa,YAAY,EAAE;AAAA,QACzC,MAAK,gBAAgB,UAAU;AAAA,EACtC;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,UAAM,IAAI,KAAK,QAAQ,QAAQ,IAAI;AACnC,WAAO,MAAM,QAAQ,MAAM,OAAO,IAAI;AAAA,EACxC;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,SAAS,OAAgB;AAC3B,SAAK,WAAW,YAAY,QAAQ,KAAK,CAAC;AAAA,EAC5C;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EACA,IAAI,QAAQ,OAAgB;AAC1B,SAAK,WAAW,WAAW,QAAQ,KAAK,CAAC;AAAA,EAC3C;AAAA,EAEmB,SAAe;AAChC,UAAM,UAAU,KAAK,QAAQ,SAAS;AACtC,UAAM,WAAW,KAAK,QAAQ,UAAU,KAAK;AAC7C,UAAM,UAAU,KAAK,QAAQ,QAAQ,QAAQ;AAC7C,UAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,UAAM,YAAY,KAAK,QAAQ,YAAY,MAAM,MAAM;AAEvD,UAAM,cAAc,KAAK,cAA2B,eAAe;AACnE,UAAM,cAAc,cAChB,8BACC,OAAO,cAAc,IAAI,IAAI;AAElC,SAAK,YAAY;AAAA,gDAC2B,OAAO;AAAA,UAC7C,CAAC,YAAY,uCAAuC,WAAW,YAAY,EAAE;AAAA;AAAA,UAE7E,YAAY,uCAAuC,WAAW,YAAY,EAAE;AAAA,UAC5E,UAAU,0DAA0D,EAAE;AAAA;AAAA,KAE3E;AAED,UAAM,MAAM,KAAK,KAAK,cAAiC,MAAM;AAC7D,QAAI,CAAC,IAAK;AAIV,UAAM,UAAU,YAAY,KAAA,MAAW;AACvC,eAAW,QAAQ,KAAK,KAAK,iBAA8B,YAAY,GAAG;AACxE,WAAK,gBAAgB,UAAU,CAAC,OAAO;AAAA,IACzC;AAIA,UAAM,WAAW,CAAC,GAAI,KAAK,cAAc,CAAA,CAAG,EAAE;AAAA,MAAK,CAAC,MAClD,EAAE,aAAa,KAAK,eACf,EAAc,eAAe,MAAM,MAAM,UACzC,EAAE,eAAe,IAAI,WAAW;AAAA,IAAA;AAEvC,QAAI,UAAU,OAAO,kBAAkB,CAAC,YAAY,OAAO;AAC3D,QAAI,UAAU;AACZ,UAAI,aAAa,YAAY,EAAE;AAC/B,UAAI,aAAa,iBAAiB,MAAM;AAAA,IAC1C,OAAO;AACL,UAAI,gBAAgB,UAAU;AAC9B,UAAI,gBAAgB,eAAe;AAAA,IACrC;AACA,QAAI,QAAS,KAAI,aAAa,aAAa,MAAM;AAAA,QAC5C,KAAI,gBAAgB,WAAW;AAMpC,QAAI,YAAY,YAAY,YAAY,SAAS;AAC/C,UAAI,iBAAiB,SAAS,MAAM;AAClC,YAAI,KAAK,QAAQ,UAAU,KAAK,KAAK,QAAQ,SAAS,EAAG;AACzD,YAAI,YAAY,YAAY,KAAK,KAAM,MAAK,KAAK,cAAA;AAAA,iBACxC,YAAY,WAAW,KAAK,KAAM,MAAK,KAAK,MAAA;AAAA,MACvD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEmB,oBAA0B;AAC3C,UAAM,kBAAA;AACN,SAAK,iBAAiB,eAAe,KAAK,WAAW;AAAA,EACvD;AAAA,EAEmB,uBAA6B;AAC9C,UAAM,qBAAA;AACN,SAAK,oBAAoB,eAAe,KAAK,WAAW;AAAA,EAC1D;AA2BF;AA3QE,UAAO,iBAAiB;AAExB,UAAgB,gBAAgC,EAAE,MAAM,QAAQ,gBAAgB,KAAA;AAChF,UAAgB,SAAS;AAAA,MACrB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AANlB,IAAM,WAAN;AA+QA,SAAS,iBAAkC;AAChD,SAAO,cAAc,aAAa,QAAQ;AAC5C;"}
1
+ {"version":3,"file":"button.js","sources":["../../../src/components/button/button.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\n\n/**\n * <fx-button> — Componente de ação (piloto).\n *\n * Form-Associated Custom Element: `type=\"submit\"` submete o <form> em que\n * vive e `type=\"reset\"` o limpa (o <button> interno está no Shadow DOM e não\n * se associa ao form sozinho — a ação é acionada via ElementInternals).\n * `<form disabled>`/`<fieldset disabled>` desabilitam o botão.\n *\n * Atributos: variant, size, disabled, loading, type, full (largura 100%\n * acompanhando o elemento pai), icon (nome do glifo Fenix Icons ou\n * emoji/texto livre) e icon-pos (left|right, padrão left).\n * Slots: default (rótulo), `icon` (ícone customizado — vence o atributo).\n * Evento: `click` nativo atravessa o Shadow DOM (composed) — ouça no host.\n */\nexport class FxButton extends FxElement {\n /** Participa de <form> como botão (submit/reset via ElementInternals). */\n static formAssociated = true;\n /** focus() e clique em <label for> atingem o botão interno do Shadow DOM. */\n static override shadowOptions: ShadowRootInit = { mode: 'open', delegatesFocus: true };\n static override styles = css`\n ${FENIX_ICON_BASE_CSS}\n :host {\n display: inline-block;\n vertical-align: middle;\n }\n /* Full width: o host inline-block é shrink-to-fit, então precisa virar\n block para o 100% do .btn interno ter contra o que esticar. */\n :host([full]) {\n display: block;\n width: 100%;\n max-width: 100%;\n }\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n gap: var(--fx-space-sm);\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n font-weight: var(--fx-font-weight);\n line-height: var(--fx-font-line-height);\n /* Padding vertical limitado ao orçamento do token --fx-size-*: a altura\n fica EXATAMENTE em sm 32 / md 40 / lg 48 (mesma medida dos campos). */\n padding: var(--fx-space-sm) var(--fx-space-xl);\n border-radius: var(--fx-radius-md);\n border: 1px solid transparent;\n color: #fff;\n background: var(--fx-color-primary);\n cursor: pointer;\n user-select: none;\n text-decoration: none;\n transition:\n background-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n transform var(--fx-motion-duration-fast) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .btn:focus-visible {\n outline: none;\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .btn:hover { filter: brightness(0.88); }\n .btn:active { transform: translateY(1px); }\n\n /* Tamanhos — alturas vindas dos tokens (personalizáveis por preset) */\n .btn { min-height: var(--fx-size-md); }\n :host([size='sm']) .btn { padding: var(--fx-space-xs) var(--fx-space-lg); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }\n :host([size='lg']) .btn { padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }\n\n /* Full width */\n :host([full]) .btn { width: 100%; }\n\n /* Variantes */\n :host([variant='secondary']) .btn { background: var(--fx-color-secondary); }\n :host([variant='success']) .btn { background: var(--fx-color-success); }\n :host([variant='warning']) .btn { background: var(--fx-color-warning); }\n :host([variant='danger']) .btn { background: var(--fx-color-danger); }\n :host([variant='info']) .btn { background: var(--fx-color-info); }\n :host([variant='ghost']) .btn { background: transparent; color: var(--fx-text-default); }\n :host([variant='ghost']) .btn:hover { background: var(--fx-surface-surface-hover); filter: none; }\n :host([variant='outline']) .btn { background: transparent; color: var(--fx-color-primary); border-color: var(--fx-color-primary); }\n :host([variant='outline']) .btn:hover { background: var(--fx-surface-surface-hover); filter: none; }\n\n /* Ícone e spinner */\n .btn__icon { display: inline-flex; align-items: center; font-size: var(--fx-button-icon-size, calc(var(--fx-font-size) + 6px)); line-height: 1; }\n .btn__icon[hidden] { display: none; }\n .btn__label { display: inline-flex; align-items: center; justify-content: center; }\n /* Icon-only: a label some (o gap do flex some junto) e o botão vira\n quadrado com padding simétrico — ícone perfeitamente centralizado. */\n .btn--icon-only { padding-left: var(--fx-space-md); padding-right: var(--fx-space-md); }\n .btn--icon-only .btn__label { display: none; }\n .btn[hidden] { display: none; }\n .btn__spinner {\n width: 1em;\n height: 1em;\n border: 2px solid currentColor;\n border-top-color: transparent;\n border-radius: var(--fx-radius-full);\n animation: fx-spin var(--fx-motion-duration-normal) linear infinite;\n }\n @keyframes fx-spin { to { transform: rotate(360deg); } }\n\n /* Estados */\n :host([disabled]) .btn,\n :host([loading]) .btn {\n opacity: 0.55;\n cursor: not-allowed;\n pointer-events: none;\n }\n\n /* Efeito ripple (). Desative com effect.ripple: '0' no preset. */\n .btn {\n position: relative;\n overflow: hidden;\n }\n .btn__ripple {\n position: absolute;\n border-radius: var(--fx-radius-full);\n background: currentColor;\n opacity: 0.28;\n transform: scale(0);\n animation: fx-ripple 600ms var(--fx-motion-easing) forwards;\n pointer-events: none;\n }\n @keyframes fx-ripple {\n to {\n transform: scale(2.6);\n opacity: 0;\n }\n }\n @media (prefers-reduced-motion: reduce) {\n .btn__ripple { display: none; }\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'size', 'disabled', 'loading', 'type', 'full', 'icon', 'icon-pos'];\n }\n\n protected readonly internals: ElementInternals | null;\n\n constructor() {\n super();\n // Ambientes sem ElementInternals (ex.: jsdom nos testes) funcionam sem\n // a integração nativa de form — o getter `form` cai no closest('form').\n this.internals =\n typeof this.attachInternals === 'function' ? this.attachInternals() : null;\n }\n\n /** Form ao qual o botão está associado (null se fora de um form). */\n get form(): HTMLFormElement | null {\n return (\n this.internals?.form ?? (this.closest('form') as HTMLFormElement | null)\n );\n }\n\n /** <form disabled>/<fieldset disabled>: espelha no atributo observado. */\n formDisabledCallback(disabled: boolean): void {\n if (disabled) this.setAttribute('disabled', '');\n else this.removeAttribute('disabled');\n }\n\n /** Tamanho. 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 disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n get loading(): boolean {\n return this.hasAttr('loading');\n }\n set loading(value: boolean) {\n this.toggleAttr('loading', Boolean(value));\n }\n\n get full(): boolean {\n return this.hasAttr('full');\n }\n set full(value: boolean) {\n this.toggleAttr('full', Boolean(value));\n }\n\n protected override render(): void {\n const loading = this.hasAttr('loading');\n const disabled = this.hasAttr('disabled') || loading;\n const btnType = this.getAttr('type', 'button');\n const icon = this.getAttr('icon');\n const iconRight = this.getAttr('icon-pos', 'left') === 'right';\n // Slot `icon` vence o atributo `icon`; sem nenhum dos dois, sem ícone.\n const slottedIcon = this.querySelector<HTMLElement>('[slot=\"icon\"]');\n const iconContent = slottedIcon\n ? '<slot name=\"icon\"></slot>'\n : (icon ? fenixIconHtml(icon) : '');\n\n this.setTemplate(`\n <button class=\"btn\" part=\"button\" type=\"${btnType}\">\n ${!iconRight ? `<span class=\"btn__icon\" part=\"icon\">${iconContent}</span>` : ''}\n <span class=\"btn__label\" part=\"label\"><slot></slot></span>\n ${iconRight ? `<span class=\"btn__icon\" part=\"icon\">${iconContent}</span>` : ''}\n ${loading ? '<span class=\"btn__spinner\" aria-hidden=\"true\"></span>' : ''}\n </button>\n `);\n\n const btn = this.root.querySelector<HTMLButtonElement>('.btn');\n if (!btn) return;\n\n // Esconde o container do ícone quando vazio, evitando espaço fantasma\n // do `gap` que descentraliza o rótulo.\n const hasIcon = iconContent.trim() !== '';\n for (const wrap of this.root.querySelectorAll<HTMLElement>('.btn__icon')) {\n wrap.toggleAttribute('hidden', !hasIcon);\n }\n\n // Icon-only: sem texto no slot default, o botão vira quadrado\n // (padding simétrico) em vez de retangular largo.\n const hasLabel = [...(this.childNodes ?? [])].some((n) =>\n n.nodeType === Node.ELEMENT_NODE\n ? (n as Element).getAttribute?.('slot') !== 'icon'\n : (n.textContent ?? '').trim() !== '',\n );\n btn.classList.toggle('btn--icon-only', !hasLabel && hasIcon);\n if (disabled) {\n btn.setAttribute('disabled', '');\n btn.setAttribute('aria-disabled', 'true');\n } else {\n btn.removeAttribute('disabled');\n btn.removeAttribute('aria-disabled');\n }\n if (loading) btn.setAttribute('aria-busy', 'true');\n else btn.removeAttribute('aria-busy');\n\n // type=\"submit\"/\"reset\": o <button> interno vive no Shadow DOM e NÃO se\n // associa ao <form> do light DOM (a fronteira do shadow corta a associação\n // por árvore) — a ação do botão é acionada manualmente via requestSubmit/\n // reset do form associado. Ouvintes de submit seguem recebendo o evento.\n if (btnType === 'submit' || btnType === 'reset') {\n btn.addEventListener('click', () => {\n if (this.hasAttr('disabled') || this.hasAttr('loading')) return;\n if (btnType === 'submit' && this.form) this.form.requestSubmit();\n else if (btnType === 'reset' && this.form) this.form.reset();\n });\n }\n }\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n this.addEventListener('pointerdown', this.spawnRipple);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.removeEventListener('pointerdown', this.spawnRipple);\n }\n\n /**\n * Efeito ripple : cria um círculo que expande do ponto\n * de clique e desaparece. Respeita o token `effect.ripple` ('0' desativa)\n * e `prefers-reduced-motion` (via CSS).\n */\n private spawnRipple = (event: PointerEvent): void => {\n if (this.hasAttr('disabled') || this.hasAttr('loading')) return;\n const enabled = getComputedStyle(this)\n .getPropertyValue('--fx-effect-ripple')\n .trim();\n if (enabled === '0' || enabled === 'false') return;\n\n const btn = this.root.querySelector<HTMLElement>('.btn');\n if (!btn) return;\n\n const rect = btn.getBoundingClientRect();\n const size = Math.max(rect.width, rect.height);\n const ripple = document.createElement('span');\n ripple.className = 'btn__ripple';\n ripple.style.width = ripple.style.height = `${size}px`;\n ripple.style.left = `${event.clientX - rect.left - size / 2}px`;\n ripple.style.top = `${event.clientY - rect.top - size / 2}px`;\n btn.appendChild(ripple);\n ripple.addEventListener('animationend', () => ripple.remove());\n };\n}\n\nexport function defineFxButton(): typeof FxButton {\n return defineElement('fx-button', FxButton);\n}"],"names":[],"mappings":";;;;AAmBO,MAAM,YAAN,MAAM,kBAAiB,UAAU;AAAA,EAgItC,cAAc;AACZ,UAAA;AA+HF,SAAQ,cAAc,CAAC,UAA8B;AACnD,UAAI,KAAK,QAAQ,UAAU,KAAK,KAAK,QAAQ,SAAS,EAAG;AACzD,YAAM,UAAU,iBAAiB,IAAI,EAClC,iBAAiB,oBAAoB,EACrC,KAAA;AACH,UAAI,YAAY,OAAO,YAAY,QAAS;AAE5C,YAAM,MAAM,KAAK,KAAK,cAA2B,MAAM;AACvD,UAAI,CAAC,IAAK;AAEV,YAAM,OAAO,IAAI,sBAAA;AACjB,YAAM,OAAO,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM;AAC7C,YAAM,SAAS,SAAS,cAAc,MAAM;AAC5C,aAAO,YAAY;AACnB,aAAO,MAAM,QAAQ,OAAO,MAAM,SAAS,GAAG,IAAI;AAClD,aAAO,MAAM,OAAO,GAAG,MAAM,UAAU,KAAK,OAAO,OAAO,CAAC;AAC3D,aAAO,MAAM,MAAM,GAAG,MAAM,UAAU,KAAK,MAAM,OAAO,CAAC;AACzD,UAAI,YAAY,MAAM;AACtB,aAAO,iBAAiB,gBAAgB,MAAM,OAAO,QAAQ;AAAA,IAC/D;AA/IE,SAAK,YACH,OAAO,KAAK,oBAAoB,aAAa,KAAK,oBAAoB;AAAA,EAC1E;AAAA,EAZA,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,QAAQ,YAAY,WAAW,QAAQ,QAAQ,QAAQ,UAAU;AAAA,EACtF;AAAA;AAAA,EAaA,IAAI,OAA+B;AACjC,WACE,KAAK,WAAW,QAAS,KAAK,QAAQ,MAAM;AAAA,EAEhD;AAAA;AAAA,EAGA,qBAAqB,UAAyB;AAC5C,QAAI,SAAU,MAAK,aAAa,YAAY,EAAE;AAAA,QACzC,MAAK,gBAAgB,UAAU;AAAA,EACtC;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,UAAM,IAAI,KAAK,QAAQ,QAAQ,IAAI;AACnC,WAAO,MAAM,QAAQ,MAAM,OAAO,IAAI;AAAA,EACxC;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,SAAS,OAAgB;AAC3B,SAAK,WAAW,YAAY,QAAQ,KAAK,CAAC;AAAA,EAC5C;AAAA,EAEA,IAAI,UAAmB;AACrB,WAAO,KAAK,QAAQ,SAAS;AAAA,EAC/B;AAAA,EACA,IAAI,QAAQ,OAAgB;AAC1B,SAAK,WAAW,WAAW,QAAQ,KAAK,CAAC;AAAA,EAC3C;AAAA,EAEA,IAAI,OAAgB;AAClB,WAAO,KAAK,QAAQ,MAAM;AAAA,EAC5B;AAAA,EACA,IAAI,KAAK,OAAgB;AACvB,SAAK,WAAW,QAAQ,QAAQ,KAAK,CAAC;AAAA,EACxC;AAAA,EAEmB,SAAe;AAChC,UAAM,UAAU,KAAK,QAAQ,SAAS;AACtC,UAAM,WAAW,KAAK,QAAQ,UAAU,KAAK;AAC7C,UAAM,UAAU,KAAK,QAAQ,QAAQ,QAAQ;AAC7C,UAAM,OAAO,KAAK,QAAQ,MAAM;AAChC,UAAM,YAAY,KAAK,QAAQ,YAAY,MAAM,MAAM;AAEvD,UAAM,cAAc,KAAK,cAA2B,eAAe;AACnE,UAAM,cAAc,cAChB,8BACC,OAAO,cAAc,IAAI,IAAI;AAElC,SAAK,YAAY;AAAA,gDAC2B,OAAO;AAAA,UAC7C,CAAC,YAAY,uCAAuC,WAAW,YAAY,EAAE;AAAA;AAAA,UAE7E,YAAY,uCAAuC,WAAW,YAAY,EAAE;AAAA,UAC5E,UAAU,0DAA0D,EAAE;AAAA;AAAA,KAE3E;AAED,UAAM,MAAM,KAAK,KAAK,cAAiC,MAAM;AAC7D,QAAI,CAAC,IAAK;AAIV,UAAM,UAAU,YAAY,KAAA,MAAW;AACvC,eAAW,QAAQ,KAAK,KAAK,iBAA8B,YAAY,GAAG;AACxE,WAAK,gBAAgB,UAAU,CAAC,OAAO;AAAA,IACzC;AAIA,UAAM,WAAW,CAAC,GAAI,KAAK,cAAc,CAAA,CAAG,EAAE;AAAA,MAAK,CAAC,MAClD,EAAE,aAAa,KAAK,eACf,EAAc,eAAe,MAAM,MAAM,UACzC,EAAE,eAAe,IAAI,WAAW;AAAA,IAAA;AAEvC,QAAI,UAAU,OAAO,kBAAkB,CAAC,YAAY,OAAO;AAC3D,QAAI,UAAU;AACZ,UAAI,aAAa,YAAY,EAAE;AAC/B,UAAI,aAAa,iBAAiB,MAAM;AAAA,IAC1C,OAAO;AACL,UAAI,gBAAgB,UAAU;AAC9B,UAAI,gBAAgB,eAAe;AAAA,IACrC;AACA,QAAI,QAAS,KAAI,aAAa,aAAa,MAAM;AAAA,QAC5C,KAAI,gBAAgB,WAAW;AAMpC,QAAI,YAAY,YAAY,YAAY,SAAS;AAC/C,UAAI,iBAAiB,SAAS,MAAM;AAClC,YAAI,KAAK,QAAQ,UAAU,KAAK,KAAK,QAAQ,SAAS,EAAG;AACzD,YAAI,YAAY,YAAY,KAAK,KAAM,MAAK,KAAK,cAAA;AAAA,iBACxC,YAAY,WAAW,KAAK,KAAM,MAAK,KAAK,MAAA;AAAA,MACvD,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEmB,oBAA0B;AAC3C,UAAM,kBAAA;AACN,SAAK,iBAAiB,eAAe,KAAK,WAAW;AAAA,EACvD;AAAA,EAEmB,uBAA6B;AAC9C,UAAM,qBAAA;AACN,SAAK,oBAAoB,eAAe,KAAK,WAAW;AAAA,EAC1D;AA2BF;AAlRE,UAAO,iBAAiB;AAExB,UAAgB,gBAAgC,EAAE,MAAM,QAAQ,gBAAgB,KAAA;AAChF,UAAgB,SAAS;AAAA,MACrB,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AANlB,IAAM,WAAN;AAsRA,SAAS,iBAAkC;AAChD,SAAO,cAAc,aAAa,QAAQ;AAC5C;"}
@@ -14,6 +14,8 @@ import { FxElement } from '../../core/base';
14
14
  export declare class FxCard extends FxElement {
15
15
  static styles: string;
16
16
  static get observedAttributes(): string[];
17
+ get padded(): boolean;
18
+ set padded(value: boolean);
17
19
  protected render(): void;
18
20
  /**
19
21
  * Esconde `header`/`footer` quando o respectivo slot não recebeu nenhum nó.
@@ -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;;;;;;;;;;;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"}
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;IAED,IAAI,MAAM,IAAI,OAAO,CAAmC;IACxD,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,EAAgD;cAEtD,MAAM,IAAI,IAAI;IAmBjC;;;;OAIG;IACH,OAAO,CAAC,UAAU;CAiBnB;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
@@ -6,6 +6,12 @@ const _FxCard = class _FxCard extends FxElement {
6
6
  static get observedAttributes() {
7
7
  return ["variant", "radius", "size", "padded", "heading"];
8
8
  }
9
+ get padded() {
10
+ return this.hasAttr("padded");
11
+ }
12
+ set padded(value) {
13
+ this.toggleAttr("padded", Boolean(value));
14
+ }
9
15
  render() {
10
16
  const heading = this.getAttr("heading");
11
17
  this.setTemplate(`
@@ -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 *\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;"}
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 get padded(): boolean { return this.hasAttr('padded'); }\n set padded(value: boolean) { this.toggleAttr('padded', Boolean(value)); }\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,EAEA,IAAI,SAAkB;AAAE,WAAO,KAAK,QAAQ,QAAQ;AAAA,EAAG;AAAA,EACvD,IAAI,OAAO,OAAgB;AAAE,SAAK,WAAW,UAAU,QAAQ,KAAK,CAAC;AAAA,EAAG;AAAA,EAErD,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;AAxGE,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;AA2GA,SAAS,eAA8B;AAC5C,SAAO,cAAc,WAAW,MAAM;AACxC;"}
@@ -9,6 +9,10 @@ import { FxFormElement } from '../../core/form-element';
9
9
  * Atributos: checked, indeterminate, disabled, value, name, size (sm|md|lg).
10
10
  * Eventos: `change` e `checkedChange` (composed, detail: { checked, value }) —
11
11
  * `checkedChange` é o two-way binding do Angular [(checked)].
12
+ *
13
+ * Vue: o `v-model` padrão de elemento customizado usa `value` + `input` (não
14
+ * `checked`). Para checkbox/switch use `:checked="estado"` + `@change` (ou
15
+ * `@checkedChange`) — o componente reflete o estado no atributo `checked`.
12
16
  */
13
17
  export declare class FxCheckbox extends FxFormElement {
14
18
  static styles: string;
@@ -23,7 +27,7 @@ export declare class FxCheckbox extends FxFormElement {
23
27
  protected getFormValue(): string | FormData | null;
24
28
  /** getter/setter `value` → espelho do atributo `value` (compat com v-model / [(value)]). */
25
29
  get value(): string;
26
- set value(v: string);
30
+ set value(v: string | null | undefined);
27
31
  /** Tamanho do controle. Padrão: `'md'`. */
28
32
  get size(): string;
29
33
  set size(value: string);
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAIxD;;;;;;;;;;GAUG;AACH,qBAAa,UAAW,SAAQ,aAAa;IAC3C,OAAgB,MAAM,SA6DpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,aAAa,IAAI,OAAO,CAE3B;IACD,IAAI,aAAa,CAAC,KAAK,EAAE,OAAO,EAE/B;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,kFAAkF;cAC/D,YAAY,IAAI,MAAM,GAAG,QAAQ,GAAG,IAAI;IAI3D,4FAA4F;IAC5F,IAAI,KAAK,IAAI,MAAM,CAAkC;IACrD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,EAAoC;IAEvD,2CAA2C;IAC3C,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;cAEkB,MAAM,IAAI,IAAI;IAgCjC,4DAA4D;IAC5D,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,uBAAuB,CAAS;cAErB,iBAAiB,IAAI,IAAI;IAQ5C,+FAA+F;IACtF,iBAAiB,IAAI,IAAI;IAMlC,OAAO,CAAC,MAAM;CAoBf;AAED,wBAAgB,gBAAgB,IAAI,OAAO,UAAU,CAEpD"}
1
+ {"version":3,"file":"checkbox.d.ts","sourceRoot":"","sources":["../../../src/components/checkbox/checkbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAIxD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,UAAW,SAAQ,aAAa;IAC3C,OAAgB,MAAM,SA6DpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAEzB;IAED,IAAI,aAAa,IAAI,OAAO,CAE3B;IACD,IAAI,aAAa,CAAC,KAAK,EAAE,OAAO,EAE/B;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;IAED,kFAAkF;cAC/D,YAAY,IAAI,MAAM,GAAG,QAAQ,GAAG,IAAI;IAI3D,4FAA4F;IAC5F,IAAI,KAAK,IAAI,MAAM,CAAkC;IACrD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAGrC;IAED,2CAA2C;IAC3C,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;cAEkB,MAAM,IAAI,IAAI;IAgCjC,4DAA4D;IAC5D,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,uBAAuB,CAAS;cAErB,iBAAiB,IAAI,IAAI;IAQ5C,+FAA+F;IACtF,iBAAiB,IAAI,IAAI;IAMlC,OAAO,CAAC,MAAM;CAoBf;AAED,wBAAgB,gBAAgB,IAAI,OAAO,UAAU,CAEpD"}
@@ -8,7 +8,7 @@ const _FxCheckbox = class _FxCheckbox extends FxFormElement {
8
8
  this._initialCheckedCaptured = false;
9
9
  }
10
10
  static get observedAttributes() {
11
- return ["checked", "disabled", "indeterminate"];
11
+ return ["checked", "disabled", "indeterminate", "size"];
12
12
  }
13
13
  get checked() {
14
14
  return this.hasAttr("checked");
@@ -37,7 +37,8 @@ const _FxCheckbox = class _FxCheckbox extends FxFormElement {
37
37
  return this.getAttr("value");
38
38
  }
39
39
  set value(v) {
40
- this.setAttribute("value", v);
40
+ this.setAttribute("value", v == null ? "" : String(v));
41
+ this.updateFormValue();
41
42
  }
42
43
  /** Tamanho do controle. Padrão: `'md'`. */
43
44
  get size() {
@@ -49,9 +50,7 @@ const _FxCheckbox = class _FxCheckbox extends FxFormElement {
49
50
  }
50
51
  render() {
51
52
  const px = this.size === "sm" ? "14px" : this.size === "lg" ? "22px" : "18px";
52
- if (!this.style.getPropertyValue("--fx-size-checkbox")) {
53
- this.style.setProperty("--fx-size-checkbox", px);
54
- }
53
+ this.style.setProperty("--_fx-size-checkbox", px);
55
54
  this.setTemplate(`
56
55
  <span class="box" part="box" role="checkbox" tabindex="0"
57
56
  aria-checked="${this.indeterminate ? "mixed" : this.checked}"
@@ -123,8 +122,8 @@ _FxCheckbox.styles = css`
123
122
  -webkit-tap-highlight-color: transparent;
124
123
  }
125
124
  .control {
126
- width: var(--fx-size-checkbox, 18px);
127
- height: var(--fx-size-checkbox, 18px);
125
+ width: var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px));
126
+ height: var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px));
128
127
  flex-shrink: 0;
129
128
  display: inline-flex;
130
129
  align-items: center;
@@ -133,7 +132,7 @@ _FxCheckbox.styles = css`
133
132
  border-radius: calc(var(--fx-radius-sm) + 2px);
134
133
  background: var(--fx-surface-background);
135
134
  color: #fff;
136
- font-size: calc(var(--fx-size-checkbox, 18px) - 6px);
135
+ font-size: calc(var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px)) - 6px);
137
136
  line-height: 1;
138
137
  transition:
139
138
  background-color var(--fx-motion-duration-fast) var(--fx-motion-easing),
@@ -1 +1 @@
1
- {"version":3,"file":"checkbox.js","sources":["../../../src/components/checkbox/checkbox.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\n\n/**\n * <fx-checkbox> — Caixa de seleção com suporte a estado indeterminado.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`) com `value` (ou 'on') quando marcado — desmarcado\n * não envia nada, igual ao checkbox nativo. Recebe reset/disable do form.\n *\n * Atributos: checked, indeterminate, disabled, value, name, size (sm|md|lg).\n * Eventos: `change` e `checkedChange` (composed, detail: { checked, value }) —\n * `checkedChange` é o two-way binding do Angular [(checked)].\n */\nexport class FxCheckbox extends FxFormElement {\n static override styles = css`\n :host {\n display: inline-flex;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n .box {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-sm);\n cursor: pointer;\n user-select: none;\n color: var(--fx-text-default);\n -webkit-tap-highlight-color: transparent;\n }\n .control {\n width: var(--fx-size-checkbox, 18px);\n height: var(--fx-size-checkbox, 18px);\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--fx-border-hover);\n border-radius: calc(var(--fx-radius-sm) + 2px);\n background: var(--fx-surface-background);\n color: #fff;\n font-size: calc(var(--fx-size-checkbox, 18px) - 6px);\n line-height: 1;\n transition:\n background-color var(--fx-motion-duration-fast) var(--fx-motion-easing),\n border-color var(--fx-motion-duration-fast) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .box:hover .control { border-color: var(--fx-color-primary); }\n .box:focus-visible .control {\n outline: none;\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([checked]) .control {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n }\n :host([indeterminate]) .control {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n }\n /* Validação */\n :host([error]) .control,\n :host([invalid]) .control { border-color: var(--fx-color-danger, #dc2626); }\n :host([success]) .control,\n :host([valid]) .control { border-color: var(--fx-color-success, #16a34a); }\n .mark { visibility: hidden; }\n :host([checked]) .mark,\n :host([indeterminate]) .mark { visibility: visible; }\n .label { display: inline-flex; align-items: center; }\n :host([disabled]) .box {\n opacity: 0.55;\n cursor: not-allowed;\n pointer-events: none;\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['checked', 'disabled', 'indeterminate'];\n }\n\n get checked(): boolean {\n return this.hasAttr('checked');\n }\n set checked(value: boolean) {\n this.toggleAttr('checked', Boolean(value));\n }\n\n get indeterminate(): boolean {\n return this.hasAttr('indeterminate');\n }\n set indeterminate(value: boolean) {\n this.toggleAttr('indeterminate', Boolean(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 /** No FormData: `value` (ou 'on') quando marcado; null (não envia) quando não. */\n protected override getFormValue(): string | FormData | null {\n return this.checked ? (this.getAttr('value') || 'on') : null;\n }\n\n /** getter/setter `value` → espelho do atributo `value` (compat com v-model / [(value)]). */\n get value(): string { return this.getAttr('value'); }\n set value(v: string) { this.setAttribute('value', v); }\n\n /** Tamanho do controle. 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 protected override render(): void {\n // Escala por tamanho via token local.\n const px = this.size === 'sm' ? '14px' : this.size === 'lg' ? '22px' : '18px';\n if (!this.style.getPropertyValue('--fx-size-checkbox')) {\n this.style.setProperty('--fx-size-checkbox', px);\n }\n\n this.setTemplate(`\n <span class=\"box\" part=\"box\" role=\"checkbox\" tabindex=\"0\"\n aria-checked=\"${this.indeterminate ? 'mixed' : this.checked}\"\n aria-disabled=\"${this.disabled}\">\n <span class=\"control\" part=\"control\">\n <span class=\"mark\">${this.indeterminate ? '–' : '✓'}</span>\n </span>\n <span class=\"label\" part=\"label\"><slot></slot></span>\n </span>\n `);\n\n const box = this.root.querySelector<HTMLElement>('.box');\n if (!box) return;\n box.addEventListener('click', () => this.toggle());\n box.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === ' ' || (e as KeyboardEvent).key === 'Enter') {\n e.preventDefault();\n this.toggle();\n }\n });\n\n // Publica name/value no form (ElementInternals) a cada render.\n this.updateFormValue();\n }\n\n /** Estado marcado inicial (para form.reset() restaurar). */\n private _initialChecked = false;\n private _initialCheckedCaptured = false;\n\n protected override connectedCallback(): void {\n if (!this._initialCheckedCaptured) {\n this._initialCheckedCaptured = true;\n this._initialChecked = this.checked;\n }\n super.connectedCallback();\n }\n\n /** form.reset(): restaura o estado marcado inicial (checked/indeterminate não enviam nada). */\n override formResetCallback(): void {\n this.checked = this._initialChecked;\n this.indeterminate = false;\n this.updateFormValue();\n }\n\n private toggle(): void {\n if (this.disabled) return;\n this.indeterminate = false; // interagir resolve o estado misto\n this.checked = !this.checked;\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { checked: this.checked, value: this.getAttr('value') },\n }),\n );\n // Two-way binding do Angular: [(checked)] = [checked] + (checkedChange).\n this.dispatchEvent(\n new CustomEvent('checkedChange', {\n bubbles: true,\n composed: true,\n detail: { checked: this.checked, value: this.getAttr('value') },\n }),\n );\n }\n}\n\nexport function defineFxCheckbox(): typeof FxCheckbox {\n return defineElement('fx-checkbox', FxCheckbox);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checkbox.js","sources":["../../../src/components/checkbox/checkbox.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\n\n/**\n * <fx-checkbox> — Caixa de seleção com suporte a estado indeterminado.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`) com `value` (ou 'on') quando marcado — desmarcado\n * não envia nada, igual ao checkbox nativo. Recebe reset/disable do form.\n *\n * Atributos: checked, indeterminate, disabled, value, name, size (sm|md|lg).\n * Eventos: `change` e `checkedChange` (composed, detail: { checked, value }) —\n * `checkedChange` é o two-way binding do Angular [(checked)].\n *\n * Vue: o `v-model` padrão de elemento customizado usa `value` + `input` (não\n * `checked`). Para checkbox/switch use `:checked=\"estado\"` + `@change` (ou\n * `@checkedChange`) — o componente reflete o estado no atributo `checked`.\n */\nexport class FxCheckbox extends FxFormElement {\n static override styles = css`\n :host {\n display: inline-flex;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n }\n .box {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-sm);\n cursor: pointer;\n user-select: none;\n color: var(--fx-text-default);\n -webkit-tap-highlight-color: transparent;\n }\n .control {\n width: var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px));\n height: var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px));\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--fx-border-hover);\n border-radius: calc(var(--fx-radius-sm) + 2px);\n background: var(--fx-surface-background);\n color: #fff;\n font-size: calc(var(--fx-size-checkbox, var(--_fx-size-checkbox, 18px)) - 6px);\n line-height: 1;\n transition:\n background-color var(--fx-motion-duration-fast) var(--fx-motion-easing),\n border-color var(--fx-motion-duration-fast) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .box:hover .control { border-color: var(--fx-color-primary); }\n .box:focus-visible .control {\n outline: none;\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([checked]) .control {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n }\n :host([indeterminate]) .control {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n }\n /* Validação */\n :host([error]) .control,\n :host([invalid]) .control { border-color: var(--fx-color-danger, #dc2626); }\n :host([success]) .control,\n :host([valid]) .control { border-color: var(--fx-color-success, #16a34a); }\n .mark { visibility: hidden; }\n :host([checked]) .mark,\n :host([indeterminate]) .mark { visibility: visible; }\n .label { display: inline-flex; align-items: center; }\n :host([disabled]) .box {\n opacity: 0.55;\n cursor: not-allowed;\n pointer-events: none;\n }\n `;\n\n static override get observedAttributes(): string[] {\n return ['checked', 'disabled', 'indeterminate', 'size'];\n }\n\n get checked(): boolean {\n return this.hasAttr('checked');\n }\n set checked(value: boolean) {\n this.toggleAttr('checked', Boolean(value));\n }\n\n get indeterminate(): boolean {\n return this.hasAttr('indeterminate');\n }\n set indeterminate(value: boolean) {\n this.toggleAttr('indeterminate', Boolean(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 /** No FormData: `value` (ou 'on') quando marcado; null (não envia) quando não. */\n protected override getFormValue(): string | FormData | null {\n return this.checked ? (this.getAttr('value') || 'on') : null;\n }\n\n /** getter/setter `value` → espelho do atributo `value` (compat com v-model / [(value)]). */\n get value(): string { return this.getAttr('value'); }\n set value(v: string | null | undefined) {\n this.setAttribute('value', v == null ? '' : String(v));\n this.updateFormValue();\n }\n\n /** Tamanho do controle. 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 protected override render(): void {\n // Escala por tamanho via token interno. Atualiza SEMPRE para reagir ao\n // `:size`; um `--fx-size-checkbox` definido pelo consumidor continua com\n // precedência (é consultado antes do token interno).\n const px = this.size === 'sm' ? '14px' : this.size === 'lg' ? '22px' : '18px';\n this.style.setProperty('--_fx-size-checkbox', px);\n\n this.setTemplate(`\n <span class=\"box\" part=\"box\" role=\"checkbox\" tabindex=\"0\"\n aria-checked=\"${this.indeterminate ? 'mixed' : this.checked}\"\n aria-disabled=\"${this.disabled}\">\n <span class=\"control\" part=\"control\">\n <span class=\"mark\">${this.indeterminate ? '–' : '✓'}</span>\n </span>\n <span class=\"label\" part=\"label\"><slot></slot></span>\n </span>\n `);\n\n const box = this.root.querySelector<HTMLElement>('.box');\n if (!box) return;\n box.addEventListener('click', () => this.toggle());\n box.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === ' ' || (e as KeyboardEvent).key === 'Enter') {\n e.preventDefault();\n this.toggle();\n }\n });\n\n // Publica name/value no form (ElementInternals) a cada render.\n this.updateFormValue();\n }\n\n /** Estado marcado inicial (para form.reset() restaurar). */\n private _initialChecked = false;\n private _initialCheckedCaptured = false;\n\n protected override connectedCallback(): void {\n if (!this._initialCheckedCaptured) {\n this._initialCheckedCaptured = true;\n this._initialChecked = this.checked;\n }\n super.connectedCallback();\n }\n\n /** form.reset(): restaura o estado marcado inicial (checked/indeterminate não enviam nada). */\n override formResetCallback(): void {\n this.checked = this._initialChecked;\n this.indeterminate = false;\n this.updateFormValue();\n }\n\n private toggle(): void {\n if (this.disabled) return;\n this.indeterminate = false; // interagir resolve o estado misto\n this.checked = !this.checked;\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { checked: this.checked, value: this.getAttr('value') },\n }),\n );\n // Two-way binding do Angular: [(checked)] = [checked] + (checkedChange).\n this.dispatchEvent(\n new CustomEvent('checkedChange', {\n bubbles: true,\n composed: true,\n detail: { checked: this.checked, value: this.getAttr('value') },\n }),\n );\n }\n}\n\nexport function defineFxCheckbox(): typeof FxCheckbox {\n return defineElement('fx-checkbox', FxCheckbox);\n}\n"],"names":[],"mappings":"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"}