Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Added CSS upload loader #219

Merged
merged 19 commits into from
Jul 10, 2018
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 47 additions & 11 deletions src/imageupload/imageuploadprogress.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import ViewRange from '@ckeditor/ckeditor5-engine/src/view/range';

import '../../theme/imageuploadprogress.css';
import '../../theme/imageuploadicon.css';
import '../../theme/imageuploadloader.css';

/**
* The image upload progress plugin.
Expand Down Expand Up @@ -118,6 +119,9 @@ export default class ImageUploadProgress extends Plugin {
// Symbol added to progress bar UIElement to distinguish it from other elements.
const progressBarSymbol = Symbol( 'progress-bar' );

// Symbol added to placeholder UIElement to distinguish it from other elements.
const placeholderSymbol = Symbol( 'placeholder' );

// Adds ck-appear class to the image figure if one is not already applied.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
Expand All @@ -138,6 +142,7 @@ function _stopAppearEffect( viewFigure, writer ) {

// Shows placeholder together with infinite progress bar on given image figure.
//
// @param {String} Data-uri with a svg placeholder.
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/writer~Writer} writer
function _showPlaceholder( placeholder, viewFigure, writer ) {
Expand All @@ -154,6 +159,10 @@ function _showPlaceholder( placeholder, viewFigure, writer ) {
if ( viewImg.getAttribute( 'src' ) !== placeholder ) {
writer.setAttribute( 'src', placeholder, viewImg );
}

if ( !_getUIElement( viewFigure, placeholderSymbol ) ) {
writer.insert( ViewPosition.createAfter( viewImg ), _createPlaceholder( writer ) );
}
}

// Removes placeholder together with infinite progress bar on given image figure.
Expand All @@ -168,6 +177,8 @@ function _hidePlaceholder( viewFigure, writer ) {
if ( viewFigure.hasClass( 'ck-infinite-progress' ) ) {
writer.removeClass( 'ck-infinite-progress', viewFigure );
}

_removeUIElement( viewFigure, writer, placeholderSymbol );
}

// Shows progress bar displaying upload progress.
Expand All @@ -178,7 +189,7 @@ function _hidePlaceholder( viewFigure, writer ) {
// @param {module:upload/filerepository~FileLoader} loader
// @param {module:engine/view/view~View} view
function _showProgressBar( viewFigure, writer, loader, view ) {
const progressBar = createProgressBar( writer );
const progressBar = _createProgressBar( writer );
writer.insert( ViewPosition.createAt( viewFigure, 'end' ), progressBar );

// Update progress bar width when uploadedPercent is changed.
Expand All @@ -194,11 +205,7 @@ function _showProgressBar( viewFigure, writer, loader, view ) {
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/writer~Writer} writer
function _hideProgressBar( viewFigure, writer ) {
const progressBar = getProgressBar( viewFigure );

if ( progressBar ) {
writer.remove( ViewRange.createOn( progressBar ) );
}
_removeUIElement( viewFigure, writer, progressBarSymbol );
}

// Shows complete icon and hides after a certain amount of time.
Expand All @@ -221,23 +228,52 @@ function _showCompleteIcon( viewFigure, writer, view ) {
// @private
// @param {module:engine/view/writer~Writer} writer
// @returns {module:engine/view/uielement~UIElement}
function createProgressBar( writer ) {
function _createProgressBar( writer ) {
const progressBar = writer.createUIElement( 'div', { class: 'ck-progress-bar' } );

writer.setCustomProperty( progressBarSymbol, true, progressBar );

return progressBar;
}

// Returns progress bar {@link module:engine/view/uielement~UIElement} from image figure element. Returns `undefined` if
// progress bar element is not found.
// Create placeholder element using {@link module:engine/view/uielement~UIElement}.
//
// @private
// @param {module:engine/view/writer~Writer} writer
// @returns {module:engine/view/uielement~UIElement}
function _createPlaceholder( writer ) {
const placeholder = writer.createUIElement( 'div', { class: 'ck-upload-placeholder-loader' } );

writer.setCustomProperty( placeholderSymbol, true, placeholder );

return placeholder;
}

// Returns {@link module:engine/view/uielement~UIElement} of given unique property from image figure element.
// Returns `undefined` if element is not found.
//
// @private
// @param {module:engine/view/element~Element} imageFigure
// @param {Symbol} uniqueProperty
// @returns {module:engine/view/uielement~UIElement|undefined}
function getProgressBar( imageFigure ) {
function _getUIElement( imageFigure, uniqueProperty ) {
for ( const child of imageFigure.getChildren() ) {
if ( child.getCustomProperty( progressBarSymbol ) ) {
if ( child.getCustomProperty( uniqueProperty ) ) {
return child;
}
}
}

// Removes {@link module:engine/view/uielement~UIElement} of given unique property from image figure element.
//
// @private
// @param {module:engine/view/element~Element} imageFigure
// @param {module:engine/view/writer~Writer} writer
// @param {Symbol} uniqueProperty
function _removeUIElement( viewFigure, writer, uniqueProperty ) {
const element = _getUIElement( viewFigure, uniqueProperty );

if ( element ) {
writer.remove( ViewRange.createOn( element ) );
}
}
22 changes: 13 additions & 9 deletions tests/imageupload/imageuploadprogress.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe( 'ImageUploadProgress', () => {

// eslint-disable-next-line max-len
const base64Sample = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=';
let editor, model, document, fileRepository, view, nativeReaderMock, loader, adapterMock;
let editor, model, doc, fileRepository, view, nativeReaderMock, loader, adapterMock;

class UploadAdapterPluginMock extends Plugin {
init() {
Expand Down Expand Up @@ -55,7 +55,7 @@ describe( 'ImageUploadProgress', () => {
.then( newEditor => {
editor = newEditor;
model = editor.model;
document = model.document;
doc = model.document;
view = editor.editing.view;

fileRepository = editor.plugins.get( FileRepository );
Expand All @@ -75,6 +75,7 @@ describe( 'ImageUploadProgress', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-appear ck-image-upload-placeholder ck-infinite-progress ck-widget image" contenteditable="false">' +
`<img src="data:image/svg+xml;utf8,${ imagePlaceholder }"></img>` +
'<div class="ck-upload-placeholder-loader"></div>' +
'</figure>]<p>foo</p>'
);
} );
Expand Down Expand Up @@ -103,7 +104,7 @@ describe( 'ImageUploadProgress', () => {
const loader = fileRepository.createLoader( file );

setModelData( model, '<image></image>' );
const image = document.getRoot().getChild( 0 );
const image = doc.getRoot().getChild( 0 );

// Set attributes directly on image to simulate instant "uploading" status.
model.change( writer => {
Expand All @@ -122,7 +123,7 @@ describe( 'ImageUploadProgress', () => {

it( 'should work correctly when there is no "reading" status and go straight to "uploading" - external changes', () => {
setModelData( model, '<image></image>' );
const image = document.getRoot().getChild( 0 );
const image = doc.getRoot().getChild( 0 );

// Set attributes directly on image to simulate instant "uploading" status.
model.change( writer => {
Expand All @@ -133,13 +134,14 @@ describe( 'ImageUploadProgress', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-appear ck-image-upload-placeholder ck-infinite-progress ck-widget image" contenteditable="false">' +
`<img src="data:image/svg+xml;utf8,${ imagePlaceholder }"></img>` +
'<div class="ck-upload-placeholder-loader"></div>' +
'</figure>]'
);
} );

it( 'should "clear" image when uploadId changes to null', () => {
setModelData( model, '<image></image>' );
const image = document.getRoot().getChild( 0 );
const image = doc.getRoot().getChild( 0 );

// Set attributes directly on image to simulate instant "uploading" status.
model.change( writer => {
Expand All @@ -154,7 +156,7 @@ describe( 'ImageUploadProgress', () => {

expect( getViewData( view ) ).to.equal(
'[<figure class="ck-widget image" contenteditable="false">' +
`<img src="data:image/svg+xml;utf8,${ imagePlaceholder }"></img>` +
`<img src="data:image/svg+xml;utf8,${ imagePlaceholder }"></img>` +
'</figure>]'
);
} );
Expand All @@ -168,8 +170,8 @@ describe( 'ImageUploadProgress', () => {

expect( getViewData( view ) ).to.equal(
'[<figure class="ck-appear ck-widget image" contenteditable="false">' +
`<img src="${ base64Sample }"></img>` +
'<div class="ck-progress-bar" style="width:40%"></div>' +
`<img src="${ base64Sample }"></img>` +
'<div class="ck-progress-bar" style="width:40%"></div>' +
'</figure>]<p>foo</p>'
);

Expand Down Expand Up @@ -221,6 +223,7 @@ describe( 'ImageUploadProgress', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-appear ck-image-upload-placeholder ck-infinite-progress ck-widget image" contenteditable="false">' +
`<img src="${ base64Sample }"></img>` +
'<div class="ck-upload-placeholder-loader"></div>' +
'</figure>]<p>foo</p>'
);
} );
Expand All @@ -241,7 +244,7 @@ describe( 'ImageUploadProgress', () => {
it( 'should not show progress bar and complete icon if there is no loader with given uploadId', () => {
setModelData( model, '<image uploadId="123" uploadStatus="reading"></image>' );

const image = document.getRoot().getChild( 0 );
const image = doc.getRoot().getChild( 0 );

model.change( writer => {
writer.setAttribute( 'uploadStatus', 'uploading', image );
Expand All @@ -250,6 +253,7 @@ describe( 'ImageUploadProgress', () => {
expect( getViewData( view ) ).to.equal(
'[<figure class="ck-appear ck-image-upload-placeholder ck-infinite-progress ck-widget image" contenteditable="false">' +
`<img src="data:image/svg+xml;utf8,${ imagePlaceholder }"></img>` +
'<div class="ck-upload-placeholder-loader"></div>' +
'</figure>]'
);

Expand Down
2 changes: 1 addition & 1 deletion theme/icons/image_placeholder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions theme/imageuploadloader.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md.
*/

:root {
--ck-upload-placeholder-loader-size: 24px;
}

.ck .ck-upload-placeholder-loader {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
top: 0;
left: 0;

&::before {
content: '';
position: relative;
width: var(--ck-upload-placeholder-loader-size);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Styles like width, height, belong to Lark. position, top, content, flex should stay here.

height: var(--ck-upload-placeholder-loader-size);
}
}