From ccfa92403591b6ee48aed6ceb376b2234c2d7cc3 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Tue, 25 Feb 2020 13:02:51 +0100 Subject: [PATCH] Add custom color picker to sidebar editor Signed-off-by: Georg Ehrke --- css/app-sidebar.scss | 13 ++ package-lock.json | 70 ++++++++- .../Properties/PropertyCalendarPicker.vue | 6 + .../Editor/Properties/PropertyColor.vue | 138 ++++++++++++++++++ src/components/Shared/CalendarPicker.vue | 4 + src/fullcalendar/eventSourceFunction.js | 3 +- src/mixins/EditorMixin.js | 20 +++ src/models/calendarObjectInstance.js | 8 + src/store/calendarObjectInstance.js | 28 +++- src/views/EditSidebar.vue | 22 ++- src/views/EditSimple.vue | 2 +- .../fullcalendar/eventSourceFunction.test.js | 27 +++- 12 files changed, 329 insertions(+), 12 deletions(-) create mode 100644 src/components/Editor/Properties/PropertyColor.vue diff --git a/css/app-sidebar.scss b/css/app-sidebar.scss index 6be5d0b0d..02fe53160 100644 --- a/css/app-sidebar.scss +++ b/css/app-sidebar.scss @@ -467,6 +467,7 @@ .property-text, .property-select, + .property-color, .property-select-multiple, .property-title { display: flex; @@ -524,6 +525,18 @@ } } + .property-color { + &__color-preview { + border-radius: var(--border-radius); + height: 34px !important; + width: 34px !important; + } + + &__info { + margin-top: 0; + } + } + .property-text { &__input { textarea { diff --git a/package-lock.json b/package-lock.json index dcda39ff0..edb9e50e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7171,6 +7171,27 @@ "is-regexp": "^2.0.0" } }, + "closest-css-color": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/closest-css-color/-/closest-css-color-0.1.1.tgz", + "integrity": "sha512-Vor9mkq3sXlEO8FTTjxVhV8jlmh2dQ9oVsrhcSan2f4qikBgnqZnJQXSvxrYrGWEp5UHUIAEEtPZ1OLeyfMa5w==", + "requires": { + "colour-proximity": "0.0.2", + "css-color-names": "0.0.4", + "hex-rgb": "^2.0.0", + "lodash.merge": "^4.6.1", + "lodash.pick": "^4.4.0", + "lodash.sortby": "^4.7.0", + "lodash.uniqby": "^4.7.0" + }, + "dependencies": { + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=" + } + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -7223,6 +7244,29 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "color-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.1.3.tgz", + "integrity": "sha1-6GXS4+WfZlw68N4UOD9r8HBWhfM=", + "requires": { + "color-convert": "0.2.x" + }, + "dependencies": { + "color-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.2.1.tgz", + "integrity": "sha1-NjyrI8lLMaDWTbcQSLjGqUD4xow=" + } + } + }, + "colour-proximity": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/colour-proximity/-/colour-proximity-0.0.2.tgz", + "integrity": "sha1-E5rTr+zzAbyAO42mmPMslyl0dpw=", + "requires": { + "color-string": "~0.1.2" + } + }, "combined-stream": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", @@ -7533,6 +7577,11 @@ } } }, + "css-color-names": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-1.0.1.tgz", + "integrity": "sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==" + }, "css-loader": { "version": "3.4.2", "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz", @@ -10112,6 +10161,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hex-rgb": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hex-rgb/-/hex-rgb-2.0.0.tgz", + "integrity": "sha512-JDPETuFBgYKFWufLB9tk7SaLoia5nSsOSa2DWzbLvK0TXxQglIQ4FBxrO9QmfUKenJJjBfFQ0jJ6jSIK2468kQ==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -13513,14 +13567,17 @@ "lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, "lodash.throttle": { "version": "4.1.1", @@ -13533,6 +13590,11 @@ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", "dev": true }, + "lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI=" + }, "log-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", diff --git a/src/components/Editor/Properties/PropertyCalendarPicker.vue b/src/components/Editor/Properties/PropertyCalendarPicker.vue index cff0d6622..bd824beb3 100644 --- a/src/components/Editor/Properties/PropertyCalendarPicker.vue +++ b/src/components/Editor/Properties/PropertyCalendarPicker.vue @@ -76,6 +76,12 @@ export default { }, }, methods: { + /** + * Emits the select calendar event + * + * // TODO: this should emit the calendar id instead + * @param {Object} value The calendar Object + */ selectCalendar(value) { this.$emit('selectCalendar', value) }, diff --git a/src/components/Editor/Properties/PropertyColor.vue b/src/components/Editor/Properties/PropertyColor.vue new file mode 100644 index 000000000..7eb0783ca --- /dev/null +++ b/src/components/Editor/Properties/PropertyColor.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/src/components/Shared/CalendarPicker.vue b/src/components/Shared/CalendarPicker.vue index 3fdc3cacf..470f588fd 100644 --- a/src/components/Shared/CalendarPicker.vue +++ b/src/components/Shared/CalendarPicker.vue @@ -44,6 +44,10 @@ export default { }, }, methods: { + /** + * TODO: this should emit the calendar id instead + * @param {Object} newCalendar The selected calendar + */ change(newCalendar) { if (!newCalendar) { return diff --git a/src/fullcalendar/eventSourceFunction.js b/src/fullcalendar/eventSourceFunction.js index 06e51eed6..499a31195 100644 --- a/src/fullcalendar/eventSourceFunction.js +++ b/src/fullcalendar/eventSourceFunction.js @@ -24,7 +24,7 @@ import { hexToRGB, isLight, generateTextColorForHex, - getHexForColorName + getHexForColorName, } from '../utils/color.js' import logger from '../utils/logger.js' @@ -94,7 +94,6 @@ export function eventSourceFunction(calendarObjects, calendar, start, end, timez if (object.color) { const customColor = getHexForColorName(object.color) - console.debug(customColor) if (customColor) { fcEvent.backgroundColor = customColor fcEvent.borderColor = customColor diff --git a/src/mixins/EditorMixin.js b/src/mixins/EditorMixin.js index d7130071f..e617b3c4f 100644 --- a/src/mixins/EditorMixin.js +++ b/src/mixins/EditorMixin.js @@ -177,6 +177,14 @@ export default { backgroundImage() { return getIllustrationForTitle(this.title) }, + /** + * Returns the color the illustration should be colored in + * + * @returns {String} + */ + illustrationColor() { + return this.color || this.selectedCalendarColor + }, /** * Returns the color of the calendar selected by the user * This is used to color illustration @@ -195,6 +203,18 @@ export default { return this.selectedCalendar.color }, + /** + * Returns the custom color of this event + * + * @returns {null|String} + */ + color() { + if (!this.calendarObjectInstance) { + return null + } + + return this.calendarObjectInstance.customColor + }, /** * Returns whether or not to display event details * diff --git a/src/models/calendarObjectInstance.js b/src/models/calendarObjectInstance.js index adaec751f..3e7f77649 100644 --- a/src/models/calendarObjectInstance.js +++ b/src/models/calendarObjectInstance.js @@ -23,6 +23,7 @@ import { getDateFromDateTimeValue } from '../utils/date.js' import DurationValue from 'calendar-js/src/values/durationValue.js' import { getWeekDayFromDate } from '../utils/recurrence.js' import { getAmountAndUnitForTimedEvents, getAmountHoursMinutesAndUnitForAllDayEvents } from '../utils/alarms.js' +import { getHexForColorName } from '../utils/color.js' /** * Creates a complete calendar-object-instance-object based on given props @@ -129,6 +130,13 @@ export const mapEventComponentToCalendarObjectInstanceObject = (eventComponent) calendarObjectInstanceObject.attendees = getAttendeesFromEventComponent(eventComponent) calendarObjectInstanceObject.alarms = getAlarmsFromEventComponent(eventComponent) + if (eventComponent.hasProperty('COLOR')) { + const hexColor = getHexForColorName(eventComponent.getFirstPropertyFirstValue('COLOR')) + if (hexColor !== null) { + calendarObjectInstanceObject.customColor = hexColor + } + } + return calendarObjectInstanceObject } diff --git a/src/store/calendarObjectInstance.js b/src/store/calendarObjectInstance.js index 06eecb642..d97b8ec91 100644 --- a/src/store/calendarObjectInstance.js +++ b/src/store/calendarObjectInstance.js @@ -39,6 +39,10 @@ import { getAmountHoursMinutesAndUnitForAllDayEvents, getTotalSecondsFromAmountAndUnitForTimedEvents, getTotalSecondsFromAmountHourMinutesAndUnitForAllDayEvents, } from '../utils/alarms.js' +import { + getClosestCSS3ColorNameForHex, + getHexForColorName, +} from '../utils/color.js' const state = { isNew: null, @@ -355,11 +359,29 @@ const mutations = { * @param {Object} state The Vuex state * @param {Object} data The destructuring object * @param {Object} data.calendarObjectInstance The calendarObjectInstance object - * @param {String} data.customColor New color to set + * @param {String|null} data.customColor New color to set */ changeCustomColor(state, { calendarObjectInstance, customColor }) { - calendarObjectInstance.eventComponent.customColor = customColor - calendarObjectInstance.customColor = customColor + if (customColor === null) { + calendarObjectInstance.eventComponent.deleteAllProperties('COLOR') + Vue.set(calendarObjectInstance, 'customColor', null) + return + } + + const cssColorName = getClosestCSS3ColorNameForHex(customColor) + const hexColorOfCssName = getHexForColorName(cssColorName) + + // Abort if either is undefined + if (!cssColorName || !hexColorOfCssName) { + console.error('Setting custom color failed') + console.error('customColor: ', customColor) + console.error('cssColorName: ', cssColorName) + console.error('hexColorOfCssName: ', hexColorOfCssName) + return + } + + calendarObjectInstance.eventComponent.color = cssColorName + Vue.set(calendarObjectInstance, 'customColor', hexColorOfCssName) }, /** diff --git a/src/views/EditSidebar.vue b/src/views/EditSidebar.vue index 511985b0f..968259e7c 100644 --- a/src/views/EditSidebar.vue +++ b/src/views/EditSidebar.vue @@ -51,7 +51,7 @@