window div(class="wrapper {focused: focused, dragged: dragged}") .title .title-inner { windowTitle } .close a(href="#") X .outer .inner-wrapper .inner view-manager(router="{opts.router}", view-prefix="openng-view") .resizer(show="{opts.resizable}") script. const $ = require("jquery"); const debounce = require("debounce"); const query = require("riot-query"); require("../../lib/jquery/draggable")($); this.mixin(query.mixin); this.mixin(require("../../lib/riot/mixins/change")); Object.assign(this, { dragged: false, focused: false, resized: false, width: parseInt(opts.width), height: parseInt(opts.height), windowTitle: opts.windowTitle, x: parseInt(opts.x), y: parseInt(opts.y), z: parseInt(opts.z), focus: () => { this.trigger("focused"); this.change({ focused: true }); }, defocus: () => { this.trigger("defocused"); this.change({ focused: false }); }, setZIndex: (index) => { this.change({ z: index }); }, setSize: (width, height) => { this.change({ width: width, height: height }); }, setWidth: (width) => { this.change({ width: width }); }, setHeight: (height) => { this.change({ height: height }); }, setTitle: (title) => { this.change({ windowTitle: title }); }, reportClose: () => { this.trigger("closing"); }, _handleMouseDown: (event) => { this.focus(); } }); let updatePosition = () => { $(this.queryOne("//.wrapper")).css({ left: `${this.x}px`, top: `${this.y}px`, width: `${this.width}px`, height: `${this.height}px`, zIndex: this.z }); } this.on("updated", () => { updatePosition(); }); this.on("mount", () => { let viewManager = this.queryOne("view-manager"); let resizeHandle = $(this.queryOne("//.resizer")); let titleBar = $(this.queryOne("//.title")); let closeButton = $(this.queryOne("//.close")); if (opts.url != null) { viewManager.get(opts.url); } viewManager.on("switched", () => { query(viewManager.currentView, "window-meta").forEach((windowMeta) => { if (windowMeta.windowTitle != null) { this.setTitle(windowMeta.windowTitle); } if (windowMeta.requestedWidth != null) { this.setWidth(windowMeta.requestedWidth); } if (windowMeta.requestedHeight != null) { this.setHeight(windowMeta.requestedHeight); } }) }); closeButton.on("click", (event) => { event.stopPropagation(); event.preventDefault; this.trigger("requestClose"); }) let startWidth, startHeight; resizeHandle.draggable(); resizeHandle.on("draggable:start", (event) => { startWidth = this.width; startHeight = this.height; this.change({ resized: true }); }); resizeHandle.on("draggable:end", (event) => { this.change({ resized: false }); }); resizeHandle.on("draggable:move", (event, data) => { this.change({ width: startWidth + data.offsetX, height: startHeight + data.offsetY }); }); let startX, startY; titleBar.draggable(); titleBar.on("draggable:start", (event) => { startX = this.x; startY = this.y; this.change({ dragged: true }); }); titleBar.on("draggable:end", (event) => { this.change({ dragged: false }); }); titleBar.on("draggable:move", (event, data) => { this.change({ x: startX + data.offsetX, y: startY + data.offsetY }); }); $(this.queryOne("//.wrapper")).on("mousedown", () => { this._handleMouseDown(); }); updatePosition(); }); style(scoped, type="scss"). .noscroll { overflow-x: hidden !important; overflow-y: hidden !important; } /* Common styling */ .wrapper { position: absolute; } @mixin shadow { -webkit-box-shadow: 5px 5px 10px #1a1a1a; -moz-box-shadow: 5px 5px 10px #1a1a1a; box-shadow: 5px 5px 10px #1a1a1a; } .title { @include shadow; position: absolute; z-index: 2; left: 0px; right: 0px; top: 0px; cursor: default; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; border-top-left-radius: 10px; border-top-right-radius: 10px; height: 16px; color: white; font-size: 14px; font-weight: bold; padding: 4px; padding-left: 7px; border-top: 1px solid #959595; border-right: 1px solid #959595; border-left: 1px solid #959595; background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0, rgb(82,82,82)), color-stop(1, rgb(145,172,190)) ); background-image: -moz-linear-gradient( center bottom, rgb(82,82,82) 0%, rgb(145,172,190) 100% ); filter:alpha(opacity=95); opacity:0.95; } .outer { @include shadow; position: absolute; z-index: 3; left: 0px; right: 0px; bottom: 0px; font-size: 13px; top: 25px; border-bottom: 1px solid gray; border-right: 1px solid gray; border-left: 1px solid gray; background-color: #F7F7F0; filter:alpha(opacity=95); opacity:0.95; } .inner-wrapper { position: absolute; top: 0px; bottom: 0px; left: 0px; right: 0px; overflow-y: auto; overflow-x: auto; } .inner { padding: 7px; } .close { float: right; a { position: absolute; right: 3px; top: 2px; display: block; padding: 1px 4px; text-decoration: none; font-size: 12px; border-radius: 5px; color: white; border: 1px solid #014D8C; &:hover { background-color: #014D8C; border: 1px solid white; } } } .resizer { position: absolute; width: 12px; height: 12px; bottom: -6px; right: -6px; cursor: se-resize; } /* Special states */ .focused { .title { border-top: 1px solid #6262FF; border-right: 1px solid #6262FF; border-left: 1px solid #6262FF; background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0, rgb(0,87,179)), color-stop(1, rgb(0,153,255)) ); background-image: -moz-linear-gradient( center bottom, rgb(0,87,179) 0%, rgb(0,153,255) 100% ); filter:alpha(opacity=85); opacity:0.85; } } @mixin noShadow { -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; } @mixin unselectable { cursor: default; user-select: none; -webkit-user-select: none; /* Chrome/Safari/Opera */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /* IE/Edge */ -webkit-touch-callout: none; /* iOS Safari */ } .dragged { .title { @include noShadow; background-color: #0070D5; background-image: none; } .outer { @include noShadow; background: none; } .inner { visibility: hidden; } } .resized { @include unselectable; }