@govbr-ds/webcomponents 2.0.0-next.61 → 2.0.0-next.62

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 (38) hide show
  1. package/dist/cjs/br-modal.cjs.entry.js +119 -15
  2. package/dist/cjs/br-modal.entry.cjs.js.map +1 -1
  3. package/dist/cjs/br-scrim.cjs.entry.js +132 -38
  4. package/dist/cjs/br-scrim.entry.cjs.js.map +1 -1
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/webcomponents.cjs.js +1 -1
  7. package/dist/collection/components/modal/modal.js +152 -37
  8. package/dist/collection/components/modal/modal.js.map +1 -1
  9. package/dist/collection/components/scrim/scrim.js +132 -38
  10. package/dist/collection/components/scrim/scrim.js.map +1 -1
  11. package/dist/collection/pages/components/modal/index.js +1 -53
  12. package/dist/components/br-modal.js +123 -26
  13. package/dist/components/br-modal.js.map +1 -1
  14. package/dist/components/br-scrim.js +125 -44
  15. package/dist/components/br-scrim.js.map +1 -1
  16. package/dist/esm/br-modal.entry.js +119 -15
  17. package/dist/esm/br-modal.entry.js.map +1 -1
  18. package/dist/esm/br-scrim.entry.js +132 -38
  19. package/dist/esm/br-scrim.entry.js.map +1 -1
  20. package/dist/esm/loader.js +1 -1
  21. package/dist/esm/webcomponents.js +1 -1
  22. package/dist/hydrate/index.js +252 -55
  23. package/dist/hydrate/index.mjs +252 -55
  24. package/dist/types/components/modal/modal.d.ts +46 -4
  25. package/dist/types/components/scrim/scrim.d.ts +39 -1
  26. package/dist/types/components.d.ts +11 -11
  27. package/dist/webcomponents/br-modal.entry.esm.js.map +1 -1
  28. package/dist/webcomponents/br-scrim.entry.esm.js.map +1 -1
  29. package/dist/webcomponents/{p-e7b38c63.entry.js → p-216dc111.entry.js} +126 -45
  30. package/dist/webcomponents/p-216dc111.entry.js.map +1 -0
  31. package/dist/webcomponents/{p-28dba962.entry.js → p-463b06a4.entry.js} +129 -33
  32. package/dist/webcomponents/p-463b06a4.entry.js.map +1 -0
  33. package/dist/webcomponents/webcomponents.esm.js +1 -1
  34. package/package.json +1 -1
  35. package/dist/custom-elements.json +0 -18276
  36. package/dist/webcomponents/p-28dba962.entry.js.map +0 -1
  37. package/dist/webcomponents/p-e7b38c63.entry.js.map +0 -1
  38. package/dist/webcomponents.html-custom-data.json +0 -1214
@@ -25,32 +25,132 @@ export class Modal {
25
25
  this.customId = Helpers.generateUniqueId('modal');
26
26
  /** Controla a visibilidade do modal. */
27
27
  this.show = false;
28
- /** Se `true`, o modal se fechará automaticamente ao clicar no botão de fechar ou pressionar a tecla 'Escape'. */
28
+ /**
29
+ * Define o comportamento de fechamento do modal.
30
+ *
31
+ * - `true`: O modal fecha automaticamente ao clicar no botão fechar.
32
+ * - `false`: O modal emite `brModalBeforeClose` mas não fecha, permitindo a implementação de lógica customizada (validação, confirmação, etc.) antes do fechamento. O desenvolvedor deve
33
+ * controlar manualmente o fechamento.
34
+ */
29
35
  this.autoClose = false;
30
36
  /**
31
37
  * Define o tamanho (largura) do modal.
32
38
  */
33
39
  this.size = 'medium';
34
- /** Se `true`, exibe um fundo escurecido (scrim) atrás do modal. */
35
- this.scrim = false;
36
40
  /** Se `true`, habilita a rolagem interna do conteúdo do modal. */
37
41
  this.scrollable = false;
38
42
  /**
39
43
  * Define o alinhamento do conteúdo do rodapé (slot="footer").
40
44
  */
41
45
  this.alignFooter = 'center';
46
+ this.isOpeningParentScrim = false;
47
+ /**
48
+ * Handler para evento brScrimOpen do parent scrim
49
+ * Quando scrim abre programaticamente, a modal deve abrir também
50
+ */
51
+ this.handleParentScrimOpen = () => {
52
+ const parentScrim = this.el.closest('br-scrim');
53
+ if (parentScrim === null)
54
+ return;
55
+ if (!this.show) {
56
+ this.show = true;
57
+ }
58
+ };
59
+ /**
60
+ * Handler para evento brScrimClose do parent scrim
61
+ * Se autoClose=true, fecha a modal junto com o scrim
62
+ * Se autoClose=false, não faz nada (modal permanece aberta)
63
+ */
64
+ this.handleParentScrimClose = () => {
65
+ const parentScrim = this.el.closest('br-scrim');
66
+ if (parentScrim === null)
67
+ return;
68
+ if (this.autoClose && this.show) {
69
+ this.show = false;
70
+ }
71
+ };
42
72
  this.handleClose = () => {
43
- this.brModalClose.emit();
73
+ this.brModalBeforeClose.emit();
44
74
  if (this.autoClose) {
45
75
  this.show = false;
46
- // Se o modal está dentro de um br-scrim, fecha o scrim também
47
- const parentScrim = this.el.closest('br-scrim');
48
- if (parentScrim) {
49
- parentScrim.close();
50
- }
51
76
  }
52
77
  };
53
78
  }
