diff --git a/buildtools/svg-viewbox-loader.js b/buildtools/svg-viewbox-cmd.js old mode 100644 new mode 100755 similarity index 87% rename from buildtools/svg-viewbox-loader.js rename to buildtools/svg-viewbox-cmd.js index 39e91b948e3b..36c3f20b041f --- a/buildtools/svg-viewbox-loader.js +++ b/buildtools/svg-viewbox-cmd.js @@ -20,24 +20,21 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** - * Webpack loader to be used after `svg-inline-loader`. - * * It will: * - Fill the `viewBox` if it's missing (to be able to change the `width` and the `height`). * - Fill the `width` and `height` (required by OpenLayers). * - Allows to change the `width` and `height` (with `width` or `height` arguments). * - Remove the unneeded `x` and `y` property. - * - * See also the `svg` example. */ const simpleHTMLTokenizer = require('simple-html-tokenizer'); const querystring = require('querystring'); const generate = require('./generate-xml-from-tokens.js'); const parseUnit = require('parse-absolute-css-unit'); +const {program} = require('commander'); +const fs = require('fs'); -module.exports = function (source) { - this.cacheable(true); +function process(source, resourceQuery) { let tokens = simpleHTMLTokenizer.tokenize(source); tokens = tokens.map((tag) => { @@ -86,9 +83,7 @@ module.exports = function (source) { if (viewBox === undefined) { tag.attributes.push(['viewBox', `0 0 ${width} ${height}`, true]); } - const queryString = this.resourceQuery.startsWith('?') - ? this.resourceQuery.substring(1) - : this.resourceQuery; + const queryString = resourceQuery.startsWith('?') ? resourceQuery.substring(1) : resourceQuery; const query = querystring.decode(queryString); if (query.width !== undefined) { const parsed = query.width.match(/^([0-9]+)([a-z]*)$/); @@ -108,4 +103,20 @@ module.exports = function (source) { }); return generate(tokens); -}; +} + +function main(args) { + const uri = args[0]; + const resourceQuery = args[1]; + const source = fs.readFileSync(uri, 'utf-8'); + const result = process(source, resourceQuery); + fs.writeFileSync(uri, result, 'utf-8'); +} + +// If running this module directly then call the main function. +if (require.main === module) { + program.parse(process.argv); + main(program.args); +} + +module.exports = main; diff --git a/buildtools/webpack.dev.js b/buildtools/webpack.dev.js index ad52fa24f887..21ce0b4c0d4a 100644 --- a/buildtools/webpack.dev.js +++ b/buildtools/webpack.dev.js @@ -38,44 +38,15 @@ const resourcesRule = { const svgRule = { test: /\.svg$/, - oneOf: [ - { - resourceQuery: /url/, - use: [ - { - loader: 'file-loader', - options: { - esModule: false, - name: '[name].[ext]', - }, - }, - 'svgo-loader', - ], - }, - { - resourceQuery: /viewbox/, - use: [ - { - loader: 'svg-inline-loader', - options: { - removeSVGTagAttrs: false, - }, - }, - './buildtools/svg-viewbox-loader', - 'svgo-loader', - ], - }, + use: [ { - use: [ - { - loader: 'svg-inline-loader', - options: { - removeSVGTagAttrs: false, - }, - }, - 'svgo-loader', - ], + loader: 'file-loader', + options: { + esModule: false, + name: '[name].[ext]', + }, }, + 'svgo-loader', ], }; diff --git a/buildtools/webpack.prod.js b/buildtools/webpack.prod.js index b40e5ff0d32a..46bff58e6d24 100644 --- a/buildtools/webpack.prod.js +++ b/buildtools/webpack.prod.js @@ -37,44 +37,15 @@ const resourcesRule = { const svgRule = { test: /\.svg$/, - oneOf: [ - { - resourceQuery: /url/, - use: [ - { - loader: 'file-loader', - options: { - esModule: false, - name: '[name].[hash:6].[ext]', - }, - }, - 'svgo-loader', - ], - }, - { - resourceQuery: /viewbox/, - use: [ - { - loader: 'svg-inline-loader', - options: { - removeSVGTagAttrs: false, - }, - }, - './buildtools/svg-viewbox-loader', - 'svgo-loader', - ], - }, + use: [ { - use: [ - { - loader: 'svg-inline-loader', - options: { - removeSVGTagAttrs: false, - }, - }, - 'svgo-loader', - ], + loader: 'file-loader', + options: { + esModule: false, + name: '[name].[hash:6].[ext]', + }, }, + 'svgo-loader', ], }; diff --git a/contribs/gmf/apps/desktop/index.html.ejs b/contribs/gmf/apps/desktop/index.html.ejs index 6cb865c50ee4..ea5ed9df6bfa 100644 --- a/contribs/gmf/apps/desktop/index.html.ejs +++ b/contribs/gmf/apps/desktop/index.html.ejs @@ -13,9 +13,7 @@
- - <%=require('gmf/icons/spinner.svg?viewbox&height=1em')%> - +
diff --git a/contribs/gmf/apps/desktop_alt/index.html.ejs b/contribs/gmf/apps/desktop_alt/index.html.ejs index 44156416446d..28628edd37f7 100644 --- a/contribs/gmf/apps/desktop_alt/index.html.ejs +++ b/contribs/gmf/apps/desktop_alt/index.html.ejs @@ -13,9 +13,7 @@
- - <%=require('gmf/icons/spinner.svg?viewbox&height=1em')%> - +
+ ng-click="mainCtrl.toggleLeftNavVisibility()"> diff --git a/examples/svg.html b/examples/svg.html deleted file mode 100644 index 22471fdd2de0..000000000000 --- a/examples/svg.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - SVG example - - - - - -

This example shows different ways to include an SVG in the application using Webpack.

-
In the HTML
-

Default: <%=require("./inline.svg")%>.

-

- With ?viewbox&height=1em, 2rem, style on circle: - <%=require("./font.svg?viewbox&height=1em")%>. -

-

- With ?viewbox&height=1em, 4rem, style on circle: - <%=require("./font.svg?viewbox&height=1em")%>. -

-

- Font Awesome ?viewbox&height=2em, style fill: #65b0ff: - <%=require("@fortawesome/fontawesome-free/svgs/regular/check-circle.svg?viewbox&height=2em")%>. -

-

With ?viewbox: <%=require("./inline.svg?viewbox")%>.

-

With ?viewbox&width=3em: <%=require("./inline.svg?viewbox&width=3em")%>.

-

With ?viewbox&height=3em: <%=require("./inline.svg?viewbox&height=3em")%>.

-

As URL with ?url: " />

-

Note: in font, and in inline mode, CSS rules can be used to style the SVG.

-
In OpenLayers symbols
- -

Using src (left to right):

- - - - - diff --git a/examples/svg.js b/examples/svg.js deleted file mode 100644 index 6bd24509b9fb..000000000000 --- a/examples/svg.js +++ /dev/null @@ -1,103 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2019-2024 Camptocamp SA -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import angular from 'angular'; -import './svg.css'; -import EPSG2056 from 'ngeo/proj/EPSG_2056'; -import Map from 'ol/Map'; -import View from 'ol/View'; -import LayerVector from 'ol/layer/Vector'; -import SourceVector from 'ol/source/Vector'; -import Feature from 'ol/Feature'; -import Point from 'ol/geom/Point'; -import Style from 'ol/style/Style'; -import Icon from 'ol/style/Icon'; -import gmfMapComponent from 'gmf/map/component'; -import options from './options'; - -/** @type {!angular.IModule} **/ -const appmodule = angular.module('app', ['gettext', gmfMapComponent.name]); - -/** - * @class - * @hidden - */ -function MainController() { - const source = new SourceVector(); - const feature1 = new Feature({ - geometry: new Point([2599000, 1200000]), - }); - feature1.setStyle([ - new Style({ - image: new Icon({ - // @ts-ignore: webpack - src: 'data:image/svg+xml;base64,' + btoa(require('./inline.svg')), - // For IE compatibility - imgSize: [65, 65], - }), - }), - ]); - source.addFeature(feature1); - const feature2 = new Feature({ - geometry: new Point([2600000, 1200000]), - }); - feature2.setStyle([ - new Style({ - image: new Icon({ - // @ts-ignore: webpack - src: 'data:image/svg+xml;base64,' + btoa(require('./inline.svg?viewbox&width=30px')), - // For IE compatibility - imgSize: [30, 30], - }), - }), - ]); - source.addFeature(feature2); - const feature3 = new Feature({ - geometry: new Point([2601000, 1200000]), - }); - feature3.setStyle([ - new Style({ - image: new Icon({ - // @ts-ignore: webpack - src: require('./url.svg?url'), - // For IE compatibility - imgSize: [65, 65], - }), - }), - ]); - source.addFeature(feature3); - this.map = new Map({ - layers: [ - new LayerVector({ - source, - }), - ], - view: new View({ - projection: EPSG2056, - resolutions: [200, 100, 50, 20, 10, 5, 2.5, 2, 1], - center: [2600000, 1200000], - zoom: 4, - }), - }); -} -appmodule.controller('MainController', MainController); -options(appmodule); -export default appmodule; diff --git a/package.json b/package.json index a5d4a12c308f..eeb30b6e3172 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ }, "bin": { "compile-catalog": "buildtools/compile-catalog.js", - "check-example": "buildtools/check-example.js" + "check-example": "buildtools/check-example.js", + "svg-viewbox": "buildtools/svg-viewbox-cmd.js" }, "browser": { "ngeo/test": "./test/spec", @@ -199,7 +200,6 @@ "start-server-and-test": "2.0.5", "strip-bom": "5.0.0", "style-loader": "2.0.0", - "svg-inline-loader": "0.8.2", "svgo": "3.3.2", "svgo-loader": "3.0.3", "terser": "5.31.3", diff --git a/src/auth/FormElement.ts b/src/auth/FormElement.ts index ea65e432577c..ca5f8febd7ce 100644 --- a/src/auth/FormElement.ts +++ b/src/auth/FormElement.ts @@ -25,7 +25,7 @@ import GmfBaseElement from 'gmfapi/elements/BaseElement'; import {Message, MessageType} from 'ngeo/message/Message'; import ngeoMessageNotification from 'ngeo/message/Notification'; import {unsafeSVG} from 'lit/directives/unsafe-svg'; -import loadingSvg from 'gmf/icons/spinner.svg'; +import loadingSvg from 'gmf/icons/spinner'; import {gmfBackgroundlayerStatus} from 'ngeo/backgroundlayerselector/status'; import user, {User, UserState} from 'gmfapi/store/user'; // @ts-ignore @@ -225,12 +225,7 @@ export default class GmfAuthForm extends GmfBaseElement { ${this.isLoading ? html` ` : ''} diff --git a/src/auth/PanelElement.ts b/src/auth/PanelElement.ts index b670a607e128..f9ae5ec1c61e 100644 --- a/src/auth/PanelElement.ts +++ b/src/auth/PanelElement.ts @@ -22,7 +22,7 @@ import {html, TemplateResult, CSSResult, css, unsafeCSS} from 'lit'; import {customElement, property, state} from 'lit/decorators.js'; import {unsafeSVG} from 'lit/directives/unsafe-svg.js'; -import loadingSvg from 'gmf/icons/spinner.svg'; +import loadingSvg from 'gmf/icons/spinner'; import i18next from 'i18next'; import {PasswordValidator} from './FormElement'; import './FormElement'; @@ -62,12 +62,7 @@ export default class GmfAuthPanel extends ToolPanelElement { const spinnerTemplate = this.postLoading ? html`
- ${ - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - unsafeSVG(loadingSvg) - } + ${unsafeSVG(loadingSvg)} ${i18next.t('Loading themes, please wait...')}
` diff --git a/src/drawing/drawFeatureComponent.html b/src/drawing/drawFeatureComponent.html index 5418421f7a4b..b1f16a29a5b9 100644 --- a/src/drawing/drawFeatureComponent.html +++ b/src/drawing/drawFeatureComponent.html @@ -15,9 +15,11 @@ class="btn btn-default ngeo-drawfeature-point" ng-class="{active: dfCtrl.drawPoint.active}" ng-model="dfCtrl.drawPoint.active" - > - <%=require('gmf/icons/point.svg?viewbox&height=1em')%> - + > + - <%=require('gmf/icons/line.svg?viewbox&height=1em')%> - + > + - <%=require('gmf/icons/polygon.svg?viewbox&height=1em')%> - + > + - <%=require('gmf/icons/circle.svg?viewbox&height=1em')%> - + > + - <%=require('gmf/icons/rectangle.svg?viewbox&height=1em')%> - + > + - <%=require('gmf/icons/text.svg?viewbox&height=1em')%> - + > +
diff --git a/src/editing/editFeatureComponent.html b/src/editing/editFeatureComponent.html index 1842d6648b5b..d158cc2d8a55 100644 --- a/src/editing/editFeatureComponent.html +++ b/src/editing/editFeatureComponent.html @@ -16,22 +16,52 @@ > - <%=require('gmf/icons/point.svg?viewbox&height=1em')%> {{'Draw new point' | translate}} + + + + {{'Draw new point' | translate}} - <%=require('gmf/icons/point.svg?viewbox&height=1em')%> {{'Draw new point' | translate}} + + + + {{'Draw new point' | translate}} - <%=require('gmf/icons/line.svg?viewbox&height=1em')%> {{'Draw new linestring' | translate}} + + + + {{'Draw new linestring' | translate}} - <%=require('gmf/icons/line.svg?viewbox&height=1em')%> {{'Draw new linestring' | translate}} + + + + {{'Draw new linestring' | translate}} - <%=require('gmf/icons/polygon.svg?viewbox&height=1em')%> {{'Draw new polygon' | translate}} + + + + {{'Draw new polygon' | translate}} - <%=require('gmf/icons/polygon.svg?viewbox&height=1em')%> {{'Draw new polygon' | translate}} + + + + {{'Draw new polygon' | translate}} diff --git a/src/filter/rulecomponent.html b/src/filter/rulecomponent.html index e4bbe8c33040..3001bb400e9a 100644 --- a/src/filter/rulecomponent.html +++ b/src/filter/rulecomponent.html @@ -36,11 +36,36 @@
- <%=require('gmf/icons/point.svg')%> - <%=require('gmf/icons/line.svg')%> - <%=require('gmf/icons/polygon.svg')%> - <%=require('gmf/icons/circle.svg')%> - <%=require('gmf/icons/rectangle.svg')%> + + + + + + + + + +
- <%=require('gmf/icons/point.svg')%> - + > + - <%=require('gmf/icons/line.svg')%> - + > + - <%=require('gmf/icons/polygon.svg')%> - + > + - <%=require('gmf/icons/circle.svg')%> - + > + - <%=require('gmf/icons/rectangle.svg')%> - + > +
diff --git a/src/icons/README.md b/src/icons/README.md new file mode 100644 index 000000000000..04e8e3200904 --- /dev/null +++ b/src/icons/README.md @@ -0,0 +1,9 @@ +The icons that includes in the HTML are generated from the corresponding icon. + +They are generated by the following: + +```bash +cp src/icons/.svg src/icons/-1em.svg +node_modules/.bin/svgo src/icons/-1em.svg +node buildtools/svg-viewbox-cmd.js src/icons/-1em.svg 'height=1em' +``` diff --git a/src/icons/spinner.ts b/src/icons/spinner.ts new file mode 100644 index 000000000000..2c12a99e37f5 --- /dev/null +++ b/src/icons/spinner.ts @@ -0,0 +1,23 @@ +// The MIT License (MIT) +// +// Copyright (c) 2024 Camptocamp SA +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// svgo of spinner.svg +export default ``; diff --git a/src/import/importdatasourceComponent.html b/src/import/importdatasourceComponent.html index ad35c48e70e2..0b9cd66ffa09 100644 --- a/src/import/importdatasourceComponent.html +++ b/src/import/importdatasourceComponent.html @@ -63,9 +63,107 @@
- - <%=require('gmf/icons/spinner.svg?viewbox&height=1em')%> - + + + + + + + +
diff --git a/src/permalink/shareComponent.html b/src/permalink/shareComponent.html index 47a2ab7a05ac..0a5903067994 100644 --- a/src/permalink/shareComponent.html +++ b/src/permalink/shareComponent.html @@ -58,9 +58,107 @@ >
- - <%=require('gmf/icons/spinner.svg?viewbox&height=1rem')%> - + + + + + + + + diff --git a/src/print/component.html b/src/print/component.html index f5fc151c50f0..9c9119ae534e 100644 --- a/src/print/component.html +++ b/src/print/component.html @@ -153,9 +153,107 @@ diff --git a/src/query/panelComponent.html b/src/query/panelComponent.html index 52a447c880fe..c74f3f215708 100644 --- a/src/query/panelComponent.html +++ b/src/query/panelComponent.html @@ -5,27 +5,21 @@ class="btn btn-default" ng-class="{active: $ctrl.ngeoQueryModeSelector.mode === 'click'}" ng-click="$ctrl.ngeoQueryModeSelector.mode = 'click'" - > - <%=require('ngeo/icons/point.svg?viewbox&height=1em')%> - + > + > + >
diff --git a/src/query/windowComponent.html b/src/query/windowComponent.html index fa85a91bdc1d..9697607bbb58 100644 --- a/src/query/windowComponent.html +++ b/src/query/windowComponent.html @@ -1,5 +1,105 @@
- <%=require('gmf/icons/spinner.svg?viewbox&height=3rem')%> + + + + + + + +
{{ctrl.elevationValue}} - - <%=require('gmf/icons/spinner.svg?viewbox&height=1rem')%> - + + + + + + + + Raster diff --git a/tsconfig.json b/tsconfig.json index e5224a22ba80..3578a889f706 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "types": ["cypress", "node", "cypress-real-events"], "paths": { "ngeo/*": ["src/*"], + "gmf/*": ["src/*"], "gmfapi/*": ["srcapi/*"], "api/*": ["api/src/*"], "lib/*": ["lib/*"],