-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Enable rule switching and resize logic between rule list and displayed CSS rule #515
Changes from 11 commits
92cd898
1d30a1d
842bdc2
1ddd301
4c14327
66e58e5
b326bef
044ee96
b97e8c2
1f44754
21d3407
07fc5cd
784d7c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,7 @@ define(function (require, exports, module) { | |
|
||
/** | ||
* @constructor | ||
* @extends {InlineEditor} | ||
* @extends {InlineWidget} | ||
*/ | ||
function CSSInlineEditor(rules) { | ||
InlineTextEditor.call(this); | ||
|
@@ -28,6 +28,9 @@ define(function (require, exports, module) { | |
CSSInlineEditor.prototype = new InlineTextEditor(); | ||
CSSInlineEditor.prototype.constructor = CSSInlineEditor; | ||
CSSInlineEditor.prototype.parentClass = InlineTextEditor.prototype; | ||
CSSInlineEditor.prototype.$editorsDiv = null; | ||
CSSInlineEditor.prototype.$relatedContainer = null; | ||
CSSInlineEditor.prototype.updateRelatedContainerProxy = null; | ||
|
||
/** | ||
* @override | ||
|
@@ -43,7 +46,7 @@ define(function (require, exports, module) { | |
$location; | ||
|
||
// Create DOM to hold editors and related list | ||
var $editorsDiv = $(document.createElement('div')).addClass("inlineEditorHolder"); | ||
this.$editorsDiv = $(document.createElement('div')).addClass("inlineEditorHolder"); | ||
|
||
// Outer container for border-left and scrolling | ||
this.$relatedContainer = $(document.createElement("div")).addClass("relatedContainer"); | ||
|
@@ -57,16 +60,6 @@ define(function (require, exports, module) { | |
// Rule list | ||
var $ruleList = $(document.createElement("ul")).appendTo($related); | ||
|
||
// Keyboard shortcuts | ||
var extraKeys = { | ||
"Alt-Up" : $.proxy(this.previousRule, this), | ||
"Alt-Down" : $.proxy(this.nextRule, this) | ||
}; | ||
|
||
// load first rule | ||
var rule = this._rules[0]; | ||
this.createInlineEditorFromText(rule.document, rule.lineStart, rule.lineEnd, $editorsDiv.get(0), extraKeys); | ||
|
||
// create rule list | ||
this._ruleItems = []; | ||
this._rules.forEach(function (rule, i) { | ||
|
@@ -87,29 +80,76 @@ define(function (require, exports, module) { | |
self.setSelectedRule(0); | ||
|
||
// attach to main container | ||
this.$htmlContent.append($editorsDiv).append(this.$relatedContainer); | ||
this.$htmlContent.append(this.$editorsDiv).append(this.$relatedContainer); | ||
|
||
// initialize position based on the main #editorHolder | ||
setTimeout(function () { | ||
self._updateRelatedContainer(); | ||
}, 0); | ||
|
||
var updateRelatedContainerProxy = $.proxy(this._updateRelatedContainer, this); | ||
this._updateRelatedContainerProxy = $.proxy(this._updateRelatedContainer, this); | ||
|
||
// Changes to the host editor should update the relatedContainer | ||
$(this.hostEditor).on("change.CSSInlineEditor", updateRelatedContainerProxy); | ||
|
||
// TODO (jasonsj): install on active inline editor | ||
// Changes in size to the inline editor should update the relatedContainer | ||
$(this.editors[0]).on("change.CSSInlineEditor", updateRelatedContainerProxy); | ||
$(this.hostEditor).on("change.CSSInlineEditor", this.updateRelatedContainerProxy); | ||
|
||
// Since overflow-y is hidden on the CM scrollerElement, the scroll event is never fired. | ||
// Instead, we add a hook to CM's onScroll to reposition the relatedContainer. | ||
this.hostEditor._codeMirror.setOption("onScroll", updateRelatedContainerProxy); | ||
this.hostEditor._codeMirror.setOption("onScroll", this.updateRelatedContainerProxy); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here and on line 93 I think you meant this. _updateRelatedContainerProxy. This is why the rules list isn't scrolling with the inline widget. |
||
|
||
return (new $.Deferred()).resolve(); | ||
}; | ||
|
||
/** | ||
* | ||
* | ||
*/ | ||
CSSInlineEditor.prototype.setSelectedRule = function (index) { | ||
var newIndex = Math.min(Math.max(0, index), this._rules.length - 1); | ||
|
||
this._selectedRuleIndex = newIndex; | ||
var $ruleItem = this._ruleItems[this._selectedRuleIndex]; | ||
|
||
this._ruleItems[this._selectedRuleIndex].toggleClass("selected", true); | ||
|
||
// Remove previous editors | ||
this.editors.forEach(function (editor) { | ||
editor.destroy(); //release ref on Document | ||
}); | ||
this.editors = []; | ||
this.$editorsDiv.children().remove(); | ||
$(this.editors[0]).off("change.CSSInlineEditor"); | ||
|
||
// Keyboard shortcuts | ||
var extraKeys = { | ||
"Alt-Up" : $.proxy(this.previousRule, this), | ||
"Alt-Down" : $.proxy(this.nextRule, this) | ||
}; | ||
|
||
|
||
// Add new editor | ||
var rule = this.getSelectedRule(); | ||
this.createInlineEditorFromText(rule.document, rule.lineStart, rule.lineEnd, this.$editorsDiv.get(0), extraKeys); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to re-add change listeners "change.CSSInlineEditor". See load(), my previous impl was always installing this to the first editor. Also, I noticed that focus doesn't go to the new editor after pressing Alt-Up/Down. |
||
this.editors[0].focus() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing semi-colon There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
|
||
// Changes in size to the inline editor should update the relatedContainer | ||
$(this.editors[0]).on("change.CSSInlineEditor", this.updateRelatedContainerProxy); | ||
|
||
this.sizeInlineWidgetToContents(true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should call this._updateRelatedContainer() immediately after this line so that relatedContainer can take on the new height. |
||
this._updateRelatedContainer(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Drive-by comment: out of curiosity, how come the keyboard shortcuts and editor-switching code live in InlineTextEditor? I see that the rule list lives in CSSInlineProvider (seems reasonable for now), but since the notion of having multiple editors lives in the base class InlineTextEditor, shouldn't the API to let that UI code switch amongst those multiple editors live in the base class too? |
||
// scroll the selection to the ruleItem, use setTimeout to wait for DOM updates | ||
var self = this; | ||
setTimeout(function () { | ||
var itemTop = $ruleItem.position().top; | ||
self.$selectedMarker.css("top", itemTop); | ||
self.$selectedMarker.height($ruleItem.height()); | ||
|
||
// FUTURE (jasonsj): figure out if rule list should scroll | ||
itemTop -= $ruleItem.parent().css("paddingTop").replace("px", ""); | ||
self.$relatedContainer.scrollTop(itemTop); | ||
}, 0); | ||
}; | ||
|
||
/** | ||
* Called any time inline was closed, whether manually (via closeThisInline()) or automatically | ||
*/ | ||
|
@@ -122,47 +162,63 @@ define(function (require, exports, module) { | |
this.hostEditor._codeMirror.setOption("onScroll", null); | ||
}; | ||
|
||
/** | ||
* | ||
* | ||
*/ | ||
CSSInlineEditor.prototype._updateRelatedContainer = function () { | ||
var borderThickness = (this.$htmlContent.outerHeight() - this.$htmlContent.innerHeight()) / 2; | ||
this.$relatedContainer.css("top", this.$htmlContent.offset().top + borderThickness); | ||
this.$relatedContainer.height(this.$htmlContent.height()); | ||
}; | ||
|
||
// TY TODO: part of sprint 6 | ||
/** | ||
* | ||
* | ||
*/ | ||
CSSInlineEditor.prototype.getRules = function () { | ||
return this._rules; | ||
}; | ||
|
||
|
||
/** | ||
* | ||
* | ||
*/ | ||
CSSInlineEditor.prototype.getSelectedRule = function () { | ||
return this._rules[this._selectedRuleIndex]; | ||
}; | ||
|
||
|
||
CSSInlineEditor.prototype.setSelectedRule = function (index) { | ||
var newIndex = Math.min(Math.max(0, index), this._rules.length - 1); | ||
|
||
this._selectedRuleIndex = newIndex; | ||
|
||
var $ruleItem = this._ruleItems[this._selectedRuleIndex]; | ||
|
||
// scroll the selection to the ruleItem, use setTimeout to wait for DOM updates | ||
var self = this; | ||
setTimeout(function () { | ||
var itemTop = $ruleItem.position().top; | ||
self.$selectedMarker.css("top", itemTop); | ||
self.$selectedMarker.height($ruleItem.height()); | ||
|
||
// FUTURE (jasonsj): figure out if rule list should scroll | ||
itemTop -= $ruleItem.parent().css("paddingTop").replace("px", ""); | ||
self.$relatedContainer.scrollTop(itemTop); | ||
}, 0); | ||
}; | ||
|
||
/** | ||
* Display the next css rule in the rule list | ||
*/ | ||
CSSInlineEditor.prototype.nextRule = function () { | ||
this.setSelectedRule(this._selectedRuleIndex + 1); | ||
}; | ||
|
||
/** | ||
* Display the previous css rule in the rule list | ||
*/ | ||
CSSInlineEditor.prototype.previousRule = function () { | ||
this.setSelectedRule(this._selectedRuleIndex - 1); | ||
}; | ||
|
||
/** | ||
* Sizes the inline widget height to be the maximum between the rule list height and the editor height | ||
* @overide | ||
*/ | ||
CSSInlineEditor.prototype.sizeInlineWidgetToContents = function (force) { | ||
// Size the code mirror editors height to the editor content | ||
this.parentClass.sizeInlineWidgetToContents.call(this, force); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need this override anymore. As long as _updateRelatedContainer() is installed correctly to each editor and we call in load() (see comment above) we should be ok. If we need to enforce a min/max height, it may be better to do in _updateRelatedContainer(). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like $htmlContent.scrollHeight does include the relatedContainer even though it uses position:fixed. This is causing the +1px height change even when the editor content gets smaller. We should change widget height to use the .inlineEditorHolder div instead.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good find. Looking into that now... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I modified the .related class and removed the 100% height because I need one div to measure the size that the related list wants to be and another div to set it. If I use the same div the list never shrinks. So now I measure related and _updateRelatedContainer sets relatedContainer (related's parent) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused var? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed |
||
var relatedSize = this.$relatedContainer.find("related").height; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unused var |
||
// Size the widget height to the max between the editor content and the related rules list | ||
var widgetHeight = Math.max(this.$relatedContainer.find(".related").height(), this.$editorsDiv.height()); | ||
this.hostEditor.setInlineWidgetHeight(this.inlineId, widgetHeight, true); | ||
|
||
// The related rules container size itself based on htmlContent which is set by setInlineWidgetHeight above. | ||
this._updateRelatedContainer(); | ||
}; | ||
|
||
|
||
/** | ||
* Given a position in an HTML editor, returns the relevant selector for the attribute/tag | ||
|
@@ -215,7 +271,7 @@ define(function (require, exports, module) { | |
* | ||
* @param {!Editor} editor | ||
* @param {!{line:Number, ch:Number}} pos | ||
* @return {$.Promise} a promise that will be resolved with an InlineEditor | ||
* @return {$.Promise} a promise that will be resolved with an InlineWidget | ||
* or null if we're not going to provide anything. | ||
*/ | ||
function htmlToCSSProvider(hostEditor, pos) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,7 +112,7 @@ define(function (require, exports, module) { | |
* @private | ||
* Bound to Ctrl+E on outermost editors. | ||
* @param {!Editor} editor the candidate host editor | ||
* @return {$.Promise} a promise that will be resolved when an InlineEditor | ||
* @return {$.Promise} a promise that will be resolved when an InlineWidget | ||
* is created or rejected when no inline editors are available. | ||
*/ | ||
function _openInlineWidget(editor) { | ||
|
@@ -129,21 +129,21 @@ define(function (require, exports, module) { | |
|
||
// If one of them will provide a widget, show it inline once ready | ||
if (inlinePromise) { | ||
inlinePromise.done(function (inlineEditor) { | ||
$(inlineEditor.htmlContent).append('<div class="shadow top"/>') | ||
inlinePromise.done(function (inlineWidget) { | ||
$(inlineWidget.htmlContent).append('<div class="shadow top"/>') | ||
.append('<div class="shadow bottom"/>'); | ||
|
||
var closeCallback = function () { | ||
inlineEditor.onClosed(); | ||
inlineWidget.onClosed(); | ||
}; | ||
var parentShowCallback = function () { | ||
inlineEditor.onParentShown(); | ||
inlineWidget.onParentShown(); | ||
}; | ||
|
||
var inlineId = editor.addInlineWidget(pos, inlineEditor.htmlContent, inlineEditor.height, | ||
parentShowCallback, closeCallback, inlineEditor); | ||
var inlineId = editor.addInlineWidget(pos, inlineWidget.htmlContent, inlineWidget.height, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In InlineWidget you replaced assigning height with editorHeight. I believe inlineWidget.height here will always be zero. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. editorHeight is now removed |
||
parentShowCallback, closeCallback, inlineWidget); | ||
|
||
inlineEditor.onAdded(inlineId); | ||
inlineWidget.onAdded(inlineId); | ||
result.resolve(); | ||
}).fail(function () { | ||
result.reject(); | ||
|
@@ -190,7 +190,7 @@ define(function (require, exports, module) { | |
* {!Editor} editor, {!{line:Number, ch:Number}} pos | ||
* | ||
* Returns: | ||
* {$.Promise} a promise that will be resolved with an InlineEditor | ||
* {$.Promise} a promise that will be resolved with an inlineWidget | ||
* or null to indicate the provider doesn't create an editor in this case | ||
*/ | ||
function registerInlineEditProvider(provider) { | ||
|
@@ -246,7 +246,7 @@ define(function (require, exports, module) { | |
* @param {?{startLine:Number, endLine:Number}} range If specified, all lines outside the given | ||
* range are hidden from the editor. Range is inclusive. Line numbers start at 0. | ||
* @param {HTMLDivContainer} inlineContent | ||
* @param {function(InlineEditor)} closeThisInline | ||
* @param {function(inlineWidget)} closeThisInline | ||
* | ||
* @return {{content:DOMElement, editor:Editor}} | ||
*/ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make private "_"