mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-30 13:29:56 +00:00
ca6dd93214
* Add support for a custom class to modal wrapper As per https://github.com/Jermolene/TiddlyWiki5/issues/4485 add support for a custom class to modal wrapper, by means of a field in the modal tiddler. The class is added to the modal wrapper in addition to the default class, allowing for custom styling via css of any part of the modal. * Remove redundant check for tiddler.
219 lines
6.9 KiB
JavaScript
219 lines
6.9 KiB
JavaScript
/*\
|
|
title: $:/core/modules/utils/dom/modal.js
|
|
type: application/javascript
|
|
module-type: utils
|
|
|
|
Modal message mechanism
|
|
|
|
\*/
|
|
(function(){
|
|
|
|
/*jslint node: true, browser: true */
|
|
/*global $tw: false */
|
|
"use strict";
|
|
|
|
var widget = require("$:/core/modules/widgets/widget.js");
|
|
|
|
var Modal = function(wiki) {
|
|
this.wiki = wiki;
|
|
this.modalCount = 0;
|
|
};
|
|
|
|
/*
|
|
Display a modal dialogue
|
|
title: Title of tiddler to display
|
|
options: see below
|
|
Options include:
|
|
downloadLink: Text of a big download link to include
|
|
*/
|
|
Modal.prototype.display = function(title,options) {
|
|
options = options || {};
|
|
this.srcDocument = options.variables && (options.variables.rootwindow === "true" ||
|
|
options.variables.rootwindow === "yes") ? document :
|
|
(options.event.event && options.event.event.target ? options.event.event.target.ownerDocument : document);
|
|
this.srcWindow = this.srcDocument.defaultView;
|
|
var self = this,
|
|
refreshHandler,
|
|
duration = $tw.utils.getAnimationDuration(),
|
|
tiddler = this.wiki.getTiddler(title);
|
|
// Don't do anything if the tiddler doesn't exist
|
|
if(!tiddler) {
|
|
return;
|
|
}
|
|
// Create the variables
|
|
var variables = $tw.utils.extend({currentTiddler: title},options.variables);
|
|
// Create the wrapper divs
|
|
var wrapper = this.srcDocument.createElement("div"),
|
|
modalBackdrop = this.srcDocument.createElement("div"),
|
|
modalWrapper = this.srcDocument.createElement("div"),
|
|
modalHeader = this.srcDocument.createElement("div"),
|
|
headerTitle = this.srcDocument.createElement("h3"),
|
|
modalBody = this.srcDocument.createElement("div"),
|
|
modalLink = this.srcDocument.createElement("a"),
|
|
modalFooter = this.srcDocument.createElement("div"),
|
|
modalFooterHelp = this.srcDocument.createElement("span"),
|
|
modalFooterButtons = this.srcDocument.createElement("span");
|
|
// Up the modal count and adjust the body class
|
|
this.modalCount++;
|
|
this.adjustPageClass();
|
|
// Add classes
|
|
$tw.utils.addClass(wrapper,"tc-modal-wrapper");
|
|
if(tiddler.fields && tiddler.fields.class) {
|
|
$tw.utils.addClass(wrapper,tiddler.fields.class);
|
|
}
|
|
$tw.utils.addClass(modalBackdrop,"tc-modal-backdrop");
|
|
$tw.utils.addClass(modalWrapper,"tc-modal");
|
|
$tw.utils.addClass(modalHeader,"tc-modal-header");
|
|
$tw.utils.addClass(modalBody,"tc-modal-body");
|
|
$tw.utils.addClass(modalFooter,"tc-modal-footer");
|
|
// Join them together
|
|
wrapper.appendChild(modalBackdrop);
|
|
wrapper.appendChild(modalWrapper);
|
|
modalHeader.appendChild(headerTitle);
|
|
modalWrapper.appendChild(modalHeader);
|
|
modalWrapper.appendChild(modalBody);
|
|
modalFooter.appendChild(modalFooterHelp);
|
|
modalFooter.appendChild(modalFooterButtons);
|
|
modalWrapper.appendChild(modalFooter);
|
|
// Render the title of the message
|
|
var headerWidgetNode = this.wiki.makeTranscludeWidget(title,{
|
|
field: "subtitle",
|
|
mode: "inline",
|
|
children: [{
|
|
type: "text",
|
|
attributes: {
|
|
text: {
|
|
type: "string",
|
|
value: title
|
|
}}}],
|
|
parentWidget: $tw.rootWidget,
|
|
document: this.srcDocument,
|
|
variables: variables,
|
|
importPageMacros: true
|
|
});
|
|
headerWidgetNode.render(headerTitle,null);
|
|
// Render the body of the message
|
|
var bodyWidgetNode = this.wiki.makeTranscludeWidget(title,{
|
|
parentWidget: $tw.rootWidget,
|
|
document: this.srcDocument,
|
|
variables: variables,
|
|
importPageMacros: true
|
|
});
|
|
bodyWidgetNode.render(modalBody,null);
|
|
// Setup the link if present
|
|
if(options.downloadLink) {
|
|
modalLink.href = options.downloadLink;
|
|
modalLink.appendChild(this.srcDocument.createTextNode("Right-click to save changes"));
|
|
modalBody.appendChild(modalLink);
|
|
}
|
|
// Render the footer of the message
|
|
if(tiddler.fields && tiddler.fields.help) {
|
|
var link = this.srcDocument.createElement("a");
|
|
link.setAttribute("href",tiddler.fields.help);
|
|
link.setAttribute("target","_blank");
|
|
link.setAttribute("rel","noopener noreferrer");
|
|
link.appendChild(this.srcDocument.createTextNode("Help"));
|
|
modalFooterHelp.appendChild(link);
|
|
modalFooterHelp.style.float = "left";
|
|
}
|
|
var footerWidgetNode = this.wiki.makeTranscludeWidget(title,{
|
|
field: "footer",
|
|
mode: "inline",
|
|
children: [{
|
|
type: "button",
|
|
attributes: {
|
|
message: {
|
|
type: "string",
|
|
value: "tm-close-tiddler"
|
|
}
|
|
},
|
|
children: [{
|
|
type: "text",
|
|
attributes: {
|
|
text: {
|
|
type: "string",
|
|
value: $tw.language.getString("Buttons/Close/Caption")
|
|
}}}
|
|
]}],
|
|
parentWidget: $tw.rootWidget,
|
|
document: this.srcDocument,
|
|
variables: variables,
|
|
importPageMacros: true
|
|
});
|
|
footerWidgetNode.render(modalFooterButtons,null);
|
|
// Set up the refresh handler
|
|
refreshHandler = function(changes) {
|
|
headerWidgetNode.refresh(changes,modalHeader,null);
|
|
bodyWidgetNode.refresh(changes,modalBody,null);
|
|
footerWidgetNode.refresh(changes,modalFooterButtons,null);
|
|
};
|
|
this.wiki.addEventListener("change",refreshHandler);
|
|
// Add the close event handler
|
|
var closeHandler = function(event) {
|
|
// Remove our refresh handler
|
|
self.wiki.removeEventListener("change",refreshHandler);
|
|
// Decrease the modal count and adjust the body class
|
|
self.modalCount--;
|
|
self.adjustPageClass();
|
|
// Force layout and animate the modal message away
|
|
$tw.utils.forceLayout(modalBackdrop);
|
|
$tw.utils.forceLayout(modalWrapper);
|
|
$tw.utils.setStyle(modalBackdrop,[
|
|
{opacity: "0"}
|
|
]);
|
|
$tw.utils.setStyle(modalWrapper,[
|
|
{transform: "translateY(" + self.srcWindow.innerHeight + "px)"}
|
|
]);
|
|
// Set up an event for the transition end
|
|
self.srcWindow.setTimeout(function() {
|
|
if(wrapper.parentNode) {
|
|
// Remove the modal message from the DOM
|
|
self.srcDocument.body.removeChild(wrapper);
|
|
}
|
|
},duration);
|
|
// Don't let anyone else handle the tm-close-tiddler message
|
|
return false;
|
|
};
|
|
headerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
|
bodyWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
|
footerWidgetNode.addEventListener("tm-close-tiddler",closeHandler,false);
|
|
// Set the initial styles for the message
|
|
$tw.utils.setStyle(modalBackdrop,[
|
|
{opacity: "0"}
|
|
]);
|
|
$tw.utils.setStyle(modalWrapper,[
|
|
{transformOrigin: "0% 0%"},
|
|
{transform: "translateY(" + (-this.srcWindow.innerHeight) + "px)"}
|
|
]);
|
|
// Put the message into the document
|
|
this.srcDocument.body.appendChild(wrapper);
|
|
// Set up animation for the styles
|
|
$tw.utils.setStyle(modalBackdrop,[
|
|
{transition: "opacity " + duration + "ms ease-out"}
|
|
]);
|
|
$tw.utils.setStyle(modalWrapper,[
|
|
{transition: $tw.utils.roundTripPropertyName("transform") + " " + duration + "ms ease-in-out"}
|
|
]);
|
|
// Force layout
|
|
$tw.utils.forceLayout(modalBackdrop);
|
|
$tw.utils.forceLayout(modalWrapper);
|
|
// Set final animated styles
|
|
$tw.utils.setStyle(modalBackdrop,[
|
|
{opacity: "0.7"}
|
|
]);
|
|
$tw.utils.setStyle(modalWrapper,[
|
|
{transform: "translateY(0px)"}
|
|
]);
|
|
};
|
|
|
|
Modal.prototype.adjustPageClass = function() {
|
|
var windowContainer = $tw.pageContainer ? ($tw.pageContainer === this.srcDocument.body.firstChild ? $tw.pageContainer : this.srcDocument.body.firstChild) : null;
|
|
if(windowContainer) {
|
|
$tw.utils.toggleClass(windowContainer,"tc-modal-displayed",this.modalCount > 0);
|
|
}
|
|
};
|
|
|
|
exports.Modal = Modal;
|
|
|
|
})();
|