adamhaile / surplus

High performance JSX web views for S.js applications
639 stars 26 forks source link

Attribute spread and fn don't work #86

Closed Qix- closed 5 years ago

Qix- commented 5 years ago

Alternatively, allow us to use :before and :after in our scripts 🙃

I'm trying to get the following wrapper to work:

// css.js
import pseudo from './pseudo';

export default function css(styles) {
    let pseudoSpec = null;
    let styleSpec = {};

    for (const key of Object.keys(styles)) {
        switch (key) {
            case ':before':
                if (!pseudoSpec) pseudoSpec = {};
                pseudoSpec.before = styles[key];
                break;
            case ':after':
                if (!pseudoSpec) pseudoSpec = {};
                pseudoSpec.after = styles[key];
                break;
            default:
                styleSpec[key] = styles[key];
                break;
        }
    }

    const result = {};

    if (Object.keys(styleSpec).length > 0) {
        result.style = styleSpec;
    }

    if (pseudoSpec) {
        result.fn = pseudo(pseudoSpec);
    }

    return result;
}
// pseudo.js
let pseudoUid = 0;

function pseudo(node, element, prop, value) {
    const sheetId = 'pseudoStyles';
    const head = document.head || document.getElementsByTagName('head')[0];
    const sheet = document.getElementById(sheetId) || document.createElement('style');
    sheet.id = sheetId;
    const className = 'pseudoStyle' + (pseudoUid++);

    node.className += ' ' + className;

    sheet.innerHTML += ' .' + className + ':' + element + '{' + prop  + ':' + value + '}';
    head.appendChild(sheet);
}

export default function applyPseudo({before={}, after={}} = {}) {
    return e => {
        for (const key of Object.keys(before)) {
            pseudo(e, 'before', key, before[key]);
        }
        for (const key of Object.keys(after)) {
            pseudo(e, 'after', key, after[key]);
        }
    };
};
// example
const Foo = (<div {...css({
    ':after': {content: 'hello'},
    background: 'red'
})} />);

The style attribute gets applied just fine, but fn seems to be ignored.

Here's a simpler example:

const Foo = () => (<div fn={() => console.log('first')} {...{fn: () => console.log('second')}} />);

In the above, only first is printed to the console.


By the way, this is all stemming from the fact there's no documented way to use pseudo-styles on elements :/

Qix- commented 5 years ago

To be clear, I realize this isn't your fault. Pseudo elements aren't supported from javascript and have to be hacked in. I just wish there was a better way. :|

derekhawker commented 5 years ago

Not sure if that spread problem is related to pseudo-styles. Only a guess, but it's almost like the surplus compiler is only checking for fn attributes at compile time and in this case the fn attribute would only be resolved at runtime. That would explain why Surplus can support multiple fn attributes with the same name.

Qix- commented 5 years ago

That's a very good guess.

And right, the spread isn't directly related to pseudo styles. It's just my use case is adding them. However, upon further testing, pseudo styles from JavaScript are nearly impossible to do elegantly. I ended up using CSS variables instead.