svgr icon indicating copy to clipboard operation
svgr copied to clipboard

prefix the all id attributes with a string, random or customisable

Open revelt opened this issue 2 years ago • 7 comments

🚀 Feature Proposal

Prepend random, set string to each id, so that they're unique per this SVG.

Motivation

When you export multiple SVG from let's say Illustrator, you get same id names, for example #b, #d and so on. They tend to clash, so I have to manually go to the output of svgr and prepend some string to each id value. Can this be automated somehow?

Example

Can we use maybe nanoid or let's say add an input to the GUI sidebar to prepend that string to each id?

Pitch

Because in real-life, more often than not, there will be multiple SVG's on a given page, which, in turn, will have to have unique ids because they're on the same page.

revelt avatar Jun 05 '22 13:06 revelt

I actually had a similar issue with one of my projects, and I created a plugin to solve it.

You can find my plugin here https://github.com/AlfieJones/theme-toggles/blob/main/packages/react/plugins/babel-plugin-update-id-attribute.js

AlfieJones avatar Jun 08 '22 14:06 AlfieJones

My plugin adds a prop to each component to allow for an id prefix

AlfieJones avatar Jun 08 '22 14:06 AlfieJones

@AlfieJones Nice! How cool would it be if the GUI version had this functionality...

revelt avatar Jun 08 '22 14:06 revelt

I think it is a good addition, let's do it!

gregberge avatar Jun 19 '22 10:06 gregberge

@AlfieJones That's a good start but it only updates the ID for clipPath. URL references for masks, for example, would not work.

matthew-dean avatar Sep 12 '22 22:09 matthew-dean

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 13 '22 00:11 stale[bot]

Anyone looking for a now solution for this:

I've taken the current version of @AlfieJones plugin, fixed a bug around Hex values:

const t = require("@babel/core").types;
const template = require("@babel/core").template;

const getValueWithProps = (value, { prefix, suffix }) =>
  `${prefix ? "${props.idPrefix || ''}" : ""}${value}${
    suffix ? "${props.idSuffix || ''}" : ""
  }`;

const isHexValue = (possibleHexValue) => {
  return (
    possibleHexValue[0] === "#" &&
    !isNaN(parseInt(possibleHexValue.slice(1), 16))
  );
};

const getAttributeValue = (value, opts) => {
  let id = "";
  let prefix = "";
  let suffix = "";
  if (value && !isHexValue(value) && value.charAt(0) === "#") {
    id = value.slice(1);
    prefix = "#";
  } else if (value && value.match(/^url\(#/)) {
    id = value.slice(5, -1);
    prefix = "url(#";
    suffix = ")";
  }
  if (id) {
    return t.jsxExpressionContainer(
      template.ast(`\`${prefix}${getValueWithProps(id, opts)}${suffix}\``)
        .expression,
    );
  }
};

const getIdValue = (value, opts) =>
  t.jsxExpressionContainer(
    template.ast(`\`${getValueWithProps(value, opts)}\``).expression,
  );

const plugin = (api, opts) => ({
  visitor: {
    JSXAttribute(path) {
      if (!opts.prefix && !opts.suffix) return;

      const valuePath = path.get("value");
      const namePath = path.get("name");

      const value = valuePath?.container?.value?.value;
      const name = namePath?.container?.name?.name;

      if (name === "id") {
        valuePath.replaceWith(getIdValue(value, opts));
      } else {
        const attr = getAttributeValue(value, opts);
        if (attr) {
          valuePath.replaceWith(attr);
        }
      }
    },
  },
});

module.exports = plugin;

stick it in your svgr config like so:

module.exports = {
  // the rest of your config
  jsx: {
    babelConfig: {
      plugins: [
        [
          path.resolve("./dynamic-ids.js"),
          {
            prefix: true,
          },
        ],
      ],
    },
  },
};

Then you can set a prefix for your IDs via the idPrefix prop on the generated SVG components

G1itcher avatar Jul 14 '23 11:07 G1itcher