diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js index e1c837cb09e..0065584cec5 100644 --- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js +++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js @@ -1,11 +1,13 @@ /* global Flash */ +import Spinner from '../../spinner'; import sqljs from 'sql.js'; class BalsamiqViewer { constructor(viewer) { this.viewer = viewer; this.endpoint = this.viewer.dataset.endpoint; + this.spinner = new Spinner(this.viewer); } loadFile() { @@ -17,10 +19,14 @@ class BalsamiqViewer { xhr.onload = this.renderFile.bind(this); xhr.onerror = BalsamiqViewer.onError; + this.spinner.start(); + xhr.send(); } renderFile(loadEvent) { + this.spinner.stop(); + const container = document.createElement('ul'); this.initDatabase(loadEvent.target.response); @@ -29,7 +35,7 @@ class BalsamiqViewer { const renderedPreviews = previews.map(preview => this.renderPreview(preview, container)); container.innerHTML = renderedPreviews.join(''); - container.classList.add('list-inline'); + container.classList.add('list-inline', 'previews'); this.viewer.appendChild(container); } @@ -41,14 +47,15 @@ class BalsamiqViewer { } getPreviews() { - const thumnails = this.database.exec('SELECT * FROM thumbnails'); + const thumbnails = this.database.exec('SELECT * FROM thumbnails'); - return thumnails[0].values.map(BalsamiqViewer.parsePreview); + return thumbnails[0].values.map(BalsamiqViewer.parsePreview); } renderPreview(preview) { const previewElement = document.createElement('li'); + previewElement.classList.add('preview'); previewElement.innerHTML = this.renderTemplate(preview); return previewElement.outerHTML; diff --git a/app/assets/javascripts/spinner.js b/app/assets/javascripts/spinner.js new file mode 100644 index 00000000000..b7bfe1a2572 --- /dev/null +++ b/app/assets/javascripts/spinner.js @@ -0,0 +1,28 @@ +class Spinner { + constructor(renderable) { + this.renderable = renderable; + + this.container = Spinner.createContainer(); + } + + start() { + this.renderable.prepend(this.container); + } + + stop() { + this.container.remove(); + } + + static createContainer() { + const container = document.createElement('div'); + container.classList.add('loading'); + + container.innerHTML = Spinner.TEMPLATE; + + return container; + } +} + +Spinner.TEMPLATE = ''; + +export default Spinner; diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index a5a8522739e..b8dab538fee 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -168,6 +168,17 @@ &.code { padding: 0; } + + .list-inline.previews { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + padding: $gl-padding; + + .preview { + flex-shrink: 0; + } + } } }