79
+ handleShowChange(newValue, oldValue) {
80
+ if (newValue) {
81
+ this.brModalOpen.emit();
82
+ this.openParentScrim();
83
+ }
84
+ else if (oldValue === true) {
85
+ this.brModalClose.emit();
86
+ this.closeParentScrim();
87
+ }
88
+ }
89
+ closeParentScrim() {
90
+ const parentScrim = this.el.closest('br-scrim');
91
+ if (parentScrim && typeof parentScrim.close === 'function') {
92
+ parentScrim.close();
93
+ }
94
+ }
95
+ componentWillLoad() {
96
+ if (!this.show) {
97
+ const parentScrim = this.el.closest('br-scrim');
98
+ if (parentScrim !== null && parentScrim.isOpen) {
99
+ this.show = true;
100
+ }
101
+ }
102
+ }
103
+ componentDidLoad() {
104
+ this.listenToParentScrimEvents();
105
+ if (this.show) {
106
+ this.openParentScrim();
107
+ }
108
+ }
109
+ disconnectedCallback() {
110
+ this.isOpeningParentScrim = false;
111
+ this.removeParentScrimEventListeners();
112
+ }
113
+ /**
114
+ * Adiciona listeners para eventos do parent scrim
115
+ */
116
+ listenToParentScrimEvents() {
117
+ const parentScrim = this.el.closest('br-scrim');
118
+ if (parentScrim !== null) {
119
+ parentScrim.addEventListener('brScrimOpen', this.handleParentScrimOpen);
120
+ parentScrim.addEventListener('brScrimClose', this.handleParentScrimClose);
121
+ }
122
+ }
123
+ /**
124
+ * Remove listeners de eventos do parent scrim
125
+ */
126
+ removeParentScrimEventListeners() {
127
+ const parentScrim = this.el.closest('br-scrim');
128
+ if (parentScrim !== null) {
129
+ parentScrim.removeEventListener('brScrimOpen', this.handleParentScrimOpen);
130
+ parentScrim.removeEventListener('brScrimClose', this.handleParentScrimClose);
131
+ }
132
+ }
133
+ openParentScrim() {
134
+ if (this.isOpeningParentScrim)
135
+ return;
136
+ const parentScrim = this.el.closest('br-scrim');
137
+ if (parentScrim && !parentScrim.isOpen) {
138
+ this.isOpeningParentScrim = true;
139
+ Promise.resolve()
140
+ .then(() => {
141
+ try {
142
+ return parentScrim.open();
143
+ }
144
+ catch (error) {
145
+ console.warn('br-modal: Erro ao abrir scrim pai:', error);
146
+ throw error;
147
+ }
148
+ })
149
+ .finally(() => {
150
+ this.isOpeningParentScrim = false;
151
+ });
152
+ }
153
+ }
54
154
  get titleId() {
55
155
  return `${this.customId}-title`;
56
156
  }
