diff --git a/app/controllers/settings/general.js b/app/controllers/settings/general.js index 9708501c5..9d5df5e47 100644 --- a/app/controllers/settings/general.js +++ b/app/controllers/settings/general.js @@ -8,6 +8,7 @@ import { IMAGE_MIME_TYPES } from 'ghost-admin/components/gh-image-uploader'; import {computed} from '@ember/object'; +import {htmlSafe} from '@ember/string'; import {run} from '@ember/runloop'; import {inject as service} from '@ember/service'; import {task} from 'ember-concurrency'; @@ -34,7 +35,6 @@ export default Controller.extend({ iconMimeTypes: 'image/png,image/x-icon', imageExtensions: IMAGE_EXTENSIONS, imageMimeTypes: IMAGE_MIME_TYPES, - _scratchFacebook: null, _scratchTwitter: null, @@ -50,6 +50,19 @@ export default Controller.extend({ return `${blogUrl}/${publicHash}/rss`; }), + backgroundStyle: computed('settings.brand.primaryColor', function () { + let color = this.get('settings.brand.primaryColor') || '#ffffff'; + return htmlSafe(`background-color: ${color}`); + }), + + brandColor: computed('settings.brand.primaryColor', function () { + let color = this.get('settings.brand.primaryColor'); + if (color && color[0] === '#') { + return color.slice(1); + } + return color; + }), + actions: { save() { this.save.perform(); @@ -257,6 +270,42 @@ export default Controller.extend({ this.get('settings.hasValidated').pushObject('twitter'); return; } + }, + validateBrandColor() { + let newColor = this.get('brandColor'); + let oldColor = this.get('settings.brand.primaryColor'); + let errMessage = ''; + + // reset errors and validation + this.get('settings.errors').remove('brandColor'); + this.get('settings.hasValidated').removeObject('brandColor'); + + if (newColor === '') { + // Clear out the brand color + this.set('settings.brand.primaryColor', ''); + return; + } + + // brandColor will be null unless the user has input something + if (!newColor) { + newColor = oldColor; + } + + if (newColor[0] !== '#') { + newColor = `#${newColor}`; + } + + if (newColor.match(/#[0-9A-Fa-f]{6}$/)) { + this.set('settings.brand.primaryColor', ''); + run.schedule('afterRender', this, function () { + this.set('settings.brand.primaryColor', newColor); + }); + } else { + errMessage = 'The color should be in valid hex format'; + this.get('settings.errors').add('brandColor', errMessage); + this.get('settings.hasValidated').pushObject('brandColor'); + return; + } } }, diff --git a/app/models/setting.js b/app/models/setting.js index 74015f667..dad0fabc3 100644 --- a/app/models/setting.js +++ b/app/models/setting.js @@ -11,6 +11,7 @@ export default Model.extend(ValidationEngine, { logo: attr('string'), coverImage: attr('string'), icon: attr('string'), + brand: attr('json-string'), defaultLocale: attr('string'), activeTimezone: attr('string', {defaultValue: 'Etc/UTC'}), codeinjectionHead: attr('string'), diff --git a/app/styles/layouts/settings.css b/app/styles/layouts/settings.css index a5642ac96..da4a29647 100644 --- a/app/styles/layouts/settings.css +++ b/app/styles/layouts/settings.css @@ -796,3 +796,62 @@ p.theme-validation-details { .blog-icon { background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3ERectangle%3C/title%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath fill='%23E6EEF2' d='M0 0h24v24H0z'/%3E%3Cpath fill='%23D8E2E8' d='M0 0h12v12H0zM12 12h12v12H12z'/%3E%3C/g%3E%3C/svg%3E"); } + +/** CSS for accent color */ +.input-color-form-group { + display: flex; + align-items: flex-end; + flex-direction: column; + margin-bottom: 0; +} + +.input-color { + display: flex; + position: relative; +} + +.input-color:after { + content: "#"; + position: absolute; + top: 6px; + left: 40px; + color: var(--midlightgrey); + font-family: "Consolas", monaco, monospace; + font-size: 13px; +} + +.input-color:focus { + border: none; +} + +.input-color input { + padding-left: 48px; + width: 110px; + height: 33px; + padding-right: 8px; + font-family: "Consolas", monaco, monospace; + font-size: 13px; +} + +.input-color .color-box { + position: absolute; + top: 1px; + left: 1px; + width: 31px; + height: 31px; + display: inline-block; + background-color: var(--lightgrey); + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; + border-right: 1px solid var(--input-border); + box-shadow: inset 0 0 0 1px var(--white); +} + +.input-color input:focus + .color-box { + top: 2px; + left: 2px; + width: 30px; + height: 29px; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; +} \ No newline at end of file diff --git a/app/templates/settings/general.hbs b/app/templates/settings/general.hbs index 8817bf96b..62d1c0922 100644 --- a/app/templates/settings/general.hbs +++ b/app/templates/settings/general.hbs @@ -164,6 +164,31 @@ {{/gh-uploader}} + {{#if this.config.enableDeveloperExperiments}} +
+
+
Accent Color
+
Primary color used in your publication theme
+
+
+ {{#gh-form-group errors=settings.errors hasValidated=settings.hasValidated property="brandColor" class="input-color-form-group"}} +
+ {{gh-text-input + name="brand-color" + placeholder="abcdef" + autocorrect="off" + maxlength="6" + focus-out=(action "validateBrandColor") + value=brandColor + data-test-brand-color-input=true + }} +
+
+ {{gh-error-message errors=settings.errors property="brandColor" data-test-brandColor-error=true}} + {{/gh-form-group}} +
+
+ {{/if}}
{{#gh-uploader extensions=this.imageExtensions