Today, I switch back to my comic book collection app written in Dart with Hipster MVC. Currently, I have a very simple fade-in animation that displays the create comic book form:
#library('Form view to add new comic book to my sweet collection.');
#import('dart:html');
#import('https://raw.github.com/eee-c/hipster-mvc/master/HipsterView.dart');
class AddComicForm extends HipsterView {
// ...
render() {
el.style.opacity = '0';
el.innerHTML = template();
window.setTimeout(() {
el.style.transition = '1s';
el.style.opacity = '1';
}, 1);
}
}There is nothing fancy there—the form element is simply placed in the normal document flow. The 1 millisecond timeout seems to be necessary to give Dart a chance to render the element with the form from the template() function. Without it, the transition is ignored.The first step for a modal dialog is the black, semi-opaque background that covers the rest of the document, ensuring that nothing else can be clicked. For that, I create a new
<div> tag, position it absolutely, guess at the appropriate size, make the background color black with a slight opacity, and render it above the rest of the document. All of this can be done with the style property on Element: render() {
// Black background
Element bg = new Element.tag('div');
document.body.nodes.add(bg);
bg.style.position = 'absolute';
bg.style.top = '0px';
bg.style.left = '0px';
bg.style.width = "1000px";
bg.style.height = "1000px";
bg.style.backgroundColor = 'black';
bg.style.opacity = '0.8';
bg.style.zIndex = '1000';
// Display the dialog
// ...
}The problem is the guess at the dimensions. Not surprisingly they only cover a portion of the document:I could make the dimensions larger, but that will only end up adding scrollbars when they are not warranted. This means that I have to calculate dimensions. Interestingly, Dart does this inside a future (to prevent blocking during a sometimes expensive calculation). So I move the background styling into the
rect property's future so that I can use the document's width and height for the modal dialog's background: render() {
// Black background
Element bg = new Element.tag('div');
document.body.nodes.add(bg);
window.document.rect.then((document) {
bg.style.position = 'absolute';
bg.style.top = '0px';
bg.style.left = '0px';
bg.style.width = "${document.offset.width}px";
bg.style.height = "${document.client.height}px";
bg.style.backgroundColor = 'black';
bg.style.opacity = '0.8';
bg.style.zIndex = '1000';
// Display the dialog
// ...
});
}With that, I am ready to place my dialog: render() {
// Black background
Element bg = new Element.tag('div');
document.body.nodes.add(bg);
window.document.rect.then((document) {
// Place the background above the document
// ...
// Place the modal dialog on top of the background
el.style.opacity = '0';
el.innerHTML = template();
el.style.position = 'absolute';
el.style.top = '0px';
el.style.left = '0px';
el.style.backgroundColor = 'white';
el.style.padding = '20px';
el.style.borderRadius = '10px';
el.style.transition = 'opacity 1s ease-in-out';
el.style.opacity = '1';
el.style.zIndex = '1001';
});
}That works, but the dialog is somewhat poorly placed:To fix that, I need another
rect future. This time, I grab the rect for the form dialog element so that I can use that to calculate the distance from the left side of the page: render() {
// Black background
Element bg = new Element.tag('div');
document.body.nodes.add(bg);
window.document.rect.then((document) {
// Place the background above the document
// ...
// Place the modal dialog on top of the background
// ...
el.rect.then((dialog) {
el.style.top = '10px';
int offset_left = document.offset.width/2 - dialog.offset.width/2;
el.style.left = "${offset_left}px";
});
});
}And that seems to do the trick:I am undecided on how much I like the
rect property. It is a little awkward, but I do appreciate the performance consideration. As for the rest, this was pretty easy to build. Best of all, I can rely on Dart to generate compatible Javascript and not have to worry (much) about IE, Mozilla, and Safari. I do think that a separate class is needed to do a modal right. Right now, I have to manually remove both the background and dialog. Something for another day.Day #328



No comments:
Post a Comment