Comme j'ai récemment ajouté [TypeScript et WebPack encore à Grav] (/blog/adding-webpack-encore-and-typescript-to-grav), j'ai voulu trouver un moyen d'ajouter CSS et JavaScript uniquement à certaines pages.
Générer des sorties multiples pour les assets
Jusqu'à présent, j'ai généré une page assets.html qui contenait les liens vers les fichiers JavaScript et CSS générés par
WebPack :
<script defer src="/user/themes/lingonberry-custom/build/app/app.js"></script>
<link href="/user/themes/lingonberry-custom/build/app/app.css" rel="stylesheet">Pour générer différents fichiers, j'ai modifié le fichier webpack.config.js. J'ai trouvé la plupart des informations dans
la [documentation Symfony] (https://symfony.com/doc/current/frontend/encore/advanced-config.html).
Cependant, comme je n'aime pas trop me répéter, j'ai créé une fonction :
function generateBaseConfig(name, buildPath, inputFile, outputFile, cleanOutput = true) {
        let itemBuildPath = path.join(BUILD_PATH_BASE, name);
        if (buildPath) {
                itemBuildPath = path.join(BUILD_PATH_BASE, buildPath);
        }
        if (!inputFile) {
                inputFile = `${name}.ts`
        }
        if (!outputFile) {
                outputFile = 'assets.html'
        }
        let config = Encore
                // directory where compiled assets will be stored
                .setOutputPath(itemBuildPath)
                // public path used by the web server to access the output path
                .setPublicPath(path.join('/', itemBuildPath))
                .addEntry(name, `./${path.join(PATH, 'js', inputFile)}`)
                // When enabled, Webpack "splits" your files into smaller pieces for greater optimization.
                //.splitEntryChunks()
                // will require an extra script tag for runtime.js
                // but, you probably want this, unless you're building a single-page app
                //.enableSingleRuntimeChunk()
                .disableSingleRuntimeChunk()
                .enableSourceMaps(!Encore.isProduction())
                // enables hashed filenames (e.g. app.abc123.css)
                .enableVersioning(Encore.isProduction())
                .addPlugin(new CompressionPlugin())
                // .addPlugin(new MiniCssExtractPlugin())
                .addPlugin(new HtmlWebpackPlugin({
                        inject: false,
                        filename: outputFile,
                        publicPath: path.join('/', itemBuildPath),
                        scriptLoading: 'defer',
                        templateContent: ({htmlWebpackPlugin}) => `
        ${htmlWebpackPlugin.tags.headTags}
        `
                }))
                // enables @babel/preset-env polyfills
                .configureBabelPresetEnv((config) => {
                        config.useBuiltIns = 'usage';
                        config.corejs = 3;
                })
                .enableSassLoader()
                .enableTypeScriptLoader();
        if (cleanOutput) {
                config.cleanupOutputBeforeBuild()
        }
        return config.getWebpackConfig()
}Donc maintenant pour ajouter plusieurs entrées/sorties je n'ai plus qu'à ajouter quelques lignes :
const PATH = path.join('user', 'themes', 'lingonberry-custom')
const BUILD_PATH_BASE = path.join(PATH, 'build')
const baseEncoreConfiguration = generateBaseConfig('app');
baseEncoreConfiguration.name = 'baseEncoreConfiguration'
Encore.reset();
const animationsConfiguration = generateBaseConfig('animations');
animationsConfiguration.name = 'animationsConfiguration'
module.exports = [baseEncoreConfiguration, animationsConfiguration];Si vous ajoutez seulement un nom lors de l'appel de la fonction generateBaseConfig, l'entrée sera
de {{themeName}}/js/{{name}}.ts et la sortie sera {{themeName}}/build/{{name}}/assets.html. donc dans l'exemple précédent j'avais les fichiers suivants :
user/themes/lingonberry-custom
âââ js
   âââ animations.ts
   âââ app.tsCe qui a gĂ©nĂ©rĂ© la sortie suivante :
user/themes/lingonberry-custom
âââ build
    âââ animations
    â   âââ animations.css
    â   âââ animations.js
    â   âââ animations.js.gz
    â   âââ assets.html
    â   âââ assets.html.gz
    â   âââ entrypoints.json
    â   âââ manifest.json
    â   âââ manifest.json.gz
    âââ app
    â   âââ app.css
    â   âââ app.css.gz
    â   âââ app.js
    â   âââ app.js.gz
    â   âââ assets.html
    â   âââ assets.html.gz
    â   âââ entrypoints.json
    â   âââ fonts
    â   âââ images
    â   âââ manifest.json
    â   âââ manifest.json.gz
    âââ entrypoints.json
    âââ manifest.json
    âââ manifest.json.gzChaque entrĂ©e de fichier a un rĂ©pertoire de sortie sĂ©parĂ©. Sans cela la fonction cleanupOutputBeforeBuild d'Encore viderait toujours le rĂ©pertoire principal de construction. Comme les constructions se dĂ©roulent en parallĂšle, il n'en resterait toujours qu'un Ă  la fin,
les autres étant supprimés.
Ajout des fichiers générés aux modÚles twig
Maintenant qu'ils sont gĂ©nĂ©rĂ©s, les fichiers de sortie doivent ĂȘtre inclus dans nos modĂšles twig. Pour les assets principaux, j'ai simplement ajoutĂ©
{% include 'app/assets.html' %} dans la balise header du fichier twig de base de mes templates.
J'ai également voulu simplifier l'ajout de fichiers à partir du backend pour une page de blog spécifique. Pour ce faire, j'ai [généré un nouveau plugin grav] (https://learn.getgrav.org/17/plugins/plugin-tutorial) Header Include.
Pour ajouter des options supplémentaires à une page, j'ai ajouté un blueprint plugins/header-include/blueprints/header-include.yaml :
form:
  fields:
    tabs:
      fields:
        options:
          type: tab
          fields:
            header-include:
              type: section
              title: HEADER_INCLUDE.PLUGIN.NAME
              underline: true
              fields:
                header.header-include.active:
                  type: toggle
                  toggleable: true
                  label: HEADER_INCLUDE.PLUGIN.ACTIVE
                  help: HEADER_INCLUDE.PLUGIN.ACTIVE_HELP
                  highlight: 1
                  # config-default@: plugins.header-include.active
                  options:
                    1: PLUGIN_ADMIN.YES
                    0: PLUGIN_ADMIN.NO
                  validate:
                    type: bool
                header.include:
                 type: multilevel
                 label: HEADER_INCLUDE.PLUGIN.ITEMS
                 value_only: true
                 validate:
                   type: arrayIl contient juste un bouton on-off, et une liste d'Ă©lĂ©ments que nous voulons inclure dans notre en-tĂȘte. Pour inclure les nouveaux fichiers d'animation dans les options de cette page, j'ai dĂ©fini la configuration suivante :

Maintenant pour s'assurer que tous les fichiers définis dans les options sont effectivement inclus, j'ai ajouté un modÚle {{themeName}}/partials/includes.html.twig :
{% for filename in page.header.include %}
    {% include filename %}
{% endof %}et j'ai inclus ce fichier dans le fichier de base de mon modĂšle.
Maintenant, lorsque j'ajoute quelques feuilles de style en cascade (CSS) au fichier animation.scss (que j'ai volé à CSS-Tricks), un peu de script, et je peux obtenir ces jolis effets, mais uniquement dans les pages qui en ont besoin.
@use 'variables' as *;
.typewriter {
  max-width: fit-content;
  display: block;
  text-align: left;
  overflow: hidden; /* Ensures the content is not revealed until the animation */
  border-right: .15em solid $primary-color;/* The typwriter cursor */
  white-space: nowrap; /* Keeps the content on a single line */
  margin: 0 auto; /* Gives that scrolling effect as the typing happens */
  letter-spacing: .15em; /* Adjust as needed */
  animation:
          typing 3.5s steps(38, end),
          blink-caret .75s step-end infinite;
  &.slow {
    animation:
            typing 7s steps(52, end),
            blink-caret 1.5s step-end infinite;
  }
}
/* The typing effect */
@keyframes typing {
  from { width: 0 }
  to { width: 100% }
}
/* The typewriter cursor effect */
@keyframes blink-caret {
  from, to { border-color: transparent }
  50% { border-color: $primary-color}
}
.retype-btn {
  border-radius: 5px;
  padding: 0.6rem 1.2rem;
  cursor: pointer;
  background-color: $darkmode-bg-color;
  color: $primary-color;
  transition: color 200ms ease-in-out,
  background-color 200ms ease-out;
;
  &:hover {
    color: $darkmode-bg-color;
    background-color: $primary-color;
  }
}
//components/typewriter.ts
export default class Typewriter {
    retypeButtons: HTMLButtonElement[]
    typewriters: HTMLElement[]
    constructor() {
        this.retypeButtons = <HTMLButtonElement[]><any>document.getElementsByClassName('retype-btn');
        this.typewriters = <HTMLElement[]><any>document.getElementsByClassName('typewriter');
        this.retypeButtons.forEach((item) => {
            item.addEventListener('click', (event) => {
                this.typewriters.forEach((item) => {
                    const clone = item.cloneNode(true)
                    item.parentNode?.replaceChild(clone, item)
                })
            })
        })
    }
}
//typewriter.ts
import '../css/animations.scss'
import Typewriter from './components/typewriter'
document.addEventListener("DOMContentLoaded", function () {
    new Typewriter();
});Ici, dans l'éditeur markdown de Grav admin pour ma page de blog, j'ajoute
<span class="typewriter">I'm a Typewriter !!</span>
<span class="typewriter slow">I'm a sloooow Typewriter !!</span>
<button id="retype-btn" class="retype-btn">Retype</button>I'm a Typewriter !! I'm a sloooow Typewriter !!
Les CSS et JavaScript sont maintenant appliquĂ©s mais seulement Ă cette page :) đ đ
Dennis de Best
 
                                         
                                         
                                         
                                        
Commentaires