@ -1,57 +1,180 @@
< template class = "personal-theme" >
< h4 class = "mb-4" > { { t ( "Configure chamilo colors" ) } } < / h4 >
< div class = "grid grid-cols-2 gap-2 mb-8" >
<!-- Advanced mode -- >
< div v-show ="isAdvancedMode" >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "primaryColor"
: label = "t('Pick p rimary color')"
v - model = "colorPrimary "
: label = "t('Primary color')"
/ >
< BaseColorPicker
v - model = "primaryColor Gradient"
: label = "t('Pick primary color gradient ')"
v - model = "colorPrimary Gradient"
: label = "t('Primary color hover/background ')"
/ >
< BaseColorPicker
v - model = "secondaryColor"
: label = "t('Pick secondary color')"
v - model = "colorPrimaryButtonText"
: label = "t('Primary color button text')"
: error = "colorPrimaryButtonTextError"
/ >
< BaseColorPicker
v - model = "secondaryColorGradient"
: label = "t('Pick secondary color gradient')"
v - model = "colorPrimaryButtonAlternativeText"
: label = "t('Primary color button alternative text')"
: error = "colorPrimaryButtonAlternativeTextError"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "tertiaryColor"
: label = "t('Pick tertiary color')"
v - model = "colorSecondary "
: label = "t('Second ary color')"
/ >
< BaseColorPicker
v - model = "tertiaryColor Gradient"
: label = "t('Pick tertiary color gradient ')"
v - model = "colorSecondary Gradient"
: label = "t('Secondary color hover/background ')"
/ >
< BaseColorPicker
v - model = "successColor"
: label = "t('Pick success color')"
v - model = "colorSecondaryButtonText"
: label = "t('Secondary color button text')"
: error = "colorSecondaryButtonTextError"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "successColorGradient"
: label = "t('Pick success color gradient')"
v - model = "colorTertiary "
: label = "t('Tertiary color ')"
/ >
< BaseColorPicker
v - model = "dangerColor "
: label = "t('Pick danger color ')"
v - model = "colorTertiaryGradient "
: label = "t('Tertiary color hover/background ')"
/ >
< / div >
< div class = "flex flex-wrap mb-4 gap-4" >
< BaseInputText
v - model = "themeTitle"
: label = "t('Title')"
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "colorSuccess"
: label = "t('Success color')"
/ >
< BaseColorPicker
v - model = "colorSuccessGradient"
: label = "t('Success color hover/background')"
/ >
< BaseColorPicker
v - model = "colorSuccessButtonText"
: label = "t('Success color button text')"
: error = "colorSuccessButtonTextError"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "colorInfo"
: label = "t('Info color')"
/ >
< BaseColorPicker
v - model = "colorInfoGradient"
: label = "t('Info color hover/background')"
/ >
< BaseColorPicker
v - model = "colorInfoButtonText"
: label = "t('Info color button text')"
: error = "colorInfoButtonTextError"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "colorWarning"
: label = "t('Warning color')"
/ >
< BaseColorPicker
v - model = "colorWarningGradient"
: label = "t('Warning color hover/background')"
/ >
< BaseColorPicker
v - model = "colorWarningButtonText"
: label = "t('Warning color button text')"
: error = "colorWarningButtonTextError"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "colorDanger"
: label = "t('Danger color')"
/ >
< BaseColorPicker
v - model = "colorDangerGradient"
: label = "t('Danger color hover/background')"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "formColor"
: label = "t('Form outline color')"
/ >
< / div >
< / div >
<!-- Simple mode -- >
< div v-show ="!isAdvancedMode" >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "colorPrimary"
: label = "t('Primary color')"
/ >
< BaseColorPicker
v - model = "colorSecondary"
: label = "t('Secondary color')"
/ >
< BaseColorPicker
v - model = "colorTertiary"
: label = "t('Tertiary color')"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "colorSuccess"
: label = "t('Success color')"
/ >
< BaseColorPicker
v - model = "colorInfo"
: label = "t('Info color')"
/ >
< BaseColorPicker
v - model = "colorWarning"
: label = "t('Warning color')"
/ >
< BaseColorPicker
v - model = "colorDanger"
: label = "t('Danger color')"
/ >
< / div >
< div class = "flex flex-col gap-2 mb-3 md:grid md:grid-cols-2 xl:grid-cols-4" >
< BaseColorPicker
v - model = "formColor"
: label = "t('Form outline color')"
/ >
< / div >
< / div >
< div class = "flex flex-wrap mb-4 gap-3" >
< BaseButton
type = "primary"
icon = "send"
: label = "t('Save')"
@ click = "saveColors"
/ >
< BaseButton
type = "black"
icon = "cog"
: label = "isAdvancedMode ? t('Hide advanced mode') : t('Show advanced mode')"
@ click = "isAdvancedMode = !isAdvancedMode"
/ >
< / div >
< hr / >
@ -59,19 +182,18 @@
< div class = "mb-4" >
< p class = "mb-3 text-lg" > { { t ( "Buttons" ) } } < / p >
< div class = "flex flex-row flex-wrap" >
< div class = "flex flex-row flex-wrap mb-3 " >
< BaseButton
class = "mr-2 mb-2"
: label = "t('Button ')"
: label = "t('Primary ')"
type = "primary"
icon = "eye-on"
/ >
< BaseButton
class = "mr-2 mb-2"
: label = "t('Disabled ')"
type = "primary"
: label = "t('Primary alternative ')"
type = "primary-alternative "
icon = "eye-on"
disabled
/ >
< BaseButton
class = "mr-2 mb-2"
@ -85,16 +207,24 @@
type = "black"
icon = "eye-on"
/ >
< / div >
< div class = "flex flex-row flex-wrap mb-3" >
< BaseButton
class = "mr-2 mb-2"
type = "primary "
icon = "cog "
only - icon
: label = "t('Success') "
type = "success "
icon = "send"
/ >
< BaseButton
class = "mr-2 mb-2"
: label = "t('Success')"
type = "success"
: label = "t('Info')"
type = "info"
icon = "send"
/ >
< BaseButton
class = "mr-2 mb-2"
: label = "t('Warning')"
type = "warning"
icon = "send"
/ >
< BaseButton
@ -104,12 +234,28 @@
icon = "delete"
/ >
< / div >
< div class = "flex flex-row flex-wrap mb-3" >
< BaseButton
class = "mr-2 mb-2"
: label = "t('Disabled')"
type = "primary"
icon = "eye-on"
disabled
/ >
< BaseButton
class = "mr-2 mb-2"
type = "primary"
icon = "cog"
only - icon
/ >
< / div >
< / div >
< div class = "mb-4" >
< p class = "mb-3 text-lg" > { { t ( "Menu on button pressed" ) } } < / p >
< p class = "mb-3 text-lg" > { { t ( "Dropdowns" ) } } < / p >
< div class = "flex flex-row gap-3" >
< BaseButton
class = "mr-2 mb-2"
class = "mr-3 mb-2"
type = "primary"
icon = "cog"
popup - identifier = "menu"
@ -120,7 +266,31 @@
id = "menu"
ref = "menu"
: model = "menuItems"
> < / BaseMenu >
/ >
< BaseDropdown
v - model = "dropdown"
class = "w-36"
input - id = "dropdown"
option - label = "label"
option - value = "value"
: label = "t('Dropdown')"
: options = " [
{
label : t ( 'Option 1' ) ,
value : 'option_1' ,
} ,
{
label : t ( 'Option 2' ) ,
value : 'option_2' ,
} ,
{
label : t ( 'Option 3' ) ,
value : 'option_3' ,
} ,
] "
name = "dropdown"
/ >
< / div >
< / div >
< div class = "mb-4" >
@ -147,15 +317,39 @@
< / div >
< div class = "mb-4" >
< p class = "mb-3 text-lg" > Forms < / p >
< p class = "mb-3 text-lg" > { { t ( "Toggle" ) } } < / p >
< BaseToggleButton
: model - value = "toggleState"
: off - label = "t('Show all')"
: on - label = "t('Hide all')"
off - icon = "eye-on"
on - icon = "eye-off"
size = "normal"
without - borders
@ update : model - value = "toggleState = !toggleState"
/ >
< / div >
< div class = "mb-4" >
< p class = "mb-3 text-lg" > { { t ( "Forms" ) } } < / p >
< BaseInputText
: label = "t('This is the default form')"
: model - value = "null"
/ >
< BaseInputText
v - model = "inputText"
: label = "t('This is a text example')"
: label = "t('This is a form with an error')"
: is - invalid = "true"
: model - value = "null"
/ >
< BaseInputDate
id = "date"
: label = "t('Date')"
class = "w-32"
/ >
< / div >
< div class = "mb-4" >
< p class = "mb-3 text-lg" > Dialogs < / p >
< p class = "mb-3 text-lg" > { { t ( " Dialogs" ) } } < / p >
< BaseButton
: label = "t('Show dialog')"
type = "black"
@ -169,13 +363,25 @@
@ cancel - clicked = "isDialogVisible = false"
/ >
< / div >
< div class = "mb-4" >
< p class = "mb-3 text-lg" > { { t ( "Some more elements" ) } } < / p >
< div class = "course-tool cursor-pointer" >
< div class = "course-tool__link hover:primary-gradient hover:bg-primary-gradient/10" >
< span
class = "course-tool__icon mdi mdi-bookshelf"
aria - hidden = "true"
/ >
< / div >
< p class = "course-tool__title" > { { t ( "Documents" ) } } < / p >
< / div >
< / div >
< / template >
< script setup >
import BaseButton from "../../components/basecomponents/BaseButton.vue"
import { useI18n } from "vue-i18n"
import BaseMenu from "../../components/basecomponents/BaseMenu.vue"
import { ref } from "vue"
import { provide , ref , watch } from "vue"
import BaseCheckbox from "../../components/basecomponents/BaseCheckbox.vue"
import BaseRadioButtons from "../../components/basecomponents/BaseRadioButtons.vue"
import BaseDialogConfirmCancel from "../../components/basecomponents/BaseDialogConfirmCancel.vue"
@ -183,36 +389,210 @@ import BaseInputText from "../../components/basecomponents/BaseInputText.vue"
import BaseColorPicker from "../../components/basecomponents/BaseColorPicker.vue"
import { useTheme } from "../../composables/theme"
import axios from "axios"
import { useNotification } from "../../composables/notification"
import BaseDropdown from "../../components/basecomponents/BaseDropdown.vue"
import BaseInputDate from "../../components/basecomponents/BaseInputDate.vue"
import BaseToggleButton from "../../components/basecomponents/BaseToggleButton.vue"
import Color from "colorjs.io"
const { t } = useI18n ( )
const { getColorTheme , getColors } = useTheme ( )
const { showSuccessNotification , showErrorNotification } = useNotification ( )
let colorPrimary = getColorTheme ( "--color-primary-base" )
let colorPrimaryGradient = getColorTheme ( "--color-primary-gradient" )
let colorPrimaryButtonText = getColorTheme ( "--color-primary-button-text" )
let colorPrimaryButtonAlternativeText = getColorTheme ( "--color-primary-button-alternative-text" )
let colorSecondary = getColorTheme ( "--color-secondary-base" )
let colorSecondaryGradient = getColorTheme ( "--color-secondary-gradient" )
let colorSecondaryButtonText = getColorTheme ( "--color-secondary-button-text" )
let colorTertiary = getColorTheme ( "--color-tertiary-base" )
let colorTertiaryGradient = getColorTheme ( "--color-tertiary-gradient" )
let colorTertiaryButtonText = getColorTheme ( "--color-tertiary-button-text" )
const themeTitle = ref ( )
let primaryColor = getColorTheme ( "--color-primary-base" )
let primaryColorGradient = getColorTheme ( "--color-primary-gradient" )
let secondaryColor = getColorTheme ( "--color-secondary-base" )
let secondaryColorGradient = getColorTheme ( "--color-secondary-gradient" )
let tertiaryColor = getColorTheme ( "--color-tertiary-base" )
let tertiaryColorGradient = getColorTheme ( "--color-tertiary-gradient" )
let successColor = getColorTheme ( "--color-success-base" )
let successColorGradient = getColorTheme ( "--color-success-gradient" )
let dangerColor = getColorTheme ( "--color-danger-base" )
let colorSuccess = getColorTheme ( "--color-success-base" )
let colorSuccessGradient = getColorTheme ( "--color-success-gradient" )
let colorSuccessButtonText = getColorTheme ( "--color-success-button-text" )
let colorInfo = getColorTheme ( "--color-info-base" )
let colorInfoGradient = getColorTheme ( "--color-info-gradient" )
let colorInfoButtonText = getColorTheme ( "--color-info-button-text" )
let colorWarning = getColorTheme ( "--color-warning-base" )
let colorWarningGradient = getColorTheme ( "--color-warning-gradient" )
let colorWarningButtonText = getColorTheme ( "--color-warning-button-text" )
let colorDanger = getColorTheme ( "--color-danger-base" )
let colorDangerGradient = getColorTheme ( "--color-danger-gradient" )
let colorDangerButtonText = getColorTheme ( "--color-danger-button-text" )
let formColor = getColorTheme ( "--color-form-base" )
const saveColors = async ( ) => {
let colors = getColors ( )
/ / T O D O s e n d c o l o r s t o b a c k e n d , t h e n n o t i f y i f w a s c o r r e c t o r i n c o r r e c t
try {
await axios . post ( "/api/color_themes" , {
title : themeTitle . value ,
variables : colors ,
title : themeTitle . value , variables : colors ,
} )
showSuccessNotification ( t ( "Colors updated" ) )
} catch ( error ) {
showErrorNotification ( error )
console . error ( error )
}
}
const isAdvancedMode = ref ( false )
watch ( colorPrimary , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorPrimaryGradient . value = makeGradient ( newValue )
colorPrimaryButtonText . value = newValue
colorPrimaryButtonAlternativeText . value = makeTextWithContrast ( newValue )
}
checkColorContrast ( newValue , colorPrimaryButtonText . value , colorPrimaryButtonAlternativeTextError )
} )
watch ( colorSecondary , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorSecondaryGradient . value = makeGradient ( newValue )
colorSecondaryButtonText . value = makeTextWithContrast ( newValue )
}
checkColorContrast ( newValue , colorSecondaryButtonText . value , colorSecondaryButtonTextError )
} )
watch ( colorTertiary , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorTertiaryButtonText . value = newValue
colorTertiaryGradient . value = makeGradient ( newValue )
}
} )
watch ( colorSuccess , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorSuccessGradient . value = makeGradient ( newValue )
colorSuccessButtonText . value = makeTextWithContrast ( newValue )
}
checkColorContrast ( newValue , colorSuccessButtonText . value , colorSuccessButtonTextError )
} )
watch ( colorInfo , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorInfoGradient . value = makeGradient ( newValue )
colorInfoButtonText . value = makeTextWithContrast ( newValue )
}
checkColorContrast ( newValue , colorInfoButtonText . value , colorInfoButtonTextError )
} )
watch ( colorWarning , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorWarningGradient . value = makeGradient ( newValue )
colorWarningButtonText . value = makeTextWithContrast ( newValue )
}
checkColorContrast ( newValue , colorWarningButtonText . value , colorWarningButtonTextError )
} )
watch ( colorDanger , ( newValue ) => {
if ( ! isAdvancedMode . value ) {
colorDangerGradient . value = makeGradient ( newValue )
colorDangerButtonText . value = makeTextWithContrast ( newValue )
}
} )
function makeGradient ( color ) {
const light = color . clone ( ) . to ( "oklab" ) . l
/ / w h e n c o l o r i s l i g h t ( l i g h t n e s s > 0 . 5 ) , d a r k e n g r a d i e n t c o l o r
/ / w h e n c o l o r i s d a r k , l i g h t e n g r a d i e n t c o l o r
/ / T h e v a l u e s 0 . 5 a n d 1 . 6 w e r e c h o s e n t h r o u g h e x p e r i m e n t a t i o n , t h e r e c o u l d b e a b e t t e r w a y t o d o t h i s
if ( light > 0.5 ) {
return color
. clone ( )
. set ( { "oklab.l" : ( l ) => l * 0.8 } )
. to ( "srgb" )
} else {
return color
. clone ( )
. set ( { "oklab.l" : ( l ) => l * 1.6 } )
. to ( "srgb" )
}
}
function makeTextWithContrast ( color ) {
/ / a c c o r d i n g t o c o l o r j s l i b r a r y h t t p s : / / c o l o r j s . i o / d o c s / c o n t r a s t # a c c e s s i b l e - p e r c e p t u a l - c o n t r a s t - a l g o r i t h m - a p c a
/ / t h i s a l g o r i t h m i s b e t t e r t h a n W C A G G 2 . 1 t o c h e c k f o r c o n t r a s t
/ / " A P C A i s b e i n g e v a l u a t e d f o r u s e i n v e r s i o n 3 o f t h e W 3 C W e b C o n t e n t A c c e s s i b i l i t y G u i d e l i n e s ( W C A G ) "
let onWhite = Math . abs ( color . contrast ( "white" , "APCA" ) )
let onBlack = Math . abs ( color . contrast ( "black" , "APCA" ) )
return onWhite > onBlack ? new Color ( "white" ) : new Color ( "black" )
}
/ / c h e c k f o r c o n t r a s t o f t e x t
const colorPrimaryButtonTextError = ref ( "" )
watch ( colorPrimaryButtonText , ( newValue ) => {
checkColorContrast ( new Color ( "white" ) , newValue , colorPrimaryButtonTextError )
} )
const colorPrimaryButtonAlternativeTextError = ref ( "" )
watch ( colorPrimaryButtonAlternativeText , ( newValue ) => {
checkColorContrast ( colorPrimary . value , newValue , colorPrimaryButtonAlternativeTextError )
} )
const colorSecondaryButtonTextError = ref ( "" )
watch ( colorSecondaryButtonText , ( newValue ) => {
checkColorContrast ( colorSecondary . value , newValue , colorSecondaryButtonTextError )
} )
const colorTertiaryButtonTextError = ref ( "" )
watch ( colorTertiaryButtonText , ( newValue ) => {
checkColorContrast ( colorTertiary . value , newValue , colorTertiaryButtonTextError )
} )
const colorSuccessButtonTextError = ref ( "" )
watch ( colorSuccessButtonText , ( newValue ) => {
checkColorContrast ( colorSuccess . value , newValue , colorSuccessButtonTextError )
} )
const colorInfoButtonTextError = ref ( "" )
watch ( colorInfoButtonText , ( newValue ) => {
checkColorContrast ( colorInfo . value , newValue , colorInfoButtonTextError )
} )
const colorWarningButtonTextError = ref ( "" )
watch ( colorWarningButtonText , ( newValue ) => {
checkColorContrast ( colorWarning . value , newValue , colorWarningButtonTextError )
} )
const colorDangerButtonTextError = ref ( "" )
watch ( colorDangerButtonText , ( newValue ) => {
checkColorContrast ( new Color ( "white" ) , newValue , colorDangerButtonTextError )
} )
function checkColorContrast ( background , foreground , textErrorRef ) {
if ( isAdvancedMode . value ) {
/ / u s i n g A P C A f o r t e x t c o n t r a s t i n b u t t o n s . I n c h a m i l o b u t t o n s t h e t e x t
/ / h a s a f o n t s i z e o f 1 6 p x a n d w e i g h t o f 6 0 0
/ / L c 6 0 T h e m i n i m u m l e v e l r e c o m m e n d e d f o r c o n t e n t t e x t t h a t i s n o t b o d y , c o l u m n , o r b l o c k t e x t
/ / h t t p s : / / g i t . a p c a c o n t r a s t . c o m / d o c u m e n t a t i o n / A P C A _ i n _ a _ N u t s h e l l # u s e - c a s e - - s i z e - r a n g e s
let contrast = Math . abs ( background . contrast ( foreground , "APCA" ) )
console . log ( ` Contrast ${ contrast } ` )
if ( contrast < 60 ) {
textErrorRef . value = t ( "Does not have enough contrast against background" )
} else {
textErrorRef . value = ""
}
}
}
/ / p r o p e r t i e s f o r e x a m p l e c o m p o n e n t s
const menu = ref ( "menu" )
const menuItems = [ { label : t ( "Item 1" ) } , { label : t ( "Item 2" ) } , { label : t ( "Item 3" ) } ]
const toggle = ( event ) => {
menu . value . toggle ( event )
}
const dropdown = ref ( "" )
const checkbox1 = ref ( true )
const checkbox2 = ref ( false )
@ -226,5 +606,11 @@ const radioValue = ref("value1")
const isDialogVisible = ref ( false )
const inputText = ref ( "" )
const toggleState = ref ( true )
/ / n e e d e d f o r c o u r s e t o o l
const isSorting = ref ( false )
const isCustomizing = ref ( false )
provide ( "isSorting" , isSorting )
provide ( "isCustomizing" , isCustomizing )
< / script >