@wrrdev/fenix-ui 1.0.13 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +332 -332
  2. package/dist/components/accordion/accordion.d.ts +1 -0
  3. package/dist/components/accordion/accordion.d.ts.map +1 -1
  4. package/dist/components/accordion/accordion.js +18 -13
  5. package/dist/components/accordion/accordion.js.map +1 -1
  6. package/dist/components/accordion/index.js.map +1 -1
  7. package/dist/components/alert/alert.d.ts.map +1 -1
  8. package/dist/components/alert/alert.js +2 -1
  9. package/dist/components/alert/alert.js.map +1 -1
  10. package/dist/components/alert/index.js.map +1 -1
  11. package/dist/components/autocomplete/autocomplete.d.ts +3 -0
  12. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  13. package/dist/components/autocomplete/autocomplete.js +28 -4
  14. package/dist/components/autocomplete/autocomplete.js.map +1 -1
  15. package/dist/components/autocomplete/index.js.map +1 -1
  16. package/dist/components/datepicker/datepicker.d.ts.map +1 -1
  17. package/dist/components/datepicker/datepicker.js +7 -6
  18. package/dist/components/datepicker/datepicker.js.map +1 -1
  19. package/dist/components/dialog/dialog.d.ts +4 -0
  20. package/dist/components/dialog/dialog.d.ts.map +1 -1
  21. package/dist/components/dialog/dialog.js +42 -4
  22. package/dist/components/dialog/dialog.js.map +1 -1
  23. package/dist/components/dialog/index.js.map +1 -1
  24. package/dist/components/drawer/drawer.d.ts +5 -0
  25. package/dist/components/drawer/drawer.d.ts.map +1 -1
  26. package/dist/components/drawer/drawer.js +49 -15
  27. package/dist/components/drawer/drawer.js.map +1 -1
  28. package/dist/components/dropdown/dropdown.d.ts +5 -0
  29. package/dist/components/dropdown/dropdown.d.ts.map +1 -1
  30. package/dist/components/dropdown/dropdown.js +34 -10
  31. package/dist/components/dropdown/dropdown.js.map +1 -1
  32. package/dist/components/input/input.d.ts.map +1 -1
  33. package/dist/components/input/input.js +4 -3
  34. package/dist/components/input/input.js.map +1 -1
  35. package/dist/components/knob/index.js.map +1 -1
  36. package/dist/components/knob/knob.d.ts +2 -0
  37. package/dist/components/knob/knob.d.ts.map +1 -1
  38. package/dist/components/knob/knob.js +15 -3
  39. package/dist/components/knob/knob.js.map +1 -1
  40. package/dist/components/multiselect/multiselect.d.ts +2 -0
  41. package/dist/components/multiselect/multiselect.d.ts.map +1 -1
  42. package/dist/components/multiselect/multiselect.js +38 -8
  43. package/dist/components/multiselect/multiselect.js.map +1 -1
  44. package/dist/components/orderlist/orderlist.d.ts.map +1 -1
  45. package/dist/components/orderlist/orderlist.js +1 -3
  46. package/dist/components/orderlist/orderlist.js.map +1 -1
  47. package/dist/components/pagination/index.js.map +1 -1
  48. package/dist/components/pagination/pagination.d.ts +1 -0
  49. package/dist/components/pagination/pagination.d.ts.map +1 -1
  50. package/dist/components/pagination/pagination.js +19 -8
  51. package/dist/components/pagination/pagination.js.map +1 -1
  52. package/dist/components/picklist/picklist.d.ts.map +1 -1
  53. package/dist/components/picklist/picklist.js +1 -3
  54. package/dist/components/picklist/picklist.js.map +1 -1
  55. package/dist/components/progress/index.js.map +1 -1
  56. package/dist/components/progress/progress.d.ts.map +1 -1
  57. package/dist/components/progress/progress.js +2 -1
  58. package/dist/components/progress/progress.js.map +1 -1
  59. package/dist/components/radio/radio.js +1 -1
  60. package/dist/components/radio/radio.js.map +1 -1
  61. package/dist/components/select/select.d.ts +2 -0
  62. package/dist/components/select/select.d.ts.map +1 -1
  63. package/dist/components/select/select.js +36 -5
  64. package/dist/components/select/select.js.map +1 -1
  65. package/dist/components/skeleton/index.js.map +1 -1
  66. package/dist/components/skeleton/skeleton.d.ts +2 -0
  67. package/dist/components/skeleton/skeleton.d.ts.map +1 -1
  68. package/dist/components/skeleton/skeleton.js +7 -2
  69. package/dist/components/skeleton/skeleton.js.map +1 -1
  70. package/dist/components/table/expr.d.ts +38 -0
  71. package/dist/components/table/expr.d.ts.map +1 -0
  72. package/dist/components/table/expr.js +429 -0
  73. package/dist/components/table/expr.js.map +1 -0
  74. package/dist/components/table/table.d.ts +26 -8
  75. package/dist/components/table/table.d.ts.map +1 -1
  76. package/dist/components/table/table.js +402 -140
  77. package/dist/components/table/table.js.map +1 -1
  78. package/dist/components/tabs/tabs.d.ts.map +1 -1
  79. package/dist/components/tabs/tabs.js +3 -2
  80. package/dist/components/tabs/tabs.js.map +1 -1
  81. package/dist/components/textarea/index.js.map +1 -1
  82. package/dist/components/textarea/textarea.d.ts.map +1 -1
  83. package/dist/components/textarea/textarea.js +4 -3
  84. package/dist/components/textarea/textarea.js.map +1 -1
  85. package/dist/components/toast/index.js.map +1 -1
  86. package/dist/components/tooltip/directive.js.map +1 -1
  87. package/dist/components/tooltip/index.js.map +1 -1
  88. package/dist/components/tooltip/tooltip.d.ts.map +1 -1
  89. package/dist/components/tooltip/tooltip.js +2 -1
  90. package/dist/components/tooltip/tooltip.js.map +1 -1
  91. package/dist/core/jsx.js.map +1 -1
  92. package/dist/core/presets.js.map +1 -1
  93. package/dist/core/sanitize.d.ts +36 -0
  94. package/dist/core/sanitize.d.ts.map +1 -0
  95. package/dist/core/sanitize.js +37 -0
  96. package/dist/core/sanitize.js.map +1 -0
  97. package/dist/core/tokens.js.map +1 -1
  98. package/dist/core/vue.js.map +1 -1
  99. package/dist/docs/app.d.ts.map +1 -1
  100. package/dist/docs/componentes/table.doc.d.ts.map +1 -1
  101. package/dist/fenix-ui.esm.js.map +1 -1
  102. package/dist/fenix-ui.umd.min.js +339 -208
  103. package/dist/fenix-ui2.esm.js +1156 -243
  104. package/dist/fenix-ui2.esm.js.map +1 -1
  105. package/dist/fenix-ui3.esm.js +7 -1
  106. package/dist/fenix-ui3.esm.js.map +1 -1
  107. package/dist/index.d.ts +2 -1
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +4 -0
  110. package/dist/index.js.map +1 -1
  111. package/dist/plugins/auto-import.d.ts.map +1 -1
  112. package/dist/plugins/auto-import.js +7 -1
  113. package/dist/plugins/auto-import.js.map +1 -1
  114. package/package.json +212 -212
