From f9d239ed03d0f1b0b957c746603f4bfd09b53cf8 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Fri, 3 Aug 2018 16:22:03 +0100 Subject: [PATCH 1/6] add basic docs for the customizations and remove reference of tool --- README.md | 2 +- docs/customizations.md | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 docs/customizations.md diff --git a/README.md b/README.md index d522f56ab..2b5dc56b6 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,9 @@ is to run `make` in the relevant module directory, with a Brooklyn REST server o For developers, the following links may be useful: * [Overview and architecture of the project](docs/overview.md) -* [Creating a new UI module](tools/module-generator/README.md) * [Building individual module / Dev environment](ui-modules/home/README.md) * [Skinning the UI with custom themes](docs/skinning.md) +* [Customizing and embedding the UI](docs/customizations.md) ## Troubleshooting diff --git a/docs/customizations.md b/docs/customizations.md new file mode 100644 index 000000000..4458a8838 --- /dev/null +++ b/docs/customizations.md @@ -0,0 +1,13 @@ + +The UI is designed so modules can be used individually, or added as library modules in the usual NodeJS manner +and widgets cherry-picked as needed. + +There are a number of points where UI default choices can be overridden: + +* Actions on Composer: by changing the config value for `'actionServiceProvider'` in `ui-modules/blueprint-composer/app/index.js`, + a different set of actions can be displayed on the composer screen + +* Composer - Virtual palette items and alternate catalog endpoints: by registering a different `blueprintServiceProvider` + items for the palette can come from Brooklyn and/or other sources, with other sources converted to "virtual items" that + can extend existing Brooklyn items + From 29ba70e1936367a4e226823add0a4272252b0922 Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Mon, 6 Aug 2018 22:59:11 +0100 Subject: [PATCH 2/6] initial JS poc of custom config widget all the hard angular stuff working, but hard-coded. now just need to wire it to real tags. --- docs/customizations.md | 8 +++ .../suggestion-dropdown.html | 48 ++++++++++++++ .../suggestion-dropdown.js | 44 +++++++++++++ .../suggestion-dropdown.less | 30 +++++++++ .../spec-editor/spec-editor.directive.js | 64 ++++++++++++++++++- .../components/spec-editor/spec-editor.less | 26 +++++++- .../spec-editor/spec-editor.template.html | 22 +++++-- ui-modules/blueprint-composer/app/index.js | 6 +- ui-modules/blueprint-composer/app/index.less | 1 + 9 files changed, 238 insertions(+), 11 deletions(-) create mode 100644 ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html create mode 100644 ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js create mode 100644 ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.less diff --git a/docs/customizations.md b/docs/customizations.md index 4458a8838..991231717 100644 --- a/docs/customizations.md +++ b/docs/customizations.md @@ -11,3 +11,11 @@ There are a number of points where UI default choices can be overridden: items for the palette can come from Brooklyn and/or other sources, with other sources converted to "virtual items" that can extend existing Brooklyn items +* Composer - Custom Config Widgets: special widgets to use for config keys can be specified in a registered type's + definition as a map tag, for example for the demo widget `suggestion-dropout` included we might have: + '{ ui-composer-hints: { config-widgets: [ { key: start.timeout, suggestion-values: [ 30s, 2m, 5m, 30m, 2h ], + widget: suggestion-dropdown, label-collapsed: fail after, label-expanded: Fail if not successful within } ] } }`; + widgets should be registered as angular directives using the standard naming conventions (e.g. suggestionDropdownDirective), + as done for that directive in app/index.js and app/index.less. + + diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html new file mode 100644 index 000000000..c511dfee5 --- /dev/null +++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html @@ -0,0 +1,48 @@ + +
+ + + + + + + {{getCustomConfigWidgetModeTitle(item)}} [{{item.name}}] + + + +
+ +
+ Custom widget: + {{ config[item.name] }} +
+
+ +
\ No newline at end of file diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js new file mode 100644 index 000000000..d36c801da --- /dev/null +++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import angular from 'angular'; +import template from './suggestion-dropdown.html'; + +const MODULE_NAME = 'brooklyn.components.custom-config-widget.suggestion-dropdown'; + +angular.module(MODULE_NAME, []) + .directive('suggestionDropdown', ['$rootScope', suggestionDropdownDirective]); + +export default MODULE_NAME; + +export function suggestionDropdownDirective($rootScope) { + return { + restrict: 'E', + scope: { + item: '=' + }, + template: template, + link: link, + }; + + function link(scope) { + scope.$parent.copyScopeForCustomConfigWidget(scope); + } + +} \ No newline at end of file diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.less b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.less new file mode 100644 index 000000000..c080a1b0d --- /dev/null +++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.less @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +suggestion-dropdown { + + width: 100%; + + .control-value { + // for widgets that are text, this aligns it with the label when collapsed and gives nice spacing when expanded. + // (for widgets that are dropdowns etc this should be omitted) + padding-top: 2px; + } + +} diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js index 7a581b7d5..a0eeba80d 100644 --- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js +++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js @@ -33,7 +33,7 @@ const ANY_MEMBERSPEC_REGEX = /(^.*[m,M]ember[s,S]pec$)/; const REPLACED_DSL_ENTITYSPEC = '___brooklyn:entitySpec'; angular.module(MODULE_NAME, [onEnter, autoGrow, blurOnEnter, brooklynDslEditor, brooklynDslViewer]) - .directive('specEditor', ['$rootScope', '$filter', '$log', '$sce', '$timeout', '$document', 'blueprintService', specEditorDirective]) + .directive('specEditor', ['$rootScope', '$templateCache', '$injector', '$sanitize', '$filter', '$log', '$sce', '$timeout', '$document', 'blueprintService', specEditorDirective]) .filter('specEditorConfig', specEditorConfigFilter) .filter('specEditorType', specEditorTypeFilter); @@ -70,7 +70,7 @@ export const CONFIG_FILTERS = [ } ]; -export function specEditorDirective($rootScope, $filter, $log, $sce, $timeout, $document, blueprintService) { +export function specEditorDirective($rootScope, $templateCache, $injector, $sanitize, $filter, $log, $sce, $timeout, $document, blueprintService) { return { restrict: 'E', scope: { @@ -106,6 +106,10 @@ export function specEditorDirective($rootScope, $filter, $log, $sce, $timeout, $ codeModeActive: {}, codeModeForced: {}, codeModeError: {}, + customWidget: { + "defaultDisplayName": { mode: "full", widget: "known-widget-missing" }, + "download.url": { mode: "full", widget: "suggestion-dropdown" }, + }, }, location: { open: false @@ -514,7 +518,7 @@ export function specEditorDirective($rootScope, $filter, $log, $sce, $timeout, $ // local config changed, make sure model is updated too setModelFromLocalConfig(); } - } + }; scope.getJsonModeTitle = (itemName) => { if (!scope.state.config.codeModeActive[itemName]) { return "Treat this value as a JSON-encoded object ["+itemName+"]"; @@ -525,6 +529,60 @@ export function specEditorDirective($rootScope, $filter, $log, $sce, $timeout, $ return "Edit in simple mode, unwrapping JSON if possible ["+itemName+"]"; } } + scope.getCustomConfigWidgetMode = (item) => { + var mode = (scope.state.config.customWidget[item.name] || {})["mode"] || null; + if (mode) { + if ((scope.state.config.customWidget[item.name] || {})["error"]) return null; + } + return mode; + } ; + scope.toggleCustomConfigWidgetMode = (item, newval) => { + if (!scope.state.config.customWidget[item.name]) { + $log.error('Custom widget mode should not be toggled when not available: '+item.name); + return null; + } + if (newval) scope.state.config.customWidget[item.name].mode = newval; + else { + if (scope.state.config.customWidget[item.name].mode === "disabled") { + // TODO not always full + scope.state.config.customWidget[item.name].mode = "full" + } else { + scope.state.config.customWidget[item.name].mode = "full" + } + } + } + scope.getCustomConfigWidgetModeTitle = (item) => { + if (!scope.state.config.customWidget[item.name]) { + // shouldn't be visible + return "(custom widget not available)"; + } + if (scope.state.config.customWidget[item.name].mode === "disabled") return "Enable custom widget"; + else return "Disable custom widget"; + }; + scope.copyScopeForCustomConfigWidget = (descendantScope) => { + descendantScope.toggleCustomConfigWidgetMode = scope.toggleCustomConfigWidgetMode; + descendantScope.getCustomConfigWidgetModeTitle = scope.getCustomConfigWidgetModeTitle; + descendantScope.defined = scope.defined; + descendantScope.config = scope.config; + descendantScope.state = scope.state; + descendantScope.copyScopeForCustomConfigWidget = scope.copyScopeForCustomConfigWidget; + }; + scope.getCustomConfigWidgetTemplate = (item) => { + var widgetName = $sanitize(scope.state.config.customWidget[item.name].widget); + var templateName = 'custom-config-widget-'+widgetName; + if (!$templateCache.get(templateName)) { + var widgetDirective = widgetName.replace(/(-[a-z])/g, function($1){return $1[1].toUpperCase();})+'Directive'; + if ($injector.has(widgetDirective)) { + $templateCache.put(templateName, '<'+widgetName+' item="item">foo'); + } else { + $log.error('Missing directive '+widgetDirective+' for custom widget for '+item.name+'; falling back to default widget'); + scope.state.config.customWidget[item.name].error = "Missing directive"; + templateName = "error-" + templateName; + $templateCache.put(templateName, 'Widget '+widgetName+' missing'); + } + } + return templateName; + }; scope.isDsl = (key, index) => { let val = scope.model.config.get(key); diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less index 1c14c922e..233d93750 100644 --- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less +++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less @@ -272,12 +272,23 @@ spec-editor { margin-right: 0.5em; .remove-affordance(); } + span.rhs-buttons-subspan { + display: flex; + } .json-config i { margin-top: 2px; //nudge down to make aligned } - .dsl-open-wizard-from-toolbar, .dsl-manual-override-editor, .json-config { + .dsl-open-wizard-from-toolbar, .dsl-manual-override-editor, .json-config, .custom-widget-enable-button { .toolbar-button-affordance() } + .custom-widget-enable-button.custom-widget-active { + i { + color: @brand-primary; + } + &:hover i { + color: @gray-light; + } + } .dsl-manual-override-editor.dsl-viewer-manual-override, .json-config.code-mode-active { i { @@ -327,8 +338,11 @@ spec-editor { } } .form-group { - display: flex; - flex-flow: row wrap; + &, .config-flex-row, .custom-widget-full { + display: flex; + flex-flow: row wrap; + width: 100%; + } .control-label { flex: 0 0 auto; order: 10; @@ -406,6 +420,9 @@ spec-editor { } } &:not(:focus-within):not(:focus) { + .hide-when-collapsed { + display: none; + } input { // the RHS is square if DSL button hidden; simplest fix is to make LHS square too border-radius: 0; @@ -426,6 +443,9 @@ spec-editor { } } .form-group:focus, .form-group:focus-within { + .hide-when-expanded { + display: none; + } .control-value:not(.inline-control) { flex-basis: 100%; } diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html index b305651c6..cc3e11ebc 100644 --- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html +++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html @@ -131,12 +131,20 @@

No matching configuration

-
+
+ +
+ + +
+
+
diff --git a/ui-modules/blueprint-composer/app/index.js b/ui-modules/blueprint-composer/app/index.js index f7a55b23d..a970dc232 100755 --- a/ui-modules/blueprint-composer/app/index.js +++ b/ui-modules/blueprint-composer/app/index.js @@ -48,6 +48,7 @@ import { catalogSelectorSortFilter } from "components/catalog-selector/catalog-selector.directive"; import customActionDirective from "./components/custom-action/custom-action.directive"; +import customConfigSuggestionDropdown from "components/custom-config-widget/suggestion-dropdown"; import {onErrorDirective} from "components/catalog-selector/on-error.directive"; import {breadcrumbsDirective} from "components/breacrumbs/breadcrumbs.directive"; import {recursionHelperFactory} from "components/factories/recursion-helper.factory"; @@ -71,7 +72,10 @@ import {graphicalEditDslState, dslParamLabelFilter} from "views/main/graphical/e import bottomSheet from "brooklyn-ui-utils/bottom-sheet/bottom-sheet"; import stackViewer from 'angular-java-stack-viewer'; -angular.module('app', [ngAnimate, ngResource, ngCookies, ngClipboard, uiRouter, 'ui.router.state.events', brCore, brServerStatus, brAutoFocus, brIconGenerator, brInterstitialSpinner, brooklynModuleLinks, brooklynUserManagement, brYamlEditor, brUtils, brSpecEditor, brooklynCatalogSaver, brooklynApi, bottomSheet, stackViewer, brDragndrop, customActionDirective, paletteApiProvider]) +angular.module('app', [ngAnimate, ngResource, ngCookies, ngClipboard, uiRouter, 'ui.router.state.events', brCore, + brServerStatus, brAutoFocus, brIconGenerator, brInterstitialSpinner, brooklynModuleLinks, brooklynUserManagement, + brYamlEditor, brUtils, brSpecEditor, brooklynCatalogSaver, brooklynApi, bottomSheet, stackViewer, brDragndrop, + customActionDirective, customConfigSuggestionDropdown, paletteApiProvider]) .directive('designer', ['$log', '$state', '$q', 'iconGenerator', 'catalogApi', 'blueprintService', 'brSnackbar', 'paletteDragAndDropService', designerDirective]) .directive('onError', onErrorDirective) .directive('catalogSelector', catalogSelectorDirective) diff --git a/ui-modules/blueprint-composer/app/index.less b/ui-modules/blueprint-composer/app/index.less index 5baf074c1..1562b4069 100644 --- a/ui-modules/blueprint-composer/app/index.less +++ b/ui-modules/blueprint-composer/app/index.less @@ -31,6 +31,7 @@ @import "components/spec-editor/spec-editor"; @import "components/catalog-selector/catalog-selector"; @import "components/breacrumbs/breadcrumbs"; +@import "components/custom-config-widget/suggestion-dropdown"; @import "components/blueprint-data-manager/blueprint-data-manager.style.less"; @import "components/catalog-saver/catalog-saver.less"; @import "components/dsl-editor/dsl-editor"; From 19de59de10894f8a3720c989d0e3be7fd68b260a Mon Sep 17 00:00:00 2001 From: Alex Heneveld Date: Tue, 7 Aug 2018 01:18:01 +0100 Subject: [PATCH 3/6] collect config-widget metadata from ui-composer-hints tags on registered types all that's left is to show the suggested dropdown values --- .../suggestion-dropdown.html | 6 +- .../suggestion-dropdown.js | 4 +- .../providers/blueprint-service.provider.js | 16 +++++ .../spec-editor/spec-editor.directive.js | 63 +++++++++++-------- .../components/spec-editor/spec-editor.less | 2 +- .../spec-editor/spec-editor.template.html | 6 +- 6 files changed, 63 insertions(+), 34 deletions(-) diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html index c511dfee5..4ca05cc38 100644 --- a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html +++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.html @@ -30,17 +30,17 @@ - + {{getCustomConfigWidgetModeTitle(item)}} [{{item.name}}]
- Extra information + {{ params['label-expanded'] || "" }}
- Custom widget: + {{ params['label-collapsed'] || "" }} {{ config[item.name] }}
diff --git a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js index d36c801da..7c230927a 100644 --- a/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js +++ b/ui-modules/blueprint-composer/app/components/custom-config-widget/suggestion-dropdown.js @@ -31,7 +31,8 @@ export function suggestionDropdownDirective($rootScope) { return { restrict: 'E', scope: { - item: '=' + item: '=', + params: '=', }, template: template, link: link, @@ -39,6 +40,7 @@ export function suggestionDropdownDirective($rootScope) { function link(scope) { scope.$parent.copyScopeForCustomConfigWidget(scope); + console.log("params for widget for "+scope.item.name, scope.params); } } \ No newline at end of file diff --git a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js index 0e4b3601a..9f2491a8d 100644 --- a/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js +++ b/ui-modules/blueprint-composer/app/components/providers/blueprint-service.provider.js @@ -499,10 +499,26 @@ function BlueprintService($log, $q, $sce, paletteApi, iconGenerator, dslService) })); entity.miscData.set('sensors', data.sensors || []); entity.miscData.set('traits', data.supertypes || []); + entity.miscData.set('tags', data.tags || []); + var uiHints = {}; + data.tags.forEach( (t) => { + mergeAppendingLists(uiHints, t['ui-composer-hints']); + }); + entity.miscData.set('ui-composer-hints', uiHints); entity.miscData.set('virtual', data.virtual || null); addUnlistedConfigKeysDefinitions(entity); return entity; } + function mergeAppendingLists(dst, src) { + for (let p in src) { + if (Array.isArray(dst[p]) || Array.isArray(src[p])) { + dst[p] = [].concat(dst[p] || [], src[p]); + } else { + dst[p] = Object.assign({}, dst[p], src[p]); + } + } + return dst; + } function populateEntityFromApiError(entity) { entity.clearIssues({group: 'type'}); diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js index a0eeba80d..17ab03a57 100644 --- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js +++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.directive.js @@ -106,9 +106,9 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani codeModeActive: {}, codeModeForced: {}, codeModeError: {}, - customWidget: { - "defaultDisplayName": { mode: "full", widget: "known-widget-missing" }, - "download.url": { mode: "full", widget: "suggestion-dropdown" }, + customConfigWidgetMetadata: { + "defaultDisplayName": { enabled: true, widget: "known-widget-missing" }, + "download.url": { enabled: true, widget: "suggestion-dropdown" }, }, }, location: { @@ -148,6 +148,8 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani scope.state.config.add.list = getAddListConfig(); }); + loadCustomConfigWidgetMetadata(scope); + scope.config = {}; scope.$watch('model', (newVal, oldVal)=> { if (newVal && !newVal.equals(oldVal)) { @@ -528,36 +530,29 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani } else { return "Edit in simple mode, unwrapping JSON if possible ["+itemName+"]"; } - } + }; + /** returns 'enabled' or 'disabled' if a widget is defined, or null if no special widget is defined */ scope.getCustomConfigWidgetMode = (item) => { - var mode = (scope.state.config.customWidget[item.name] || {})["mode"] || null; - if (mode) { - if ((scope.state.config.customWidget[item.name] || {})["error"]) return null; - } - return mode; - } ; + var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name]; + if (!widgetMetadata || widgetMetadata["error"]) return null; + return widgetMetadata["enabled"] ? 'enabled' : 'disabled'; + }; scope.toggleCustomConfigWidgetMode = (item, newval) => { - if (!scope.state.config.customWidget[item.name]) { + var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name]; + if (!widgetMetadata) { $log.error('Custom widget mode should not be toggled when not available: '+item.name); return null; } - if (newval) scope.state.config.customWidget[item.name].mode = newval; - else { - if (scope.state.config.customWidget[item.name].mode === "disabled") { - // TODO not always full - scope.state.config.customWidget[item.name].mode = "full" - } else { - scope.state.config.customWidget[item.name].mode = "full" - } - } + if (!scope.defined(newval)) newval = !widgetMetadata.enabled; + widgetMetadata.enabled = newval; } scope.getCustomConfigWidgetModeTitle = (item) => { - if (!scope.state.config.customWidget[item.name]) { + var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name]; + if (!widgetMetadata) { // shouldn't be visible return "(custom widget not available)"; } - if (scope.state.config.customWidget[item.name].mode === "disabled") return "Enable custom widget"; - else return "Disable custom widget"; + return widgetMetadata.enabled ? "Use standard widget" : "Use custom widget"; }; scope.copyScopeForCustomConfigWidget = (descendantScope) => { descendantScope.toggleCustomConfigWidgetMode = scope.toggleCustomConfigWidgetMode; @@ -568,15 +563,16 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani descendantScope.copyScopeForCustomConfigWidget = scope.copyScopeForCustomConfigWidget; }; scope.getCustomConfigWidgetTemplate = (item) => { - var widgetName = $sanitize(scope.state.config.customWidget[item.name].widget); + var widgetMetadata = scope.state.config.customConfigWidgetMetadata[item.name]; + var widgetName = $sanitize(widgetMetadata.widget || '--no-widget--'); var templateName = 'custom-config-widget-'+widgetName; if (!$templateCache.get(templateName)) { var widgetDirective = widgetName.replace(/(-[a-z])/g, function($1){return $1[1].toUpperCase();})+'Directive'; if ($injector.has(widgetDirective)) { - $templateCache.put(templateName, '<'+widgetName+' item="item">foo'); + $templateCache.put(templateName, '<'+widgetName+' item="item" params="state.config.customConfigWidgetMetadata[item.name]"/>'); } else { $log.error('Missing directive '+widgetDirective+' for custom widget for '+item.name+'; falling back to default widget'); - scope.state.config.customWidget[item.name].error = "Missing directive"; + scope.state.config.customConfigWidgetMetadata[item.name].error = "Missing directive"; templateName = "error-" + templateName; $templateCache.put(templateName, 'Widget '+widgetName+' missing'); } @@ -615,6 +611,21 @@ export function specEditorDirective($rootScope, $templateCache, $injector, $sani }); } + function loadCustomConfigWidgetMetadata(model) { + console.log("misc data", model.miscData, scope.model.miscData.get('ui-composer-hints')); + var customConfigWidgets = (scope.model.miscData.get('ui-composer-hints') || {})['config-widgets'] || []; + console.log("customs", customConfigWidgets); + customConfigWidgets.forEach( (wd) => { + console.log("looking at", wd); + var keys = wd.keys || [ wd.key ]; + keys.forEach( (k) => { + console.log("setting key", k); + scope.state.config.customConfigWidgetMetadata[k] = angular.extend({ enabled: true }, scope.state.config.customConfigWidgetMetadata[k], wd); + }); + }); + console.log("custom config", scope.state.config.customConfigWidgetMetadata); + } + /* config state for each item is stored in multiple places: * * scope.config = map of values used/set by editor (strings, map of strings, json code if using code mode, etc); * this should be suitable for ng-model to work with, so e.g. if using code mode we need to put JSON.stringify value in here, diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less index 233d93750..da74f0f19 100644 --- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less +++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.less @@ -338,7 +338,7 @@ spec-editor { } } .form-group { - &, .config-flex-row, .custom-widget-full { + &, .config-flex-row, .custom-config-widget { display: flex; flex-flow: row wrap; width: 100%; diff --git a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html index cc3e11ebc..740d295ce 100644 --- a/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html +++ b/ui-modules/blueprint-composer/app/components/spec-editor/spec-editor.template.html @@ -139,12 +139,12 @@

No matching configuration

auto-focus-not-if-within="true" ng-focus="recordFocus(item)" on-enter="advanceControlInFormGroup"> -
- +
+
-
+