Box Shadow CSS Parser

384 Views Asked by At

I am trying to implement a box-shadow CSS parser for my project. Initially, I tried to split the CSS using , but this is not an appropriate solution. Cause some box shadow has RGB color code and some have hex color code. I need the correct Regex code and javascript code snippets for this task. I can't figure out how can I make this array. My box shadow CSS is: rgb(165, 33, 33) 5px 4px 20px 0px, inset rgb(0, 80, 255, 0.3) 15px 21px 36px 6px, #000000 5px 4px 20px 0px;

My desired output is:

[
  {
    color: {
      value: 'rgb(165, 33, 33)'
    },
    x: {
      value: 5,
      unit: 'px'
    },
    y: {
      value: 4,
      unit: 'px'
    },
    blur: {
      value: 20,
      unit: 'px'
    },
    spread: {
      value: 0,
      unit: 'px'
    },
    placement: {
      value: 'outset'
    }
  },
  {
    color: {
      value: 'rgb(0, 80, 255, 0.3)'
    },
    x: {
      value: 15,
      unit: 'px'
    },
    y: {
      value: 21,
      unit: 'px'
    },
    blur: {
      value: 36,
      unit: 'px'
    },
    spread: {
      value: 6,
      unit: 'px'
    },
    placement: {
      value: 'inset'
    }
  },
  {
    color: {
      value: '#000000'
    },
    x: {
      value: 5,
      unit: 'px'
    },
    y: {
      value: 4,
      unit: 'px'
    },
    blur: {
      value: 20,
      unit: 'px'
    },
    spread: {
      value: 6,
      unit: 'px'
    },
    placement: {
      value: 'outset'
    }
  }
]

My code:

const getValueAndUnit = (val = '') => {
    if (!specialValues.has(val.toLowerCase())) {
        const propRegx = /^([+-]?(?:\d+|\d*\.\d+))([a-z]*|%)$/;
        const matches = val.match(propRegx);
        let value = 0;
        let unit = 'px';

        if (Array.isArray(matches)) [, value, unit] = matches;

        return {
            value: Number(value),
            unit,
        };
    }
    return { value: 'auto', unit: 'auto' };
}


const getBoxShadowArray = (boxshadow) => {
  let shadow_arr = [];
  boxshadow
                .trim()
                .split(',')
                .forEach((v) => {
                    let styleObj = {};
                    let styleArray = v.trim().split(' ');
                    if (styleArray.length === 6) {
                        const [placementValue, colorValue, xValue, yValue, blurValue, spreadValue] = styleArray;
                        styleObj.placement = { value: placementValue };
                        styleObj.color = { value: colorValue };
                        styleObj.x = { ...getValueAndUnit(xValue) };
                        styleObj.y = { ...getValueAndUnit(yValue) };
                        styleObj.blur = { ...getValueAndUnit(blurValue) };
                        styleObj.spread = { ...getValueAndUnit(spreadValue) };
                    } else {
                        const [colorValue, xValue, yValue, blurValue, spreadValue] = styleArray;
                        styleObj.placement = { value: 'outset' };
                        styleObj.color = { value: colorValue };
                        styleObj.x = { ...getValueAndUnit(xValue) };
                        styleObj.y = { ...getValueAndUnit(yValue) };
                        styleObj.blur = { ...getValueAndUnit(blurValue) };
                        styleObj.spread = { ...getValueAndUnit(spreadValue) };
                    }
                    shadow_arr.push(styleObj);
                });
            return shadow_arr;
}


getBoxShadowArray('rgb(165, 33, 33) 5px 4px 20px 0px, inset rgb(0, 80, 255, 0.3) 15px 21px 36px 6px, #000000 5px 4px 20px 0px;');
1

There are 1 best solutions below

0
Poul Bak On

As @AKX says in comment, you can't reliably do this simply by using a regex, because the CSS format can be in many formats.

However, if you still want to try, you can use this regex, which won't split in the middle of colors:

/(?<!rgb\(\s*\d+(?:,\s*\d+(?:\.\d+)?)*),/g

Explanation:

(?<! - negative look behind for

rgb\(\s*\d+ - literal rgb( followed by zero or more whitespaces and 1 or more digits

(?: - non capturing group

,\s*\d+ - comma (,) followed by zero or more whitespaces and 1 or more digits

(?:\.\d+)? optionally a dot (.) followed by one or more digits

)* zero or more of this group

) - end group , - finally match comma (,)

This at least works on the example you gave, splitting into this:

0   rgb(165, 33, 33) 5px 4px 20px 0px
1   inset rgb(0, 80, 255, 0.3) 15px 21px 36px 6px
2   #000000 5px 4px 20px 0px;