From 0d4c874d8482f53fd641516c992aaa2bcb499150 Mon Sep 17 00:00:00 2001 From: CSchmitz81 Date: Sun, 7 May 2017 21:53:31 -0700 Subject: [PATCH 1/2] initial: added support for showing segments in element dialog --- src/ctree-dialogs/ctree-dialogs.html | 34 +- .../ctree-element-dialog-accessor.html | 23 +- .../ctree-element-preview.html | 115 +------ .../ctree-description.html | 300 ++++++++++++++++++ .../ctree-details-page.html | 74 ++--- .../ctree-element-screen.html | 36 +-- .../ctree-segment-container.html | 264 +++++++++++++++ .../ctree-element-description-behavior.html | 160 ++++++++++ .../ctree-element-loader-test.html | 5 +- .../ctree-loader-behavior-test.html | 36 ++- src/ctree-segments/ctree-segment-image.html | 1 + 11 files changed, 838 insertions(+), 210 deletions(-) create mode 100644 src/ctree-element-screen/ctree-description.html create mode 100644 src/ctree-element-screen/ctree-segment-container.html create mode 100644 src/ctree-element/ctree-element-description-behavior.html diff --git a/src/ctree-dialogs/ctree-dialogs.html b/src/ctree-dialogs/ctree-dialogs.html index fa60a4a..4e4cbfb 100644 --- a/src/ctree-dialogs/ctree-dialogs.html +++ b/src/ctree-dialogs/ctree-dialogs.html @@ -77,7 +77,7 @@ - + @@ -122,9 +122,9 @@ observer: '_descriptionConfigIdChanged', }, - descriptionSegmentVariationIds: { + descriptionSegmentIds: { type: Array, - observer: '_descriptionSegmentVariationIdsChanged', + observer: '_descriptionSegmentIdsChanged', }, feedbackSegment: Number, @@ -206,10 +206,10 @@ } else if (this.queryParams.hasOwnProperty('c')) { delete this.queryParams.c; } - if (isElement && this.descriptionSegmentVariationIds && this.descriptionSegmentVariationIds.length > 0) { + if (isElement && this.descriptionSegmentIds && this.descriptionSegmentIds.length > 0) { // TODO: use this for Polymer 2.0 - //this.queryParams.s = this.descriptionSegmentVariationIds.join(','); - this.set('queryParams.s', this.descriptionSegmentVariationIds.join(',')); + //this.queryParams.s = this.descriptionSegmentIds.join(','); + this.set('queryParams.s', this.descriptionSegmentIds.join(',')); } else if (this.queryParams.hasOwnProperty('s')) { delete this.queryParams.s; } @@ -283,19 +283,19 @@ } }, - _descriptionSegmentVariationsParamChanged: function(descriptionSegmentVariationIdsStr) { - if (descriptionSegmentVariationIdsStr) { - var descriptionSegmentVariationIds = descriptionSegmentVariationIdsStr.split(','); - for (var i = 0; i < descriptionSegmentVariationIds.length; i++) { - var id = parseInt(descriptionSegmentVariationIds[i]); + _descriptionSegmentVariationsParamChanged: function(descriptionSegmentIdsStr) { + if (descriptionSegmentIdsStr) { + var descriptionSegmentIds = descriptionSegmentIdsStr.split(','); + for (var i = 0; i < descriptionSegmentIds.length; i++) { + var id = parseInt(descriptionSegmentIds[i]); if (id != NaN) { - descriptionSegmentVariationIds[i] = id; + descriptionSegmentIds[i] = id; } else { - descriptionSegmentVariationIds.splice(i, 1); + descriptionSegmentIds.splice(i, 1); i--; } } - this.descriptionSegmentVariationIds = descriptionSegmentVariationIds; + this.descriptionSegmentIds = descriptionSegmentIds; } else if (this.queryParams.d === 'element' && this.queryParams.hasOwnProperty('s')) { delete this.queryParams.s; @@ -367,10 +367,10 @@ // TODO: }, - _descriptionSegmentVariationIdsChanged: function(descriptionSegmentVariationIds) { + _descriptionSegmentIdsChanged: function(descriptionSegmentIds) { if (this.queryParams.d === 'element') { - if (descriptionSegmentVariationIds.length > 0) { - this.set('queryParams.s', descriptionSegmentVariationIds.join(',')); + if (descriptionSegmentIds.length > 0) { + this.set('queryParams.s', descriptionSegmentIds.join(',')); } else if (this.queryParams.hasOwnProperty('s')) { delete this.queryParams.s; // TODO: use this for Polymer 2.0 diff --git a/src/ctree-dialogs/ctree-element-dialog-accessor.html b/src/ctree-dialogs/ctree-element-dialog-accessor.html index 80965c2..ae38edc 100644 --- a/src/ctree-dialogs/ctree-element-dialog-accessor.html +++ b/src/ctree-dialogs/ctree-element-dialog-accessor.html @@ -61,7 +61,10 @@ * segments: [{ * id: Number, * type: SegmentType, - * variations: [], // data type depends on type + * variations: [{ + * id: Number, + * data: dynamic, // data type depends on segment type + * }, ...], * }, ...], * }, ...], * feedback: [{ @@ -71,19 +74,11 @@ * bookmarked: Boolean, * } */ - element: { - type: Object, - }, - - descriptionConfigId: { - type: Number, - observer: '_descriptionConfigIdChanged', - }, - - descriptionSegmentIds: { - type: Array, - observer: '_descriptionSegmentIdsChanged', - }, + element: Object, + + descriptionConfigId: Number, + + descriptionSegmentIds: Array, feedbackSegment: Number, diff --git a/src/ctree-element-preview/ctree-element-preview.html b/src/ctree-element-preview/ctree-element-preview.html index e14e921..1e1089f 100644 --- a/src/ctree-element-preview/ctree-element-preview.html +++ b/src/ctree-element-preview/ctree-element-preview.html @@ -34,6 +34,7 @@ + @@ -117,7 +118,7 @@ - +
@@ -139,6 +140,10 @@ Polymer({ is: 'ctree-element-preview', + behaviors: [ + Polymer.CTreeElementDescriptionBehavior, + ], + properties: { /** * The unique key identifying a cTree. If the site only has one cTree @@ -166,60 +171,6 @@ observer: '_listItemChanged', }, - /** - * Element data - * - * Structure: - * { - * id: Number, - * type: ElementType, - * title: String, - * parents: [Number], - * children: [Number], - * childrenSearchComplete: Boolean, - * designer: String, // TODO: should probably be an object (user ID or reference to public user object) - * description: [{ - * id: Number, - * contributors: [String], // TODO: should probably be an object (user ID or reference to public user object) - * segments: [{ - * id: Number, - * type: SegmentType, - * variations: [], // data type depends on type - * }, ...], - * }, ...], - * feedback: [{ - * reviewer: String, // TODO: should probably be an object (user ID or reference to public user object) - * text: String, - * }, ...], - * bookmarked: Boolean, - * } - */ - element: { - type: Object, - observer: '_elementChanged', - }, - - /** - * ID of description config to show. If this ID isn't included in the - * element the first config from the element is used. - */ - descriptionConfig: { - type: Number, - observer: '_descriptionConfigChanged', - }, - - /** - * IDs of variation to show for each segment. For any segments where the - * ID isn't included in the description config the first variation is used. - * - * Structure: - * [Number, ...] - */ - descriptionSegments: { - type: Array, - observer: '_descriptionSegmentsChanged', - }, - /** * Comment to display at the bottom of the element preview. */ @@ -240,8 +191,6 @@ value: false, observer: '_viewedChanged', }, - - _bulkChange: Boolean, }, _isEmpty: function(str) { @@ -249,57 +198,21 @@ }, _listItemChanged: function(listItem) { - var descriptionChanged = false; - - this._bulkChange = true; - if (typeof listItem.element !== 'undefined' && listItem.element !== this.element) { - this.element = listItem.element; - descriptionChanged = true; - } - if (typeof listItem.descriptionConfig !== 'undefined' && listItem.descriptionConfig !== this.descriptionConfig) { - this.descriptionConfig = listItem.descriptionConfig; - descriptionChanged = true; - } - if (typeof listItem.descriptionSegments !== 'undefined' && listItem.descriptionSegments !== this.descriptionSegments) { - this.descriptionSegments = listItem.descriptionSegments; - descriptionChanged = true; - } - this._bulkChange = false; - - if (descriptionChanged) { - this._updateDescription(this.element, this.descriptionConfig, this.descriptionSegments); - } + this._bulkUpdateDescriptionProperties(listItem.element, listItem.descriptionConfig, listItem.descriptionSegments); this.viewed = listItem.viewed; }, _elementChanged: function(element) { - // TODO: remove this call once make _updateDescription an explicit observer after updating to Polymer 2.0 - this._updateDescription(element, this.descriptionConfig, this.descriptionSegments); - this.viewed = element.viewed; }, - // TODO: remove these individual observers and make _updateDescription an explicit observer once update to Polymer 2.0 - _descriptionConfigChanged: function(descriptionConfig) { - this._updateDescription(this.element, descriptionConfig, this.descriptionSegments); - }, - - _descriptionSegmentsChanged: function(descriptionSegments) { - this._updateDescription(this.element, this.descriptionConfig, descriptionSegments); - }, - - _getFromArrayWithId: Polymer.CTreeLoader.getFromArrayWithId, - - _updateDescription: function(element, descriptionConfig, descriptionSegments) { - if (this._bulkChange || !this.element.description || this.element.description.length == 0) return; - var description = this._getFromArrayWithId(this.element.description, this.descriptionConfig); - if (!description || !description.segments || (descriptionSegments && description.segments.length != descriptionSegments.length)) return; - + _descriptionChanged: function(description, descriptionSegments) { // clear segments var segmentsElement = this.$.segments; - while (segmentsElement.firstChild) { - segmentsElement.removeChild(segmentsElement.firstChild); + var child; + while (child = segmentsElement.firstChild) { + segmentsElement.removeChild(child); } // add thumbnail @@ -311,7 +224,8 @@ if (type.canBeThumbnail) { thumbnailIndex = i; var component = document.createElement(type.componentName); - component.data = this._getFromArrayWithId(segment.variations, descriptionSegments ? descriptionSegments[i] : undefined); + var variation = this._getFromArrayWithId(segment.variations, descriptionSegments ? descriptionSegments[i] : undefined); + component.data = variation.data; component.scale = '80%'; component.heightPercent = '56.25%'; segmentsElement.appendChild(component); @@ -327,7 +241,8 @@ var segment = segments[i]; var type = segment.type; var component = document.createElement(type.componentName); - component.data = this._getFromArrayWithId(segment.variations, descriptionSegments ? descriptionSegments[i] : undefined); + var variation = this._getFromArrayWithId(segment.variations, descriptionSegments ? descriptionSegments[i] : undefined); + component.data = variation.data; component.scale = '80%'; segmentsElement.appendChild(component); } diff --git a/src/ctree-element-screen/ctree-description.html b/src/ctree-element-screen/ctree-description.html new file mode 100644 index 0000000..d244c69 --- /dev/null +++ b/src/ctree-element-screen/ctree-description.html @@ -0,0 +1,300 @@ + + + + + + + + + + + + + diff --git a/src/ctree-element-screen/ctree-details-page.html b/src/ctree-element-screen/ctree-details-page.html index fd47cd7..1391230 100644 --- a/src/ctree-element-screen/ctree-details-page.html +++ b/src/ctree-element-screen/ctree-details-page.html @@ -31,8 +31,10 @@ + + @@ -123,20 +88,31 @@ * @param {{segment: segmentData}} detail Contains data for segment whos feedback button was tapped. */ + behaviors: [ + Polymer.CTreeElementDescriptionBehavior, + ], + properties: { + /** + * Segments data + * + * Structure: + * [{ + * id: Number, + * type: SegmentType, + * variations: [{ + * id: Number, + * data: dynamic, // data type depends on segment type + * }, ...], + * }, ...], + */ segments: { type: Array, - value: ["", "", ""], - }, - - descriptionConfigId: { - type: Number, - observer: '_onDescriptionConfigChanged', }, }, - _onDescriptionConfigChanged: function(descriptionConfigId) { - // TODO: refresh description details + _descriptionChanged: function(description, descriptionSegments) { + this.segments = description.segments; // TODO: copy array using .slice() so we can mutate it separately? }, _fireContributorsTapped: function() { diff --git a/src/ctree-element-screen/ctree-element-screen.html b/src/ctree-element-screen/ctree-element-screen.html index 2f55b23..2f739cd 100644 --- a/src/ctree-element-screen/ctree-element-screen.html +++ b/src/ctree-element-screen/ctree-element-screen.html @@ -87,15 +87,15 @@ - + - - - + + + - + @@ -137,15 +137,15 @@ notify: true, }, - descriptionConfigId: { + descriptionConfig: { type: Number, - observer: '_descriptionConfigIdChanged', + observer: '_descriptionConfigChanged', notify: true, }, - descriptionSegmentVariationIds: { + descriptionSegments: { type: Array, - observer: '_descriptionSegmentVariationIdsChanged', + observer: '_descriptionSegmentsChanged', notify: true, }, @@ -180,7 +180,7 @@ this._ensureDescriptionConfigSet(); } else if (page == 'related') { // applies to entire element so enter general state instead of specific description config state - this.descriptionConfigId = null; + this.descriptionConfig = null; } this.$.importPage.load(); }, @@ -189,21 +189,21 @@ if (element) { this.elementId = element.id; } - if (!this.descriptionConfigId && element) { + if (!this.descriptionConfig && element) { this._pageChanged(this.page); } // TODO: }, - _descriptionConfigIdChanged: function(descriptionConfigId) { - this._descriptionVersionBarVisible = (descriptionConfigId ? true : false); - if (!descriptionConfigId && this.element) { + _descriptionConfigChanged: function(descriptionConfig) { + this._descriptionVersionBarVisible = (descriptionConfig ? true : false); + if (!descriptionConfig && this.element) { this._pageChanged(this.page); } // TODO: }, - _descriptionSegmentVariationIdsChanged: function(descriptionSegmentVariationIds) { + _descriptionSegmentsChanged: function(descriptionSegments) { // TODO: }, @@ -219,9 +219,9 @@ }, _ensureDescriptionConfigSet: function() { - if (!this.descriptionConfigId) { + if (!this.descriptionConfig) { // TODO: get first config ID from element data - this.descriptionConfigId = 1; + this.descriptionConfig = 1; } }, @@ -243,7 +243,7 @@ this.historyContributor = detail.contributor; if (detail.generalHistory) { // history shown should be for the entire element, not a specific description config - this.descriptionConfigId = null; + this.descriptionConfig = null; } this.page = 'history'; }, diff --git a/src/ctree-element-screen/ctree-segment-container.html b/src/ctree-element-screen/ctree-segment-container.html new file mode 100644 index 0000000..244e42c --- /dev/null +++ b/src/ctree-element-screen/ctree-segment-container.html @@ -0,0 +1,264 @@ + + + + + + + + + + + + diff --git a/src/ctree-element/ctree-element-description-behavior.html b/src/ctree-element/ctree-element-description-behavior.html new file mode 100644 index 0000000..d4cd2d1 --- /dev/null +++ b/src/ctree-element/ctree-element-description-behavior.html @@ -0,0 +1,160 @@ + + + + + + + + + diff --git a/src/ctree-loader/ctree-element-loader-test.html b/src/ctree-loader/ctree-element-loader-test.html index 5955043..4f5e15d 100644 --- a/src/ctree-loader/ctree-element-loader-test.html +++ b/src/ctree-loader/ctree-element-loader-test.html @@ -110,7 +110,10 @@ * segments: [{ * id: Number, * type: SegmentType, - * variations: [], // data type depends on type + * variations: [{ + * id: Number, + * data: dynamic, // data type depends on segment type + * }, ...], * }, ...], * }, ...], * feedback: [{ diff --git a/src/ctree-loader/ctree-loader-behavior-test.html b/src/ctree-loader/ctree-loader-behavior-test.html index 490e1ba..dc0df17 100644 --- a/src/ctree-loader/ctree-loader-behavior-test.html +++ b/src/ctree-loader/ctree-loader-behavior-test.html @@ -40,6 +40,7 @@ // monostate data var cTreesData = {}; var globalUsersData = {}; + var nextVariationId = 1; // test configuration values var Test = { @@ -218,27 +219,27 @@ var element = testElements[i]; var description = []; - for (var j = Math.floor(Math.random() * (Test.MAX_DESCRIPTION_CONFIGS - 1)) + 1; j >= 0; j--) { + for (var j = Math.floor(Math.random() * (Test.MAX_DESCRIPTION_CONFIGS - 1)); j >= 0; j--) { var segments = []; var contributors = []; // TODO: add contributors, ordered from most contributions to least (pick contributors for each segment added, also include feedback) - for (var k = Math.floor(Math.random() * (Test.MAX_DESCRIPTION_SEGMENTS - 1)) + 1; k >= 0; k--) { + for (var k = Math.floor(Math.random() * (Test.MAX_DESCRIPTION_SEGMENTS - 1)); k >= 0; k--) { var descriptionSegmentTypeId = Math.floor(Math.random() * (segmentTypes.length - 1)) + 1; // no ID 0 var descriptionSegmentVariations = []; - for (var l = Math.floor(Math.random() * (Test.MAX_DESCRIPTION_SEGMENT_VARIATIONS - 1)) + 1; l >= 0; l--) { - var segment; + for (var l = Math.floor(Math.random() * (Test.MAX_DESCRIPTION_SEGMENT_VARIATIONS - 1)); l >= 0; l--) { + var segmentData; switch (descriptionSegmentTypeId) { case 1: // Text (String) - segment = Lorem.generate('2-5s'); + segmentData = Lorem.generate('2-5s'); break; case 2: // Image (String) - segment = Lorem.imageUrl(); - if (!segment.startsWith('http')) { - segment = imagePath + segment; + segmentData = Lorem.imageUrl(); + if (!segmentData.startsWith('http')) { + segmentData = imagePath + segmentData; } break; } - if (segment) { - descriptionSegmentVariations.push(segment); + if (segmentData) { + descriptionSegmentVariations.push({id: nextVariationId++, data: segmentData}); } else { console.warn('failed to create segment'); } @@ -395,7 +396,10 @@ * segments: [{ * id: Number, * type: SegmentType, - * variations: [], // data type depends on type + * variations: [{ + * id: Number, + * data: dynamic, // data type depends on segment type + * }, ...], * }, ...], * }, ...], * feedback: [{ @@ -531,6 +535,16 @@ SIBLING: 32, }; + /** + * Gets the first object from the array with the id property equal to the id + * parameter passed to this function. If defaultToFirst is true this will + * return the first item from the array if no matching item is found, else + * will return undefined. + * + * @param {Array} array + * @param {number} id + * @param {boolean} [defaultToFirst=true] + */ Polymer.CTreeLoader.getFromArrayWithId = function(array, id, defaultToFirst=true) { if (!array) return undefined; diff --git a/src/ctree-segments/ctree-segment-image.html b/src/ctree-segments/ctree-segment-image.html index a13c41e..9e5caed 100644 --- a/src/ctree-segments/ctree-segment-image.html +++ b/src/ctree-segments/ctree-segment-image.html @@ -86,6 +86,7 @@ _heightPercentageChanged: function(heightPercentage) { if (heightPercentage && heightPercentage.length > 0) { this.$.image.style.height = '100%'; + this.$.image.sizing = 'cover'; this.$.container.style.paddingTop = heightPercentage; } else { this.$.image.style.height = 'auto'; From 1fcaa13700846366998fa7e81d76861a81d2bd6c Mon Sep 17 00:00:00 2001 From: CSchmitz81 Date: Wed, 31 May 2017 17:49:27 -0700 Subject: [PATCH 2/2] finished changes for issue #23 --- .../ctree-description.html | 151 +++++++++++++++--- .../ctree-details-page.html | 18 ++- .../ctree-segment-container.html | 107 ++++++++++--- .../ctree-feedback-bar.html | 19 ++- src/ctree-icons/ctree-icons.html | 2 + .../ctree-segment-behavior.html | 68 +++++++- src/ctree-segments/ctree-segment-image.html | 95 ++++++++++- src/ctree-segments/ctree-segment-text.html | 88 ++++++++-- 8 files changed, 480 insertions(+), 68 deletions(-) diff --git a/src/ctree-element-screen/ctree-description.html b/src/ctree-element-screen/ctree-description.html index d244c69..d5bd997 100644 --- a/src/ctree-element-screen/ctree-description.html +++ b/src/ctree-element-screen/ctree-description.html @@ -33,13 +33,30 @@ +/** + * TODO: use template instead of manually adding items, so we can automatically + * use data binding. An example of using multiple templates ca be found at + * https://github.com/Polymer/polymer/issues/3755: + * this.templatize(tplA); + * this.templatize(tplB); + * for(let i = 0 ; i < 3 ; i++) { + * this._templatized = tplA; + * this.ctor = tplA._content._ctor; + * this.appendChild(this.stamp({nameA: i}).root); + * + * this._templatized = tplB; + * this.ctor = tplB._content._ctor; + * this.appendChild(this.stamp({nameB: i}).root); + * } + * + * TODO: try making + buttons only visible while editing, and make them a new + * component type (to allow selection of segment type when clicked) + */ @@ -59,13 +79,6 @@ Polymer({ is: 'ctree-description', - /** - * Fired when a the feedback button is tapped for a segment. - * - * @event feedback-tapped - * @param {{segment: segmentData}} detail Contains data for segment whos feedback button was tapped. - */ - properties: { /** * Segments data @@ -91,14 +104,22 @@ notify: true, observer: '_segmentIdsChanged', }, + + editingSegmentId: { + type: Number, + notify: true, + }, }, __LISTENER_MAPS: { 'PAPER-ICON-BUTTON': { - tap: '_addClicked', + 'tap': '_addClicked', }, 'CTREE-SEGMENT-CONTAINER': { - + 'editing-segment-changed': '_editingChanged', + 'delete-tapped': '_deleteTapped', + 'move-up-tapped': '_moveUpTapped', + 'move-down-tapped': '_moveDownTapped', }, }, @@ -130,6 +151,96 @@ // TODO: show dialog to let user select which segment type to add }, + _editingChanged: function(e, detail) { + // ignore update if segment not really changing editing state + if (detail.value === (this.editingSegmentId === detail.segment.id)) return; + + var segments = this.segments; + var components = this.$.container.children; + if (detail.value) { + // editing + var segmentId = detail.segment.id; + this.editingSegmentId = segmentId; + for (var i = 0; i < segments.length; i++) { + var segment = segments[i]; + if (segment.id !== segmentId) { + var component = components[i * 2 + 1]; + if (component.editing) { + component.set('editing', false); + } + } + } + } else { + // not editing + this.editingSegmentId = undefined; + } + for (var i = 0; i <= segments.length; i++) { + var component = components[i * 2]; + if (!this.editingSegmentId || this.editingSegmentId < 0) { + component.setAttribute('hidden', 'true'); + } else { + component.removeAttribute('hidden'); + } + } + }, + + _getIndex: function(segment) { + var segmentId = segment.id; + var segments = this.segments; + for (var i = 0; i < segments.length; i++) { + if (segments[i].id === segmentId) { + return i; + } + } + return null; + }, + + _deleteTapped: function(e, detail) { + var index = this._getIndex(detail.segment); + if (index !== null) { + this.splice('segments', index, 1); + if (this.segmentIds && index < this.segmentIds.length) { + this.splice('segmentIds', index, 1); + } else { + this._segmentIdsChanged(this.segmentIds); + } + } + }, + + _moveUpTapped: function(e, detail) { + var index = this._getIndex(detail.segment); + if (index !== null && index > 0) { + this.__swapSegments(index, index - 1); + } + }, + + _moveDownTapped: function(e, detail) { + var index = this._getIndex(detail.segment); + if (index !== null && index < this.segments.length - 1) { + this.__swapSegments(index, index + 1); + } + }, + + __swapSegments: function(i1, i2) { + if (i1 == i2) return; + + if (i1 > i2) { + var temp = i1; + i1 = i2; + i2 = temp; + } + + var temp = this.segments[i1]; + this.segments[i1] = this.segments[i2]; + this.segments[i2] = temp; + if (this.segmentIds && i2 < this.segmentIds.length) { + temp = this.segmentIds[i1]; + this.segmentIds[i1] = this.segmentIds[i2]; + this.segmentIds[i2] = temp; + } + this._segmentIdsChanged(this.segmentIds); + }, + _segmentsChanged: function(segments) { // clear segments var root = this.$.container; @@ -137,6 +248,7 @@ while (child = root.firstChild) { this._removeElement(root, child); } + this.editingSegmentId = undefined; this._segmentIdsChanged(this.segmentIds); }, @@ -148,6 +260,9 @@ element.set(key, properties[key]); } } + if (this.editingSegmentId && properties.segment && this.editingSegmentId === properties.segment.id) { + element.set('editing', true); + } if (beforeChild) { parent.insertBefore(element, beforeChild); } else { @@ -170,7 +285,10 @@ icon: 'add-circle', title: 'Add Segment', }; - this._addElement(parent, 'paper-icon-button', properties, segmentContainerElement); + var button = this._addElement(parent, 'paper-icon-button', properties, segmentContainerElement); + if (!this.editingSegmentId || this.editingSegmentId < 0) { + button.setAttribute('hidden', 'true'); + } return true; }, @@ -205,6 +323,8 @@ if (this.__ensureHasPlusButton(root, childIndex, child)) { childIndex++; } + child.set('firstSegment', i == 0); + child.set('lastSegment', i == segments.length - 1); childIndex++; added = true; } else { @@ -274,7 +394,7 @@ } // remove trailing children - for (var i = childIndex; i < children.length; i++) { + while (childIndex < children.length) { var child = children[childIndex]; if (child.tagName === 'ctree-segment-container' || previousChildWasButton) { this._removeElement(root, child); @@ -290,11 +410,6 @@ this.__ensureHasPlusButton(root, children.length); } }, - - _fireFeedbackTapped: function(e, detail) { - // TODO: figure out segment data from e or detail & pass as detail segmentData - this.fire('feedback-tapped', {segmentData: {}}); - }, }); diff --git a/src/ctree-element-screen/ctree-details-page.html b/src/ctree-element-screen/ctree-details-page.html index 1391230..de74202 100644 --- a/src/ctree-element-screen/ctree-details-page.html +++ b/src/ctree-element-screen/ctree-details-page.html @@ -47,7 +47,7 @@ position: absolute; top: 0; left: 0; - right: 0; + right: 16px; } ctree-description { position: absolute; @@ -61,7 +61,7 @@ } - + @@ -108,7 +108,10 @@ */ segments: { type: Array, + observer: '__segmentsChanged', }, + + __tempSegments: Array, }, _descriptionChanged: function(description, descriptionSegments) { @@ -123,10 +126,15 @@ this.fire('related-tapped'); }, - _fireFeedbackTapped: function(e, detail) { - // TODO: figure out segment data from e or detail & pass as detail segmentData - this.fire('feedback-tapped', {segmentData: {}}); + __segmentsChanged: function(e, detail) { + if (this.segments) { + this.__tempSegments = this.segments.slice(); + } else { + this.__tempSegments = this.segments; + } }, + + // TODO: before close element or switch description check if tempSegments changed, and if ask user if they want to save their changes }); diff --git a/src/ctree-element-screen/ctree-segment-container.html b/src/ctree-element-screen/ctree-segment-container.html index 244e42c..00beec0 100644 --- a/src/ctree-element-screen/ctree-segment-container.html +++ b/src/ctree-element-screen/ctree-segment-container.html @@ -29,6 +29,7 @@ + @@ -47,7 +48,8 @@ vertical-align: middle; padding: 0; } - #componentContainer { + #content { + position: relative; width: 100%; } .move { @@ -57,6 +59,9 @@ paper-icon-button[hidden] { display: none !important; } + paper-ripple[hidden] { + display: none !important; + } ctree-feedback-bar { text-align: right; white-space: nowrap; @@ -66,16 +71,16 @@ - + - + - +
- - + +
@@ -83,6 +88,15 @@ Polymer({ is: 'ctree-segment-container', + /** + * Fired when a the delete button is tapped. + * + * @event editing-segment-changed + * @param {{value: Boolean, segment: segmentData}} detail + * value: New value of editing parameter. + * segment: Contains data for segment held by container. + */ + /** * Fired when a the delete button is tapped. * @@ -104,15 +118,6 @@ * @param {{segment: segmentData}} detail Contains data for segment held by container. */ - /** - * Fired when a the feedback button is tapped. - * - * @event feedback-tapped - * @param {{segment: segmentData, variationId: Number}} detail - * segment: Contains data for segment held by container. - * variationId: ID of segment variation shown when button tapped. - */ - properties: { /** * Segments data @@ -133,7 +138,6 @@ observer: '_segmentChanged', }, - // TODO: segments should probably be ID based, but the use of an array of data doesn't make that possible variationId: { type: Number, notify: true, @@ -147,6 +151,7 @@ editing: { type: Boolean, value: false, + observer: '__editingChanged', }, _variationIndex: { @@ -160,6 +165,22 @@ }, }, + attached: function() { + var component = this.$$('#component'); + if (component) { + this.listen(component, 'editing-changed', '__editingComponentChanged'); + this.listen(component, 'add-segment-variation', '_addSegmentVariation'); + } + }, + + detached: function() { + var component = this.$$('#component'); + if (component) { + this.unlisten(component, 'editing-changed', '__editingComponentChanged'); + this.unlisten(component, 'add-segment-variation', '_addSegmentVariation'); + } + }, + previous: function() { if (this._variationIndex <= 0) return; @@ -177,19 +198,43 @@ var type = segment.type; var component = this.$$('#component'); if (!type || !component || component.tagName.toLowerCase() !== type.componentName) { - var parent = this.$.componentContainer; + var parent = this.$.container; var child; + if (component) { + this.unlisten(component, 'editing-changed', '__editingComponentChanged'); + this.unlisten(component, 'add-segment-variation', '_addSegmentVariation'); + } while (child = parent.firstChild) { parent.removeChild(child); } if (type) { component = document.createElement(type.componentName); component.id = 'component'; + this.listen(component, 'editing-changed', '__editingComponentChanged'); + this.listen(component, 'add-segment-variation', '_addSegmentVariation'); parent.appendChild(component); } } }, + _contentClicked: function() { + if (this.editing) return; + + this.editing = true; + }, + + _addSegmentVariation: function(e, detail) { + var segmentData = detail.data; + var id = Math.floor(Math.random() * 2000000000) + 1; // TODO: this would come from loading when uploading it + this.segment.variations.splice(this._variationIndex + 1, 0, {id: id, data: segmentData}); + this._variationIndex = this._variationIndex + 1; + this.editing = false; + + this.__updatingVariation = true; + this.variationId = id; + this.__updatingVariation = false; + }, + __variationIdChanged: function(id) { if (!this.segment || this.__updatingVariation) return; @@ -212,6 +257,30 @@ this.__updatingVariation = false; }, + __editingChanged: function(value) { + var component = this.$$('#component'); + if (component) { + component.editing = value; + this.fire('editing-segment-changed', {value: value, segment: this.segment}); + } + }, + + __editingComponentChanged: function() { + this.async(this.__updateEditingFromComponent); + }, + + __updateEditingFromComponent: function() { + var component = this.$$('#component'); + if (!component || this.editing === component.editing) return; + + this.editing = component.editing; + if (!this.editing) { + component.notifyPath('data'); + // TODO: should be able to remove after update to Polymer 2.0 + component._dataChanged(component.data); + } + }, + __variationIndexChanged: function(index) { if (!this.segment) return; @@ -255,10 +324,6 @@ _fireMoveDownTapped: function() { this.fire('move-down-tapped', {segment: this.segment}); }, - - _fireFeedbackTapped: function() { - this.fire('feedback-tapped', {segment: this.segment, variation: this.variationId}); - }, });
diff --git a/src/ctree-feedback-bar/ctree-feedback-bar.html b/src/ctree-feedback-bar/ctree-feedback-bar.html index 40d6a14..1c21100 100644 --- a/src/ctree-feedback-bar/ctree-feedback-bar.html +++ b/src/ctree-feedback-bar/ctree-feedback-bar.html @@ -67,11 +67,26 @@ */ properties: { + /** + * Segments data + * + * Structure: + * { + * id: Number, + * type: SegmentType, + * variations: [{ + * id: Number, + * data: dynamic, // data type depends on segment type + * }, ...], + * } + */ + segment: { + type: Object, + }, }, _fireFeedbackTapped: function() { - // TODO: figure out segment data & pass as detail segmentData - this.fire('feedback-tapped', {segmentData: {}}); + this.fire('feedback-tapped', {segment: this.segment}); }, }); diff --git a/src/ctree-icons/ctree-icons.html b/src/ctree-icons/ctree-icons.html index 615cb3f..eb9b92b 100644 --- a/src/ctree-icons/ctree-icons.html +++ b/src/ctree-icons/ctree-icons.html @@ -57,6 +57,8 @@ + + diff --git a/src/ctree-segments/ctree-segment-behavior.html b/src/ctree-segments/ctree-segment-behavior.html index 85b8fea..644fc66 100644 --- a/src/ctree-segments/ctree-segment-behavior.html +++ b/src/ctree-segments/ctree-segment-behavior.html @@ -43,7 +43,7 @@ * static getText(data) * * Gets text interpretation of data for segment type. If no text interpretation - * is possible this may function may not be defined or may return an undefined + * is possible this function may not be defined or may return an undefined * result. * * @param {Object} data Segment type specific data to get text interpretation for @@ -51,7 +51,71 @@ */ Polymer.CTreeSegmentBehavior = { - // TODO: add shared behavior code for segments + /** + * Notify segment container that a new variation should be added. + * + * @event add-segment-variation + * @param {{data: variationData}} detail New segment variation data. + */ + + properties: { + data: Object, + + editing: { + type: Boolean, + value: false, + notify: true, + }, + + validChangeMade: { + type: Boolean, + value: false, + }, + + loadingChangeMade: { + type: Boolean, + value: false, + }, + + heightPercent: String, + + scale: { + type: String, + value: '100%', + }, + + _editUpdateDelay: { + type: Number, + value: 800, + }, + + __editUpdateHandle: Number, + }, + + _addSegmentVariation: function(data) { + if (!this.validChangeMade) return; + + this.validChangeMade = false; + // TODO: prompt user to select reason for new segment (list of default reasons from behavior & custom reasons from type) + // TODO: may be able to remove this once saving variation to loader + this.fire('add-segment-variation', {data: data}); + }, + + _delayEditUpdate: function() { + this.cancelAsync(this.__editUpdateHandle); + this.__editUpdateHandle = this.async(() => { + this.__editUpdateHandle = undefined; + this._editUpdate(); + }, this._editUpdateDelay); + }, + + _cancelEditUpdate: function() { + this.cancelAsync(this.__editUpdateHandle); + }, + + _editUpdate: function() { + // TODO: override to add custom logic when edit update timer elapsed + }, }; diff --git a/src/ctree-segments/ctree-segment-image.html b/src/ctree-segments/ctree-segment-image.html index 9e5caed..ab97286 100644 --- a/src/ctree-segments/ctree-segment-image.html +++ b/src/ctree-segments/ctree-segment-image.html @@ -29,6 +29,9 @@ + + + @@ -52,10 +55,35 @@ width: 100%; background-color: #000; } + #url { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 8px; + background-color: rgba(255, 255, 255, 0.8); + } + #loadingIndicator { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + } + paper-input[hidden] { + display: none !important; + } + paper-icon-button[hidden] { + display: none !important; + }
- + + + + + +
@@ -68,19 +96,32 @@ ], properties: { - data: { - type: Object, - observer: '_dataChanged', + _changed: { + type: Boolean, + value: false, }, + }, - heightPercent: { - type: String, - observer: '_heightPercentageChanged', - }, + observers: [ + '_editingChanged(editing)', + '_dataChanged(data)', + '_heightPercentageChanged(heightPercent)', + '__updateValidChangeMade(_changed, loading, error)', + '__updatLoadingChangeMade(editing, loading)', + ], + + _editingChanged: function(editing) { + if (editing) { + this.$.url.focus(); + } }, _dataChanged: function(data) { + this.validChangeMade = false; + this.loadingChangeMade = false; + this._changed = false; this.$.image.src = data; + this.$.url.value = data; }, _heightPercentageChanged: function(heightPercentage) { @@ -92,6 +133,44 @@ this.$.image.style.height = 'auto'; } }, + + _inputChanged: function(value) { + if (this.$.url.value === this.$.image.src) { + this._cancelEditUpdate(); + this._waiting = false; + } else { + this._delayEditUpdate(); + } + }, + + _checkForEnter: function(e) { + // check if 'enter' was pressed + if (e.keyCode === 13) { + this._editSaveClicked(); + } + }, + + _editUpdate: function() { + var value = this.$.url.value.trim(); + this.$.image.src = value; + this._changed = (value !== this.data); + }, + + _editSaveClicked: function() { + this._addSegmentVariation(this.$.image.src); + }, + + _editCancelClicked: function() { + this.editing = false; + }, + + __updateValidChangeMade: function(changed, loading, error) { + this.validChangeMade = changed && !loading && !error; + }, + + __updatLoadingChangeMade: function(editing, loading) { + this.loadingChangeMade = editing && loading; + }, }); diff --git a/src/ctree-segments/ctree-segment-text.html b/src/ctree-segments/ctree-segment-text.html index c54e57d..caf9189 100644 --- a/src/ctree-segments/ctree-segment-text.html +++ b/src/ctree-segments/ctree-segment-text.html @@ -28,6 +28,9 @@ --> + + + @@ -38,12 +41,39 @@ :host { display: block; } - #text { + .text { + width: calc(100% - 16px); padding: 4px 8px; } + #input { + width: 100%; + } + #buttons > div { + display: inline-block; + position: relative; + padding: 8px; + } + #buttons > div[hidden] { + display: none !important; + } -
+
+
+ +
+
+ + Save + +
+
+ + Cancel + +
+
+