@wrrdev/fenix-ui 1.1.6 → 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 +7 -2
- package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
- package/dist/components/autocomplete/autocomplete.js +9 -6
- 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 +5 -3
- package/dist/components/button/button.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 +5 -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.map +1 -1
- package/dist/components/input/input.js +12 -6
- 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 +8 -3
- 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 +9 -3
- 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 +7 -3
- 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 +22 -0
- package/dist/core/jsx.d.ts.map +1 -1
- package/dist/core/jsx.js.map +1 -1
- 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 +3 -1
- package/dist/core/vue.d.ts.map +1 -1
- package/dist/core/vue.js.map +1 -1
- package/dist/docs/app.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/fenix-ui.umd.min.js +168 -131
- package/dist/fenix-ui2.esm.js.map +1 -1
- package/dist/fenix-ui3.esm.js +32 -5
- package/dist/fenix-ui3.esm.js.map +1 -1
- package/dist/fenix-ui4.esm.js +97 -40
- package/dist/fenix-ui4.esm.js.map +1 -1
- package/dist/fenix-ui5.esm.js +1 -0
- package/dist/fenix-ui5.esm.js.map +1 -1
- 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 +5 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileupload.js","sources":["../../../src/components/fileupload/fileupload.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\n\n/**\n * <fx-fileupload> — Seletor de arquivos com upload e progresso (estilo FileUpload do PrimeVue).\n *\n * Atributos:\n * - mode (basic|advanced — `basic`: botão; `advanced`: dropzone com drag & drop);\n * - size (sm|md|lg, padrão md);\n * - severity (primary|secondary|success|warning|danger|info — cor do botão no modo basic);\n * - label (texto do botão/área, padrão 'Escolher arquivo');\n * - icon (glifo/emoji exibido antes do texto — sem o atributo, sem ícone);\n * - accept, multiple, disabled (como no <input type=\"file\">);\n * - show-progress (exibe barra + % de upload);\n * - progress (0-100 — o consumidor atualiza durante o upload real);\n * - value (nome(s) do(s) arquivo(s) carregado(s), somente leitura/espelho).\n *\n * Eventos (composed): `select` (detail: { files: File[] }), `remove`, `complete`.\n */\nexport class FxFileUpload 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 }\n :host([disabled]) { opacity: 0.55; pointer-events: none; }\n input[type='file'] { display: none; }\n\n /* ---- botão (basic) ---- */\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--fx-space-sm);\n /* Largura acompanha o host: CSS externo/classe/inline no elemento esticam\n o botão sem precisar do full (host é shrink-to-fit por padrão). */\n width: 100%;\n min-height: var(--fx-size-md);\n padding: var(--fx-space-md) var(--fx-space-lg);\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 cursor: pointer;\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 background var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .btn:hover { border-color: var(--fx-border-hover); background: var(--fx-surface-surface-hover, rgba(0,0,0,0.03)); }\n .btn:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n /* Severities (como no <fx-button>) — aplicam-se ao botão do modo basic */\n :host([severity='primary']) .btn { background: var(--fx-color-primary); border-color: transparent; color: #fff; }\n :host([severity='secondary']) .btn { background: var(--fx-color-secondary); border-color: transparent; color: #fff; }\n :host([severity='success']) .btn { background: var(--fx-color-success); border-color: transparent; color: #fff; }\n :host([severity='warning']) .btn { background: var(--fx-color-warning); border-color: transparent; color: #fff; }\n :host([severity='danger']) .btn { background: var(--fx-color-danger); border-color: transparent; color: #fff; }\n :host([severity='info']) .btn { background: var(--fx-color-info); border-color: transparent; color: #fff; }\n :host([severity]) .btn:hover { filter: brightness(0.88); }\n :host([severity]) .btn:focus-visible { border-color: transparent; }\n :host([size='sm']) .btn { min-height: var(--fx-size-sm); padding: var(--fx-space-sm) var(--fx-space-md); font-size: calc(var(--fx-font-size) - 2px); }\n :host([size='lg']) .btn { min-height: var(--fx-size-lg); padding: var(--fx-space-lg) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); }\n :host([size='sm']) .drop { min-height: 72px; }\n :host([size='lg']) .drop { min-height: 132px; }\n\n /* ---- dropzone (advanced) ---- */\n .drop {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--fx-space-sm);\n /* Acompanha a largura do host (definida por CSS externo/classe/inline). */\n width: 100%;\n min-height: 100px;\n padding: var(--fx-space-lg);\n border: 2px dashed var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n color: var(--fx-text-muted);\n cursor: pointer;\n text-align: center;\n transition: border-color var(--fx-motion-duration-normal) var(--fx-motion-easing), background var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .drop:hover, .drop.over {\n border-color: var(--fx-color-primary);\n background: color-mix(in srgb, var(--fx-color-primary) 6%, transparent);\n }\n .drop .icon { font-size: calc(var(--fx-font-size) + 12px); line-height: 1; }\n .drop .hint { font-size: calc(var(--fx-font-size) - 2px); }\n\n /* ---- ícone comum ---- */\n .icon { line-height: 1; }\n\n /* ---- arquivo(s) selecionado(s) ---- */\n .files { display: flex; flex-direction: column; gap: var(--fx-space-xs); margin-top: var(--fx-space-sm); }\n .file {\n display: flex;\n align-items: center;\n gap: var(--fx-space-sm);\n padding: var(--fx-space-xs) var(--fx-space-sm);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-text-default);\n }\n .file .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .file .size { color: var(--fx-text-muted); white-space: nowrap; }\n .file .remove {\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n padding: 0 var(--fx-space-xs);\n border-radius: var(--fx-radius-full);\n }\n .file .remove:hover { color: var(--fx-color-danger); }\n\n /* ---- progresso ---- */\n .progress-row { display: flex; align-items: center; gap: var(--fx-space-sm); margin-top: var(--fx-space-sm); }\n .track {\n flex: 1;\n height: 6px;\n background: var(--fx-surface-surface-hover);\n border-radius: var(--fx-radius-full);\n overflow: hidden;\n }\n .bar {\n height: 100%;\n width: 0%;\n background: var(--fx-color-primary);\n border-radius: var(--fx-radius-full);\n transition: width var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n :host([progress='100']) .bar { background: var(--fx-color-success); }\n .pct {\n font-size: calc(var(--fx-font-size) - 2px);\n font-weight: 600;\n color: var(--fx-text-default);\n min-width: 34px;\n text-align: right;\n }\n /* Full width: o host estica até o pai e o botão/dropzone acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .btn { width: 100%; }\n :host([full]) .drop { width: 100%; }\n /* Base dos glifos Fenix Icons (o @font-face vem de @wrrdev/fenix-ui/icons). */\n ${FENIX_ICON_BASE_CSS}\n `;\n\n static override get observedAttributes(): string[] {\n return ['mode', 'size', 'severity', 'label', 'icon', 'accept', 'multiple', 'disabled', 'show-progress', 'progress', 'value', 'full'];\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(value: string) { this.setAttribute('size', value); }\n\n /** Nomes dos arquivos selecionados (espelho em `value`). */\n get value(): string { return this.getAttr('value'); }\n set value(v: string) { this.setAttribute('value', v); }\n\n get progress(): number { return Math.min(100, Math.max(0, Number(this.getAttr('progress', '0')) || 0)); }\n set progress(v: number) { this.setAttribute('progress', String(v)); }\n\n /** Arquivos selecionados (não serializável em atributo). */\n get files(): File[] { return this._files; }\n set files(files: File[]) {\n this._files = Array.from(files);\n this.value = this._files.map((f) => f.name).join(', ');\n this._syncFilesArea();\n }\n\n protected override render(): void {\n const mode = this.getAttr('mode', 'basic');\n const advanced = mode === 'advanced';\n const label = this.getAttr('label', 'Escolher arquivo');\n const icon = this.getAttr('icon');\n const accept = this.getAttr('accept');\n const multiple = this.hasAttr('multiple');\n const disabled = this.hasAttr('disabled');\n const showProgress = this.hasAttr('show-progress');\n const pct = this.progress;\n\n const fileInput = `<input type=\"file\" class=\"picker\" ${accept ? `accept=\"${esc(accept)}\"` : ''} ${multiple ? 'multiple' : ''} ${disabled ? 'disabled' : ''} />`;\n\n this.setTemplate(`\n ${advanced\n ? `<div class=\"drop\" part=\"dropzone\" role=\"button\" tabindex=\"0\" aria-label=\"${esc(label)}\">\n ${icon ? `<span class=\"icon\" part=\"icon\" aria-hidden=\"true\">${fenixIconHtml(icon)}</span>` : ''}\n <span>${esc(label)}</span>\n <span class=\"hint\">ou arraste e solte aqui</span>\n </div>`\n : `<button type=\"button\" class=\"btn\" part=\"button\">\n ${icon ? `<span class=\"icon\" part=\"icon\" aria-hidden=\"true\">${fenixIconHtml(icon)}</span>` : ''}\n <span>${esc(label)}</span>\n </button>`}\n ${fileInput}\n <div class=\"files\" part=\"files\"></div>\n ${showProgress ? `\n <div class=\"progress-row\" part=\"progress\">\n <div class=\"track\" role=\"progressbar\" aria-valuenow=\"${pct}\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"bar\" style=\"width: ${pct}%\"></div>\n </div>\n <span class=\"pct\">${pct}%</span>\n </div>` : ''}\n `);\n\n this._bindPicker();\n this._syncFilesArea();\n\n if (!this._done && showProgress && pct >= 100 && this._files.length) {\n this._done = true;\n this.dispatchEvent(new CustomEvent('complete', { bubbles: true, composed: true }));\n }\n if (pct < 100) this._done = false;\n }\n\n private _files: File[] = [];\n private _done = false;\n\n private _picker(): HTMLInputElement | null {\n return this.root.querySelector<HTMLInputElement>('.picker');\n }\n\n private _bindPicker(): void {\n const picker = this._picker();\n if (!picker) return;\n const trigger = this.root.querySelector('.btn') ?? this.root.querySelector('.drop');\n trigger?.addEventListener('click', () => picker.click());\n trigger?.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Enter' || (e as KeyboardEvent).key === ' ') {\n e.preventDefault();\n picker.click();\n }\n });\n picker.addEventListener('change', () => this._acceptFiles(picker.files ?? []));\n\n // drag & drop (advanced)\n const drop = this.root.querySelector<HTMLElement>('.drop');\n if (!drop) return;\n drop.addEventListener('dragover', (e) => { e.preventDefault(); drop.classList.add('over'); });\n drop.addEventListener('dragleave', () => drop.classList.remove('over'));\n drop.addEventListener('drop', (e) => {\n e.preventDefault();\n drop.classList.remove('over');\n if (e.dataTransfer?.files.length) this._acceptFiles(e.dataTransfer.files);\n });\n }\n\n private _acceptFiles(list: FileList | File[]): void {\n const picked = this.hasAttr('multiple') ? Array.from(list) : [list[0]].filter(Boolean);\n this.files = picked;\n const picker = this._picker();\n if (picker) picker.value = '';\n this.dispatchEvent(\n new CustomEvent('select', { bubbles: true, composed: true, detail: { files: picked } }),\n );\n }\n\n private _fmtSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n\n /** Renderiza a lista de arquivos sem re-renderizar o template (mantém estado). */\n private _syncFilesArea(): void {\n const area = this.root?.querySelector?.('.files') as HTMLElement | null;\n if (!area) return;\n area.innerHTML = this._files\n .map(\n (f, i) => `\n <div class=\"file\" part=\"file\">\n <span class=\"name\">${esc(f.name)}</span>\n <span class=\"size\">${this._fmtSize(f.size)}</span>\n <button type=\"button\" class=\"remove\" data-i=\"${i}\" part=\"remove\" aria-label=\"Remover ${esc(f.name)}\">×</button>\n </div>`,\n )\n .join('');\n area.querySelectorAll<HTMLButtonElement>('.remove').forEach((btn) =>\n btn.addEventListener('click', () => {\n const removed = this._files.splice(Number(btn.dataset.i), 1)[0];\n this.value = this._files.map((f) => f.name).join(', ');\n this.progress = 0;\n this._syncFilesArea();\n this.dispatchEvent(\n new CustomEvent('remove', { bubbles: true, composed: true, detail: { file: removed } }),\n );\n }),\n );\n }\n}\n\nexport function defineFxFileUpload(): typeof FxFileUpload {\n return defineElement('fx-fileupload', FxFileUpload);\n}\n\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fileupload.js","sources":["../../../src/components/fileupload/fileupload.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\n\n/**\n * <fx-fileupload> — Seletor de arquivos com upload e progresso.\n *\n * Atributos:\n * - mode (basic|advanced — `basic`: botão; `advanced`: dropzone com drag & drop);\n * - size (sm|md|lg, padrão md);\n * - severity (primary|secondary|success|warning|danger|info — cor do botão no modo basic);\n * - label (texto do botão/área, padrão 'Escolher arquivo');\n * - icon (glifo/emoji exibido antes do texto — sem o atributo, sem ícone);\n * - accept, multiple, disabled (como no <input type=\"file\">);\n * - show-progress (exibe barra + % de upload);\n * - progress (0-100 — o consumidor atualiza durante o upload real);\n * - value (nome(s) do(s) arquivo(s) carregado(s), somente leitura/espelho).\n *\n * Eventos (composed): `select` (detail: { files: File[] }), `remove`, `complete`.\n */\nexport class FxFileUpload 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 }\n :host([disabled]) { opacity: 0.55; pointer-events: none; }\n input[type='file'] { display: none; }\n\n /* ---- botão (basic) ---- */\n .btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--fx-space-sm);\n /* Largura acompanha o host: CSS externo/classe/inline no elemento esticam\n o botão sem precisar do full (host é shrink-to-fit por padrão). */\n width: 100%;\n min-height: var(--fx-size-md);\n /* Padding vertical no orçamento do token --fx-size-* ⇒ altura padrão. */\n padding: var(--fx-space-sm) var(--fx-space-lg);\n font-family: inherit;\n font-size: inherit;\n font-weight: var(--fx-font-weight);\n line-height: var(--fx-font-line-height);\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 cursor: pointer;\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 background var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .btn:hover { border-color: var(--fx-border-hover); background: var(--fx-surface-surface-hover, rgba(0,0,0,0.03)); }\n .btn:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n /* Severities (como no <fx-button>) — aplicam-se ao botão do modo basic */\n :host([severity='primary']) .btn { background: var(--fx-color-primary); border-color: transparent; color: #fff; }\n :host([severity='secondary']) .btn { background: var(--fx-color-secondary); border-color: transparent; color: #fff; }\n :host([severity='success']) .btn { background: var(--fx-color-success); border-color: transparent; color: #fff; }\n :host([severity='warning']) .btn { background: var(--fx-color-warning); border-color: transparent; color: #fff; }\n :host([severity='danger']) .btn { background: var(--fx-color-danger); border-color: transparent; color: #fff; }\n :host([severity='info']) .btn { background: var(--fx-color-info); border-color: transparent; color: #fff; }\n :host([severity]) .btn:hover { filter: brightness(0.88); }\n :host([severity]) .btn:focus-visible { border-color: transparent; }\n :host([size='sm']) .btn { min-height: var(--fx-size-sm); padding: var(--fx-space-xs) var(--fx-space-md); font-size: calc(var(--fx-font-size) - 2px); }\n :host([size='lg']) .btn { min-height: var(--fx-size-lg); padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); }\n :host([size='sm']) .drop { min-height: 72px; }\n :host([size='lg']) .drop { min-height: 132px; }\n\n /* ---- dropzone (advanced) ---- */\n .drop {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--fx-space-sm);\n /* Acompanha a largura do host (definida por CSS externo/classe/inline). */\n width: 100%;\n min-height: 100px;\n padding: var(--fx-space-lg);\n border: 2px dashed var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n color: var(--fx-text-muted);\n cursor: pointer;\n text-align: center;\n transition: border-color var(--fx-motion-duration-normal) var(--fx-motion-easing), background var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .drop:hover, .drop.over {\n border-color: var(--fx-color-primary);\n background: color-mix(in srgb, var(--fx-color-primary) 6%, transparent);\n }\n .drop .icon { font-size: calc(var(--fx-font-size) + 12px); line-height: 1; }\n .drop .hint { font-size: calc(var(--fx-font-size) - 2px); }\n\n /* ---- ícone comum ---- */\n .icon { line-height: 1; }\n\n /* ---- arquivo(s) selecionado(s) ---- */\n .files { display: flex; flex-direction: column; gap: var(--fx-space-xs); margin-top: var(--fx-space-sm); }\n .file {\n display: flex;\n align-items: center;\n gap: var(--fx-space-sm);\n padding: var(--fx-space-xs) var(--fx-space-sm);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-text-default);\n }\n .file .name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .file .size { color: var(--fx-text-muted); white-space: nowrap; }\n .file .remove {\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n padding: 0 var(--fx-space-xs);\n border-radius: var(--fx-radius-full);\n }\n .file .remove:hover { color: var(--fx-color-danger); }\n\n /* ---- progresso ---- */\n .progress-row { display: flex; align-items: center; gap: var(--fx-space-sm); margin-top: var(--fx-space-sm); }\n .track {\n flex: 1;\n height: 6px;\n background: var(--fx-surface-surface-hover);\n border-radius: var(--fx-radius-full);\n overflow: hidden;\n }\n .bar {\n height: 100%;\n width: 0%;\n background: var(--fx-color-primary);\n border-radius: var(--fx-radius-full);\n transition: width var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n :host([progress='100']) .bar { background: var(--fx-color-success); }\n .pct {\n font-size: calc(var(--fx-font-size) - 2px);\n font-weight: 600;\n color: var(--fx-text-default);\n min-width: 34px;\n text-align: right;\n }\n /* Full width: o host estica até o pai e o botão/dropzone acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .btn { width: 100%; }\n :host([full]) .drop { width: 100%; }\n /* Base dos glifos Fenix Icons (o @font-face vem de @wrrdev/fenix-ui/icons). */\n ${FENIX_ICON_BASE_CSS}\n `;\n\n static override get observedAttributes(): string[] {\n return ['mode', 'size', 'severity', 'label', 'icon', 'accept', 'multiple', 'disabled', 'show-progress', 'progress', 'value', 'full'];\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(value: string) { this.setAttribute('size', value); }\n\n /** Nomes dos arquivos selecionados (espelho em `value`). */\n get value(): string { return this.getAttr('value'); }\n set value(v: string) { this.setAttribute('value', v); }\n\n get progress(): number { return Math.min(100, Math.max(0, Number(this.getAttr('progress', '0')) || 0)); }\n set progress(v: number) { this.setAttribute('progress', String(v)); }\n\n /** Arquivos selecionados (não serializável em atributo). */\n get files(): File[] { return this._files; }\n set files(files: File[]) {\n this._files = Array.from(files);\n this.value = this._files.map((f) => f.name).join(', ');\n this._syncFilesArea();\n }\n\n protected override render(): void {\n const mode = this.getAttr('mode', 'basic');\n const advanced = mode === 'advanced';\n const label = this.getAttr('label', 'Escolher arquivo');\n const icon = this.getAttr('icon');\n const accept = this.getAttr('accept');\n const multiple = this.hasAttr('multiple');\n const disabled = this.hasAttr('disabled');\n const showProgress = this.hasAttr('show-progress');\n const pct = this.progress;\n\n const fileInput = `<input type=\"file\" class=\"picker\" ${accept ? `accept=\"${esc(accept)}\"` : ''} ${multiple ? 'multiple' : ''} ${disabled ? 'disabled' : ''} />`;\n\n this.setTemplate(`\n ${advanced\n ? `<div class=\"drop\" part=\"dropzone\" role=\"button\" tabindex=\"0\" aria-label=\"${esc(label)}\">\n ${icon ? `<span class=\"icon\" part=\"icon\" aria-hidden=\"true\">${fenixIconHtml(icon)}</span>` : ''}\n <span>${esc(label)}</span>\n <span class=\"hint\">ou arraste e solte aqui</span>\n </div>`\n : `<button type=\"button\" class=\"btn\" part=\"button\">\n ${icon ? `<span class=\"icon\" part=\"icon\" aria-hidden=\"true\">${fenixIconHtml(icon)}</span>` : ''}\n <span>${esc(label)}</span>\n </button>`}\n ${fileInput}\n <div class=\"files\" part=\"files\"></div>\n ${showProgress ? `\n <div class=\"progress-row\" part=\"progress\">\n <div class=\"track\" role=\"progressbar\" aria-valuenow=\"${pct}\" aria-valuemin=\"0\" aria-valuemax=\"100\">\n <div class=\"bar\" style=\"width: ${pct}%\"></div>\n </div>\n <span class=\"pct\">${pct}%</span>\n </div>` : ''}\n `);\n\n this._bindPicker();\n this._syncFilesArea();\n\n if (!this._done && showProgress && pct >= 100 && this._files.length) {\n this._done = true;\n this.dispatchEvent(new CustomEvent('complete', { bubbles: true, composed: true }));\n }\n if (pct < 100) this._done = false;\n }\n\n private _files: File[] = [];\n private _done = false;\n\n private _picker(): HTMLInputElement | null {\n return this.root.querySelector<HTMLInputElement>('.picker');\n }\n\n private _bindPicker(): void {\n const picker = this._picker();\n if (!picker) return;\n const trigger = this.root.querySelector('.btn') ?? this.root.querySelector('.drop');\n trigger?.addEventListener('click', () => picker.click());\n trigger?.addEventListener('keydown', (e) => {\n if ((e as KeyboardEvent).key === 'Enter' || (e as KeyboardEvent).key === ' ') {\n e.preventDefault();\n picker.click();\n }\n });\n picker.addEventListener('change', () => this._acceptFiles(picker.files ?? []));\n\n // drag & drop (advanced)\n const drop = this.root.querySelector<HTMLElement>('.drop');\n if (!drop) return;\n drop.addEventListener('dragover', (e) => { e.preventDefault(); drop.classList.add('over'); });\n drop.addEventListener('dragleave', () => drop.classList.remove('over'));\n drop.addEventListener('drop', (e) => {\n e.preventDefault();\n drop.classList.remove('over');\n if (e.dataTransfer?.files.length) this._acceptFiles(e.dataTransfer.files);\n });\n }\n\n private _acceptFiles(list: FileList | File[]): void {\n const picked = this.hasAttr('multiple') ? Array.from(list) : [list[0]].filter(Boolean);\n this.files = picked;\n const picker = this._picker();\n if (picker) picker.value = '';\n this.dispatchEvent(\n new CustomEvent('select', { bubbles: true, composed: true, detail: { files: picked } }),\n );\n }\n\n private _fmtSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n\n /** Renderiza a lista de arquivos sem re-renderizar o template (mantém estado). */\n private _syncFilesArea(): void {\n const area = this.root?.querySelector?.('.files') as HTMLElement | null;\n if (!area) return;\n area.innerHTML = this._files\n .map(\n (f, i) => `\n <div class=\"file\" part=\"file\">\n <span class=\"name\">${esc(f.name)}</span>\n <span class=\"size\">${this._fmtSize(f.size)}</span>\n <button type=\"button\" class=\"remove\" data-i=\"${i}\" part=\"remove\" aria-label=\"Remover ${esc(f.name)}\">×</button>\n </div>`,\n )\n .join('');\n area.querySelectorAll<HTMLButtonElement>('.remove').forEach((btn) =>\n btn.addEventListener('click', () => {\n const removed = this._files.splice(Number(btn.dataset.i), 1)[0];\n this.value = this._files.map((f) => f.name).join(', ');\n this.progress = 0;\n this._syncFilesArea();\n this.dispatchEvent(\n new CustomEvent('remove', { bubbles: true, composed: true, detail: { file: removed } }),\n );\n }),\n );\n }\n}\n\nexport function defineFxFileUpload(): typeof FxFileUpload {\n return defineElement('fx-fileupload', FxFileUpload);\n}\n\n\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { FxElement } from '../../core/base';
|
|
2
|
+
/**
|
|
3
|
+
* <fx-icon> — Glifo da Fenix Icons com AUTOCOMPLETE no editor.
|
|
4
|
+
*
|
|
5
|
+
* Por que existe: a forma antiga (`<i class="fx-icon fx-icon-home">`) é CSS
|
|
6
|
+
* puro — o TypeScript não valida nem sugere nada enquanto você digita, e o
|
|
7
|
+
* auto-import só injeta o import. Com o elemento, o atributo `name` é tipado
|
|
8
|
+
* como `FenixIconName`, então Volar/vue-tsc, React e TSX listam os +4.000
|
|
9
|
+
* glifos válidos (e o hover mostra a assinatura do atributo).
|
|
10
|
+
*
|
|
11
|
+
* Atributos:
|
|
12
|
+
* - name → nome do glifo (ex.: `home`); texto livre/emoji também funciona
|
|
13
|
+
* - size → sm | md | lg | xl (default: 1em, herda o texto ao redor)
|
|
14
|
+
* - label → nome acessível; sem label o ícone é aria-hidden (decorativo)
|
|
15
|
+
* - fill → variante preenchida
|
|
16
|
+
* - bold → traço mais pesado
|
|
17
|
+
*
|
|
18
|
+
* O glifo sai por LIGADURA (o nome vira o conteúdo de texto com a
|
|
19
|
+
* font-family de ícones) — por isso funciona dentro do Shadow DOM, onde as
|
|
20
|
+
* classes `fx-icon-<nome>` do documento não atravessam a fronteira.
|
|
21
|
+
*
|
|
22
|
+
* Tamanho via CSS: --fx-icon-size (-sm/-md/-lg/-xl).
|
|
23
|
+
*/
|
|
24
|
+
export declare class FxIcon extends FxElement {
|
|
25
|
+
static styles: string;
|
|
26
|
+
static get observedAttributes(): string[];
|
|
27
|
+
/** Nome do glifo (ou texto livre/emoji). */
|
|
28
|
+
get name(): string;
|
|
29
|
+
set name(value: string);
|
|
30
|
+
/** Tamanho: '' (herda 1em) | 'sm' | 'md' | 'lg' | 'xl'. */
|
|
31
|
+
get size(): string;
|
|
32
|
+
set size(value: string);
|
|
33
|
+
/** Nome acessível; vazio = ícone decorativo (aria-hidden). */
|
|
34
|
+
get label(): string;
|
|
35
|
+
set label(value: string);
|
|
36
|
+
protected connectedCallback(): void;
|
|
37
|
+
protected render(): void;
|
|
38
|
+
}
|
|
39
|
+
export declare function defineFxIcon(): typeof FxIcon;
|
|
40
|
+
//# sourceMappingURL=icon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../../../src/components/icon/icon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAO5C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAoBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,4CAA4C;IAC5C,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,2DAA2D;IAC3D,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAGrB;IAED,8DAA8D;IAC9D,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAGtB;cAEkB,iBAAiB,IAAI,IAAI;cAMzB,MAAM,IAAI,IAAI;CAclC;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/icon/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,CAAC;AAIlB,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,SAAS,EAAE,MAAM,CAAC;KACnB;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/input/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM5C;;;;;;;;;;;;GAYG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACpC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/input/input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAM5C;;;;;;;;;;;;GAYG;AACH,qBAAa,OAAQ,SAAQ,SAAS;IACpC,OAAgB,MAAM,SAuIpB;IAMF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAMjD;IAED,wCAAwC;IACxC,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAErB;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,KAAK,EAAE,MAAM,EAEtB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAE1B;cAEkB,MAAM,IAAI,IAAI;CA8DlC;AAED,wBAAgB,aAAa,IAAI,OAAO,OAAO,CAE9C"}
|
|
@@ -121,13 +121,17 @@ _FxInput.styles = css`
|
|
|
121
121
|
.field {
|
|
122
122
|
font-family: inherit;
|
|
123
123
|
font-size: inherit;
|
|
124
|
+
/* Altura determinística: com line-height fixo, o padding vertical cabe no
|
|
125
|
+
token --fx-size-* e a caixa fica EXATAMENTE no tamanho padrão
|
|
126
|
+
(sm 32 / md 40 / lg 48), igual para todos os campos. */
|
|
127
|
+
line-height: var(--fx-font-line-height);
|
|
124
128
|
min-height: var(--fx-size-md);
|
|
125
129
|
font-weight: var(--fx-font-weight);
|
|
126
130
|
color: var(--fx-text-default);
|
|
127
131
|
background-color: var(--fx-surface-background);
|
|
128
132
|
border: 1px solid var(--fx-border-default);
|
|
129
133
|
border-radius: var(--fx-radius-md);
|
|
130
|
-
padding: var(--fx-space-
|
|
134
|
+
padding: var(--fx-space-sm) var(--fx-space-lg);
|
|
131
135
|
/* Acompanha a largura definida no :host. */
|
|
132
136
|
width: 100%;
|
|
133
137
|
box-sizing: border-box;
|
|
@@ -148,25 +152,27 @@ _FxInput.styles = css`
|
|
|
148
152
|
cursor: not-allowed;
|
|
149
153
|
background-color: var(--fx-surface-surface-hover);
|
|
150
154
|
}
|
|
151
|
-
/* Validação:
|
|
155
|
+
/* Validação: borda sempre; o brilho (anel) segue o token do preset
|
|
156
|
+
--fx-effect-error-ring, que acompanha effect.focus-ring — anel
|
|
157
|
+
desligado ⇒ erro só com a borda vermelha, ligado ⇒ soma o brilho. */
|
|
152
158
|
:host([error]) .field,
|
|
153
159
|
:host([invalid]) .field,
|
|
154
160
|
:host([error]) .field:focus-visible,
|
|
155
161
|
:host([invalid]) .field:focus-visible {
|
|
156
162
|
border-color: var(--fx-color-danger, #dc2626);
|
|
157
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent);
|
|
163
|
+
box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
|
|
158
164
|
}
|
|
159
165
|
:host([success]) .field,
|
|
160
166
|
:host([valid]) .field,
|
|
161
167
|
:host([success]) .field:focus-visible,
|
|
162
168
|
:host([valid]) .field:focus-visible {
|
|
163
169
|
border-color: var(--fx-color-success, #16a34a);
|
|
164
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent);
|
|
170
|
+
box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
|
|
165
171
|
}
|
|
166
172
|
:host([size='sm']) { width: var(--fx-input-width-sm, 220px); }
|
|
167
|
-
:host([size='sm']) .field { padding: var(--fx-space-
|
|
173
|
+
:host([size='sm']) .field { padding: var(--fx-space-xs) var(--fx-space-md); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }
|
|
168
174
|
:host([size='lg']) { width: var(--fx-input-width-lg, 300px); }
|
|
169
|
-
:host([size='lg']) .field { padding: var(--fx-space-
|
|
175
|
+
:host([size='lg']) .field { padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }
|
|
170
176
|
/* Full width: o host estica até o pai e o campo interno acompanha
|
|
171
177
|
(usar com um container de largura controlada, ex. w-full no Vue). */
|
|
172
178
|
:host([full]) { display: block; width: 100%; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input.js","sources":["../../../src/components/input/input.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\n\n/**\n * <fx-input> — Campo de texto estilizado com os tokens do tema.\n *\n * Atributos: type (text|number|email|password|search|tel|url), value, size,\n * placeholder, disabled, readonly, min, max, step, clearable,\n * full (largura 100% acompanhando o elemento pai), icon (nome do glifo Fenix Icons\n * ou emoji/texto livre) e icon-pos (left|right, padrão left).\n * Largura: o default (260px; sm 220px / lg 300px) fica no HOST via tokens\n * --fx-input-width(-sm|-lg) — CSS externo, classes e style inline no elemento\n * vencem o default sem precisar de atributo.\n * Slots: `icon` (ícone customizado no lugar do atributo).\n * Eventos: `input` e `change` (composed, detail: { value }).\n */\nexport class FxInput extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n /* Largura padrão no HOST (e não no .field interno): assim CSS externo,\n classes do framework (ex.: w-full) e style inline no elemento vencem\n este default naturalmente — regras do documento têm precedência sobre\n :host. Default global trocável via token: --fx-input-width (-sm/-lg). */\n width: var(--fx-input-width, 260px);\n vertical-align: middle;\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 min-height: var(--fx-size-md);\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 /* Acompanha a largura definida no :host. */\n width: 100%;\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 .field:disabled,\n .field[readonly] {\n opacity: 0.55;\n cursor: not-allowed;\n background-color: var(--fx-surface-surface-hover);\n }\n /* Validação: sobrescrevem a borda/foco via token do preset. */\n :host([error]) .field,\n :host([invalid]) .field,\n :host([error]) .field:focus-visible,\n :host([invalid]) .field:focus-visible {\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 :host([success]) .field:focus-visible,\n :host([valid]) .field:focus-visible {\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 :host([size='sm']) { width: var(--fx-input-width-sm, 220px); }\n :host([size='sm']) .field { padding: var(--fx-space-sm) var(--fx-space-md); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }\n :host([size='lg']) { width: var(--fx-input-width-lg, 300px); }\n :host([size='lg']) .field { padding: var(--fx-space-lg) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }\n /* Full width: o host estica até o pai e o campo interno acompanha\n (usar com um container de largura controlada, ex. w-full no Vue). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { width: 100%; }\n /* Clearable */\n :host([clearable]) { position: relative; display: inline-flex; }\n :host([clearable][full]) { display: block; }\n .wrap { position: relative; display: inline-flex; align-items: center; width: 100%; }\n :host([full]) .wrap { display: flex; width: 100%; }\n :host([clearable]) .field { padding-right: var(--fx-space-xl); }\n :host([clearable][full]) .field { flex: 1 1 auto; }\n /* Ícones (attr icon + slot icon) — DENTRO do campo, sobre o padding\n (padrão Material/PrimeVue). Tamanho ajustável via --fx-input-icon-size. */\n .field-icon {\n position: relative;\n display: inline-flex;\n box-sizing: border-box;\n width: 100%;\n }\n .field-icon .field { flex: 1 1 auto; min-width: 0; }\n :host([full]) .field-icon { display: flex; width: 100%; }\n .field-icon .fx-icon,\n .field-icon ::slotted([slot='icon']) {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px));\n line-height: 1;\n color: var(--fx-text-muted);\n pointer-events: none;\n z-index: 1;\n }\n .field-icon.icon-left .fx-icon,\n .field-icon.icon-left ::slotted([slot='icon']) { left: var(--fx-space-md); }\n .field-icon.icon-right .fx-icon,\n .field-icon.icon-right ::slotted([slot='icon']) { right: var(--fx-space-md); }\n /* padding = posição do ícone + largura + folga extra para o texto\n não colar no glifo. */\n .field-icon.icon-left .field {\n padding-left: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n .field-icon.icon-right .field {\n padding-right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n /* icon-pos right + clearable: desloca o × para não sobrepor o ícone. */\n .field-icon.icon-right + .clear {\n right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-lg));\n }\n .clear {\n position: absolute;\n right: var(--fx-space-xs);\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n cursor: pointer;\n padding: 0 var(--fx-space-xs);\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n /* Base dos glifos Fenix Icons (o @font-face vem de @wrrdev/fenix-ui/icons). */\n ${FENIX_ICON_BASE_CSS}\n `;\n\n // `value` fica FORA da observação: refleti-lo a cada tecla não pode\n // re-renderizar o template, senão o campo perde o foco ao digitar.\n // `error`/`success` (+ aliases `invalid`/`valid`) PRECISAM ser observados:\n // é assim que o toggle no playground / setAttribute no submit re-renderiza.\n static override get observedAttributes(): string[] {\n return [\n 'type', 'size', 'placeholder',\n 'disabled', 'readonly', 'min', 'max', 'step',\n 'icon', 'icon-pos', 'error', 'invalid', 'success', 'valid',\n ];\n }\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string) {\n this.setAttribute('value', value);\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n protected override render(): void {\n const type = this.getAttr('type', 'text');\n const placeholder = this.getAttr('placeholder');\n const readonly = this.hasAttr('readonly');\n const disabled = this.hasAttr('disabled');\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n const step = this.getAttr('step');\n const icon = this.getAttr('icon');\n const iconPos = this.getAttr('icon-pos', 'left') === 'right' ? 'right' : 'left';\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\" part=\"icon\"></slot>'\n : (icon ? fenixIconHtml(icon) : '');\n const useIconBox = Boolean(iconContent);\n\n this.setTemplate(`\n ${this.hasAttr('clearable') ? '<div class=\"wrap\" part=\"wrap\">' : ''}\n ${useIconBox ? `<div class=\"field-icon icon-${iconPos}\">` : ''}\n ${useIconBox && iconPos === 'left' ? iconContent : ''}\n <input class=\"field\" part=\"input\" type=\"${esc(type)}\"\n ${placeholder ? `placeholder=\"${esc(placeholder)}\"` : ''}\n ${min ? `min=\"${esc(min)}\"` : ''} ${max ? `max=\"${esc(max)}\"` : ''} ${step ? `step=\"${esc(step)}\"` : ''}\n />\n ${useIconBox && iconPos === 'right' ? iconContent : ''}\n ${useIconBox ? '</div>' : ''}\n ${this.hasAttr('clearable') ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\" tabindex=\"-1\">×</button></div>' : ''}\n `);\n\n const field = this.root.querySelector<HTMLInputElement>('.field');\n if (!field) return;\n field.value = this.getAttr('value');\n if (disabled) field.setAttribute('disabled', '');\n if (readonly) field.setAttribute('readonly', '');\n\n const emit = (event: string): void => {\n this.value = field.value;\n this.dispatchEvent(\n new CustomEvent(event, { bubbles: true, composed: true, detail: { value: field.value } }),\n );\n };\n // Bloqueia o evento nativo (composed, sem detail) para que só o\n // CustomEvent com detail chegue aos consumidores.\n field.addEventListener('input', (e) => { e.stopPropagation(); emit('input'); });\n field.addEventListener('change', (e) => { e.stopPropagation(); emit('change'); });\n\n // Clearable: botão × que limpa o campo e emite input/change vazios.\n const clearBtn = this.root.querySelector<HTMLButtonElement>('.clear');\n const syncClear = (): void => {\n clearBtn?.toggleAttribute('hidden', field.value === '');\n };\n syncClear();\n clearBtn?.addEventListener('click', () => {\n field.value = '';\n syncClear();\n emit('input');\n emit('change');\n field.focus();\n });\n field.addEventListener('input', syncClear);\n }\n}\n\nexport function defineFxInput(): typeof FxInput {\n return defineElement('fx-input', FxInput);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"input.js","sources":["../../../src/components/input/input.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\nimport { FENIX_ICON_BASE_CSS, fenixIconHtml } from '../../icons/base-css';\n\n/**\n * <fx-input> — Campo de texto estilizado com os tokens do tema.\n *\n * Atributos: type (text|number|email|password|search|tel|url), value, size,\n * placeholder, disabled, readonly, min, max, step, clearable,\n * full (largura 100% acompanhando o elemento pai), icon (nome do glifo Fenix Icons\n * ou emoji/texto livre) e icon-pos (left|right, padrão left).\n * Largura: o default (260px; sm 220px / lg 300px) fica no HOST via tokens\n * --fx-input-width(-sm|-lg) — CSS externo, classes e style inline no elemento\n * vencem o default sem precisar de atributo.\n * Slots: `icon` (ícone customizado no lugar do atributo).\n * Eventos: `input` e `change` (composed, detail: { value }).\n */\nexport class FxInput extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n /* Largura padrão no HOST (e não no .field interno): assim CSS externo,\n classes do framework (ex.: w-full) e style inline no elemento vencem\n este default naturalmente — regras do documento têm precedência sobre\n :host. Default global trocável via token: --fx-input-width (-sm/-lg). */\n width: var(--fx-input-width, 260px);\n vertical-align: middle;\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: com line-height fixo, o padding vertical cabe no\n token --fx-size-* e a caixa fica EXATAMENTE no tamanho padrão\n (sm 32 / md 40 / lg 48), igual para todos os campos. */\n line-height: var(--fx-font-line-height);\n min-height: var(--fx-size-md);\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 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 .field:disabled,\n .field[readonly] {\n opacity: 0.55;\n cursor: not-allowed;\n background-color: var(--fx-surface-surface-hover);\n }\n /* Validação: borda sempre; o brilho (anel) segue o token do preset\n --fx-effect-error-ring, que acompanha effect.focus-ring — anel\n desligado ⇒ erro só com a borda vermelha, ligado ⇒ soma o brilho. */\n :host([error]) .field,\n :host([invalid]) .field,\n :host([error]) .field:focus-visible,\n :host([invalid]) .field:focus-visible {\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 :host([success]) .field:focus-visible,\n :host([valid]) .field:focus-visible {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([size='sm']) { width: var(--fx-input-width-sm, 220px); }\n :host([size='sm']) .field { padding: var(--fx-space-xs) var(--fx-space-md); font-size: var(--fx-font-size); min-height: var(--fx-size-sm); }\n :host([size='lg']) { width: var(--fx-input-width-lg, 300px); }\n :host([size='lg']) .field { padding: var(--fx-space-sm) var(--fx-space-xl); font-size: calc(var(--fx-font-size) + 4px); min-height: var(--fx-size-lg); }\n /* Full width: o host estica até o pai e o campo interno acompanha\n (usar com um container de largura controlada, ex. w-full no Vue). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { width: 100%; }\n /* Clearable */\n :host([clearable]) { position: relative; display: inline-flex; }\n :host([clearable][full]) { display: block; }\n .wrap { position: relative; display: inline-flex; align-items: center; width: 100%; }\n :host([full]) .wrap { display: flex; width: 100%; }\n :host([clearable]) .field { padding-right: var(--fx-space-xl); }\n :host([clearable][full]) .field { flex: 1 1 auto; }\n /* Ícones (attr icon + slot icon) — DENTRO do campo, sobre o padding\n (padrão Material/PrimeVue). Tamanho ajustável via --fx-input-icon-size. */\n .field-icon {\n position: relative;\n display: inline-flex;\n box-sizing: border-box;\n width: 100%;\n }\n .field-icon .field { flex: 1 1 auto; min-width: 0; }\n :host([full]) .field-icon { display: flex; width: 100%; }\n .field-icon .fx-icon,\n .field-icon ::slotted([slot='icon']) {\n position: absolute;\n top: 50%;\n transform: translateY(-50%);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-size: var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px));\n line-height: 1;\n color: var(--fx-text-muted);\n pointer-events: none;\n z-index: 1;\n }\n .field-icon.icon-left .fx-icon,\n .field-icon.icon-left ::slotted([slot='icon']) { left: var(--fx-space-md); }\n .field-icon.icon-right .fx-icon,\n .field-icon.icon-right ::slotted([slot='icon']) { right: var(--fx-space-md); }\n /* padding = posição do ícone + largura + folga extra para o texto\n não colar no glifo. */\n .field-icon.icon-left .field {\n padding-left: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n .field-icon.icon-right .field {\n padding-right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-md) + var(--fx-space-sm));\n }\n /* icon-pos right + clearable: desloca o × para não sobrepor o ícone. */\n .field-icon.icon-right + .clear {\n right: calc(var(--fx-input-icon-size, calc(var(--fx-font-size) + 6px)) + var(--fx-space-lg));\n }\n .clear {\n position: absolute;\n right: var(--fx-space-xs);\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n cursor: pointer;\n padding: 0 var(--fx-space-xs);\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n /* Base dos glifos Fenix Icons (o @font-face vem de @wrrdev/fenix-ui/icons). */\n ${FENIX_ICON_BASE_CSS}\n `;\n\n // `value` fica FORA da observação: refleti-lo a cada tecla não pode\n // re-renderizar o template, senão o campo perde o foco ao digitar.\n // `error`/`success` (+ aliases `invalid`/`valid`) PRECISAM ser observados:\n // é assim que o toggle no playground / setAttribute no submit re-renderiza.\n static override get observedAttributes(): string[] {\n return [\n 'type', 'size', 'placeholder',\n 'disabled', 'readonly', 'min', 'max', 'step',\n 'icon', 'icon-pos', 'error', 'invalid', 'success', 'valid',\n ];\n }\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string) {\n this.setAttribute('value', value);\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n protected override render(): void {\n const type = this.getAttr('type', 'text');\n const placeholder = this.getAttr('placeholder');\n const readonly = this.hasAttr('readonly');\n const disabled = this.hasAttr('disabled');\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n const step = this.getAttr('step');\n const icon = this.getAttr('icon');\n const iconPos = this.getAttr('icon-pos', 'left') === 'right' ? 'right' : 'left';\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\" part=\"icon\"></slot>'\n : (icon ? fenixIconHtml(icon) : '');\n const useIconBox = Boolean(iconContent);\n\n this.setTemplate(`\n ${this.hasAttr('clearable') ? '<div class=\"wrap\" part=\"wrap\">' : ''}\n ${useIconBox ? `<div class=\"field-icon icon-${iconPos}\">` : ''}\n ${useIconBox && iconPos === 'left' ? iconContent : ''}\n <input class=\"field\" part=\"input\" type=\"${esc(type)}\"\n ${placeholder ? `placeholder=\"${esc(placeholder)}\"` : ''}\n ${min ? `min=\"${esc(min)}\"` : ''} ${max ? `max=\"${esc(max)}\"` : ''} ${step ? `step=\"${esc(step)}\"` : ''}\n />\n ${useIconBox && iconPos === 'right' ? iconContent : ''}\n ${useIconBox ? '</div>' : ''}\n ${this.hasAttr('clearable') ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\" tabindex=\"-1\">×</button></div>' : ''}\n `);\n\n const field = this.root.querySelector<HTMLInputElement>('.field');\n if (!field) return;\n field.value = this.getAttr('value');\n if (disabled) field.setAttribute('disabled', '');\n if (readonly) field.setAttribute('readonly', '');\n\n const emit = (event: string): void => {\n this.value = field.value;\n this.dispatchEvent(\n new CustomEvent(event, { bubbles: true, composed: true, detail: { value: field.value } }),\n );\n };\n // Bloqueia o evento nativo (composed, sem detail) para que só o\n // CustomEvent com detail chegue aos consumidores.\n field.addEventListener('input', (e) => { e.stopPropagation(); emit('input'); });\n field.addEventListener('change', (e) => { e.stopPropagation(); emit('change'); });\n\n // Clearable: botão × que limpa o campo e emite input/change vazios.\n const clearBtn = this.root.querySelector<HTMLButtonElement>('.clear');\n const syncClear = (): void => {\n clearBtn?.toggleAttribute('hidden', field.value === '');\n };\n syncClear();\n clearBtn?.addEventListener('click', () => {\n field.value = '';\n syncClear();\n emit('input');\n emit('change');\n field.focus();\n });\n field.addEventListener('input', syncClear);\n }\n}\n\nexport function defineFxInput(): typeof FxInput {\n return defineElement('fx-input', FxInput);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { FxElement } from '../../core/base';
|
|
|
2
2
|
/**
|
|
3
3
|
* <fx-knob> - Controle giratorio circular para ajuste de valor.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
5
|
+
* permite ajustar um valor arrastando o mouse
|
|
6
6
|
* ou usando as setas do teclado. Exibe um arco de progresso circular com
|
|
7
7
|
* o valor central configuravel via valueTemplate.
|
|
8
8
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knob.d.ts","sourceRoot":"","sources":["../../../src/components/knob/knob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;GAUG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"knob.d.ts","sourceRoot":"","sources":["../../../src/components/knob/knob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;;GAUG;AACH,qBAAa,MAAO,SAAQ,SAAS;IACnC,OAAgB,MAAM,SAoEpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAcjD;IAED,IAAI,KAAK,IAAI,MAAM,CAElB;IACD,IAAI,KAAK,CAAC,CAAC,EAAE,MAAM,EAclB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IACD,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,EAEhB;IAED,IAAI,GAAG,IAAI,MAAM,CAEhB;IACD,IAAI,GAAG,CAAC,CAAC,EAAE,MAAM,EAEhB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IACD,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAGjB;IACD,IAAI,IAAI,CAAC,CAAC,EAAE,MAAM,EAEjB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IACD,IAAI,WAAW,CAAC,CAAC,EAAE,MAAM,EAExB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IACD,IAAI,UAAU,CAAC,CAAC,EAAE,MAAM,EAEvB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IACD,IAAI,UAAU,CAAC,CAAC,EAAE,MAAM,EAEvB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,EAEtB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IACD,IAAI,QAAQ,CAAC,CAAC,EAAE,OAAO,EAEtB;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IACD,IAAI,aAAa,CAAC,CAAC,EAAE,MAAM,EAE1B;IAED,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,WAAW,CAAK;cAEL,MAAM,IAAI,IAAI;IAwCjC,OAAO,CAAC,UAAU;IAOlB,gEAAgE;IAChE,OAAO,CAAC,SAAS;IASjB,OAAO,CAAC,UAAU;IAYlB,OAAO,CAAC,UAAU;IAclB,OAAO,CAAC,eAAe;IAsEvB,OAAO,CAAC,YAAY,CAKlB;IAEF,OAAO,CAAC,UAAU,CAIhB;cAEiB,oBAAoB,IAAI,IAAI;CAKhD;AAED,wBAAgB,YAAY,IAAI,OAAO,MAAM,CAE5C"}
|
|
@@ -321,7 +321,9 @@ _FxKnob.styles = css`
|
|
|
321
321
|
outline: none;
|
|
322
322
|
}
|
|
323
323
|
.knob:focus-visible svg {
|
|
324
|
-
|
|
324
|
+
/* Mesmo fallback none dos demais componentes: sem motor de tema (ou com
|
|
325
|
+
effect.focus-ring desligado) não há anel de foco. */
|
|
326
|
+
filter: drop-shadow(var(--fx-effect-focus-ring, none));
|
|
325
327
|
}
|
|
326
328
|
`;
|
|
327
329
|
let FxKnob = _FxKnob;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"knob.js","sources":["../../../src/components/knob/knob.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-knob> - Controle giratorio circular para ajuste de valor.\n *\n * Similar ao Knob do PrimeVue, permite ajustar um valor arrastando o mouse\n * ou usando as setas do teclado. Exibe um arco de progresso circular com\n * o valor central configuravel via valueTemplate.\n *\n * Atributos: value, min, max, step, size, strokeWidth, valueColor, rangeColor,\n * readonly, disabled, valueTemplate.\n * Evento: `change` (composed, detail: { value }).\n */\nexport class FxKnob extends FxElement {\n static override styles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n --_size: 100px;\n --_stroke-width: 8px;\n --_value-color: var(--fx-color-primary);\n --_range-color: var(--fx-border-default);\n }\n :host([size='sm']) { --_size: 60px; }\n :host([size='lg']) { --_size: 140px; }\n .knob {\n position: relative;\n width: var(--_size);\n height: var(--_size);\n cursor: pointer;\n user-select: none;\n touch-action: none;\n }\n :host([readonly]) .knob,\n :host([disabled]) .knob {\n cursor: default;\n pointer-events: none;\n }\n :host([disabled]) .knob {\n opacity: 0.55;\n }\n svg {\n width: 100%;\n height: 100%;\n transform: rotate(-90deg);\n }\n .track {\n fill: none;\n stroke: var(--_range-color);\n stroke-width: var(--_stroke-width);\n }\n .value {\n fill: none;\n stroke: var(--_value-color);\n stroke-width: var(--_stroke-width);\n stroke-linecap: round;\n transition: stroke-dashoffset var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .label {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 600;\n color: var(--fx-text-default);\n pointer-events: none;\n }\n :host([size='sm']) .label { font-size: 11px; }\n :host([size='md']) .label { font-size: 14px; }\n :host([size='lg']) .label { font-size: 18px; }\n .knob:focus-visible {\n outline: none;\n }\n .knob:focus-visible svg {\n filter: drop-shadow(var(--fx-effect-focus-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-primary) 22%, transparent)));\n }\n `;\n\n static override get observedAttributes(): string[] {\n return [\n 'value',\n 'min',\n 'max',\n 'step',\n 'size',\n 'stroke-width',\n 'value-color',\n 'range-color',\n 'readonly',\n 'disabled',\n 'value-template',\n ];\n }\n\n get value(): number {\n return this.clampValue(Number(this.getAttr('value', '0')) || 0);\n }\n set value(v: number) {\n if (this.readonly || this.disabled) return;\n const clamped = this.clampValue(v);\n const current = this.clampValue(Number(this.getAttr('value', '0')) || 0);\n if (clamped !== current) {\n this.setAttribute('value', String(clamped));\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { value: clamped },\n }),\n );\n }\n }\n\n get min(): number {\n return Number(this.getAttr('min', '0')) || 0;\n }\n set min(v: number) {\n this.setAttribute('min', String(v));\n }\n\n get max(): number {\n return Number(this.getAttr('max', '100')) || 100;\n }\n set max(v: number) {\n this.setAttribute('max', String(v));\n }\n\n get step(): number {\n return Number(this.getAttr('step', '1')) || 1;\n }\n set step(v: number) {\n this.setAttribute('step', String(v));\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) {\n this.setAttribute('size', v);\n }\n\n get strokeWidth(): number {\n return Number(this.getAttr('stroke-width', '8')) || 8;\n }\n set strokeWidth(v: number) {\n this.setAttribute('stroke-width', String(v));\n }\n\n get valueColor(): string {\n return this.getAttr('value-color', '');\n }\n set valueColor(v: string) {\n this.setAttribute('value-color', v);\n }\n\n get rangeColor(): string {\n return this.getAttr('range-color', '');\n }\n set rangeColor(v: string) {\n this.setAttribute('range-color', v);\n }\n\n get readonly(): boolean {\n return this.hasAttr('readonly');\n }\n set readonly(v: boolean) {\n this.toggleAttr('readonly', Boolean(v));\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(v: boolean) {\n this.toggleAttr('disabled', Boolean(v));\n }\n\n get valueTemplate(): string {\n return this.getAttr('value-template', '{value}');\n }\n set valueTemplate(v: string) {\n this.setAttribute('value-template', v);\n }\n\n private _isDragging = false;\n private _startY = 0;\n private _startValue = 0;\n\n protected override render(): void {\n const min = this.min;\n const max = this.max;\n const value = this.clampValue(this.value);\n const range = max - min;\n const pct = range > 0 ? (value - min) / range : 0;\n\n const sizeNum = this.getSizeNum();\n const strokeW = this.strokeWidth;\n const radius = (sizeNum - strokeW) / 2;\n const circumference = 2 * Math.PI * radius;\n const offset = circumference * (1 - pct);\n\n const vc = this.valueColor ? this.safeColor(this.valueColor) : '';\n const rc = this.rangeColor ? this.safeColor(this.rangeColor) : '';\n const valueColorStyle = vc ? `--_value-color: ${vc};` : '';\n const rangeColorStyle = rc ? `--_range-color: ${rc};` : '';\n\n const displayValue = this.valueTemplate.replace('{value}', String(Math.round(value)));\n\n this.setTemplate(`\n <div class=\"knob\" part=\"knob\" tabindex=\"${this.disabled ? '-1' : '0'}\"\n role=\"slider\" aria-valuenow=\"${value}\" aria-valuemin=\"${min}\" aria-valuemax=\"${max}\"\n aria-readonly=\"${this.readonly}\" aria-disabled=\"${this.disabled}\"\n style=\"${valueColorStyle}${rangeColorStyle}\">\n <svg viewBox=\"0 0 ${sizeNum} ${sizeNum}\" part=\"svg\">\n <circle class=\"track\" part=\"track\"\n cx=\"${sizeNum / 2}\" cy=\"${sizeNum / 2}\" r=\"${radius}\" />\n <circle class=\"value\" part=\"value\"\n cx=\"${sizeNum / 2}\" cy=\"${sizeNum / 2}\" r=\"${radius}\"\n stroke-dasharray=\"${circumference}\"\n stroke-dashoffset=\"${offset}\" />\n </svg>\n <div class=\"label\" part=\"label\">${esc(displayValue)}</div>\n </div>\n `);\n\n this.attachListeners();\n }\n\n private getSizeNum(): number {\n const s = this.size;\n if (s === 'sm') return 60;\n if (s === 'lg') return 140;\n return 100;\n }\n\n /** Valida um valor de cor CSS, retornando-o seguro ou vazio. */\n private safeColor(value: string): string {\n const v = value.trim();\n if (/^#[0-9a-fA-F]{3,8}$/.test(v)) return v;\n if (/^(rgb|rgba|hsl|hsla)\\([\\d\\s.,%()]+\\)$/i.test(v)) return v;\n if (/^var\\(--[\\w-]+(,\\s*[^)]*)?\\)$/.test(v)) return v;\n if (/^[a-zA-Z]+$/.test(v)) return v;\n return '';\n }\n\n private clampValue(v: number): number {\n const min = this.min;\n const max = this.max;\n const step = this.step;\n let clamped = Math.min(max, Math.max(min, v));\n if (step > 0) {\n clamped = Math.round((clamped - min) / step) * step + min;\n clamped = Math.min(max, Math.max(min, clamped));\n }\n return clamped;\n }\n\n private emitChange(newValue: number): void {\n const clamped = this.clampValue(newValue);\n if (clamped !== this.value) {\n this.value = clamped;\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { value: clamped },\n }),\n );\n }\n }\n\n private attachListeners(): void {\n const knob = this.root.querySelector<HTMLElement>('.knob');\n if (!knob) return;\n\n knob.addEventListener('mousedown', (e: MouseEvent) => {\n if (this.readonly || this.disabled) return;\n e.preventDefault();\n this._isDragging = true;\n this._startY = e.clientY;\n this._startValue = this.value;\n knob.focus();\n document.addEventListener('mousemove', this._onMouseMove);\n document.addEventListener('mouseup', this._onMouseUp);\n });\n\n knob.addEventListener('touchstart', (e: TouchEvent) => {\n if (this.readonly || this.disabled) return;\n e.preventDefault();\n this._isDragging = true;\n this._startY = e.touches[0].clientY;\n this._startValue = this.value;\n knob.focus();\n }, { passive: false });\n\n knob.addEventListener('touchmove', (e: TouchEvent) => {\n if (!this._isDragging || this.readonly || this.disabled) return;\n e.preventDefault();\n const deltaY = this._startY - e.touches[0].clientY;\n const sensitivity = (this.max - this.min) / 100;\n this.emitChange(this._startValue + deltaY * sensitivity);\n }, { passive: false });\n\n knob.addEventListener('touchend', () => {\n this._isDragging = false;\n });\n\n knob.addEventListener('keydown', (e: KeyboardEvent) => {\n if (this.readonly || this.disabled) return;\n const step = this.step;\n switch (e.key) {\n case 'ArrowUp':\n case 'ArrowRight':\n e.preventDefault();\n this.emitChange(this.value + step);\n break;\n case 'ArrowDown':\n case 'ArrowLeft':\n e.preventDefault();\n this.emitChange(this.value - step);\n break;\n case 'Home':\n e.preventDefault();\n this.emitChange(this.min);\n break;\n case 'End':\n e.preventDefault();\n this.emitChange(this.max);\n break;\n case 'PageUp':\n e.preventDefault();\n this.emitChange(this.value + step * 10);\n break;\n case 'PageDown':\n e.preventDefault();\n this.emitChange(this.value - step * 10);\n break;\n }\n });\n }\n\n private _onMouseMove = (e: MouseEvent): void => {\n if (!this._isDragging) return;\n const deltaY = this._startY - e.clientY;\n const sensitivity = (this.max - this.min) / 100;\n this.emitChange(this._startValue + deltaY * sensitivity);\n };\n\n private _onMouseUp = (): void => {\n this._isDragging = false;\n document.removeEventListener('mousemove', this._onMouseMove);\n document.removeEventListener('mouseup', this._onMouseUp);\n };\n\n protected override disconnectedCallback(): void {\n document.removeEventListener('mousemove', this._onMouseMove);\n document.removeEventListener('mouseup', this._onMouseUp);\n super.disconnectedCallback();\n }\n}\n\nexport function defineFxKnob(): typeof FxKnob {\n return defineElement('fx-knob', FxKnob);\n}"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"knob.js","sources":["../../../src/components/knob/knob.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-knob> - Controle giratorio circular para ajuste de valor.\n *\n * permite ajustar um valor arrastando o mouse\n * ou usando as setas do teclado. Exibe um arco de progresso circular com\n * o valor central configuravel via valueTemplate.\n *\n * Atributos: value, min, max, step, size, strokeWidth, valueColor, rangeColor,\n * readonly, disabled, valueTemplate.\n * Evento: `change` (composed, detail: { value }).\n */\nexport class FxKnob extends FxElement {\n static override styles = css`\n :host {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n --_size: 100px;\n --_stroke-width: 8px;\n --_value-color: var(--fx-color-primary);\n --_range-color: var(--fx-border-default);\n }\n :host([size='sm']) { --_size: 60px; }\n :host([size='lg']) { --_size: 140px; }\n .knob {\n position: relative;\n width: var(--_size);\n height: var(--_size);\n cursor: pointer;\n user-select: none;\n touch-action: none;\n }\n :host([readonly]) .knob,\n :host([disabled]) .knob {\n cursor: default;\n pointer-events: none;\n }\n :host([disabled]) .knob {\n opacity: 0.55;\n }\n svg {\n width: 100%;\n height: 100%;\n transform: rotate(-90deg);\n }\n .track {\n fill: none;\n stroke: var(--_range-color);\n stroke-width: var(--_stroke-width);\n }\n .value {\n fill: none;\n stroke: var(--_value-color);\n stroke-width: var(--_stroke-width);\n stroke-linecap: round;\n transition: stroke-dashoffset var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .label {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n font-weight: 600;\n color: var(--fx-text-default);\n pointer-events: none;\n }\n :host([size='sm']) .label { font-size: 11px; }\n :host([size='md']) .label { font-size: 14px; }\n :host([size='lg']) .label { font-size: 18px; }\n .knob:focus-visible {\n outline: none;\n }\n .knob:focus-visible svg {\n /* Mesmo fallback none dos demais componentes: sem motor de tema (ou com\n effect.focus-ring desligado) não há anel de foco. */\n filter: drop-shadow(var(--fx-effect-focus-ring, none));\n }\n `;\n\n static override get observedAttributes(): string[] {\n return [\n 'value',\n 'min',\n 'max',\n 'step',\n 'size',\n 'stroke-width',\n 'value-color',\n 'range-color',\n 'readonly',\n 'disabled',\n 'value-template',\n ];\n }\n\n get value(): number {\n return this.clampValue(Number(this.getAttr('value', '0')) || 0);\n }\n set value(v: number) {\n if (this.readonly || this.disabled) return;\n const clamped = this.clampValue(v);\n const current = this.clampValue(Number(this.getAttr('value', '0')) || 0);\n if (clamped !== current) {\n this.setAttribute('value', String(clamped));\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { value: clamped },\n }),\n );\n }\n }\n\n get min(): number {\n return Number(this.getAttr('min', '0')) || 0;\n }\n set min(v: number) {\n this.setAttribute('min', String(v));\n }\n\n get max(): number {\n return Number(this.getAttr('max', '100')) || 100;\n }\n set max(v: number) {\n this.setAttribute('max', String(v));\n }\n\n get step(): number {\n return Number(this.getAttr('step', '1')) || 1;\n }\n set step(v: number) {\n this.setAttribute('step', String(v));\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) {\n this.setAttribute('size', v);\n }\n\n get strokeWidth(): number {\n return Number(this.getAttr('stroke-width', '8')) || 8;\n }\n set strokeWidth(v: number) {\n this.setAttribute('stroke-width', String(v));\n }\n\n get valueColor(): string {\n return this.getAttr('value-color', '');\n }\n set valueColor(v: string) {\n this.setAttribute('value-color', v);\n }\n\n get rangeColor(): string {\n return this.getAttr('range-color', '');\n }\n set rangeColor(v: string) {\n this.setAttribute('range-color', v);\n }\n\n get readonly(): boolean {\n return this.hasAttr('readonly');\n }\n set readonly(v: boolean) {\n this.toggleAttr('readonly', Boolean(v));\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(v: boolean) {\n this.toggleAttr('disabled', Boolean(v));\n }\n\n get valueTemplate(): string {\n return this.getAttr('value-template', '{value}');\n }\n set valueTemplate(v: string) {\n this.setAttribute('value-template', v);\n }\n\n private _isDragging = false;\n private _startY = 0;\n private _startValue = 0;\n\n protected override render(): void {\n const min = this.min;\n const max = this.max;\n const value = this.clampValue(this.value);\n const range = max - min;\n const pct = range > 0 ? (value - min) / range : 0;\n\n const sizeNum = this.getSizeNum();\n const strokeW = this.strokeWidth;\n const radius = (sizeNum - strokeW) / 2;\n const circumference = 2 * Math.PI * radius;\n const offset = circumference * (1 - pct);\n\n const vc = this.valueColor ? this.safeColor(this.valueColor) : '';\n const rc = this.rangeColor ? this.safeColor(this.rangeColor) : '';\n const valueColorStyle = vc ? `--_value-color: ${vc};` : '';\n const rangeColorStyle = rc ? `--_range-color: ${rc};` : '';\n\n const displayValue = this.valueTemplate.replace('{value}', String(Math.round(value)));\n\n this.setTemplate(`\n <div class=\"knob\" part=\"knob\" tabindex=\"${this.disabled ? '-1' : '0'}\"\n role=\"slider\" aria-valuenow=\"${value}\" aria-valuemin=\"${min}\" aria-valuemax=\"${max}\"\n aria-readonly=\"${this.readonly}\" aria-disabled=\"${this.disabled}\"\n style=\"${valueColorStyle}${rangeColorStyle}\">\n <svg viewBox=\"0 0 ${sizeNum} ${sizeNum}\" part=\"svg\">\n <circle class=\"track\" part=\"track\"\n cx=\"${sizeNum / 2}\" cy=\"${sizeNum / 2}\" r=\"${radius}\" />\n <circle class=\"value\" part=\"value\"\n cx=\"${sizeNum / 2}\" cy=\"${sizeNum / 2}\" r=\"${radius}\"\n stroke-dasharray=\"${circumference}\"\n stroke-dashoffset=\"${offset}\" />\n </svg>\n <div class=\"label\" part=\"label\">${esc(displayValue)}</div>\n </div>\n `);\n\n this.attachListeners();\n }\n\n private getSizeNum(): number {\n const s = this.size;\n if (s === 'sm') return 60;\n if (s === 'lg') return 140;\n return 100;\n }\n\n /** Valida um valor de cor CSS, retornando-o seguro ou vazio. */\n private safeColor(value: string): string {\n const v = value.trim();\n if (/^#[0-9a-fA-F]{3,8}$/.test(v)) return v;\n if (/^(rgb|rgba|hsl|hsla)\\([\\d\\s.,%()]+\\)$/i.test(v)) return v;\n if (/^var\\(--[\\w-]+(,\\s*[^)]*)?\\)$/.test(v)) return v;\n if (/^[a-zA-Z]+$/.test(v)) return v;\n return '';\n }\n\n private clampValue(v: number): number {\n const min = this.min;\n const max = this.max;\n const step = this.step;\n let clamped = Math.min(max, Math.max(min, v));\n if (step > 0) {\n clamped = Math.round((clamped - min) / step) * step + min;\n clamped = Math.min(max, Math.max(min, clamped));\n }\n return clamped;\n }\n\n private emitChange(newValue: number): void {\n const clamped = this.clampValue(newValue);\n if (clamped !== this.value) {\n this.value = clamped;\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { value: clamped },\n }),\n );\n }\n }\n\n private attachListeners(): void {\n const knob = this.root.querySelector<HTMLElement>('.knob');\n if (!knob) return;\n\n knob.addEventListener('mousedown', (e: MouseEvent) => {\n if (this.readonly || this.disabled) return;\n e.preventDefault();\n this._isDragging = true;\n this._startY = e.clientY;\n this._startValue = this.value;\n knob.focus();\n document.addEventListener('mousemove', this._onMouseMove);\n document.addEventListener('mouseup', this._onMouseUp);\n });\n\n knob.addEventListener('touchstart', (e: TouchEvent) => {\n if (this.readonly || this.disabled) return;\n e.preventDefault();\n this._isDragging = true;\n this._startY = e.touches[0].clientY;\n this._startValue = this.value;\n knob.focus();\n }, { passive: false });\n\n knob.addEventListener('touchmove', (e: TouchEvent) => {\n if (!this._isDragging || this.readonly || this.disabled) return;\n e.preventDefault();\n const deltaY = this._startY - e.touches[0].clientY;\n const sensitivity = (this.max - this.min) / 100;\n this.emitChange(this._startValue + deltaY * sensitivity);\n }, { passive: false });\n\n knob.addEventListener('touchend', () => {\n this._isDragging = false;\n });\n\n knob.addEventListener('keydown', (e: KeyboardEvent) => {\n if (this.readonly || this.disabled) return;\n const step = this.step;\n switch (e.key) {\n case 'ArrowUp':\n case 'ArrowRight':\n e.preventDefault();\n this.emitChange(this.value + step);\n break;\n case 'ArrowDown':\n case 'ArrowLeft':\n e.preventDefault();\n this.emitChange(this.value - step);\n break;\n case 'Home':\n e.preventDefault();\n this.emitChange(this.min);\n break;\n case 'End':\n e.preventDefault();\n this.emitChange(this.max);\n break;\n case 'PageUp':\n e.preventDefault();\n this.emitChange(this.value + step * 10);\n break;\n case 'PageDown':\n e.preventDefault();\n this.emitChange(this.value - step * 10);\n break;\n }\n });\n }\n\n private _onMouseMove = (e: MouseEvent): void => {\n if (!this._isDragging) return;\n const deltaY = this._startY - e.clientY;\n const sensitivity = (this.max - this.min) / 100;\n this.emitChange(this._startValue + deltaY * sensitivity);\n };\n\n private _onMouseUp = (): void => {\n this._isDragging = false;\n document.removeEventListener('mousemove', this._onMouseMove);\n document.removeEventListener('mouseup', this._onMouseUp);\n };\n\n protected override disconnectedCallback(): void {\n document.removeEventListener('mousemove', this._onMouseMove);\n document.removeEventListener('mouseup', this._onMouseUp);\n super.disconnectedCallback();\n }\n}\n\nexport function defineFxKnob(): typeof FxKnob {\n return defineElement('fx-knob', FxKnob);\n}"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiselect.d.ts","sourceRoot":"","sources":["../../../src/components/multiselect/multiselect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAC1C,OAAgB,MAAM,
|
|
1
|
+
{"version":3,"file":"multiselect.d.ts","sourceRoot":"","sources":["../../../src/components/multiselect/multiselect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C;;;;;;;;;GASG;AACH,qBAAa,aAAc,SAAQ,SAAS;IAC1C,OAAgB,MAAM,SAqOpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAIjD;IAED,OAAO,CAAC,QAAQ,CAAC,CAAmB;IACpC,OAAO,CAAC,WAAW,CAAC,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAqB;IAErC,qEAAqE;IACrE,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;IAED,OAAO,KAAK,OAAO,GAKlB;cAEkB,iBAAiB,IAAI,IAAI;cAezB,oBAAoB,IAAI,IAAI;IAM/C,gDAAgD;IAChD,OAAO,CAAC,MAAM;IAYd,OAAO,CAAC,WAAW;IAMnB,qEAAqE;IACrE,OAAO,CAAC,gBAAgB;cAiBL,MAAM,IAAI,IAAI;CAwIlC;AAED,wBAAgB,mBAAmB,IAAI,OAAO,aAAa,CAE1D"}
|
|
@@ -214,6 +214,8 @@ _FxMultiselect.styles = css`
|
|
|
214
214
|
min-height: var(--fx-size-md);
|
|
215
215
|
padding: var(--fx-space-xs) var(--fx-space-md);
|
|
216
216
|
font: inherit;
|
|
217
|
+
/* Altura determinística: a caixa fica no token --fx-size-*. */
|
|
218
|
+
line-height: var(--fx-font-line-height);
|
|
217
219
|
color: var(--fx-text-default);
|
|
218
220
|
background-color: var(--fx-surface-background);
|
|
219
221
|
border: 1px solid var(--fx-border-default);
|
|
@@ -239,28 +241,31 @@ _FxMultiselect.styles = css`
|
|
|
239
241
|
/* Full width: o host estica até o pai e o trigger acompanha. */
|
|
240
242
|
:host([full]) { display: block; width: 100%; }
|
|
241
243
|
:host([full]) .trigger { width: 100%; }
|
|
242
|
-
/* Validação
|
|
244
|
+
/* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o brilho
|
|
245
|
+
segue o token --fx-effect-error-ring, que acompanha effect.focus-ring. */
|
|
243
246
|
:host([error]) .trigger,
|
|
244
247
|
:host([invalid]) .trigger {
|
|
245
248
|
border-color: var(--fx-color-danger, #dc2626);
|
|
249
|
+
box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
|
|
246
250
|
}
|
|
247
251
|
:host([error]) .trigger:focus-visible,
|
|
248
252
|
:host([invalid]) .trigger:focus-visible,
|
|
249
253
|
:host([error][open]) .trigger,
|
|
250
254
|
:host([invalid][open]) .trigger {
|
|
251
255
|
border-color: var(--fx-color-danger, #dc2626);
|
|
252
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent);
|
|
256
|
+
box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));
|
|
253
257
|
}
|
|
254
258
|
:host([success]) .trigger,
|
|
255
259
|
:host([valid]) .trigger {
|
|
256
260
|
border-color: var(--fx-color-success, #16a34a);
|
|
261
|
+
box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
|
|
257
262
|
}
|
|
258
263
|
:host([success]) .trigger:focus-visible,
|
|
259
264
|
:host([valid]) .trigger:focus-visible,
|
|
260
265
|
:host([success][open]) .trigger,
|
|
261
266
|
:host([valid][open]) .trigger {
|
|
262
267
|
border-color: var(--fx-color-success, #16a34a);
|
|
263
|
-
box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent);
|
|
268
|
+
box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));
|
|
264
269
|
}
|
|
265
270
|
.trigger:hover { border-color: var(--fx-border-hover); }
|
|
266
271
|
.trigger:focus-visible {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiselect.js","sources":["../../../src/components/multiselect/multiselect.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-multiselect> — Seleção múltipla .\n *\n * Dropdown customizado com painel (overlay), checkbox por item, pesquisa\n * opcional e ação de limpar. Opções via <option> nativos no light DOM.\n *\n * Atributos: values (CSV), searchable, clearable, disabled, placeholder,\n * size (sm|md|lg), no-results.\n * Evento: `change` (composed, detail: { values: string[] }).\n */\nexport class FxMultiselect extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n /* Largura 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-multiselect-width, 240px);\n }\n .trigger {\n display: inline-flex;\n align-items: center;\n flex-wrap: nowrap;\n gap: var(--fx-space-xs);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n box-sizing: border-box;\n min-height: var(--fx-size-md);\n padding: var(--fx-space-xs) var(--fx-space-md);\n font: inherit;\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 cursor: pointer;\n text-align: left;\n transition: border-color var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n /* Área dos chips: encolhe e quebra linha internamente,\n mantendo os ícones sempre dentro do componente. */\n .lead {\n display: inline-flex;\n flex-wrap: wrap;\n gap: var(--fx-space-xs);\n flex: 1;\n min-width: 0;\n overflow: hidden;\n }\n :host([size='sm']) { width: var(--fx-multiselect-width-sm, 200px); }\n :host([size='sm']) .trigger { min-height: var(--fx-size-sm); }\n :host([size='lg']) { width: var(--fx-multiselect-width-lg, 280px); }\n :host([size='lg']) .trigger { min-height: var(--fx-size-lg); font-size: calc(var(--fx-font-size) + 4px); }\n /* Full width: o host estica até o pai e o trigger acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .trigger { width: 100%; }\n /* Validação */\n :host([error]) .trigger,\n :host([invalid]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n }\n :host([error]) .trigger:focus-visible,\n :host([invalid]) .trigger:focus-visible,\n :host([error][open]) .trigger,\n :host([invalid][open]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent);\n }\n :host([success]) .trigger,\n :host([valid]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n }\n :host([success]) .trigger:focus-visible,\n :host([valid]) .trigger:focus-visible,\n :host([success][open]) .trigger,\n :host([valid][open]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent);\n }\n .trigger:hover { border-color: var(--fx-border-hover); }\n .trigger:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .trigger[aria-expanded='true'] { border-color: var(--fx-color-primary); }\n .placeholder { color: var(--fx-text-muted); }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n background: color-mix(in srgb, var(--fx-color-primary) 14%, transparent);\n color: var(--fx-color-primary);\n border-radius: var(--fx-radius-sm);\n padding: 2px 6px;\n font-size: calc(var(--fx-font-size) - 2px);\n }\n .chip__x {\n border: none;\n background: none;\n color: inherit;\n cursor: pointer;\n font-size: calc(var(--fx-font-size) - 1px);\n line-height: 1;\n padding: 0;\n }\n .trigger-right {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--fx-text-muted);\n }\n .icon-btn {\n border: none;\n background: none;\n color: inherit;\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n padding: 0 2px;\n border-radius: var(--fx-radius-full);\n }\n .icon-btn:hover { color: var(--fx-color-danger); }\n .caret {\n font-size: calc(var(--fx-font-size) - 3px);\n transition: transform var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n :host([open]) .caret { transform: rotate(180deg); }\n\n /* Painel (overlay ) */\n .panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n width: max(100%, 240px);\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 display: none;\n overflow: hidden;\n }\n :host([up]) .panel {\n top: auto;\n bottom: calc(100% + 4px);\n }\n :host([open]) .panel { display: block; }\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-space-sm);\n padding: var(--fx-space-xs) var(--fx-space-md);\n border-bottom: 1px solid var(--fx-border-default);\n background: var(--fx-surface-surface-hover);\n }\n .count { font-size: calc(var(--fx-font-size) - 2px); color: var(--fx-text-muted); }\n .clear-all {\n border: none;\n background: none;\n font: inherit;\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-color-primary);\n cursor: pointer;\n padding: 0;\n }\n .clear-all:hover { text-decoration: underline; }\n .search {\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: none;\n border-bottom: 1px solid var(--fx-border-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n outline: none;\n }\n .list {\n max-height: 240px;\n overflow-y: auto;\n list-style: none;\n margin: 0;\n padding: var(--fx-space-3xs, 4px);\n }\n .opt {\n display: flex;\n align-items: center;\n gap: var(--fx-space-sm);\n width: 100%;\n padding: var(--fx-space-xs) var(--fx-space-md);\n border: none;\n background: none;\n font: inherit;\n color: var(--fx-text-default);\n cursor: pointer;\n border-radius: var(--fx-radius-sm);\n text-align: left;\n }\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, transparent); }\n .opt[aria-selected='true'] {\n background: color-mix(in srgb, var(--fx-color-primary) 16%, transparent);\n color: var(--fx-color-primary);\n font-weight: var(--fx-font-weight);\n }\n /* Checkbox visual */\n .box {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--fx-border-default);\n border-radius: calc(var(--fx-radius-sm) / 2);\n font-size: 11px;\n line-height: 1;\n color: transparent;\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 color var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .opt[aria-selected='true'] .box {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n color: #fff;\n }\n .opt[aria-selected='true'] .box::after { content: '✓'; }\n .empty { padding: var(--fx-space-md); color: var(--fx-text-muted); text-align: center; }\n :host([disabled]) .trigger { opacity: 0.55; cursor: not-allowed; background: var(--fx-surface-surface-hover); }\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 ['disabled', 'placeholder', 'error', 'invalid', 'success', 'valid'];\n }\n\n private observer?: MutationObserver;\n private docListener?: (e: Event) => void;\n private selected = new Set<string>();\n\n /** Valores selecionados (CSV no atributo / array na propriedade). */\n get values(): string[] {\n const csv = this.getAttr('values');\n return csv ? csv.split(',').map((v) => v.trim()).filter(Boolean) : [];\n }\n set values(list: string[]) {\n this.setAttribute('values', list.join(','));\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n private get options(): { value: string; label: string }[] {\n return [...this.querySelectorAll('option')].map((o) => ({\n value: o.getAttribute('value') ?? o.textContent?.trim() ?? '',\n label: o.textContent?.trim() ?? '',\n }));\n }\n\n protected override connectedCallback(): void {\n // Popula a seleção ANTES do primeiro render (super chama render).\n this.selected = new Set(this.values);\n super.connectedCallback();\n this.observer = new MutationObserver(() => this.render());\n this.observer.observe(this, { childList: true, subtree: true, characterData: true });\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this.removeAttribute('open');\n this.render();\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.observer?.disconnect();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n /** Aplica a seleção, reflete e emite change. */\n private commit(): void {\n this.values = [...this.selected];\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { values: [...this.selected] },\n }),\n );\n }\n\n private toggleValue(value: string): void {\n if (this.selected.has(value)) this.selected.delete(value);\n else this.selected.add(value);\n this.commit();\n }\n\n /** Navegação por teclado entre as opções do listbox (WCAG 2.1.1). */\n private _navigateOptions(e: KeyboardEvent): void {\n e.preventDefault();\n if (!this.hasAttr('open')) {\n this.toggleAttribute('open');\n this.render();\n }\n const opts = Array.from(this.root.querySelectorAll<HTMLButtonElement>('.opt'));\n if (!opts.length) return;\n const current = opts.indexOf(this.root.activeElement as HTMLButtonElement);\n let next = 0;\n if (e.key === 'ArrowDown') next = current === -1 ? 0 : Math.min(current + 1, opts.length - 1);\n else if (e.key === 'ArrowUp') next = current === -1 ? opts.length - 1 : Math.max(current - 1, 0);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = opts.length - 1;\n opts[next]?.focus();\n }\n\n protected override render(): void {\n const wasOpen = this.hasAttr('open');\n // Preserva o estado do campo de pesquisa entre re-renders.\n const search = this.root.querySelector<HTMLInputElement>('.search')?.value ?? '';\n\n const opts = this.options;\n for (const v of [...this.selected]) {\n if (!opts.some((o) => o.value === v)) this.selected.delete(v);\n }\n\n const chips = opts.filter((o) => this.selected.has(o.value));\n const placeholder = this.getAttr('placeholder', 'Selecione…');\n const filtered = search\n ? opts.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))\n : opts;\n\n this.setTemplate(`\n <span class=\"trigger\" part=\"trigger\" role=\"button\" tabindex=\"${this.disabled ? -1 : 0}\" aria-haspopup=\"listbox\" aria-expanded=\"${wasOpen}\" aria-disabled=\"${this.disabled}\">\n <span class=\"lead\">\n ${\n chips.length\n ? chips.map((c) => `\n <span class=\"chip\" data-value=\"${esc(c.value)}\">\n ${esc(c.label)}\n <span class=\"chip__x\" role=\"button\" aria-label=\"Remover ${esc(c.label)}\">×</span>\n </span>`).join('')\n : `<span class=\"placeholder\">${esc(placeholder)}</span>`\n }\n </span>\n <span class=\"trigger-right\">\n ${this.hasAttr('clearable') && chips.length ? '<button type=\"button\" class=\"icon-btn clear\" part=\"clear\" aria-label=\"Limpar seleção\">×</button>' : ''}\n <span class=\"caret\">▼</span>\n </span>\n </span>\n <div class=\"panel\" part=\"panel\" role=\"listbox\" aria-multiselectable=\"true\">\n <div class=\"panel-header\">\n <span class=\"count\">${chips.length} selecionado${chips.length === 1 ? '' : 's'}</span>\n ${this.hasAttr('clearable') && chips.length ? '<button type=\"button\" class=\"clear-all\">Limpar tudo</button>' : ''}\n </div>\n ${this.hasAttr('searchable') ? `<input class=\"search\" part=\"search\" type=\"text\" placeholder=\"${esc(this.getAttr('search-placeholder', 'Pesquisar…'))}\">` : ''}\n <ul class=\"list\">\n ${filtered.map((o) => `\n <li><button type=\"button\" class=\"opt\" role=\"option\" data-value=\"${esc(o.value)}\"\n aria-selected=\"${this.selected.has(o.value)}\">\n <span class=\"box\" aria-hidden=\"true\"></span>${esc(o.label)}\n </button></li>`).join('')}\n ${filtered.length === 0 ? `<li class=\"empty\">${esc(this.getAttr('no-results', 'Nenhum resultado'))}</li>` : ''}\n </ul>\n </div>\n `);\n\n // Reabre o painel se estava aberto antes do re-render.\n if (wasOpen) this.setAttribute('open', '');\n\n const searchInput = this.root.querySelector<HTMLInputElement>('.search');\n if (searchInput) {\n searchInput.value = search;\n // Preserva foco e posição do cursor entre os re-renders da digitação.\n searchInput.addEventListener('input', () => {\n const pos = searchInput.selectionStart ?? searchInput.value.length;\n this.render();\n const si = this.root.querySelector<HTMLInputElement>('.search');\n if (si) {\n si.focus();\n si.setSelectionRange(pos, pos);\n }\n });\n searchInput.addEventListener('click', (e) => e.stopPropagation());\n }\n\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\n if (!trigger) return;\n\n trigger.addEventListener('click', (e) => {\n if (this.disabled) return;\n if ((e.target as HTMLElement).closest('.clear')) return;\n if ((e.target as HTMLElement).closest('.chip__x')) return;\n this.toggleAttribute('open');\n // Flip vertical: se não cabe abaixo, abre para cima.\n if (this.hasAttr('open')) {\n const r = this.getBoundingClientRect();\n this.toggleAttribute('up', r.bottom + 300 > window.innerHeight && r.top > 300);\n } else {\n this.removeAttribute('up');\n }\n this.render();\n });\n\n // Acessibilidade: Enter/Espaço abrem o painel (span role=button); setas navegam.\n trigger.addEventListener('keydown', (e) => {\n if (this.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n trigger.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n if (e.key === 'Escape') {\n if (this.hasAttr('open')) {\n this.removeAttribute('open');\n this.render();\n this.root.querySelector<HTMLElement>('.trigger')?.focus();\n }\n return;\n }\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n this._navigateOptions(e);\n }\n });\n\n // × de limpar tudo no trigger.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.selected.clear();\n this.commit();\n });\n\n // \"Limpar tudo\" no cabeçalho do painel.\n this.root.querySelector('.clear-all')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.selected.clear();\n this.commit();\n });\n\n // Remover chip individual.\n this.root.querySelectorAll<HTMLElement>('.chip').forEach((chip) => {\n chip.querySelector('.chip__x')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.toggleValue(chip.dataset.value!);\n });\n });\n\n // Alternar opções.\n this.root.querySelectorAll<HTMLElement>('.opt').forEach((opt) => {\n opt.addEventListener('click', () => this.toggleValue(opt.dataset.value!));\n });\n }\n}\n\nexport function defineFxMultiselect(): typeof FxMultiselect {\n return defineElement('fx-multiselect', FxMultiselect);\n}\n\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multiselect.js","sources":["../../../src/components/multiselect/multiselect.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-multiselect> — Seleção múltipla .\n *\n * Dropdown customizado com painel (overlay), checkbox por item, pesquisa\n * opcional e ação de limpar. Opções via <option> nativos no light DOM.\n *\n * Atributos: values (CSV), searchable, clearable, disabled, placeholder,\n * size (sm|md|lg), no-results.\n * Evento: `change` (composed, detail: { values: string[] }).\n */\nexport class FxMultiselect extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n /* Largura 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-multiselect-width, 240px);\n }\n .trigger {\n display: inline-flex;\n align-items: center;\n flex-wrap: nowrap;\n gap: var(--fx-space-xs);\n /* Acompanha a largura definida no :host. */\n width: 100%;\n box-sizing: border-box;\n min-height: var(--fx-size-md);\n padding: var(--fx-space-xs) var(--fx-space-md);\n font: inherit;\n /* Altura determinística: a caixa fica no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\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 cursor: pointer;\n text-align: left;\n transition: border-color var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n /* Área dos chips: encolhe e quebra linha internamente,\n mantendo os ícones sempre dentro do componente. */\n .lead {\n display: inline-flex;\n flex-wrap: wrap;\n gap: var(--fx-space-xs);\n flex: 1;\n min-width: 0;\n overflow: hidden;\n }\n :host([size='sm']) { width: var(--fx-multiselect-width-sm, 200px); }\n :host([size='sm']) .trigger { min-height: var(--fx-size-sm); }\n :host([size='lg']) { width: var(--fx-multiselect-width-lg, 280px); }\n :host([size='lg']) .trigger { min-height: var(--fx-size-lg); font-size: calc(var(--fx-font-size) + 4px); }\n /* Full width: o host estica até o pai e o trigger acompanha. */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .trigger { width: 100%; }\n /* Validação — borda e brilho JÁ EM REPOUSO (igual ao fx-input); o brilho\n segue o token --fx-effect-error-ring, que acompanha effect.focus-ring. */\n :host([error]) .trigger,\n :host([invalid]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([error]) .trigger:focus-visible,\n :host([invalid]) .trigger:focus-visible,\n :host([error][open]) .trigger,\n :host([invalid][open]) .trigger {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .trigger,\n :host([valid]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([success]) .trigger:focus-visible,\n :host([valid]) .trigger:focus-visible,\n :host([success][open]) .trigger,\n :host([valid][open]) .trigger {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n .trigger:hover { border-color: var(--fx-border-hover); }\n .trigger:focus-visible {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .trigger[aria-expanded='true'] { border-color: var(--fx-color-primary); }\n .placeholder { color: var(--fx-text-muted); }\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n background: color-mix(in srgb, var(--fx-color-primary) 14%, transparent);\n color: var(--fx-color-primary);\n border-radius: var(--fx-radius-sm);\n padding: 2px 6px;\n font-size: calc(var(--fx-font-size) - 2px);\n }\n .chip__x {\n border: none;\n background: none;\n color: inherit;\n cursor: pointer;\n font-size: calc(var(--fx-font-size) - 1px);\n line-height: 1;\n padding: 0;\n }\n .trigger-right {\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--fx-text-muted);\n }\n .icon-btn {\n border: none;\n background: none;\n color: inherit;\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n padding: 0 2px;\n border-radius: var(--fx-radius-full);\n }\n .icon-btn:hover { color: var(--fx-color-danger); }\n .caret {\n font-size: calc(var(--fx-font-size) - 3px);\n transition: transform var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n :host([open]) .caret { transform: rotate(180deg); }\n\n /* Painel (overlay ) */\n .panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n width: max(100%, 240px);\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 display: none;\n overflow: hidden;\n }\n :host([up]) .panel {\n top: auto;\n bottom: calc(100% + 4px);\n }\n :host([open]) .panel { display: block; }\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-space-sm);\n padding: var(--fx-space-xs) var(--fx-space-md);\n border-bottom: 1px solid var(--fx-border-default);\n background: var(--fx-surface-surface-hover);\n }\n .count { font-size: calc(var(--fx-font-size) - 2px); color: var(--fx-text-muted); }\n .clear-all {\n border: none;\n background: none;\n font: inherit;\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-color-primary);\n cursor: pointer;\n padding: 0;\n }\n .clear-all:hover { text-decoration: underline; }\n .search {\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: none;\n border-bottom: 1px solid var(--fx-border-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n outline: none;\n }\n .list {\n max-height: 240px;\n overflow-y: auto;\n list-style: none;\n margin: 0;\n padding: var(--fx-space-3xs, 4px);\n }\n .opt {\n display: flex;\n align-items: center;\n gap: var(--fx-space-sm);\n width: 100%;\n padding: var(--fx-space-xs) var(--fx-space-md);\n border: none;\n background: none;\n font: inherit;\n color: var(--fx-text-default);\n cursor: pointer;\n border-radius: var(--fx-radius-sm);\n text-align: left;\n }\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, transparent); }\n .opt[aria-selected='true'] {\n background: color-mix(in srgb, var(--fx-color-primary) 16%, transparent);\n color: var(--fx-color-primary);\n font-weight: var(--fx-font-weight);\n }\n /* Checkbox visual */\n .box {\n width: 16px;\n height: 16px;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--fx-border-default);\n border-radius: calc(var(--fx-radius-sm) / 2);\n font-size: 11px;\n line-height: 1;\n color: transparent;\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 color var(--fx-motion-duration-fast) var(--fx-motion-easing);\n }\n .opt[aria-selected='true'] .box {\n background: var(--fx-color-primary);\n border-color: var(--fx-color-primary);\n color: #fff;\n }\n .opt[aria-selected='true'] .box::after { content: '✓'; }\n .empty { padding: var(--fx-space-md); color: var(--fx-text-muted); text-align: center; }\n :host([disabled]) .trigger { opacity: 0.55; cursor: not-allowed; background: var(--fx-surface-surface-hover); }\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 ['disabled', 'placeholder', 'error', 'invalid', 'success', 'valid'];\n }\n\n private observer?: MutationObserver;\n private docListener?: (e: Event) => void;\n private selected = new Set<string>();\n\n /** Valores selecionados (CSV no atributo / array na propriedade). */\n get values(): string[] {\n const csv = this.getAttr('values');\n return csv ? csv.split(',').map((v) => v.trim()).filter(Boolean) : [];\n }\n set values(list: string[]) {\n this.setAttribute('values', list.join(','));\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n private get options(): { value: string; label: string }[] {\n return [...this.querySelectorAll('option')].map((o) => ({\n value: o.getAttribute('value') ?? o.textContent?.trim() ?? '',\n label: o.textContent?.trim() ?? '',\n }));\n }\n\n protected override connectedCallback(): void {\n // Popula a seleção ANTES do primeiro render (super chama render).\n this.selected = new Set(this.values);\n super.connectedCallback();\n this.observer = new MutationObserver(() => this.render());\n this.observer.observe(this, { childList: true, subtree: true, characterData: true });\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this.removeAttribute('open');\n this.render();\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.observer?.disconnect();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n /** Aplica a seleção, reflete e emite change. */\n private commit(): void {\n this.values = [...this.selected];\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', {\n bubbles: true,\n composed: true,\n detail: { values: [...this.selected] },\n }),\n );\n }\n\n private toggleValue(value: string): void {\n if (this.selected.has(value)) this.selected.delete(value);\n else this.selected.add(value);\n this.commit();\n }\n\n /** Navegação por teclado entre as opções do listbox (WCAG 2.1.1). */\n private _navigateOptions(e: KeyboardEvent): void {\n e.preventDefault();\n if (!this.hasAttr('open')) {\n this.toggleAttribute('open');\n this.render();\n }\n const opts = Array.from(this.root.querySelectorAll<HTMLButtonElement>('.opt'));\n if (!opts.length) return;\n const current = opts.indexOf(this.root.activeElement as HTMLButtonElement);\n let next = 0;\n if (e.key === 'ArrowDown') next = current === -1 ? 0 : Math.min(current + 1, opts.length - 1);\n else if (e.key === 'ArrowUp') next = current === -1 ? opts.length - 1 : Math.max(current - 1, 0);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = opts.length - 1;\n opts[next]?.focus();\n }\n\n protected override render(): void {\n const wasOpen = this.hasAttr('open');\n // Preserva o estado do campo de pesquisa entre re-renders.\n const search = this.root.querySelector<HTMLInputElement>('.search')?.value ?? '';\n\n const opts = this.options;\n for (const v of [...this.selected]) {\n if (!opts.some((o) => o.value === v)) this.selected.delete(v);\n }\n\n const chips = opts.filter((o) => this.selected.has(o.value));\n const placeholder = this.getAttr('placeholder', 'Selecione…');\n const filtered = search\n ? opts.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))\n : opts;\n\n this.setTemplate(`\n <span class=\"trigger\" part=\"trigger\" role=\"button\" tabindex=\"${this.disabled ? -1 : 0}\" aria-haspopup=\"listbox\" aria-expanded=\"${wasOpen}\" aria-disabled=\"${this.disabled}\">\n <span class=\"lead\">\n ${\n chips.length\n ? chips.map((c) => `\n <span class=\"chip\" data-value=\"${esc(c.value)}\">\n ${esc(c.label)}\n <span class=\"chip__x\" role=\"button\" aria-label=\"Remover ${esc(c.label)}\">×</span>\n </span>`).join('')\n : `<span class=\"placeholder\">${esc(placeholder)}</span>`\n }\n </span>\n <span class=\"trigger-right\">\n ${this.hasAttr('clearable') && chips.length ? '<button type=\"button\" class=\"icon-btn clear\" part=\"clear\" aria-label=\"Limpar seleção\">×</button>' : ''}\n <span class=\"caret\">▼</span>\n </span>\n </span>\n <div class=\"panel\" part=\"panel\" role=\"listbox\" aria-multiselectable=\"true\">\n <div class=\"panel-header\">\n <span class=\"count\">${chips.length} selecionado${chips.length === 1 ? '' : 's'}</span>\n ${this.hasAttr('clearable') && chips.length ? '<button type=\"button\" class=\"clear-all\">Limpar tudo</button>' : ''}\n </div>\n ${this.hasAttr('searchable') ? `<input class=\"search\" part=\"search\" type=\"text\" placeholder=\"${esc(this.getAttr('search-placeholder', 'Pesquisar…'))}\">` : ''}\n <ul class=\"list\">\n ${filtered.map((o) => `\n <li><button type=\"button\" class=\"opt\" role=\"option\" data-value=\"${esc(o.value)}\"\n aria-selected=\"${this.selected.has(o.value)}\">\n <span class=\"box\" aria-hidden=\"true\"></span>${esc(o.label)}\n </button></li>`).join('')}\n ${filtered.length === 0 ? `<li class=\"empty\">${esc(this.getAttr('no-results', 'Nenhum resultado'))}</li>` : ''}\n </ul>\n </div>\n `);\n\n // Reabre o painel se estava aberto antes do re-render.\n if (wasOpen) this.setAttribute('open', '');\n\n const searchInput = this.root.querySelector<HTMLInputElement>('.search');\n if (searchInput) {\n searchInput.value = search;\n // Preserva foco e posição do cursor entre os re-renders da digitação.\n searchInput.addEventListener('input', () => {\n const pos = searchInput.selectionStart ?? searchInput.value.length;\n this.render();\n const si = this.root.querySelector<HTMLInputElement>('.search');\n if (si) {\n si.focus();\n si.setSelectionRange(pos, pos);\n }\n });\n searchInput.addEventListener('click', (e) => e.stopPropagation());\n }\n\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\n if (!trigger) return;\n\n trigger.addEventListener('click', (e) => {\n if (this.disabled) return;\n if ((e.target as HTMLElement).closest('.clear')) return;\n if ((e.target as HTMLElement).closest('.chip__x')) return;\n this.toggleAttribute('open');\n // Flip vertical: se não cabe abaixo, abre para cima.\n if (this.hasAttr('open')) {\n const r = this.getBoundingClientRect();\n this.toggleAttribute('up', r.bottom + 300 > window.innerHeight && r.top > 300);\n } else {\n this.removeAttribute('up');\n }\n this.render();\n });\n\n // Acessibilidade: Enter/Espaço abrem o painel (span role=button); setas navegam.\n trigger.addEventListener('keydown', (e) => {\n if (this.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n trigger.dispatchEvent(new MouseEvent('click', { bubbles: true }));\n return;\n }\n if (e.key === 'Escape') {\n if (this.hasAttr('open')) {\n this.removeAttribute('open');\n this.render();\n this.root.querySelector<HTMLElement>('.trigger')?.focus();\n }\n return;\n }\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n this._navigateOptions(e);\n }\n });\n\n // × de limpar tudo no trigger.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.selected.clear();\n this.commit();\n });\n\n // \"Limpar tudo\" no cabeçalho do painel.\n this.root.querySelector('.clear-all')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.selected.clear();\n this.commit();\n });\n\n // Remover chip individual.\n this.root.querySelectorAll<HTMLElement>('.chip').forEach((chip) => {\n chip.querySelector('.chip__x')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.toggleValue(chip.dataset.value!);\n });\n });\n\n // Alternar opções.\n this.root.querySelectorAll<HTMLElement>('.opt').forEach((opt) => {\n opt.addEventListener('click', () => this.toggleValue(opt.dataset.value!));\n });\n }\n}\n\nexport function defineFxMultiselect(): typeof FxMultiselect {\n return defineElement('fx-multiselect', FxMultiselect);\n}\n\n"],"names":[],"mappings":"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"}
|