%css .Modal { z-index: 9; } .Modal-bg { position: absolute; position: static; background-color: rgba(0, 0, 0, .6); } .Modal-content { position: absolute; left: 0; right: 0; margin: 0 auto; top: 4%; width: 600px; background-color: #fff; box-shadow: 0 2px 10px 2px rgba(255,255,255,.5); } .sm .Modal-content { width: 94%; } .Modal--blur { filter: blur(5px); transform: scale(.85); transform-origin: 50% 100vh; } // reverse animation: x1, y1, x2, y2 -> (1 - x2), (1 - y2), (1 - x1), (1 - y1) // https://developer.mozilla.org/en-US/docs/Web/API/Notification // https://developers.google.com/web/fundamentals/push-notifications/display-a-notification // var n = new Notification(title, options); // { // "//": "Visual Options", // "body": "Did you make a $1,000,000 purchase at Dr. Evil...", // "icon": "images/ccard.png", // 192px or more is a safe bet // "image": "", // width 1350px or more, ratio of 4:3 for desktop and Android will crop the image // "badge": "", // 72px or more should be good // "vibrate": "", // "sound": "", // "dir": "", // "//": "Behavioural Options", // "tag": "", // group messages so that any old notifications that are currently displayed will be closed if they have the same tag as a new notification. // "data": "", // "requireInteraction": "", // for Chrome on desktop // "renotify": "", // "silent": "", // "//": "Both Visual & Behavioural Options", // "actions": "", // "//": "Information Option. No visual affect.", // "timestamp": "" // ms // } // Star Wars shamelessly taken from the awesome Peter Beverloo // https://tests.peter.sh/notification-generator/ // "vibrate": [500,110,500,110,450,110,200,110,170,40,450,110,200,110,170,40,500] // "actions": [ // { "action": "yes", "title": "Yes", "icon": "images/yes.png" }, // { "action": "no", "title": "No", "icon": "images/no.png" } // ] %js $ui.on("modal", function(title, opts, next) { El.blur() if (!next && typeof opts === "function") { next = opts opts = null } var sound, vibrate , code = "" , el = El("Modal") , scope = Object.assign(El.scope(el, el), opts) , kbMap = {} , body = document.body , blurEl = $(scope.blur) || $ui.root.lastChild scope.title = title || "Confirm?" if (!scope.actions) scope.actions = [ { action: "close", title: "Close", key: "esc" } ] for (var a, i = 0; a = scope.actions[i++]; ) { if (typeof a == "string") a = scope.actions[i-1] = {title:a,action:a} if (a.key) kbMap[a.key] = resolve.bind(el, el, a.action) } El.cls(blurEl, "Modal--blur") El.cls(el.lastChild, "Modal--blur", false, 1) El.append(body, el) El.render(el, scope) if (scope.code) { $$(".js-numpad", el).on("click", numpad) kbMap.backspace = kbMap.del = kbMap.num = numpad } El.addKb(kbMap, el) $$(".js-btn", el).on("click", resolve) $ui.one("show", resolve) if (scope.bgClose) El.on(el, "click", resolve) El.on(el, "wheel", Event.stop) El.on(el.lastChild, "click", Event.stop) if (scope.vibrate && navigator.vibrate) { vibrate = navigator.vibrate(scope.vibrate) } if (scope.sound && window.Audio) { sound = new Audio(scope.sound) sound.play() } function numpad(e, _num) { // Enter pressed on focused element if (_num == void 0 && e.clientX == 0) return var num = _num == void 0 ? e.target[El.T] : _num code += num if (num == "CLEAR" || num == "del" || num == "backspace") code = "" El.md($(".js-body", el), code.replace(/./g, "•") || opts.body) if (typeof scope.code == "number" && code.length == scope.code && id && !sent) next(sent = code, id, resolve, reject) } function resolve(e, key) { if (el) { var action = key || El.get(this, "data-action") , result = { code: code, input: El.val($(".js-input", el)), inputMd: El.val($(".js-inputMd", el)), select: El.val($(".js-select", el)) } El.kill(el, "transparent") El.cls(blurEl, "Modal--blur", el = false) if (action && next) { if (typeof next === "function") next(action, result) else if (typeof next[action] === "function") next[action](result) else if (next[action]) $ui.emit(next[action], result) } if (vibrate) navigator.vibrate(0) if (sound) sound.pause() } } scope.resolve = resolve $ui.emit("modal:open", scope) }) %el Dialog-numpad .row.js-numpad @click ".btn", function(){} ;each: num in [1,2,3,4,5,6,7,8,9,"CLEAR",0] .col.w4>.btn {num} %js $ui.on("showDialog", function(e, el, dialog) { console.log("showDialog", El.scope(el), arguments) El.kill(dialog) }) %el Dialog .Dialog.grid.p2.anim Dialog .col.ts3 ;txt!_(title, map) .col.js-body ;md!_(body, map) .col .group ;each: action in actions .btn.js-btn ;txt!_(action.title) ;class!"w" + (12/actions.length) ;nop: this.focus() ;set: "data-action", action.action ;class!"is-" + action.action, action.action %el Modal .Modal.max.fix.anim .Modal-bg.max.abs .Modal-content.Modal--blur.grid.p2.anim .row ;if: input .col>input.field.js-input .row ;if: $d.inputMd!=null .col textarea.field.js-inputMd @keyup [this.parentNode.nextSibling.nextSibling], "renderMd" ;val: inputMd .col.ts3 Preview .p4 ;md: inputMd .row ;if: select .col select.field.js-select ;list: select, [""] option ;val:: item.id ;txt:: _(item.name)