Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(modal): convert modal logic to modifier #934

Merged
merged 1 commit into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
luytena marked this conversation as resolved.
Show resolved Hide resolved
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;
luytena marked this conversation as resolved.
Show resolved Hide resolved

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

registerDestructor(this, () => {
this.#events.forEach(([event, handler]) => {
UIkit.util.off(this.#modal.$el, event, handler);
luytena marked this conversation as resolved.
Show resolved Hide resolved
});

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
2 changes: 1 addition & 1 deletion tests/dummy/config/optional-features.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"application-template-wrapper": false,
"default-async-observers": true,
"default-async-observers": false,
"jquery-integration": false,
"template-only-glimmer-components": true
}
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