diff --git a/assets/stylesheets/_vendor.scss b/assets/stylesheets/_vendor.scss index 44276c9e566bbd..bd8bf83a5da623 100644 --- a/assets/stylesheets/_vendor.scss +++ b/assets/stylesheets/_vendor.scss @@ -5,15 +5,15 @@ .gridicon { fill: currentColor; - &.needs-offset g { + &.needs-offset use { transform: translate( 1px, 1px ); /* translates to .5px because it's in a child element */ } - &.needs-offset-x g { + &.needs-offset-x use { transform: translate( 1px, 0 ); /* only nudges horizontally */ } - &.needs-offset-y g { + &.needs-offset-y use { transform: translate( 0, 1px ); /* only nudges vertically */ } } diff --git a/client/blocks/inline-help/style.scss b/client/blocks/inline-help/style.scss index 9c0b82bdebdb53..00dc78432e8d7b 100644 --- a/client/blocks/inline-help/style.scss +++ b/client/blocks/inline-help/style.scss @@ -74,7 +74,7 @@ height: 36px; width: 36px; - g { + use { transform: none; } } diff --git a/client/components/async-gridicons/fallback.jsx b/client/components/async-gridicons/fallback.jsx deleted file mode 100644 index 935e1845eb3aa8..00000000000000 --- a/client/components/async-gridicons/fallback.jsx +++ /dev/null @@ -1,23 +0,0 @@ -/** @format */ - -/** - * External dependencies - */ -import React from 'react'; - -/** - * Internal dependencies - */ - -export default function FallbackIcon() { - /* eslint-disable wpcalypso/jsx-classname-namespace */ - return ( - - ); -} diff --git a/client/components/async-gridicons/index.jsx b/client/components/async-gridicons/index.jsx deleted file mode 100644 index c0cafbbd035e4b..00000000000000 --- a/client/components/async-gridicons/index.jsx +++ /dev/null @@ -1,63 +0,0 @@ -/** @format */ - -/** - * External dependencies - */ -import React, { Component } from 'react'; - -/** - * Internal dependencies - */ -import FallbackIcon from './fallback'; - -const loadedIcons = new Map(); - -function loadIcon( icon ) { - return import( /* webpackChunkName: "gridicons", webpackInclude: /\.js$/, webpackExclude: /dist\/(index\.js|example\.js)$/, webpackMode: "lazy-once" */ - `gridicons/dist/${ icon }` ).then( - g => { - loadedIcons.set( icon, g.default ); - return g.default; - }, - err => { - loadedIcons.set( icon, false ); - console.warn( `Error loading icon '${ icon }':`, err.message ); // eslint-disable-line no-console - } - ); -} - -class AsyncGridicon extends Component { - constructor( props ) { - super( props ); - } - checkAndLoad() { - if ( ! loadedIcons.has( this.props.icon ) ) { - loadIcon( this.props.icon ).then( () => this.update() ); - } - } - - componentDidMount() { - this.checkAndLoad(); - } - componentDidUpdate() { - this.checkAndLoad(); - } - - update = () => this.forceUpdate(); - - componentWillUnmount() { - this.update = () => {}; - } - - render() { - const { icon = '', ...rest } = this.props; - if ( loadedIcons.get( icon ) ) { - const Icon = loadedIcons.get( icon ); - return ; - } - - return ; - } -} - -export default AsyncGridicon; diff --git a/client/components/external-gridicons/icons-offset.js b/client/components/external-gridicons/icons-offset.js new file mode 100644 index 00000000000000..540e7b96ad528d --- /dev/null +++ b/client/components/external-gridicons/icons-offset.js @@ -0,0 +1,83 @@ +export const iconsThatNeedOffset = [ + 'gridicons-add-outline', + 'gridicons-add', + 'gridicons-align-image-center', + 'gridicons-align-image-left', + 'gridicons-align-image-none', + 'gridicons-align-image-right', + 'gridicons-attachment', + 'gridicons-bold', + 'gridicons-bookmark-outline', + 'gridicons-bookmark', + 'gridicons-calendar', + 'gridicons-cart', + 'gridicons-create', + 'gridicons-custom-post-type', + 'gridicons-external', + 'gridicons-folder', + 'gridicons-heading', + 'gridicons-help-outline', + 'gridicons-help', + 'gridicons-history', + 'gridicons-info-outline', + 'gridicons-info', + 'gridicons-italic', + 'gridicons-layout-blocks', + 'gridicons-link-break', + 'gridicons-link', + 'gridicons-list-checkmark', + 'gridicons-list-ordered', + 'gridicons-list-unordered', + 'gridicons-menus', + 'gridicons-minus', + 'gridicons-my-sites', + 'gridicons-notice-outline', + 'gridicons-notice', + 'gridicons-plus-small', + 'gridicons-plus', + 'gridicons-popout', + 'gridicons-posts', + 'gridicons-scheduled', + 'gridicons-share-ios', + 'gridicons-star-outline', + 'gridicons-star', + 'gridicons-stats', + 'gridicons-status', + 'gridicons-thumbs-up', + 'gridicons-textcolor', + 'gridicons-time', + 'gridicons-trophy', + 'gridicons-user-circle', + 'gridicons-reader-follow', + 'gridicons-reader-following', +]; + +export const iconsThatNeedOffsetY = [ + 'gridicons-align-center', + 'gridicons-align-justify', + 'gridicons-align-left', + 'gridicons-align-right', + 'gridicons-arrow-left', + 'gridicons-arrow-right', + 'gridicons-house', + 'gridicons-indent-left', + 'gridicons-indent-right', + 'gridicons-minus-small', + 'gridicons-print', + 'gridicons-sign-out', + 'gridicons-stats-alt', + 'gridicons-trash', + 'gridicons-underline', + 'gridicons-video-camera', +]; + +export const iconsThatNeedOffsetX = [ + 'gridicons-arrow-down', + 'gridicons-arrow-up', + 'gridicons-comment', + 'gridicons-clear-formatting', + 'gridicons-flag', + 'gridicons-menu', + 'gridicons-reader', + 'gridicons-strikethrough', +]; diff --git a/client/components/external-gridicons/index.js b/client/components/external-gridicons/index.js new file mode 100644 index 00000000000000..619cdcce163a26 --- /dev/null +++ b/client/components/external-gridicons/index.js @@ -0,0 +1,65 @@ +/** @format */ + +/** + * External dependencies + */ +import React from 'react'; +import PropTypes from 'prop-types'; +import svg4everybody from 'svg4everybody'; + +/** + * Internal dependencies + */ +import { iconsThatNeedOffset, iconsThatNeedOffsetX, iconsThatNeedOffsetY } from './icons-offset'; + +function needsOffset( name, icons ) { + return icons.indexOf( name ) >= 0; +} + +const isBrowser = typeof window !== 'undefined'; +if ( isBrowser ) { + // Polyfill SVG external content support. Noop in the evergreen build. + svg4everybody(); +} + +function ExternalGridicons( props ) { + const { size = 24, icon, onClick, className, ...otherProps } = props; + const isModulo18 = size % 18 === 0; + + // Using a missing icon doesn't produce any errors, just a blank icon, which is the exact intended behaviour. + // This means we don't need to perform any checks on the icon name. + const iconName = `gridicons-${ icon }`; + const offsetClasses = isModulo18 + ? [ + needsOffset( iconName, iconsThatNeedOffset ) ? 'needs-offset' : false, + needsOffset( iconName, iconsThatNeedOffsetX ) ? 'needs-offset-x' : false, + needsOffset( iconName, iconsThatNeedOffsetY ) ? 'needs-offset-y' : false, + ] + : []; + const iconClass = [ 'gridicon', iconName, className, ...offsetClasses ] + .filter( Boolean ) // Remove all falsy values. + .join( ' ' ); + + return ( + + + + ); +} + +ExternalGridicons.propTypes = { + icon: PropTypes.string.isRequired, + size: PropTypes.number, + onClick: PropTypes.func, + className: PropTypes.string, +}; + +export default React.memo( ExternalGridicons ); diff --git a/client/my-sites/plugins/plugin-action/style.scss b/client/my-sites/plugins/plugin-action/style.scss index 7a91ebdfd8664e..a12c07b27d841b 100644 --- a/client/my-sites/plugins/plugin-action/style.scss +++ b/client/my-sites/plugins/plugin-action/style.scss @@ -75,7 +75,7 @@ display: block; } - .gridicons-info-outline g { + .gridicons-info-outline use { // revert the translate(1px,1px) done by needs-offset transform: none; } diff --git a/client/my-sites/plugins/plugin-automated-transfer/style.scss b/client/my-sites/plugins/plugin-automated-transfer/style.scss index 5f427ce885568a..af18ae66c32399 100644 --- a/client/my-sites/plugins/plugin-automated-transfer/style.scss +++ b/client/my-sites/plugins/plugin-automated-transfer/style.scss @@ -1,10 +1,10 @@ .plugin-automated-transfer__notice { - .gridicons-sync g { + .gridicons-sync use { animation: spinning-sync-icon linear 2s infinite; transform-origin: center; } - .gridicons-checkmark g, - .gridicons-notice g { + .gridicons-checkmark use, + .gridicons-notice use { transform: rotate( 0deg ); } } diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 69d5daa14493b8..3a99eb1d730062 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -33,7 +33,7 @@ "caniuse-api": "3.0.0", "css-loader": "2.1.1", "duplicate-package-checker-webpack-plugin": "3.0.0", - "mini-css-extract-plugin-with-rtl": "github:Automattic/mini-css-extract-plugin-with-rtl#af1300db7027af8caa9a3015f54a34aec545cc54", + "mini-css-extract-plugin-with-rtl": "github:Automattic/mini-css-extract-plugin-with-rtl", "node-sass": "4.11.0", "postcss-custom-properties": "8.0.9", "postcss-loader": "3.0.0", @@ -47,7 +47,7 @@ "dependencies": { "autoprefixer": { "version": "9.4.4", - "bundled": true, + "resolved": "", "dev": true, "requires": { "browserslist": "^4.3.7", @@ -5280,7 +5280,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -5298,11 +5299,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5315,15 +5318,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -5426,7 +5432,8 @@ }, "inherits": { "version": "2.0.3", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -5436,6 +5443,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5448,17 +5456,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5475,6 +5486,7 @@ "mkdirp": { "version": "0.5.1", "bundled": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -5547,7 +5559,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -5557,6 +5570,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -5632,7 +5646,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -5662,6 +5677,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5679,6 +5695,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -5717,11 +5734,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.0.3", - "bundled": true + "bundled": true, + "optional": true } } } @@ -11442,7 +11461,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -11463,12 +11483,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11483,17 +11505,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -11610,7 +11635,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -11622,6 +11648,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -11636,6 +11663,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -11643,12 +11671,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -11667,6 +11697,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -11747,7 +11778,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -11759,6 +11791,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -11844,7 +11877,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -11880,6 +11914,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -11899,6 +11934,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -11942,12 +11978,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } } @@ -19065,6 +19103,11 @@ "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", "dev": true }, + "svg4everybody": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/svg4everybody/-/svg4everybody-2.1.9.tgz", + "integrity": "sha1-W9n23vwTOFmgRGRtR0P6vCjbfi0=" + }, "svgo": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.2.1.tgz", diff --git a/package.json b/package.json index 7dc9d1bfeedbe2..556686a765390a 100644 --- a/package.json +++ b/package.json @@ -184,6 +184,7 @@ "store": "2.0.12", "striptags": "2.2.1", "superagent": "3.8.3", + "svg4everybody": "2.1.9", "textarea-caret": "3.1.0", "tinymce": "4.8.5", "to-title-case": "1.0.0", diff --git a/public/images/gridicons/sprite.svg b/public/images/gridicons/sprite.svg new file mode 100644 index 00000000000000..798fb9013e02eb --- /dev/null +++ b/public/images/gridicons/sprite.svg @@ -0,0 +1,990 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webpack.config.js b/webpack.config.js index e7b8cdefad767a..18bd79c95d182a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -252,7 +252,7 @@ function getWebpackConfig( { 'social-logos/example': 'social-logos/build/example', debug: path.resolve( __dirname, 'node_modules/debug' ), store: 'store/dist/store.modern', - gridicons$: path.resolve( __dirname, 'client/components/async-gridicons' ), + gridicons$: path.resolve( __dirname, 'client/components/external-gridicons' ), }, getAliasesForExtensions( { extensionsDirectory: path.join( __dirname, 'client', 'extensions' ), @@ -331,6 +331,13 @@ function getWebpackConfig( { ); } + // The SVG external content polyfill (svg4everybody) isn't needed for evergreen browsers, so don't bundle it. + if ( browserslistEnv === 'evergreen' ) { + webpackConfig.plugins.push( + new webpack.NormalModuleReplacementPlugin( /^svg4everybody$/, 'lodash/noop' ) + ); + } + return webpackConfig; }