Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Commit

Permalink
Merge pull request #5532 from adobe/css-quick-edit-new-selector
Browse files Browse the repository at this point in the history
[Initial review] Create new rule from CSS quick edit
  • Loading branch information
dangoor committed Oct 17, 2013
2 parents b5eccf8 + 167e419 commit 4ef64fd
Show file tree
Hide file tree
Showing 20 changed files with 1,114 additions and 194 deletions.
168 changes: 156 additions & 12 deletions src/editor/CSSInlineEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,25 @@


/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
/*global define, $, CodeMirror, window */
/*global define, $, CodeMirror, window, Mustache */

define(function (require, exports, module) {
"use strict";

// Load dependent modules
var CSSUtils = require("language/CSSUtils"),
DocumentManager = require("document/DocumentManager"),
DropdownEventHandler = require("utils/DropdownEventHandler").DropdownEventHandler,
EditorManager = require("editor/EditorManager"),
Editor = require("editor/Editor").Editor,
FileIndexManager = require("project/FileIndexManager"),
HTMLUtils = require("language/HTMLUtils"),
MultiRangeInlineEditor = require("editor/MultiRangeInlineEditor").MultiRangeInlineEditor;
Menus = require("command/Menus"),
MultiRangeInlineEditor = require("editor/MultiRangeInlineEditor").MultiRangeInlineEditor,
PopUpManager = require("widgets/PopUpManager"),
Strings = require("strings");

var StylesheetsMenuTemplate = require("text!htmlContent/stylesheets-menu.html");

/**
* Given a position in an HTML editor, returns the relevant selector for the attribute/tag
Expand Down Expand Up @@ -78,6 +87,33 @@ define(function (require, exports, module) {
return selectorName;
}

/**
* @private
* Create the list of stylesheets in the dropdown menu.
* @return {string} The html content
*/
function _renderList(cssFileInfos) {
var templateVars = {
styleSheetList : cssFileInfos
};

return Mustache.render(StylesheetsMenuTemplate, templateVars);
}

/**
* @private
* Add a new rule for the given selector to the given document, then add the rule to the
* given inline editor.
* @param {string} selectorName The selector to create a rule for.
* @param {MultiRangeInlineEditor} inlineEditor The inline editor to display the new rule in.
* @param {Document} styleDoc The document the rule should be inserted in.
*/
function _addRule(selectorName, inlineEditor, styleDoc) {
var newRuleInfo = CSSUtils.addRuleToDocument(styleDoc, selectorName, Editor.getUseTabChar(), Editor.getSpaceUnits());
inlineEditor.addAndSelectRange(selectorName, styleDoc, newRuleInfo.range.from.line, newRuleInfo.range.to.line);
inlineEditor.editor.setCursorPos(newRuleInfo.pos.line, newRuleInfo.pos.ch);
}

/**
* This function is registered with EditManager as an inline editor provider. It creates a CSSInlineEditor
* when cursor is on an HTML tag name, class attribute, or id attribute, find associated
Expand All @@ -89,6 +125,7 @@ define(function (require, exports, module) {
* or null if we're not going to provide anything.
*/
function htmlToCSSProvider(hostEditor, pos) {

// Only provide a CSS editor when cursor is in HTML content
if (hostEditor.getLanguageForSelection().getId() !== "html") {
return null;
Expand All @@ -107,19 +144,126 @@ define(function (require, exports, module) {
return null;
}

var result = new $.Deferred();
var result = new $.Deferred(),
cssInlineEditor,
cssFileInfos = [],
$newRuleButton,
$dropdown,
$dropdownItem,
dropdownEventHandler;

/**
* @private
* Close the dropdown externally to dropdown, which ultimately calls the
* _cleanupDropdown callback.
*/
function _closeDropdown() {
if (dropdownEventHandler) {
dropdownEventHandler.close();
}
}

/**
* @private
* Remove the various event handlers that close the dropdown. This is called by the
* PopUpManager when the dropdown is closed.
*/
function _cleanupDropdown() {
$("html").off("click", _closeDropdown);
dropdownEventHandler = null;
$dropdown = null;

EditorManager.focusEditor();
}

/**
* @private
* Callback when item from dropdown list is selected
* @param {jQueryObject} $link The `a` element selected with mouse or keyboard
*/
function _onSelect($link) {
var path = $link.data("path");

if (path) {
DocumentManager.getDocumentForPath(path).done(function (styleDoc) {
_addRule(selectorName, cssInlineEditor, styleDoc);
});
}
}

/**
* @private
* Show or hide the stylesheets dropdown.
*/
function _showDropdown() {
Menus.closeAll();

$dropdown = $(_renderList(cssFileInfos));

var toggleOffset = $newRuleButton.offset();
$dropdown
.css({
left: toggleOffset.left,
top: toggleOffset.top + $newRuleButton.outerHeight()
})
.appendTo($("body"));

$("html").on("click", _closeDropdown);

dropdownEventHandler = new DropdownEventHandler($dropdown, _onSelect, _cleanupDropdown);
dropdownEventHandler.open();

$dropdown.focus();
}

/**
* @private
* Checks to see if there are any stylesheets in the project, and returns the appropriate
* "no rules"/"no stylesheets" message accordingly.
* @return {$.Promise} a promise that is resolved with the message to show. Never rejected.
*/
function _getNoRulesMsg() {
var result = new $.Deferred();
FileIndexManager.getFileInfoList("css").done(function (fileInfos) {
result.resolve(fileInfos.length ? Strings.CSS_QUICK_EDIT_NO_MATCHES : Strings.CSS_QUICK_EDIT_NO_STYLESHEETS);
});
return result;
}

CSSUtils.findMatchingRules(selectorName, hostEditor.document)
.done(function (rules) {
if (rules && rules.length > 0) {
var cssInlineEditor = new MultiRangeInlineEditor(rules);
cssInlineEditor.load(hostEditor);

result.resolve(cssInlineEditor);
} else {
// No matching rules were found.
result.reject();
}
cssInlineEditor = new MultiRangeInlineEditor(rules || [], _getNoRulesMsg);
cssInlineEditor.load(hostEditor);

var $header = $(".inline-editor-header", cssInlineEditor.$htmlContent);
$newRuleButton = $("<button class='stylesheet-button btn btn-mini disabled'/>")
.text(Strings.BUTTON_NEW_RULE)
.on("click", function (e) {
if (!$newRuleButton.hasClass("disabled")) {
// toggle dropdown
if ($dropdown) {
_closeDropdown();
} else {
_showDropdown();
}
}
e.stopPropagation();
});
$header.append($newRuleButton);

result.resolve(cssInlineEditor);

// Now that dialog has been built, collect list of stylesheets
FileIndexManager.getFileInfoList("css")
.done(function (fileInfos) {
cssFileInfos = fileInfos;

// "New Rule" button is disabled by default and gets enabled
// here if there are any stylesheets in project
if (cssFileInfos.length > 0) {
$newRuleButton.removeClass("disabled");
}
});
})
.fail(function () {
console.log("Error in findMatchingRules()");
Expand Down
12 changes: 9 additions & 3 deletions src/editor/EditorManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ define(function (require, exports, module) {

if (hostEditor) {
hostEditor.getInlineWidgets().forEach(function (widget) {
if (widget instanceof InlineTextEditor) {
inlineEditors = inlineEditors.concat(widget.editors);
if (widget instanceof InlineTextEditor && widget.editor) {
inlineEditors.push(widget.editor);
}
});
}
Expand Down Expand Up @@ -343,8 +343,14 @@ define(function (require, exports, module) {
* @return {{content:DOMElement, editor:Editor}}
*/
function createInlineEditorForDocument(doc, range, inlineContent) {
// Create the Editor
// Hide the container for the editor before creating it so that CodeMirror doesn't do extra work
// when initializing the document. When we construct the editor, we have to set its text and then
// set the (small) visible range that we show in the editor. If the editor is visible, CM has to
// render a large portion of the document before setting the visible range. By hiding the editor
// first and showing it after the visible range is set, we avoid that initial render.
$(inlineContent).hide();
var inlineEditor = _createEditorForDocument(doc, false, inlineContent, range);
$(inlineContent).show();

return { content: inlineContent, editor: inlineEditor };
}
Expand Down
Loading

0 comments on commit 4ef64fd

Please sign in to comment.