eScriptorium Frontend development
The frontend is divided in three parts:
- vendored libraries, pulled from NPM registry
- the eScriptorium editor, built with Vue.js
front folder structure
front folder of eScriptorium includes several folders/files. Among them are
.js files allowing the application to run,
.vue files detailing the Single-File implementation of the Vue.js components, and various NPM and webpack configuration files.
css ➔ Folder containing
.cssfiles used to style the Vue.js application.
dist ~NOT COMMITTED~ ➔ Folder containing the compressed archives of the Vue.js code (3 chunks,
editor) that will be used by the Django application to integrate it.
- node_modules ~NOT COMMITTED~ ➔ Folder acting like a cache for the external modules that the Vue.js part depends on.
src ➔ Folder containing several required
.jsfiles to run the Vue.js part of eScriptorium.
editor ➔ Folder containing the core of the Vue.js part with notably:
main.js: the entry point of the application,
api.js: the list of API calls,
- the implementation of the VueX store in
index.jsand under the
- editor ➔ Folder containing the core of the Vue.js part with notably:
vue/components ➔ Folder gathering all the
.vueSingle File Components of the Vue.js part of eScriptorium.
package-lock.json➔ File describing a single representation of the project dependency tree.
package.json➔ File listing all Vue.js application dependencies.
webpack.common.js➔ File detailing the common configuration to serve the application code using
webpack.dev.js➔ File detailing the specific dev configuration to serve the application code using
webpack.prod.js➔ File detailing the specific production configuration to serve the application code using
How to install
We use NPM to handle project dependencies.
To install vendored libraries used by the application and generate the node_modules folder, you have to run once this command:
Once the dependencies are installed, you can choose one of the following build to serve JS libraries and the Vue application code.
- Either generate a single JS build in development mode, it will enable warnings/errors display in the browser console:
npm run build
- Or generate a continuous JS build using the
--watchoption and in development mode, it will enable warnings/errors display in the browser console. This build is particularly useful when developing since it will automatically reload and re-serve bundles after every file change:
npm run start
- Or generate an optimized production build:
npm run production
Vendored libraries are now provided by
npm package manager and not anymore included directly as
.js files in the application.
npm install as described below.
Third party dependencies are listed in
front/package.json file and installed through
npm install command that will locally generate the
🔍It's important not to commit vendored libraries and instead just rely on the
npmconfiguration for their installation. This allows us to clearly view which package at which version is needed.
How to add a new dependency
To add a new library, you will have to find your package on the npm registry.
Then you will be able to run:
npm install --save your-package@version
The dependency will be directly added to the
package.json file and cached in the
To serve your new package to eScriptorium, you'll then have to import it in the
Please commit changes to
package-lock.json in a merge request.
🔍For further advice regarding dependencies installation, you can refer to the official NPM documentation.
We use Webpack to serve the Vue.js application code and vendored libraries as compressed bundles.
Webpack allows us to build three chunks to represent the logical separation between each part:
vendor chunk is used to serve the vendored libraries of the application. Its entrypoint is represented by the file
front/src/vendor.js where every useful dependency is imported.
main chunk is used to serve the
.css files and every eScriptorium
.js files that are not directly included in the Vue.js application core but still used by the Django application. Its entrypoint is represented by the file
front/src/main.js where each style and script files are imported.
editor chunk is used to serve the Vue.js application code including VueX store and Vue.js components. This application is displayed when editing a
DocumentPart on eScriptorium. Its entrypoint is represented by the file
front/src/editor/main.js where the
Editor component encompassing the whole app and the store are imported.
Vue.js application architecture
graph TD; Editor-->ExtraInfo; Editor-->TabContent; Editor-->ExtraNav; TabContent-->VisuPanel; VisuPanel -...-> BasePanel([BasePanel mixin]); TabContent-->SourcePanel; SourcePanel -...-> BasePanel([BasePanel mixin]); TabContent-->DiploPanel; DiploPanel -...-> BasePanel([BasePanel mixin]); TabContent-->SegPanel; SegPanel -...-> BasePanel([BasePanel mixin]); VisuPanel-->VisuLine; VisuLine -...-> LineBase([LineBase mixin]); VisuPanel-->TranscriptionModal; DiploPanel-->DiploLine; DiploLine -...-> LineBase([LineBase mixin]); SegPanel-->SegLine; SegPanel-->SegRegion; SegPanel-->Help; TranscriptionModal-->LineVersion; TranscriptionModal-->HelpVersions; TranscriptionModal-->HelpCompareTranscriptions;
Each component (
.vue file) is built following the Single-File Component implementation (SFC).
Those files are composed of 3 parts:
<template/>one, containing the HTML code for the component,
<script/>one, containing its logic,
- and finally, the
<style/>one, containing its scoped CSS style.
It allows us to obtain more cohesive and maintainable components since their template, logic and style are inherently coupled.
The VueX store is divided in 5 sub-modules.
graph TD; index.js-->document.js; index.js-->lines.js; index.js-->parts.js; index.js-->regions.js; index.js-->transcriptions.js;
In each of those modules we can find various state attributes, mutations and actions related to their main focus.
The store is shared by the whole Vue.js application, it means that every component can access the store state at any time and alter it by calling actions and mutations.
🔍Follow this link to learn more about the state management pattern.
Communicate through the store
As we said above, components can easily communicate using the store to share information.
Reading the store
You can retrieve a state attribute from the store using
this.$store.state.yourmodule.yourattribute in your component. DO NOT assign this reference with a new value, use actions and mutations only to do so!
For example, to read the
id state attribute from the
document.js store module, we can use this line in a Vue.js component:
🔍Find more about this topic in the VueX official documentation on store state.
To make API calls and/or initiate an alteration of the store state, you should use VueX actions. You can call an action using
this.$store.dispatch('yourmodule/youraction', yourpayload) in your component.
For example, to retrieve the
Document and populate the shared store with its information, we can call this line in a Vue.js component:
this.$store.dispatch('document/fetchDocument', documentId). This action will call an API endpoint listed in the
api.js file and populate various store attributes with new values using mutations.
🔍Find more about this topic in the VueX official documentation on store actions.
Altering store state
To properly alter the store state, you must use VueX mutations. You can call a mutation using
this.$store.commit('yourmodule/yourmutation', payload) in your component, but note that it's better to call actions that will perform the mutations internally using
For example, to modify the value of the stored
documentId, we can call this line in a Vue.js component:
this.$store.commit('document/setId', newId). This will alter the store state and changes will be repercuted on all components since they all share the same store instance.
🔍Find more about this topic in the VueX official documentation on store mutations.
- Use the store to communicate between components:
- Avoid passing props, handle them in a store module.
- Avoid using events (
$on), prefer using store actions.
- Always add a new API call in
- Avoid calling mutations (with
this.$store.commit(...)) directly in components, prefer to call actions altering the store state through commits instead (with
- Try to implement reusable/configurable components (e.g: a generic
Modalcomponent where you can set its title, its content, etc).
🔍See also Vue.js Style Guide.