|
|
# eScriptorium Frontend development
|
|
|
|
|
|
This documentation aims to explain the new architecture of the Javascript side of the eScriptorium application. It also provides several tips to follow when adding further developments.
|
|
|
|
|
|
The frontend is divided in three parts:
|
|
|
- vendored libraries, pulled from NPM registry
|
|
|
- Javascript code directly used by the Django application (jQuery helpers, some libraries initialisation, ...)
|
|
|
- the eScriptorium editor, built with Vue.js
|
|
|
|
|
|
## `front` folder structure
|
|
|
The `front` folder of eScriptorium includes several folders/files. Among them are `.css` files, `.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 `.css` files used to style the Vue.js application.
|
|
|
- **dist** ~*NOT COMMITTED*~ ➔ Folder containing the compressed archives of the Vue.js code (3 chunks, `vendor`, `main` and `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 `.js` files 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.js` and under the `store` folder.
|
|
|
- **vue/components** ➔ Folder gathering all the `.vue` **Single 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`.
|
|
|
- `webpack.dev.js` ➔ File detailing the specific dev configuration to serve the application code using `webpack`.
|
|
|
- `webpack.prod.js` ➔ File detailing the specific production configuration to serve the application code using `webpack`.
|
|
|
|
|
|
## Setup
|
|
|
|
|
|
:warning: All the following commands must be run from the `front` folder.
|
|
|
|
|
|
### How to install
|
|
|
We use [NPM](https://docs.npmjs.com/) 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:
|
|
|
|
|
|
```console
|
|
|
npm install
|
|
|
```
|
|
|
|
|
|
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:
|
|
|
```console
|
|
|
npm run build
|
|
|
```
|
|
|
- Or generate a continuous JS build using the `--watch` option 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:
|
|
|
```console
|
|
|
npm run start
|
|
|
```
|
|
|
- Or generate an optimized production build:
|
|
|
```console
|
|
|
npm run production
|
|
|
```
|
|
|
|
|
|
## Vendored libraries
|
|
|
Vendored libraries are now provided by `npm` package manager and not anymore included directly as `.js` files in the application.
|
|
|
|
|
|
:warning: Do not commit the source of new dependencies, please use `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 `front/node_modules` folder.
|
|
|
|
|
|
> :mag: It's **important** not to commit vendored libraries and instead just rely on the `npm` configuration 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](https://www.npmjs.com/).
|
|
|
|
|
|
Then you will be able to run:
|
|
|
```console
|
|
|
npm install --save your-package@version
|
|
|
```
|
|
|
The dependency will be directly added to the `package.json` file and cached in the `node_modules` folder.
|
|
|
|
|
|
To serve your new package to eScriptorium, you'll then have to import it in the `front/src/vendor.js` file.
|
|
|
|
|
|
Please commit changes to `package.json` and `package-lock.json` in a merge request.
|
|
|
|
|
|
> :mag: For further advice regarding dependencies installation, you can refer to the [official NPM documentation](https://docs.npmjs.com/cli/v7/commands/npm-install).
|
|
|
|
|
|
## Webpack chunks
|
|
|
We use [Webpack](https://webpack.js.org/concepts/) 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
|
|
|
The `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
|
|
|
The `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
|
|
|
The `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
|
|
|
### Components
|
|
|
```mermaid
|
|
|
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;
|
|
|
```
|
|
|
|
|
|
#### Single-file components
|
|
|
Each component (`.vue` file) is built following the [Single-File Component implementation (SFC)](https://vuejs.org/v2/guide/single-file-components.html).
|
|
|
|
|
|
Those files are composed of 3 parts:
|
|
|
- the `<template/>` one, containing the HTML code for the component,
|
|
|
- the `<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.
|
|
|
|
|
|
### VueX store
|
|
|
The VueX store is divided in 5 sub-modules.
|
|
|
```mermaid
|
|
|
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.
|
|
|
> :mag: Follow [this link](https://vuex.vuejs.org/#what-is-a-state-management-pattern) 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: `this.$store.state.document.id`.
|
|
|
|
|
|
> :mag: Find more about this topic in the [VueX official documentation on store state](https://vuex.vuejs.org/fr/guide/state.html).
|
|
|
|
|
|
#### Calling actions
|
|
|
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.
|
|
|
|
|
|
> :mag: Find more about this topic in the [VueX official documentation on store actions](https://vuex.vuejs.org/fr/guide/actions.html).
|
|
|
|
|
|
#### 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 `commit()`.
|
|
|
|
|
|
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.
|
|
|
|
|
|
> :mag: Find more about this topic in the [VueX official documentation on store mutations](https://vuex.vuejs.org/fr/guide/mutations.html).
|
|
|
|
|
|
## Best practices
|
|
|
- Use the store to communicate between components:
|
|
|
- Avoid passing props, handle them in a store module.
|
|
|
- Avoid using events (`$emit`, `$on`), prefer using store actions.
|
|
|
- Always add a new API call in `front/src/api.js`.
|
|
|
- Avoid calling mutations (with `this.$store.commit(...)`) directly in components, prefer to call actions altering the store state through commits instead (with `this.$store.dispatch(...)`).
|
|
|
- Try to implement reusable/configurable components (e.g: a generic `Modal` component where you can set its title, its content, etc).
|
|
|
|
|
|
> :mag: See also [Vue.js Style Guide](https://vuejs.org/v2/style-guide/). |