diff --git a/package.json b/package.json index 36b4be69..a4bd9480 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "@ouitoulia/skenografia", - "version": "2.8.0", + "version": "2.19.0", "description": "Skenografia è un sub-theme drupal, basato su bootstrap_italia, che implementa il design delle scuole.", + "type": "module", "engineStrict": true, "engines": { "node": ">=v20.0.0" @@ -38,19 +39,20 @@ "copy-webpack-plugin": "^12.0.2", "cross-env": "^7.0.3", "css-loader": "^7.1.2", + "css-minimizer-webpack-plugin": "^7.0.0", "mini-css-extract-plugin": "^2.9.0", "postcss-loader": "^8.1.1", - "rimraf": "^5.0.7", "sass": "^1.77.4", - "sass-loader": "^14.2.1", + "sass-loader": "^16.0.4", "semver": "^7.6.2", "style-loader": "^4.0.0", - "svg-sprite-loader": "^6.0.11", + "svg-chunk-webpack-plugin": "^7.0.0", "svgo-loader": "^4.0.0", + "terser-webpack-plugin": "^5.3.10", "webpack": "^5.91.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4", - "webpack-merge": "^5.10.0" + "webpack-merge": "^6.0.1" }, "dependencies": { "bootstrap-italia": "2.12.1", diff --git a/skenografia.info.yml b/skenografia.info.yml index c14b06cf..6ab308dd 100644 --- a/skenografia.info.yml +++ b/skenografia.info.yml @@ -2,7 +2,7 @@ name: "Skenografia" type: theme description: "Skenografia is a Drupal sub-theme, based on bootstrap_italia, which implements school design." package: "Ouitoulía" -core_version_requirement: ^10 +core_version_requirement: ^10 || ^11 base theme: bootstrap_italia # Choose libraries to use. Global is managed with theme settings UI. diff --git a/skenografia.theme b/skenografia.theme index 0fef57b0..ab2312a2 100755 --- a/skenografia.theme +++ b/skenografia.theme @@ -16,6 +16,19 @@ function skenografia_library_info_build(): array { // Include all files from the includes directory. $includes_path = __DIR__ . '/includes/*.inc'; -foreach (glob($includes_path) as $filename) { + +/** + * All files in path. + * + * @var array|false $files + */ +$files = glob($includes_path); + +/** + * Name of single file. + * + * @var string $filename + */ +foreach ((array) $files as $filename) { require_once __DIR__ . '/includes/' . basename($filename); } diff --git a/src/js/custom/custom.js b/src/js/custom/custom.js index 8d524abe..43e29c64 100644 --- a/src/js/custom/custom.js +++ b/src/js/custom/custom.js @@ -3,11 +3,8 @@ * Use this folder and file to manage your custom styles. */ -// Example JS -//import './example' -//import './example-alert-event' -//import './example-bootstrap-italia-modules' -import './icons' +import './icons.js'; +import './main-menu--close.js'; /** * Customize your bootstrap diff --git a/src/js/custom/example-alert-event.js b/src/js/custom/example-alert-event.js deleted file mode 100644 index 4fc9c084..00000000 --- a/src/js/custom/example-alert-event.js +++ /dev/null @@ -1,22 +0,0 @@ -/* -(function (Drupal, once) { - 'use strict'; - - Drupal.behaviors.customBIAlert = { - attach: function (context, settings) { - // once() is run only once after the dom is loaded - once('customBIAlert', '', context).forEach(function (element) { - - // example from https://italia.github.io/bootstrap-italia/docs/componenti/alert/#eventi - element.addEventListener('closed.bs.alert', function () { - // do something, for instance, explicitly move focus to the most appropriate element, - // so it doesn't get lost/reset to the start of the page - // document.getElementById('...').focus() - }) - - }); - } - }; - -})(Drupal, once); -*/ diff --git a/src/js/custom/example-bootstrap-italia-modules.js b/src/js/custom/example-bootstrap-italia-modules.js deleted file mode 100644 index 9a563c2b..00000000 --- a/src/js/custom/example-bootstrap-italia-modules.js +++ /dev/null @@ -1,12 +0,0 @@ -/* -(function (Drupal) { - 'use strict'; - - Drupal.behaviors.myCustomModule = { - attach: function (context, settings) { - console.log(bootstrap.CarouselBI) - } - }; - -})(Drupal); -*/ diff --git a/src/js/custom/example.js b/src/js/custom/example.js deleted file mode 100755 index c419ec49..00000000 --- a/src/js/custom/example.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - * https://www.drupal.org/docs/8/api/javascript-api/javascript-api-overview - */ - -/* - -(function () { - 'use strict'; - - Drupal.behaviors.helloWorld = { - attach: function (context, settings) { - console.log('Hello World!'); - } - }; - -})(Drupal); - -*/ diff --git a/src/js/custom/main-menu--close.js b/src/js/custom/main-menu--close.js new file mode 100644 index 00000000..b3cc7e87 --- /dev/null +++ b/src/js/custom/main-menu--close.js @@ -0,0 +1,13 @@ +(function () { + 'use strict'; + + Drupal.behaviors.mainMenuClose = { + attach: function (context, settings) { + document.addEventListener('DOMContentLoaded', function () { + const e = document.querySelector('.hamburger'); + document.addEventListener('keyup', function (event) {if (event.key === 'Escape') {if (e.classList.contains('is-active')) {e.click();}}}); + }); + } + }; + +})(Drupal); diff --git a/src/js/index.js b/src/js/index.js index 451d81bc..4fbf577e 100755 --- a/src/js/index.js +++ b/src/js/index.js @@ -8,11 +8,9 @@ */ // Import Bootstrap-italia components. -import { loadPlugin } from 'bootstrap-italia/src/js/load-plugin' -import init from 'bootstrap-italia/src/js/plugins/init' -// import loadFonts from 'bootstrap-italia/src/js/plugins/fonts-loader' -// import { cookies } from 'bootstrap-italia/src/js/plugins/util/cookies' -import * as icons from 'bootstrap-italia/src/js/icons' +import { loadPlugin } from 'bootstrap-italia/src/js/load-plugin.js'; +import init from 'bootstrap-italia/src/js/plugins/init.js'; +import * as icons from 'bootstrap-italia/src/js/icons.js'; /** * Import all components, to choose components use @@ -62,10 +60,10 @@ import { List, //Transfer, //VideoPlayer, -} from 'bootstrap-italia' +} from 'bootstrap-italia'; -loadPlugin(icons) -init() +loadPlugin(icons); +init(); const bootstrap = { Alert, @@ -108,13 +106,13 @@ const bootstrap = { //ValidatorSelectAutocomplete, //loadFonts, //cookies, -} +}; // Component library initialization. -import './component-library-initialization' +import './component-library-initialization.js'; // Import custom JS. -import './custom/custom' +import './custom/custom.js'; /** * Export all bootstrap-italia components as `bootstrap`, @@ -122,4 +120,4 @@ import './custom/custom' * * @type {any} */ -window.bootstrap = bootstrap +window.bootstrap = bootstrap; diff --git a/src/scss/_bootstrap.scss b/src/scss/_bootstrap.scss index 2a0fd60d..b30673eb 100644 --- a/src/scss/_bootstrap.scss +++ b/src/scss/_bootstrap.scss @@ -24,7 +24,6 @@ @import 'bootstrap/scss/breadcrumb'; @import 'bootstrap/scss/pagination'; @import 'bootstrap/scss/badge'; -@import 'bootstrap/scss/alert'; @import 'bootstrap/scss/progress'; @import 'bootstrap/scss/list-group'; @import 'bootstrap/scss/close'; diff --git a/src/scss/custom/r-engineering/override/_cards.scss b/src/scss/custom/r-engineering/override/_cards.scss index f9452804..81990824 100644 --- a/src/scss/custom/r-engineering/override/_cards.scss +++ b/src/scss/custom/r-engineering/override/_cards.scss @@ -1054,9 +1054,9 @@ // Questo tipo di card si può realizzare con le utility di bootstrap 5.2 // components/card/card-vertical-thumb.html.twig -//.card-vertical-thumb { +.card-vertical-thumb { // padding: 0; -// .card-body { + .card-body { // display: flex; // padding: 0; // .card-thumb { @@ -1085,8 +1085,14 @@ // } // } // } -// } -//} + .card-title { + a { + // Fix bootstrap-italia v2.12.1 + display: inherit; + } + } + } +} //.card-event { // padding: 0; diff --git a/src/scss/custom/r-engineering/parts/_hero.scss b/src/scss/custom/r-engineering/parts/_hero.scss index c89067a7..65223f71 100644 --- a/src/scss/custom/r-engineering/parts/_hero.scss +++ b/src/scss/custom/r-engineering/parts/_hero.scss @@ -71,9 +71,8 @@ } } small { - font-size: 1rem; + font-size: 1.2rem; font-weight: 700; - text-transform: uppercase; margin: 0 0 25px 0; display: block; } diff --git a/svgo.config.js b/svgo.config.js new file mode 100644 index 00000000..ab5dfe67 --- /dev/null +++ b/svgo.config.js @@ -0,0 +1,22 @@ +// https://svgo.dev/docs/plugins/ +export default { + multipass: true, + plugins: [ + 'cleanupAttrs', + 'cleanupAttrs', + 'removeEmptyAttrs', + 'removeComments', + 'convertStyleToAttrs', + 'removeEmptyContainers', + 'removeEmptyText', + 'removeRasterImages', + 'removeUnknownsAndDefaults', + 'reusePaths', + { + name: 'removeAttrs', + params: { + attrs: '(fill)', + }, + }, + ], +}; diff --git a/webpack.check.js b/webpack.check.js deleted file mode 100644 index a6bc96b6..00000000 --- a/webpack.check.js +++ /dev/null @@ -1,6 +0,0 @@ -const semver = require('semver') -const { engines } = require('./package') - -if ( !semver.satisfies(process.version, engines.node) ) { - throw new Error('The current node version of node (' + process.version + ') does not satisfy the required version (' + engines.node + ').') -} diff --git a/webpack.common.js b/webpack.common.js index 0fd3ed5d..ff63c9cf 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,13 +1,16 @@ -const fs = require('fs') -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const CopyWebpackPlugin = require('copy-webpack-plugin') -const SpriteLoaderPlugin = require('svg-sprite-loader/plugin') -const rimraf = require('rimraf') -const check = require('./webpack.check') +import fs from 'fs/promises'; +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import CopyWebpackPlugin from 'copy-webpack-plugin'; +import SvgChunkWebpackPlugin from 'svg-chunk-webpack-plugin'; +import paths from './webpack.paths.js'; +import semver from 'semver'; +import packageJson from './package.json' assert { type: 'json' }; -const paths = require('./webpack.paths') +if (!semver.satisfies(process.version, packageJson.engines.node)) { + throw new Error(`The current Node.js version (${process.version}) does not satisfy the required version (${packageJson.engines.node}).`); +} -module.exports = { +export default { // Entry entry: { "bootstrap-italia": [paths.src + '/js/index.js', paths.src + '/scss/theme.scss'], @@ -33,26 +36,8 @@ module.exports = { ], use: [ { - loader: 'svg-sprite-loader', - options: { - extract: true, - outputPath: '/svg/', - spriteFilename: 'sprites.svg', - } + loader: SvgChunkWebpackPlugin.loader, }, - { - loader: 'svgo-loader', - options: { - plugins: [ - { - name: 'removeAttrs', - params: { - attrs: '(fill)', - }, - } - ] - } - } ], }, ], @@ -62,8 +47,13 @@ module.exports = { filename: 'css/[name].min.css', chunkFilename: 'css/[id].min.css' }), - new SpriteLoaderPlugin({ - plainSprite: true + new SvgChunkWebpackPlugin({ + filename: 'svg/sprites.svg', + svgstoreConfig: { + svgAttrs: { + 'xmlns': 'http://www.w3.org/2000/svg', + } + } }), new CopyWebpackPlugin({ patterns: [ @@ -103,21 +93,15 @@ module.exports = { }), { apply: (compiler) => { - compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => { - const ckeditorJsFile = compiler.options.output.path + '/js/ckeditor5.min.js'; - const ckeditorComuniJsFile = compiler.options.output.path + '/js/ckeditor5-comuni.min.js'; - const fontsJsFile = compiler.options.output.path + '/js/fonts.min.js'; - if (fs.existsSync(ckeditorJsFile)) { - rimraf.sync(ckeditorJsFile); - } - if (fs.existsSync(ckeditorComuniJsFile)) { - rimraf.sync(ckeditorComuniJsFile); - } - if (fs.existsSync(fontsJsFile)) { - rimraf.sync(fontsJsFile); + compiler.hooks.afterEmit.tapPromise('AfterEmitPlugin', async (compilation) => { + const ckeditorJsFile = `${compiler.options.output.path}/js/ckeditor5.min.js`; + try { + await fs.rm(ckeditorJsFile, { force: true }); + } catch (err) { + console.error('Error deleting ckeditor5.min.js:', err); } }); }, - } + }, ], }; diff --git a/webpack.dev.js b/webpack.dev.js index c95d165a..3434f758 100755 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,9 +1,9 @@ -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const { merge } = require('webpack-merge') +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import { merge } from 'webpack-merge'; -const common = require('./webpack.common') +import common from './webpack.common.js'; -module.exports = merge(common, { +export default merge(common, { mode: 'development', devtool: 'inline-source-map', @@ -19,7 +19,7 @@ module.exports = merge(common, { importLoaders: 1, sourceMap: true, modules: false, - } + }, }, { loader: 'postcss-loader', options: { sourceMap: true } }, { loader: 'sass-loader', options: { sourceMap: true } }, @@ -27,4 +27,4 @@ module.exports = merge(common, { }, ], }, -}) +}); diff --git a/webpack.hot.js b/webpack.hot.js index c2368aaf..cd5a39f2 100644 --- a/webpack.hot.js +++ b/webpack.hot.js @@ -1,16 +1,15 @@ -const { merge } = require('webpack-merge') +import { merge } from 'webpack-merge'; +import fs from 'fs'; +import dev from './webpack.dev.js'; -const fs = require('fs') -const customSettingsFile = './webpack.settings.js' +const customSettingsFile = './webpack.settings.js'; const settings = fs.existsSync(customSettingsFile) - ? require('./webpack.settings') - : require('./webpack.settings.dist') + ? await import('./webpack.settings.js') + : await import('./webpack.settings.dist.js'); -const dev = require('./webpack.dev') - -module.exports = merge(dev, { +export default merge(dev, { output: { publicPath: settings.hotPublicPath, }, devServer: settings.devServer, -}) +}); diff --git a/webpack.paths.js b/webpack.paths.js index 1241ee8b..1f54183f 100644 --- a/webpack.paths.js +++ b/webpack.paths.js @@ -1,17 +1,34 @@ -const path = require('path') +import path from 'path'; +import fs from 'fs'; +import { fileURLToPath } from 'url'; -const fs = require('fs') -const customSettingsFile = './webpack.settings.js' -const settings = fs.existsSync(customSettingsFile) - ? require('./webpack.settings') - : require('./webpack.settings.dist') +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); -module.exports = { +// Path setting files +const customSettingsFile = path.resolve(__dirname, './webpack.settings.js'); +const defaultSettingsFile = path.resolve(__dirname, './webpack.settings.dist.js'); + +// Check if the custom settings file exists +let settings; +if (fs.existsSync(customSettingsFile)) { + settings = await import(customSettingsFile); +} else { + settings = await import(defaultSettingsFile); +} + +// Export setting as default +settings = settings.default; + +const paths = { // Source files src: path.resolve(__dirname, settings.sourceDir), // Destination build files build: path.resolve(__dirname, settings.destinationDir), + // Modules directory modules: path.resolve(__dirname, settings.moduleDir), -} +}; + +export default paths; diff --git a/webpack.prod.js b/webpack.prod.js index 5d2067da..6c4e3665 100755 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -1,10 +1,12 @@ -const MiniCssExtractPlugin = require('mini-css-extract-plugin') -const { CleanWebpackPlugin } = require('clean-webpack-plugin') -const { merge } = require('webpack-merge') +import MiniCssExtractPlugin from 'mini-css-extract-plugin'; +import { CleanWebpackPlugin } from 'clean-webpack-plugin'; +import { merge } from 'webpack-merge'; +import TerserPlugin from 'terser-webpack-plugin'; +import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'; -const common = require('./webpack.common') +import common from './webpack.common.js'; -module.exports = merge(common, { +export default merge(common, { mode: 'production', devtool: false, @@ -12,6 +14,7 @@ module.exports = merge(common, { // Removes/cleans build folders and unused assets when rebuilding new CleanWebpackPlugin(), ], + module: { rules: [ { @@ -24,7 +27,7 @@ module.exports = merge(common, { importLoaders: 2, sourceMap: false, modules: false, - } + }, }, 'postcss-loader', 'sass-loader', @@ -32,12 +35,37 @@ module.exports = merge(common, { }, ], }, + optimization: { minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + compress: { + drop_console: true, // Removes console.logs from production code + }, + format: { + comments: false, // Remove all comments + }, + }, + extractComments: false, // Prevents extracting comments into separate files + }), + new CssMinimizerPlugin({ + minimizerOptions: { + preset: [ + 'default', + { + svgo: false, // Disable SVGO (inline scss issue) + }, + ], + }, + }), + ], }, + performance: { hints: false, maxEntrypointSize: 860000, maxAssetSize: 860000, }, -}) +}); diff --git a/webpack.settings.dist.js b/webpack.settings.dist.js index 53d76cf2..91da62b6 100644 --- a/webpack.settings.dist.js +++ b/webpack.settings.dist.js @@ -1,10 +1,10 @@ -module.exports = { - /*** Basic settings ***/ +export default { + // Basic settings sourceDir: 'src', destinationDir: 'dist', moduleDir: 'node_modules', - /*** Hot mode settings ***/ + // Hot mode settings /** * If you edit this variable, change relative path in ".libraries.yml" on 'hot' array. */ @@ -39,4 +39,4 @@ module.exports = { */ port: 8080, } -} +};