@wrrdev/fenix-ui 1.1.13 → 1.1.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 (161) hide show
  1. package/README.md +6 -5
  2. package/dist/components/autocomplete/autocomplete.d.ts +11 -5
  3. package/dist/components/autocomplete/autocomplete.d.ts.map +1 -1
  4. package/dist/components/autocomplete/autocomplete.js +18 -2
  5. package/dist/components/autocomplete/autocomplete.js.map +1 -1
  6. package/dist/components/button/button.d.ts +15 -0
  7. package/dist/components/button/button.d.ts.map +1 -1
  8. package/dist/components/button/button.js +20 -1
  9. package/dist/components/button/button.js.map +1 -1
  10. package/dist/components/checkbox/checkbox.d.ts +20 -4
  11. package/dist/components/checkbox/checkbox.d.ts.map +1 -1
  12. package/dist/components/checkbox/checkbox.js +39 -2
  13. package/dist/components/checkbox/checkbox.js.map +1 -1
  14. package/dist/components/datepicker/datepicker.d.ts +16 -2
  15. package/dist/components/datepicker/datepicker.d.ts.map +1 -1
  16. package/dist/components/datepicker/datepicker.js +46 -2
  17. package/dist/components/datepicker/datepicker.js.map +1 -1
  18. package/dist/components/editor/commands.d.ts +27 -0
  19. package/dist/components/editor/commands.d.ts.map +1 -0
  20. package/dist/components/editor/commands.js +235 -0
  21. package/dist/components/editor/commands.js.map +1 -0
  22. package/dist/components/editor/editor.d.ts +83 -0
  23. package/dist/components/editor/editor.d.ts.map +1 -0
  24. package/dist/components/editor/editor.js +1951 -0
  25. package/dist/components/editor/editor.js.map +1 -0
  26. package/dist/components/editor/index.d.ts +12 -0
  27. package/dist/components/editor/index.d.ts.map +1 -0
  28. package/dist/components/editor/index.js +7 -0
  29. package/dist/components/editor/index.js.map +1 -0
  30. package/dist/components/editor/normalize-html.d.ts +33 -0
  31. package/dist/components/editor/normalize-html.d.ts.map +1 -0
  32. package/dist/components/editor/normalize-html.js +316 -0
  33. package/dist/components/editor/normalize-html.js.map +1 -0
  34. package/dist/components/editor/sanitize-html.d.ts +37 -0
  35. package/dist/components/editor/sanitize-html.d.ts.map +1 -0
  36. package/dist/components/editor/sanitize-html.js +247 -0
  37. package/dist/components/editor/sanitize-html.js.map +1 -0
  38. package/dist/components/editor/schema.d.ts +36 -0
  39. package/dist/components/editor/schema.d.ts.map +1 -0
  40. package/dist/components/editor/schema.js +232 -0
  41. package/dist/components/editor/schema.js.map +1 -0
  42. package/dist/components/editor/selection.d.ts +22 -0
  43. package/dist/components/editor/selection.d.ts.map +1 -0
  44. package/dist/components/editor/selection.js +97 -0
  45. package/dist/components/editor/selection.js.map +1 -0
  46. package/dist/components/editor/styles.d.ts +10 -0
  47. package/dist/components/editor/styles.d.ts.map +1 -0
  48. package/dist/components/editor/styles.js +446 -0
  49. package/dist/components/editor/styles.js.map +1 -0
  50. package/dist/components/editor/table-utils.d.ts +33 -0
  51. package/dist/components/editor/table-utils.d.ts.map +1 -0
  52. package/dist/components/editor/table-utils.js +150 -0
  53. package/dist/components/editor/table-utils.js.map +1 -0
  54. package/dist/components/fileupload/fileupload.d.ts +10 -2
  55. package/dist/components/fileupload/fileupload.d.ts.map +1 -1
  56. package/dist/components/fileupload/fileupload.js +17 -2
  57. package/dist/components/fileupload/fileupload.js.map +1 -1
  58. package/dist/components/floatlabel/floatlabel.d.ts +9 -3
  59. package/dist/components/floatlabel/floatlabel.d.ts.map +1 -1
  60. package/dist/components/floatlabel/floatlabel.js +22 -7
  61. package/dist/components/floatlabel/floatlabel.js.map +1 -1
  62. package/dist/components/input/input.d.ts +18 -7
  63. package/dist/components/input/input.d.ts.map +1 -1
  64. package/dist/components/input/input.js +33 -3
  65. package/dist/components/input/input.js.map +1 -1
  66. package/dist/components/knob/knob.d.ts +7 -3
  67. package/dist/components/knob/knob.d.ts.map +1 -1
  68. package/dist/components/knob/knob.js +4 -2
  69. package/dist/components/knob/knob.js.map +1 -1
  70. package/dist/components/multiselect/multiselect.d.ts +13 -3
  71. package/dist/components/multiselect/multiselect.d.ts.map +1 -1
  72. package/dist/components/multiselect/multiselect.js +27 -2
  73. package/dist/components/multiselect/multiselect.js.map +1 -1
  74. package/dist/components/radio/radio.d.ts +27 -4
  75. package/dist/components/radio/radio.d.ts.map +1 -1
  76. package/dist/components/radio/radio.js +67 -8
  77. package/dist/components/radio/radio.js.map +1 -1
  78. package/dist/components/rating/rating.d.ts +8 -4
  79. package/dist/components/rating/rating.d.ts.map +1 -1
  80. package/dist/components/rating/rating.js +5 -2
  81. package/dist/components/rating/rating.js.map +1 -1
  82. package/dist/components/select/select.d.ts +9 -3
  83. package/dist/components/select/select.d.ts.map +1 -1
  84. package/dist/components/select/select.js +11 -2
  85. package/dist/components/select/select.js.map +1 -1
  86. package/dist/components/slider/slider.d.ts +12 -6
  87. package/dist/components/slider/slider.d.ts.map +1 -1
  88. package/dist/components/slider/slider.js +10 -2
  89. package/dist/components/slider/slider.js.map +1 -1
  90. package/dist/components/switch/switch.d.ts +20 -4
  91. package/dist/components/switch/switch.d.ts.map +1 -1
  92. package/dist/components/switch/switch.js +40 -2
  93. package/dist/components/switch/switch.js.map +1 -1
  94. package/dist/components/textarea/textarea.d.ts +12 -5
  95. package/dist/components/textarea/textarea.d.ts.map +1 -1
  96. package/dist/components/textarea/textarea.js +12 -2
  97. package/dist/components/textarea/textarea.js.map +1 -1
  98. package/dist/components/toast/toast.d.ts +27 -15
  99. package/dist/components/toast/toast.d.ts.map +1 -1
  100. package/dist/components/toast/toast.js +205 -32
  101. package/dist/components/toast/toast.js.map +1 -1
  102. package/dist/components/toggle-button-group/toggle-button-group.d.ts +13 -5
  103. package/dist/components/toggle-button-group/toggle-button-group.d.ts.map +1 -1
  104. package/dist/components/toggle-button-group/toggle-button-group.js +19 -2
  105. package/dist/components/toggle-button-group/toggle-button-group.js.map +1 -1
  106. package/dist/core/base.d.ts +7 -0
  107. package/dist/core/base.d.ts.map +1 -1
  108. package/dist/core/base.js +4 -1
  109. package/dist/core/base.js.map +1 -1
  110. package/dist/core/form-element.d.ts +81 -0
  111. package/dist/core/form-element.d.ts.map +1 -0
  112. package/dist/core/form-element.js +129 -0
  113. package/dist/core/form-element.js.map +1 -0
  114. package/dist/core/jsx.d.ts +25 -1
  115. package/dist/core/jsx.d.ts.map +1 -1
  116. package/dist/core/jsx.js.map +1 -1
  117. package/dist/core/presets.d.ts +8 -3
  118. package/dist/core/presets.d.ts.map +1 -1
  119. package/dist/core/presets.js +6 -17
  120. package/dist/core/presets.js.map +1 -1
  121. package/dist/core/types.d.ts +4 -0
  122. package/dist/core/types.d.ts.map +1 -1
  123. package/dist/core/vue.d.ts +3 -1
  124. package/dist/core/vue.d.ts.map +1 -1
  125. package/dist/core/vue.js.map +1 -1
  126. package/dist/docs/app.d.ts.map +1 -1
  127. package/dist/docs/componentes/autocomplete.doc.d.ts.map +1 -1
  128. package/dist/docs/componentes/checkbox.doc.d.ts.map +1 -1
  129. package/dist/docs/componentes/datepicker.doc.d.ts.map +1 -1
  130. package/dist/docs/componentes/editor.doc.d.ts +6 -0
  131. package/dist/docs/componentes/editor.doc.d.ts.map +1 -0
  132. package/dist/docs/componentes/fileupload.doc.d.ts.map +1 -1
  133. package/dist/docs/componentes/index.d.ts.map +1 -1
  134. package/dist/docs/componentes/input.doc.d.ts.map +1 -1
  135. package/dist/docs/componentes/knob.doc.d.ts.map +1 -1
  136. package/dist/docs/componentes/multiselect.doc.d.ts.map +1 -1
  137. package/dist/docs/componentes/rating.doc.d.ts.map +1 -1
  138. package/dist/docs/componentes/select.doc.d.ts.map +1 -1
  139. package/dist/docs/componentes/slider.doc.d.ts.map +1 -1
  140. package/dist/docs/componentes/switch.doc.d.ts.map +1 -1
  141. package/dist/docs/componentes/textarea.doc.d.ts.map +1 -1
  142. package/dist/docs/componentes/toast.doc.d.ts.map +1 -1
  143. package/dist/docs/componentes/toggle-button-group.doc.d.ts.map +1 -1
  144. package/dist/docs/frameworks.d.ts +75 -0
  145. package/dist/docs/frameworks.d.ts.map +1 -0
  146. package/dist/docs/ui/code.d.ts +4 -0
  147. package/dist/docs/ui/code.d.ts.map +1 -1
  148. package/dist/fenix-ui.umd.min.js +786 -254
  149. package/dist/fenix-ui2.esm.js.map +1 -1
  150. package/dist/fenix-ui4.esm.js +4396 -172
  151. package/dist/fenix-ui4.esm.js.map +1 -1
  152. package/dist/fenix-ui5.esm.js +1 -0
  153. package/dist/fenix-ui5.esm.js.map +1 -1
  154. package/dist/index.d.ts +2 -0
  155. package/dist/index.d.ts.map +1 -1
  156. package/dist/index.js +18 -1
  157. package/dist/index.js.map +1 -1
  158. package/dist/plugins/auto-import.d.ts.map +1 -1
  159. package/dist/plugins/auto-import.js +1 -0
  160. package/dist/plugins/auto-import.js.map +1 -1
  161. package/package.json +5 -1
@@ -1,8 +1,8 @@
1
- import { FxElement } from "../../core/base.js";
1
+ import { FxFormElement } from "../../core/form-element.js";
2
2
  import { css } from "../../core/css.js";
3
3
  import { defineElement } from "../../core/define.js";
4
4
  import { esc } from "../../core/sanitize.js";
