Skip to content

Commit

Permalink
chore(modal): convert modal logic to modifier
Browse files Browse the repository at this point in the history
Moving the modal logic to a modifier has the advantage of not needing
the ember and mutation observers and the dependency on
`@ember/render-modifiers`.
  • Loading branch information
anehx committed Sep 13, 2022
1 parent 1fbff85 commit 8582896
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 101 deletions.
16 changes: 14 additions & 2 deletions addon/components/uk-modal.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@
<div
id={{this.modalId}}
class={{@modalClass}}
{{did-insert this.initialize}}
{{will-destroy this.teardown}}
{{uk-modal
escClose=@escClose
bgClose=@bgClose
stack=@stack
container=this.containerSelector
clsPage=@clsPage
clsPanel=@clsPanel
selClose=@selClose
visible=@visible
onHide=this.hide
onHidden=(fn (mut this.focusTrapActive) false)
onShow=this.show
onShown=(fn (mut this.focusTrapActive) true)
}}
...attributes
>
<div
Expand Down
103 changes: 6 additions & 97 deletions addon/components/uk-modal.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import { getOwner } from "@ember/application";
import { assert } from "@ember/debug";
import { isDestroying } from "@ember/destroyable";
import { action } from "@ember/object";
import { guidFor } from "@ember/object/internals";
// eslint-disable-next-line ember/no-observers
import { addObserver } from "@ember/object/observers";
import { scheduleOnce } from "@ember/runloop";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import UIkit from "uikit";

function isBubbling(event) {
return event.target !== event.currentTarget;
}

export default class UkModal extends Component {
_modal;
_modalObserver;

@tracked focusTrapActive = false;

get btnClose() {
Expand All @@ -32,10 +24,6 @@ export default class UkModal extends Component {
return `${this.modalId}-header`;
}

get modalSelector() {
return `#${this.modalId}`;
}

get containerSelector() {
// Only set the container to the default if no query string was passed as argument.
if (typeof this.args.container !== "string") {
Expand All @@ -58,94 +46,15 @@ export default class UkModal extends Component {
return containerElement;
}

@action
async toggleModal() {
if (this.args.visible) {
await this._modal?.show();
} else {
await this._modal?.hide();
}
}

@action
initialize(element) {
this._modal = UIkit.modal(`#${element.id}`, {
escClose: this.args.escClose ?? true,
bgClose: this.args.bgClose ?? true,
stack: this.args.stack ?? false,
container: this.containerSelector,
clsPage: this.args.clsPage ?? "uk-modal-page",
clsPanel: this.args.clsPanel ?? "uk-modal-dialog",
selClose:
this.args.selClose ??
[
".uk-modal-close",
".uk-modal-close-default",
".uk-modal-close-outside",
".uk-modal-close-full",
].join(","),
});

// eslint-disable-next-line ember/no-observers
addObserver(this.args, "visible", this, "toggleModal");

scheduleOnce("afterRender", this, "toggleModal");
scheduleOnce("afterRender", this, "_registerMutationObserver");
}

@action
teardown() {
this._modalObserver.disconnect();
this._modalObserver = null;

this._modal?.$destroy(true);
this._modal = null;
}

_registerMutationObserver() {
UIkit.util.on(this.modalSelector, "hide", this.hide);
UIkit.util.on(this.modalSelector, "show", this.show);

// Setup a observer so we can activate the focus trap only once
// the modal has stopped animating (otherwise this will cause errors).
// To check if the modal has stopped animating, we can just check for
// the `uk-open` class on the modal. If it extists then its finished.
this._modalObserver = new MutationObserver((mutationList) => {
const mutations = mutationList
.filter(
({ target, attributeName }) =>
target.id === this.modalId && attributeName === "class"
)
.map((mutation) => mutation.target.classList);

// Short-circuit if no mutations match the filter
if (!mutations.length) {
return;
}

this.focusTrapActive = mutations.every((classList) =>
classList.contains("uk-open")
);
});
this._modalObserver.observe(
getOwner(this)
.lookup("service:-document")
.querySelector(this.modalSelector),
{ attributes: true, subtree: true, childList: false }
);
}

@action
async hide(event) {
if (!isBubbling(event) && !isDestroying(this) && this.args.visible) {
await this.args.onHide?.();
@action hide(event) {
if (!isBubbling(event) && this.args.visible) {
this.args.onHide?.();
}
}

@action
async show(event) {
if (!isBubbling(event) && !isDestroying(this) && !this.args.visible) {
await this.args.onShow?.();
@action show(event) {
if (!isBubbling(event) && !this.args.visible) {
this.args.onShow?.();
}
}
}
57 changes: 57 additions & 0 deletions addon/modifiers/uk-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { registerDestructor } from "@ember/destroyable";
import Modifier from "ember-modifier";
import UIkit from "uikit";

export default class UkModalModifier extends Modifier {
#modal;
#events;

constructor(owner, args) {
super(owner, args);

registerDestructor(this, () => {
this.#events.forEach(([event, handler]) => {
UIkit.util.off(this.#modal.$el, event, handler);
});

this.#modal.$destroy();
});
}

modify(element, positional, named) {
if (!this.#modal) {
this.initialize(element, positional, named);
}

if (named.visible) {
this.#modal.show();
} else {
this.#modal.hide();
}
}

initialize(element, positional, named) {
this.#events = [
["hide", named.onHide],
["hidden", named.onHidden],
["show", named.onShow],
["shown", named.onShown],
];

this.#modal = UIkit.modal(element, {
escClose: named.escClose ?? true,
bgClose: named.bgClose ?? true,
stack: named.stack ?? false,
container: named.container,
clsPage: named.clsPage ?? "uk-modal-page",
clsPanel: named.clsPanel ?? "uk-modal-dialog",
selClose:
named.selClose ??
".uk-modal-close,.uk-modal-close-default,.uk-modal-close-outside,.uk-modal-close-full",
});

this.#events.forEach(([event, handler]) => {
UIkit.util.on(this.#modal.$el, event, handler);
});
}
}
1 change: 1 addition & 0 deletions app/modifiers/uk-modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "ember-uikit/modifiers/uk-modal";
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
"prepare": "husky install"
},
"dependencies": {
"@ember/render-modifiers": "^2.0.4",
"@ember/string": "^3.0.0",
"@embroider/util": "^1.8.3",
"@glimmer/component": "^1.1.2",
Expand Down
12 changes: 12 additions & 0 deletions tests/integration/modifiers/uk-modal-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { setupRenderingTest } from "ember-qunit";
import { module, test } from "qunit";

module("Integration | Modifier | uk-modal", function (hooks) {
setupRenderingTest(hooks);

test("it renders", async function (assert) {
// the functionality of the uk-modal modifier is fully tested in the tests
// for the uk-modal component
assert.ok(true);
});
});
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1202,7 +1202,7 @@
mkdirp "^1.0.4"
silent-error "^1.1.1"

"@ember/render-modifiers@^2.0.0", "@ember/render-modifiers@^2.0.3", "@ember/render-modifiers@^2.0.4":
"@ember/render-modifiers@^2.0.0", "@ember/render-modifiers@^2.0.3":
version "2.0.4"
resolved "https://registry.yarnpkg.com/@ember/render-modifiers/-/render-modifiers-2.0.4.tgz#0ac7af647cb736076dbfcd54ca71e090cd329d71"
integrity sha512-Zh/fo5VUmVzYHkHVvzWVjJ1RjFUxA2jH0zCp2+DQa80Bf3DUXauiEByxU22UkN4LFT55DBFttC0xCQSJG3WTsg==
Expand Down

0 comments on commit 8582896

Please sign in to comment.