diff --git a/packages/markdown-preview/lib/markdown-preview-view.js b/packages/markdown-preview/lib/markdown-preview-view.js index 34e964f16..78a222efc 100644 --- a/packages/markdown-preview/lib/markdown-preview-view.js +++ b/packages/markdown-preview/lib/markdown-preview-view.js @@ -274,7 +274,22 @@ module.exports = class MarkdownPreviewView { return this.getMarkdownSource() .then(source => { if (source != null) { - return this.renderMarkdownText(source) + if (this.loaded) { + return this.renderMarkdownText(source); + } else { + // If we haven't loaded yet, defer before we render the Markdown + // for the first time. This allows the pane to appear and to + // display the loading indicator. Otherwise the first render + // happens before the pane is even visible. + // + // This doesn't slow anything down; it just shifts the work around + // so that the pane appears earlier in the cycle. + return new Promise((resolve) => { + setTimeout(() => { + resolve(this.renderMarkdownText(source)) + }, 0) + }) + } } }) .catch(reason => this.showError({ message: reason })) @@ -339,6 +354,7 @@ module.exports = class MarkdownPreviewView { }) await done(this.element) + this.element.classList.remove('loading') this.emitter.emit('did-change-markdown') this.element.scrollTop = scrollTop @@ -444,11 +460,7 @@ module.exports = class MarkdownPreviewView { showLoading() { this.loading = true - this.element.textContent = '' - const div = document.createElement('div') - div.classList.add('markdown-spinner') - div.textContent = 'Loading Markdown\u2026' - this.element.appendChild(div) + this.element.classList.add('loading') } selectAll() { diff --git a/packages/markdown-preview/spec/markdown-preview-spec.js b/packages/markdown-preview/spec/markdown-preview-spec.js index 8ea5bed7c..64627bcf3 100644 --- a/packages/markdown-preview/spec/markdown-preview-spec.js +++ b/packages/markdown-preview/spec/markdown-preview-spec.js @@ -41,6 +41,13 @@ describe('Markdown Preview', function () { .getActiveItem()) ) + waitsFor( + 'preview to finish loading', + () => { + return !preview.element.classList.contains('loading') + } + ) + runs(() => { expect(preview).toBeInstanceOf(MarkdownPreviewView) expect(preview.getPath()).toBe( diff --git a/packages/markdown-preview/styles/markdown-preview.less b/packages/markdown-preview/styles/markdown-preview.less index 0853c74ef..8e44e81b6 100644 --- a/packages/markdown-preview/styles/markdown-preview.less +++ b/packages/markdown-preview/styles/markdown-preview.less @@ -2,6 +2,7 @@ // Global Markdown Preview styles .markdown-preview { + contain: paint; // Hide a `pre` that comes directly after an `atom-text-editor` because the // `atom-text-editor` is the syntax-highlighted representation. @@ -35,14 +36,38 @@ .task-list-item { list-style-type: none; } + + &.loading { + display: flex; + flex-direction: column; + justify-content: center; + + // `.loading` on the preview element automatically shows the spinner/text. + // We add a slight animation delay so that, when the preview content is + // quick to appear (as usually happens), the spinner won't be shown. It + // only shows up when preview content takes a while to render. + &:before { + display: block; + content: 'Loading Markdown…'; + margin: auto; + background-image: url(images/octocat-spinner-128.gif); + background-repeat: no-repeat; + background-size: 64px; + background-position: top center; + padding-top: 70px; + text-align: center; + opacity: 0; + animation-duration: 1s; + animation-name: appear-after-short-delay; + animation-delay: 0.75s; + animation-fill-mode: forwards; + } + } } -.markdown-spinner { - margin: auto; - background-image: url(images/octocat-spinner-128.gif); - background-repeat: no-repeat; - background-size: 64px; - background-position: top center; - padding-top: 70px; - text-align: center; +// Not an actual animation; we just use an animation so that it can appear +// after a short delay. +@keyframes appear-after-short-delay { + 0% { opacity: 1; } + 100% { opacity: 1; } }