5
- const _FxDatepicker = class _FxDatepicker extends FxElement {
5
+ const _FxDatepicker = class _FxDatepicker extends FxFormElement {
6
6
  constructor() {
7
7
  super(...arguments);
8
8
  this.time = { h: 0, m: 0, s: 0 };
@@ -10,6 +10,32 @@ const _FxDatepicker = class _FxDatepicker extends FxElement {
10
10
  static get observedAttributes() {
11
11
  return ["value", "start", "end", "values", "mode", "min", "max", "placeholder", "disabled", "format", "show-time", "free-text", "error", "invalid", "success", "valid"];
12
12
  }
13
+ attributeChangedCallback(name, oldValue, newValue) {
14
+ super.attributeChangedCallback(name, oldValue, newValue);
15
+ if (name === "value" || name === "start" || name === "end" || name === "values") {
16
+ this.updateFormValue();
17
+ }
18
+ }
19
+ /** No FormData do form, conforme o modo (single/range/multiple). */
20
+ getFormValue() {
21
+ if (this.mode === "range") {
22
+ const start = this.getAttr("start");
23
+ const end = this.getAttr("end");
24
+ if (!this.name || !start && !end) return null;
25
+ const fd = new FormData();
26
+ if (start) fd.append(`${this.name}-start`, start);
27
+ if (end) fd.append(`${this.name}-end`, end);
28
+ return fd;
29
+ }
30
+ if (this.mode === "multiple") {
31
+ const values = this.values;
32
+ if (!this.name || values.length === 0) return null;
33
+ const fd = new FormData();
34
+ values.forEach((v) => fd.append(this.name, v));
35
+ return fd;
36
+ }
37
+ return this.getAttr("value") || null;
38
+ }
13
39
  get mode() {
14
40
  const m = this.getAttr("mode", "single");
15
41
  return ["single", "range", "multiple"].includes(m) ? m : "single";
@@ -38,6 +64,14 @@ const _FxDatepicker = class _FxDatepicker extends FxElement {
38
64
  this.toggleAttr("disabled", Boolean(value));
39
65
  }
40
66
  connectedCallback() {
67
+ if (!this._initialState) {
68
+ this._initialState = {
69
+ value: this.getAttr("value"),
70
+ start: this.getAttr("start"),
71
+ end: this.getAttr("end"),
72
+ values: this.getAttr("values")
73
+ };
74
+ }
41
75
  super.connectedCallback();
42
76
  this.docListener = (e) => {
43
77
  if (!this.hasAttr("open")) return;
@@ -50,6 +84,15 @@ const _FxDatepicker = class _FxDatepicker extends FxElement {
50
84
  super.disconnectedCallback();
51
85
  if (this.docListener) document.removeEventListener("click", this.docListener);
52
86
  }
87
+ /** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */
88
+ formResetCallback() {
89
+ const s = this._initialState ?? { value: "", start: "", end: "", values: "" };
90
+ this.setAttribute("value", s.value);
91
+ this.setAttribute("start", s.start);
92
+ this.setAttribute("end", s.end);
93
+ this.setAttribute("values", s.values);
94
+ this.updateFormValue();
95
+ }
53
96
  /** Formata uma data ISO ("YYYY-MM-DD" ou com "THH:mm:ss") segundo o atributo format. */
54
97
  formatDate(iso) {
55
98
  if (!iso) return "";
@@ -222,6 +265,7 @@ const _FxDatepicker = class _FxDatepicker extends FxElement {
222
265
  }
223
266
  }
224
267
  emitChange(detail) {
268
+ this.emitValueEvents(detail);
225
269
  this.dispatchEvent(
226
270
  new CustomEvent("change", { bubbles: true, composed: true, detail })
227
271
  );
@@ -1 +1 @@
1
- {"version":3,"file":"datepicker.js","sources":["../../../src/components/datepicker/datepicker.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-datepicker> — Campo de data com calendário suspenso.\n *\n * Um input com ícone de calendário no final; ao clicar, abre o\n * <fx-calendar> em um popover. As datas selecionadas aparecem no input.\n *\n * Atributos:\n * - mode=\"single\" | \"range\" | \"multiple\" (padrão: single)\n * - value=\"YYYY-MM-DD\" → single\n * - start/end → range\n * - values=\"a,b,c\" → múltiplas datas (CSV)\n * - min/max, placeholder, disabled, clearable, size (sm|md|lg)\n * - format=\"dd/mm/yyyy\" → máscara de exibição (tokens: dd mm yyyy, HH MM SS)\n * - show-time → inclui seleção de hora:minuto:segundo no popover;\n * o valor passa a ser \"YYYY-MM-DDTHH:mm:ss\"\n *\n * Evento: `change` (composed):\n * - single: { value }\n * - range: { value, start, end }\n * - multiple: { values: string[] }\n */\nexport class FxDatepicker 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 .field {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs);\n width: 100%;\n box-sizing: border-box;\n /* Base = tamanho padrão (md): 40px */\n min-height: var(--fx-size-md, 40px);\n min-width: 150px;\n padding: var(--fx-space-xs) var(--fx-space-md);\n /* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\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 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 :host([size='sm']) .field { min-height: var(--fx-size-sm); min-width: 180px; }\n :host([size='md']) .field { min-height: var(--fx-size-md); }\n :host([size='lg']) .field { min-height: var(--fx-size-lg); min-width: 220px; font-size: calc(var(--fx-font-size) + 4px); }\n /* Range e multiple exibem mais de uma data: campo mais largo */\n :host([mode='range']) .field { min-width: 250px; }\n :host([mode='multiple']) .field { min-width: 220px; }\n :host([mode='range'][size='lg']) .field { min-width: 360px; }\n :host([mode='multiple'][size='lg']) .field { min-width: 320px; }\n :host([show-time]) .field { min-width: 250px; }\n /* Full width: o host estica até o pai (os min-width dos modos são\n descartados para permitir layouts estreitos sob controle do pai). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { min-width: 0; }\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]) .field,\n :host([invalid]) .field {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([error]) .field:focus-within,\n :host([invalid]) .field:focus-within,\n :host([error][open]) .field,\n :host([invalid][open]) .field {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .field,\n :host([valid]) .field {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([success]) .field:focus-within,\n :host([valid]) .field:focus-within,\n :host([success][open]) .field,\n :host([valid][open]) .field {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-within {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .display {\n flex: 1;\n border: none;\n background: none;\n font: inherit;\n color: var(--fx-text-default);\n outline: none;\n cursor: pointer;\n text-align: left;\n padding: 0;\n min-width: 0;\n }\n .display::placeholder { color: var(--fx-text-muted); }\n .cal-icon { color: var(--fx-text-muted); display: inline-flex; pointer-events: none; }\n .clear {\n border: none;\n background: none;\n color: var(--fx-text-muted);\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n padding: 0 2px;\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n .pop {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\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 overflow: hidden;\n }\n :host([up]) .pop {\n top: auto;\n bottom: calc(100% + 4px);\n }\n :host([open]) .pop { display: block; }\n .time-row {\n display: flex;\n align-items: center;\n gap: var(--fx-space-xs);\n padding: var(--fx-space-sm) var(--fx-space-md);\n border-top: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n border-radius: 0 0 var(--fx-radius-md) var(--fx-radius-md);\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-text-default);\n }\n .time-row label { color: var(--fx-text-muted); }\n .time-row input {\n width: 38px;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n padding: 2px 2px;\n outline: none;\n text-align: center;\n -moz-appearance: textfield;\n appearance: textfield;\n }\n .time-row input::-webkit-outer-spin-button,\n .time-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n .time-row .sep { color: var(--fx-text-muted); font-weight: bold; padding: 0 1px; }\n .time-row input:focus {\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([disabled]) .field { opacity: 0.55; cursor: not-allowed; background: var(--fx-surface-surface-hover); }\n :host([disabled]) .display { cursor: not-allowed; }\n :host([free-text]) .display { cursor: text; }\n :host([free-text]) .display.invalid { border-bottom: 2px solid var(--fx-color-danger); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['value', 'start', 'end', 'values', 'mode', 'min', 'max', 'placeholder', 'disabled', 'format', 'show-time', 'free-text', 'error', 'invalid', 'success', 'valid'];\n }\n\n private docListener?: (e: Event) => void;\n /** Hora/minuto/segundo do modo show-time. */\n private time = { h: 0, m: 0, s: 0 };\n\n get mode(): string {\n const m = this.getAttr('mode', 'single');\n return ['single', 'range', 'multiple'].includes(m) ? m : 'single';\n }\n set mode(value: string) {\n this.setAttribute('mode', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(v: string) {\n this.setAttribute('value', v);\n }\n\n /** Datas do modo múltiplo (CSV no atributo). */\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 protected override connectedCallback(): void {\n super.connectedCallback();\n // Fecha o popover 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 };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n /** Formata uma data ISO (\"YYYY-MM-DD\" ou com \"THH:mm:ss\") segundo o atributo format. */\n private formatDate(iso: string): string {\n if (!iso) return '';\n const fmt = this.getAttr('format');\n // Sem máscara: exibe apenas a parte da data (a hora entra via timeText\n // quando show-time está ativo — nunca duplicada).\n if (!fmt) return iso.split('T')[0]!;\n const [datePart, timePart] = iso.split('T');\n const [y = '', m = '', d = ''] = datePart.split('-');\n const [H = '00', M = '00', S = '00'] = (timePart ?? '').split(':');\n // Placeholders para evitar colisão entre mm (mês) e MM (minuto).\n return fmt\n .replace(/yyyy/gi, '\\u0001')\n .replace(/MM/g, '\\u0005')\n .replace(/mm/g, '\\u0002')\n .replace(/dd/gi, '\\u0003')\n .replace(/HH/g, '\\u0004')\n .replace(/SS/gi, '\\u0006')\n .replace(/\\u0001/g, y)\n .replace(/\\u0002/g, m)\n .replace(/\\u0003/g, d)\n .replace(/\\u0004/g, H)\n .replace(/\\u0005/g, M)\n .replace(/\\u0006/g, S);\n }\n\n /** Texto exibido no input conforme o modo. */\n private displayText(): string {\n if (this.mode === 'range') {\n const s = this.getAttr('start');\n const e = this.getAttr('end');\n const arrow = this.hasAttr('show-time') ? ' ' : ' → ';\n if (!s && !e) return '';\n if (this.hasAttr('show-time')) {\n const f = (iso?: string): string => (iso ? `${this.formatDate(iso)} ${this.timeText()}` : iso ? `${this.formatDate(iso)} …` : '');\n return s && e ? `${f(s)} → ${f(e)}` : f(s);\n }\n void arrow;\n return s && e ? `${this.formatDate(s)} → ${this.formatDate(e)}` : s ? `${this.formatDate(s)} → …` : '';\n }\n if (this.mode === 'multiple') {\n return this.values.map((v) => this.formatDate(v)).join(', ');\n }\n const base = this.formatDate(this.value);\n return base && this.hasAttr('show-time') ? `${base} ${this.timeText()}` : base;\n }\n\n /** \"HH:MM:SS\" do estado interno de show-time. */\n private timeText(): string {\n const p = (n: number): string => String(n).padStart(2, '0');\n return `${p(this.time.h)}:${p(this.time.m)}:${p(this.time.s)}`;\n }\n\n /** Extrai hora de um valor ISO inicial, se houver. */\n private loadTimeFromValue(): void {\n const src = this.value || this.getAttr('start') || '';\n const t = src.split('T')[1];\n if (!t) return;\n const [h, m, s] = t.split(':').map((n) => parseInt(n, 10));\n this.time = { h: h || 0, m: m || 0, s: s || 0 };\n }\n\n /** Anexa o horário interno ao ISO puro vindo do calendário. */\n private withTime(iso: string): string {\n if (!iso) return iso;\n return this.hasAttr('show-time')\n ? `${iso}T${this.timeText()}`\n : iso;\n }\n\n protected override render(): void {\n const disabled = this.disabled;\n const clearable = this.hasAttr('clearable');\n const text = this.displayText();\n const hasValue = text.length > 0;\n const showTime = this.hasAttr('show-time');\n this.loadTimeFromValue();\n\n // Atributos repassados ao calendário interno, conforme o modo.\n let calAttrs = `min=\"${esc(this.getAttr('min'))}\" max=\"${esc(this.getAttr('max'))}\"`;\n if (this.mode === 'range') {\n calAttrs += ` range start=\"${esc(this.getAttr('start'))}\" end=\"${esc(this.getAttr('end'))}\"`;\n } else if (this.mode === 'multiple') {\n // Repassa apenas as partes de data (sem hora) para o calendário pintar.\n const dates = this.values.map((v) => v.split('T')[0]).join(',');\n calAttrs += ` mode=\"multiple\" values=\"${esc(dates)}\"`;\n } else {\n const v = this.value.split('T')[0] ?? '';\n calAttrs += ` value=\"${esc(v)}\"`;\n }\n\n this.setTemplate(`\n <div class=\"field\" part=\"field\">\n <input class=\"display\" part=\"display\" type=\"text\" ${this.hasAttr('free-text') && !disabled ? '' : 'readonly'}\n placeholder=\"${esc(this.getAttr('placeholder', 'dd/mm/aaaa'))}\"\n value=\"${esc(text)}\" ${disabled ? 'disabled' : ''}>\n <button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\"\n ${clearable && hasValue ? '' : 'hidden'}>\n ×\n </button>\n <span class=\"cal-icon\" part=\"icon\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M16 2v4M8 2v4M3 10h18\"/>\n </svg>\n </span>\n </div>\n <div class=\"pop\" part=\"popover\">\n <fx-calendar ${calAttrs}></fx-calendar>\n ${\n showTime\n ? `<div class=\"time-row\" part=\"time\">\n <input class=\"t-h\" type=\"number\" min=\"0\" max=\"23\" value=\"${this.time.h}\" aria-label=\"Hora\"><span class=\"sep\">:</span>\n <input class=\"t-m\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.m}\" aria-label=\"Minuto\"><span class=\"sep\">:</span>\n <input class=\"t-s\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.s}\" aria-label=\"Segundo\">\n </div>`\n : ''\n }\n </div>\n `);\n\n const field = this.root.querySelector('.field');\n if (!field) return;\n if (disabled) return;\n\n field.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.clear')) return;\n // Com free-text, clicar no próprio input serve para digitar — não abre.\n if (this.hasAttr('free-text') && (e.target as HTMLElement).classList.contains('display')) 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 + 340 > window.innerHeight && r.top > 340);\n } else {\n this.removeAttribute('up');\n }\n });\n\n // Limpar: zera tudo conforme o modo.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.removeAttribute('value');\n this.removeAttribute('start');\n this.removeAttribute('end');\n this.removeAttribute('values');\n this.removeAttribute('open');\n this.render();\n this.emitChange({});\n });\n\n // Sincroniza seleção do calendário de volta para os atributos.\n const cal = this.root.querySelector<HTMLElement>('.pop fx-calendar');\n cal?.addEventListener('change', (e) => {\n const d = (e as CustomEvent).detail ?? {};\n if (d.start !== undefined || d.end !== undefined) {\n if (d.start) this.setAttribute('start', this.withTime(d.start));\n else this.removeAttribute('start');\n if (d.end) this.setAttribute('end', this.withTime(d.end));\n else this.removeAttribute('end');\n }\n if (d.values !== undefined) {\n this.values = d.values.map((v: string) => this.withTime(v));\n }\n if (d.value !== undefined) {\n if (d.value) this.setAttribute('value', this.withTime(d.value));\n else this.removeAttribute('value');\n // No modo single, fecha ao escolher.\n if (this.mode === 'single') this.removeAttribute('open');\n }\n this.render();\n this.emitChange(d);\n });\n\n // free-text: valida ao sair do campo ou pressionar Enter.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display && this.hasAttr('free-text') && !disabled) {\n display.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.commitTyped(display);\n }\n });\n display.addEventListener('blur', () => this.commitTyped(display));\n }\n\n // show-time: captura alterações de hora/minuto/segundo.\n if (showTime) {\n const applyTime = (): void => {\n const h = this.root.querySelector<HTMLInputElement>('.t-h');\n const m = this.root.querySelector<HTMLInputElement>('.t-m');\n const s = this.root.querySelector<HTMLInputElement>('.t-s');\n const clamp = (v: string | undefined, max: number): number =>\n Math.min(max, Math.max(0, parseInt(v ?? '0', 10) || 0));\n this.time = {\n h: clamp(h?.value, 23),\n m: clamp(m?.value, 59),\n s: clamp(s?.value, 59),\n };\n // Reanexa o horário aos valores já selecionados.\n const reattach = (iso: string): string => iso.split('T')[0] ? `${iso.split('T')[0]}T${this.timeText()}` : iso;\n if (this.value) this.setAttribute('value', reattach(this.value));\n if (this.getAttr('start')) this.setAttribute('start', reattach(this.getAttr('start')));\n if (this.getAttr('end')) this.setAttribute('end', reattach(this.getAttr('end')));\n if (this.values.length) {\n this.values = this.values.map((v) => reattach(v));\n }\n // Atualiza apenas o texto do input, sem re-render completo.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display) display.value = this.displayText();\n this.emitChange({ value: this.value });\n };\n ['.t-h', '.t-m', '.t-s'].forEach((sel) => {\n this.root.querySelector(sel)?.addEventListener('input', applyTime);\n });\n }\n }\n\n private emitChange(detail: Record<string, unknown>): void {\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail }),\n );\n }\n\n /**\n * Interpreta o texto digitado livremente e devolve ISO \"YYYY-MM-DD\"\n * (ou null se inválido). Aceita a máscara do atributo `format`\n * (tokens dd/mm/yyyy) ou ISO direto.\n */\n private parseTyped(raw: string): string | null {\n const text = raw.trim();\n if (!text) return null;\n const fmt = this.getAttr('format', 'dd/mm/yyyy').toLowerCase();\n\n // Ordem dos tokens conforme a máscara informada.\n const order = ['dd', 'mm', 'yyyy'].filter((t) => fmt.includes(t));\n if (order.length < 3) return null;\n\n const parts = text.split(new RegExp(`[^0-9]+`)).filter(Boolean);\n if (parts.length !== 3) return null;\n\n const map: Record<string, string> = {};\n order.forEach((t, i) => (map[t] = parts[i]!));\n const iso = `${map['yyyy']}-${(map['mm'] ?? '').padStart(2, '0')}-${(map['dd'] ?? '').padStart(2, '0')}`;\n\n // Validação de data real.\n const d = new Date(`${iso}T12:00:00`);\n if (Number.isNaN(d.getTime())) return null;\n const [y, m, day] = iso.split('-').map((n) => parseInt(n, 10));\n if (d.getFullYear() !== y || d.getMonth() + 1 !== m || d.getDate() !== day) return null;\n\n // Limites min/max (apenas a parte da data).\n const cmp = (a: string, b: string): number => a.split('T')[0]!.localeCompare(b.split('T')[0]!);\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n if (min && cmp(iso, min) < 0) return null;\n if (max && cmp(iso, max) > 0) return null;\n\n return iso;\n }\n\n /** Aplica o texto digitado no modo free-text (blur/Enter). */\n private commitTyped(input: HTMLInputElement): void {\n const parsed = this.parseTyped(input.value);\n if (!parsed) {\n // Inválido: sinaliza, reverte o texto e emite evento.\n input.classList.add('invalid');\n this.dispatchEvent(\n new CustomEvent('invalid', { bubbles: true, composed: true, detail: { text: input.value } }),\n );\n input.value = this.displayText();\n setTimeout(() => input.classList.remove('invalid'), 1500);\n return;\n }\n if (this.mode === 'single') {\n this.setAttribute('value', this.withTime(parsed));\n this.emitChange({ value: this.value });\n } else if (this.mode === 'range') {\n // Primeira digitação vira start; com start preenchido, vira end.\n if (!this.getAttr('start') || (this.getAttr('start') && this.getAttr('end'))) {\n this.setAttribute('start', this.withTime(parsed));\n this.removeAttribute('end');\n } else {\n const [a, b] = [this.getAttr('start')!.split('T')[0]!, parsed].sort();\n this.setAttribute('start', this.withTime(a));\n this.setAttribute('end', this.withTime(b));\n }\n this.emitChange({ start: this.getAttr('start'), end: this.getAttr('end') });\n } else {\n const list = this.values.filter((v) => v.split('T')[0] !== parsed);\n list.push(this.withTime(parsed));\n this.values = list;\n this.emitChange({ values: this.values });\n }\n this.render();\n }\n}\n\nexport function defineFxDatepicker(): typeof FxDatepicker {\n return defineElement('fx-datepicker', FxDatepicker);\n}\n\n"],"names":["display"],"mappings":";;;;AA0BO,MAAM,gBAAN,MAAM,sBAAqB,UAAU;AAAA,EAArC,cAAA;AAAA,UAAA,GAAA,SAAA;AAiKL,SAAQ,OAAO,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,EAAA;AAAA,EAAE;AAAA,EANlC,WAAoB,qBAA+B;AACjD,WAAO,CAAC,SAAS,SAAS,OAAO,UAAU,QAAQ,OAAO,OAAO,eAAe,YAAY,UAAU,aAAa,aAAa,SAAS,WAAW,WAAW,OAAO;AAAA,EACxK;AAAA,EAMA,IAAI,OAAe;AACjB,UAAM,IAAI,KAAK,QAAQ,QAAQ,QAAQ;AACvC,WAAO,CAAC,UAAU,SAAS,UAAU,EAAE,SAAS,CAAC,IAAI,IAAI;AAAA,EAC3D;AAAA,EACA,IAAI,KAAK,OAAe;AACtB,SAAK,aAAa,QAAQ,KAAK;AAAA,EACjC;AAAA,EAEA,IAAI,QAAgB;AAClB,WAAO,KAAK,QAAQ,OAAO;AAAA,EAC7B;AAAA,EACA,IAAI,MAAM,GAAW;AACnB,SAAK,aAAa,SAAS,CAAC;AAAA,EAC9B;AAAA;AAAA,EAGA,IAAI,SAAmB;AACrB,UAAM,MAAM,KAAK,QAAQ,QAAQ;AACjC,WAAO,MAAM,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,OAAO,IAAI,CAAA;AAAA,EACrE;AAAA,EACA,IAAI,OAAO,MAAgB;AACzB,SAAK,aAAa,UAAU,KAAK,KAAK,GAAG,CAAC;AAAA,EAC5C;AAAA,EAEA,IAAI,WAAoB;AACtB,WAAO,KAAK,QAAQ,UAAU;AAAA,EAChC;AAAA,EACA,IAAI,SAAS,OAAgB;AAC3B,SAAK,WAAW,YAAY,QAAQ,KAAK,CAAC;AAAA,EAC5C;AAAA,EAEmB,oBAA0B;AAC3C,UAAM,kBAAA;AAEN,SAAK,cAAc,CAAC,MAAa;AAC/B,UAAI,CAAC,KAAK,QAAQ,MAAM,EAAG;AAC3B,UAAI,EAAE,aAAA,EAAe,SAAS,IAAI,EAAG;AACrC,WAAK,gBAAgB,MAAM;AAAA,IAC7B;AACA,aAAS,iBAAiB,SAAS,KAAK,WAAW;AAAA,EACrD;AAAA,EAEmB,uBAA6B;AAC9C,UAAM,qBAAA;AACN,QAAI,KAAK,YAAa,UAAS,oBAAoB,SAAS,KAAK,WAAW;AAAA,EAC9E;AAAA;AAAA,EAGQ,WAAW,KAAqB;AACtC,QAAI,CAAC,IAAK,QAAO;AACjB,UAAM,MAAM,KAAK,QAAQ,QAAQ;AAGjC,QAAI,CAAC,IAAK,QAAO,IAAI,MAAM,GAAG,EAAE,CAAC;AACjC,UAAM,CAAC,UAAU,QAAQ,IAAI,IAAI,MAAM,GAAG;AAC1C,UAAM,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,IAAI,SAAS,MAAM,GAAG;AACnD,UAAM,CAAC,IAAI,MAAM,IAAI,MAAM,IAAI,IAAI,KAAK,YAAY,IAAI,MAAM,GAAG;AAEjE,WAAO,IACJ,QAAQ,UAAU,GAAQ,EAC1B,QAAQ,OAAO,GAAQ,EACvB,QAAQ,OAAO,GAAQ,EACvB,QAAQ,QAAQ,GAAQ,EACxB,QAAQ,OAAO,GAAQ,EACvB,QAAQ,QAAQ,GAAQ,EACxB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC,EACpB,QAAQ,WAAW,CAAC;AAAA,EACzB;AAAA;AAAA,EAGQ,cAAsB;AAC5B,QAAI,KAAK,SAAS,SAAS;AACzB,YAAM,IAAI,KAAK,QAAQ,OAAO;AAC9B,YAAM,IAAI,KAAK,QAAQ,KAAK;AACd,WAAK,QAAQ,WAAW,IAAI,MAAM;AAChD,UAAI,CAAC,KAAK,CAAC,EAAG,QAAO;AACrB,UAAI,KAAK,QAAQ,WAAW,GAAG;AAC7B,cAAM,IAAI,CAAC,QAA0B,MAAM,GAAG,KAAK,WAAW,GAAG,CAAC,IAAI,KAAK,UAAU,KAAK,MAAM,GAAG,KAAK,WAAW,GAAG,CAAC,OAAO;AAC9H,eAAO,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AAAA,MAC3C;AAEA,aAAO,KAAK,IAAI,GAAG,KAAK,WAAW,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,KAAK,IAAI,GAAG,KAAK,WAAW,CAAC,CAAC,SAAS;AAAA,IACtG;AACA,QAAI,KAAK,SAAS,YAAY;AAC5B,aAAO,KAAK,OAAO,IAAI,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,EAAE,KAAK,IAAI;AAAA,IAC7D;AACA,UAAM,OAAO,KAAK,WAAW,KAAK,KAAK;AACvC,WAAO,QAAQ,KAAK,QAAQ,WAAW,IAAI,GAAG,IAAI,IAAI,KAAK,SAAA,CAAU,KAAK;AAAA,EAC5E;AAAA;AAAA,EAGQ,WAAmB;AACzB,UAAM,IAAI,CAAC,MAAsB,OAAO,CAAC,EAAE,SAAS,GAAG,GAAG;AAC1D,WAAO,GAAG,EAAE,KAAK,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC;AAAA,EAC9D;AAAA;AAAA,EAGQ,oBAA0B;AAChC,UAAM,MAAM,KAAK,SAAS,KAAK,QAAQ,OAAO,KAAK;AACnD,UAAM,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC;AAC1B,QAAI,CAAC,EAAG;AACR,UAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AACzD,SAAK,OAAO,EAAE,GAAG,KAAK,GAAG,GAAG,KAAK,GAAG,GAAG,KAAK,EAAA;AAAA,EAC9C;AAAA;AAAA,EAGQ,SAAS,KAAqB;AACpC,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,QAAQ,WAAW,IAC3B,GAAG,GAAG,IAAI,KAAK,SAAA,CAAU,KACzB;AAAA,EACN;AAAA,EAEmB,SAAe;AAChC,UAAM,WAAW,KAAK;AACtB,UAAM,YAAY,KAAK,QAAQ,WAAW;AAC1C,UAAM,OAAO,KAAK,YAAA;AAClB,UAAM,WAAW,KAAK,SAAS;AAC/B,UAAM,WAAW,KAAK,QAAQ,WAAW;AACzC,SAAK,kBAAA;AAGL,QAAI,WAAW,QAAQ,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC,UAAU,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC;AACjF,QAAI,KAAK,SAAS,SAAS;AACzB,kBAAY,iBAAiB,IAAI,KAAK,QAAQ,OAAO,CAAC,CAAC,UAAU,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC;AAAA,IAC3F,WAAW,KAAK,SAAS,YAAY;AAEnC,YAAM,QAAQ,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,GAAG;AAC9D,kBAAY,4BAA4B,IAAI,KAAK,CAAC;AAAA,IACpD,OAAO;AACL,YAAM,IAAI,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK;AACtC,kBAAY,WAAW,IAAI,CAAC,CAAC;AAAA,IAC/B;AAEA,SAAK,YAAY;AAAA;AAAA,4DAEuC,KAAK,QAAQ,WAAW,KAAK,CAAC,WAAW,KAAK,UAAU;AAAA,yBAC3F,IAAI,KAAK,QAAQ,eAAe,YAAY,CAAC,CAAC;AAAA,mBACpD,IAAI,IAAI,CAAC,KAAK,WAAW,aAAa,EAAE;AAAA;AAAA,YAE/C,aAAa,WAAW,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAY1B,QAAQ;AAAA,UAErB,WACI;AAAA,2EAC6D,KAAK,KAAK,CAAC;AAAA,2EACX,KAAK,KAAK,CAAC;AAAA,2EACX,KAAK,KAAK,CAAC;AAAA,wBAExE,EACN;AAAA;AAAA,KAEH;AAED,UAAM,QAAQ,KAAK,KAAK,cAAc,QAAQ;AAC9C,QAAI,CAAC,MAAO;AACZ,QAAI,SAAU;AAEd,UAAM,iBAAiB,SAAS,CAAC,MAAM;AACrC,UAAK,EAAE,OAAuB,QAAQ,QAAQ,EAAG;AAEjD,UAAI,KAAK,QAAQ,WAAW,KAAM,EAAE,OAAuB,UAAU,SAAS,SAAS,EAAG;AAC1F,WAAK,gBAAgB,MAAM;AAE3B,UAAI,KAAK,QAAQ,MAAM,GAAG;AACxB,cAAM,IAAI,KAAK,sBAAA;AACf,aAAK,gBAAgB,MAAM,EAAE,SAAS,MAAM,OAAO,eAAe,EAAE,MAAM,GAAG;AAAA,MAC/E,OAAO;AACL,aAAK,gBAAgB,IAAI;AAAA,MAC3B;AAAA,IACF,CAAC;AAGD,SAAK,KAAK,cAAc,QAAQ,GAAG,iBAAiB,SAAS,CAAC,MAAM;AAClE,QAAE,gBAAA;AACF,WAAK,gBAAgB,OAAO;AAC5B,WAAK,gBAAgB,OAAO;AAC5B,WAAK,gBAAgB,KAAK;AAC1B,WAAK,gBAAgB,QAAQ;AAC7B,WAAK,gBAAgB,MAAM;AAC3B,WAAK,OAAA;AACL,WAAK,WAAW,EAAE;AAAA,IACpB,CAAC;AAGD,UAAM,MAAM,KAAK,KAAK,cAA2B,kBAAkB;AACnE,SAAK,iBAAiB,UAAU,CAAC,MAAM;AACrC,YAAM,IAAK,EAAkB,UAAU,CAAA;AACvC,UAAI,EAAE,UAAU,UAAa,EAAE,QAAQ,QAAW;AAChD,YAAI,EAAE,MAAO,MAAK,aAAa,SAAS,KAAK,SAAS,EAAE,KAAK,CAAC;AAAA,YACzD,MAAK,gBAAgB,OAAO;AACjC,YAAI,EAAE,IAAK,MAAK,aAAa,OAAO,KAAK,SAAS,EAAE,GAAG,CAAC;AAAA,YACnD,MAAK,gBAAgB,KAAK;AAAA,MACjC;AACA,UAAI,EAAE,WAAW,QAAW;AAC1B,aAAK,SAAS,EAAE,OAAO,IAAI,CAAC,MAAc,KAAK,SAAS,CAAC,CAAC;AAAA,MAC5D;AACA,UAAI,EAAE,UAAU,QAAW;AACzB,YAAI,EAAE,MAAO,MAAK,aAAa,SAAS,KAAK,SAAS,EAAE,KAAK,CAAC;AAAA,YACzD,MAAK,gBAAgB,OAAO;AAEjC,YAAI,KAAK,SAAS,SAAU,MAAK,gBAAgB,MAAM;AAAA,MACzD;AACA,WAAK,OAAA;AACL,WAAK,WAAW,CAAC;AAAA,IACnB,CAAC;AAGD,UAAM,UAAU,KAAK,KAAK,cAAgC,UAAU;AACpE,QAAI,WAAW,KAAK,QAAQ,WAAW,KAAK,CAAC,UAAU;AACrD,cAAQ,iBAAiB,WAAW,CAAC,MAAM;AACzC,YAAI,EAAE,QAAQ,SAAS;AACrB,YAAE,eAAA;AACF,eAAK,YAAY,OAAO;AAAA,QAC1B;AAAA,MACF,CAAC;AACD,cAAQ,iBAAiB,QAAQ,MAAM,KAAK,YAAY,OAAO,CAAC;AAAA,IAClE;AAGA,QAAI,UAAU;AACZ,YAAM,YAAY,MAAY;AAC5B,cAAM,IAAI,KAAK,KAAK,cAAgC,MAAM;AAC1D,cAAM,IAAI,KAAK,KAAK,cAAgC,MAAM;AAC1D,cAAM,IAAI,KAAK,KAAK,cAAgC,MAAM;AAC1D,cAAM,QAAQ,CAAC,GAAuB,QACpC,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,SAAS,KAAK,KAAK,EAAE,KAAK,CAAC,CAAC;AACxD,aAAK,OAAO;AAAA,UACV,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,UACrB,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,UACrB,GAAG,MAAM,GAAG,OAAO,EAAE;AAAA,QAAA;AAGvB,cAAM,WAAW,CAAC,QAAwB,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,GAAG,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC,IAAI,KAAK,UAAU,KAAK;AAC1G,YAAI,KAAK,MAAO,MAAK,aAAa,SAAS,SAAS,KAAK,KAAK,CAAC;AAC/D,YAAI,KAAK,QAAQ,OAAO,EAAG,MAAK,aAAa,SAAS,SAAS,KAAK,QAAQ,OAAO,CAAC,CAAC;AACrF,YAAI,KAAK,QAAQ,KAAK,EAAG,MAAK,aAAa,OAAO,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC;AAC/E,YAAI,KAAK,OAAO,QAAQ;AACtB,eAAK,SAAS,KAAK,OAAO,IAAI,CAAC,MAAM,SAAS,CAAC,CAAC;AAAA,QAClD;AAEA,cAAMA,WAAU,KAAK,KAAK,cAAgC,UAAU;AACpE,YAAIA,SAASA,UAAQ,QAAQ,KAAK,YAAA;AAClC,aAAK,WAAW,EAAE,OAAO,KAAK,OAAO;AAAA,MACvC;AACA,OAAC,QAAQ,QAAQ,MAAM,EAAE,QAAQ,CAAC,QAAQ;AACxC,aAAK,KAAK,cAAc,GAAG,GAAG,iBAAiB,SAAS,SAAS;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEQ,WAAW,QAAuC;AACxD,SAAK;AAAA,MACH,IAAI,YAAY,UAAU,EAAE,SAAS,MAAM,UAAU,MAAM,OAAA,CAAQ;AAAA,IAAA;AAAA,EAEvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,WAAW,KAA4B;AAC7C,UAAM,OAAO,IAAI,KAAA;AACjB,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,MAAM,KAAK,QAAQ,UAAU,YAAY,EAAE,YAAA;AAGjD,UAAM,QAAQ,CAAC,MAAM,MAAM,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;AAChE,QAAI,MAAM,SAAS,EAAG,QAAO;AAE7B,UAAM,QAAQ,KAAK,MAAM,IAAI,OAAO,SAAS,CAAC,EAAE,OAAO,OAAO;AAC9D,QAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,UAAM,MAA8B,CAAA;AACpC,UAAM,QAAQ,CAAC,GAAG,MAAO,IAAI,CAAC,IAAI,MAAM,CAAC,CAAG;AAC5C,UAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,GAAG,GAAG,CAAC,KAAK,IAAI,IAAI,KAAK,IAAI,SAAS,GAAG,GAAG,CAAC;AAGtG,UAAM,IAAI,oBAAI,KAAK,GAAG,GAAG,WAAW;AACpC,QAAI,OAAO,MAAM,EAAE,QAAA,CAAS,EAAG,QAAO;AACtC,UAAM,CAAC,GAAG,GAAG,GAAG,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC;AAC7D,QAAI,EAAE,YAAA,MAAkB,KAAK,EAAE,SAAA,IAAa,MAAM,KAAK,EAAE,QAAA,MAAc,IAAK,QAAO;AAGnF,UAAM,MAAM,CAAC,GAAW,MAAsB,EAAE,MAAM,GAAG,EAAE,CAAC,EAAG,cAAc,EAAE,MAAM,GAAG,EAAE,CAAC,CAAE;AAC7F,UAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,UAAM,MAAM,KAAK,QAAQ,KAAK;AAC9B,QAAI,OAAO,IAAI,KAAK,GAAG,IAAI,EAAG,QAAO;AACrC,QAAI,OAAO,IAAI,KAAK,GAAG,IAAI,EAAG,QAAO;AAErC,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,YAAY,OAA+B;AACjD,UAAM,SAAS,KAAK,WAAW,MAAM,KAAK;AAC1C,QAAI,CAAC,QAAQ;AAEX,YAAM,UAAU,IAAI,SAAS;AAC7B,WAAK;AAAA,QACH,IAAI,YAAY,WAAW,EAAE,SAAS,MAAM,UAAU,MAAM,QAAQ,EAAE,MAAM,MAAM,MAAA,GAAS;AAAA,MAAA;AAE7F,YAAM,QAAQ,KAAK,YAAA;AACnB,iBAAW,MAAM,MAAM,UAAU,OAAO,SAAS,GAAG,IAAI;AACxD;AAAA,IACF;AACA,QAAI,KAAK,SAAS,UAAU;AAC1B,WAAK,aAAa,SAAS,KAAK,SAAS,MAAM,CAAC;AAChD,WAAK,WAAW,EAAE,OAAO,KAAK,OAAO;AAAA,IACvC,WAAW,KAAK,SAAS,SAAS;AAEhC,UAAI,CAAC,KAAK,QAAQ,OAAO,KAAM,KAAK,QAAQ,OAAO,KAAK,KAAK,QAAQ,KAAK,GAAI;AAC5E,aAAK,aAAa,SAAS,KAAK,SAAS,MAAM,CAAC;AAChD,aAAK,gBAAgB,KAAK;AAAA,MAC5B,OAAO;AACL,cAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,OAAO,EAAG,MAAM,GAAG,EAAE,CAAC,GAAI,MAAM,EAAE,KAAA;AAC/D,aAAK,aAAa,SAAS,KAAK,SAAS,CAAC,CAAC;AAC3C,aAAK,aAAa,OAAO,KAAK,SAAS,CAAC,CAAC;AAAA,MAC3C;AACA,WAAK,WAAW,EAAE,OAAO,KAAK,QAAQ,OAAO,GAAG,KAAK,KAAK,QAAQ,KAAK,EAAA,CAAG;AAAA,IAC5E,OAAO;AACL,YAAM,OAAO,KAAK,OAAO,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,EAAE,CAAC,MAAM,MAAM;AACjE,WAAK,KAAK,KAAK,SAAS,MAAM,CAAC;AAC/B,WAAK,SAAS;AACd,WAAK,WAAW,EAAE,QAAQ,KAAK,QAAQ;AAAA,IACzC;AACA,SAAK,OAAA;AAAA,EACP;AACF;AA1fE,cAAgB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AADpB,IAAM,eAAN;AA6fA,SAAS,qBAA0C;AACxD,SAAO,cAAc,iBAAiB,YAAY;AACpD;"}
1
+ {"version":3,"file":"datepicker.js","sources":["../../../src/components/datepicker/datepicker.ts"],"sourcesContent":["import { FxFormElement } from '../../core/form-element';\nimport { css } from '../../core/css';\nimport { defineElement } from '../../core/define';\nimport { esc } from '../../core/sanitize';\n\n/**\n * <fx-datepicker> — Campo de data com calendário suspenso.\n *\n * Form-Associated Custom Element: dentro de um <form> entra no FormData\n * (via atributo `name`): single → `name`; range → `name-start`/`name-end`;\n * multiple → uma entrada `name` por data.\n *\n * Um input com ícone de calendário no final; ao clicar, abre o\n * <fx-calendar> em um popover. As datas selecionadas aparecem no input.\n *\n * Atributos:\n * - mode=\"single\" | \"range\" | \"multiple\" (padrão: single)\n * - value=\"YYYY-MM-DD\" → single\n * - start/end → range\n * - values=\"a,b,c\" → múltiplas datas (CSV)\n * - min/max, placeholder, disabled, clearable, size (sm|md|lg)\n * - format=\"dd/mm/yyyy\" → máscara de exibição (tokens: dd mm yyyy, HH MM SS)\n * - show-time → inclui seleção de hora:minuto:segundo no popover;\n * o valor passa a ser \"YYYY-MM-DDTHH:mm:ss\"\n *\n * Evento: `change` (composed):\n * - single: { value }\n * - range: { value, start, end }\n * - multiple: { values: string[] }\n *\n * Além do `change`, emite `input`/`valueChange` com o MESMO payload\n * (v-model do Vue e [(value)] do Angular).\n */\nexport class FxDatepicker extends FxFormElement {\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 .field {\n display: inline-flex;\n align-items: center;\n gap: var(--fx-space-xs);\n width: 100%;\n box-sizing: border-box;\n /* Base = tamanho padrão (md): 40px */\n min-height: var(--fx-size-md, 40px);\n min-width: 150px;\n padding: var(--fx-space-xs) var(--fx-space-md);\n /* Altura determinística: a caixa fica EXATAMENTE no token --fx-size-*. */\n line-height: var(--fx-font-line-height);\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 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 :host([size='sm']) .field { min-height: var(--fx-size-sm); min-width: 180px; }\n :host([size='md']) .field { min-height: var(--fx-size-md); }\n :host([size='lg']) .field { min-height: var(--fx-size-lg); min-width: 220px; font-size: calc(var(--fx-font-size) + 4px); }\n /* Range e multiple exibem mais de uma data: campo mais largo */\n :host([mode='range']) .field { min-width: 250px; }\n :host([mode='multiple']) .field { min-width: 220px; }\n :host([mode='range'][size='lg']) .field { min-width: 360px; }\n :host([mode='multiple'][size='lg']) .field { min-width: 320px; }\n :host([show-time]) .field { min-width: 250px; }\n /* Full width: o host estica até o pai (os min-width dos modos são\n descartados para permitir layouts estreitos sob controle do pai). */\n :host([full]) { display: block; width: 100%; }\n :host([full]) .field { min-width: 0; }\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]) .field,\n :host([invalid]) .field {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([error]) .field:focus-within,\n :host([invalid]) .field:focus-within,\n :host([error][open]) .field,\n :host([invalid][open]) .field {\n border-color: var(--fx-color-danger, #dc2626);\n box-shadow: var(--fx-effect-error-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-danger, #dc2626) 18%, transparent));\n }\n :host([success]) .field,\n :host([valid]) .field {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n :host([success]) .field:focus-within,\n :host([valid]) .field:focus-within,\n :host([success][open]) .field,\n :host([valid][open]) .field {\n border-color: var(--fx-color-success, #16a34a);\n box-shadow: var(--fx-effect-success-ring, 0 0 0 3px color-mix(in srgb, var(--fx-color-success, #16a34a) 18%, transparent));\n }\n .field:hover { border-color: var(--fx-border-hover); }\n .field:focus-within {\n outline: none;\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n .display {\n flex: 1;\n border: none;\n background: none;\n font: inherit;\n color: var(--fx-text-default);\n outline: none;\n cursor: pointer;\n text-align: left;\n padding: 0;\n min-width: 0;\n }\n .display::placeholder { color: var(--fx-text-muted); }\n .cal-icon { color: var(--fx-text-muted); display: inline-flex; pointer-events: none; }\n .clear {\n border: none;\n background: none;\n color: var(--fx-text-muted);\n cursor: pointer;\n font-size: calc(var(--fx-font-size) + 4px);\n line-height: 1;\n padding: 0 2px;\n border-radius: var(--fx-radius-full);\n }\n .clear:hover { color: var(--fx-color-danger); }\n .clear[hidden] { display: none; }\n .pop {\n position: absolute;\n top: calc(100% + 4px);\n left: 0;\n z-index: var(--fx-z-dropdown, 1000);\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 overflow: hidden;\n }\n :host([up]) .pop {\n top: auto;\n bottom: calc(100% + 4px);\n }\n :host([open]) .pop { display: block; }\n .time-row {\n display: flex;\n align-items: center;\n gap: var(--fx-space-xs);\n padding: var(--fx-space-sm) var(--fx-space-md);\n border-top: 1px solid var(--fx-border-default);\n background: var(--fx-surface-background);\n border-radius: 0 0 var(--fx-radius-md) var(--fx-radius-md);\n font-size: calc(var(--fx-font-size) - 2px);\n color: var(--fx-text-default);\n }\n .time-row label { color: var(--fx-text-muted); }\n .time-row input {\n width: 38px;\n font: inherit;\n color: var(--fx-text-default);\n background: var(--fx-surface-background);\n border: 1px solid var(--fx-border-default);\n border-radius: var(--fx-radius-sm);\n padding: 2px 2px;\n outline: none;\n text-align: center;\n -moz-appearance: textfield;\n appearance: textfield;\n }\n .time-row input::-webkit-outer-spin-button,\n .time-row input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }\n .time-row .sep { color: var(--fx-text-muted); font-weight: bold; padding: 0 1px; }\n .time-row input:focus {\n border-color: var(--fx-color-primary);\n box-shadow: var(--fx-effect-focus-ring, none);\n }\n :host([disabled]) .field { opacity: 0.55; cursor: not-allowed; background: var(--fx-surface-surface-hover); }\n :host([disabled]) .display { cursor: not-allowed; }\n :host([free-text]) .display { cursor: text; }\n :host([free-text]) .display.invalid { border-bottom: 2px solid var(--fx-color-danger); }\n `;\n\n static override get observedAttributes(): string[] {\n return ['value', 'start', 'end', 'values', 'mode', 'min', 'max', 'placeholder', 'disabled', 'format', 'show-time', 'free-text', 'error', 'invalid', 'success', 'valid'];\n }\n\n protected override attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n super.attributeChangedCallback(name, oldValue, newValue);\n // Mantém o FormData do form sincronizado sem tocar no render.\n if (name === 'value' || name === 'start' || name === 'end' || name === 'values') {\n this.updateFormValue();\n }\n }\n\n /** No FormData do form, conforme o modo (single/range/multiple). */\n protected override getFormValue(): string | FormData | null {\n if (this.mode === 'range') {\n const start = this.getAttr('start');\n const end = this.getAttr('end');\n if (!this.name || (!start && !end)) return null;\n const fd = new FormData();\n if (start) fd.append(`${this.name}-start`, start);\n if (end) fd.append(`${this.name}-end`, end);\n return fd;\n }\n if (this.mode === 'multiple') {\n const values = this.values;\n if (!this.name || values.length === 0) return null;\n const fd = new FormData();\n values.forEach((v) => fd.append(this.name, v));\n return fd;\n }\n return this.getAttr('value') || null;\n }\n\n private docListener?: (e: Event) => void;\n /** Hora/minuto/segundo do modo show-time. */\n private time = { h: 0, m: 0, s: 0 };\n /** Estado inicial (value/start/end/values) para form.reset(). */\n private _initialState?: { value: string; start: string; end: string; values: string };\n\n get mode(): string {\n const m = this.getAttr('mode', 'single');\n return ['single', 'range', 'multiple'].includes(m) ? m : 'single';\n }\n set mode(value: string) {\n this.setAttribute('mode', value);\n }\n\n get value(): string {\n return this.getAttr('value');\n }\n set value(v: string) {\n this.setAttribute('value', v);\n }\n\n /** Datas do modo múltiplo (CSV no atributo). */\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 protected override connectedCallback(): void {\n if (!this._initialState) {\n this._initialState = {\n value: this.getAttr('value'),\n start: this.getAttr('start'),\n end: this.getAttr('end'),\n values: this.getAttr('values'),\n };\n }\n super.connectedCallback();\n // Fecha o popover 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 };\n document.addEventListener('click', this.docListener);\n }\n\n protected override disconnectedCallback(): void {\n super.disconnectedCallback();\n if (this.docListener) document.removeEventListener('click', this.docListener);\n }\n\n /** form.reset(): restaura value/start/end/values iniciais (re-render via atributos observados). */\n override formResetCallback(): void {\n const s = this._initialState ?? { value: '', start: '', end: '', values: '' };\n this.setAttribute('value', s.value);\n this.setAttribute('start', s.start);\n this.setAttribute('end', s.end);\n this.setAttribute('values', s.values);\n this.updateFormValue();\n }\n\n /** Formata uma data ISO (\"YYYY-MM-DD\" ou com \"THH:mm:ss\") segundo o atributo format. */\n private formatDate(iso: string): string {\n if (!iso) return '';\n const fmt = this.getAttr('format');\n // Sem máscara: exibe apenas a parte da data (a hora entra via timeText\n // quando show-time está ativo — nunca duplicada).\n if (!fmt) return iso.split('T')[0]!;\n const [datePart, timePart] = iso.split('T');\n const [y = '', m = '', d = ''] = datePart.split('-');\n const [H = '00', M = '00', S = '00'] = (timePart ?? '').split(':');\n // Placeholders para evitar colisão entre mm (mês) e MM (minuto).\n return fmt\n .replace(/yyyy/gi, '\\u0001')\n .replace(/MM/g, '\\u0005')\n .replace(/mm/g, '\\u0002')\n .replace(/dd/gi, '\\u0003')\n .replace(/HH/g, '\\u0004')\n .replace(/SS/gi, '\\u0006')\n .replace(/\\u0001/g, y)\n .replace(/\\u0002/g, m)\n .replace(/\\u0003/g, d)\n .replace(/\\u0004/g, H)\n .replace(/\\u0005/g, M)\n .replace(/\\u0006/g, S);\n }\n\n /** Texto exibido no input conforme o modo. */\n private displayText(): string {\n if (this.mode === 'range') {\n const s = this.getAttr('start');\n const e = this.getAttr('end');\n const arrow = this.hasAttr('show-time') ? ' ' : ' → ';\n if (!s && !e) return '';\n if (this.hasAttr('show-time')) {\n const f = (iso?: string): string => (iso ? `${this.formatDate(iso)} ${this.timeText()}` : iso ? `${this.formatDate(iso)} …` : '');\n return s && e ? `${f(s)} → ${f(e)}` : f(s);\n }\n void arrow;\n return s && e ? `${this.formatDate(s)} → ${this.formatDate(e)}` : s ? `${this.formatDate(s)} → …` : '';\n }\n if (this.mode === 'multiple') {\n return this.values.map((v) => this.formatDate(v)).join(', ');\n }\n const base = this.formatDate(this.value);\n return base && this.hasAttr('show-time') ? `${base} ${this.timeText()}` : base;\n }\n\n /** \"HH:MM:SS\" do estado interno de show-time. */\n private timeText(): string {\n const p = (n: number): string => String(n).padStart(2, '0');\n return `${p(this.time.h)}:${p(this.time.m)}:${p(this.time.s)}`;\n }\n\n /** Extrai hora de um valor ISO inicial, se houver. */\n private loadTimeFromValue(): void {\n const src = this.value || this.getAttr('start') || '';\n const t = src.split('T')[1];\n if (!t) return;\n const [h, m, s] = t.split(':').map((n) => parseInt(n, 10));\n this.time = { h: h || 0, m: m || 0, s: s || 0 };\n }\n\n /** Anexa o horário interno ao ISO puro vindo do calendário. */\n private withTime(iso: string): string {\n if (!iso) return iso;\n return this.hasAttr('show-time')\n ? `${iso}T${this.timeText()}`\n : iso;\n }\n\n protected override render(): void {\n const disabled = this.disabled;\n const clearable = this.hasAttr('clearable');\n const text = this.displayText();\n const hasValue = text.length > 0;\n const showTime = this.hasAttr('show-time');\n this.loadTimeFromValue();\n\n // Atributos repassados ao calendário interno, conforme o modo.\n let calAttrs = `min=\"${esc(this.getAttr('min'))}\" max=\"${esc(this.getAttr('max'))}\"`;\n if (this.mode === 'range') {\n calAttrs += ` range start=\"${esc(this.getAttr('start'))}\" end=\"${esc(this.getAttr('end'))}\"`;\n } else if (this.mode === 'multiple') {\n // Repassa apenas as partes de data (sem hora) para o calendário pintar.\n const dates = this.values.map((v) => v.split('T')[0]).join(',');\n calAttrs += ` mode=\"multiple\" values=\"${esc(dates)}\"`;\n } else {\n const v = this.value.split('T')[0] ?? '';\n calAttrs += ` value=\"${esc(v)}\"`;\n }\n\n this.setTemplate(`\n <div class=\"field\" part=\"field\">\n <input class=\"display\" part=\"display\" type=\"text\" ${this.hasAttr('free-text') && !disabled ? '' : 'readonly'}\n placeholder=\"${esc(this.getAttr('placeholder', 'dd/mm/aaaa'))}\"\n value=\"${esc(text)}\" ${disabled ? 'disabled' : ''}>\n <button type=\"button\" class=\"clear\" part=\"clear\" aria-label=\"Limpar\"\n ${clearable && hasValue ? '' : 'hidden'}>\n ×\n </button>\n <span class=\"cal-icon\" part=\"icon\" aria-hidden=\"true\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\"\n stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\"/>\n <path d=\"M16 2v4M8 2v4M3 10h18\"/>\n </svg>\n </span>\n </div>\n <div class=\"pop\" part=\"popover\">\n <fx-calendar ${calAttrs}></fx-calendar>\n ${\n showTime\n ? `<div class=\"time-row\" part=\"time\">\n <input class=\"t-h\" type=\"number\" min=\"0\" max=\"23\" value=\"${this.time.h}\" aria-label=\"Hora\"><span class=\"sep\">:</span>\n <input class=\"t-m\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.m}\" aria-label=\"Minuto\"><span class=\"sep\">:</span>\n <input class=\"t-s\" type=\"number\" min=\"0\" max=\"59\" value=\"${this.time.s}\" aria-label=\"Segundo\">\n </div>`\n : ''\n }\n </div>\n `);\n\n const field = this.root.querySelector('.field');\n if (!field) return;\n if (disabled) return;\n\n field.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).closest('.clear')) return;\n // Com free-text, clicar no próprio input serve para digitar — não abre.\n if (this.hasAttr('free-text') && (e.target as HTMLElement).classList.contains('display')) 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 + 340 > window.innerHeight && r.top > 340);\n } else {\n this.removeAttribute('up');\n }\n });\n\n // Limpar: zera tudo conforme o modo.\n this.root.querySelector('.clear')?.addEventListener('click', (e) => {\n e.stopPropagation();\n this.removeAttribute('value');\n this.removeAttribute('start');\n this.removeAttribute('end');\n this.removeAttribute('values');\n this.removeAttribute('open');\n this.render();\n this.emitChange({});\n });\n\n // Sincroniza seleção do calendário de volta para os atributos.\n const cal = this.root.querySelector<HTMLElement>('.pop fx-calendar');\n cal?.addEventListener('change', (e) => {\n const d = (e as CustomEvent).detail ?? {};\n if (d.start !== undefined || d.end !== undefined) {\n if (d.start) this.setAttribute('start', this.withTime(d.start));\n else this.removeAttribute('start');\n if (d.end) this.setAttribute('end', this.withTime(d.end));\n else this.removeAttribute('end');\n }\n if (d.values !== undefined) {\n this.values = d.values.map((v: string) => this.withTime(v));\n }\n if (d.value !== undefined) {\n if (d.value) this.setAttribute('value', this.withTime(d.value));\n else this.removeAttribute('value');\n // No modo single, fecha ao escolher.\n if (this.mode === 'single') this.removeAttribute('open');\n }\n this.render();\n this.emitChange(d);\n });\n\n // free-text: valida ao sair do campo ou pressionar Enter.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display && this.hasAttr('free-text') && !disabled) {\n display.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n this.commitTyped(display);\n }\n });\n display.addEventListener('blur', () => this.commitTyped(display));\n }\n\n // show-time: captura alterações de hora/minuto/segundo.\n if (showTime) {\n const applyTime = (): void => {\n const h = this.root.querySelector<HTMLInputElement>('.t-h');\n const m = this.root.querySelector<HTMLInputElement>('.t-m');\n const s = this.root.querySelector<HTMLInputElement>('.t-s');\n const clamp = (v: string | undefined, max: number): number =>\n Math.min(max, Math.max(0, parseInt(v ?? '0', 10) || 0));\n this.time = {\n h: clamp(h?.value, 23),\n m: clamp(m?.value, 59),\n s: clamp(s?.value, 59),\n };\n // Reanexa o horário aos valores já selecionados.\n const reattach = (iso: string): string => iso.split('T')[0] ? `${iso.split('T')[0]}T${this.timeText()}` : iso;\n if (this.value) this.setAttribute('value', reattach(this.value));\n if (this.getAttr('start')) this.setAttribute('start', reattach(this.getAttr('start')));\n if (this.getAttr('end')) this.setAttribute('end', reattach(this.getAttr('end')));\n if (this.values.length) {\n this.values = this.values.map((v) => reattach(v));\n }\n // Atualiza apenas o texto do input, sem re-render completo.\n const display = this.root.querySelector<HTMLInputElement>('.display');\n if (display) display.value = this.displayText();\n this.emitChange({ value: this.value });\n };\n ['.t-h', '.t-m', '.t-s'].forEach((sel) => {\n this.root.querySelector(sel)?.addEventListener('input', applyTime);\n });\n }\n }\n\n private emitChange(detail: Record<string, unknown>): void {\n // input (v-model do Vue) + valueChange ([(value)] do Angular) com o\n // mesmo payload do change (single: { value } · range: { start, end }).\n this.emitValueEvents(detail);\n this.dispatchEvent(\n new CustomEvent('change', { bubbles: true, composed: true, detail }),\n );\n }\n\n /**\n * Interpreta o texto digitado livremente e devolve ISO \"YYYY-MM-DD\"\n * (ou null se inválido). Aceita a máscara do atributo `format`\n * (tokens dd/mm/yyyy) ou ISO direto.\n */\n private parseTyped(raw: string): string | null {\n const text = raw.trim();\n if (!text) return null;\n const fmt = this.getAttr('format', 'dd/mm/yyyy').toLowerCase();\n\n // Ordem dos tokens conforme a máscara informada.\n const order = ['dd', 'mm', 'yyyy'].filter((t) => fmt.includes(t));\n if (order.length < 3) return null;\n\n const parts = text.split(new RegExp(`[^0-9]+`)).filter(Boolean);\n if (parts.length !== 3) return null;\n\n const map: Record<string, string> = {};\n order.forEach((t, i) => (map[t] = parts[i]!));\n const iso = `${map['yyyy']}-${(map['mm'] ?? '').padStart(2, '0')}-${(map['dd'] ?? '').padStart(2, '0')}`;\n\n // Validação de data real.\n const d = new Date(`${iso}T12:00:00`);\n if (Number.isNaN(d.getTime())) return null;\n const [y, m, day] = iso.split('-').map((n) => parseInt(n, 10));\n if (d.getFullYear() !== y || d.getMonth() + 1 !== m || d.getDate() !== day) return null;\n\n // Limites min/max (apenas a parte da data).\n const cmp = (a: string, b: string): number => a.split('T')[0]!.localeCompare(b.split('T')[0]!);\n const min = this.getAttr('min');\n const max = this.getAttr('max');\n if (min && cmp(iso, min) < 0) return null;\n if (max && cmp(iso, max) > 0) return null;\n\n return iso;\n }\n\n /** Aplica o texto digitado no modo free-text (blur/Enter). */\n private commitTyped(input: HTMLInputElement): void {\n const parsed = this.parseTyped(input.value);\n if (!parsed) {\n // Inválido: sinaliza, reverte o texto e emite evento.\n input.classList.add('invalid');\n this.dispatchEvent(\n new CustomEvent('invalid', { bubbles: true, composed: true, detail: { text: input.value } }),\n );\n input.value = this.displayText();\n setTimeout(() => input.classList.remove('invalid'), 1500);\n return;\n }\n if (this.mode === 'single') {\n this.setAttribute('value', this.withTime(parsed));\n this.emitChange({ value: this.value });\n } else if (this.mode === 'range') {\n // Primeira digitação vira start; com start preenchido, vira end.\n if (!this.getAttr('start') || (this.getAttr('start') && this.getAttr('end'))) {\n this.setAttribute('start', this.withTime(parsed));\n this.removeAttribute('end');\n } else {\n const [a, b] = [this.getAttr('start')!.split('T')[0]!, parsed].sort();\n this.setAttribute('start', this.withTime(a));\n this.setAttribute('end', this.withTime(b));\n }\n this.emitChange({ start: this.getAttr('start'), end: this.getAttr('end') });\n } else {\n const list = this.values.filter((v) => v.split('T')[0] !== parsed);\n list.push(this.withTime(parsed));\n this.values = list;\n this.emitChange({ values: this.values });\n }\n this.render();\n }\n}\n\nexport function defineFxDatepicker(): typeof FxDatepicker {\n return defineElement('fx-datepicker', FxDatepicker);\n}\n\n"],"names":["display"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Camada de comandos do <fx-editor>.
3
+ *
4
+ * `document.execCommand` é deprecated, mas ainda é o único caminho que
5
+ * preserva o undo nativo sem um modelo de documento. Ele fica isolado aqui;
6
+ * quando não existe (jsdom, navegador futuro), caímos para Selection/Range.
7
+ */
8
+ export declare function nativeExec(command: string, value?: string): boolean;
9
+ export declare function queryState(command: string): boolean | null;
10
+ /** Insere HTML na seleção. Usa `insertHTML` nativo quando disponível. */
11
+ export declare function insertHtml(editor: HTMLElement, html: string): boolean;
12
+ /** Alterna uma marca inline (strong/em/u/s/code/sub/sup/mark). */
13
+ export declare function toggleInline(editor: HTMLElement, tag: string): boolean;
14
+ /** Formata o bloco atual/selecionado para p, h1..h6, blockquote ou pre. */
15
+ export declare function formatBlock(editor: HTMLElement, tag: string): boolean;
16
+ /** Alinhamento de bloco. */
17
+ export declare function align(editor: HTMLElement, value: 'left' | 'center' | 'right' | 'justify'): boolean;
18
+ /** Lista com/sem ordem. */
19
+ export declare function toggleList(editor: HTMLElement, ordered: boolean): boolean;
20
+ /** Limpa formatação inline mantendo o texto. */
21
+ export declare function removeFormat(editor: HTMLElement): boolean;
22
+ export declare function applyLink(editor: HTMLElement, href: string, text?: string, target?: string): boolean;
23
+ export declare function unlink(editor: HTMLElement): boolean;
24
+ export declare function indent(editor: HTMLElement, delta: number): boolean;
25
+ export declare function escapeAttr(value: string): string;
26
+ export declare function escapeText(value: string): string;
27
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../src/components/editor/commands.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAeH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAQnE;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAQ1D;AAED,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAsBrE;AAiCD,kEAAkE;AAClE,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CA4BtE;AAED,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAkBrE;AAED,4BAA4B;AAC5B,wBAAgB,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAalG;AAED,2BAA2B;AAC3B,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAsBzE;AAED,gDAAgD;AAChD,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAczD;AAMD,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,SAAW,GAAG,OAAO,CA2CtG;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAanD;AAED,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAWlE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,235 @@
1
+ import { getSelectedBlocks, getBlock, getRange, placeCaretAtEnd } from "./selection.js";
2
+ function setSelection(range) {
3
+ const selection = document.getSelection();
4
+ if (!selection) return;
5
+ selection.removeAllRanges();
6
+ selection.addRange(range);
7
+ }
8
+ function nativeExec(command, value) {
9
+ const exec = document.execCommand;
10
+ if (typeof exec !== "function") return false;
11
+ try {
12
+ return exec.call(document, command, false, value);
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+ function insertHtml(editor, html) {
18
+ if (nativeExec("insertHTML", html)) return true;
19
+ const range = getRange(editor);
20
+ if (!range) {
21
+ editor.insertAdjacentHTML("beforeend", html);
22
+ placeCaretAtEnd(editor);
23
+ return true;
24
+ }
25
+ range.deleteContents();
26
+ const fragment = range.createContextualFragment(html);
27
+ const last = fragment.lastChild;
28
+ range.insertNode(fragment);
29
+ if (last) {
30
+ const next = document.createRange();
31
+ next.setStartAfter(last);
32
+ next.collapse(true);
33
+ setSelection(next);
34
+ }
35
+ return true;
36
+ }
37
+ function unwrap(el) {
38
+ const parent = el.parentNode;
39
+ if (!parent) return;
40
+ while (el.firstChild) parent.insertBefore(el.firstChild, el);
41
+ el.remove();
42
+ }
43
+ function wrapSelection(editor, tag) {
44
+ const range = getRange(editor);
45
+ if (!range) return false;
46
+ const el = document.createElement(tag);
47
+ if (range.collapsed) {
48
+ el.appendChild(document.createTextNode("​"));
49
+ range.insertNode(el);
50
+ const caret = document.createRange();
51
+ caret.setStart(el.firstChild ?? el, 1);
52
+ caret.collapse(true);
53
+ setSelection(caret);
54
+ return true;
55
+ }
56
+ try {
57
+ range.surroundContents(el);
58
+ } catch {
59
+ el.appendChild(range.extractContents());
60
+ range.insertNode(el);
61
+ }
62
+ return true;
63
+ }
64
+ function toggleInline(editor, tag) {
65
+ const nativeMap = {
66
+ strong: "bold",
67
+ b: "bold",
68
+ em: "italic",
69
+ i: "italic",
70
+ u: "underline",
71
+ s: "strikeThrough",
72
+ strike: "strikeThrough"
73
+ };
74
+ const nativeCommand = nativeMap[tag];
75
+ if (nativeCommand && nativeExec(nativeCommand)) return true;
76
+ const range = getRange(editor);
77
+ if (!range) return false;
78
+ const current = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest(tag);
79
+ if (current && editor.contains(current)) {
80
+ unwrap(current);
81
+ return true;
82
+ }
83
+ return wrapSelection(editor, tag);
84
+ }
85
+ function formatBlock(editor, tag) {
86
+ const normalized = tag.toLowerCase().replace(/[<>]/g, "");
87
+ if (nativeExec("formatBlock", normalized)) return true;
88
+ const blocks = getSelectedBlocks(editor);
89
+ if (!blocks.length) {
90
+ const block = getBlock(editor);
91
+ if (block) blocks.push(block);
92
+ }
93
+ for (const block of blocks) {
94
+ if (!editor.contains(block)) continue;
95
+ const next = document.createElement(normalized);
96
+ for (const attr of Array.from(block.attributes)) next.setAttribute(attr.name, attr.value);
97
+ while (block.firstChild) next.appendChild(block.firstChild);
98
+ block.replaceWith(next);
99
+ }
100
+ return blocks.length > 0;
101
+ }
102
+ function align(editor, value) {
103
+ const nativeMap = {
104
+ left: "justifyLeft",
105
+ center: "justifyCenter",
106
+ right: "justifyRight",
107
+ justify: "justifyFull"
108
+ };
109
+ if (nativeExec(nativeMap[value])) return true;
110
+ const blocks = getSelectedBlocks(editor);
111
+ for (const block of blocks) block.style.textAlign = value;
112
+ return blocks.length > 0;
113
+ }
114
+ function toggleList(editor, ordered) {
115
+ if (nativeExec(ordered ? "insertOrderedList" : "insertUnorderedList")) return true;
116
+ const blocks = getSelectedBlocks(editor);
117
+ if (!blocks.length) return false;
118
+ const list = document.createElement(ordered ? "ol" : "ul");
119
+ const first = blocks[0];
120
+ first.parentNode?.insertBefore(list, first);
121
+ for (const block of blocks) {
122
+ const li = document.createElement("li");
123
+ while (block.firstChild) li.appendChild(block.firstChild);
124
+ list.appendChild(li);
125
+ block.remove();
126
+ }
127
+ const range = document.createRange();
128
+ range.selectNodeContents(list.lastElementChild ?? list);
129
+ range.collapse(false);
130
+ setSelection(range);
131
+ return true;
132
+ }
133
+ function removeFormat(editor) {
134
+ if (nativeExec("removeFormat")) return true;
135
+ const range = getRange(editor);
136
+ if (!range) return false;
137
+ const root = range.commonAncestorContainer;
138
+ const container = root.nodeType === Node.ELEMENT_NODE ? root : root.parentElement;
139
+ if (!container) return false;
140
+ const tags = ["strong", "b", "em", "i", "u", "s", "strike", "span", "mark", "code", "sub", "sup"];
141
+ for (const el of Array.from(container.querySelectorAll(tags.join(",")))) {
142
+ if (range.intersectsNode(el)) unwrap(el);
143
+ }
144
+ return true;
145
+ }
146
+ const linkHtml = (href, label, target) => `<a href="${escapeAttr(href)}" target="${target}" rel="noopener noreferrer">${escapeText(label)}</a>`;
147
+ function applyLink(editor, href, text, target = "_blank") {
148
+ const label = text?.trim() || href;
149
+ const range = getRange(editor);
150
+ if (!range) return insertHtml(editor, linkHtml(href, label, target));
151
+ const anchor = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest("a");
152
+ if (anchor && editor.contains(anchor)) {
153
+ anchor.setAttribute("href", href);
154
+ anchor.setAttribute("target", target);
155
+ anchor.setAttribute("rel", "noopener noreferrer");
156
+ if (text && text !== anchor.textContent) anchor.textContent = text;
157
+ return true;
158
+ }
159
+ if (!range.collapsed) {
160
+ if (nativeExec("createLink", href)) {
161
+ const created = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest("a");
162
+ created?.setAttribute("target", target);
163
+ created?.setAttribute("rel", "noopener noreferrer");
164
+ return true;
165
+ }
166
+ const el = document.createElement("a");
167
+ el.setAttribute("href", href);
168
+ el.setAttribute("target", target);
169
+ el.setAttribute("rel", "noopener noreferrer");
170
+ try {
171
+ range.surroundContents(el);
172
+ } catch {
173
+ el.appendChild(range.extractContents());
174
+ range.insertNode(el);
175
+ }
176
+ return true;
177
+ }
178
+ return insertHtml(editor, linkHtml(href, label, target));
179
+ }
180
+ function unlink(editor) {
181
+ if (nativeExec("unlink")) return true;
182
+ const range = getRange(editor);
183
+ if (!range) return false;
184
+ const anchor = (range.startContainer.nodeType === Node.ELEMENT_NODE ? range.startContainer : range.startContainer.parentElement)?.closest("a");
185
+ if (anchor && editor.contains(anchor)) {
186
+ unwrap(anchor);
187
+ return true;
188
+ }
189
+ return false;
190
+ }
191
+ function indent(editor, delta) {
192
+ if (delta > 0 && nativeExec("indent")) return true;
193
+ if (delta < 0 && nativeExec("outdent")) return true;
194
+ const blocks = getSelectedBlocks(editor);
195
+ for (const block of blocks) {
196
+ const current = Number.parseInt(block.style.marginLeft || "0", 10) || 0;
197
+ const next = Math.max(0, current + delta);
198
+ block.style.marginLeft = next ? `${next}px` : "";
199
+ }
200
+ return blocks.length > 0;
201
+ }
202
+ function escapeAttr(value) {
203
+ return value.replace(/[&<>"']/g, (ch) => {
204
+ switch (ch) {
205
+ case "&":
206
+ return "&amp;";
207
+ case "<":
208
+ return "&lt;";
209
+ case ">":
210
+ return "&gt;";
211
+ case '"':
212
+ return "&quot;";
213
+ default:
214
+ return "&#39;";
215
+ }
216
+ });
217
+ }
218
+ function escapeText(value) {
219
+ return value.replace(/[&<>]/g, (ch) => ch === "&" ? "&amp;" : ch === "<" ? "&lt;" : "&gt;");
220
+ }
221
+ export {
222
+ align,
223
+ applyLink,
224
+ escapeAttr,
225
+ escapeText,
226
+ formatBlock,
227
+ indent,
228
+ insertHtml,
229
+ nativeExec,
230
+ removeFormat,
231
+ toggleInline,
232
+ toggleList,
233
+ unlink
234
+ };
235
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sources":["../../../src/components/editor/commands.ts"],"sourcesContent":["/**\n * Camada de comandos do <fx-editor>.\n *\n * `document.execCommand` é deprecated, mas ainda é o único caminho que\n * preserva o undo nativo sem um modelo de documento. Ele fica isolado aqui;\n * quando não existe (jsdom, navegador futuro), caímos para Selection/Range.\n */\n\nimport { getBlock, getRange, getSelectedBlocks, placeCaretAtEnd } from './selection';\n\ntype ExecDocument = Document & {\n execCommand?: (commandId: string, showUI?: boolean, value?: string) => boolean;\n};\n\nfunction setSelection(range: Range): void {\n const selection = document.getSelection();\n if (!selection) return;\n selection.removeAllRanges();\n selection.addRange(range);\n}\n\nexport function nativeExec(command: string, value?: string): boolean {\n const exec = (document as ExecDocument).execCommand;\n if (typeof exec !== 'function') return false;\n try {\n return exec.call(document, command, false, value);\n } catch {\n return false;\n }\n}\n\nexport function queryState(command: string): boolean | null {\n try {\n return typeof document.queryCommandState === 'function'\n ? document.queryCommandState(command)\n : null;\n } catch {\n return null;\n }\n}\n\n/** Insere HTML na seleção. Usa `insertHTML` nativo quando disponível. */\nexport function insertHtml(editor: HTMLElement, html: string): boolean {\n if (nativeExec('insertHTML', html)) return true;\n\n const range = getRange(editor);\n if (!range) {\n editor.insertAdjacentHTML('beforeend', html);\n placeCaretAtEnd(editor);\n return true;\n }\n\n range.deleteContents();\n const fragment = range.createContextualFragment(html);\n const last = fragment.lastChild;\n range.insertNode(fragment);\n\n if (last) {\n const next = document.createRange();\n next.setStartAfter(last);\n next.collapse(true);\n setSelection(next);\n }\n return true;\n}\n\nfunction unwrap(el: Element): void {\n const parent = el.parentNode;\n if (!parent) return;\n while (el.firstChild) parent.insertBefore(el.firstChild, el);\n el.remove();\n}\n\nfunction wrapSelection(editor: HTMLElement, tag: string): boolean {\n const range = getRange(editor);\n if (!range) return false;\n\n const el = document.createElement(tag);\n if (range.collapsed) {\n el.appendChild(document.createTextNode('\\u200B'));\n range.insertNode(el);\n const caret = document.createRange();\n caret.setStart(el.firstChild ?? el, 1);\n caret.collapse(true);\n setSelection(caret);\n return true;\n }\n\n try {\n range.surroundContents(el);\n } catch {\n el.appendChild(range.extractContents());\n range.insertNode(el);\n }\n return true;\n}\n\n/** Alterna uma marca inline (strong/em/u/s/code/sub/sup/mark). */\nexport function toggleInline(editor: HTMLElement, tag: string): boolean {\n const nativeMap: Record<string, string> = {\n strong: 'bold',\n b: 'bold',\n em: 'italic',\n i: 'italic',\n u: 'underline',\n s: 'strikeThrough',\n strike: 'strikeThrough',\n };\n\n const nativeCommand = nativeMap[tag];\n if (nativeCommand && nativeExec(nativeCommand)) return true;\n\n const range = getRange(editor);\n if (!range) return false;\n\n const current = (range.startContainer.nodeType === Node.ELEMENT_NODE\n ? (range.startContainer as Element)\n : range.startContainer.parentElement\n )?.closest(tag);\n\n if (current && editor.contains(current)) {\n unwrap(current);\n return true;\n }\n\n return wrapSelection(editor, tag);\n}\n\n/** Formata o bloco atual/selecionado para p, h1..h6, blockquote ou pre. */\nexport function formatBlock(editor: HTMLElement, tag: string): boolean {\n const normalized = tag.toLowerCase().replace(/[<>]/g, '');\n if (nativeExec('formatBlock', normalized)) return true;\n\n const blocks = getSelectedBlocks(editor);\n if (!blocks.length) {\n const block = getBlock(editor);\n if (block) blocks.push(block);\n }\n\n for (const block of blocks) {\n if (!editor.contains(block)) continue;\n const next = document.createElement(normalized);\n for (const attr of Array.from(block.attributes)) next.setAttribute(attr.name, attr.value);\n while (block.firstChild) next.appendChild(block.firstChild);\n block.replaceWith(next);\n }\n return blocks.length > 0;\n}\n\n/** Alinhamento de bloco. */\nexport function align(editor: HTMLElement, value: 'left' | 'center' | 'right' | 'justify'): boolean {\n const nativeMap = {\n left: 'justifyLeft',\n center: 'justifyCenter',\n right: 'justifyRight',\n justify: 'justifyFull',\n } as const;\n\n if (nativeExec(nativeMap[value])) return true;\n\n const blocks = getSelectedBlocks(editor);\n for (const block of blocks) block.style.textAlign = value;\n return blocks.length > 0;\n}\n\n/** Lista com/sem ordem. */\nexport function toggleList(editor: HTMLElement, ordered: boolean): boolean {\n if (nativeExec(ordered ? 'insertOrderedList' : 'insertUnorderedList')) return true;\n\n const blocks = getSelectedBlocks(editor);\n if (!blocks.length) return false;\n\n const list = document.createElement(ordered ? 'ol' : 'ul');\n const first = blocks[0];\n first.parentNode?.insertBefore(list, first);\n\n for (const block of blocks) {\n const li = document.createElement('li');\n while (block.firstChild) li.appendChild(block.firstChild);\n list.appendChild(li);\n block.remove();\n }\n\n const range = document.createRange();\n range.selectNodeContents(list.lastElementChild ?? list);\n range.collapse(false);\n setSelection(range);\n return true;\n}\n\n/** Limpa formatação inline mantendo o texto. */\nexport function removeFormat(editor: HTMLElement): boolean {\n if (nativeExec('removeFormat')) return true;\n\n const range = getRange(editor);\n if (!range) return false;\n const root = range.commonAncestorContainer;\n const container = root.nodeType === Node.ELEMENT_NODE ? (root as Element) : root.parentElement;\n if (!container) return false;\n\n const tags = ['strong', 'b', 'em', 'i', 'u', 's', 'strike', 'span', 'mark', 'code', 'sub', 'sup'];\n for (const el of Array.from(container.querySelectorAll(tags.join(',')))) {\n if (range.intersectsNode(el)) unwrap(el);\n }\n return true;\n}\n\n/** Cria/edita link na seleção. */\nconst linkHtml = (href: string, label: string, target: string): string =>\n `<a href=\"${escapeAttr(href)}\" target=\"${target}\" rel=\"noopener noreferrer\">${escapeText(label)}</a>`;\n\nexport function applyLink(editor: HTMLElement, href: string, text?: string, target = '_blank'): boolean {\n const label = text?.trim() || href;\n const range = getRange(editor);\n if (!range) return insertHtml(editor, linkHtml(href, label, target));\n\n const anchor = (range.startContainer.nodeType === Node.ELEMENT_NODE\n ? (range.startContainer as Element)\n : range.startContainer.parentElement\n )?.closest('a');\n\n if (anchor && editor.contains(anchor)) {\n anchor.setAttribute('href', href);\n anchor.setAttribute('target', target);\n anchor.setAttribute('rel', 'noopener noreferrer');\n if (text && text !== anchor.textContent) anchor.textContent = text;\n return true;\n }\n\n if (!range.collapsed) {\n if (nativeExec('createLink', href)) {\n const created = (range.startContainer.nodeType === Node.ELEMENT_NODE\n ? (range.startContainer as Element)\n : range.startContainer.parentElement\n )?.closest('a');\n created?.setAttribute('target', target);\n created?.setAttribute('rel', 'noopener noreferrer');\n return true;\n }\n\n const el = document.createElement('a');\n el.setAttribute('href', href);\n el.setAttribute('target', target);\n el.setAttribute('rel', 'noopener noreferrer');\n try {\n range.surroundContents(el);\n } catch {\n el.appendChild(range.extractContents());\n range.insertNode(el);\n }\n return true;\n }\n\n return insertHtml(editor, linkHtml(href, label, target));\n}\n\nexport function unlink(editor: HTMLElement): boolean {\n if (nativeExec('unlink')) return true;\n const range = getRange(editor);\n if (!range) return false;\n const anchor = (range.startContainer.nodeType === Node.ELEMENT_NODE\n ? (range.startContainer as Element)\n : range.startContainer.parentElement\n )?.closest('a');\n if (anchor && editor.contains(anchor)) {\n unwrap(anchor);\n return true;\n }\n return false;\n}\n\nexport function indent(editor: HTMLElement, delta: number): boolean {\n if (delta > 0 && nativeExec('indent')) return true;\n if (delta < 0 && nativeExec('outdent')) return true;\n\n const blocks = getSelectedBlocks(editor);\n for (const block of blocks) {\n const current = Number.parseInt(block.style.marginLeft || '0', 10) || 0;\n const next = Math.max(0, current + delta);\n block.style.marginLeft = next ? `${next}px` : '';\n }\n return blocks.length > 0;\n}\n\nexport function escapeAttr(value: string): string {\n return value.replace(/[&<>\"']/g, (ch) => {\n switch (ch) {\n case '&': return '&amp;';\n case '<': return '&lt;';\n case '>': return '&gt;';\n case '\"': return '&quot;';\n default: return '&#39;';\n }\n });\n}\n\nexport function escapeText(value: string): string {\n return value.replace(/[&<>]/g, (ch) => (ch === '&' ? '&amp;' : ch === '<' ? '&lt;' : '&gt;'));\n}\n\n"],"names":[],"mappings":";AAcA,SAAS,aAAa,OAAoB;AACxC,QAAM,YAAY,SAAS,aAAA;AAC3B,MAAI,CAAC,UAAW;AAChB,YAAU,gBAAA;AACV,YAAU,SAAS,KAAK;AAC1B;AAEO,SAAS,WAAW,SAAiB,OAAyB;AACnE,QAAM,OAAQ,SAA0B;AACxC,MAAI,OAAO,SAAS,WAAY,QAAO;AACvC,MAAI;AACF,WAAO,KAAK,KAAK,UAAU,SAAS,OAAO,KAAK;AAAA,EAClD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,SAAS,WAAW,QAAqB,MAAuB;AACrE,MAAI,WAAW,cAAc,IAAI,EAAG,QAAO;AAE3C,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,CAAC,OAAO;AACV,WAAO,mBAAmB,aAAa,IAAI;AAC3C,oBAAgB,MAAM;AACtB,WAAO;AAAA,EACT;AAEA,QAAM,eAAA;AACN,QAAM,WAAW,MAAM,yBAAyB,IAAI;AACpD,QAAM,OAAO,SAAS;AACtB,QAAM,WAAW,QAAQ;AAEzB,MAAI,MAAM;AACR,UAAM,OAAO,SAAS,YAAA;AACtB,SAAK,cAAc,IAAI;AACvB,SAAK,SAAS,IAAI;AAClB,iBAAa,IAAI;AAAA,EACnB;AACA,SAAO;AACT;AAEA,SAAS,OAAO,IAAmB;AACjC,QAAM,SAAS,GAAG;AAClB,MAAI,CAAC,OAAQ;AACb,SAAO,GAAG,WAAY,QAAO,aAAa,GAAG,YAAY,EAAE;AAC3D,KAAG,OAAA;AACL;AAEA,SAAS,cAAc,QAAqB,KAAsB;AAChE,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,KAAK,SAAS,cAAc,GAAG;AACrC,MAAI,MAAM,WAAW;AACnB,OAAG,YAAY,SAAS,eAAe,GAAQ,CAAC;AAChD,UAAM,WAAW,EAAE;AACnB,UAAM,QAAQ,SAAS,YAAA;AACvB,UAAM,SAAS,GAAG,cAAc,IAAI,CAAC;AACrC,UAAM,SAAS,IAAI;AACnB,iBAAa,KAAK;AAClB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,iBAAiB,EAAE;AAAA,EAC3B,QAAQ;AACN,OAAG,YAAY,MAAM,iBAAiB;AACtC,UAAM,WAAW,EAAE;AAAA,EACrB;AACA,SAAO;AACT;AAGO,SAAS,aAAa,QAAqB,KAAsB;AACtE,QAAM,YAAoC;AAAA,IACxC,QAAQ;AAAA,IACR,GAAG;AAAA,IACH,IAAI;AAAA,IACJ,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,QAAQ;AAAA,EAAA;AAGV,QAAM,gBAAgB,UAAU,GAAG;AACnC,MAAI,iBAAiB,WAAW,aAAa,EAAG,QAAO;AAEvD,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,WAAW,MAAM,eAAe,aAAa,KAAK,eACnD,MAAM,iBACP,MAAM,eAAe,gBACtB,QAAQ,GAAG;AAEd,MAAI,WAAW,OAAO,SAAS,OAAO,GAAG;AACvC,WAAO,OAAO;AACd,WAAO;AAAA,EACT;AAEA,SAAO,cAAc,QAAQ,GAAG;AAClC;AAGO,SAAS,YAAY,QAAqB,KAAsB;AACrE,QAAM,aAAa,IAAI,YAAA,EAAc,QAAQ,SAAS,EAAE;AACxD,MAAI,WAAW,eAAe,UAAU,EAAG,QAAO;AAElD,QAAM,SAAS,kBAAkB,MAAM;AACvC,MAAI,CAAC,OAAO,QAAQ;AAClB,UAAM,QAAQ,SAAS,MAAM;AAC7B,QAAI,MAAO,QAAO,KAAK,KAAK;AAAA,EAC9B;AAEA,aAAW,SAAS,QAAQ;AAC1B,QAAI,CAAC,OAAO,SAAS,KAAK,EAAG;AAC7B,UAAM,OAAO,SAAS,cAAc,UAAU;AAC9C,eAAW,QAAQ,MAAM,KAAK,MAAM,UAAU,EAAG,MAAK,aAAa,KAAK,MAAM,KAAK,KAAK;AACxF,WAAO,MAAM,WAAY,MAAK,YAAY,MAAM,UAAU;AAC1D,UAAM,YAAY,IAAI;AAAA,EACxB;AACA,SAAO,OAAO,SAAS;AACzB;AAGO,SAAS,MAAM,QAAqB,OAAyD;AAClG,QAAM,YAAY;AAAA,IAChB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,EAAA;AAGX,MAAI,WAAW,UAAU,KAAK,CAAC,EAAG,QAAO;AAEzC,QAAM,SAAS,kBAAkB,MAAM;AACvC,aAAW,SAAS,OAAQ,OAAM,MAAM,YAAY;AACpD,SAAO,OAAO,SAAS;AACzB;AAGO,SAAS,WAAW,QAAqB,SAA2B;AACzE,MAAI,WAAW,UAAU,sBAAsB,qBAAqB,EAAG,QAAO;AAE9E,QAAM,SAAS,kBAAkB,MAAM;AACvC,MAAI,CAAC,OAAO,OAAQ,QAAO;AAE3B,QAAM,OAAO,SAAS,cAAc,UAAU,OAAO,IAAI;AACzD,QAAM,QAAQ,OAAO,CAAC;AACtB,QAAM,YAAY,aAAa,MAAM,KAAK;AAE1C,aAAW,SAAS,QAAQ;AAC1B,UAAM,KAAK,SAAS,cAAc,IAAI;AACtC,WAAO,MAAM,WAAY,IAAG,YAAY,MAAM,UAAU;AACxD,SAAK,YAAY,EAAE;AACnB,UAAM,OAAA;AAAA,EACR;AAEA,QAAM,QAAQ,SAAS,YAAA;AACvB,QAAM,mBAAmB,KAAK,oBAAoB,IAAI;AACtD,QAAM,SAAS,KAAK;AACpB,eAAa,KAAK;AAClB,SAAO;AACT;AAGO,SAAS,aAAa,QAA8B;AACzD,MAAI,WAAW,cAAc,EAAG,QAAO;AAEvC,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO,MAAM;AACnB,QAAM,YAAY,KAAK,aAAa,KAAK,eAAgB,OAAmB,KAAK;AACjF,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,OAAO,CAAC,UAAU,KAAK,MAAM,KAAK,KAAK,KAAK,UAAU,QAAQ,QAAQ,QAAQ,OAAO,KAAK;AAChG,aAAW,MAAM,MAAM,KAAK,UAAU,iBAAiB,KAAK,KAAK,GAAG,CAAC,CAAC,GAAG;AACvE,QAAI,MAAM,eAAe,EAAE,UAAU,EAAE;AAAA,EACzC;AACA,SAAO;AACT;AAGA,MAAM,WAAW,CAAC,MAAc,OAAe,WAC7C,YAAY,WAAW,IAAI,CAAC,aAAa,MAAM,+BAA+B,WAAW,KAAK,CAAC;AAE1F,SAAS,UAAU,QAAqB,MAAc,MAAe,SAAS,UAAmB;AACtG,QAAM,QAAQ,MAAM,KAAA,KAAU;AAC9B,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,CAAC,MAAO,QAAO,WAAW,QAAQ,SAAS,MAAM,OAAO,MAAM,CAAC;AAEnE,QAAM,UAAU,MAAM,eAAe,aAAa,KAAK,eAClD,MAAM,iBACP,MAAM,eAAe,gBACtB,QAAQ,GAAG;AAEd,MAAI,UAAU,OAAO,SAAS,MAAM,GAAG;AACrC,WAAO,aAAa,QAAQ,IAAI;AAChC,WAAO,aAAa,UAAU,MAAM;AACpC,WAAO,aAAa,OAAO,qBAAqB;AAChD,QAAI,QAAQ,SAAS,OAAO,oBAAoB,cAAc;AAC9D,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,MAAM,WAAW;AACpB,QAAI,WAAW,cAAc,IAAI,GAAG;AAClC,YAAM,WAAW,MAAM,eAAe,aAAa,KAAK,eACnD,MAAM,iBACP,MAAM,eAAe,gBACtB,QAAQ,GAAG;AACd,eAAS,aAAa,UAAU,MAAM;AACtC,eAAS,aAAa,OAAO,qBAAqB;AAClD,aAAO;AAAA,IACT;AAEA,UAAM,KAAK,SAAS,cAAc,GAAG;AACrC,OAAG,aAAa,QAAQ,IAAI;AAC5B,OAAG,aAAa,UAAU,MAAM;AAChC,OAAG,aAAa,OAAO,qBAAqB;AAC5C,QAAI;AACF,YAAM,iBAAiB,EAAE;AAAA,IAC3B,QAAQ;AACN,SAAG,YAAY,MAAM,iBAAiB;AACtC,YAAM,WAAW,EAAE;AAAA,IACrB;AACA,WAAO;AAAA,EACT;AAEA,SAAO,WAAW,QAAQ,SAAS,MAAM,OAAO,MAAM,CAAC;AACzD;AAEO,SAAS,OAAO,QAA8B;AACnD,MAAI,WAAW,QAAQ,EAAG,QAAO;AACjC,QAAM,QAAQ,SAAS,MAAM;AAC7B,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,UAAU,MAAM,eAAe,aAAa,KAAK,eAClD,MAAM,iBACP,MAAM,eAAe,gBACtB,QAAQ,GAAG;AACd,MAAI,UAAU,OAAO,SAAS,MAAM,GAAG;AACrC,WAAO,MAAM;AACb,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,OAAO,QAAqB,OAAwB;AAClE,MAAI,QAAQ,KAAK,WAAW,QAAQ,EAAG,QAAO;AAC9C,MAAI,QAAQ,KAAK,WAAW,SAAS,EAAG,QAAO;AAE/C,QAAM,SAAS,kBAAkB,MAAM;AACvC,aAAW,SAAS,QAAQ;AAC1B,UAAM,UAAU,OAAO,SAAS,MAAM,MAAM,cAAc,KAAK,EAAE,KAAK;AACtE,UAAM,OAAO,KAAK,IAAI,GAAG,UAAU,KAAK;AACxC,UAAM,MAAM,aAAa,OAAO,GAAG,IAAI,OAAO;AAAA,EAChD;AACA,SAAO,OAAO,SAAS;AACzB;AAEO,SAAS,WAAW,OAAuB;AAChD,SAAO,MAAM,QAAQ,YAAY,CAAC,OAAO;AACvC,YAAQ,IAAA;AAAA,MACN,KAAK;AAAK,eAAO;AAAA,MACjB,KAAK;AAAK,eAAO;AAAA,MACjB,KAAK;AAAK,eAAO;AAAA,MACjB,KAAK;AAAK,eAAO;AAAA,MACjB;AAAS,eAAO;AAAA,IAAA;AAAA,EAEpB,CAAC;AACH;AAEO,SAAS,WAAW,OAAuB;AAChD,SAAO,MAAM,QAAQ,UAAU,CAAC,OAAQ,OAAO,MAAM,UAAU,OAAO,MAAM,SAAS,MAAO;AAC9F;"}
@@ -0,0 +1,83 @@
1
+ import { FxFormElement } from '../../core/form-element';
2
+ export type FxEditorToolbar = 'minimal' | 'basic' | 'standard' | 'full' | 'none';
3
+ export type FxEditorPasteMode = 'clean' | 'plain';
4
+ export interface FxEditorChangeDetail {
5
+ value: string;
6
+ html: string;
7
+ text: string;
8
+ }
9
+ export type FxEditorUploadResult = string | {
10
+ url: string;
11
+ alt?: string;
12
+ width?: string;
13
+ };
14
+ /**
15
+ * <fx-editor> — editor de texto rico nativo.
16
+ *
17
+ * Form-associated: com `name` entra no FormData com o HTML sanitizado.
18
+ * O atributo `value` é o conteúdo inicial (como o texto de um <textarea>);
19
+ * a propriedade `value` é o conteúdo atual.
20
+ */
21
+ export declare class FxEditor extends FxFormElement {
22
+ #private;
23
+ static styles: string;
24
+ static get observedAttributes(): string[];
25
+ /** Callback opcional de upload de imagem. */
26
+ uploadImage?: (file: File) => Promise<FxEditorUploadResult>;
27
+ /** Hook opcional de sanitização extra (roda depois do sanitizador interno). */
28
+ sanitizeHtml?: (html: string) => string;
29
+ get editor(): HTMLElement | null;
30
+ get source(): HTMLTextAreaElement | null;
31
+ get toolbarEl(): HTMLElement | null;
32
+ get value(): string;
33
+ set value(next: string);
34
+ get html(): string;
35
+ set html(next: string);
36
+ get text(): string;
37
+ get defaultValue(): string;
38
+ set defaultValue(next: string);
39
+ get disabled(): boolean;
40
+ set disabled(value: boolean);
41
+ get readonly(): boolean;
42
+ set readonly(value: boolean);
43
+ get required(): boolean;
44
+ set required(value: boolean);
45
+ get placeholder(): string;
46
+ set placeholder(value: string);
47
+ get toolbar(): FxEditorToolbar;
48
+ set toolbar(value: FxEditorToolbar);
49
+ get pasteMode(): FxEditorPasteMode;
50
+ set pasteMode(value: FxEditorPasteMode);
51
+ get fullscreen(): boolean;
52
+ set fullscreen(value: boolean);
53
+ /** Limite de caracteres de texto (0 desativa). Padrão: 30.000. */
54
+ get maxlength(): number;
55
+ set maxlength(value: number);
56
+ protected connectedCallback(): void;
57
+ protected disconnectedCallback(): void;
58
+ protected attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
59
+ formResetCallback(): void;
60
+ protected getFormValue(): string | FormData | null;
61
+ protected render(): void;
62
+ focus(options?: FocusOptions): void;
63
+ blur(): void;
64
+ execute(command: string, value?: string): void;
65
+ insertHTML(html: string): void;
66
+ /** Mescla as células tocadas pela seleção. */
67
+ mergeCells(): boolean;
68
+ /** Divide a célula mesclada atual. */
69
+ splitCell(): boolean;
70
+ getHtml(): string;
71
+ setHtml(html: string): void;
72
+ getText(): string;
73
+ clear(): void;
74
+ undo(): void;
75
+ redo(): void;
76
+ saveSelection(): void;
77
+ restoreSelection(): void;
78
+ openDialog(kind: 'link' | 'image' | 'video'): void;
79
+ toggleSource(): void;
80
+ toggleFullscreen(): void;
81
+ }
82
+ export declare function defineFxEditor(): typeof FxEditor;
83
+ //# sourceMappingURL=editor.d.ts.map