Esanders has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/356238 )
Change subject: Make author list a ListPopupTool ...................................................................... Make author list a ListPopupTool Change-Id: I6180216e04747e4d230bdf782422643f61e01574 --- M build/modules.json M rebaser/demo.js M rebaser/views/editor.ejs A src/ui/styles/widgets/ve.ui.AuthorItemWidget.css D src/ui/styles/widgets/ve.ui.AuthorListWidget.css A src/ui/tools/ve.ui.AuthorListPopupTool.js A src/ui/widgets/ve.ui.AuthorItemWidget.js D src/ui/widgets/ve.ui.AuthorListWidget.js M tests/index.html 9 files changed, 274 insertions(+), 155 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor refs/changes/38/356238/1 diff --git a/build/modules.json b/build/modules.json index 23d662a..8d99674 100644 --- a/build/modules.json +++ b/build/modules.json @@ -631,10 +631,11 @@ "src/dm/ve.dm.RebaseServer.js", "src/dm/ve.dm.RebaseClient.js", "src/dm/ve.dm.SurfaceSynchronizer.js", - "src/ui/widgets/ve.ui.AuthorListWidget.js" + "src/ui/widgets/ve.ui.AuthorItemWidget.js", + "src/ui/tools/ve.ui.AuthorListPopupTool.js" ], "styles": [ - "src/ui/styles/widgets/ve.ui.AuthorListWidget.css" + "src/ui/styles/widgets/ve.ui.AuthorItemWidget.css" ], "dependencies": [ "dompurify", diff --git a/rebaser/demo.js b/rebaser/demo.js index b5553f5..537511a 100644 --- a/rebaser/demo.js +++ b/rebaser/demo.js @@ -4,21 +4,45 @@ * @copyright 2011-2017 VisualEditor Team and others; see http://ve.mit-license.org */ -new ve.init.sa.Platform( ve.messagePaths ).initialize().done( function () { - var synchronizer, authorList, - $editor = $( '.ve-demo-editor' ), - $menu = $( '.ve-pad-menu' ), - // eslint-disable-next-line new-cap - target = new ve.demo.target(); +( function () { + function RebaserTarget() { + RebaserTarget.super.apply( this, arguments ); + } - $editor.append( target.$element ); + OO.inheritClass( RebaserTarget, ve.init.sa.Target ); - target.addSurface( ve.dm.converter.getModelFromDom( ve.createDocumentFromHtml( '' ) ) ); - synchronizer = new ve.dm.SurfaceSynchronizer( target.surface.model, ve.docName ); - target.surface.view.setSynchronizer( synchronizer ); - target.surface.view.focus(); + RebaserTarget.static.actionGroups = ve.copy( RebaserTarget.static.actionGroups ); + RebaserTarget.static.actionGroups.unshift( + { include: [ 'authorList' ] } + ); - authorList = new ve.ui.AuthorListWidget( synchronizer ); + RebaserTarget.prototype.setSurface = function ( surface ) { + var synchronizer, surfaceView; - $menu.append( authorList.$element ); -} ); + if ( surface !== this.surface ) { + surfaceView = surface.getView(); + + synchronizer = new ve.dm.SurfaceSynchronizer( + surface.getModel(), + ve.docName, + { server: this.rebaserUrl } + ); + + surfaceView.setSynchronizer( synchronizer ); + } + + // Parent method + RebaserTarget.super.prototype.setSurface.apply( this, arguments ); + }; + + new ve.init.sa.Platform( ve.messagePaths ).initialize().done( function () { + var $editor = $( '.ve-demo-editor' ), + // eslint-disable-next-line new-cap + target = new RebaserTarget(); + + $editor.append( target.$element ); + + target.addSurface( ve.dm.converter.getModelFromDom( ve.createDocumentFromHtml( '' ) ) ); + target.surface.view.focus(); + } ); +}() ); diff --git a/rebaser/views/editor.ejs b/rebaser/views/editor.ejs index 6914128..60526f4 100644 --- a/rebaser/views/editor.ejs +++ b/rebaser/views/editor.ejs @@ -40,7 +40,6 @@ </head> <body> <div class="ve-pad-logo"></div> - <div class="ve-pad-menu"></div> <div style="clear: both;"></div> <div class="ve-demo-editor"></div> diff --git a/src/ui/styles/widgets/ve.ui.AuthorItemWidget.css b/src/ui/styles/widgets/ve.ui.AuthorItemWidget.css new file mode 100644 index 0000000..f6713ed --- /dev/null +++ b/src/ui/styles/widgets/ve.ui.AuthorItemWidget.css @@ -0,0 +1,23 @@ +/*! + * VisualEditor AuthorInterface AuthorItemWidget styles. + * + * @copyright 2011-2017 VisualEditor Team and others; see http://ve.mit-license.org + */ + +.ve-ui-authorItemWidget { + position: relative; + margin: 0.2em 0; + padding-left: 2.5em; + line-height: 2em; +} + +.ve-ui-authorItemWidget-color { + position: absolute; + width: 2em; + height: 2em; + left: 0; +} + +.ve-ui-authorItemWidget-editable .ve-ui-authorItemWidget-color { + top: 0.25em; +} diff --git a/src/ui/styles/widgets/ve.ui.AuthorListWidget.css b/src/ui/styles/widgets/ve.ui.AuthorListWidget.css deleted file mode 100644 index 929b6c6..0000000 --- a/src/ui/styles/widgets/ve.ui.AuthorListWidget.css +++ /dev/null @@ -1,33 +0,0 @@ -/*! - * VisualEditor AuthorInterface AuthorListWidget styles. - * - * @copyright 2011-2017 VisualEditor Team and others; see http://ve.mit-license.org - */ - -.ve-ui-authorListWidget { - margin-right: 1em; -} - -.ve-ui-authorListWidget-editName { - width: 20em; -} - -.ve-ui-authorListWidget-editName, -.ve-ui-authorListWidget-listPopup { - margin-right: 1em; - display: inline-block; - vertical-align: top; -} - -.ve-ui-authorListWidget-author { - pointer-events: none; -} - -.ve-ui-authorListWidget-author.oo-ui-iconElement .oo-ui-iconElement-icon { - top: 0.3em; - height: 1.875em; -} - -.ve-ui-authorListWidget-author-self .oo-ui-labelElement-label { - font-weight: bold; -} diff --git a/src/ui/tools/ve.ui.AuthorListPopupTool.js b/src/ui/tools/ve.ui.AuthorListPopupTool.js new file mode 100644 index 0000000..6e5bf51 --- /dev/null +++ b/src/ui/tools/ve.ui.AuthorListPopupTool.js @@ -0,0 +1,125 @@ +/*! + * VisualEditor UserInterface AuthorListPopupTool class. + * + * @copyright 2011-2017 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * UserInterface AuthorListPopupTool + * + * @class + * @extends OO.ui.PopupTool + * + * @constructor + * @param {OO.ui.ToolGroup} toolGroup + * @param {Object} [config] + */ +ve.ui.AuthorListPopupTool = function VeUiAuthorListPopupTool( toolGroup, config ) { + this.$authorList = $( '<div>' ); + + // Parent constructor + ve.ui.AuthorListPopupTool.super.call( this, toolGroup, ve.extendObject( { + popup: { + classes: [ 've-ui-authorListWidget-listPopup' ], + $content: this.$authorList, + padded: true, + align: 'center' + } + }, config ) ); + + // Events + this.toolbar.connect( this, { surfaceChange: 'onSurfaceChange' } ); + + this.setup( this.toolbar.getSurface() ); + + this.$element.addClass( 've-ui-authorListPopupTool' ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.AuthorListPopupTool, OO.ui.PopupTool ); + +/* Methods */ + +ve.ui.AuthorListPopupTool.prototype.onSurfaceChange = function ( surface ) { + this.setup( surface ); +}; + +/** + * Setup the popup which a specific surface + * + * @param {ve.ui.Surface} surface Surface + */ +ve.ui.AuthorListPopupTool.prototype.setup = function ( surface ) { + var tool = this, + synchronizer = surface.getView().synchronizer, + updatingName = false, + oldName = '', + authorItems = {}; + + // TODO: Unbind from an existing surface if one is set + + function updateName() { + if ( !updatingName ) { + synchronizer.changeName( tool.selfItem.input.getValue() ); + } + } + + function updateListCount() { + tool.setTitle( ( Object.keys( authorItems ).length + 1 ).toString() ); + } + + this.selfItem = new ve.ui.AuthorItemWidget( synchronizer, { editable: true } ); + this.$authorList.prepend( this.selfItem.$element ); + this.selfItem.input.on( 'change', ve.debounce( updateName, 250 ) ); + + synchronizer.on( 'authorNameChange', function ( authorId ) { + var authorItem = authorItems[ authorId ], + newName = synchronizer.authorNames[ authorId ]; + + if ( authorId !== synchronizer.author ) { + if ( !authorItem ) { + authorItem = new ve.ui.AuthorItemWidget( synchronizer, { authorId: authorId } ); + authorItems[ authorId ] = authorItem; + updateListCount(); + tool.$authorList.append( authorItem.$element ); + } else { + authorItem.update(); + } + } else { + // Don't update nameInput if the author is still changing it + if ( tool.selfItem.input.getValue() === oldName ) { + // Don't send this "new" name back to the server + updatingName = true; + tool.selfItem.setAuthor( synchronizer.author ); + tool.selfItem.update(); + updatingName = false; + } + } + oldName = newName; + } ); + + synchronizer.on( 'authorDisconnect', function ( authorId ) { + var authorItem = authorItems[ authorId ]; + if ( authorItem ) { + authorItem.$element.remove(); + delete authorItems[ authorId ]; + updateListCount(); + } + } ); +}; + +/* Static Properties */ + +ve.ui.AuthorListPopupTool.static.name = 'authorList'; +ve.ui.AuthorListPopupTool.static.group = 'utility'; +ve.ui.AuthorListPopupTool.static.icon = 'speechBubbles'; // TODO: Change to userAvatar once it is available in Apex +ve.ui.AuthorListPopupTool.static.title = '1'; +ve.ui.AuthorListPopupTool.static.autoAddToCatchall = false; +ve.ui.AuthorListPopupTool.static.autoAddToGroup = false; +ve.ui.AuthorListPopupTool.static.displayBothIconAndLabel = true; + +/* Registration */ + +ve.ui.toolFactory.register( ve.ui.AuthorListPopupTool ); diff --git a/src/ui/widgets/ve.ui.AuthorItemWidget.js b/src/ui/widgets/ve.ui.AuthorItemWidget.js new file mode 100644 index 0000000..11f829f --- /dev/null +++ b/src/ui/widgets/ve.ui.AuthorItemWidget.js @@ -0,0 +1,83 @@ +/*! + * VisualEditor UserInterface AuthorItemWidget class. + * + * @copyright 2011-2017 VisualEditor Team and others; see AUTHORS.txt + * @license The MIT License (MIT); see LICENSE.txt + */ + +/** + * UserInterface AuthorItemWidget + * + * @class + * @extends OO.ui.Widget + * @mixins OO.ui.mixin.IconElement + * @mixins OO.ui.mixin.LabelElement + * + * @constructor + * @param {ve.dm.SurfaceSynchronizer} synchronizer Surface synchronizer + * @param {Object} [config] Configuration options + */ +ve.ui.AuthorItemWidget = function VeUiAuthorItemWidget( synchronizer, config ) { + config = config || {}; + + // Parent constructor + ve.ui.AuthorItemWidget.super.call( this, config ); + + // Mixin constructors + OO.ui.mixin.LabelElement.call( this, config ); + + this.synchronizer = synchronizer; + this.editable = !!config.editable; + this.authorId = config.authorId; + + this.$color = $( '<div>' ).addClass( 've-ui-authorItemWidget-color' ); + this.$element.append( this.$color ); + + if ( this.editable ) { + this.input = new OO.ui.TextInputWidget(); + this.$element + .addClass( 've-ui-authorItemWidget-editable' ) + .append( this.input.$element ); + } else { + this.$element.append( this.$label ); + } + + this.update(); + + this.$element.addClass( 've-ui-authorItemWidget' ); +}; + +/* Inheritance */ + +OO.inheritClass( ve.ui.AuthorItemWidget, OO.ui.Widget ); + +OO.mixinClass( ve.ui.AuthorItemWidget, OO.ui.mixin.IconElement ); + +OO.mixinClass( ve.ui.AuthorItemWidget, OO.ui.mixin.LabelElement ); + +/* Methods */ + +/** + * Set author ID + * + * @param {number} authorId Author ID + */ +ve.ui.AuthorItemWidget.prototype.setAuthor = function ( authorId ) { + this.authorId = authorId; +}; + +/** + * Update name and color from synchronizer + */ +ve.ui.AuthorItemWidget.prototype.update = function () { + var name = this.synchronizer.authorNames[ this.authorId ], + color = this.synchronizer.constructor.static.getAuthorColor( this.authorId ); + + this.$color.css( 'background-color', '#' + color ); + + if ( this.editable ) { + this.input.setValue( name ); + } else { + this.setLabel( name ); + } +}; diff --git a/src/ui/widgets/ve.ui.AuthorListWidget.js b/src/ui/widgets/ve.ui.AuthorListWidget.js deleted file mode 100644 index a58cd11..0000000 --- a/src/ui/widgets/ve.ui.AuthorListWidget.js +++ /dev/null @@ -1,104 +0,0 @@ -/*! - * VisualEditor AuthorInterface AuthorListWidget class. - * - * @copyright 2011-2017 VisualEditor Team and others; see AUTHORS.txt - * @license The MIT License (MIT); see LICENSE.txt - */ - -/** - * Creates a ve.ui.AuthorListWidget object. - * - * @class - * @extends OO.ui.Element - * - * @constructor - * @param {ve.dm.SurfaceSynchronizer} synchronizer Surface synchronizer - * @param {Object} [config] Configuration options - */ -ve.ui.AuthorListWidget = function VeUiAuthorListWidget( synchronizer, config ) { - - var updatingName = false, - nameInput = new OO.ui.TextInputWidget(), - editNameLayout = new OO.ui.FieldLayout( nameInput, { - classes: [ 've-ui-authorListWidget-editName' ], - align: 'right', - label: ve.msg( 'visualeditor-rebase-client-author-name' ) - } ), - oldName = '', - $authorList = $( '<div>' ), - authorLabels = {}, - listPopup = new OO.ui.PopupButtonWidget( { - classes: [ 've-ui-authorListWidget-listPopup' ], - icon: 'speechBubbles', // TODO: Change to userAvatar once it is available in Apex - indicator: 'down', - popup: { - $content: $authorList, - padded: true, - align: 'center' - } - } ); - - // Parent constructor - ve.ui.AuthorListWidget.super.call( this, config ); - - function updateName() { - if ( !updatingName ) { - synchronizer.changeName( nameInput.getValue() ); - } - } - - function updateListCount() { - listPopup.setLabel( Object.keys( authorLabels ).length.toString() ); - } - - synchronizer.on( 'authorNameChange', function ( authorId ) { - var authorLabel = authorLabels[ authorId ], - newName = synchronizer.authorNames[ authorId ]; - - if ( !authorLabel ) { - // FIXME use something more suitable than DecoratedOptionWidget - authorLabel = new OO.ui.DecoratedOptionWidget( { - classes: [ 've-ui-authorListWidget-author' ], - // HACK: force the icon to show, but override the background with a color - icon: 'none' - } ); - authorLabel.$icon.css( 'background', '#' + synchronizer.constructor.static.getAuthorColor( authorId ) ); - authorLabels[ authorId ] = authorLabel; - updateListCount(); - $authorList.append( authorLabel.$element ); - } - authorLabel.setLabel( newName ); - if ( authorId === synchronizer.author ) { - // Ensure you are at the top of the list - $authorList.prepend( authorLabel.$element.addClass( 've-ui-authorListWidget-author-self' ) ); - // Don't update nameInput if the author is still changing it - if ( nameInput.getValue() === oldName ) { - // Don't send this "new" name back to the server - updatingName = true; - nameInput.setValue( newName ); - updatingName = false; - } - } - oldName = newName; - } ); - - synchronizer.on( 'authorDisconnect', function ( authorId ) { - var authorLabel = authorLabels[ authorId ]; - if ( authorLabel ) { - authorLabel.$element.remove(); - delete authorLabels[ authorId ]; - updateListCount(); - } - } ); - - nameInput.on( 'change', ve.debounce( updateName, 250 ) ); - - this.$element.addClass( 've-ui-authorListWidget' ).append( - editNameLayout.$element, - listPopup.$element - ); -}; - -/* Inheritance */ - -OO.inheritClass( ve.ui.AuthorListWidget, OO.ui.Widget ); diff --git a/tests/index.html b/tests/index.html index 9f4d3a6..0769b6e 100644 --- a/tests/index.html +++ b/tests/index.html @@ -442,7 +442,8 @@ <script src="../src/dm/ve.dm.RebaseServer.js"></script> <script src="../src/dm/ve.dm.RebaseClient.js"></script> <script src="../src/dm/ve.dm.SurfaceSynchronizer.js"></script> - <script src="../src/ui/widgets/ve.ui.AuthorListWidget.js"></script> + <script src="../src/ui/widgets/ve.ui.AuthorItemWidget.js"></script> + <script src="../src/ui/tools/ve.ui.AuthorListPopupTool.js"></script> <!-- visualEditor.test --> <script src="../tests/ve.qunit.js"></script> -- To view, visit https://gerrit.wikimedia.org/r/356238 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I6180216e04747e4d230bdf782422643f61e01574 Gerrit-PatchSet: 1 Gerrit-Project: VisualEditor/VisualEditor Gerrit-Branch: master Gerrit-Owner: Esanders <esand...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits