diff --git a/builder/scripts/sprite.js b/builder/scripts/sprite.js index a2fa4e038..0204ffe0f 100644 --- a/builder/scripts/sprite.js +++ b/builder/scripts/sprite.js @@ -30,6 +30,15 @@ const path = require("path"); const defaultPlugins = require("../conf/svgoDefaultPlugins"); const SVGSpriter = require("svg-sprite"); +let svgMarkup; + +const addSvgMarkup = (filePath) => { + const fileContents = fs.readFileSync(filePath, { encoding: "utf-8" }); + svgMarkup = svgMarkup += ""; // Closing svg tag. + const updatedContents = fileContents.replace("", svgMarkup); + fs.writeFileSync(filePath, updatedContents); +}; + module.exports = (entry, dest, options) => { const iconList = Array.isArray(options.list) ? options.list.flat(1) @@ -64,7 +73,7 @@ module.exports = (entry, dest, options) => { }, }, }); - + let svgYPosition = 0; files.forEach((file) => { let filePath; if (Array.isArray(entry)) { @@ -75,6 +84,10 @@ module.exports = (entry, dest, options) => { } else { filePath = path.resolve(entry, file); } + const id = path.basename(file, path.extname(file)); // Extracting file name without extension as id + svgMarkup = + svgMarkup += ``; + svgYPosition = svgYPosition + 16; spriter.add( filePath, file, @@ -90,6 +103,8 @@ module.exports = (entry, dest, options) => { result[mode][resource].path, result[mode][resource].contents ); + const outputPath = result[mode][resource].path; + addSvgMarkup(outputPath); }); }); }); diff --git a/src/compositions/bcl-icons-list/icons-list.story.js b/src/compositions/bcl-icons-list/icons-list.story.js index 0e8e0232b..d5311b73a 100644 --- a/src/compositions/bcl-icons-list/icons-list.story.js +++ b/src/compositions/bcl-icons-list/icons-list.story.js @@ -1,7 +1,9 @@ import icons_list from "@openeuropa/bcl-icons-list/icons-list.html.twig"; +import icons_manager from "@openeuropa/bcl-icons-list/icons-manager.html.twig"; import icons from "@openeuropa/bcl-theme-default/src/icons/icons"; import customIcons from "@openeuropa/bcl-theme-default/src/icons/custom-icons"; import defaultSprite from "@openeuropa/bcl-theme-default/icons/bcl-default-icons.svg"; +import { addIconPath } from "@openeuropa/bcl-story-utils"; let iconsList = icons.concat(customIcons); iconsList = iconsList.map((icon) => icon.substring(0, icon.length - 4)); @@ -17,3 +19,8 @@ export default { export const Default = () => icons_list({ icons: iconsList, path: defaultSprite }); + +export const Attribute = () => + icons_manager({ icons: iconsList, path: defaultSprite }); + +Attribute.decorators = [addIconPath]; diff --git a/src/compositions/bcl-icons-list/icons-manager.html.twig b/src/compositions/bcl-icons-list/icons-manager.html.twig new file mode 100644 index 000000000..8cf66c7cf --- /dev/null +++ b/src/compositions/bcl-icons-list/icons-manager.html.twig @@ -0,0 +1,105 @@ +{% apply spaceless %} + +{# Parameters: + - icons: (icon[]) (default: []) + - path (string) (default: '') +#} + +{% set _icons = icons|default([]) %} +{% set _path = path|default('') %} + +{% if _icons is not empty and _icons is iterable %} +
+
+

Icon Colours

+
+ +

Default

+
+
+ +

Primary
icon-color-primary

+
+
+ +

Secondary
icon-color-secondary

+
+
+ +

Success
icon-color-success

+
+
+ +

Info
icon-color-info

+
+
+ +

Warning
icon-color-warning

+
+
+ +

Danger
icon-color-danger

+
+
+ +

Dark
icon-color-dark

+
+
+
+ +
+

Light
icon-color-light

+
+
+
+

Icon Sizes

+
+
+ +

2XS - icon--2xs

+
+
+ +

XS - icon--xs

+
+
+ +

S - icon--s

+
+
+ +

M - icon--m

+
+
+
+
+ +

L - icon--l

+
+
+ +

XL - icon--xl

+
+
+ +

2XL - icon--2xl

+
+
+ +

Fluid - icon--fluid

+
+
+
+
+

Icons List

+ {% for _icon in _icons %} +
+ +

{{ _icon }}

+
+ {% endfor %} +
+
+{% endif %} + +{% endapply %} diff --git a/src/themes/default/src/js/icon-manager/icon-manager.js b/src/themes/default/src/js/icon-manager/icon-manager.js new file mode 100644 index 000000000..6e9d9f500 --- /dev/null +++ b/src/themes/default/src/js/icon-manager/icon-manager.js @@ -0,0 +1,37 @@ +/** + * -------------------------------------------------------------------------- + * IconManager Class Documentation + * -------------------------------------------------------------------------- + * + * Overview: + * The `IconManager` class is a utility for dynamically setting the `mask-image` + * of HTML elements based on custom data attributes. It uses CSS custom properties + * and JavaScript to ensure the correct SVG icon is applied to elements with the class `.icon`. + * + */ + +import { defineJQueryPlugin } from '@openeuropa/bcl-bootstrap/js/src/util/index' + +class IconManager { + initializeIcons() { + this.iconPath = getComputedStyle(document.body).getPropertyValue('--icon-path').trim().replace(/['"]/g, ''); + document.querySelectorAll('.icon[data-icon]').forEach(icon => { + const iconId = icon.getAttribute('data-icon'); + this.setIconMask(icon, iconId); + }); + } + + setIconMask(icon, iconId) { + icon.style.maskImage = `url(${this.iconPath}#${iconId}-view)`; + icon.style.webkitMaskImage = `url(${this.iconPath}#${iconId}-view)`; + } +} + +document.addEventListener('DOMContentLoaded', () => { + const iconManager = new IconManager(); + iconManager.initializeIcons(); +}); + +defineJQueryPlugin(IconManager); + +export default IconManager; diff --git a/src/themes/default/src/js/index.esm.js b/src/themes/default/src/js/index.esm.js index 16e946ebc..4b9057fc4 100644 --- a/src/themes/default/src/js/index.esm.js +++ b/src/themes/default/src/js/index.esm.js @@ -19,6 +19,7 @@ import ScrollSpy from "@openeuropa/bcl-bootstrap/js/src/scrollspy-legacy"; import Tab from "@openeuropa/bcl-bootstrap/js/src/tab"; import Toast from "@openeuropa/bcl-bootstrap/js/src/toast"; import Tooltip from "@openeuropa/bcl-bootstrap/js/src/tooltip"; +import IconManager from "@openeuropa/bcl-theme-default/src/js/icon-manager/icon-manager"; export { Alert, @@ -35,4 +36,5 @@ export { Tab, Toast, Tooltip, + IconManager, }; diff --git a/src/themes/default/src/js/index.umd.js b/src/themes/default/src/js/index.umd.js index 8a3f1ec16..cdf884df3 100644 --- a/src/themes/default/src/js/index.umd.js +++ b/src/themes/default/src/js/index.umd.js @@ -19,6 +19,7 @@ import ScrollSpy from "@openeuropa/bcl-bootstrap/js/src/scrollspy-legacy"; import Tab from "@openeuropa/bcl-bootstrap/js/src/tab"; import Toast from "@openeuropa/bcl-bootstrap/js/src/toast"; import Tooltip from "@openeuropa/bcl-bootstrap/js/src/tooltip"; +import IconManager from "@openeuropa/bcl-theme-default/src/js/icon-manager/icon-manager"; export default { Alert, @@ -35,4 +36,5 @@ export default { Tab, Toast, Tooltip, + IconManager, }; diff --git a/src/themes/default/src/scss/_icon_manager.scss b/src/themes/default/src/scss/_icon_manager.scss new file mode 100644 index 000000000..d3a22fcca --- /dev/null +++ b/src/themes/default/src/scss/_icon_manager.scss @@ -0,0 +1,16 @@ +.icon { + width: 1em; + height: 1em; + display: block; + background-color: var(--bs-dark); + -webkit-mask-size: contain; + mask-size: contain; + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; +} + +@each $color, $value in $theme-colors { + .icon-color-#{$color} { + background-color: $value; + } +} diff --git a/src/themes/default/src/scss/oe-bcl-default.scss b/src/themes/default/src/scss/oe-bcl-default.scss index 66afc8eb8..09f92140c 100644 --- a/src/themes/default/src/scss/oe-bcl-default.scss +++ b/src/themes/default/src/scss/oe-bcl-default.scss @@ -67,6 +67,7 @@ @import "@openeuropa/bcl-theme-default/src/scss/header"; @import "@openeuropa/bcl-theme-default/src/scss/footer"; @import "@openeuropa/bcl-theme-default/src/scss/tabs"; +@import "@openeuropa/bcl-theme-default/src/scss/icon_manager"; @import "@openeuropa/bcl-theme-default/src/scss/icon"; @import "@openeuropa/bcl-theme-default/src/scss/search-form"; @import "@openeuropa/bcl-theme-default/src/scss/input"; diff --git a/tools/story-utils/index.js b/tools/story-utils/index.js index 6448f21d5..7e71703c7 100644 --- a/tools/story-utils/index.js +++ b/tools/story-utils/index.js @@ -342,6 +342,15 @@ export const initScrollspy = (story) => { ${demo}`; }; +export const addIconPath = (story) => { + const demo = story(); + return ` + + ${demo}`; +}; + export const initBadges = (story) => { const demo = story(); return `