3
0

Improve branding editor UI and merge it into one card

This commit is contained in:
Jože Fortun
2024-01-12 17:51:58 +01:00
committed by Mumbi Francis
parent 9da9ad1163
commit 3d9a2da551
18 changed files with 189 additions and 413 deletions
@@ -27,22 +27,22 @@
<b-tab
v-for="theme in themes"
:key="theme.id"
:title="$t(`tabs.${theme.id}`)"
:title="theme.title"
>
<b-row>
<b-row v-if="theme.id !== 'general'">
<b-col
v-for="(key, index) in themeInputs"
v-for="key in themeVariables"
:key="key"
md="6"
cols="12"
>
<b-form-group
:label="$t(`theme.values.${key}`)"
:label="$t(`theme.variables.${key}`)"
label-class="text-primary"
>
<c-input-color-picker
ref="picker"
v-model="theme.values[key]"
v-model="theme.variables[key]"
:data-test-id="`input-${key}-color`"
:translations="{
modalTitle: $t('colorPicker'),
@@ -50,11 +50,14 @@
saveBtnLabel: $t('general:label.saveAndClose')
}"
>
<template #footer>
<template
v-if="['light', 'dark'].includes(theme.id)"
#footer
>
<b-button
variant="outline-primary"
class="mr-auto"
@click="resetColor(key, index, theme.id)"
@click="resetColor(key, theme)"
>
{{ $t('label.default') }}
</b-button>
@@ -63,42 +66,43 @@
</b-form-group>
</b-col>
</b-row>
<!-- <b-row>
<b-row>
<b-col>
<b-form-group
:label="$t('custom-css')"
label-class="text-primary"
class="mb-0"
>
<c-ace-editor
v-model="theme.values"
v-model="theme.customCSS"
lang="css"
height="300px"
height="400px"
font-size="14px"
show-line-numbers
:border="false"
:show-popout="true"
@open="openCustomCSSModal(theme.id)"
/>
</b-form-group>
</b-col>
</b-row> -->
</b-row>
</b-tab>
</b-tabs>
<!-- <b-modal
<b-modal
id="custom-css-editor"
v-model="theme.showEditorModal"
v-model="customCSSModal.show"
:title="$t('modal.editor')"
cancel-variant="link"
size="lg"
:ok-title="$t('general:label.saveAndClose')"
:cancel-title="$t('general:label.cancel')"
body-class="p-0"
@ok="saveCustomCSSInput(theme.id)"
@hidden="resetCustomCSSInput(theme.id)"
@ok="saveCustomCSSModal()"
@hidden="resetCustomCSSModal()"
>
<c-ace-editor
v-model="theme.modalValue"
v-model="customCSSModal.value"
lang="scss"
height="500px"
font-size="14px"
@@ -106,7 +110,7 @@
:border="false"
:show-popout="false"
/>
</b-modal> -->
</b-modal>
<template #footer>
<c-button-submit
@@ -162,7 +166,12 @@ export default {
data () {
return {
themeInputs: [
themeTabs: [
'general',
'light',
'dark',
],
themeVariables: [
'black',
'white',
'primary',
@@ -187,6 +196,8 @@ export default {
'light': '#F3F5F7',
'extra-light': '#E4E9EF',
'body-bg': '#F3F5F7',
'sidebar-bg': '#FFFFFF',
'topbar-bg': '#F3F5F7',
},
darkModeVariables: {
'black': '#FBF7F4',
@@ -196,11 +207,20 @@ export default {
'success': '#43AA8B',
'warning': '#E2A046',
'danger': '#E54122',
'light': '#768D9A',
'extra-light': '#23495F',
'body-bg': '#162425',
'light': '#23495F',
'extra-light': '#768D9A',
'body-bg': '#092B40',
'sidebar-bg': '#0B344E',
'topbar-bg': '#092B40',
},
themes: [],
customCSSModal: {
show: false,
id: '',
value: '',
},
}
},
@@ -219,39 +239,70 @@ export default {
settings: {
immediate: true,
handler (settings) {
if (settings['ui.studio.themes']) {
this.themes = settings['ui.studio.themes'].map(theme => {
theme.values = JSON.parse(theme.values)
return theme
})
} else {
this.themes = [
{
id: 'light',
values: this.lightModeVariables,
},
{
id: 'dark',
values: this.darkModeVariables,
},
]
}
const themes = settings['ui.studio.themes'] || []
const customCSS = settings['ui.studio.custom-css'] || []
this.themes = this.themeTabs.map((id) => {
const { title, values = '' } = themes.find(t => t.id === id) || {}
const defaultCustomCSS = customCSS.find(t => t.id === id) || {}
let variables = JSON.parse(values || '{}')
if (!values && ['light', 'dark'].includes(id)) {
variables = id === 'light' ? this.lightModeVariables : this.darkModeVariables
}
return {
id: id,
title: title || this.$t(`tabs.${id}`),
variables,
customCSS: defaultCustomCSS.values || '',
}
})
},
},
},
methods: {
onSubmit () {
this.$emit('submit', { 'ui.studio.themes': this.themes.map(theme => {
theme.values = JSON.stringify(theme.values)
return theme
}),
this.$emit('submit', {
'ui.studio.themes': this.themes.map(theme => {
return {
id: theme.id,
title: theme.title,
values: JSON.stringify(theme.variables),
}
}),
'ui.studio.custom-css': this.themes.map(theme => {
return {
id: theme.id,
title: theme.title,
values: theme.customCSS,
}
}),
})
},
resetColor (key, index, id) {
this.themes.find(theme => theme.id === id).values[key] = id === 'light' ? this.lightModeVariables[key] : this.darkModeVariables[key]
this.$refs.picker[index].closeMenu()
openCustomCSSModal (id) {
const { customCSS } = this.themes.find(t => t.id === id) || {}
this.customCSSModal.id = id
this.customCSSModal.value = customCSS
this.customCSSModal.show = true
},
saveCustomCSSModal () {
this.themes.find(t => t.id === this.customCSSModal.id).customCSS = this.customCSSModal.value
},
resetCustomCSSModal () {
this.customCSSModal.id = ''
this.customCSSModal.value = ''
this.customCSSModal.show = false
},
resetColor (key, theme) {
this.$set(theme.variables, key, theme.id === 'light' ? this.lightModeVariables[key] : this.darkModeVariables[key])
},
},
@@ -1,199 +0,0 @@
<template>
<b-card
title="Foo"
body-class="p-0"
header-bg-variant="white"
footer-bg-variant="white"
footer-class="d-flex flex-wrap flex-fill-child gap-1"
class="shadow-sm"
>
<!-- <template #header>
<h4 class="m-0">
{{ $t('title') }}
</h4>
</template> -->
<b-tabs
data-test-id="theme-tabs"
card
>
<b-tab
v-for="theme in themes"
:key="theme.id"
:title="$t(`tabs.${theme.id}`)"
>
<c-ace-editor
v-model="theme.values"
lang="css"
height="300px"
font-size="14px"
show-line-numbers
:border="false"
:show-popout="true"
@open="openEditorModal(theme.id)"
/>
<b-modal
id="custom-css-editor"
v-model="theme.showEditorModal"
:title="$t('modal.editor')"
cancel-variant="link"
size="lg"
:ok-title="$t('general:label.saveAndClose')"
:cancel-title="$t('general:label.cancel')"
body-class="p-0"
@ok="saveCustomCSSInput(theme.id)"
@hidden="resetCustomCSSInput(theme.id)"
>
<c-ace-editor
v-model="theme.modalValue"
lang="scss"
height="500px"
font-size="14px"
show-line-numbers
:border="false"
:show-popout="false"
/>
</b-modal>
</b-tab>
</b-tabs>
<template #footer>
<c-button-submit
v-if="canManage"
:processing="processing"
:success="success"
:text="$t('admin:general.label.submit')"
class="ml-auto"
@submit="onSubmit"
/>
</template>
</b-card>
</template>
<script>
import { components } from '@cortezaproject/corteza-vue'
const { CAceEditor } = components
export default {
name: 'CUIEditorCustomCSS',
i18nOptions: {
namespaces: 'ui.settings',
keyPrefix: 'editor.custom-css',
},
components: {
CAceEditor,
},
props: {
settings: {
type: Object,
required: true,
},
processing: {
type: Boolean,
value: false,
},
success: {
type: Boolean,
value: false,
},
canManage: {
type: Boolean,
required: true,
},
},
data () {
return {
themes: [
{
'id': 'general',
'title': 'General',
'values': '',
'modalValue': undefined,
showEditorModal: false,
},
{
'id': 'light',
'title': 'Light mode',
'values': '',
'modalValue': undefined,
showEditorModal: false,
},
{
'id': 'dark',
'title': 'Dark mode',
'values': '',
'modalValue': undefined,
showEditorModal: false,
},
],
}
},
watch: {
settings: {
immediate: true,
handler (settings) {
if (settings['ui.studio.custom-css']) {
this.themes = settings['ui.studio.custom-css'].map((theme) => {
return {
id: theme.id,
title: theme.title,
values: theme.values,
modalValue: undefined,
showEditorModal: false,
}
})
}
},
},
},
methods: {
onSubmit () {
this.$emit('submit', {
'ui.studio.custom-css': this.themes.map((theme) => {
return {
id: theme.id,
title: theme.title,
values: theme.values,
}
}),
})
},
openEditorModal (themeId) {
this.themes.forEach((theme) => {
if (theme.id === themeId) {
theme.modalValue = theme.values
theme.showEditorModal = true
}
})
},
saveCustomCSSInput (themeId) {
this.themes.forEach((theme) => {
if (theme.id === themeId) {
theme.values = theme.modalValue
}
})
},
resetCustomCSSInput (themeId) {
this.themes.forEach((theme) => {
if (theme.id === themeId) {
theme.modalValue = undefined
}
})
},
},
}
</script>
@@ -28,7 +28,7 @@ button:disabled {
}
.gap-3 {
gap: 1.5rem;
gap: 1.5rem;
}
.grab {
+1 -18
View File
@@ -64,7 +64,7 @@
-->
<template>
<div
class="spacer"
class="sidebar-spacer d-print-none"
:class="{
'expanded': expanded && pinned,
}"
@@ -221,20 +221,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.spacer {
min-width: 0;
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
&.expanded {
min-width: var(--sidebar-width);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
</style>
+17 -7
View File
@@ -96,7 +96,9 @@ export default {
methods: {
fetchSettings () {
this.incLoader()
this.$SystemAPI.settingsList({ prefix: prefix })
this.$Settings.fetch()
return this.$SystemAPI.settingsList({ prefix: prefix })
.then(settings => {
this.settings = {}
@@ -118,19 +120,27 @@ export default {
})
this.$SystemAPI.settingsUpdate({ values })
.then(() => {
return this.fetchSettings().then(() => {
if ((type === 'branding' && this.settings['ui.studio.sass-installed']) || type === 'customCSS') {
// window.location.reload()
return new Promise((resolve) => {
setTimeout(() => {
const stylesheet = document.querySelector('link[href="custom.css"]')
stylesheet.href = 'custom.css'
resolve()
}, 1000)
})
}
})
})
.then(() => {
this.animateSuccess(type)
this.toastSuccess(this.$t('notification:settings.ui.update.success'))
this.$Settings.fetch()
})
.catch(this.toastErrorHandler(this.$t('notification:settings.ui.update.error')))
.finally(() => {
this[type].processing = false
// Refresh the page if branding variables is updated and sass installed or custom CSS was updated
if ((type === 'branding' && this.settings['ui.studio.sass-installed']) || type === 'customCSS') {
window.location.reload()
}
})
},
},
+1 -19
View File
@@ -63,7 +63,7 @@
-->
<template>
<div
class="spacer d-print-none"
class="sidebar-spacer d-print-none"
:class="{
'expanded': expanded && pinned,
}"
@@ -305,21 +305,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.spacer {
min-width: 0;
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
&.expanded {
min-width: var(--sidebar-width);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
</style>
+1 -19
View File
@@ -42,7 +42,7 @@
-->
<template>
<div
class="spacer"
class="sidebar-spacer d-print-none"
:class="{
'expanded': expanded && pinned,
}"
@@ -123,21 +123,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.spacer {
min-width: 0;
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
&.expanded {
min-width: var(--sidebar-width);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
</style>
+1 -19
View File
@@ -58,7 +58,7 @@
-->
<template>
<div
class="spacer"
class="sidebar-spacer d-print-none"
:class="{
'expanded': expanded && pinned,
}"
@@ -127,21 +127,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.spacer {
min-width: 0;
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
&.expanded {
min-width: var(--sidebar-width);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
</style>
+1 -19
View File
@@ -59,7 +59,7 @@
-->
<template>
<div
class="spacer"
class="sidebar-spacer d-print-none"
:class="{
'expanded': expanded && pinned,
}"
@@ -180,21 +180,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.spacer {
min-width: 0;
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
&.expanded {
min-width: var(--sidebar-width);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
</style>
+1 -19
View File
@@ -54,7 +54,7 @@
-->
<template>
<div
class="spacer"
class="sidebar-spacer d-print-none"
:class="{
'expanded': expanded && pinned,
}"
@@ -141,21 +141,3 @@ export default {
},
}
</script>
<style lang="scss" scoped>
.spacer {
min-width: 0;
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
&.expanded {
min-width: var(--sidebar-width);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
</style>
@@ -145,6 +145,12 @@ export default {
},
},
watch: {
value (value) {
this.currentColor = value
},
},
methods: {
updateColor: debounce(function ({ hex8 = '' }) {
this.currentColor = hex8
@@ -159,7 +165,7 @@ export default {
},
saveColor () {
this.$emit('input', this.currentColor || this.value)
this.$emit('input', this.currentColor)
this.closeMenu()
},
@@ -20,8 +20,8 @@
>
<template #header>
<div
class="d-flex align-items-center justify-content-between px-2"
style="height: 50px;"
class="d-flex align-items-center justify-content-between pl-2"
style="height: 47px;"
>
<img
data-test-id="img-main-logo"
+13 -33
View File
@@ -1,17 +1,17 @@
<template>
<div class="header-navigation d-flex flex-wrap align-items-center pr-3 pb-1 mb-2">
<div class="header-navigation d-flex flex-wrap align-items-center py-2 px-3 gap-1">
<div
class="spacer"
class="sidebar-spacer"
:class="{
'expanded': sidebarPinned,
}"
/>
<h2 class="title py-1 mb-0">
<h2 class="title mb-0">
<slot name="title" />
</h2>
<div class="tools-wrapper py-1 ml-auto">
<div class="tools-wrapper ml-auto">
<slot name="tools" />
</div>
@@ -324,16 +324,8 @@ export default {
</script>
<style lang="scss" scoped>
$header-height: 64px;
$nav-width: 320px;
$nav-icon-size: 40px;
$nav-user-icon-size: 50px;
.icon-logo {
height: calc(#{$header-height} / 2);
background-repeat: no-repeat;
background-position: center;
}
$nav-icon-size: calc(var(--topbar-height) - 24px);
$nav-user-icon-size: calc(var(--topbar-height) - 16px);
.nav-icon {
width: $nav-icon-size;
@@ -347,8 +339,12 @@ $nav-user-icon-size: 50px;
.header-navigation {
width: 100vw;
min-height: $header-height;
min-height: var(--topbar-height);
background-color: var(--topbar-bg);
.sidebar-spacer.expanded {
min-width: calc(var(--sidebar-width) - 50px);
}
}
.avatar {
@@ -365,27 +361,11 @@ $nav-user-icon-size: 50px;
}
}
.spacer {
min-width: 0px;
-webkit-transition: min-width 0.15s ease-in-out;
-moz-transition: min-width 0.15s ease-in-out;
-o-transition: min-width 0.15s ease-in-out;
transition: min-width 0.15s ease-in-out;
&.expanded {
min-width: calc(#{$nav-width} - 42px);
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
.title {
display: flex;
align-items: center;
min-height: $header-height;
padding-left: calc(3.5rem + 6px);
min-height: $nav-user-icon-size;
padding-left: 42px;
.vue-portal-target {
display: -webkit-box; /* For Safari and old versions of Chrome */
@@ -31,10 +31,11 @@ editor:
label:
default: Default
tabs:
general: General
light: Light
dark: Dark
theme:
values:
variables:
white: White
black: Black
primary: Primary
@@ -1,3 +1,15 @@
.gap-1 {
gap: 0.5rem;
}
.gap-2 {
gap: 1rem;
}
.gap-3 {
gap: 1.5rem;
}
:disabled, .disabled {
cursor: not-allowed;
}
@@ -84,3 +96,19 @@ a.btn:not(.btn-link) {
::-webkit-scrollbar-corner {
background: transparent;
}
.sidebar-spacer {
min-width: 0px;
-webkit-transition: min-width 0.15s ease-in-out;
-moz-transition: min-width 0.15s ease-in-out;
-o-transition: min-width 0.15s ease-in-out;
transition: min-width 0.15s ease-in-out;
&.expanded {
min-width: calc(var(--sidebar-width));
-webkit-transition: min-width 0.2s ease-in-out;
-moz-transition: min-width 0.2s ease-in-out;
-o-transition: min-width 0.2s ease-in-out;
transition: min-width 0.2s ease-in-out;
}
}
@@ -531,6 +531,10 @@
color: $headings-color;
}
hr {
border-color: $hr-border-color;
}
//Utilities
@each $color, $value in $theme-colors {
@include bg-variant(".bg-#{$color}", $value, true);
@@ -38,7 +38,7 @@ $gray-700: if(lightness($black) < lightness($white), mix($black, $white, 70%), m
$gray-800: if(lightness($black) < lightness($white), mix($black, $white, 80%), mix($white, $black, 80%));
$gray-900: if(lightness($black) < lightness($white), mix($black, $white, 90%), mix($white, $black, 90%));
$yiq-contrasted-threshold: 43; // Our magic number, we can pretend its 42..
$yiq-contrasted-threshold: 42; // Our magic number, we can pretend its 42..
$yiq-text-dark: $black !default;
$yiq-text-light: $white !default;
@@ -53,6 +53,7 @@ $theme-colors: map-merge(
);
// Grid
$grid-gutter-width: 32px !default;
$grid-breakpoints: (
xs: 0,
sm: 576px,
@@ -66,6 +67,7 @@ $grid-breakpoints: (
$body-bg: $light !default;
$body-color: $black !default;
$border-color: $extra-light !default;
$hr-border-color: $border-color !default;
// Options
$enable-rounded: true !default;
@@ -155,16 +157,18 @@ $input-placeholder-color: $extra-light !default;
// Corteza specific variables
// Topbar
$topbar-height: 64px !default;
$topbar-bg: #F3F5F7 !default;
$topbar-bg: $body-bg !default;
// Sidebar
$sidebar-width: 320px !default;
$sidebar-bg: #F3F5F7 !default;
$sidebar-bg: $white !default;
$corteza-specific: () !default;
$corteza-specific: map-merge(
(
"body-bg": $body-bg,
"topbar-height": $topbar-height,
"topbar-bg": $topbar-bg,
"sidebar-width": $sidebar-width,
"sidebar-bg": $sidebar-bg,
"font-regular": $font-regular,
@@ -174,8 +178,6 @@ $corteza-specific: map-merge(
"btn-font-family": $btn-font-family,
"btn-padding-x": $btn-padding-x,
"btn-padding-y": $btn-padding-y,
"topbar-height": $topbar-height,
"topbar-bg": $topbar-bg
),
$corteza-specific
);
+5 -5
View File
@@ -109,7 +109,7 @@ func processNewTheme() (themes []types.Theme) {
"light":"#F3F5F7",
"extra-light":"#E4E9EF",
"body-bg":"#F3F5F7",
"sidebar-bg": "#F3F5F7",
"sidebar-bg": "#FFFFFF",
"topbar-bg": "#F3F5F7"
}`
@@ -122,11 +122,11 @@ func processNewTheme() (themes []types.Theme) {
"success":"#43AA8B",
"warning":"#E2A046",
"danger":"#E54122",
"light":"#768D9A",
"extra-light":"#23495F",
"light":"#23495F",
"extra-light":"#768D9A",
"body-bg":"#092B40",
"sidebar-bg": "#768D9A",
"topbar-bg": "#768D9A"
"sidebar-bg": "#0B344E",
"topbar-bg": "#092B40"
}`
themes = []types.Theme{