jenkins-bot has submitted this change and it was merged. (
https://gerrit.wikimedia.org/r/354450 )
Change subject: rebaser: Tidy up author list UI
......................................................................
rebaser: Tidy up author list UI
* Use more OOUI widgets
* Don't hide cursor after 2s, add a class after 5s. This could
be used to hide user label and/or grey out cursor.
* Ensure authorId is always a number.
Change-Id: I3a41c1da74352750a8d175637e6355e53b25dc7a
---
M demos/ve/demo.css
M rebaser/demo.js
M rebaser/server.js
M rebaser/views/editor.ejs
M src/ce/styles/ve.ce.Surface.css
M src/ce/ve.ce.Surface.js
M src/dm/ve.dm.SurfaceSynchronizer.js
7 files changed, 109 insertions(+), 63 deletions(-)
Approvals:
Catrope: Looks good to me, but someone else must approve
jenkins-bot: Verified
Jforrester: Looks good to me, approved
diff --git a/demos/ve/demo.css b/demos/ve/demo.css
index 5a70bfc..8ad3b0c 100644
--- a/demos/ve/demo.css
+++ b/demos/ve/demo.css
@@ -22,6 +22,35 @@
background-size: contain;
}
+.ve-pad-menu {
+ float: right;
+ margin-top: 1em;
+}
+
+.ve-pad-menu > .oo-ui-fieldLayout {
+ width: 20em;
+}
+
+.ve-pad-menu > .oo-ui-fieldLayout,
+.ve-pad-menu > .oo-ui-popupButtonWidget {
+ margin-right: 1em;
+ display: inline-block;
+ vertical-align: top;
+}
+
+.ve-pad-menu-author {
+ pointer-events: none;
+}
+
+.ve-pad-menu-author.oo-ui-iconElement .oo-ui-iconElement-icon {
+ top: 0.3em;
+ height: 1.875em;
+}
+
+.ve-pad-menu-author-self .oo-ui-labelElement-label {
+ font-weight: bold;
+}
+
.ve-pad-logo {
background-image: url( VisualEditor-pad-logo.svg );
width: 8.5em;
diff --git a/rebaser/demo.js b/rebaser/demo.js
index 5d47eeb..9a6b129 100644
--- a/rebaser/demo.js
+++ b/rebaser/demo.js
@@ -6,78 +6,81 @@
new ve.init.sa.Platform( ve.messagePaths ).initialize().done( function () {
var synchronizer,
+ updatingName = false,
$editor = $( '.ve-demo-editor' ),
- $sidebar = $( '<div>' ),
+ $menu = $( '.ve-pad-menu' ),
nameInput = new OO.ui.TextInputWidget(),
- changeNameButton = new OO.ui.ButtonWidget( { label: 'Change
name' } ),
- authorList = new OO.ui.SelectWidget(),
+ editNameLayout = new OO.ui.FieldLayout( nameInput, { align:
'right', label: 'Name' } ),
+ oldName = '',
+ $authorList = $( '<div>' ),
+ authorLabels = {},
+ userPopup = new OO.ui.PopupButtonWidget( {
+ label: 'Users',
+ indicator: 'down',
+ popup: {
+ label: 'Users',
+ $content: $authorList,
+ padded: true,
+ align: 'center'
+ }
+ } ),
// eslint-disable-next-line new-cap
target = new ve.demo.target();
function updateName() {
- synchronizer.changeName( nameInput.getValue() );
+ if ( !updatingName ) {
+ synchronizer.changeName( nameInput.getValue() );
+ }
}
- $sidebar.append(
- // FIXME FieldLayouts exist for this purpose
- nameInput.$element
- .css( { display: 'inline-block', width: 'auto' } ),
- changeNameButton.$element,
- authorList.$element
+ $menu.append(
+ editNameLayout.$element,
+ userPopup.$element
);
- $editor
- .append(
- $( '<div>' )
- .css( { display: 'table', width: '100%' } )
- .append(
- $( '<div>' )
- .css( { display: 'table-row' } )
- .append(
- $( '<div>' )
- .css( {
display: 'table-cell', width: '80%' } )
- .append(
target.$element ),
- $sidebar
- .css( {
display: 'table-cell', 'padding-left': '1em' } )
- )
- )
- );
+ $editor.append( target.$element );
+
target.addSurface( ve.dm.converter.getModelFromDom(
ve.createDocumentFromHtml( '' ) ) );
synchronizer = new ve.dm.SurfaceSynchronizer( target.surface.model,
ve.docName );
target.surface.view.setSynchronizer( synchronizer );
synchronizer.on( 'authorNameChange', function ( authorId ) {
- var color,
- authorLabel = authorList.getItemFromData( String(
authorId ) );
- if ( !authorLabel ) {
- // FIXME: Duplicated from SurfaceSynchronizer
- color = '#' +
- ( 8 * ( 1 - Math.sin( 5 * authorId ) )
).toString( 16 ).slice( 0, 1 ) +
- ( 6 * ( 1 - Math.cos( 3 * authorId ) )
).toString( 16 ).slice( 0, 1 ) +
- '0';
+ var authorLabel = authorLabels[ authorId ],
+ newName = synchronizer.authorNames[ authorId ];
+ if ( !authorLabel ) {
// FIXME use something more suitable than
DecoratedOptionWidget
authorLabel = new OO.ui.DecoratedOptionWidget( {
- data: String( authorId ),
+ classes: [ 've-pad-menu-author' ],
// HACK: force the icon to show, but override
the background with a color
icon: 'none'
} );
- authorLabel.$icon.css( 'background', color );
- authorList.addItems( [ authorLabel ] );
+ authorLabel.$icon.css( 'background', '#' +
synchronizer.constructor.static.getAuthorColor( authorId ) );
+ authorLabels[ authorId ] = authorLabel;
+ $authorList.append( authorLabel.$element );
}
- authorLabel.setLabel( String( synchronizer.authorNames[
authorId ] ) );
- if ( String( authorId ) === String( synchronizer.author ) ) {
- nameInput.setValue( String( synchronizer.authorNames[
authorId ] ) );
+ authorLabel.setLabel( newName );
+ if ( authorId === synchronizer.author ) {
+ // Ensure you are at the top of the list
+ $authorList.prepend( authorLabel.$element.addClass(
've-pad-menu-author-self' ) );
+ // Don't update nameInput if the user 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 = authorList.getItemFromData( String( authorId
) );
+ var authorLabel = authorLabels[ authorId ];
if ( authorLabel ) {
- authorList.removeItems( [ authorLabel ] );
+ authorLabel.$element.remove();
+ delete authorLabels[ authorId ];
}
} );
- changeNameButton.on( 'click', updateName );
- nameInput.on( 'enter', updateName );
+ nameInput.on( 'change', ve.debounce( updateName, 250 ) );
} );
diff --git a/rebaser/server.js b/rebaser/server.js
index 755254a..c797472 100644
--- a/rebaser/server.js
+++ b/rebaser/server.js
@@ -39,7 +39,7 @@
author = 1 + ( lastAuthorForDoc.get( docName ) || 0 ),
authorData = rebaseServer.getAuthorData( docName,
author );
lastAuthorForDoc.set( docName, author );
- rebaseServer.setAuthorName( docName, author, 'Anonymous coward
' + author );
+ rebaseServer.setAuthorName( docName, author, 'User ' + author
); // TODO: i18n
logServerEvent( {
type: 'newClient',
doc: docName,
diff --git a/rebaser/views/editor.ejs b/rebaser/views/editor.ejs
index 7870522..1ae80b3 100644
--- a/rebaser/views/editor.ejs
+++ b/rebaser/views/editor.ejs
@@ -36,6 +36,7 @@
</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/ce/styles/ve.ce.Surface.css b/src/ce/styles/ve.ce.Surface.css
index a9402dc..a95f902 100644
--- a/src/ce/styles/ve.ce.Surface.css
+++ b/src/ce/styles/ve.ce.Surface.css
@@ -83,6 +83,11 @@
text-overflow: ellipsis;
}
+.ve-ce-surface-highlights-user-cursor-inactive,
+.ve-ce-surface-highlights-user-selection-inactive {
+ opacity: 0.5;
+}
+
.ve-ce-surface-paste {
position: fixed;
/* FIXME T126024: Stop the viewport scrolling when the paste target is
typed into */
diff --git a/src/ce/ve.ce.Surface.js b/src/ce/ve.ce.Surface.js
index 8eb9632..d3020d2 100644
--- a/src/ce/ve.ce.Surface.js
+++ b/src/ce/ve.ce.Surface.js
@@ -3983,11 +3983,7 @@
* @param {number} author The author ID
*/
ve.ce.Surface.prototype.onSynchronizerAuthorUpdate = function ( author ) {
- try {
- this.paintAuthor( author );
- } catch ( error ) {
- return;
- }
+ this.paintAuthor( author );
};
/**
@@ -3997,10 +3993,7 @@
*/
ve.ce.Surface.prototype.paintAuthor = function ( author ) {
var i, l, rects, rect, overlays,
- color = '#' +
- ( 8 * ( 1 - Math.sin( 5 * author ) ) ).toString( 16
).slice( 0, 1 ) +
- ( 6 * ( 1 - Math.cos( 3 * author ) ) ).toString( 16
).slice( 0, 1 ) +
- '0',
+ color = '#' +
this.synchronizer.constructor.static.getAuthorColor( author ),
selection = this.synchronizer.authorSelections[ author ];
if ( author === this.author ) {
@@ -4011,22 +4004,23 @@
this.userSelectionOverlays[ author ] = {
$cursor: $( '<div>' ),
$selection: $( '<div>' ),
- clearDebounced: ve.debounce( function () {
- overlays.$cursor.detach();
- overlays.$selection.detach();
- }, 2000 )
+ deactivateDebounced: ve.debounce( function () {
+ // TODO: Transition away the user label when
inactive, maybe dim selection
+ overlays.$cursor.addClass(
've-ce-surface-highlights-user-cursor-inactive' );
+ overlays.$selection.addClass(
've-ce-surface-highlights-user-selection-inactive' );
+ }, 5000 )
};
}
overlays = this.userSelectionOverlays[ author ];
- if ( selection.isNull() ) {
+ if ( !selection || selection.isNull() ) {
overlays.$cursor.detach();
overlays.$selection.detach();
return;
}
- overlays.$cursor.empty();
- overlays.$selection.empty();
+ overlays.$cursor.empty().removeClass(
've-ce-surface-highlights-user-cursor-inactive' );
+ overlays.$selection.empty().removeClass(
've-ce-surface-highlights-user-selection-inactive' );
if ( !selection.isCollapsed() ) {
rects = ve.ce.Selection.static.newFromModel( selection, this
).getSelectionRects();
@@ -4063,7 +4057,7 @@
this.$highlightsUserCursors.append( overlays.$cursor );
this.$highlightsUserSelections.append( overlays.$selection );
- overlays.clearDebounced();
+ overlays.deactivateDebounced();
};
/**
diff --git a/src/dm/ve.dm.SurfaceSynchronizer.js
b/src/dm/ve.dm.SurfaceSynchronizer.js
index 2995b25..f313fe5 100644
--- a/src/dm/ve.dm.SurfaceSynchronizer.js
+++ b/src/dm/ve.dm.SurfaceSynchronizer.js
@@ -71,6 +71,20 @@
* @param {string} author The author whose selection has changed
*/
+/* Static methods */
+
+/**
+ * TODO: Let authors choose color
+ *
+ * @param {number} authorId
+ * @return {string} Color, RRGGBB
+ */
+ve.dm.SurfaceSynchronizer.static.getAuthorColor = function ( authorId ) {
+ return ( 8 * ( 1 - Math.sin( 5 * authorId ) ) ).toString( 16 ).slice(
0, 1 ) +
+ ( 6 * ( 1 - Math.cos( 3 * authorId ) ) ).toString( 16 ).slice(
0, 1 ) +
+ '0';
+};
+
/* Methods */
/**
@@ -267,7 +281,7 @@
return;
}
for ( authorId in data.names ) {
- this.onNameChange( { authorId: authorId, authorName:
data.names[ authorId ] } );
+ this.onNameChange( { authorId: +authorId, authorName:
data.names[ authorId ] } );
}
history = ve.dm.Change.static.deserialize( data.history, this.doc );
this.acceptChange( history );
--
To view, visit https://gerrit.wikimedia.org/r/354450
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I3a41c1da74352750a8d175637e6355e53b25dc7a
Gerrit-PatchSet: 7
Gerrit-Project: VisualEditor/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>
Gerrit-Reviewer: Catrope <[email protected]>
Gerrit-Reviewer: Jforrester <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits