@wrrdev/fenix-ui 1.1.5 → 1.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -2
- package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
- package/dist/components/autocomplete/autocomplete.js +16 -7
- package/dist/components/autocomplete/autocomplete.js.map +1 -1
- package/dist/components/button/button.d.ts.map +1 -1
- package/dist/components/button/button.js +7 -5
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/card/card.d.ts +2 -1
- package/dist/components/card/card.d.ts.map +1 -1
- package/dist/components/card/card.js +5 -2
- package/dist/components/card/card.js.map +1 -1
- package/dist/components/datepicker/datepicker.d.ts.map +1 -1
- package/dist/components/datepicker/datepicker.js +11 -6
- package/dist/components/datepicker/datepicker.js.map +1 -1
- package/dist/components/fileupload/fileupload.d.ts +1 -1
- package/dist/components/fileupload/fileupload.d.ts.map +1 -1
- package/dist/components/fileupload/fileupload.js +10 -3
- package/dist/components/fileupload/fileupload.js.map +1 -1
- package/dist/components/icon/icon.d.ts +40 -0
- package/dist/components/icon/icon.d.ts.map +1 -0
- package/dist/components/icon/index.d.ts +8 -0
- package/dist/components/icon/index.d.ts.map +1 -0
- package/dist/components/input/input.d.ts +3 -0
- package/dist/components/input/input.d.ts.map +1 -1
- package/dist/components/input/input.js +24 -8
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/knob/knob.d.ts +1 -1
- package/dist/components/knob/knob.d.ts.map +1 -1
- package/dist/components/knob/knob.js +3 -1
- package/dist/components/knob/knob.js.map +1 -1
- package/dist/components/multiselect/multiselect.d.ts.map +1 -1
- package/dist/components/multiselect/multiselect.js +17 -6
- package/dist/components/multiselect/multiselect.js.map +1 -1
- package/dist/components/orderlist/orderlist.js +2 -2
- package/dist/components/orderlist/orderlist.js.map +1 -1
- package/dist/components/picklist/picklist.js +2 -2
- package/dist/components/picklist/picklist.js.map +1 -1
- package/dist/components/select/select.d.ts.map +1 -1
- package/dist/components/select/select.js +18 -5
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/textarea/textarea.d.ts.map +1 -1
- package/dist/components/textarea/textarea.js +16 -5
- package/dist/components/textarea/textarea.js.map +1 -1
- package/dist/components/toggle-button-group/toggle-button-group.d.ts.map +1 -1
- package/dist/components/toggle-button-group/toggle-button-group.js +3 -1
- package/dist/components/toggle-button-group/toggle-button-group.js.map +1 -1
- package/dist/core/jsx.d.ts +233 -29
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- package/dist/core/presets.js +1 -1
- package/dist/core/presets.js.map +1 -1
- package/dist/core/react.d.ts +20 -0
- package/dist/core/react.d.ts.map +1 -0
- package/dist/core/react.js +5 -0
- package/dist/core/react.js.map +1 -0
- package/dist/core/theme.d.ts.map +1 -1
- package/dist/core/theme.js +12 -0
- package/dist/core/theme.js.map +1 -1
- package/dist/core/tokens.d.ts +4 -0
- package/dist/core/tokens.d.ts.map +1 -1
- package/dist/core/tokens.js +9 -1
- package/dist/core/tokens.js.map +1 -1
- package/dist/core/vue.d.ts +47 -2
- package/dist/core/vue.d.ts.map +1 -1
- package/dist/core/vue.js.map +1 -1
- package/dist/docs/app.d.ts +6 -0
- package/dist/docs/app.d.ts.map +1 -1
- package/dist/docs/componentes/autocomplete.doc.d.ts.map +1 -1
- package/dist/docs/componentes/card.doc.d.ts.map +1 -1
- package/dist/docs/componentes/icon.doc.d.ts +9 -0
- package/dist/docs/componentes/icon.doc.d.ts.map +1 -0
- package/dist/docs/componentes/index.d.ts.map +1 -1
- package/dist/docs/componentes/input.doc.d.ts.map +1 -1
- package/dist/docs/componentes/multiselect.doc.d.ts.map +1 -1
- package/dist/docs/componentes/select.doc.d.ts.map +1 -1
- package/dist/docs/componentes/textarea.doc.d.ts.map +1 -1
- package/dist/fenix-ui.esm.js +2 -2
- package/dist/fenix-ui.esm.js.map +1 -1
- package/dist/fenix-ui.umd.min.js +296 -215
- package/dist/fenix-ui2.esm.js +2 -4321
- package/dist/fenix-ui2.esm.js.map +1 -1
- package/dist/fenix-ui3.esm.js +4338 -10616
- package/dist/fenix-ui3.esm.js.map +1 -1
- package/dist/fenix-ui4.esm.js +10724 -104
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/dist/fenix-ui5.esm.js +111 -0
- package/dist/fenix-ui5.esm.js.map +1 -0
- package/dist/icons/font.d.ts +26 -0
- package/dist/icons/font.d.ts.map +1 -0
- package/dist/icons/font.js +42 -0
- package/dist/icons/font.js.map +1 -0
- package/dist/icons/index.d.ts +3 -1
- package/dist/icons/index.d.ts.map +1 -1
- package/dist/icons/index.js +11 -13
- package/dist/icons/index.js.map +1 -1
- package/dist/icons/name-union.d.ts +11 -0
- package/dist/icons/name-union.d.ts.map +1 -0
- package/dist/icons/names.d.ts +0 -1
- package/dist/icons/names.d.ts.map +1 -1
- package/dist/icons/names.js.map +1 -1
- package/dist/icons/types.d.ts +14 -0
- package/dist/icons/types.d.ts.map +1 -0
- package/dist/plugins/auto-import.d.ts.map +1 -1
- package/dist/plugins/auto-import.js +1 -0
- package/dist/plugins/auto-import.js.map +1 -1
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ import { FenixUI } from '@wrrdev/fenix-ui'; // API de tema/tokens (índice)
|
|
|
18
18
|
FenixUI.theme('dark');
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.
|
|
21
|
+
> 📦 npm: [`@wrrdev/fenix-ui`](https://www.npmjs.com/package/@wrrdev/fenix-ui) — **v1.1.7**, acesso público.
|
|
22
22
|
> Subpaths disponíveis: `./theme`, `./tokens`, `./jsx`, `./vue`, `./button`, `./badge`, `./spinner`, `./select`, `./input`, `./switch`, `./textarea`, `./checkbox`, `./radio`, `./calendar`, `./datepicker`, `./multiselect`, `./table`, `./floatlabel`, `./dialog`, `./toast`, `./tooltip`, `./tabs`, `./progress`, `./skeleton`, `./alert`, `./drawer`, `./dropdown`, `./sidebar`, `./pagination`, `./autocomplete`, `./auto-import` e o curinga `./components/*`.
|
|
23
23
|
|
|
24
24
|
|
|
@@ -65,7 +65,7 @@ Carregue o bundle **UMD** único: ele registra todos os componentes e expõe os
|
|
|
65
65
|
<fx-toast></fx-toast>
|
|
66
66
|
|
|
67
67
|
<!--
|
|
68
|
-
🔗 Links diretos para CDN (versão 1.1.
|
|
68
|
+
🔗 Links diretos para CDN (versão 1.1.7):
|
|
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
|
-->
|
|
@@ -181,6 +181,8 @@ FenixUI.configure({
|
|
|
181
181
|
effect: {
|
|
182
182
|
ripple: '1', // '0' desativa o ripple do fx-button
|
|
183
183
|
'focus-ring': 'none', // campos sem anel de foco/sobra
|
|
184
|
+
// error-ring/success-ring acompanham o focus-ring automaticamente:
|
|
185
|
+
// desligado, a validação error/success mostra só a borda (sem brilho).
|
|
184
186
|
},
|
|
185
187
|
},
|
|
186
188
|
});
|
|
@@ -249,6 +251,59 @@ import '@wrrdev/fenix-ui'; // registra componentes + habilita as tipagens JSX
|
|
|
249
251
|
Se o autocomplete ainda não aparecer, garanta que o `tsconfig` do seu projeto tem
|
|
250
252
|
`"moduleResolution": "bundler"` (ou `"node16"`/`"nodenext"`) para resolver os `exports` do pacote.
|
|
251
253
|
|
|
254
|
+
#### React
|
|
255
|
+
|
|
256
|
+
- **`"jsx": "react-jsx"` (padrão no Create React App, Vite e Next):** importe o subpath `./react`
|
|
257
|
+
**uma única vez** no `main.tsx` — o namespace `React.JSX` não herda a augmentação global de `JSX`:
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
import '@wrrdev/fenix-ui/react'; // tipa fx-* em React.JSX (react-jsx)
|
|
261
|
+
|
|
262
|
+
<fx-button variant="primary" size="lg" loading>Salvar</fx-button>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
- **`"jsx": "react"` clássico / Preact / Vue JSX:** basta `import '@wrrdev/fenix-ui/jsx'`
|
|
266
|
+
(ou importar a lib inteira), que faz a augmentação do namespace global `JSX`.
|
|
267
|
+
|
|
268
|
+
> Requer `@types/react` instalado no projeto para resolver a augmentação do módulo `react`.
|
|
269
|
+
|
|
270
|
+
#### Angular
|
|
271
|
+
|
|
272
|
+
Importe os subpaths dos componentes usados (ou use o plugin Auto Import) e adicione
|
|
273
|
+
`CUSTOM_ELEMENTS_SCHEMA` ao componente — templates Angular não são typecheckados como TSX,
|
|
274
|
+
mas o código TypeScript imperativo é tipado via `HTMLElementTagNameMap`:
|
|
275
|
+
|
|
276
|
+
```ts
|
|
277
|
+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
|
|
278
|
+
import '@wrrdev/fenix-ui/stepper';
|
|
279
|
+
import '@wrrdev/fenix-ui/button';
|
|
280
|
+
|
|
281
|
+
@Component({
|
|
282
|
+
selector: 'app-demo',
|
|
283
|
+
template: `
|
|
284
|
+
<fx-stepper active="0" show-numbers>
|
|
285
|
+
<div slot="step-0" step-title="Dados">Conteúdo do passo.</div>
|
|
286
|
+
</fx-stepper>
|
|
287
|
+
`,
|
|
288
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA], // permite tags fx-* no template
|
|
289
|
+
})
|
|
290
|
+
export class DemoComponent {}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### TypeScript puro / HTML / JSF
|
|
294
|
+
|
|
295
|
+
Importar a lib (ou qualquer subpath) habilita a tipagem imperativa — `document.createElement('fx-*')`,
|
|
296
|
+
classes, propriedades e eventos ficam conhecidos do compilador:
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
import '@wrrdev/fenix-ui/button';
|
|
300
|
+
|
|
301
|
+
const btn = document.createElement('fx-button'); // tipado via HTMLElementTagNameMap
|
|
302
|
+
btn.setAttribute('variant', 'primary');
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Veja a página **Tipagens** da documentação para o guia completo por framework.
|
|
306
|
+
|
|
252
307
|
#### Vue 3 / Nuxt
|
|
253
308
|
|
|
254
309
|
No Vue, o autocomplete dos atributos `fx-*` nos templates é habilitado por um módulo próprio.
|
|
@@ -312,6 +367,23 @@ ative o modo estrito do Volar no `tsconfig.json`:
|
|
|
312
367
|
Todos os componentes com tamanho usam **`md` como padrão** (valores inválidos caem para `md`).
|
|
313
368
|
Escala alinhada ao mercado (Material/Ant): **sm ≈ 32px · md ≈ 40px (padrão) · lg ≈ 48px**.
|
|
314
369
|
|
|
370
|
+
### Largura dos campos de formulário
|
|
371
|
+
|
|
372
|
+
Os campos (fx-input, fx-textarea, fx-autocomplete, fx-select, fx-multiselect)
|
|
373
|
+
têm largura padrão fixa, mas ela **respeita CSS externo**: qualquer largura definida no
|
|
374
|
+
elemento — folha de estilo, classe do framework (ex.: w-full/w-64 do Tailwind) ou
|
|
375
|
+
style inline — vence o default, sem precisar do atributo full:
|
|
376
|
+
|
|
377
|
+
```html
|
|
378
|
+
<fx-input style="width: 320px"></fx-input>
|
|
379
|
+
<fx-input class="w-full"></fx-input>
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
O default global também é trocável por tokens: --fx-input-width, --fx-textarea-width,
|
|
383
|
+
--fx-autocomplete-width, --fx-multiselect-width e --fx-select-width (com sufixos
|
|
384
|
+
-sm/-lg; para o select, --fx-select-min-width). O atributo full continua
|
|
385
|
+
disponível como atalho para largura 100%.
|
|
386
|
+
|
|
315
387
|
Eventos usam o nativo `click` no botão; `input`, `change` e o `change` do select são
|
|
316
388
|
`CustomEvent`s **composed** com `detail: { value }` (atravessam o Shadow DOM).
|
|
317
389
|
|
|
@@ -322,6 +394,9 @@ FenixUI.setTokens({
|
|
|
322
394
|
effect: {
|
|
323
395
|
ripple: '0', // desativa o efeito ripple do fx-button
|
|
324
396
|
'focus-ring': 'none', // campos sem anel de foco/sobra
|
|
397
|
+
// 'error-ring' e 'success-ring' acompanham o focus-ring (desligado ⇒
|
|
398
|
+
// validação error/success apenas com a borda, sem brilho) — ou defina
|
|
399
|
+
// explicitamente um brilho próprio.
|
|
325
400
|
},
|
|
326
401
|
});
|
|
327
402
|
```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/autocomplete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"autocomplete.d.ts","sourceRoot":"","sources":["../../../src/components/autocomplete/autocomplete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,SAAS;IAC3C,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,EAAoC;IAEvD,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;CA6DlC;AAED,wBAAgB,oBAAoB,IAAI,OAAO,cAAc,CAE5D"}
|
|
@@ -103,19 +103,25 @@ _FxAutocomplete.styles = css`
|
|
|
103
103
|
:host {
|
|
104
104
|
position: relative;
|
|
105
105
|
display: inline-block;
|
|
106
|
+
/* Largura padrão no HOST: CSS externo, classes e style inline no elemento
|
|
107
|
+
vencem este default naturalmente (regras do documento > :host). */
|
|
108
|
+
width: var(--fx-autocomplete-width, 260px);
|
|
106
109
|
font-family: var(--fx-font-family);
|
|
107
110
|
font-size: var(--fx-font-size);
|
|
108
111
|
}
|
|
109
112
|
.field {
|
|
110
113
|
font-family: inherit;
|
|
111
114
|
font-size: inherit;
|
|
115
|
+
/* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */
|
|
116
|
+
line-height: var(--fx-font-line-height);
|
|
112
117
|
font-weight: var(--fx-font-weight);
|
|
113
118
|
color: var(--fx-text-default);
|
|
114
119
|
background-color: var(--fx-surface-background);
|
|
115
120
|
border: 1px solid var(--fx-border-default);
|
|
116
121
|
border-radius: var(--fx-radius-md);
|
|
117
|
-
padding: var(--fx-space-
|
|
118
|
-
|
|
122
|
+
padding: var(--fx-space-sm) var(--fx-space-lg);
|
|
123
|
+
/* Acompanha a largura definida no :host. */
|
|
124
|
+
width: 100%;
|
|
119
125
|
min-height: var(--fx-size-md);
|
|
120
126
|
box-sizing: border-box;
|
|
121
127
|
transition:
|
|
@@ -129,21 +135,24 @@ _FxAutocomplete.styles = css`
|
|
|
129
135
|
border-color: var(--fx-color-primary);
|
|
130
136
|
box-shadow: var(--fx-effect-focus-ring, none);
|
|
131
137
|
}
|
|
132
|
-
:host([size='sm'])
|
|
133
|
-
:host([size='
|
|
138
|
+
:host([size='sm']) { width: var(--fx-autocomplete-width-sm, 220px); }
|
|
139
|
+
:host([size='sm']) .field { min-height: var(--fx-size-sm); padding: var(--fx-space-xs) var(--fx-space-md); }
|
|
140
|
+
:host([size='lg']) { width: var(--fx-autocomplete-width-lg, 300px); }
|
|
141
|
+
:host([size='lg']) .field { min-height: var(--fx-size-lg); padding: var(--fx-space-sm) var(--fx-space-lg); }
|
|
134
142
|
/* Full width: o host estica até o pai e o campo interno acompanha. */
|
|
135
143
|
:host([full]) { display: block; width: 100%; }
|
|
136
144
|
:host([full]) .field { width: 100%; }
|
|
137
|
-
/* Validação:
|
|
145
|
+
/* Validação: borda sempre; brilho (anel) via token que acompanha
|
|
146
|
+
effect.focus-ring — desligado ⇒ erro só com a borda vermelha. */
|
|
138
147
|
:host([error]) .field,
|
|
139
148
|
:host([invalid]) .field {
|
|
140
149
|
border-color: var(--fx-color-danger, #dc2626);
|
|
141
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent);
|
|
150
|
+
box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
|
|
142
151
|
}
|
|
143
152
|
:host([success]) .field,
|
|
144
153
|
:host([valid]) .field {
|
|
145
154
|
border-color: var(--fx-color-success, #16a34a);
|
|
146
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent);
|
|
155
|
+
box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
|
|
147
156
|
}
|
|
148
157
|
.list {
|
|
149
158
|
position: absolute;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autocomplete.js","sources":["../../../src/components/autocomplete/autocomplete.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-autocomplete> — Campo de texto com sugestões filtradas.\n *\n * Atributos: value, size, placeholder, source (JSON array de strings),\n * disabled, min-chars (padrão 1).\n * Evento: `select` (composed, detail: { value }).\n */\nexport class FxAutocomplete extends FxElement {\n static override styles = css`\n :host {\n position: relative;\n display: inline-block;\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 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-md) var(--fx-space-lg);\n width: 260px;\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']) .field { width: 220px; min-height: var(--fx-size-sm); padding: var(--fx-space-sm) var(--fx-space-md); }\n :host([size='lg']) .field { width: 300px; min-height: var(--fx-size-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: sobrescrevem a borda/foco via token do preset. */\n :host([error]) .field,\n :host([invalid]) .field {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: 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: 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) { this.setAttribute('value', v); }\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.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 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 field.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n this.toggleAttr('open', false);\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}\n\nexport function defineFxAutocomplete(): typeof FxAutocomplete {\n return defineElement('fx-autocomplete', FxAutocomplete);\n}\n"],"names":[],"mappings":";;;;AAYO,MAAM,kBAAN,MAAM,wBAAuB,UAAU;AAAA,EA+E5C,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,EACpD,IAAI,QAAgB;AAAE,WAAO,KAAK,QAAQ,OAAO;AAAA,EAAG;AAAA,EACpD,IAAI,MAAM,GAAW;AAAE,SAAK,aAAa,SAAS,CAAC;AAAA,EAAG;AAAA,EAEtD,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,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,eAAA;AAAA,IACF,CAAC;AACD,UAAM,iBAAiB,SAAS,MAAM;AAAE,UAAI,MAAM,MAAM,UAAU,SAAU,UAAA;AAAA,IAAY,CAAC;AAEzF,UAAM,iBAAiB,WAAW,CAAC,MAAM;AACvC,UAAI,EAAE,QAAQ,UAAU;AACtB,aAAK,WAAW,QAAQ,KAAK;AAC7B;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;AAAA,EACH;AACF;AAvLE,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;AADpB,IAAM,iBAAN;AA0LA,SAAS,uBAA8C;AAC5D,SAAO,cAAc,mBAAmB,cAAc;AACxD;"}
|
|
1
|
+
{"version":3,"file":"autocomplete.js","sources":["../../../src/components/autocomplete/autocomplete.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-autocomplete> — Campo de texto com sugestões filtradas.\n *\n * Atributos: value, size, placeholder, source (JSON array de strings),\n * disabled, min-chars (padrão 1).\n * Evento: `select` (composed, detail: { value }).\n */\nexport class FxAutocomplete extends FxElement {\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) { this.setAttribute('value', v); }\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.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 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 field.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') {\n this.toggleAttr('open', false);\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}\n\nexport function defineFxAutocomplete(): typeof FxAutocomplete {\n return defineElement('fx-autocomplete', FxAutocomplete);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -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;;;;;;;;GAQG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,
|
|
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;;;;;;;;GAQG;AACH,qBAAa,QAAS,SAAQ,SAAS;IACrC,OAAgB,MAAM,SAmHpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,+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;cAkDd,iBAAiB,IAAI,IAAI;cAKzB,oBAAoB,IAAI,IAAI;IAK/C;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAmBjB;CACH;AAED,wBAAgB,cAAc,IAAI,OAAO,QAAQ,CAEhD"}
|
|
@@ -113,7 +113,9 @@ _FxButton.styles = css`
|
|
|
113
113
|
font-size: var(--fx-font-size);
|
|
114
114
|
font-weight: var(--fx-font-weight);
|
|
115
115
|
line-height: var(--fx-font-line-height);
|
|
116
|
-
|
|
116
|
+
/* Padding vertical limitado ao orçamento do token --fx-size-*: a altura
|
|
117
|
+
fica EXATAMENTE em sm 32 / md 40 / lg 48 (mesma medida dos campos). */
|
|
118
|
+
padding: var(--fx-space-sm) var(--fx-space-xl);
|
|
117
119
|
border-radius: var(--fx-radius-md);
|
|
118
120
|
border: 1px solid transparent;
|
|
119
121
|
color: #fff;
|
|
@@ -128,16 +130,16 @@ _FxButton.styles = css`
|
|
|
128
130
|
box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);
|
|
129
131
|
}
|
|
130
132
|
.btn:focus-visible {
|
|
131
|
-
outline:
|
|
132
|
-
|
|
133
|
+
outline: none;
|
|
134
|
+
box-shadow: var(--fx-effect-focus-ring, none);
|
|
133
135
|
}
|
|
134
136
|
.btn:hover { filter: brightness(0.88); }
|
|
135
137
|
.btn:active { transform: translateY(1px); }
|
|
136
138
|
|
|
137
139
|
/* Tamanhos — alturas vindas dos tokens (personalizáveis por preset) */
|
|
138
140
|
.btn { min-height: var(--fx-size-md); }
|
|
139
|
-
:host([size='sm']) .btn { padding: var(--fx-space-
|
|
140
|
-
:host([size='lg']) .btn { padding: var(--fx-space-
|
|
141
|
+
: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); }
|
|
142
|
+
: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); }
|
|
141
143
|
|
|
142
144
|
/* Full width */
|
|
143
145
|
:host([full]) .btn { width: 100%; }
|
|
@@ -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 * 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 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: var(--fx-space-md) 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: 2px solid var(--fx-color-primary);\n outline-offset: 2px;\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-sm) 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-lg) 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 /** 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\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":"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"}
|
|
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 * 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 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 /** 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\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":"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"}
|
|
@@ -2,7 +2,8 @@ import { FxElement } from '../../core/base';
|
|
|
2
2
|
/**
|
|
3
3
|
* <fx-card> — Container de conteúdo com cabeçalho, corpo e rodapé.
|
|
4
4
|
*
|
|
5
|
-
* Atributos: variant (elevated|flat|outline, padrão elevated),
|
|
5
|
+
* Atributos: variant (elevated|flat|outline|ghost, padrão elevated),
|
|
6
|
+
* radius (sm|md|lg — arredondamento; `size` segue aceito como alias legado),
|
|
6
7
|
* padded (exibe padding interno), heading (rótulo opcional no cabeçalho).
|
|
7
8
|
* Slots: `header`, padrão (conteúdo), `footer`.
|
|
8
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/components/card/card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C
|
|
1
|
+
{"version":3,"file":"card.d.ts","sourceRoot":"","sources":["../../../src/components/card/card.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;GAOG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAsDpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;CAUlC;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
|
|
@@ -4,7 +4,7 @@ import { defineElement } from "../../core/define.js";
|
|
|
4
4
|
import { esc } from "../../core/sanitize.js";
|
|
5
5
|
const _FxCard = class _FxCard extends FxElement {
|
|
6
6
|
static get observedAttributes() {
|
|
7
|
-
return ["variant", "size", "padded", "heading"];
|
|
7
|
+
return ["variant", "radius", "size", "padded", "heading"];
|
|
8
8
|
}
|
|
9
9
|
render() {
|
|
10
10
|
const heading = this.getAttr("heading");
|
|
@@ -41,8 +41,11 @@ _FxCard.styles = css`
|
|
|
41
41
|
:host([variant='outline']) .card { box-shadow: none; border-style: solid; border-width: 1px; }
|
|
42
42
|
:host([variant='ghost']) .card { box-shadow: none; border-color: transparent; background: transparent; }
|
|
43
43
|
|
|
44
|
-
/*
|
|
44
|
+
/* Raio do container: radius (nome atual) — size continua aceito
|
|
45
|
+
como alias legado para não quebrar quem já usava o atributo. */
|
|
46
|
+
:host([radius='sm']) .card,
|
|
45
47
|
:host([size='sm']) .card { border-radius: var(--fx-radius-sm); }
|
|
48
|
+
:host([radius='lg']) .card,
|
|
46
49
|
:host([size='lg']) .card { border-radius: var(--fx-radius-xl); }
|
|
47
50
|
|
|
48
51
|
header {
|
|
@@ -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, padrão elevated)
|
|
1
|
+
{"version":3,"file":"card.js","sources":["../../../src/components/card/card.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-card> — Container de conteúdo com cabeçalho, corpo e rodapé.\n *\n * Atributos: variant (elevated|flat|outline|ghost, padrão elevated),\n * radius (sm|md|lg — arredondamento; `size` segue aceito como alias legado),\n * padded (exibe padding interno), heading (rótulo opcional no cabeçalho).\n * Slots: `header`, padrão (conteúdo), `footer`.\n */\nexport class FxCard extends FxElement {\n static override styles = css`\n :host {\n display: block;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n box-sizing: border-box;\n }\n .card {\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-lg);\n box-shadow: var(--fx-shadow-sm);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n /* Elevado: sombra principal; Flat: sem sombra mas cor plana; Outline: sem sombra e borda sólida */\n :host([variant='elevated']) .card { box-shadow: var(--fx-shadow-md); }\n :host([variant='elevated']) .card:hover { box-shadow: var(--fx-shadow-xl); }\n :host([variant='flat']) .card { box-shadow: none; border-color: transparent; }\n :host([variant='outline']) .card { box-shadow: none; border-style: solid; border-width: 1px; }\n :host([variant='ghost']) .card { box-shadow: none; border-color: transparent; background: transparent; }\n\n /* Raio do container: radius (nome atual) — size continua aceito\n como alias legado para não quebrar quem já usava o atributo. */\n :host([radius='sm']) .card,\n :host([size='sm']) .card { border-radius: var(--fx-radius-sm); }\n :host([radius='lg']) .card,\n :host([size='lg']) .card { border-radius: var(--fx-radius-xl); }\n\n header {\n padding: var(--fx-space-md) var(--fx-space-xl);\n border-bottom: 1px solid var(--fx-border-default);\n font-weight: 600;\n color: var(--fx-text-default);\n font-size: calc(var(--fx-font-size) + 2px);\n }\n header[hidden] { display: none; }\n\n .body {\n flex: 1 1 auto;\n }\n :host([padded]) .body { padding: var(--fx-space-lg) var(--fx-space-xl); }\n :host([padded]) .body:empty { padding: 0; }\n\n footer {\n padding: var(--fx-space-md) var(--fx-space-xl);\n border-top: 1px solid var(--fx-border-default);\n display: flex;\n justify-content: flex-end;\n gap: var(--fx-space-sm);\n }\n footer[hidden] { display: none; }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'radius', 'size', 'padded', 'heading'];\n }\n\n protected override render(): void {\n const heading = this.getAttr('heading');\n this.setTemplate(`\n <div class=\"card\" part=\"card\">\n <header part=\"header\" ${heading ? '' : 'hidden'}>${esc(heading)}<slot name=\"header\"></slot></header>\n <div class=\"body\" part=\"content\"><slot></slot></div>\n <footer part=\"footer\"><slot name=\"footer\"></slot></footer>\n </div>\n `);\n }\n}\n\nexport function defineFxCard(): typeof FxCard {\n return defineElement('fx-card', FxCard);\n}\n"],"names":[],"mappings":";;;;AAaO,MAAM,UAAN,MAAM,gBAAe,UAAU;AAAA,EAyDpC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,UAAU,QAAQ,UAAU,SAAS;AAAA,EAC1D;AAAA,EAEmB,SAAe;AAChC,UAAM,UAAU,KAAK,QAAQ,SAAS;AACtC,SAAK,YAAY;AAAA;AAAA,gCAEW,UAAU,KAAK,QAAQ,IAAI,IAAI,OAAO,CAAC;AAAA;AAAA;AAAA;AAAA,KAIlE;AAAA,EACH;AACF;AAtEE,QAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADpB,IAAM,SAAN;AAyEA,SAAS,eAA8B;AAC5C,SAAO,cAAc,WAAW,MAAM;AACxC;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../src/components/datepicker/datepicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"datepicker.d.ts","sourceRoot":"","sources":["../../../src/components/datepicker/datepicker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,YAAa,SAAQ,SAAS;IACzC,OAAgB,MAAM,SAwJpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,OAAO,CAAC,WAAW,CAAC,CAAqB;IACzC,6CAA6C;IAC7C,OAAO,CAAC,IAAI,CAAwB;IAEpC,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,EAElB;IAED,gDAAgD;IAChD,IAAI,MAAM,IAAI,MAAM,EAAE,CAGrB;IACD,IAAI,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAExB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;cAEkB,iBAAiB,IAAI,IAAI;cAWzB,oBAAoB,IAAI,IAAI;IAK/C,wFAAwF;IACxF,OAAO,CAAC,UAAU;IAyBlB,8CAA8C;IAC9C,OAAO,CAAC,WAAW;IAoBnB,iDAAiD;IACjD,OAAO,CAAC,QAAQ;IAKhB,sDAAsD;IACtD,OAAO,CAAC,iBAAiB;IAQzB,+DAA+D;IAC/D,OAAO,CAAC,QAAQ;cAOG,MAAM,IAAI,IAAI;IAqJjC,OAAO,CAAC,UAAU;IAMlB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAgClB,8DAA8D;IAC9D,OAAO,CAAC,WAAW;CAkCpB;AAED,wBAAgB,kBAAkB,IAAI,OAAO,YAAY,CAExD"}
|
|
@@ -305,6 +305,8 @@ _FxDatepicker.styles = css`
|
|
|
305
305
|
min-height: var(--fx-size-md, 40px);
|
|
306
306
|
min-width: 150px;
|
|
307
307
|
padding: var(--fx-space-xs) var(--fx-space-md);
|
|
308
|
+
/* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */
|
|
309
|
+
line-height: var(--fx-font-line-height);
|
|
308
310
|
background-color: var(--fx-surface-background);
|
|
309
311
|
border: 1px solid var(--fx-border-default);
|
|
310
312
|
border-radius: var(--fx-radius-md);
|
|
@@ -313,9 +315,9 @@ _FxDatepicker.styles = css`
|
|
|
313
315
|
border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),
|
|
314
316
|
box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);
|
|
315
317
|
}
|
|
316
|
-
:host([size='sm']) .field { min-height:
|
|
317
|
-
:host([size='md']) .field { min-height:
|
|
318
|
-
:host([size='lg']) .field { min-height:
|
|
318
|
+
:host([size='sm']) .field { min-height: var(--fx-size-sm); min-width: 180px; }
|
|
319
|
+
:host([size='md']) .field { min-height: var(--fx-size-md); }
|
|
320
|
+
:host([size='lg']) .field { min-height: var(--fx-size-lg); min-width: 220px; font-size: calc(var(--fx-font-size) + 4px); }
|
|
319
321
|
/* Range e multiple exibem mais de uma data: campo mais largo */
|
|
320
322
|
:host([mode='range']) .field { min-width: 250px; }
|
|
321
323
|
:host([mode='multiple']) .field { min-width: 220px; }
|
|
@@ -326,28 +328,31 @@ _FxDatepicker.styles = css`
|
|
|
326
328
|
descartados para permitir layouts estreitos sob controle do pai). */
|
|
327
329
|
:host([full]) { display: block; width: 100%; }
|
|
328
330
|
:host([full]) .field { min-width: 0; }
|
|
329
|
-
/* Validação
|
|
331
|
+
/* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o brilho
|
|
332
|
+
segue o token --fx-effect-error-ring, que acompanha effect.focus-ring. */
|
|
330
333
|
:host([error]) .field,
|
|
331
334
|
:host([invalid]) .field {
|
|
332
335
|
border-color: var(--fx-color-danger, #dc2626);
|
|
336
|
+
box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
|
|
333
337
|
}
|
|
334
338
|
:host([error]) .field:focus-within,
|
|
335
339
|
:host([invalid]) .field:focus-within,
|
|
336
340
|
:host([error][open]) .field,
|
|
337
341
|
:host([invalid][open]) .field {
|
|
338
342
|
border-color: var(--fx-color-danger, #dc2626);
|
|
339
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent);
|
|
343
|
+
box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
|
|
340
344
|
}
|
|
341
345
|
:host([success]) .field,
|
|
342
346
|
:host([valid]) .field {
|
|
343
347
|
border-color: var(--fx-color-success, #16a34a);
|
|
348
|
+
box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
|
|
344
349
|
}
|
|
345
350
|
:host([success]) .field:focus-within,
|
|
346
351
|
:host([valid]) .field:focus-within,
|
|
347
352
|
:host([success][open]) .field,
|
|
348
353
|
:host([valid][open]) .field {
|
|
349
354
|
border-color: var(--fx-color-success, #16a34a);
|
|
350
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent);
|
|
355
|
+
box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
|
|
351
356
|
}
|
|
352
357
|
.field:hover { border-color: var(--fx-border-hover); }
|
|
353
358
|
.field:focus-within {
|