样式化组件末尾的字符串是什么?

时间:2019-04-16 15:29:45

标签: javascript reactjs typescript styled-components

技术:反应,打字稿

我确定这是重复的,但我只是不知道如何查找...

我想为样式组件包装创建包装器...
API's reference for the package that I'm talking about

无论我在哪里创建组件,都必须这样称呼它:

import styled from 'styled-components'

const mydiv = styled.div<WhateverInterface>`
  height: 20px;
`

这是我见过的唯一一种表示法,声明的末尾只有一个字符串...

无论我将一个prop传递给该特定的div,要使其覆盖默认值,我都需要执行以下操作:

import styled from 'styled-components'

const mydiv = styled.div<WhateverInterface>`
  height: ${props => props.height ? props.height : '20px'};
`

我创建了一个对所有键都执行此操作的函数,因此我想为 styled。[div,select,span,etc] ...创建一个包装器。被这样称呼:

import styled from 'styled-components'

function wrapper(styleComponent, rules) {
   return styledComponent`
       ${props => mergePropsAndDefaultRules(props, rules)}
   `
}

const mydiv = wrapper(styled.div<WhateverInterface>, `
    height: 20px;
`);

问题是...我不知道styled.div<WhateverInterface>末尾的字符串如何工作,表现如何……里面的道具甚至从哪里来?

有关此字符串的任何信息将不胜感激<3


PS:如果不清楚,我随机插入我的代码mergePropsAndDefaultRules中的函数将像这样工作:

  • 对于每个CSS属性,它将检查两个属性是否都存在。如果两个属性都存在,则将属性设置为props中的值,如果仅默认存在,则将css属性设置为props在默认规则内,道具相同...

示例:

props = {
  height: '30px',
  width: '20px',  
}

defaultRules = `
   height: 20px;
   border: 1px solid black;
`
const output = mergePropsAndDefaultRules(props, defaultRules)
console.log(typeof output, output)
/* 
Output:
string,
border: 1px solid black;
height: 30px;
width: 20px;
*/

1 个答案:

答案 0 :(得分:0)

所以...感谢@Tholle的评论,让我得以解决...


type CSSStyle = {[key: string] : (string|boolean)};

type CSSProperty = CSSStyle | {[key:string]: CSSStyle};

type Styles =  {[key: string] : CSSProperty};

interface IStyleGenerator {
    generateCSS: (props: Styles, standart: string) => string;
}

const CaseHelper = {
    snakeToCamel: s => s.replace(/(\-\w)/g, function(m){return m[1].toUpperCase();}),
    camelToSnake: c => c.replace(/([A-Z])/g, a => `-${a.toLowerCase()}`)
}

class LiberStyle implements IStyleGenerator {
    readonly defaultNamespace : string = 'standart';

    /**
     * 
     * @param propsStyle the style field from the props
     * @param standartStyle the css rules as string
     */
    generateCSS(propsStyle: Styles, standartStyle: string) : string {
        let rules = standartStyle.split('\n').map(a => a.trim()); // O(n * k);
        const defaultStyle = {};
        let namespace = this.defaultNamespace;
        defaultStyle[namespace] = {};
        const namespacePattern = /&:(\w+).+/;
        const hasDoubleMark = new RegExp(/:/);
        for(let rule of rules) {
            if(rule.length <= 0) continue;
            rule = rule.trim();
            if(rule.length <= 2) continue;
            if(rule === '}') {
                namespace = this.defaultNamespace
            } else {
                if(rule.length >= 2 && rule[0] === '&' && rule[1] === ':') {
                    namespace = rule.replace(namespacePattern, '$1');
                    defaultStyle[namespace] = {};
                } else {
                    if(!hasDoubleMark.test(rule)) continue;
                    const [key, value] = this.extractKeyValue(rule);
                    defaultStyle[namespace][key] = value;
                }
            }
        }
        const propsRemappedStyle = {};
        propsRemappedStyle[this.defaultNamespace] = {};
        Object['entries'](propsStyle).forEach(entry => {
            const [nmspace, rules] = entry;
            if(typeof rules === 'object') {
                propsRemappedStyle[nmspace] = rules
            } else {
                propsRemappedStyle[this.defaultNamespace][nmspace] = rules;
            }
        })
        return this.stringifyRules(propsRemappedStyle, defaultStyle);

    }

    /**
     * 
     *  PRIVATE METHODS ---------------------------------------------
     * 
     */


    extractKeyValue(rule : string) {
        let [key, value] = rule.split(':');
        value = value.trim();
        key = CaseHelper.snakeToCamel(key.trim());
        return [key, value.substring(0,value.length - 1)];
    }

