166 lines
3.8 KiB
JavaScript
166 lines
3.8 KiB
JavaScript
// saladui/components/dialog.js
|
|
import Component from "../core/component";
|
|
import SaladUI from "../index";
|
|
import FocusTrap from "../core/focus-trap";
|
|
import ClickOutsideMonitor from "../core/click-outside";
|
|
|
|
class DialogComponent extends Component {
|
|
constructor(el, hookContext) {
|
|
super(el, { hookContext, initialState: "closed" });
|
|
|
|
// Initialize properties
|
|
this.root = this.el;
|
|
this.content = this.getPart("content");
|
|
this.contentPanel = this.getPart("content-panel");
|
|
this.config.preventDefaultKeys = ["Escape"];
|
|
|
|
this.setupEvents();
|
|
this.transition(this.el.dataset.open == "true" ? "open" : "close");
|
|
}
|
|
|
|
getComponentConfig() {
|
|
return {
|
|
stateMachine: {
|
|
closed: {
|
|
enter: "onClosedEnter",
|
|
exit: "onClosedExit",
|
|
transitions: {
|
|
open: "open",
|
|
},
|
|
},
|
|
open: {
|
|
enter: "onOpenEnter",
|
|
transitions: {
|
|
close: "closed",
|
|
},
|
|
},
|
|
},
|
|
events: {
|
|
closed: {
|
|
keyMap: {},
|
|
},
|
|
open: {
|
|
keyMap: {
|
|
Escape: "close",
|
|
},
|
|
},
|
|
},
|
|
hiddenConfig: {
|
|
closed: {
|
|
content: true,
|
|
},
|
|
open: {
|
|
content: false,
|
|
},
|
|
},
|
|
ariaConfig: {
|
|
content: {
|
|
all: {
|
|
role: "dialog",
|
|
},
|
|
open: {
|
|
hidden: "false",
|
|
modal: "true",
|
|
},
|
|
closed: {
|
|
hidden: "true",
|
|
},
|
|
},
|
|
"content-panel": {
|
|
open: {
|
|
labelledby: () => this.getPartId("title"),
|
|
describedby: () => this.getPartId("description"),
|
|
},
|
|
},
|
|
"close-trigger": {
|
|
all: {
|
|
label: "Close dialog",
|
|
},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
// Setup component events
|
|
setupComponentEvents() {
|
|
super.setupComponentEvents();
|
|
|
|
// Only setup click handler if closeOnOutsideClick is enabled
|
|
if (this.options.closeOnOutsideClick) {
|
|
this.setupOutsideClickDetection();
|
|
}
|
|
}
|
|
|
|
setupOutsideClickDetection() {
|
|
// Create click outside monitor to handle clicks on the overlay
|
|
this.clickOutsideMonitor = new ClickOutsideMonitor(
|
|
[this.contentPanel],
|
|
(event) => {
|
|
// Only close if click was directly on the content container (overlay area)
|
|
if (
|
|
event.target === this.content ||
|
|
event.target.dataset.part === "overlay"
|
|
) {
|
|
this.transition("close");
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
// State machine handlers
|
|
onClosedEnter() {
|
|
// Clean up focus trap
|
|
if (this.focusTrap) {
|
|
this.focusTrap.deactivate();
|
|
}
|
|
|
|
// Clean up click outside monitor
|
|
if (this.clickOutsideMonitor) {
|
|
this.clickOutsideMonitor.stop();
|
|
}
|
|
|
|
// Notify the server of the state change
|
|
this.pushEvent("closed");
|
|
}
|
|
|
|
onClosedExit() {
|
|
// No special handling needed
|
|
}
|
|
|
|
onOpenEnter() {
|
|
// Initialize focus trap if not already created
|
|
this.el.focus();
|
|
if (!this.focusTrap) {
|
|
this.focusTrap = new FocusTrap(this.contentPanel);
|
|
}
|
|
|
|
// Activate focus trap
|
|
this.focusTrap.activate();
|
|
|
|
// Activate click outside monitor if enabled
|
|
if (this.clickOutsideMonitor) {
|
|
this.clickOutsideMonitor.start();
|
|
}
|
|
|
|
// Setup escape key handling - now handled by the component's keyMap
|
|
|
|
// Notify the server of the state change
|
|
this.pushEvent("opened");
|
|
}
|
|
|
|
beforeDestroy() {
|
|
// Clean up focus trap
|
|
this.focusTrap?.destroy();
|
|
this.focusTrap = null;
|
|
|
|
// Clean up click outside monitor
|
|
this.clickOutsideMonitor?.destroy();
|
|
this.clickOutsideMonitor = null;
|
|
}
|
|
}
|
|
|
|
// Register the component
|
|
SaladUI.register("dialog", DialogComponent);
|
|
|
|
export default DialogComponent;
|