@@ -1 +1 @@
1
- {"version":3,"file":"select.js","sources":["../../../src/components/select/select.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\r\nimport { css } from '../../core/css';\r\nimport { defineElement } from '../../core/define';\r\n\r\n/**\r\n * <fx-select> — Campo de seleção com dropdown customizado.\r\n *\r\n * Escreva os filhos como `<option>` nativos no light DOM; eles são\r\n * espelhados automaticamente (MutationObserver):\r\n *\r\n * <fx-select value=\"b\" searchable clearable>\r\n * <option value=\"a\">Opção A</option>\r\n * <option value=\"b\">Opção B</option>\r\n * </fx-select>\r\n *\r\n * Atributos: value, size (sm|md|lg), disabled, placeholder,\r\n * searchable, clearable, search-placeholder, no-results.\r\n * Evento: `change` (composed, detail: { value }).\r\n */\r\nexport class FxSelect extends FxElement {\r\n static override styles = css`\r\n :host {\r\n display: inline-block;\r\n vertical-align: middle;\r\n font-family: var(--fx-font-family);\r\n font-size: var(--fx-font-size);\r\n position: relative;\r\n }\r\n .trigger {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: var(--fx-space-sm);\r\n box-sizing: border-box;\r\n min-height: var(--fx-size-md);\r\n min-width: 200px;\r\n font: inherit;\r\n font-weight: var(--fx-font-weight);\r\n color: var(--fx-text-default);\r\n text-align: left;\r\n background-color: var(--fx-surface-background);\r\n border: 1px solid var(--fx-border-default);\r\n border-radius: var(--fx-radius-md);\r\n padding: var(--fx-space-xs) var(--fx-space-md);\r\n cursor: pointer;\r\n transition:\r\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\r\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\r\n }\r\n .trigger:hover { border-color: var(--fx-border-hover); }\r\n .trigger:focus-visible,\r\n :host([open]) .trigger {\r\n outline: none;\r\n border-color: var(--fx-color-primary);\r\n box-shadow: var(--fx-effect-focus-ring, none);\r\n }\r\n :host([size='sm']) .trigger { min-width: 180px; min-height: var(--fx-size-sm); }\r\n :host([size='lg']) .trigger { min-height: var(--fx-size-lg); font-size: calc(var(--fx-font-size) + 4px); }\r\n /* Validação */\r\n :host([error]) .trigger,\r\n :host([invalid]) .trigger {\r\n border-color: var(--fx-color-danger, #dc2626);\r\n }\r\n :host([error]) .trigger:focus-visible,\r\n :host([invalid]) .trigger:focus-visible,\r\n :host([error][open]) .trigger,\r\n :host([invalid][open]) .trigger {\r\n border-color: var(--fx-color-danger, #dc2626);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent);\r\n }\r\n :host([success]) .trigger,\r\n :host([valid]) .trigger {\r\n border-color: var(--fx-color-success, #16a34a);\r\n }\r\n :host([success]) .trigger:focus-visible,\r\n :host([valid]) .trigger:focus-visible,\r\n :host([success][open]) .trigger,\r\n :host([valid][open]) .trigger {\r\n border-color: var(--fx-color-success, #16a34a);\r\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent);\r\n }\r\n :host([disabled]) .trigger,\r\n .trigger[aria-disabled='true'] { opacity: 0.55; cursor: not-allowed; background-color: var(--fx-surface-surface-hover); }\r\n /* O rótulo encolhe (ellipsis) e os ícones NUNCA saem do campo. */\r\n .label,\r\n .placeholder {\r\n flex: 1 1 auto;\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n }\r\n .placeholder { color: var(--fx-text-muted); }\r\n .actions {\r\n display: inline-flex;\r\n align-items: center;\r\n gap: var(--fx-space-xs, 8px);\r\n flex-shrink: 0;\r\n }\r\n .caret { font-size: calc(var(--fx-font-size) - 3px); color: var(--fx-text-muted); pointer-events: none; }\r\n\r\n /* Painel do dropdown */\r\n .panel {\r\n position: absolute;\r\n top: calc(100% + 4px);\r\n left: 0;\r\n z-index: var(--fx-z-dropdown, 1000);\r\n width: max(100%, 220px);\r\n max-height: 260px;\r\n overflow-y: auto;\r\n display: none;\r\n background: var(--fx-surface-background);\r\n border: 1px solid var(--fx-border-default);\r\n border-radius: var(--fx-radius-md);\r\n box-shadow: var(--fx-shadow-lg);\r\n }\r\n :host([open]) .panel { display: block; }\r\n .search {\r\n position: sticky;\r\n top: 0;\r\n width: 100%;\r\n box-sizing: border-box;\r\n font: inherit;\r\n color: var(--fx-text-default);\r\n background: var(--fx-surface-background);\r\n border: none;\r\n border-bottom: 1px solid var(--fx-border-default);\r\n padding: var(--fx-space-sm) var(--fx-space-md);\r\n outline: none;\r\n }\r\n .opt {\r\n display: block;\r\n width: 100%;\r\n text-align: left;\r\n font: inherit;\r\n color: var(--fx-text-default);\r\n background: none;\r\n border: none;\r\n padding: var(--fx-space-sm) var(--fx-space-md);\r\n cursor: pointer;\r\n }\r\n /* Hover e selecionado com a cor primária do tema (igual ao multiselect). */\r\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, transparent); }\r\n .opt[aria-selected='true'] {\r\n background: color-mix(in srgb, var(--fx-color-primary) 18%, transparent);\r\n color: var(--fx-color-primary);\r\n font-weight: var(--fx-font-weight);\r\n }\r\n .empty { padding: var(--fx-space-sm) var(--fx-space-md); color: var(--fx-text-muted); }\r\n\r\n /* Clearable */\r\n .clear {\r\n border: none;\r\n background: transparent;\r\n color: var(--fx-text-muted);\r\n font-size: calc(var(--fx-font-size) + 2px);\r\n line-height: 1;\r\n cursor: pointer;\r\n padding: 0;\r\n }\r\n .clear:hover { color: var(--fx-color-danger); }\r\n .clear[hidden] { display: none; }\r\n `;\r\n\r\n // `value` fica FORA da observação: refleti-lo no change não pode\r\n // re-renderizar o template e fechar o dropdown.\r\n static override get observedAttributes(): string[] {\r\n return ['size', 'disabled', 'placeholder', 'searchable', 'clearable', 'error', 'success'];\r\n }\r\n\r\n private observer?: MutationObserver;\r\n private docListener?: (e: Event) => void;\r\n\r\n /** Tamanho do campo. Padrão: `'md'`. */\r\n get size(): string {\r\n const s = this.getAttr('size', 'md');\r\n return s === 'sm' || s === 'lg' ? s : 'md';\r\n }\r\n set size(value: string) {\r\n this.setAttribute('size', value);\r\n }\r\n\r\n get value(): string {\r\n return this.getAttr('value');\r\n }\r\n set value(value: string) {\r\n this.setAttribute('value', value);\r\n }\r\n\r\n get disabled(): boolean {\r\n return this.hasAttr('disabled');\r\n }\r\n set disabled(value: boolean) {\r\n this.toggleAttr('disabled', Boolean(value));\r\n }\r\n\r\n private get options(): { value: string; label: string }[] {\r\n return [...this.querySelectorAll('option')].map((o) => ({\r\n value: o.getAttribute('value') ?? o.textContent?.trim() ?? '',\r\n label: o.textContent?.trim() ?? '',\r\n }));\r\n }\r\n\r\n protected override connectedCallback(): void {\r\n super.connectedCallback();\r\n // Espelha mudanças nas <option> do light DOM.\r\n this.observer = new MutationObserver(() => this.render());\r\n this.observer.observe(this, { childList: true, subtree: true, characterData: true });\r\n // Fecha o dropdown ao clicar fora.\r\n this.docListener = (e: Event) => {\r\n if (!this.hasAttr('open')) return;\r\n if (e.composedPath().includes(this)) return;\r\n this.removeAttribute('open');\r\n this.render();\r\n };\r\n document.addEventListener('click', this.docListener);\r\n }\r\n\r\n protected override disconnectedCallback(): void {\r\n super.disconnectedCallback();\r\n this.observer?.disconnect();\r\n if (this.docListener) document.removeEventListener('click', this.docListener);\r\n }\r\n\r\n private select(value: string): void {\r\n this.value = value;\r\n this.removeAttribute('open');\r\n this.render();\r\n this.dispatchEvent(\r\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value } }),\r\n );\r\n }\r\n\r\n protected override render(): void {\r\n const prevOpen = this.hasAttr('open');\r\n const prevSearch = this.root.querySelector<HTMLInputElement>('.search');\r\n // Estado do campo de busca antes de re-renderizar (valor, foco e cursor).\r\n const search = prevSearch?.value ?? '';\r\n const searchFocused =\r\n prevSearch != null && this.root.activeElement === prevSearch;\r\n const caretPos = searchFocused ? prevSearch!.selectionStart : null;\r\n\r\n const opts = this.options;\r\n let current = this.getAttr('value');\r\n if (!current) {\r\n const explicit = this.querySelector('option[selected]');\r\n current = explicit?.getAttribute('value') ?? explicit?.textContent?.trim() ?? '';\r\n }\r\n const selectedLabel = opts.find((o) => o.value === current)?.label ?? '';\r\n const placeholder = this.getAttr('placeholder', 'Selecione…');\r\n\r\n const filtered = search\r\n ? opts.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))\r\n : opts;\r\n\r\n this.setTemplate(`\r\n <span class=\"trigger\" part=\"trigger\" role=\"button\" tabindex=\"${this.disabled ? -1 : 0}\" aria-haspopup=\"listbox\" aria-expanded=\"${prevOpen}\">\r\n <span class=\"${selectedLabel ? 'label' : 'placeholder'}\">${selectedLabel || placeholder}</span>\r\n <span class=\"actions\">\r\n ${this.hasAttr('clearable') && current ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\">×</button>' : ''}\r\n <span class=\"caret\">▼</span>\r\n </span>\r\n </span>\r\n <div class=\"panel\" part=\"panel\" role=\"listbox\">\r\n ${this.hasAttr('searchable') ? `<input class=\"search\" part=\"search\" type=\"text\" placeholder=\"${this.getAttr('search-placeholder', 'Pesquisar…')}\">` : ''}\r\n ${filtered.map((o) => `\r\n <button type=\"button\" class=\"opt\" role=\"option\" data-value=\"${o.value}\"\r\n aria-selected=\"${o.value === current}\">${o.label}</button>`).join('')}\r\n ${filtered.length === 0 ? `<div class=\"empty\">${this.getAttr('no-results', 'Nenhum resultado')}</div>` : ''}\r\n </div>\r\n `);\r\n\r\n if (prevOpen) this.setAttribute('open', '');\r\n const searchInput = this.root.querySelector<HTMLInputElement>('.search');\r\n if (searchInput) {\r\n searchInput.value = search;\r\n searchInput.addEventListener('input', () => this.render());\r\n searchInput.addEventListener('click', (e) => e.stopPropagation());\r\n // Re-render não pode roubar o foco nem perder a posição do cursor.\r\n if (searchFocused) {\r\n searchInput.focus();\r\n try { searchInput.setSelectionRange(caretPos!, caretPos!); } catch { /* type=text sempre suporta */ }\r\n }\r\n }\r\n\r\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\r\n if (!trigger) return;\r\n if (this.disabled) trigger.setAttribute('aria-disabled', 'true');\r\n\r\n trigger.addEventListener('click', (e) => {\r\n if (this.disabled) return;\r\n if ((e.target as HTMLElement).closest('.clear')) return;\r\n this.toggleAttribute('open');\r\n this.render();\r\n this.root.querySelector<HTMLInputElement>('.search')?.focus();\r\n });\r\n\r\n // Teclado: Enter/Espaço abrem o dropdown.\r\n trigger.addEventListener('keydown', (e) => {\r\n if (e.key === 'Enter' || e.key === ' ') {\r\n e.preventDefault();\r\n trigger.click();\r\n }\r\n });\r\n\r\n // Limpar.\r\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\r\n e.stopPropagation();\r\n this.value = '';\r\n this.removeAttribute('open');\r\n this.render();\r\n this.dispatchEvent(\r\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value: '' } }),\r\n );\r\n });\r\n\r\n // Escolher opção.\r\n this.root.querySelectorAll<HTMLElement>('.opt').forEach((opt) => {\r\n opt.addEventListener('click', () => this.select(opt.dataset.value!));\r\n });\r\n }\r\n}\r\n\r\nexport function defineFxSelect(): typeof FxSelect {\r\n return defineElement('fx-select', FxSelect);\r\n}\r\n\r\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"select.js","sources":["../../../src/components/select/select.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-select> — Campo de seleção com dropdown customizado.\n *\n * Escreva os filhos como `<option>` nativos no light DOM; eles são\n * espelhados automaticamente (MutationObserver):\n *\n * <fx-select value=\"b\" searchable clearable>\n * <option value=\"a\">Opção A</option>\n * <option value=\"b\">Opção B</option>\n * </fx-select>\n *\n * Atributos: value, size (sm|md|lg), disabled, placeholder,\n * searchable, clearable, search-placeholder, no-results.\n * Evento: `change` (composed, detail: { value }).\n */\nexport class FxSelect extends FxElement {\n static override styles = css`\n :host {\n display: inline-block;\n vertical-align: middle;\n font-family: var(--fx-font-family);\n font-size: var(--fx-font-size);\n position: relative;\n }\n .trigger {\n display: inline-flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--fx-space-sm);\n box-sizing: border-box;\n min-height: var(--fx-size-md);\n min-width: 200px;\n font: inherit;\n font-weight: var(--fx-font-weight);\n color: var(--fx-text-default);\n text-align: left;\n background-color: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n padding: var(--fx-space-xs) var(--fx-space-md);\n cursor: pointer;\n transition:\n border-color var(--fx-motion-duration-normal) var(--fx-motion-easing),\n box-shadow var(--fx-motion-duration-normal) var(--fx-motion-easing);\n }\n .trigger:hover { border-color: var(--fx-border-hover); }\n .trigger:focus-visible,\n :host([open]) .trigger {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([size='sm']) .trigger { min-width: 180px; min-height: var(--fx-size-sm); }\n :host([size='lg']) .trigger { min-height: var(--fx-size-lg); font-size: calc(var(--fx-font-size) + 4px); }\n /* 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 :host([disabled]) .trigger,\n .trigger[aria-disabled='true'] { opacity: 0.55; cursor: not-allowed; background-color: var(--fx-surface-surface-hover); }\n /* O rótulo encolhe (ellipsis) e os ícones NUNCA saem do campo. */\n .label,\n .placeholder {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .placeholder { color: var(--fx-text-muted); }\n .actions {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs, 8px);\n flex-shrink: 0;\n }\n .caret { font-size: calc(var(--fx-font-size) - 3px); color: var(--fx-text-muted); pointer-events: none; }\n\n /* Painel do dropdown */\n .panel {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\n width: max(100%, 220px);\n max-height: 260px;\n overflow-y: auto;\n display: none;\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-md);\n box-shadow: var(--fx-shadow-lg);\n }\n :host([open]) .panel { display: block; }\n .search {\n position: sticky;\n top: 0;\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: none;\n border-bottom: 1px solid var(--fx-border-default);\n padding: var(--fx-space-sm) var(--fx-space-md);\n outline: none;\n }\n .opt {\n display: block;\n width: 100%;\n text-align: left;\n font: inherit;\n color: var(--fx-text-default);\n background: none;\n border: none;\n padding: var(--fx-space-sm) var(--fx-space-md);\n cursor: pointer;\n }\n /* Hover e selecionado com a cor primária do tema (igual ao multiselect). */\n .opt:hover { background: color-mix(in srgb, var(--fx-color-primary) 12%, transparent); }\n .opt[aria-selected='true'] {\n background: color-mix(in srgb, var(--fx-color-primary) 18%, transparent);\n color: var(--fx-color-primary);\n font-weight: var(--fx-font-weight);\n }\n .empty { padding: var(--fx-space-sm) var(--fx-space-md); color: var(--fx-text-muted); }\n\n /* Clearable */\n .clear {\n border: none;\n background: transparent;\n color: var(--fx-text-muted);\n font-size: calc(var(--fx-font-size) + 2px);\n line-height: 1;\n cursor: pointer;\n padding: 0;\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n `;\n\n // `value` fica FORA da observação: refleti-lo no change não pode\n // re-renderizar o template e fechar o dropdown.\n static override get observedAttributes(): string[] {\n return ['size', 'disabled', 'placeholder', 'searchable', 'clearable', 'error', 'success'];\n }\n\n private observer?: MutationObserver;\n private docListener?: (e: Event) => void;\n\n /** Tamanho do campo. Padrão: `'md'`. */\n get size(): string {\n const s = this.getAttr('size', 'md');\n return s === 'sm' || s === 'lg' ? s : 'md';\n }\n set size(value: string) {\n this.setAttribute('size', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(value: string) {\n this.setAttribute('value', value);\n }\n\n get disabled(): boolean {\n return this.hasAttr('disabled');\n }\n set disabled(value: boolean) {\n this.toggleAttr('disabled', Boolean(value));\n }\n\n private get options(): { value: string; label: string }[] {\n return [...this.querySelectorAll('option')].map((o) => ({\n value: o.getAttribute('value') ?? o.textContent?.trim() ?? '',\n label: o.textContent?.trim() ?? '',\n }));\n }\n\n protected override connectedCallback(): void {\n super.connectedCallback();\n // Espelha mudanças nas <option> do light DOM.\n this.observer = new MutationObserver(() => this.render());\n this.observer.observe(this, { childList: true, subtree: true, characterData: true });\n // Fecha o dropdown ao clicar fora.\n this.docListener = (e: Event) => {\n if (!this.hasAttr('open')) return;\n if (e.composedPath().includes(this)) return;\n this.removeAttribute('open');\n this.render();\n };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n this.observer?.disconnect();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n private select(value: string): void {\n this.value = value;\n this.removeAttribute('open');\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value } }),\n );\n }\n\n /** Navegação por teclado entre as opções do listbox (WCAG 2.1.1). */\n private _navigateOptions(e: KeyboardEvent): void {\n e.preventDefault();\n if (!this.hasAttr('open')) {\n this.toggleAttribute('open');\n this.render();\n }\n const opts = Array.from(this.root.querySelectorAll<HTMLButtonElement>('.opt'));\n if (!opts.length) return;\n const current = opts.indexOf(this.root.activeElement as HTMLButtonElement);\n let next = 0;\n if (e.key === 'ArrowDown') next = current === -1 ? 0 : Math.min(current + 1, opts.length - 1);\n else if (e.key === 'ArrowUp') next = current === -1 ? opts.length - 1 : Math.max(current - 1, 0);\n else if (e.key === 'Home') next = 0;\n else if (e.key === 'End') next = opts.length - 1;\n opts[next]?.focus();\n }\n\n protected override render(): void {\n const prevOpen = this.hasAttr('open');\n const prevSearch = this.root.querySelector<HTMLInputElement>('.search');\n // Estado do campo de busca antes de re-renderizar (valor, foco e cursor).\n const search = prevSearch?.value ?? '';\n const searchFocused =\n prevSearch != null && this.root.activeElement === prevSearch;\n const caretPos = searchFocused ? prevSearch!.selectionStart : null;\n\n const opts = this.options;\n let current = this.getAttr('value');\n if (!current) {\n const explicit = this.querySelector('option[selected]');\n current = explicit?.getAttribute('value') ?? explicit?.textContent?.trim() ?? '';\n }\n const selectedLabel = opts.find((o) => o.value === current)?.label ?? '';\n const placeholder = this.getAttr('placeholder', 'Selecione…');\n\n const filtered = search\n ? opts.filter((o) => o.label.toLowerCase().includes(search.toLowerCase()))\n : opts;\n\n this.setTemplate(`\n <span class=\"trigger\" part=\"trigger\" role=\"button\" tabindex=\"${this.disabled ? -1 : 0}\" aria-haspopup=\"listbox\" aria-expanded=\"${prevOpen}\">\n <span class=\"${selectedLabel ? 'label' : 'placeholder'}\">${esc(selectedLabel) || esc(placeholder)}</span>\n <span class=\"actions\">\n ${this.hasAttr('clearable') && current ? '<button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\">×</button>' : ''}\n <span class=\"caret\">▼</span>\n </span>\n </span>\n <div class=\"panel\" part=\"panel\" role=\"listbox\">\n ${this.hasAttr('searchable') ? `<input class=\"search\" part=\"search\" type=\"text\" placeholder=\"${esc(this.getAttr('search-placeholder', 'Pesquisar…'))}\">` : ''}\n ${filtered.map((o) => `\n <button type=\"button\" class=\"opt\" role=\"option\" data-value=\"${esc(o.value)}\"\n aria-selected=\"${o.value === current}\">${esc(o.label)}</button>`).join('')}\n ${filtered.length === 0 ? `<div class=\"empty\">${esc(this.getAttr('no-results', 'Nenhum resultado'))}</div>` : ''}\n </div>\n `);\n\n if (prevOpen) this.setAttribute('open', '');\n const searchInput = this.root.querySelector<HTMLInputElement>('.search');\n if (searchInput) {\n searchInput.value = search;\n searchInput.addEventListener('input', () => this.render());\n searchInput.addEventListener('click', (e) => e.stopPropagation());\n // Re-render não pode roubar o foco nem perder a posição do cursor.\n if (searchFocused) {\n searchInput.focus();\n try { searchInput.setSelectionRange(caretPos!, caretPos!); } catch { /* type=text sempre suporta */ }\n }\n }\n\n const trigger = this.root.querySelector<HTMLElement>('.trigger');\n if (!trigger) return;\n if (this.disabled) trigger.setAttribute('aria-disabled', 'true');\n\n trigger.addEventListener('click', (e) => {\n if (this.disabled) return;\n if ((e.target as HTMLElement).closest('.clear')) return;\n this.toggleAttribute('open');\n this.render();\n this.root.querySelector<HTMLInputElement>('.search')?.focus();\n });\n\n // Teclado: Enter/Espaço abrem o dropdown; setas/Home/End/Escape navegam.\n trigger.addEventListener('keydown', (e) => {\n if (this.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n trigger.click();\n return;\n }\n if (e.key === 'Escape') {\n if (this.hasAttr('open')) {\n this.removeAttribute('open');\n this.render();\n this.root.querySelector<HTMLElement>('.trigger')?.focus();\n }\n return;\n }\n if (e.key === 'ArrowDown' || e.key === 'ArrowUp' || e.key === 'Home' || e.key === 'End') {\n this._navigateOptions(e);\n }\n });\n\n // Limpar.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.value = '';\n this.removeAttribute('open');\n this.render();\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail: { value: '' } }),\n );\n });\n\n // Escolher opção.\n this.root.querySelectorAll<HTMLElement>('.opt').forEach((opt) => {\n opt.addEventListener('click', () => this.select(opt.dataset.value!));\n });\n }\n}\n\nexport function defineFxSelect(): typeof FxSelect {\n return defineElement('fx-select', FxSelect);\n}\n\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/components/skeleton/index.ts"],"sourcesContent":["import { FxSkeleton, defineFxSkeleton } from './skeleton';\n\nexport { FxSkeleton };\n\n\ndefineFxSkeleton();\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'fx-skeleton': FxSkeleton;\n }\n}\n\r\n\r\n"],"names":[],"mappings":";;AAKA,iBAAA;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/components/skeleton/index.ts"],"sourcesContent":["import { FxSkeleton, defineFxSkeleton } from './skeleton';\n\nexport { FxSkeleton };\n\n\ndefineFxSkeleton();\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'fx-skeleton': FxSkeleton;\n }\n}\n\n\n"],"names":[],"mappings":";;AAKA,iBAAA;"}
@@ -7,6 +7,8 @@ import { FxElement } from '../../core/base';
7
7
  export declare class FxSkeleton extends FxElement {
8
8
  static styles: string;
9
9
  static get observedAttributes(): string[];
10
+ /** Valida um valor de dimensão CSS, retornando-o seguro ou vazio. */
11
+ private safeSize;
10
12
  protected render(): void;
11
13
  }
