salesforce-ux / theo

Theo is a an abstraction for transforming and formatting Design Tokens

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Supporting nested maps?

jeaster12 opened this issue · comments

Hi All!

I'm wondering if there are some further examples of custom formatters? I'm attempting to support nested sass maps easier and haven't found a great solution.

For instance given:

props:
  - name: primary
    value:
      0: "#e8f7ff"
      1: "#ccedff"
      2: "#a3daff"
  - name: success
    value:
      0: "#ebfbee"
      1: "#d3f9d8"
      2: "#b2f2bb"

I could get something like:

$color-map: (
    'primary': (
        0: "#e8f7ff"
        1: "#ccedff"
        2: "#a3daff"
    ),
    'success': (
        0: "#ebfbee"
        1: "#d3f9d8"
        2: "#b2f2bb"
    ),
) !default;

Perhaps with the option to return a variables map?:

$color-map: (
    'primary': (
        0: "$brand-primary-0"
        1: "$brand-primary-1"
        2: "$brand-primary-2"
    ),
    'success': (
        0: "$brand-success-0"
        1: "$brand-success-1"
        2: "$brand-success-2"
    ),
) !default;

Thanks for any guidance!

Hey @jeaster12 you can try something like this for a custom format:

//test.format.js

const { Map } = require('immutable');

module.exports = theo => {

  theo.registerFormat('nested.map.scss', raw => {
    const props = raw.get('props');

    const data = props.map(prop => {
      const value = prop.get('value');
      if (Map.isMap(value)) {
        const name = prop.get('name');

        return {
          name,
          value: value.toObject()
        };
      } else {
        return value;
      }
    });

    const map = data.toArray().map(d => {
      const body = Object.keys(d.value).map(k => `${k}: ${d.value[k]}`);

      return `'${d.name}': ( ${body} )`;
    });

    return `$color-map: ( ${map.join('\n')} ) !default`;
  });
};

and then can run it from the CLI like so:

theo --setup test.format.js test2.yaml --format nested.map.scss --dest output

Let me know if this works for you

@trazek thanks! this is a great help! Is it also possible to return the map with variables?

$color-map: (
    'primary': (
        0: "$brand-primary-0"
        1: "$brand-primary-1"
        2: "$brand-primary-2"
    ),
    'success': (
        0: "$brand-success-0"
        1: "$brand-success-1"
        2: "$brand-success-2"
    ),
) !default;

Well you can make const body = Object.keys(d.value).map(k => ${k}: ${d.value[k]}); return whatever string you want. If you make it return $brand-primary-0, etc, it's not really formatting the token data however. This being a custom format, you are free to return the structure you desire and also return the mapping for $brand-primary-0, 1, 2 etc.

If I have time, I can create an example of what I mean but let me know if you get the gist.

Cheers

Perfect I got it figured out! Thanks again.

Is there a way to keep the platform specific type conversions with lists and maps? For example, given a map of colors

global:
  category: colors
  type: color
imports:
  - ../aliases/colors.yml
props:
  colors:
    value:
      primary: "{!blue}"
      secondary: "{!red}"

Theo will try to convert the map to a color and the result will be colors: "rgb(0, 0, 0)", when the target is web, instead of a map of colors.

Maybe a recursive definition for props would allow setting the type for any key. Although that ends up being quite verbose and allowing polymorphic types is questionable. A better option may be to add a new leafType option, that specifies the type of the leaf nodes.

I was able to do what I wanted using meta data and a custom formatter without too much difficulty. Still, it would be nice to declaratively represent the shape of the design tokens in a config file and not have to write a custom formatter to reshape them.