    myInclude(array : any[], value : string) {
        let found = false;
        array.forEach((e:any) => {
            found = e === value;
            return found;
        })
        return found;
    }

    getNamespaceKeys(propKeys : any[]= [], defaultKeys : any[] = []) {
        let keys = defaultKeys.filter(style => !this.myInclude(propKeys,style));
        let ret = keys.concat(propKeys);
        // console.log({ret , propKeys, defaultKeys});
        return ret;
    }

    getKeysAndNamespace(propStyles : Styles, defaultStyles : Styles) {
        let namespacedKeys = Object.keys(propStyles)
        let namespaceMissingKeys = Object.keys(defaultStyles).filter((nmspace:string) => (!this.myInclude(namespacedKeys,nmspace) && (!this.defaultNamespace)));
        namespacedKeys = namespacedKeys.concat(namespaceMissingKeys);
        let allKeys = {};
        namespacedKeys.forEach(namespace => {
            allKeys[namespace] = this.getNamespaceKeys(Object.keys(propStyles[namespace] || {}), Object.keys(defaultStyles[namespace] || {}));
        })
        return { keys: allKeys, namespaces: namespacedKeys };
    }


     stringifyNamespace(namespace, keys, propRules, defaultRules) {
        let unifiedRules = '';
        let tab = namespace !== this.defaultNamespace ? '\t\t' : '\t'; 
        const camelToSnake = CaseHelper.camelToSnake;
        keys.forEach(key => {
            unifiedRules = `${unifiedRules}\n${tab}${camelToSnake(key)}: ${propRules[key] ? propRules[key] : defaultRules[key]};`
        });
        return namespace !== this.defaultNamespace ? `\t&:${namespace} {${unifiedRules}\n\t}` : unifiedRules;
    }

    stringifyRules(propsStyle : Styles, defaultStyle : Styles) {
        const  { keys, namespaces } = this.getKeysAndNamespace(propsStyle, defaultStyle);
        // console.log({keys, namespaces, propsStyle, defaultStyle });
        return namespaces.reduce((final, namespace) => {
            return `${final}\n${this.stringifyNamespace(namespace, keys[namespace], propsStyle[namespace], defaultStyle[namespace])}`
        }, '');
    }

}

export default new LiberStyle();

代码不是完美的...但是您可以使用它来创建如下包装器:

import LiberStyle from './liberStyle';

interface A {
}

interface B {

}

function generic(oi) {
    return 
}

const __props__ : B = {
    style: { 
        height: '40px',
        hover: {
            color: 'red'
        }
    },
    show: true

}
/**
 * This is just a function I created to mimick styled.div because I didnt have it installed on the PC I wrote this code
 */
function styled_div<T>(staticStrings: TemplateStringsArray, ...params) {
    let merged = '';
    const size = Math.max(staticStrings.length, params.length);
    for(let i=0; i < size; i++) {
        if(staticStrings[i]) {
            merged = merged + staticStrings[i].trim();
        }
        if(params[i]) {
            if(typeof params[i] === 'function') {
                merged = merged + params[i](__props__);
            } else if(typeof params[i] === 'object') {
                merged = merged + JSON.parse(params[i]);
            } else if(typeof params[i] === 'string') {
                merged = merged + params[i].trim();
            } else {
                merged = merged + params[i];
            }
        }
    }
    return merged;
}

function merge(props, staticStrings: TemplateStringsArray, params: any[]) {
    let merged = '';
    const size = Math.max(staticStrings.length, params.length);
    for(let i=0; i < size; i++) {
        if(staticStrings[i]) {
            merged = merged + staticStrings[i].trim();
        }
        if(params[i]) {
            if(typeof params[i] === 'function') {
                merged = merged + params[i](props);
            } else if(typeof params[i] === 'object') {
                merged = merged + JSON.parse(params[i]);
            } else {
                merged = merged + params[i].trim();
            }
        }
    }
    return merged;
}


const styled = {
    div: function div<T>(staticStrings: TemplateStringsArray, ...params) {
        return styled_div<T>`
            ${props => {
                const m = merge(props, staticStrings, params);
                //console.log({ style: props.style, m});
                return LiberStyle.generateCSS(props.style,m)}
            }
        `
    },
}

const waka = styled.div<A>`
    display: ${props => props.show ? 'block' : 'none'};
    height: 20px;
    width: 30px;
`;

console.log(waka);

这不是完美的,但是我希望如果有人遇到相同的问题,它可以为您带来启发...

相关问题