Skip to content

Instantly share code, notes, and snippets.

@MoOx
Last active December 3, 2018 08:50
Show Gist options
  • Select an option

  • Save MoOx/1eb30eac43b2114de73a to your computer and use it in GitHub Desktop.

Select an option

Save MoOx/1eb30eac43b2114de73a to your computer and use it in GitHub Desktop.

Revisions

  1. MoOx revised this gist Jun 1, 2015. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions svgicon.js
    Original file line number Diff line number Diff line change
    @@ -94,7 +94,7 @@ export default class SVGIcon extends Component {
    height = width
    }

    const props = {...this.props}
    const props = {...this.props, svg:null, fill: null, width: null, height:null}

    const classes = cx({
    "SVGIcon": true,
    @@ -109,7 +109,7 @@ export default class SVGIcon extends Component {
    React.createElement(
    component,
    {
    {...this.props, svg:null, fill: null, width: null, height:null}, // take most props
    ...props, // take most props
    className: classes,
    dangerouslySetInnerHTML: {
    __html: SVGIcon.cleanupSvg(svg, cleanup).replace(
  2. MoOx revised this gist Jun 1, 2015. 1 changed file with 1 addition and 6 deletions.
    7 changes: 1 addition & 6 deletions svgicon.js
    Original file line number Diff line number Diff line change
    @@ -95,11 +95,6 @@ export default class SVGIcon extends Component {
    }

    const props = {...this.props}
    // remove useless props for wrapper
    delete props.svg
    delete props.fill
    delete props.width
    delete props.height

    const classes = cx({
    "SVGIcon": true,
    @@ -114,7 +109,7 @@ export default class SVGIcon extends Component {
    React.createElement(
    component,
    {
    ...props, // take most props
    {...this.props, svg:null, fill: null, width: null, height:null}, // take most props
    className: classes,
    dangerouslySetInnerHTML: {
    __html: SVGIcon.cleanupSvg(svg, cleanup).replace(
  3. MoOx revised this gist May 29, 2015. 5 changed files with 271 additions and 96 deletions.
    30 changes: 0 additions & 30 deletions iconsvg.css
    Original file line number Diff line number Diff line change
    @@ -1,30 +0,0 @@
    .IconSvg {
    transform: translate3d(0,0,0); /* fix webkit/blink poor rendering issues */

    display: inline-block;

    /*width: 1em;*/
    height: 1em;
    line-height: 1;
    vertical-align: middle;

    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    }

    .IconSvg--half {
    width: .5em;
    height: .5em;
    }

    .IconSvg--white { color: #fff }
    .IconSvg--black { color: #000 }

    .IconSvg svg {
    width: inherit;
    height: inherit;
    line-height: inherit;

    color: inherit;
    fill: currentColor;
    }
    66 changes: 0 additions & 66 deletions iconsvg.js
    Original file line number Diff line number Diff line change
    @@ -1,66 +0,0 @@
    /**
    * <IconSvg />
    *
    * Usage
    * <IconSvg svg={require("gear.svg")} />
    * <IconSvg svg={require("gear.svg")} modifier="white" />
    * <IconSvg svg={require("gear.svg")} modifier="half" color="#f00" />
    *
    * @es6
    */

    require("./index.css")

    var React = require("react")

    function getSvgBody(svg) {
    return svg
    // remove xml prolog
    .replace(/<\?xml[\s\S]*?>/gi, "")
    // remove doctype
    .replace(/<!doctype[\s\S]*?>/gi, "")
    // remove comments
    .replace(/<!--[\s\S]*?-->/g, "")
    // remove hardcoded dimensions
    .replace(/width="\d+(\.\d+)?(px)?"/gi, "")
    .replace(/height="\d+(\.\d+)?(px)?"/gi, "")
    .trim()
    }

    export default React.createClass({
    propTypes: {
    svg: React.PropTypes.string.isRequired,
    id: React.PropTypes.string,
    modifier: React.PropTypes.string,
    color: React.PropTypes.string
    },

    render() {
    if (!this.props.svg.trim().match(/^\s*</g)) {
    console.warn("Please use <IconSvg> with <svg> file. props= " + JSON.stringify(this.props))
    }

    var iconClass = "IconSvg"
    var style = {}
    if (this.props.modifier) {
    iconClass += " IconSvg--" + this.props.modifier
    }
    if (this.props.color) {
    style.fill = this.props.color
    }
    if (this.props.size) {
    style.fontSize = this.props.size
    }

    return (
    <span
    className={(this.props.className ? this.props.className + " " : "") + iconClass}
    onClick={this.props.onClick}
    style={this.props.onClick ? {cursor: "pointer"} : null} // iOS won't trigger onClick if pointer is not pointer
    hidden={this.props.hidden}
    title={this.props.title}
    dangerouslySetInnerHTML={{__html: getSvgBody(this.props.svg)}}>
    </span>
    )
    }
    })
    22 changes: 22 additions & 0 deletions svgicon.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,22 @@
    .SVGIcon {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;

    /* fix webkit/blink poor rendering issues */
    transform: translate3d(0,0,0);

    /* it's better defined directly because of the cascade shit
    width: inherit;
    height: inherit;
    line-height: inherit;
    */
    }

    .SVGIcon-svg {
    width: inherit;
    height: inherit;
    line-height: inherit;

    color: inherit;
    fill: currentColor;
    }
    144 changes: 144 additions & 0 deletions svgicon.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    import React, {Component, PropTypes} from "react"
    // import styled from "bloody-react-styled"
    import cx from "classnames"

    // import styles from "./styles"

    const cleanups = {
    // some useless stuff for us
    // that svgo doesn't remove
    title: /<title>.*<\/title>/gi,
    desc: /<desc>.*<\/desc>/gi,
    comment: /<!--.*-->/gi,
    defs: /<defs>.*<\/defs>/gi,

    // remove hardcoded dimensions
    width: / +width="\d+(\.\d+)?(px)?"/gi,
    height: / +height="\d+(\.\d+)?(px)?"/gi,

    // remove fill
    fill: / +fill=\"(none|#[0-9a-f]+)\"/gi,

    // Sketch.app shit
    sketchMSShapeGroup: / +sketch:type=\"MSShapeGroup\"/gi,
    sketchMSPage: / +sketch:type=\"MSPage\"/gi,
    sketchMSLayerGroup: / +sketch:type=\"MSLayerGroup\"/gi,
    }

    // @styled(styles)
    export default class SVGIcon extends Component {

    static defaultProps = {
    component: "span",
    classSuffix: "-svg",
    cleanup: [],
    cleanupExceptions: [],
    }

    static propTypes = {
    component: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.func,
    ]),
    svg: PropTypes.string.isRequired,
    fill: PropTypes.string,
    cleanup: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.array,
    ]),
    width: PropTypes.string,
    height: PropTypes.string,
    }

    static cleanupSvg(svg, cleanup = []) {
    return Object.keys(cleanups)
    .filter(key => cleanup.includes(key))
    .reduce((acc, key) => {
    return acc.replace(cleanups[key], "")
    }, svg)
    .trim()
    }

    render() {
    const {
    className,
    component,
    svg,
    fill
    } = this.props

    let cleanup = this.props.cleanup
    if (
    // simple way to enable entire cleanup
    cleanup === true ||
    // passing cleanupExceptions enable cleanup as well
    (
    this.props.cleanup.length === 0 &&
    this.props.cleanupExceptions.length > 0
    )
    ) {
    cleanup = Object.keys(cleanups)
    }
    cleanup = cleanup.filter(
    key => {
    return !this.props.cleanupExceptions.includes(key)
    }
    )

    let {
    width,
    height
    } = this.props

    if (width && height === undefined) {
    height = width
    }

    const props = {...this.props}
    // remove useless props for wrapper
    delete props.svg
    delete props.fill
    delete props.width
    delete props.height

    const classes = cx({
    "SVGIcon": true,
    "SVGIcon--cleaned": cleanup.length,
    [className]: className,
    })
    const svgClasses = classes
    .split(" ")
    .join(this.props.classSuffix + " ") + this.props.classSuffix

    return (
    React.createElement(
    component,
    {
    ...props, // take most props
    className: classes,
    dangerouslySetInnerHTML: {
    __html: SVGIcon.cleanupSvg(svg, cleanup).replace(
    /<svg/,
    `<svg class="${ svgClasses }"` +
    (
    fill
    ? ` fill="${ fill }"`
    : ``
    ) +
    (
    width || height
    ? (
    ` style="` +
    (width ? `width: ${width};` : ``) +
    (height ? `height: ${height};` : ``) +
    `"`
    )
    : ""
    )
    ),
    },
    }
    )
    )
    }
    }
    105 changes: 105 additions & 0 deletions test.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,105 @@
    import tape from "tape-catch"
    import React, {Component} from "react"
    import SVGIcon from ".."

    tape("SVGIcon", (test) => {

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon className="TestSVG" svg="<svg><g></g></svg>" />
    ),
    `<span class="SVGIcon TestSVG"><svg class="SVGIcon-svg TestSVG-svg"` +
    `><g></g></svg></span>`,
    "passes & merges className"
    )

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon
    component="div"
    className="TestSVG"
    svg="<svg><g></g></svg>"
    />
    ),
    `<div class="SVGIcon TestSVG"><svg class="SVGIcon-svg TestSVG-svg"` +
    `><g></g></svg></div>`,
    "parent component can be chosen by tagName"
    )

    class TestComponent extends Component {
    render() {
    return (
    <div {...this.props} className="foo" />
    )
    }
    }

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon
    component={TestComponent}
    className="TestSVG"
    svg="<svg><g></g></svg>" />
    ),
    `<div class="foo"><svg class="SVGIcon-svg TestSVG-svg"><g></g></svg>` +
    `</div>`,
    "parent composite component can be chosen"
    )

    const svgPiece = `width="24" height="16px"><g fill="none"><path ` +
    `fill="#ab234f"></path></g></svg>`
    const SVGIconStart = `<span class="SVGIcon"><svg class="SVGIcon-svg"`
    const SVGIconCleanedStart = `<span class="SVGIcon SVGIcon--cleaned">` +
    `<svg class="SVGIcon-svg SVGIcon--cleaned-svg"`

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon svg={`<svg ${svgPiece}`} />
    ),
    `${SVGIconStart} ${svgPiece}</span>`,
    "doesn't cleanup the svg by default"
    )

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon cleanup svg={`<svg ${svgPiece}`} />
    ),
    `${SVGIconCleanedStart}><g><path></path></g></svg></span>`,
    "can cleanup the svg"
    )

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon cleanupExceptions={["fill"]} svg={`<svg ${svgPiece}`} />
    ),
    `${SVGIconCleanedStart}><g fill="none"><path fill="#ab234f"></path></g>` +
    `</svg></span>`,
    "cleanup the svg with exceptions"
    )

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon svg={`<svg><g></g></svg>`} width="1rem" />
    ),
    `${SVGIconStart} style="width: 1rem;height: 1rem;"><g></g></svg></span>`,
    "should add width (and height automatically)"
    )

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon svg={`<svg><g></g></svg>`} width="1rem" height="auto" />
    ),
    `${SVGIconStart} style="width: 1rem;height: auto;"><g></g></svg></span>`,
    "should add width & height"
    )

    test.equal(
    React.renderToStaticMarkup(
    <SVGIcon svg={`<svg><g></g></svg>`} height="1rem" />
    ),
    `${SVGIconStart} style="height: 1rem;"><g></g></svg></span>`,
    "should add height"
    )

    test.end()
    })
  4. MoOx revised this gist Mar 6, 2015. 6 changed files with 96 additions and 80 deletions.
    4 changes: 0 additions & 4 deletions iconsvg-_-helper.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +0,0 @@
    /* small helper for outside react view */
    module.exports = function(key, modifier) {
    return "<svg class=\"IconSvg IconSvg-" + key + " IconSvg--" + modifier + "\"><use xlink:href=\"#IconSvg-" + key + "\"></use></svg>"
    }
    19 changes: 0 additions & 19 deletions iconsvg-_-index.css
    Original file line number Diff line number Diff line change
    @@ -1,19 +0,0 @@
    .IconSvg {
    display: inline-block;

    width: 1em;
    height: 1em;
    line-height: 1;
    vertical-align: middle;

    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    }

    .IconSvg--half {
    width: .5em;
    height: .5em;
    }

    .IconSvg--white { fill: #fff }
    .IconSvg--black { fill: #000 }
    41 changes: 0 additions & 41 deletions iconsvg-_-index.js
    Original file line number Diff line number Diff line change
    @@ -1,41 +0,0 @@
    /**
    * <IconSvg />
    *
    * Usage
    * <IconSvg id="gear" />
    * <IconSvg id="gear" modifier="white"/>
    * <IconSvg id="gear" modifier="half" color="#f00" size="2rem" />
    *
    * @es6
    * @jsx React.DOM
    */

    /** @jsx React.DOM */

    module React from "react"

    export default React.createClass({
    render() {
    var iconClass = (this.props.className ? this.props.className + " " : "") + "IconSvg IconSvg-" + this.props.key
    var style = {}
    if (this.props.modifier) {
    iconClass += " IconSvg--" + this.props.modifier
    }
    if (this.props.color) {
    style.fill = this.props.color
    }
    if (this.props.size) {
    style.fontSize = this.props.size
    }

    return this.transferPropsTo(
    <svg
    className={iconClass}
    style={style}
    dangerouslySetInnerHTML={{__html:
    "<use xlink:href=\"#IconSvg-" + this.props.key + "\"></use>"
    }}>
    </svg>
    )
    }
    })
    30 changes: 30 additions & 0 deletions iconsvg.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,30 @@
    .IconSvg {
    transform: translate3d(0,0,0); /* fix webkit/blink poor rendering issues */

    display: inline-block;

    /*width: 1em;*/
    height: 1em;
    line-height: 1;
    vertical-align: middle;

    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    }

    .IconSvg--half {
    width: .5em;
    height: .5em;
    }

    .IconSvg--white { color: #fff }
    .IconSvg--black { color: #000 }

    .IconSvg svg {
    width: inherit;
    height: inherit;
    line-height: inherit;

    color: inherit;
    fill: currentColor;
    }
    66 changes: 66 additions & 0 deletions iconsvg.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,66 @@
    /**
    * <IconSvg />
    *
    * Usage
    * <IconSvg svg={require("gear.svg")} />
    * <IconSvg svg={require("gear.svg")} modifier="white" />
    * <IconSvg svg={require("gear.svg")} modifier="half" color="#f00" />
    *
    * @es6
    */

    require("./index.css")

    var React = require("react")

    function getSvgBody(svg) {
    return svg
    // remove xml prolog
    .replace(/<\?xml[\s\S]*?>/gi, "")
    // remove doctype
    .replace(/<!doctype[\s\S]*?>/gi, "")
    // remove comments
    .replace(/<!--[\s\S]*?-->/g, "")
    // remove hardcoded dimensions
    .replace(/width="\d+(\.\d+)?(px)?"/gi, "")
    .replace(/height="\d+(\.\d+)?(px)?"/gi, "")
    .trim()
    }

    export default React.createClass({
    propTypes: {
    svg: React.PropTypes.string.isRequired,
    id: React.PropTypes.string,
    modifier: React.PropTypes.string,
    color: React.PropTypes.string
    },

    render() {
    if (!this.props.svg.trim().match(/^\s*</g)) {
    console.warn("Please use <IconSvg> with <svg> file. props= " + JSON.stringify(this.props))
    }

    var iconClass = "IconSvg"
    var style = {}
    if (this.props.modifier) {
    iconClass += " IconSvg--" + this.props.modifier
    }
    if (this.props.color) {
    style.fill = this.props.color
    }
    if (this.props.size) {
    style.fontSize = this.props.size
    }

    return (
    <span
    className={(this.props.className ? this.props.className + " " : "") + iconClass}
    onClick={this.props.onClick}
    style={this.props.onClick ? {cursor: "pointer"} : null} // iOS won't trigger onClick if pointer is not pointer
    hidden={this.props.hidden}
    title={this.props.title}
    dangerouslySetInnerHTML={{__html: getSvgBody(this.props.svg)}}>
    </span>
    )
    }
    })
    16 changes: 0 additions & 16 deletions tasks-_-icons.js
    Original file line number Diff line number Diff line change
    @@ -1,16 +0,0 @@
    var gulp = require("gulp")
    var opts = require("./options")
    var util = require("gulp-util")
    var plumber = require("gulp-plumber")
    var svgSymbols = require("gulp-svg-symbols")

    module.exports = function() {
    return gulp.src("./src/components/**/icons/*.svg")
    .pipe(opts.plumber ? plumber() : util.noop())
    .pipe(svgSymbols({
    svgId: "IconSvg-%f",
    className: ".IconSvg-%f",
    fontSize: 512 // svg icons default font-size
    }))
    .pipe(gulp.dest("./dist/"))
    }
  5. MoOx revised this gist Jul 11, 2014. 3 changed files with 28 additions and 17 deletions.
    4 changes: 2 additions & 2 deletions iconsvg-_-helper.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    /* small helper for outside react view */
    module.exports = function(id, modifier) {
    return "<svg viewBox=\"0 0 512 512\" class=\"IconSvg IconSvg-" + id + " IconSvg--" + modifier + "\"><use xlink:href=\"#IconSvg-" + id + "\"></use></svg>"
    module.exports = function(key, modifier) {
    return "<svg class=\"IconSvg IconSvg-" + key + " IconSvg--" + modifier + "\"><use xlink:href=\"#IconSvg-" + key + "\"></use></svg>"
    }
    26 changes: 19 additions & 7 deletions iconsvg-_-index.js
    Original file line number Diff line number Diff line change
    @@ -4,26 +4,38 @@
    * Usage
    * <IconSvg id="gear" />
    * <IconSvg id="gear" modifier="white"/>
    * <IconSvg id="gear" modifier="half" color="#f00" />
    * <IconSvg id="gear" modifier="half" color="#f00" size="2rem" />
    *
    * @es6
    * @jsx React.DOM
    */

    /** @jsx React.DOM */

    module React from "react"

    class _IconSvg{
    export default React.createClass({
    render() {
    var iconClass = "IconSvg IconSvg-" + this.props.id
    var iconClass = (this.props.className ? this.props.className + " " : "") + "IconSvg IconSvg-" + this.props.key
    var style = {}
    if (this.props.modifier) {
    iconClass += " IconSvg--" + this.props.modifier
    }
    if (this.props.color) {
    style.fill = this.props.color
    }
    return <svg viewBox="0 0 512 512" className={iconClass} style={style} dangerouslySetInnerHTML={{__html: "<use xlink:href=\"#IconSvg-" + this.props.id + "\"></use>"}}></svg>
    }
    }
    if (this.props.size) {
    style.fontSize = this.props.size
    }

    export const IconSvg= React.createClass(_IconSvg.prototype);
    return this.transferPropsTo(
    <svg
    className={iconClass}
    style={style}
    dangerouslySetInnerHTML={{__html:
    "<use xlink:href=\"#IconSvg-" + this.props.key + "\"></use>"
    }}>
    </svg>
    )
    }
    })
    15 changes: 7 additions & 8 deletions tasks-_-icons.js
    Original file line number Diff line number Diff line change
    @@ -2,16 +2,15 @@ var gulp = require("gulp")
    var opts = require("./options")
    var util = require("gulp-util")
    var plumber = require("gulp-plumber")
    var svgSprites = require("gulp-svg-sprites").svg
    var svgSymbols = require("gulp-svg-symbols")

    module.exports = function() {
    return gulp.src("src/**/*.svg")
    .pipe(svgSprites({
    return gulp.src("./src/components/**/icons/*.svg")
    .pipe(opts.plumber ? plumber() : util.noop())
    .pipe(svgSymbols({
    svgId: "IconSvg-%f",
    defs: true,
    svg: {defs: "icons.svg"},
    generatePreview: false,
    generateCSS: false
    className: ".IconSvg-%f",
    fontSize: 512 // svg icons default font-size
    }))
    .pipe(gulp.dest("./dist/"))
    }
    }
  6. MoOx revised this gist May 22, 2014. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion tasks-_-icons.js
    Original file line number Diff line number Diff line change
    @@ -10,7 +10,8 @@ module.exports = function() {
    svgId: "IconSvg-%f",
    defs: true,
    svg: {defs: "icons.svg"},
    generatePreview: false
    generatePreview: false,
    generateCSS: false
    }))
    .pipe(gulp.dest("./dist/"))
    }
  7. MoOx created this gist May 21, 2014.
    4 changes: 4 additions & 0 deletions iconsvg-_-helper.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,4 @@
    /* small helper for outside react view */
    module.exports = function(id, modifier) {
    return "<svg viewBox=\"0 0 512 512\" class=\"IconSvg IconSvg-" + id + " IconSvg--" + modifier + "\"><use xlink:href=\"#IconSvg-" + id + "\"></use></svg>"
    }
    19 changes: 19 additions & 0 deletions iconsvg-_-index.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    .IconSvg {
    display: inline-block;

    width: 1em;
    height: 1em;
    line-height: 1;
    vertical-align: middle;

    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    }

    .IconSvg--half {
    width: .5em;
    height: .5em;
    }

    .IconSvg--white { fill: #fff }
    .IconSvg--black { fill: #000 }
    29 changes: 29 additions & 0 deletions iconsvg-_-index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    /**
    * <IconSvg />
    *
    * Usage
    * <IconSvg id="gear" />
    * <IconSvg id="gear" modifier="white"/>
    * <IconSvg id="gear" modifier="half" color="#f00" />
    *
    * @es6
    * @jsx React.DOM
    */

    module React from "react"

    class _IconSvg{
    render() {
    var iconClass = "IconSvg IconSvg-" + this.props.id
    var style = {}
    if (this.props.modifier) {
    iconClass += " IconSvg--" + this.props.modifier
    }
    if (this.props.color) {
    style.fill = this.props.color
    }
    return <svg viewBox="0 0 512 512" className={iconClass} style={style} dangerouslySetInnerHTML={{__html: "<use xlink:href=\"#IconSvg-" + this.props.id + "\"></use>"}}></svg>
    }
    }

    export const IconSvg= React.createClass(_IconSvg.prototype);
    16 changes: 16 additions & 0 deletions tasks-_-icons.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,16 @@
    var gulp = require("gulp")
    var opts = require("./options")
    var util = require("gulp-util")
    var plumber = require("gulp-plumber")
    var svgSprites = require("gulp-svg-sprites").svg

    module.exports = function() {
    return gulp.src("src/**/*.svg")
    .pipe(svgSprites({
    svgId: "IconSvg-%f",
    defs: true,
    svg: {defs: "icons.svg"},
    generatePreview: false
    }))
    .pipe(gulp.dest("./dist/"))
    }