@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.
- package/dist/cjs/br-modal.cjs.entry.js +119 -15
- package/dist/cjs/br-modal.entry.cjs.js.map +1 -1
- package/dist/cjs/br-scrim.cjs.entry.js +132 -38
- package/dist/cjs/br-scrim.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/webcomponents.cjs.js +1 -1
- package/dist/collection/components/modal/modal.js +152 -37
- package/dist/collection/components/modal/modal.js.map +1 -1
- package/dist/collection/components/scrim/scrim.js +132 -38
- package/dist/collection/components/scrim/scrim.js.map +1 -1
- package/dist/collection/pages/components/modal/index.js +1 -53
- package/dist/components/br-modal.js +123 -26
- package/dist/components/br-modal.js.map +1 -1
- package/dist/components/br-scrim.js +125 -44
- package/dist/components/br-scrim.js.map +1 -1
- package/dist/esm/br-modal.entry.js +119 -15
- package/dist/esm/br-modal.entry.js.map +1 -1
- package/dist/esm/br-scrim.entry.js +132 -38
- package/dist/esm/br-scrim.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/webcomponents.js +1 -1
- package/dist/hydrate/index.js +252 -55
- package/dist/hydrate/index.mjs +252 -55
- package/dist/types/components/modal/modal.d.ts +46 -4
- package/dist/types/components/scrim/scrim.d.ts +39 -1
- package/dist/types/components.d.ts +11 -11
- package/dist/webcomponents/br-modal.entry.esm.js.map +1 -1
- package/dist/webcomponents/br-scrim.entry.esm.js.map +1 -1
- package/dist/webcomponents/{p-e7b38c63.entry.js → p-216dc111.entry.js} +126 -45
- package/dist/webcomponents/p-216dc111.entry.js.map +1 -0
- package/dist/webcomponents/{p-28dba962.entry.js → p-463b06a4.entry.js} +129 -33
- package/dist/webcomponents/p-463b06a4.entry.js.map +1 -0
- package/dist/webcomponents/webcomponents.esm.js +1 -1
- package/package.json +1 -1
- package/dist/custom-elements.json +0 -18276
- package/dist/webcomponents/p-28dba962.entry.js.map +0 -1
- package/dist/webcomponents/p-e7b38c63.entry.js.map +0 -1
- package/dist/webcomponents.html-custom-data.json +0 -1214
|
@@ -11,7 +11,9 @@ const modalCss = "*,*::before,*::after{-webkit-box-sizing:border-box;box-sizing:
|
|
|
11
11
|
const Modal = class {
|
|
12
12
|
constructor(hostRef) {
|
|
13
13
|
registerInstance(this, hostRef);
|
|
14
|
+
this.brModalOpen = createEvent(this, "brModalOpen", 7);
|
|
14
15
|
this.brModalClose = createEvent(this, "brModalClose", 7);
|
|
16
|
+
this.brModalBeforeClose = createEvent(this, "brModalBeforeClose", 7);
|
|
15
17
|
if (hostRef.$hostElement$["s-ei"]) {
|
|
16
18
|
this.elementInternals = hostRef.$hostElement$["s-ei"];
|
|
17
19
|
}
|
|
@@ -26,32 +28,132 @@ const Modal = class {
|
|
|
26
28
|
this.customId = Helpers.generateUniqueId('modal');
|
|
27
29
|
/** Controla a visibilidade do modal. */
|
|
28
30
|
this.show = false;
|
|
29
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Define o comportamento de fechamento do modal.
|
|
33
|
+
*
|
|
34
|
+
* - `true`: O modal fecha automaticamente ao clicar no botão fechar.
|
|
35
|
+
* - `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
|
|
36
|
+
* controlar manualmente o fechamento.
|
|
37
|
+
*/
|
|
30
38
|
this.autoClose = false;
|
|
31
39
|
/**
|
|
32
40
|
* Define o tamanho (largura) do modal.
|
|
33
41
|
*/
|
|
34
42
|
this.size = 'medium';
|
|
35
|
-
/** Se `true`, exibe um fundo escurecido (scrim) atrás do modal. */
|
|
36
|
-
this.scrim = false;
|
|
37
43
|
/** Se `true`, habilita a rolagem interna do conteúdo do modal. */
|
|
38
44
|
this.scrollable = false;
|
|
39
45
|
/**
|
|
40
46
|
* Define o alinhamento do conteúdo do rodapé (slot="footer").
|
|
41
47
|
*/
|
|
42
48
|
this.alignFooter = 'center';
|
|
49
|
+
this.isOpeningParentScrim = false;
|
|
50
|
+
/**
|
|
51
|
+
* Handler para evento brScrimOpen do parent scrim
|
|
52
|
+
* Quando scrim abre programaticamente, a modal deve abrir também
|
|
53
|
+
*/
|
|
54
|
+
this.handleParentScrimOpen = () => {
|
|
55
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
56
|
+
if (parentScrim === null)
|
|
57
|
+
return;
|
|
58
|
+
if (!this.show) {
|
|
59
|
+
this.show = true;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Handler para evento brScrimClose do parent scrim
|
|
64
|
+
* Se autoClose=true, fecha a modal junto com o scrim
|
|
65
|
+
* Se autoClose=false, não faz nada (modal permanece aberta)
|
|
66
|
+
*/
|
|
67
|
+
this.handleParentScrimClose = () => {
|
|
68
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
69
|
+
if (parentScrim === null)
|
|
70
|
+
return;
|
|
71
|
+
if (this.autoClose && this.show) {
|
|
72
|
+
this.show = false;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
43
75
|
this.handleClose = () => {
|
|
44
|
-
this.
|
|
76
|
+
this.brModalBeforeClose.emit();
|
|
45
77
|
if (this.autoClose) {
|
|
46
78
|
this.show = false;
|
|
47
|
-
// Se o modal está dentro de um br-scrim, fecha o scrim também
|
|
48
|
-
const parentScrim = this.el.closest('br-scrim');
|
|
49
|
-
if (parentScrim) {
|
|
50
|
-
parentScrim.close();
|
|
51
|
-
}
|
|
52
79
|
}
|
|
53
80
|
};
|
|
54
81
|
}
|
|
82
|
+
handleShowChange(newValue, oldValue) {
|
|
83
|
+
if (newValue) {
|
|
84
|
+
this.brModalOpen.emit();
|
|
85
|
+
this.openParentScrim();
|
|
86
|
+
}
|
|
87
|
+
else if (oldValue === true) {
|
|
88
|
+
this.brModalClose.emit();
|
|
89
|
+
this.closeParentScrim();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
closeParentScrim() {
|
|
93
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
94
|
+
if (parentScrim && typeof parentScrim.close === 'function') {
|
|
95
|
+
parentScrim.close();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
componentWillLoad() {
|
|
99
|
+
if (!this.show) {
|
|
100
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
101
|
+
if (parentScrim !== null && parentScrim.isOpen) {
|
|
102
|
+
this.show = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
componentDidLoad() {
|
|
107
|
+
this.listenToParentScrimEvents();
|
|
108
|
+
if (this.show) {
|
|
109
|
+
this.openParentScrim();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
disconnectedCallback() {
|
|
113
|
+
this.isOpeningParentScrim = false;
|
|
114
|
+
this.removeParentScrimEventListeners();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Adiciona listeners para eventos do parent scrim
|
|
118
|
+
*/
|
|
119
|
+
listenToParentScrimEvents() {
|
|
120
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
121
|
+
if (parentScrim !== null) {
|
|
122
|
+
parentScrim.addEventListener('brScrimOpen', this.handleParentScrimOpen);
|
|
123
|
+
parentScrim.addEventListener('brScrimClose', this.handleParentScrimClose);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Remove listeners de eventos do parent scrim
|
|
128
|
+
*/
|
|
129
|
+
removeParentScrimEventListeners() {
|
|
130
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
131
|
+
if (parentScrim !== null) {
|
|
132
|
+
parentScrim.removeEventListener('brScrimOpen', this.handleParentScrimOpen);
|
|
133
|
+
parentScrim.removeEventListener('brScrimClose', this.handleParentScrimClose);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
openParentScrim() {
|
|
137
|
+
if (this.isOpeningParentScrim)
|
|
138
|
+
return;
|
|
139
|
+
const parentScrim = this.el.closest('br-scrim');
|
|
140
|
+
if (parentScrim && !parentScrim.isOpen) {
|
|
141
|
+
this.isOpeningParentScrim = true;
|
|
142
|
+
Promise.resolve()
|
|
143
|
+
.then(() => {
|
|
144
|
+
try {
|
|
145
|
+
return parentScrim.open();
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.warn('br-modal: Erro ao abrir scrim pai:', error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
})
|
|
152
|
+
.finally(() => {
|
|
153
|
+
this.isOpeningParentScrim = false;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
55
157
|
get titleId() {
|
|
56
158
|
return `${this.customId}-title`;
|
|
57
159
|
}
|
|
@@ -71,6 +173,7 @@ const Modal = class {
|
|
|
71
173
|
open() {
|
|
72
174
|
return __awaiter(this, void 0, void 0, function* () {
|
|
73
175
|
this.show = true;
|
|
176
|
+
this.openParentScrim();
|
|
74
177
|
});
|
|
75
178
|
}
|
|
76
179
|
/**
|
|
@@ -78,22 +181,23 @@ const Modal = class {
|
|
|
78
181
|
*/
|
|
79
182
|
close() {
|
|
80
183
|
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
-
this.
|
|
82
|
-
|
|
83
|
-
const parentScrim = this.el.closest('br-scrim');
|
|
84
|
-
if (parentScrim) {
|
|
85
|
-
parentScrim.close();
|
|
184
|
+
if (!this.autoClose) {
|
|
185
|
+
return;
|
|
86
186
|
}
|
|
187
|
+
this.show = false;
|
|
87
188
|
});
|
|
88
189
|
}
|
|
89
190
|
render() {
|
|
90
191
|
if (!this.show) {
|
|
91
192
|
return null;
|
|
92
193
|
}
|
|
93
|
-
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" })))));
|
|
194
|
+
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" })))));
|
|
94
195
|
}
|
|
95
196
|
static get formAssociated() { return true; }
|
|
96
197
|
get el() { return getElement(this); }
|
|
198
|
+
static get watchers() { return {
|
|
199
|
+
"show": ["handleShowChange"]
|
|
200
|
+
}; }
|
|
97
201
|
};
|
|
98
202
|
Modal.style = modalCss;
|
|
99
203
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"br-modal.entry.js","sources":["../../packages/webcomponents/src/components/modal/modal.scss?tag=br-modal&encapsulation=shadow","../../packages/webcomponents/src/components/modal/modal.tsx"],"sourcesContent":["@import \"~@govbr-ds/core/src/components/modal/modal\";\n\n.br-modal.scrollable {\n max-height: 400px;\n}\n\n.br-modal-footer.justify-content-end {\n justify-content: flex-end;\n}\n","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"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"br-modal.entry.js","sources":["../../packages/webcomponents/src/components/modal/modal.scss?tag=br-modal&encapsulation=shadow","../../packages/webcomponents/src/components/modal/modal.tsx"],"sourcesContent":["@import \"~@govbr-ds/core/src/components/modal/modal\";\n\n.br-modal.scrollable {\n max-height: 400px;\n}\n\n.br-modal-footer.justify-content-end {\n justify-content: flex-end;\n}\n","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"],"names":[],"mappings":"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"}
|
|
@@ -87,6 +87,29 @@ const Scrim = class {
|
|
|
87
87
|
this.previouslyFocusedElement = null;
|
|
88
88
|
this.focusTimeoutId = null;
|
|
89
89
|
this.restoreFocusTimeoutId = null;
|
|
90
|
+
// ======================================================================
|
|
91
|
+
// GERENCIAMENTO DE COMPONENTES FILHOS
|
|
92
|
+
// ======================================================================
|
|
93
|
+
/**
|
|
94
|
+
* Handler para evento brModalOpen emitido por modal filha
|
|
95
|
+
*/
|
|
96
|
+
this.handleModalOpen = (event) => {
|
|
97
|
+
const modalElement = event.target;
|
|
98
|
+
if (!this.isDirectChild(modalElement))
|
|
99
|
+
return;
|
|
100
|
+
if (!this.isOpen) {
|
|
101
|
+
this.open();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Handler para evento brModalClose emitido por modal filha
|
|
106
|
+
*/
|
|
107
|
+
this.handleModalClose = (event) => {
|
|
108
|
+
const modalElement = event.target;
|
|
109
|
+
if (!this.isDirectChild(modalElement))
|
|
110
|
+
return;
|
|
111
|
+
this.close();
|
|
112
|
+
};
|
|
90
113
|
this.handleActivatorClick = () => {
|
|
91
114
|
this.toggle();
|
|
92
115
|
};
|
|
@@ -104,6 +127,10 @@ const Scrim = class {
|
|
|
104
127
|
const reference = (_a = this.scrollOrigin) !== null && _a !== void 0 ? _a : this.lastScrollTop;
|
|
105
128
|
if (Math.abs(currentScroll - reference) > this.scrollThreshold) {
|
|
106
129
|
this.scrollOrigin = null;
|
|
130
|
+
// Não fecha se tiver modal filha com autoClose=false ou não definido
|
|
131
|
+
if (this.hasModalWithAutoClose()) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
107
134
|
this.isOpen = false;
|
|
108
135
|
this.handleOpenChange(false);
|
|
109
136
|
return;
|
|
@@ -139,7 +166,9 @@ const Scrim = class {
|
|
|
139
166
|
}
|
|
140
167
|
if (event.key === 'Escape' && !this.disableCloseOnClick) {
|
|
141
168
|
event.preventDefault();
|
|
142
|
-
this.
|
|
169
|
+
if (!this.hasModalWithAutoClose()) {
|
|
170
|
+
this.isOpen = false;
|
|
171
|
+
}
|
|
143
172
|
}
|
|
144
173
|
};
|
|
145
174
|
// Intercepta TODOS os eventos de clique globalmente quando scrim está ativo
|
|
@@ -173,11 +202,15 @@ const Scrim = class {
|
|
|
173
202
|
}, 0);
|
|
174
203
|
}
|
|
175
204
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
205
|
+
/**
|
|
206
|
+
* Handler para clique na máscara do scrim
|
|
207
|
+
* Fecha o scrim se disableCloseOnClick for false e não houver modal filha com autoClose=false
|
|
208
|
+
*/
|
|
179
209
|
this.handleMaskClick = (_event) => {
|
|
180
210
|
if (!this.disableCloseOnClick) {
|
|
211
|
+
if (this.hasModalWithAutoClose()) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
181
214
|
this.close();
|
|
182
215
|
}
|
|
183
216
|
};
|
|
@@ -186,37 +219,39 @@ const Scrim = class {
|
|
|
186
219
|
// LIFECYCLE HOOKS E WATCHERS
|
|
187
220
|
// ======================================================================
|
|
188
221
|
handleOpenChange(newValue) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
this.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
222
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
223
|
+
// Cancelar qualquer timeout anterior
|
|
224
|
+
if (this.focusTimeoutId !== null) {
|
|
225
|
+
clearTimeout(this.focusTimeoutId);
|
|
226
|
+
this.focusTimeoutId = null;
|
|
227
|
+
}
|
|
228
|
+
if (this.restoreFocusTimeoutId !== null) {
|
|
229
|
+
clearTimeout(this.restoreFocusTimeoutId);
|
|
230
|
+
this.restoreFocusTimeoutId = null;
|
|
231
|
+
}
|
|
232
|
+
this.isActive = newValue;
|
|
233
|
+
if (newValue && this.scrollStrategy === 'close') {
|
|
234
|
+
const currentScroll = window.scrollY;
|
|
235
|
+
this.lastScrollTop = currentScroll;
|
|
236
|
+
this.scrollOrigin = currentScroll;
|
|
237
|
+
}
|
|
238
|
+
this.updateStyles();
|
|
239
|
+
if (this.isActive) {
|
|
240
|
+
this.previouslyFocusedElement = document.activeElement;
|
|
241
|
+
this.applyScrollStrategy();
|
|
242
|
+
this.addGlobalEventInterception();
|
|
243
|
+
this.focusTimeoutId = setTimeout(() => {
|
|
244
|
+
this.focusFirstElement();
|
|
245
|
+
}, 100);
|
|
246
|
+
this.brScrimOpen.emit();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
this.resetScrolling();
|
|
250
|
+
this.removeGlobalEventInterception();
|
|
251
|
+
this.restoreFocus();
|
|
252
|
+
this.brScrimClose.emit();
|
|
253
|
+
}
|
|
254
|
+
});
|
|
220
255
|
}
|
|
221
256
|
updateStyles() {
|
|
222
257
|
if (this.zIndex !== null && this.zIndex !== undefined) {
|
|
@@ -243,6 +278,7 @@ const Scrim = class {
|
|
|
243
278
|
this.setupActivator();
|
|
244
279
|
this.setupActivatorSlotClick();
|
|
245
280
|
this.addContentEventListener();
|
|
281
|
+
this.listenToModalEvents();
|
|
246
282
|
if (this.isActive) {
|
|
247
283
|
this.applyScrollStrategy();
|
|
248
284
|
this.addGlobalEventInterception();
|
|
@@ -276,7 +312,6 @@ const Scrim = class {
|
|
|
276
312
|
clearTimeout(this.restoreFocusTimeoutId);
|
|
277
313
|
this.restoreFocusTimeoutId = null;
|
|
278
314
|
}
|
|
279
|
-
// Remover event listeners
|
|
280
315
|
window.removeEventListener('scroll', this.handleScrollClose);
|
|
281
316
|
window.removeEventListener('scroll', this.handleBlockScroll);
|
|
282
317
|
if (this.contentRef !== null) {
|
|
@@ -285,6 +320,58 @@ const Scrim = class {
|
|
|
285
320
|
this.removeActivatorClickListener();
|
|
286
321
|
this.cleanupActivatorSlotClick();
|
|
287
322
|
this.removeGlobalEventInterception();
|
|
323
|
+
this.removeModalEventListener();
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Verifica se um elemento é filho direto deste scrim (não nested)
|
|
327
|
+
*/
|
|
328
|
+
isDirectChild(element) {
|
|
329
|
+
const children = this.getDirectChildren();
|
|
330
|
+
return children.some((child) => child === element || child.contains(element));
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Adiciona listeners para eventos brModalOpen e brModalClose das modais filhas
|
|
334
|
+
*/
|
|
335
|
+
listenToModalEvents() {
|
|
336
|
+
this.el.addEventListener('brModalOpen', this.handleModalOpen);
|
|
337
|
+
this.el.addEventListener('brModalClose', this.handleModalClose);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Remove listeners dos eventos da modal
|
|
341
|
+
*/
|
|
342
|
+
removeModalEventListener() {
|
|
343
|
+
this.el.removeEventListener('brModalOpen', this.handleModalOpen);
|
|
344
|
+
this.el.removeEventListener('brModalClose', this.handleModalClose);
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Retorna os elementos filhos diretos do scrim via slot padrão
|
|
348
|
+
*/
|
|
349
|
+
getDirectChildren() {
|
|
350
|
+
const shadowRoot = this.el.shadowRoot;
|
|
351
|
+
if (shadowRoot === null)
|
|
352
|
+
return [];
|
|
353
|
+
const slot = shadowRoot.querySelector('slot:not([name])');
|
|
354
|
+
if (slot === null)
|
|
355
|
+
return [];
|
|
356
|
+
return slot.assignedElements();
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Busca uma modal filha direta do scrim
|
|
360
|
+
*/
|
|
361
|
+
findModalChild() {
|
|
362
|
+
var _a;
|
|
363
|
+
const children = this.getDirectChildren();
|
|
364
|
+
const modalElement = children.find((child) => child.tagName === 'BR-MODAL');
|
|
365
|
+
return (_a = modalElement) !== null && _a !== void 0 ? _a : null;
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Verifica se há uma modal filha com autoClose=false (padrão ou explícito)
|
|
369
|
+
* Retorna true se:
|
|
370
|
+
* - Existe modal filha E autoClose não é explicitamente true
|
|
371
|
+
*/
|
|
372
|
+
hasModalWithAutoClose() {
|
|
373
|
+
const modal = this.findModalChild();
|
|
374
|
+
return modal !== null && modal.autoClose !== true;
|
|
288
375
|
}
|
|
289
376
|
// ======================================================================
|
|
290
377
|
// GERENCIAMENTO DO ACTIVATOR (ELEMENTO ACIONADOR)
|
|
@@ -473,7 +560,11 @@ const Scrim = class {
|
|
|
473
560
|
return elements.filter((el) => el.offsetWidth > 0 && el.offsetHeight > 0 && getComputedStyle(el).visibility !== 'hidden');
|
|
474
561
|
}
|
|
475
562
|
shouldHandleEscape(event) {
|
|
476
|
-
|
|
563
|
+
if ((event.key === 'Escape' || event.key === 'Esc') && this.isActive && !this.disableCloseOnClick) {
|
|
564
|
+
// Não fecha se tiver modal filha com autoClose=false ou não definido
|
|
565
|
+
return !this.hasModalWithAutoClose();
|
|
566
|
+
}
|
|
567
|
+
return false;
|
|
477
568
|
}
|
|
478
569
|
handleTabNavigation(event) {
|
|
479
570
|
const focusableElements = this.getFocusableElements();
|
|
@@ -534,6 +625,9 @@ const Scrim = class {
|
|
|
534
625
|
document.removeEventListener('focus', this.handleGlobalFocus, true);
|
|
535
626
|
document.removeEventListener('focusin', this.handleGlobalFocus, true);
|
|
536
627
|
}
|
|
628
|
+
// ======================================================================
|
|
629
|
+
// UTILITÁRIOS
|
|
630
|
+
// ======================================================================
|
|
537
631
|
getCssClassMap() {
|
|
538
632
|
return {
|
|
539
633
|
'br-scrim': true,
|
|
@@ -584,7 +678,7 @@ const Scrim = class {
|
|
|
584
678
|
});
|
|
585
679
|
}
|
|
586
680
|
render() {
|
|
587
|
-
return (h(Host, { key: '
|
|
681
|
+
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' })))));
|
|
588
682
|
}
|
|
589
683
|
static get formAssociated() { return true; }
|
|
590
684
|
get el() { return getElement(this); }
|