diff --git a/app/javascript/retrospring/features/settings/theme.ts b/app/javascript/retrospring/features/settings/theme.ts new file mode 100644 index 00000000..c4cf02ff --- /dev/null +++ b/app/javascript/retrospring/features/settings/theme.ts @@ -0,0 +1,100 @@ +import "@melloware/coloris/dist/coloris.css"; +import Coloris from "@melloware/coloris"; + +let previewStyle = null; +let previewTimeout = null; + +const previewTheme = (): void => { + const payload = {}; + + Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { + const name = color.name.substring(6, color.name.length - 1); + payload[name] = parseInt(color.value.substr(1, 6), 16); + }); + + generateTheme(payload); +} + +const generateTheme = (payload: object): void => { + const themeAttributeMap = { + 'primary_color': 'primary', + 'primary_text': 'primary-text', + 'danger_color': 'danger', + 'danger_text': 'danger-text', + 'warning_color': 'warning', + 'warning_text': 'warning-text', + 'info_color': 'info', + 'info_text': 'info-text', + 'success_color': 'success', + 'success_text': 'success-text', + 'dark_color': 'dark', + 'dark_text': 'dark-text', + 'light_color': 'light', + 'light_text': 'light-text', + 'raised_background': 'raised-bg', + 'raised_accent': 'raised-accent', + 'background_color': 'background', + 'body_text': 'body-text', + 'input_color': 'input-bg', + 'input_text': 'input-text', + 'muted_text': 'muted-text' + }; + + let body = ":root {\n"; + + (Object.keys(payload)).forEach((payloadKey) => { + if (themeAttributeMap[payloadKey]) { + if (themeAttributeMap[payloadKey].includes('text')) { + const hex = getHexColorFromThemeValue(payload[payloadKey]); + body += `--${themeAttributeMap[payloadKey]}: ${getDecimalTripletsFromHex(hex)};\n`; + } + else { + body += `--${themeAttributeMap[payloadKey]}: #${getHexColorFromThemeValue(payload[payloadKey])};\n`; + } + } + }); + + body += "}"; + + previewStyle.innerHTML = body; +} + +const getHexColorFromThemeValue = (themeValue: string): string => { + return ('000000' + parseInt(themeValue).toString(16)).substr(-6, 6); +} + +const getDecimalTripletsFromHex = (hex: string): string => { + return hex.match(/.{1,2}/g).map((value) => parseInt(value, 16)).join(', '); +} + +export function themeDocumentHandler(): void { + if (!document.querySelector('#update_theme')) return; + + previewStyle = document.createElement('style'); + previewStyle.setAttribute('data-preview-style', ''); + document.body.appendChild(previewStyle); + + Coloris.init(); + + Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { + color.value = `#${getHexColorFromThemeValue(color.value)}`; + + Coloris({ + el: '.color', + wrap: false, + formatToggle: false, + alpha: false + }); + + color.addEventListener('input', () => { + clearTimeout(previewTimeout); + setTimeout(previewTheme, 1000); + }); + }); +} + +export function themeSubmitHandler(): void { + Array.from(document.querySelectorAll('#update_theme .color')).forEach((color: HTMLInputElement) => { + color.value = String(parseInt(color.value.substr(1, 6), 16)); + }); +} \ No newline at end of file diff --git a/app/views/settings/_profile.haml b/app/views/settings/_profile.haml index ea87af87..984588fa 100644 --- a/app/views/settings/_profile.haml +++ b/app/views/settings/_profile.haml @@ -8,16 +8,10 @@ .media-body = f.file_field :profile_picture, label: t('views.settings.profile.avatar'), accept: APP_CONFIG[:accepted_image_formats].join(',') - .row#profile-picture-crop-controls{ style: 'display: none;' } + .row.d-none#profile-picture-crop-controls .col-sm-10.col-md-8 %strong= t('views.settings.profile.avatar_adjust') %img#profile-picture-cropper{ src: current_user.profile_picture.url(:medium) } - .col-sm-2.col-md-4 - .btn-group - %button.btn.btn-inverse#cropper-zoom-out{ type: :button } - %i.fa.fa-search-minus - %button.btn.btn-inverse#cropper-zoom-in{ type: :button } - %i.fa.fa-search-plus .row.mb-2#profile-header-media .col @@ -25,16 +19,10 @@ .col-xs-12.mt-3.mt-sm-0.pl-3.pr-3 = f.file_field :profile_header, label: t('views.settings.profile.header'), accept: APP_CONFIG[:accepted_image_formats].join(',') - .row#profile-header-crop-controls{ style: 'display: none;' } + .row.d-none#profile-header-crop-controls .col-sm-10.col-md-8 %strong= t('views.settings.profile.header_adjust') %img#profile-header-cropper{ src: current_user.profile_header.url(:web) } - .col-sm-2.col-md-4 - .btn-group - %button.btn.btn-inverse#cropper-header-zoom-out{ type: :button } - %i.fa.fa-search-minus - %button.btn.btn-inverse#cropper-header-zoom-in{ type: :button } - %i.fa.fa-search-plus = f.check_box :show_foreign_themes, label: 'Render other user themes when visiting their profile'