12
14
  export declare function defineFxSkeleton(): typeof FxSkeleton;
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/skeleton/skeleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,SAAS;IACvC,OAAgB,MAAM,SAkBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;cAEkB,MAAM,IAAI,IAAI;CAyBlC;AAED,wBAAgB,gBAAgB,IAAI,OAAO,UAAU,CAEpD"}
1
+ {"version":3,"file":"skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/skeleton/skeleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C;;;;GAIG;AACH,qBAAa,UAAW,SAAQ,SAAS;IACvC,OAAgB,MAAM,SAkBpB;IAEF,WAAoB,kBAAkB,IAAI,MAAM,EAAE,CAEjD;IAED,qEAAqE;IACrE,OAAO,CAAC,QAAQ;cAKG,MAAM,IAAI,IAAI;CAyBlC;AAED,wBAAgB,gBAAgB,IAAI,OAAO,UAAU,CAEpD"}
@@ -5,10 +5,15 @@ const _FxSkeleton = class _FxSkeleton extends FxElement {
5
5
  static get observedAttributes() {
6
6
  return ["variant", "width", "height", "lines"];
7
7
  }
8
+ /** Valida um valor de dimensão CSS, retornando-o seguro ou vazio. */
9
+ safeSize(value) {
10
+ const v = value.trim();
11
+ return /^-?\d*\.?\d+(px|em|rem|%|vw|vh|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/.test(v) ? v : "";
12
+ }
8
13
  render() {
9
14
  const variant = this.getAttr("variant", "text");
10
- const width = this.getAttr("width");
11
- const height = this.getAttr("height");
15
+ const width = this.safeSize(this.getAttr("width"));
16
+ const height = this.safeSize(this.getAttr("height"));
12
17
  const style = `${width ? `width:${width};` : ""}${height ? `height:${height};` : ""}`;
13
18
  if (variant === "circle") {
14
19
  const size = height || width || "40px";
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton.js","sources":["../../../src/components/skeleton/skeleton.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\n\n/**\n * <fx-skeleton> — Placeholder animado para carregamento de conteúdo.\n *\n * Atributos: variant (text|circle|rect, padrão text), width, height, lines (para text).\n */\nexport class FxSkeleton extends FxElement {\n static override styles = css`\n :host { display: block; width: 100%; min-width: 80px; font-family: var(--fx-font-family); }\n .bone {\n background: linear-gradient(\n 90deg,\n var(--fx-surface-surface-hover) 25%,\n color-mix(in srgb, var(--fx-surface-surface-hover) 55%, var(--fx-surface-background)) 50%,\n var(--fx-surface-surface-hover) 75%\n );\n background-size: 200% 100%;\n animation: shimmer 1.4s ease-in-out infinite;\n border-radius: var(--fx-radius-sm);\n }\n @keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n .bone[hidden] { display: none; }\n .text { height: 12px; width: 100%; margin-bottom: var(--fx-space-sm); }\n .text:last-of-type { width: 60%; margin-bottom: 0; }\n .circle { border-radius: var(--fx-radius-full); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'width', 'height', 'lines'];\n }\n\n protected override render(): void {\n const variant = this.getAttr('variant', 'text');\n const width = this.getAttr('width');\n const height = this.getAttr('height');\n const style = `${width ? `width:${width};` : ''}${height ? `height:${height};` : ''}`;\n\n if (variant === 'circle') {\n const size = height || width || '40px';\n const diameter = !style.includes('height') ? `width:${size};height:${size};` : style;\n this.setTemplate(`<div class=\"bone circle\" part=\"bone\" style=\"${diameter}\"></div>`);\n return;\n }\n\n if (variant === 'rect') {\n const s = style || 'width:100%;height:80px;';\n this.setTemplate(`<div class=\"bone\" part=\"bone\" style=\"${s}\"></div>`);\n return;\n }\n\n // text (padrão): N linhas\n const lines = Number(this.getAttr('lines', '3')) || 3;\n this.setTemplate(`\n ${Array.from({ length: lines }, () => '<div class=\"bone text\"></div>').join('')}\n `);\n }\n}\n\nexport function defineFxSkeleton(): typeof FxSkeleton {\n return defineElement('fx-skeleton', FxSkeleton);\n}\n"],"names":[],"mappings":";;;AASO,MAAM,cAAN,MAAM,oBAAmB,UAAU;AAAA,EAqBxC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,SAAS,UAAU,OAAO;AAAA,EAC/C;AAAA,EAEmB,SAAe;AAChC,UAAM,UAAU,KAAK,QAAQ,WAAW,MAAM;AAC9C,UAAM,QAAQ,KAAK,QAAQ,OAAO;AAClC,UAAM,SAAS,KAAK,QAAQ,QAAQ;AACpC,UAAM,QAAQ,GAAG,QAAQ,SAAS,KAAK,MAAM,EAAE,GAAG,SAAS,UAAU,MAAM,MAAM,EAAE;AAEnF,QAAI,YAAY,UAAU;AACxB,YAAM,OAAO,UAAU,SAAS;AAChC,YAAM,WAAW,CAAC,MAAM,SAAS,QAAQ,IAAI,SAAS,IAAI,WAAW,IAAI,MAAM;AAC/E,WAAK,YAAY,+CAA+C,QAAQ,UAAU;AAClF;AAAA,IACF;AAEA,QAAI,YAAY,QAAQ;AACtB,YAAM,IAAI,SAAS;AACnB,WAAK,YAAY,wCAAwC,CAAC,UAAU;AACpE;AAAA,IACF;AAGA,UAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,GAAG,CAAC,KAAK;AACpD,SAAK,YAAY;AAAA,QACb,MAAM,KAAK,EAAE,QAAQ,MAAA,GAAS,MAAM,+BAA+B,EAAE,KAAK,EAAE,CAAC;AAAA,KAChF;AAAA,EACH;AACF;AAjDE,YAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADpB,IAAM,aAAN;AAoDA,SAAS,mBAAsC;AACpD,SAAO,cAAc,eAAe,UAAU;AAChD;"}
1
+ {"version":3,"file":"skeleton.js","sources":["../../../src/components/skeleton/skeleton.ts"],"sourcesContent":["import { FxElement } from '../../core/base';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\n\n/**\n * <fx-skeleton> — Placeholder animado para carregamento de conteúdo.\n *\n * Atributos: variant (text|circle|rect, padrão text), width, height, lines (para text).\n */\nexport class FxSkeleton extends FxElement {\n static override styles = css`\n :host { display: block; width: 100%; min-width: 80px; font-family: var(--fx-font-family); }\n .bone {\n background: linear-gradient(\n 90deg,\n var(--fx-surface-surface-hover) 25%,\n color-mix(in srgb, var(--fx-surface-surface-hover) 55%, var(--fx-surface-background)) 50%,\n var(--fx-surface-surface-hover) 75%\n );\n background-size: 200% 100%;\n animation: shimmer 1.4s ease-in-out infinite;\n border-radius: var(--fx-radius-sm);\n }\n @keyframes shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n .bone[hidden] { display: none; }\n .text { height: 12px; width: 100%; margin-bottom: var(--fx-space-sm); }\n .text:last-of-type { width: 60%; margin-bottom: 0; }\n .circle { border-radius: var(--fx-radius-full); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['variant', 'width', 'height', 'lines'];\n }\n\n /** Valida um valor de dimensão CSS, retornando-o seguro ou vazio. */\n private safeSize(value: string): string {\n const v = value.trim();\n return /^-?\\d*\\.?\\d+(px|em|rem|%|vw|vh|vmin|vmax|ch|ex|cm|mm|in|pt|pc)$/.test(v) ? v : '';\n }\n\n protected override render(): void {\n const variant = this.getAttr('variant', 'text');\n const width = this.safeSize(this.getAttr('width'));\n const height = this.safeSize(this.getAttr('height'));\n const style = `${width ? `width:${width};` : ''}${height ? `height:${height};` : ''}`;\n\n if (variant === 'circle') {\n const size = height || width || '40px';\n const diameter = !style.includes('height') ? `width:${size};height:${size};` : style;\n this.setTemplate(`<div class=\"bone circle\" part=\"bone\" style=\"${diameter}\"></div>`);\n return;\n }\n\n if (variant === 'rect') {\n const s = style || 'width:100%;height:80px;';\n this.setTemplate(`<div class=\"bone\" part=\"bone\" style=\"${s}\"></div>`);\n return;\n }\n\n // text (padrão): N linhas\n const lines = Number(this.getAttr('lines', '3')) || 3;\n this.setTemplate(`\n ${Array.from({ length: lines }, () => '<div class=\"bone text\"></div>').join('')}\n `);\n }\n}\n\nexport function defineFxSkeleton(): typeof FxSkeleton {\n return defineElement('fx-skeleton', FxSkeleton);\n}\n"],"names":[],"mappings":";;;AASO,MAAM,cAAN,MAAM,oBAAmB,UAAU;AAAA,EAqBxC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,WAAW,SAAS,UAAU,OAAO;AAAA,EAC/C;AAAA;AAAA,EAGQ,SAAS,OAAuB;AACtC,UAAM,IAAI,MAAM,KAAA;AAChB,WAAO,kEAAkE,KAAK,CAAC,IAAI,IAAI;AAAA,EACzF;AAAA,EAEmB,SAAe;AAChC,UAAM,UAAU,KAAK,QAAQ,WAAW,MAAM;AAC9C,UAAM,QAAQ,KAAK,SAAS,KAAK,QAAQ,OAAO,CAAC;AACjD,UAAM,SAAS,KAAK,SAAS,KAAK,QAAQ,QAAQ,CAAC;AACnD,UAAM,QAAQ,GAAG,QAAQ,SAAS,KAAK,MAAM,EAAE,GAAG,SAAS,UAAU,MAAM,MAAM,EAAE;AAEnF,QAAI,YAAY,UAAU;AACxB,YAAM,OAAO,UAAU,SAAS;AAChC,YAAM,WAAW,CAAC,MAAM,SAAS,QAAQ,IAAI,SAAS,IAAI,WAAW,IAAI,MAAM;AAC/E,WAAK,YAAY,+CAA+C,QAAQ,UAAU;AAClF;AAAA,IACF;AAEA,QAAI,YAAY,QAAQ;AACtB,YAAM,IAAI,SAAS;AACnB,WAAK,YAAY,wCAAwC,CAAC,UAAU;AACpE;AAAA,IACF;AAGA,UAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,GAAG,CAAC,KAAK;AACpD,SAAK,YAAY;AAAA,QACb,MAAM,KAAK,EAAE,QAAQ,MAAA,GAAS,MAAM,+BAA+B,EAAE,KAAK,EAAE,CAAC;AAAA,KAChF;AAAA,EACH;AACF;AAvDE,YAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADpB,IAAM,aAAN;AA0DA,SAAS,mBAAsC;AACpD,SAAO,cAAc,eAAe,UAAU;AAChD;"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Avaliador de expressões **seguro** (sem `eval`/`Function`) usado pelos
3
+ * templates de célula do `<fx-table>`.
4
+ *
5
+ * Permite personalizar o conteúdo de uma coluna:
6
+ *
7
+ * ```html
8
+ * <fx-column field="preco" header="Preço">
9
+ * <template>R$ {{ value | currency }}</template>
10
+ * </fx-column>
11
+ *
12
+ * <fx-column field="status" header="Situação">
13
+ * <template>
14
+ * <i class="pi pi-{{ row.status === 'ativo' ? 'check' : 'times' }}"></i>
15
+ * </template>
16
+ * </fx-column>
17
+ * ```
18
+ *
19
+ * Contexto de variáveis dentro de `{{ }}`:
20
+ * - `value` — valor do campo da coluna (row[field])
21
+ * - `row` — objeto completo da linha
22
+ * - `row.campo` — acesso a qualquer campo da linha
23
+ *
24
+ * Pipes: `value | currency`, `value | date`, `value | date: 'short'`,
25
+ * `value | dateTime`, `value | number`, `value | number: 2`
26
+ */
27
+ type Row = Record<string, unknown>;
28
+ export type EvalCtx = {
29
+ value: unknown;
30
+ row: Row;
31
+ };
32
+ export declare const PIPES: Record<string, (v: unknown, arg?: string) => string>;
33
+ /** Avalia uma única expressão `{{ }}` retornando uma string. */
34
+ export declare function evaluate(expr: string, ctx: EvalCtx): string;
35
+ /** Processa um template HTML substituindo todas as ocorrências `{{ expr }}`. */
36
+ export declare function renderCell(template: string, row: Row, field: string): string;
37
+ export {};
38
+ //# sourceMappingURL=expr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expr.d.ts","sourceRoot":"","sources":["../../../src/components/table/expr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,KAAK,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AACnC,MAAM,MAAM,OAAO,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,CAAC;AASnD,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,MAAM,CA6DtE,CAAC;AAoWF,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAS3D;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI5E"}
@@ -0,0 +1,429 @@
1
+ import { esc } from "../../core/sanitize.js";
2
+ const LOCALE = "pt-BR";
3
+ const PIPES = {
4
+ /** Formata como moeda brasileira (R$). */
5
+ currency: (v) => {
6
+ if (v == null || v === "") return "";
7
+ try {
8
+ return new Intl.NumberFormat(LOCALE, {
9
+ style: "currency",
10
+ currency: "BRL"
11
+ }).format(Number(v));
12
+ } catch {
13
+ return String(v);
14
+ }
15
+ },
16
+ /** Formata como data. Estilos: short | medium | long | full (default medium). */
17
+ date: (v, arg) => {
18
+ const d = toDate(v);
19
+ if (!d) return String(v ?? "");
20
+ const styles = {
21
+ short: { dateStyle: "short" },
22
+ medium: { dateStyle: "medium" },
23
+ long: { dateStyle: "long" },
24
+ full: { dateStyle: "full" }
25
+ };
26
+ const opts = arg && styles[arg] ? styles[arg] : styles.medium;
27
+ try {
28
+ return new Intl.DateTimeFormat(LOCALE, opts).format(d);
29
+ } catch {
30
+ return d.toLocaleDateString(LOCALE);
31
+ }
32
+ },
33
+ /** Formata como data + hora (medium/short). */
34
+ dateTime: (v) => {
35
+ const d = toDate(v);
36
+ if (!d) return String(v ?? "");
37
+ try {
38
+ return new Intl.DateTimeFormat(LOCALE, {
39
+ dateStyle: "medium",
40
+ timeStyle: "short"
41
+ }).format(d);
42
+ } catch {
43
+ return d.toLocaleString(LOCALE);
44
+ }
45
+ },
46
+ /** Formata como número (pt-BR). Opcional: `number: 2` (casas decimais). */
47
+ number: (v, arg) => {
48
+ if (v == null || v === "") return "";
49
+ try {
50
+ const opt = {};
51
+ if (arg !== void 0) {
52
+ const dec = Number(arg);
53
+ opt.minimumFractionDigits = dec;
54
+ opt.maximumFractionDigits = dec;
55
+ }
56
+ return new Intl.NumberFormat(LOCALE, opt).format(Number(v));
57
+ } catch {
58
+ return String(v);
59
+ }
60
+ }
61
+ };
62
+ function toDate(v) {
63
+ if (v == null || v === "") return null;
64
+ if (v instanceof Date) return isNaN(v.getTime()) ? null : v;
65
+ if (typeof v === "number") {
66
+ const d = new Date(v);
67
+ return isNaN(d.getTime()) ? null : d;
68
+ }
69
+ if (typeof v === "string") {
70
+ const d = new Date(v);
71
+ return isNaN(d.getTime()) ? null : d;
72
+ }
73
+ return null;
74
+ }
75
+ const IDENT_START = (ch) => /[a-z_$]/i.test(ch);
76
+ const IDENT_PART = (ch) => /[a-z0-9_$]/i.test(ch);
77
+ function tokenize(expr) {
78
+ const tokens = [];
79
+ let i = 0;
80
+ while (i < expr.length) {
81
+ const ch = expr[i];
82
+ if (/\s/.test(ch)) {
83
+ i++;
84
+ continue;
85
+ }
86
+ if (ch === "'" || ch === '"') {
87
+ const quote = ch;
88
+ let str = "";
89
+ i++;
90
+ while (i < expr.length && expr[i] !== quote) {
91
+ if (expr[i] === "\\" && i + 1 < expr.length) {
92
+ str += expr[i + 1];
93
+ i += 2;
94
+ } else {
95
+ str += expr[i];
96
+ i++;
97
+ }
98
+ }
99
+ i++;
100
+ tokens.push({ type: "str", value: str });
101
+ continue;
102
+ }
103
+ if (ch >= "0" && ch <= "9") {
104
+ let numStr = "";
105
+ while (i < expr.length && (expr[i] >= "0" && expr[i] <= "9" || expr[i] === ".")) {
106
+ numStr += expr[i];
107
+ i++;
108
+ }
109
+ tokens.push({ type: "num", value: numStr });
110
+ continue;
111
+ }
112
+ if (IDENT_START(ch)) {
113
+ let ident = "";
114
+ while (i < expr.length && IDENT_PART(expr[i])) {
115
+ ident += expr[i];
116
+ i++;
117
+ }
118
+ if (ident === "true" || ident === "false" || ident === "null") {
119
+ tokens.push({ type: "kw", value: ident });
120
+ } else {
121
+ tokens.push({ type: "id", value: ident });
122
+ }
123
+ continue;
124
+ }
125
+ const two = expr.slice(i, i + 2);
126
+ if (two === "==" || two === "!=" || two === ">=" || two === "<=") {
127
+ tokens.push({ type: "cmp", value: two });
128
+ i += 2;
129
+ continue;
130
+ }
131
+ if (ch === "+") {
132
+ tokens.push({ type: "op", value: "+" });
133
+ i++;
134
+ continue;
135
+ }
136
+ if (ch === "-") {
137
+ tokens.push({ type: "op", value: "-" });
138
+ i++;
139
+ continue;
140
+ }
141
+ if (ch === "*") {
142
+ tokens.push({ type: "op", value: "*" });
143
+ i++;
144
+ continue;
145
+ }
146
+ if (ch === "/") {
147
+ tokens.push({ type: "op", value: "/" });
148
+ i++;
149
+ continue;
150
+ }
151
+ if (ch === ">") {
152
+ tokens.push({ type: "cmp", value: ">" });
153
+ i++;
154
+ continue;
155
+ }
156
+ if (ch === "<") {
157
+ tokens.push({ type: "cmp", value: "<" });
158
+ i++;
159
+ continue;
160
+ }
161
+ if (ch === "!") {
162
+ tokens.push({ type: "op", value: "!" });
163
+ i++;
164
+ continue;
165
+ }
166
+ if (ch === "?") {
167
+ tokens.push({ type: "qm", value: "?" });
168
+ i++;
169
+ continue;
170
+ }
171
+ if (ch === ":") {
172
+ tokens.push({ type: "colon", value: ":" });
173
+ i++;
174
+ continue;
175
+ }
176
+ if (ch === "|") {
177
+ tokens.push({ type: "pipe", value: "|" });
178
+ i++;
179
+ continue;
180
+ }
181
+ if (ch === "(") {
182
+ tokens.push({ type: "lparen", value: "(" });
183
+ i++;
184
+ continue;
185
+ }
186
+ if (ch === ")") {
187
+ tokens.push({ type: "rparen", value: ")" });
188
+ i++;
189
+ continue;
190
+ }
191
+ if (ch === ".") {
192
+ tokens.push({ type: "dot", value: "." });
193
+ i++;
194
+ continue;
195
+ }
196
+ i++;
197
+ }
198
+ return tokens;
199
+ }
200
+ class Parser {
201
+ constructor(tokens, ctx) {
202
+ this.tokens = tokens;
203
+ this.ctx = ctx;
204
+ this.pos = 0;
205
+ }
206
+ peek() {
207
+ return this.pos < this.tokens.length ? this.tokens[this.pos] : null;
208
+ }
209
+ next() {
210
+ return this.pos < this.tokens.length ? this.tokens[this.pos++] : null;
211
+ }
212
+ matchType(type) {
213
+ if (this.peek()?.type === type) return this.next();
214
+ return null;
215
+ }
216
+ parse() {
217
+ return this.parsePipe();
218
+ }
219
+ /* pipeExpr := ternary ('|' IDENT (':' pipeArg)? )* */
220
+ parsePipe() {
221
+ let val = this.parseTernary();
222
+ while (this.peek()?.type === "pipe") {
223
+ this.next();
224
+ const nameTok = this.matchType("id");
225
+ if (!nameTok) break;
226
+ let arg;
227
+ if (this.peek()?.type === "colon") {
228
+ this.next();
229
+ const argTok = this.peek();
230
+ if (argTok && (argTok.type === "str" || argTok.type === "num" || argTok.type === "id")) {
231
+ arg = argTok.type === "str" ? argTok.value : String(argTok.value);
232
+ this.next();
233
+ }
234
+ }
235
+ val = this.applyPipe(nameTok.value, arg, val);
236
+ }
237
+ return val;
238
+ }
239
+ /* ternary := comparison ('?' pipeExpr ':' pipeExpr)? */
240
+ parseTernary() {
241
+ const cond = this.parseComparison();
242
+ if (this.peek()?.type === "qm") {
243
+ this.next();
244
+ const trueVal = this.parsePipe();
245
+ if (this.peek()?.type === "colon") {
246
+ this.next();
247
+ const falseVal = this.parsePipe();
248
+ return cond ? trueVal : falseVal;
249
+ }
250
+ return trueVal;
251
+ }
252
+ return cond;
253
+ }
254
+ /* comparison := additive (COMP_OP additive)? */
255
+ parseComparison() {
256
+ const left = this.parseAdditive();
257
+ const tok = this.peek();
258
+ if (tok?.type === "cmp") {
259
+ this.next();
260
+ const right = this.parseAdditive();
261
+ return this.compare(left, tok.value, right);
262
+ }
263
+ return left;
264
+ }
265
+ compare(left, op, right) {
266
+ const l = toComparable(left);
267
+ const r = toComparable(right);
268
+ switch (op) {
269
+ case "==":
270
+ return l == r;
271
+ case "!=":
272
+ return l != r;
273
+ case ">":
274
+ return l > r;
275
+ case "<":
276
+ return l < r;
277
+ case ">=":
278
+ return l >= r;
279
+ case "<=":
280
+ return l <= r;
281
+ }
282
+ return false;
283
+ }
284
+ /* additive := multiplicative (('+' | '-') multiplicative)* */
285
+ parseAdditive() {
286
+ let left = this.parseMultiplicative();
287
+ for (; ; ) {
288
+ const tok = this.peek();
289
+ if (tok?.type === "op" && (tok.value === "+" || tok.value === "-")) {
290
+ this.next();
291
+ const right = this.parseMultiplicative();
292
+ left = tok.value === "+" ? this.add(left, right) : this.subtract(left, right);
293
+ } else break;
294
+ }
295
+ return left;
296
+ }
297
+ add(l, r) {
298
+ if (typeof l === "string" || typeof r === "string") {
299
+ return String(l ?? "") + String(r ?? "");
300
+ }
301
+ return Number(l) + Number(r);
302
+ }
303
+ subtract(l, r) {
304
+ return Number(l) - Number(r);
305
+ }
306
+ /* multiplicative := unary (('*' | '/') unary)* */
307
+ parseMultiplicative() {
308
+ let left = this.parseUnary();
309
+ for (; ; ) {
310
+ const tok = this.peek();
311
+ if (tok?.type === "op" && (tok.value === "*" || tok.value === "/")) {
312
+ this.next();
313
+ const right = this.parseUnary();
314
+ left = tok.value === "*" ? Number(left) * Number(right) : Number(left) / Number(right);
315
+ } else break;
316
+ }
317
+ return left;
318
+ }
319
+ /* unary := ('+' | '-' | '!') unary | primary */
320
+ parseUnary() {
321
+ const tok = this.peek();
322
+ if (tok?.type === "op" && (tok.value === "+" || tok.value === "-")) {
323
+ this.next();
324
+ const val = Number(this.parseUnary());
325
+ return tok.value === "-" ? -val : val;
326
+ }
327
+ if (tok?.type === "op" && tok.value === "!") {
328
+ this.next();
329
+ return !this.parseUnary();
330
+ }
331
+ return this.parsePrimary();
332
+ }
333
+ /* primary := STRING | NUMBER | kw | id ( '.' id )* | '(' pipeExpr ')' */
334
+ parsePrimary() {
335
+ const tok = this.peek();
336
+ if (!tok) return void 0;
337
+ if (tok.type === "lparen") {
338
+ this.next();
339
+ const val = this.parsePipe();
340
+ this.matchType("rparen");
341
+ return val;
342
+ }
343
+ if (tok.type === "str") {
344
+ this.next();
345
+ return tok.value;
346
+ }
347
+ if (tok.type === "num") {
348
+ this.next();
349
+ return Number(tok.value);
350
+ }
351
+ if (tok.type === "kw") {
352
+ this.next();
353
+ return tok.value === "true" ? true : tok.value === "false" ? false : null;
354
+ }
355
+ if (tok.type === "id") {
356
+ this.next();
357
+ let result = this.resolveId(tok.value);
358
+ while (this.peek()?.type === "dot") {
359
+ this.next();
360
+ const propTok = this.peek();
361
+ if (propTok?.type === "id") {
362
+ this.next();
363
+ result = result == null ? void 0 : result[propTok.value];
364
+ } else break;
365
+ }
366
+ return result;
367
+ }
368
+ this.next();
369
+ return void 0;
370
+ }
371
+ resolveId(name) {
372
+ switch (name) {
373
+ case "value":
374
+ return this.ctx.value;
375
+ case "row":
376
+ return this.ctx.row;
377
+ case "true":
378
+ return true;
379
+ case "false":
380
+ return false;
381
+ case "null":
382
+ return null;
383
+ default:
384
+ return this.ctx.row[name];
385
+ }
386
+ }
387
+ applyPipe(name, arg, input) {
388
+ const pipe = PIPES[name];
389
+ if (!pipe) return formatDefault(input);
390
+ try {
391
+ return pipe(input, arg);
392
+ } catch {
393
+ return formatDefault(input);
394
+ }
395
+ }
396
+ }
397
+ function toComparable(v) {
398
+ if (typeof v === "number") return v;
399
+ if (typeof v === "boolean") return v ? 1 : 0;
400
+ return String(v ?? "");
401
+ }
402
+ function formatDefault(v) {
403
+ if (v == null) return "";
404
+ if (typeof v === "object") return "[object Object]";
405
+ return String(v);
406
+ }
407
+ function evaluate(expr, ctx) {
408
+ try {
409
+ const tokens = tokenize(expr);
410
+ const parser = new Parser(tokens, ctx);
411
+ const result = parser.parse();
412
+ return formatDefault(result);
413
+ } catch {
414
+ return "";
415
+ }
416
+ }
417
+ function renderCell(template, row, field) {
418
+ const ctx = { value: row[field], row };
419
+ return template.replace(/\{\{([^}]+)\}\}/g, (_, expr) => esc(evaluate(decodeEntities(expr.trim()), ctx)));
420
+ }
421
+ function decodeEntities(str) {
422
+ return str.replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&#39;/g, "'");
423
+ }
424
+ export {
425
+ PIPES,
426
+ evaluate,
427
+ renderCell
428
+ };
429
+ //# sourceMappingURL=expr.js.map