diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index d1bbfe65db..3e37f902d9 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -162,6 +162,7 @@ gulp.task('dependencies', function () { "./node_modules/ace-builds/src-min-noconflict/theme-chrome.js", "./node_modules/ace-builds/src-min-noconflict/mode-razor.js", "./node_modules/ace-builds/src-min-noconflict/mode-javascript.js", + "./node_modules/ace-builds/src-min-noconflict/mode-css.js", "./node_modules/ace-builds/src-min-noconflict/worker-javascript.js" ], "base": "./node_modules/ace-builds" diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 3100433a4a..4fb16c08ec 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -149,6 +149,7 @@ @import "components/umb-box.less"; @import "components/umb-number-badge.less"; @import "components/umb-progress-circle.less"; +@import "components/umb-stylesheet.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less new file mode 100644 index 0000000000..fd976bba32 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-stylesheet.less @@ -0,0 +1,38 @@ +.umb-stylesheet-rules { + width: 600px; +} + +.umb-stylesheet-rules__listitem { + display: flex; + padding: 6px; + margin: 10px 0 !important; + background: @gray-10; + cursor: move; +} + +.umb-stylesheet-rules__listitem i { + display: flex; + align-items: center; + margin-right: 5px +} + +.umb-stylesheet-rules__listitem a { + cursor: pointer; + margin-left: auto; +} + +.umb-stylesheet-rules__listitem input { + width: 295px; +} + +.umb-styleheet-rules__left { + display: block; + flex: 1 1 auto; + overflow: hidden; +} + +.umb-styleheet-rules__right { + display: flex; + flex: 0 0 auto; + align-items: center; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js index 33aa0f979b..09278a5521 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function StyleSheetsEditController($scope, $routeParams, $timeout, appState, editorState, navigationService, assetsService, codefileResource, contentEditingHelper, notificationsService, localizationService, templateHelper, angularHelper) { + function StyleSheetsEditController($scope, $routeParams, $timeout, $http, appState, editorState, navigationService, assetsService, codefileResource, contentEditingHelper, notificationsService, localizationService, templateHelper, angularHelper, umbRequestHelper) { var vm = this; var currentPosition = null; @@ -12,6 +12,22 @@ vm.page.menu.currentSection = appState.getSectionState("currentSection"); vm.page.menu.currentNode = null; vm.page.saveButtonState = "init"; + // TODO: localization + vm.page.navigation = [ + { + "name": "Code", + "alias": "code", + "icon": "icon-brackets", + "view": "views/stylesheets/views/code/code.html", + "active": true + }, + { + "name": "Styles", + "alias": "rules", + "icon": "icon-font", + "view": "views/stylesheets/views/rules/rules.html" + } + ]; //Used to toggle the keyboard shortcut modal //From a custom keybinding in ace editor - that conflicts with our own to show the dialog @@ -125,7 +141,7 @@ } vm.aceOption = { - mode: "stylesheet", + mode: "css", theme: "chrome", showPrintMargin: false, advanced: { @@ -193,8 +209,49 @@ currentForm.$setPristine(); } } + } + $scope.selectApp = function (app) { + vm.page.loading = true; + var payload = { + content: vm.stylesheet.content, + rules: vm.stylesheet.rules + }; + if (app.alias === "code") { + umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "stylesheetApiBaseUrl", + "PostInterpolateStylesheetRules"), + payload), + "Failed to interpolate sheet rules") + .then( + function(content) { + vm.page.loading = false; + vm.stylesheet.content = content; + }, + function(err) { + } + ); + } + else { + umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "stylesheetApiBaseUrl", + "PostExtractStylesheetRules"), + payload), + "Failed to extract style sheet rules") + .then( + function (rules) { + vm.page.loading = false; + vm.stylesheet.rules = rules; + }, + function(err) { + } + ); + } } init(); diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html index 61252f5a54..7daac56b38 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/edit.html @@ -13,19 +13,17 @@ hide-alias="true" description="vm.stylesheet.virtualPath" description-locked="true" + navigation="vm.page.navigation" + on-select-navigation-item="selectApp(item)" hide-icon="true"> - - -
-
-
-
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html new file mode 100644 index 0000000000..ce8b2c8b2f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/code/code.html @@ -0,0 +1,9 @@ + + +
+
+
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js new file mode 100644 index 0000000000..59293c1b9e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.controller.js @@ -0,0 +1,26 @@ +angular.module("umbraco").controller("Umbraco.Editors.StyleSheets.RulesController", + function ($scope) { + $scope.sortableOptions = { + axis: 'y', + containment: 'parent', + cursor: 'move', + items: '> div.control-group', + tolerance: 'pointer', + update: function (e, ui) { + // TODO + console.log("TODO: set dirty") + } + }; + + $scope.add = function (evt) { + evt.preventDefault(); + + $scope.model.stylesheet.rules.push({}); + } + + $scope.remove = function (rule, evt) { + evt.preventDefault(); + + $scope.model.stylesheet.rules = _.without($scope.model.stylesheet.rules, rule); + } + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html new file mode 100644 index 0000000000..4e6ba8c8a1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/views/rules/rules.html @@ -0,0 +1,51 @@ + + + + +
+
+
Rich text editor styles
+ Some explanatory text about rich text editor styles goes here +
+
+
+
+
+ +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ Remove +
+
+
+ +
+
+
+
+
diff --git a/src/Umbraco.Web/Editors/StylesheetController.cs b/src/Umbraco.Web/Editors/StylesheetController.cs index 99ab0add34..28b1930fb4 100644 --- a/src/Umbraco.Web/Editors/StylesheetController.cs +++ b/src/Umbraco.Web/Editors/StylesheetController.cs @@ -1,8 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Core; +using Umbraco.Core.Strings.Css; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; +using StylesheetRule = Umbraco.Web.Models.ContentEditing.StylesheetRule; namespace Umbraco.Web.Editors { @@ -30,6 +33,60 @@ namespace Umbraco.Web.Editors return css.Properties.Select(x => new StylesheetRule() { Name = x.Name, Selector = x.Alias }); } - } + public StylesheetRule[] PostExtractStylesheetRules(StylesheetData data) + { + if (data.Content.IsNullOrWhiteSpace()) + { + return new StylesheetRule[0]; + } + + return StylesheetHelper.ParseRules(data.Content)?.Select(rule => new StylesheetRule + { + Name = rule.Name, + Selector = rule.Selector, + Styles = rule.Styles + }).ToArray(); + } + + public string PostInterpolateStylesheetRules(StylesheetData data) + { + // first remove all existing rules + var existingRules = data.Content.IsNullOrWhiteSpace() + ? new Core.Strings.Css.StylesheetRule[0] + : StylesheetHelper.ParseRules(data.Content).ToArray(); + foreach (var rule in existingRules) + { + data.Content = StylesheetHelper.ReplaceRule(data.Content, rule.Name, null); + } + + data.Content = data.Content.TrimEnd('\n', '\r'); + + // now add all the posted rules + if (data.Rules != null && data.Rules.Any()) + { + foreach (var rule in data.Rules) + { + data.Content = StylesheetHelper.AppendRule(data.Content, new Core.Strings.Css.StylesheetRule + { + Name = rule.Name, + Selector = rule.Selector, + Styles = rule.Styles + }); + } + + data.Content += Environment.NewLine; + } + + return data.Content; + } + + // this is an internal class for passing stylesheet data from the client to the controller while editing + public class StylesheetData + { + public string Content { get; set; } + + public StylesheetRule[] Rules { get; set; } + } + } } diff --git a/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs b/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs index 9219cce4f5..b3212445ae 100644 --- a/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs +++ b/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs @@ -16,5 +16,7 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "selector")] public string Selector { get; set; } + [DataMember(Name = "styles")] + public string Styles { get; set; } } }