Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
66572a8
Add button to add a new custom theme
akila-i May 28, 2023
f419ae1
Add sample custom themes for reference
akila-i May 28, 2023
cb1a403
Add buttons for available customThemes
akila-i May 28, 2023
3e4dc47
Fix visibility of Add New Custom Theme Button
akila-i May 28, 2023
4d27411
Set custom themes to be saved in local storage
akila-i May 31, 2023
720327e
Refactor saving custom themes in local storage
akila-i Jun 3, 2023
b0bd616
Add option to delete custom themes
akila-i Jun 4, 2023
ef36d8c
Add button to edit custom themes
akila-i Jun 4, 2023
dac252a
Add modal to select colors for custom themes (no color picker yet)
akila-i Jun 8, 2023
f65f1d7
Change modal to select colors for edit custom themes & appear colors …
akila-i Jun 9, 2023
d47e13b
Refactor code to follow the new eslint
akila-i Jun 12, 2023
65378e0
Add text box component for theme name
akila-i Jun 12, 2023
e244455
Make only the 1st letter capital in strings
akila-i Jun 13, 2023
065d4cf
Add buttons to pick customTheme colors (no color picker yet)
akila-i Jun 13, 2023
8471168
Add text translations in the modal
akila-i Jun 14, 2023
3aa6a3d
Add color generation when button clicked
akila-i Jun 14, 2023
ace7972
Add dependancy vue-color for epub viewer
akila-i Jun 18, 2023
79bf698
Add ColorPicker Modal for epub viewer
akila-i Jun 18, 2023
730b1cd
Modify addNewTheme to use the color picker
akila-i Jun 18, 2023
d8d9331
Add styling for the color picker
akila-i Jun 22, 2023
1dfaa80
Delete sample custom themes as they are no longer needed
akila-i Jun 22, 2023
3497425
Add changes from lint-frontend
akila-i Jun 22, 2023
a595250
Add textbox funstions for customThemeName
akila-i Jul 1, 2023
af390b8
Fix textbox validation - partially
akila-i Jul 1, 2023
1882507
Fix linting errors
akila-i Jul 1, 2023
d23af43
Change color picker btn text from Accept to Select
akila-i Jul 3, 2023
ab910e5
Add a label to theme preview box
akila-i Jul 3, 2023
5155f56
Add themeAriaLabel for custom themes
akila-i Jul 7, 2023
32e371f
Change color picker library
akila-i Jul 9, 2023
546af85
Fix linting errors
akila-i Jul 9, 2023
461755a
Make the theme applied when added or edited
akila-i Jul 9, 2023
e8ecf4e
Introduce new AddEditThemeModal
akila-i Jul 19, 2023
c158bd1
Change the Modal component into AddEditCustomThemeModal
akila-i Jul 21, 2023
2d25b0e
Add warning for unsigned users
akila-i Jul 21, 2023
426c7a0
Change link color when a theme is applied
akila-i Jul 23, 2023
b53070a
bugFix: set theme name on saving
akila-i Jul 23, 2023
a41a7a2
Apply default theme when deleting the currently applied theme
akila-i Jul 23, 2023
718ea2b
Remove unused components
akila-i Jul 23, 2023
0f61185
Add aria labels for edit & delete theme buttons
akila-i Aug 21, 2023
8e64e86
Add aria labels for color select buttons in modal
akila-i Aug 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,331 @@
<template>

<div>
<!-- Modal of theme options -->
<KModal
v-if="!showColorPicker"
:title="generateTitle"
:submitText="coreString('saveAction')"
:cancelText="coreString('cancelAction')"
:disabled="submitting"
@submit="handleSubmit"
@cancel="$emit('cancel')"
>

<KTextbox
ref="customThemeName"
v-model.trim="customThemeName"
class="theme-name"
type="text"
:label="$tr('customThemeNameLabel')"
:autofocus="true"
:disabled="submitting"
:invalid="customThemeNameIsInvalid"
:invalidText="customThemeNameIsInvalidText"
:maxlength="50"
@blur="customThemeNameBlurred = true"
/>