@@ -70,6 +170,7 @@ export class Modal {
70
170
  open() {
71
171
  return __awaiter(this, void 0, void 0, function* () {
72
172
  this.show = true;
173
+ this.openParentScrim();
73
174
  });
74
175
  }
75
176
  /**
@@ -77,19 +178,17 @@ export class Modal {
77
178
  */
78
179
  close() {
79
180
  return __awaiter(this, void 0, void 0, function* () {
80
- this.show = false;
81
- // Se o modal está dentro de um br-scrim, fecha o scrim também
82
- const parentScrim = this.el.closest('br-scrim');
83
- if (parentScrim) {
84
- parentScrim.close();
181
+ if (!this.autoClose) {
182
+ return;
85
183
  }
184
+ this.show = false;
86
185
  });
87
186
  }
88
187
  render() {
89
188
  if (!this.show) {
90
189
  return null;
91
190
  }
92
- return (h(Host, null, h("div", { class: this.getCssClassMap(), role: "dialog", "aria-modal": "true", "aria-labelledby": this.titleId, "aria-describedby": this.contentId }, h("div", { class: "br-modal-header" }, h("div", { class: "modal-title", id: this.titleId }, h("slot", { name: "header" }, this.titleText)), h("slot", { name: "close-button" }, h("br-button", { class: "close", shape: "circle", density: "small", "aria-label": "Fechar", onClick: this.handleClose }, h("br-icon", { "icon-name": "fa-solid:times" })))), h("div", { id: this.contentId, class: "br-modal-body" }, h("slot", null)), h("div", { class: `br-modal-footer ${this.alignFooter ? `justify-content-${this.alignFooter}` : ''}` }, h("slot", { name: "footer" })))));
191
+ return (h(Host, null, h("div", { class: this.getCssClassMap(), role: "dialog", "aria-modal": "true", "aria-labelledby": this.titleId, "aria-describedby": this.contentId }, h("div", { class: "br-modal-header" }, h("div", { class: "modal-title", id: this.titleId }, h("slot", { name: "header" }, this.titleText)), h("slot", { name: "close-button" }, h("br-button", { class: "close", shape: "circle", density: "small", "aria-label": "Fechar", onClick: this.handleClose }, h("br-icon", { "icon-name": "fa-solid:times" })))), h("div", { id: this.contentId, class: "br-modal-body" }, h("slot", null)), h("div", { class: `br-modal-footer ${this.alignFooter !== undefined ? `justify-content-${this.alignFooter}` : ''}` }, h("slot", { name: "footer" })))));
93
192
  }
94
193
  static get is() { return "br-modal"; }
95
194
  static get encapsulation() { return "shadow"; }
@@ -177,7 +276,7 @@ export class Modal {
177
276
  "optional": false,
178
277
  "docs": {
179
278
  "tags": [],
180
- "text": "Se `true`, o modal se fechar\u00E1 automaticamente ao clicar no bot\u00E3o de fechar ou pressionar a tecla 'Escape'."
279
+ "text": "Define o comportamento de fechamento do modal.\n\n- `true`: O modal fecha automaticamente ao clicar no bot\u00E3o fechar.\n- `false`: O modal emite `brModalBeforeClose` mas n\u00E3o fecha, permitindo a implementa\u00E7\u00E3o de l\u00F3gica customizada (valida\u00E7\u00E3o, confirma\u00E7\u00E3o, etc.) antes do fechamento. O desenvolvedor deve\n controlar manualmente o fechamento."
181
280
  },
182
281
  "getter": false,
183
282
  "setter": false,
@@ -224,26 +323,6 @@ export class Modal {
224
323
  "attribute": "size",
225
324
  "defaultValue": "'medium'"
226
325
  },
227
- "scrim": {
228
- "type": "boolean",
229
- "mutable": false,
230
- "complexType": {
231
- "original": "false",
232
- "resolved": "boolean",
233
- "references": {}
234
- },
235
- "required": false,
236
- "optional": false,
237
- "docs": {
238
- "tags": [],
239
- "text": "Se `true`, exibe um fundo escurecido (scrim) atr\u00E1s do modal."
240
- },
241
- "getter": false,
242
- "setter": false,
243
- "reflect": true,
244
- "attribute": "scrim",
245
- "defaultValue": "false"
246
- },
247
326
  "scrollable": {
248
327
  "type": "boolean",
249
328
  "mutable": false,
@@ -288,6 +367,21 @@ export class Modal {
288
367
  }
289
368
  static get events() {
290
369
  return [{
370
+ "method": "brModalOpen",
371
+ "name": "brModalOpen",
372
+ "bubbles": true,
373
+ "cancelable": true,
374
+ "composed": true,
375
+ "docs": {
376
+ "tags": [],
377
+ "text": "Evento emitido quando o modal \u00E9 aberto (quando `show` muda de `false` para `true`)."
378
+ },
379
+ "complexType": {
380
+ "original": "void",
381
+ "resolved": "void",
382
+ "references": {}
383
+ }
384
+ }, {
291
385
  "method": "brModalClose",
292
386
  "name": "brModalClose",
293
387
  "bubbles": true,
@@ -295,7 +389,22 @@ export class Modal {
295
389
  "composed": true,
296
390
  "docs": {
297
391
  "tags": [],
298
- "text": "Evento emitido quando o modal solicita ser fechado (pelo bot\u00E3o 'x', tecla 'Escape' ou clique no scrim)."
392
+ "text": "Evento emitido ap\u00F3s o modal ser fechado (quando `show` muda de `true` para `false`)."
393
+ },
394
+ "complexType": {
395
+ "original": "void",
396
+ "resolved": "void",
397
+ "references": {}
398
+ }
399
+ }, {
400
+ "method": "brModalBeforeClose",
401
+ "name": "brModalBeforeClose",
402
+ "bubbles": true,
403
+ "cancelable": true,
404
+ "composed": true,
405
+ "docs": {
406
+ "tags": [],
407
+ "text": "Evento emitido antes do fechamento do modal (quando o bot\u00E3o X \u00E9 clicado).\nSe autoClose est\u00E1 desativado, o desenvolvedor deve fechar manualmente o modal ap\u00F3s este evento.\nSe autoClose est\u00E1 ativado, o modal fecha automaticamente ap\u00F3s este evento."
299
408
  },
300
409
  "complexType": {
301
410
  "original": "void",
@@ -343,6 +452,12 @@ export class Modal {
343
452
  };
344
453
  }
345
454
  static get elementRef() { return "el"; }
455
+ static get watchers() {
456
+ return [{
457
+ "propName": "show",
458
+ "methodName": "handleShowChange"
459
+ }];
460
+ }
346
461
  static get attachInternalsMemberName() { return "elementInternals"; }
347
462
  }
348
463
  //# sourceMappingURL=modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"modal.js","sourceRoot":"","sources":["../../../../../../packages/webcomponents/src/components/modal/modal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AttachInternals, Component, Element, Event, EventEmitter, h, Host, Method, Prop } from '@stencil/core'\n\nimport Helpers from '../../utils/helpers'\nimport { CssClassMap } from '../../utils/interfaces'\n\n/**\n * ## Design System\n *\n * Para a documentação completa de design, incluindo diretrizes de uso, acessibilidade e exemplos visuais, consulte o [Design System do GovBR](https://www.gov.br/ds/components/modal?tab=designer).\n *\n * @slot header - Cabeçalho do modal. Se não for usado, o `title-text` será exibido.\n * @slot default - Corpo do modal.\n * @slot footer - Rodapé do modal.\n * @slot close-button - Botão de fechar customizado. Se não for usado, o botão padrão será exibido.\n *\n */\n@Component({\n tag: 'br-modal',\n styleUrl: 'modal.scss',\n shadow: true,\n formAssociated: true,\n})\nexport class Modal {\n /** Referência ao elemento host do componente. */\n @Element() el: HTMLBrModalElement\n\n @AttachInternals() elementInternals: ElementInternals\n\n /**\n * Identificador único.\n * Caso não seja fornecido, um ID gerado automaticamente será usado.\n */\n @Prop({ reflect: true }) readonly customId: string = Helpers.generateUniqueId('modal')\n\n /** O texto do título a ser exibido no cabeçalho do modal. Usado quando o slot `header` não é fornecido. */\n @Prop() readonly titleText: string\n\n /** Controla a visibilidade do modal. */\n @Prop({ reflect: true, mutable: true }) show = false\n\n /** Se `true`, o modal se fechará automaticamente ao clicar no botão de fechar ou pressionar a tecla 'Escape'. */\n @Prop({ reflect: true }) autoClose = false\n\n /** Seletor CSS para o elemento que deve receber foco quando o modal é aberto. */\n @Prop() readonly initialFocusSelector: string\n\n /**\n * Define o tamanho (largura) do modal.\n */\n @Prop({ reflect: true }) readonly size: 'xsmall' | 'small' | 'medium' | 'large' | 'auto' = 'medium'\n\n /** Se `true`, exibe um fundo escurecido (scrim) atrás do modal. */\n @Prop({ reflect: true }) readonly scrim = false\n\n /** Se `true`, habilita a rolagem interna do conteúdo do modal. */\n @Prop({ reflect: true }) readonly scrollable = false\n\n /**\n * Define o alinhamento do conteúdo do rodapé (slot=\"footer\").\n */\n @Prop({ reflect: true }) readonly alignFooter: 'start' | 'end' | 'center' = 'center'\n\n /** Evento emitido quando o modal solicita ser fechado (pelo botão 'x', tecla 'Escape' ou clique no scrim). */\n @Event() brModalClose: EventEmitter<void>\n\n private get titleId() {\n return `${this.customId}-title`\n }\n\n private get contentId() {\n return `${this.customId}-content`\n }\n\n private getCssClassMap(): CssClassMap {\n return {\n 'br-modal': true,\n [this.size]: true,\n scrollable: this.scrollable,\n }\n }\n\n private handleClose = () => {\n this.brModalClose.emit()\n if (this.autoClose) {\n this.show = false\n\n // Se o modal está dentro de um br-scrim, fecha o scrim também\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim) {\n parentScrim.close()\n }\n }\n }\n\n /**\n * Método público para abrir o modal\n */\n @Method()\n async open() {\n this.show = true\n }\n\n /**\n * Método público para fechar o modal\n */\n @Method()\n async close() {\n this.show = false\n\n // Se o modal está dentro de um br-scrim, fecha o scrim também\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim) {\n parentScrim.close()\n }\n }\n\n render() {\n if (!this.show) {\n return null\n }\n\n return (\n <Host>\n <div\n class={this.getCssClassMap()}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={this.titleId}\n aria-describedby={this.contentId}\n >\n <div class=\"br-modal-header\">\n <div class=\"modal-title\" id={this.titleId}>\n <slot name=\"header\">{this.titleText}</slot>\n </div>\n <slot name=\"close-button\">\n <br-button class=\"close\" shape=\"circle\" density=\"small\" aria-label=\"Fechar\" onClick={this.handleClose}>\n <br-icon icon-name=\"fa-solid:times\"></br-icon>\n </br-button>\n </slot>\n </div>\n <div id={this.contentId} class=\"br-modal-body\">\n <slot></slot>\n </div>\n <div class={`br-modal-footer ${this.alignFooter ? `justify-content-${this.alignFooter}` : ''}`}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </Host>\n )\n }\n}\n"]}
1
+ {"version":3,"file":"modal.js","sourceRoot":"","sources":["../../../../../../packages/webcomponents/src/components/modal/modal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AttachInternals, Component, Element, Event, EventEmitter, h, Host, Method, Prop, Watch } from '@stencil/core'\n\nimport Helpers from '../../utils/helpers'\nimport { CssClassMap } from '../../utils/interfaces'\n\n/**\n * ## Design System\n *\n * Para a documentação completa de design, incluindo diretrizes de uso, acessibilidade e exemplos visuais, consulte o [Design System do GovBR](https://www.gov.br/ds/components/modal?tab=designer).\n *\n * @slot header - Cabeçalho do modal. Se não for usado, o `title-text` será exibido.\n * @slot default - Corpo do modal.\n * @slot footer - Rodapé do modal.\n * @slot close-button - Botão de fechar customizado. Se não for usado, o botão padrão será exibido.\n *\n */\n@Component({\n tag: 'br-modal',\n styleUrl: 'modal.scss',\n shadow: true,\n formAssociated: true,\n})\nexport class Modal {\n /** Referência ao elemento host do componente. */\n @Element() el: HTMLBrModalElement\n\n @AttachInternals() elementInternals: ElementInternals\n\n /**\n * Identificador único.\n * Caso não seja fornecido, um ID gerado automaticamente será usado.\n */\n @Prop({ reflect: true }) readonly customId: string = Helpers.generateUniqueId('modal')\n\n /** O texto do título a ser exibido no cabeçalho do modal. Usado quando o slot `header` não é fornecido. */\n @Prop() readonly titleText: string\n\n /** Controla a visibilidade do modal. */\n @Prop({ reflect: true, mutable: true }) show = false\n\n /**\n * Define o comportamento de fechamento do modal.\n *\n * - `true`: O modal fecha automaticamente ao clicar no botão fechar.\n * - `false`: O modal emite `brModalBeforeClose` mas não fecha, permitindo a implementação de lógica customizada (validação, confirmação, etc.) antes do fechamento. O desenvolvedor deve\n * controlar manualmente o fechamento.\n */\n @Prop({ reflect: true }) autoClose = false\n\n /** Seletor CSS para o elemento que deve receber foco quando o modal é aberto. */\n @Prop() readonly initialFocusSelector: string\n\n /**\n * Define o tamanho (largura) do modal.\n */\n @Prop({ reflect: true }) readonly size: 'xsmall' | 'small' | 'medium' | 'large' | 'auto' = 'medium'\n\n /** Se `true`, habilita a rolagem interna do conteúdo do modal. */\n @Prop({ reflect: true }) readonly scrollable = false\n\n /**\n * Define o alinhamento do conteúdo do rodapé (slot=\"footer\").\n */\n @Prop({ reflect: true }) readonly alignFooter: 'start' | 'end' | 'center' = 'center'\n\n private isOpeningParentScrim = false\n\n /**\n * Evento emitido quando o modal é aberto (quando `show` muda de `false` para `true`).\n */\n @Event() brModalOpen: EventEmitter<void>\n\n /**\n * Evento emitido após o modal ser fechado (quando `show` muda de `true` para `false`).\n */\n @Event() brModalClose: EventEmitter<void>\n\n /**\n * Evento emitido antes do fechamento do modal (quando o botão X é clicado).\n * Se autoClose está desativado, o desenvolvedor deve fechar manualmente o modal após este evento.\n * Se autoClose está ativado, o modal fecha automaticamente após este evento.\n */\n @Event() brModalBeforeClose: EventEmitter<void>\n\n @Watch('show')\n handleShowChange(newValue: boolean, oldValue: boolean) {\n if (newValue) {\n this.brModalOpen.emit()\n this.openParentScrim()\n } else if (oldValue === true) {\n this.brModalClose.emit()\n this.closeParentScrim()\n }\n }\n\n private closeParentScrim() {\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim && typeof parentScrim.close === 'function') {\n parentScrim.close()\n }\n }\n\n componentWillLoad() {\n if (!this.show) {\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim !== null && parentScrim.isOpen) {\n this.show = true\n }\n }\n }\n\n componentDidLoad() {\n this.listenToParentScrimEvents()\n\n if (this.show) {\n this.openParentScrim()\n }\n }\n\n disconnectedCallback() {\n this.isOpeningParentScrim = false\n this.removeParentScrimEventListeners()\n }\n\n /**\n * Adiciona listeners para eventos do parent scrim\n */\n private listenToParentScrimEvents() {\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim !== null) {\n parentScrim.addEventListener('brScrimOpen', this.handleParentScrimOpen)\n parentScrim.addEventListener('brScrimClose', this.handleParentScrimClose)\n }\n }\n\n /**\n * Remove listeners de eventos do parent scrim\n */\n private removeParentScrimEventListeners() {\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim !== null) {\n parentScrim.removeEventListener('brScrimOpen', this.handleParentScrimOpen)\n parentScrim.removeEventListener('brScrimClose', this.handleParentScrimClose)\n }\n }\n\n /**\n * Handler para evento brScrimOpen do parent scrim\n * Quando scrim abre programaticamente, a modal deve abrir também\n */\n private handleParentScrimOpen = () => {\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim === null) return\n\n if (!this.show) {\n this.show = true\n }\n }\n\n /**\n * Handler para evento brScrimClose do parent scrim\n * Se autoClose=true, fecha a modal junto com o scrim\n * Se autoClose=false, não faz nada (modal permanece aberta)\n */\n private handleParentScrimClose = () => {\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim === null) return\n\n if (this.autoClose && this.show) {\n this.show = false\n }\n }\n\n private openParentScrim() {\n if (this.isOpeningParentScrim) return\n\n const parentScrim = this.el.closest('br-scrim')\n if (parentScrim && !parentScrim.isOpen) {\n this.isOpeningParentScrim = true\n\n Promise.resolve()\n .then(() => {\n try {\n return parentScrim.open()\n } catch (error) {\n console.warn('br-modal: Erro ao abrir scrim pai:', error)\n throw error\n }\n })\n .finally(() => {\n this.isOpeningParentScrim = false\n })\n }\n }\n\n private get titleId() {\n return `${this.customId}-title`\n }\n\n private get contentId() {\n return `${this.customId}-content`\n }\n\n private getCssClassMap(): CssClassMap {\n return {\n 'br-modal': true,\n [this.size]: true,\n scrollable: this.scrollable,\n }\n }\n\n private handleClose = () => {\n this.brModalBeforeClose.emit()\n\n if (this.autoClose) {\n this.show = false\n }\n }\n\n /**\n * Método público para abrir o modal\n */\n @Method()\n async open() {\n this.show = true\n\n this.openParentScrim()\n }\n\n /**\n * Método público para fechar o modal\n */\n @Method()\n async close() {\n if (!this.autoClose) {\n return\n }\n\n this.show = false\n }\n\n render() {\n if (!this.show) {\n return null\n }\n\n return (\n <Host>\n <div\n class={this.getCssClassMap()}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={this.titleId}\n aria-describedby={this.contentId}\n >\n <div class=\"br-modal-header\">\n <div class=\"modal-title\" id={this.titleId}>\n <slot name=\"header\">{this.titleText}</slot>\n </div>\n <slot name=\"close-button\">\n <br-button class=\"close\" shape=\"circle\" density=\"small\" aria-label=\"Fechar\" onClick={this.handleClose}>\n <br-icon icon-name=\"fa-solid:times\"></br-icon>\n </br-button>\n </slot>\n </div>\n <div id={this.contentId} class=\"br-modal-body\">\n <slot></slot>\n </div>\n <div class={`br-modal-footer ${this.alignFooter !== undefined ? `justify-content-${this.alignFooter}` : ''}`}>\n <slot name=\"footer\"></slot>\n </div>\n </div>\n </Host>\n )\n }\n}\n"]}
@@ -82,6 +82,29 @@ export class Scrim {
82
82
  this.previouslyFocusedElement = null;
83
83
  this.focusTimeoutId = null;
84
84
  this.restoreFocusTimeoutId = null;
85
+ // ======================================================================
86
+ // GERENCIAMENTO DE COMPONENTES FILHOS
87
+ // ======================================================================
88
+ /**
89
+ * Handler para evento brModalOpen emitido por modal filha
90
+ */
91
+ this.handleModalOpen = (event) => {
92
+ const modalElement = event.target;
93
+ if (!this.isDirectChild(modalElement))
94
+ return;
95
+ if (!this.isOpen) {
96
+ this.open();
97
+ }
98
+ };
99
+ /**
100
+ * Handler para evento brModalClose emitido por modal filha
101
+ */
102
+ this.handleModalClose = (event) => {
103
+ const modalElement = event.target;
104
+ if (!this.isDirectChild(modalElement))
105
+ return;
106
+ this.close();
107
+ };
85
108
  this.handleActivatorClick = () => {
86
109
  this.toggle();
87
110
  };
@@ -99,6 +122,10 @@ export class Scrim {
99
122
  const reference = (_a = this.scrollOrigin) !== null && _a !== void 0 ? _a : this.lastScrollTop;
100
123
  if (Math.abs(currentScroll - reference) > this.scrollThreshold) {
101
124
  this.scrollOrigin = null;
125
+ // Não fecha se tiver modal filha com autoClose=false ou não definido
126
+ if (this.hasModalWithAutoClose()) {
127
+ return;
128
+ }
102
129
  this.isOpen = false;
103
130
  this.handleOpenChange(false);
104
131
  return;
@@ -134,7 +161,9 @@ export class Scrim {
134
161
  }
135
162
  if (event.key === 'Escape' && !this.disableCloseOnClick) {
136
163
  event.preventDefault();
137
- this.isOpen = false;
164
+ if (!this.hasModalWithAutoClose()) {
165
+ this.isOpen = false;
166
+ }
138
167
  }
139
168
  };
140
169
  // Intercepta TODOS os eventos de clique globalmente quando scrim está ativo
@@ -168,11 +197,15 @@ export class Scrim {
168
197
  }, 0);
169
198
  }
170
199
  };
171
- // ======================================================================
172
- // MANIPULAÇÃO DE EVENTOS E UTILITÁRIOS
173
- // ======================================================================
200
+ /**
201
+ * Handler para clique na máscara do scrim
202
+ * Fecha o scrim se disableCloseOnClick for false e não houver modal filha com autoClose=false
203
+ */
174
204
  this.handleMaskClick = (_event) => {
175
205
  if (!this.disableCloseOnClick) {
206
+ if (this.hasModalWithAutoClose()) {
207
+ return;
208
+ }
176
209
  this.close();
177
210
  }
178
211
  };
@@ -181,37 +214,39 @@ export class Scrim {
181
214
  // LIFECYCLE HOOKS E WATCHERS
182
215
  // ======================================================================
183
216
  handleOpenChange(newValue) {
184
- // Cancelar qualquer timeout anterior
185
- if (this.focusTimeoutId !== null) {
186
- clearTimeout(this.focusTimeoutId);
187
- this.focusTimeoutId = null;
188
- }
189
- if (this.restoreFocusTimeoutId !== null) {
190
- clearTimeout(this.restoreFocusTimeoutId);
191
- this.restoreFocusTimeoutId = null;
192
- }
193
- this.isActive = newValue;
194
- if (newValue && this.scrollStrategy === 'close') {
195
- const currentScroll = window.scrollY;
196
- this.lastScrollTop = currentScroll;
197
- this.scrollOrigin = currentScroll;
198
- }
199
- this.updateStyles();
200
- if (this.isActive) {
201
- this.previouslyFocusedElement = document.activeElement;
202
- this.applyScrollStrategy();
203
- this.addGlobalEventInterception();
204
- this.focusTimeoutId = setTimeout(() => {
205
- this.focusFirstElement();
206
- }, 100);
207
- this.brScrimOpen.emit();
208
- }
209
- else {
210
- this.resetScrolling();
211
- this.removeGlobalEventInterception();
212
- this.restoreFocus();
213
- this.brScrimClose.emit();
214
- }
217
+ return __awaiter(this, void 0, void 0, function* () {
218
+ // Cancelar qualquer timeout anterior
219
+ if (this.focusTimeoutId !== null) {
220
+ clearTimeout(this.focusTimeoutId);
221
+ this.focusTimeoutId = null;
222
+ }
223
+ if (this.restoreFocusTimeoutId !== null) {
224
+ clearTimeout(this.restoreFocusTimeoutId);
225
+ this.restoreFocusTimeoutId = null;
226
+ }
227
+ this.isActive = newValue;
228
+ if (newValue && this.scrollStrategy === 'close') {
229
+ const currentScroll = window.scrollY;
230
+ this.lastScrollTop = currentScroll;
231
+ this.scrollOrigin = currentScroll;
232
+ }
233
+ this.updateStyles();
234
+ if (this.isActive) {
235
+ this.previouslyFocusedElement = document.activeElement;
236
+ this.applyScrollStrategy();
237
+ this.addGlobalEventInterception();
238
+ this.focusTimeoutId = setTimeout(() => {
239
+ this.focusFirstElement();
240
+ }, 100);
241
+ this.brScrimOpen.emit();
242
+ }
243
+ else {
244
+ this.resetScrolling();
245
+ this.removeGlobalEventInterception();
246
+ this.restoreFocus();
247
+ this.brScrimClose.emit();
248
+ }
249
+ });
215
250
  }
216
251
  updateStyles() {
217
252
  if (this.zIndex !== null && this.zIndex !== undefined) {
@@ -238,6 +273,7 @@ export class Scrim {
238
273
  this.setupActivator();
239
274
  this.setupActivatorSlotClick();
240
275
  this.addContentEventListener();
276
+ this.listenToModalEvents();
241
277
  if (this.isActive) {
242
278
  this.applyScrollStrategy();
243
279
  this.addGlobalEventInterception();
@@ -271,7 +307,6 @@ export class Scrim {
271
307
  clearTimeout(this.restoreFocusTimeoutId);
272
308
  this.restoreFocusTimeoutId = null;
273
309
  }
274
- // Remover event listeners
275
310
  window.removeEventListener('scroll', this.handleScrollClose);
276
311
  window.removeEventListener('scroll', this.handleBlockScroll);
277
312
  if (this.contentRef !== null) {
@@ -280,6 +315,58 @@ export class Scrim {
280
315
  this.removeActivatorClickListener();
281
316
  this.cleanupActivatorSlotClick();
282
317
  this.removeGlobalEventInterception();
318
+ this.removeModalEventListener();
319
+ }
320
+ /**
321
+ * Verifica se um elemento é filho direto deste scrim (não nested)
322
+ */
323
+ isDirectChild(element) {
324
+ const children = this.getDirectChildren();
325
+ return children.some((child) => child === element || child.contains(element));
326
+ }
327
+ /**
328
+ * Adiciona listeners para eventos brModalOpen e brModalClose das modais filhas
329
+ */
330
+ listenToModalEvents() {
331
+ this.el.addEventListener('brModalOpen', this.handleModalOpen);
332
+ this.el.addEventListener('brModalClose', this.handleModalClose);
333
+ }
334
+ /**
335
+ * Remove listeners dos eventos da modal
336
+ */
337
+ removeModalEventListener() {
338
+ this.el.removeEventListener('brModalOpen', this.handleModalOpen);
339
+ this.el.removeEventListener('brModalClose', this.handleModalClose);
340
+ }
341
+ /**
342
+ * Retorna os elementos filhos diretos do scrim via slot padrão
343
+ */
344
+ getDirectChildren() {
345
+ const shadowRoot = this.el.shadowRoot;
346
+ if (shadowRoot === null)
347
+ return [];
348
+ const slot = shadowRoot.querySelector('slot:not([name])');
349
+ if (slot === null)
350
+ return [];
351
+ return slot.assignedElements();
352
+ }
353
+ /**
354
+ * Busca uma modal filha direta do scrim
355
+ */
356
+ findModalChild() {
357
+ var _a;
358
+ const children = this.getDirectChildren();
359
+ const modalElement = children.find((child) => child.tagName === 'BR-MODAL');
360
+ return (_a = modalElement) !== null && _a !== void 0 ? _a : null;
361
+ }
362
+ /**
363
+ * Verifica se há uma modal filha com autoClose=false (padrão ou explícito)
364
+ * Retorna true se:
365
+ * - Existe modal filha E autoClose não é explicitamente true
366
+ */
367
+ hasModalWithAutoClose() {
368
+ const modal = this.findModalChild();
369
+ return modal !== null && modal.autoClose !== true;
283
370
  }
284
371
  // ======================================================================
285
372
  // GERENCIAMENTO DO ACTIVATOR (ELEMENTO ACIONADOR)
@@ -468,7 +555,11 @@ export class Scrim {
468
555
  return elements.filter((el) => el.offsetWidth > 0 && el.offsetHeight > 0 && getComputedStyle(el).visibility !== 'hidden');
469
556
  }
470
557
  shouldHandleEscape(event) {
471
- return (event.key === 'Escape' || event.key === 'Esc') && this.isActive && !this.disableCloseOnClick;
558
+ if ((event.key === 'Escape' || event.key === 'Esc') && this.isActive && !this.disableCloseOnClick) {
559
+ // Não fecha se tiver modal filha com autoClose=false ou não definido
560
+ return !this.hasModalWithAutoClose();
561
+ }
562
+ return false;
472
563
  }
473
564
  handleTabNavigation(event) {
474
565
  const focusableElements = this.getFocusableElements();
@@ -530,6 +621,9 @@ export class Scrim {
530
621
  document.removeEventListener('focus', this.handleGlobalFocus, true);
531
622
  document.removeEventListener('focusin', this.handleGlobalFocus, true);
532
623
  }
624
+ // ======================================================================
625
+ // UTILITÁRIOS
626
+ // ======================================================================
533
627
  getCssClassMap() {
534
628
  return {
535
629
  'br-scrim': true,
@@ -580,7 +674,7 @@ export class Scrim {
580
674
  });
581
675
  }
582
676
  render() {
583
- return (h(Host, { key: '1aea7418642867cfb24c099729f34412f9aec7bf' }, h("slot", { key: '4468892291450f13cded3627ee91f31716108443', name: "activator" }), h("div", { key: '6019de52484c176a5f5d706ca1dc759039dae940', class: this.getCssClassMap(), id: this.customId, role: "dialog", "aria-modal": "true", "aria-label": this.ariaLabel || 'Conteúdo do diálogo', "data-visible": this.isActive ? 'true' : 'false' }, h("div", { key: '6c6df83177f1a14097ec5c08e409caaca3953d3b', class: "mask", onClick: this.handleMaskClick }), h("div", { key: '1af0ecd53a0aaf630069139a7e6935533a563e10', ref: (el) => (this.contentRef = el), id: `${this.customId}-content`, class: "content", tabIndex: -1, onKeyDown: this.handleKeyDown }, h("slot", { key: '2cb75f5b254ed22bbe821998700d9c1f4152dd5b' })))));
677
+ return (h(Host, { key: '249de7af4056263464615cbe086b419f74b8981b' }, h("slot", { key: '1698faa6b492e1ec594eba8425f20582a2af0e4a', name: "activator" }), h("div", { key: 'adedf3eb84f3d8f79ac9bb19209022301453c019', class: this.getCssClassMap(), id: this.customId, role: "dialog", "aria-modal": "true", "aria-label": this.ariaLabel || 'Conteúdo do diálogo', "data-visible": this.isActive ? 'true' : 'false' }, h("div", { key: 'ef7408a6672fabf4d2f79bc7922f301fd102652e', class: "mask", onClick: this.handleMaskClick }), h("div", { key: '91ffb1a84478f09fc90cefbb97180c5db13dba5c', ref: (el) => (this.contentRef = el), id: `${this.customId}-content`, class: "content", tabIndex: -1, onKeyDown: this.handleKeyDown }, h("slot", { key: '0600ac804980446103154cebf83389ac92e44d9d' })))));
584
678
  }
585
679
  static get is() { return "br-scrim"; }
586
680
  static get encapsulation() { return "shadow"; }