@@ -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..d5bd997
--- /dev/null
+++ b/src/ctree-element-screen/ctree-description.html
@@ -0,0 +1,415 @@
+
+
+
+
+
+
+
+
+/**
+ * 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)
+ */
+
+
+
+
+
+
+
+
diff --git a/src/ctree-element-screen/ctree-details-page.html b/src/ctree-element-screen/ctree-details-page.html
index fd47cd7..de74202 100644
--- a/src/ctree-element-screen/ctree-details-page.html
+++ b/src/ctree-element-screen/ctree-details-page.html
@@ -31,8 +31,10 @@
+
+
@@ -45,58 +47,21 @@
position: absolute;
top: 0;
left: 0;
- right: 0;
+ right: 16px;
}
- iron-list {
+ ctree-description {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
padding-top: 64px;
- }
- table {
- width: 100%;
- border: 0;
- border-spacing: 0;
- }
- table td {
- vertical-align: middle;
- padding: 0;
- }
- .content {
- width: 100%;
- }
- .move {
- width: 100%;
- text-align: center;
- }
- ctree-feedback-bar {
- text-align: right;
- white-space: nowrap;
+ overflow-x: hidden;
+ overflow-y: auto;
}
-
-
-
-
-
-
+
@@ -123,20 +88,34 @@
* @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: ["", "", ""],
+ observer: '__segmentsChanged',
},
- descriptionConfigId: {
- type: Number,
- observer: '_onDescriptionConfigChanged',
- },
+ __tempSegments: Array,
},
- _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() {
@@ -147,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-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..00beec0
--- /dev/null
+++ b/src/ctree-element-screen/ctree-segment-container.html
@@ -0,0 +1,329 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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-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-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-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 a13c41e..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,29 +96,81 @@
],
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) {
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';
}
},
+
+ _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;
+ }
-
+
+