<UiAlert
v-if="showUIAlert"
:dismissible="false"
:removeIcon="true"
type="warning"
:style="{ margin: '8px 24px 12px 24px', width: 'auto' }"
>
{{ $tr('signIn') }}
</UiAlert>

<h3 id="theme-preview-h3">
{{ $tr('customThemePreview') }}
</h3>

<div
class="theme-preview"
:style="{ backgroundColor: tempTheme.backgroundColor, color: tempTheme.textColor }"
>
<p>
The quick brown fox jumps over the lazy dog.
<a :style="{ color: tempTheme.linkColor }">This is a link</a>
</p>
</div>

<div :class="{ 'color-select-container-mobile': windowIsSmall }">
<div class="theme-option-container">
<KButton
class="theme-color-button"
:aria-label="generateSelectColorAriaLabel('backgroundColor')"
:appearanceOverrides="themeColorOptionStyles(tempTheme.backgroundColor)"
@click="showColorPicker = 'backgroundColor'"
/>
<p>{{ $tr('themeBackgroundColorButtonDescription') }}</p>
</div>

<div class="theme-option-container">
<KButton
class="theme-color-button"
:aria-label="generateSelectColorAriaLabel('textColor')"
:appearanceOverrides="themeColorOptionStyles(tempTheme.textColor)"
@click="showColorPicker = 'textColor'"
/>
<p>{{ $tr('themeTextColorButtonDescription') }}</p>
</div>

<div class="theme-option-container">
<KButton
class="theme-color-button"
:aria-label="generateSelectColorAriaLabel('linkColor')"
:appearanceOverrides="themeColorOptionStyles(tempTheme.linkColor)"
@click="showColorPicker = 'linkColor'"
/>
<p>{{ $tr('themeLinkColorButtonDescription') }}</p>
</div>
</div>

</KModal>

<!-- modal of color picker -->
<ColorPickerModal
v-if="showColorPicker"
:colorPicker="showColorPicker"
:color="tempTheme[showColorPicker]"
@submit="setThemeColor($event)"
@cancel="showColorPicker = null"
/>
<!-- @submit="setThemeColor($event)" -->

</div>

</template>


<script>

import commonCoreStrings from 'kolibri.coreVue.mixins.commonCoreStrings';
import Lockr from 'lockr';
import useKResponsiveWindow from 'kolibri.coreVue.composables.useKResponsiveWindow';
import { mapGetters } from 'vuex';
import UiAlert from 'kolibri-design-system/lib/keen/UiAlert';
import ColorPickerModal from './ColorPickerModal';

export default {
name: 'AddEditCustomThemeModal',
components: {
ColorPickerModal,
UiAlert,
},
mixins: [commonCoreStrings],
setup() {
const { windowIsLarge, windowIsMedium, windowIsSmall } = useKResponsiveWindow();
return {
windowIsLarge,
windowIsMedium,
windowIsSmall,
};
},
props: {
modalMode: {
type: String,
required: true,
},
theme: {
type: Object,
required: true,
},
themeName: {
type: String,
required: true,
},
},
data() {
return {
customThemeName: this.themeName,
customThemeNameBlurred: false,
submitting: false,
formSubmitted: false,
tempTheme: {
backgroundColor: this.theme.backgroundColor,
textColor: this.theme.textColor,
linkColor: this.theme.linkColor || '#0000EE', //because fixed themes dont have a link color
},
showColorPicker: null,
existingCustomThemeNames: [],
showUIAlert: false,
};
},
computed: {
...mapGetters(['isUserLoggedIn']),
generateTitle() {
if (this.modalMode === 'add') {
return this.$tr('addCustomThemeTitle');
} else if (this.modalMode === 'edit') {
return this.$tr('editCustomThemeTitle');
} else {
return ''; // not supposed to happen
}
},
customThemeNameIsInvalidText() {
if (!this.formSubmitted) {
if (this.customThemeName === '') {
return this.coreString('requiredFieldError');
}
if (this.existingCustomThemeNames.includes(this.customThemeName)) {
return this.$tr('duplicateCustomThemeName');
}
}
return '';
},
customThemeNameIsInvalid() {
return Boolean(this.customThemeNameIsInvalidText);
},
formIsValid() {
return !this.customThemeNameIsInvalid;
},
},
mounted() {
this.existingCustomThemeNames = Object.keys(
Lockr.get('kolibriEpubRendererCustomThemes') || {}
);
//if the modalMode is 'edit',
//remove the name of the theme being edited from the list of existing names
if (this.modalMode === 'edit') {
const selectedThemeIndex = this.existingCustomThemeNames.indexOf(this.themeName);
this.existingCustomThemeNames.splice(selectedThemeIndex, 1);
}
},
methods: {
handleSubmit() {
this.submitting = true;
if (this.formIsValid && this.isUserLoggedIn) {
this.formSubmitted = true;
this.$emit('submit', { ...this.tempTheme, name: this.customThemeName });
} else {
this.showUIAlert = !this.isUserLoggedIn;
this.submitting = false;
this.$refs.customThemeName.focus();
}
},
themeColorOptionStyles(bgColor) {
return {
backgroundColor: bgColor,
':hover': {
backgroundColor: bgColor,
opacity: 0.9,
boxShadow: '0 1px 4px',
},
};
},
setThemeColor(color) {
if (this.showColorPicker == 'backgroundColor') {
this.tempTheme.backgroundColor = color.hex;
} else if (this.showColorPicker == 'textColor') {
this.tempTheme.textColor = color.hex;
} else if (this.showColorPicker == 'linkColor') {
this.tempTheme.linkColor = color.hex;
}
this.showColorPicker = null;
},
generateSelectColorAriaLabel(color) {
return this.$tr('select', { color });
},
},
$trs: {
customThemePreview: {
message: 'Theme preview',
context: 'Heading for the preview of the custom theme that is being created or edited',
},
themeBackgroundColorButtonDescription: {
message: 'Background',
context: 'Description of the button to change the background color of the custom theme',
},
themeTextColorButtonDescription: {
message: 'Text',
context: 'Description of the button to change the text color of the custom theme',
},
themeLinkColorButtonDescription: {
message: 'Links',
context: 'Description of the button to change the link color of the custom theme',
},
addCustomThemeTitle: {
message: 'Add new theme',
context: 'Title of the modal to add a new custom theme',
},
editCustomThemeTitle: {
message: 'Edit theme',
context: 'Title of the modal to edit an existing custom theme',
},
duplicateCustomThemeName: {
message: 'A theme with this name already exists',
context: 'Error message when trying to add a custom theme with a name that already exists',
},
customThemeNameLabel: {
message: 'Theme name',
context: 'Label for the textbox to enter the name of the custom theme',
},
signIn: {
message: 'Sign in or create an account to save your new theme',
context:
'Message that a learner will see upon trying to save a custom theme if they are not signed in to Kolibri.',
},
select: {
message: 'Select {color}',
context: 'Aria label for the button to select a color for a custom theme',
},
},
};

</script>


<style>

.theme-name {
margin: 24px;
}

.theme-preview {
padding: 24px;
margin: 12px 24px;
border: 1px solid #cccccc;
border-radius: 4px;
}

#theme-preview-h3 {
margin: 0 24px;
}

.theme-option-container {
float: left;
width: 33.33%;
padding: 10px;
text-align: center;
}

.theme-color-button {
width: 75px;
height: 6vh;
margin: 0 auto;
border: 1px solid #cccccc;
border-radius: 4px;
transition: 'box-shadow 0.3s ease-in-out';
}

.color-select-container-mobile {
display: flex;
flex-direction: column;
width: 90%;
margin: auto;
}

.color-select-container-mobile .theme-option-container {
display: flex;
flex-direction: row-reverse;
width: 100%;
margin-left: 10px;
}

.color-select-container-mobile .theme-option-container .theme-color-button {
margin-right: 10px;
}

</style>
Loading