Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to write Nesting ? #319

Open
zhishaofei3 opened this issue Apr 4, 2018 · 9 comments
Open

how to write Nesting ? #319

zhishaofei3 opened this issue Apr 4, 2018 · 9 comments

Comments

@zhishaofei3
Copy link

.jump-btn {
            width: 32px;
            height: 32px;
            background: url('jump_btn.png') no-repeat 0 0;
            &.disabled {
                background-position: -32px 0;
            }
            &.back {
                background-position: -64px 0;
            }
}
@dmiller9911
Copy link
Contributor

This is somewhat not supported. The only real nesting that is supported is pseudo selectors (:hover, :active, etc) and media queries. The way you would do it in Aphrodite would be to do something like

const styles = StyleSheet.create({
  jumpBtn: {
    width: 32,
    height: 32,
    background: 'background: url('jump_btn.png') no-repeat 0 0',
  },
  disabled: {
    backgroundPosition: '-32px 0',
  },
  back: {
    backgroundPosition: '-64px 0',
  },
});
const classes = css(styles.jumpBtn, isDisabled && styles.disabled, isBack && styles.back);

There is an escape hatch by doing something like

const styles = StyleSheet.create({
  jumpBtn: {
    ...styles,
    ':not(foobar).disabled': { ....disabledStyles }
  },
});
const classes = 'disabled' + css(styles.jumpBtn)

I would try hard to not use this though.

@zhishaofei3
Copy link
Author

thank you verymuch @dmiller9911

@nakwa
Copy link

nakwa commented May 13, 2018

You can do that pretty easily by adding a custom selectorHandler with StyleSheet.extend

import { StyleSheet as Aphrodite } from 'aphrodite';
const { StyleSheet, css } = Aphrodite.extend([{
  selectorHandler: (selector, baseSelector, generateSubtreeStyles) => {
    if (selector[0] === '>') {
      const tag = selector.slice(1);
      const nestedTag = generateSubtreeStyles(`${baseSelector} ${tag}`);
      return nestedTag;
    }
    return null;
  }
}]);

Then just write your style as :

{
    color: "red",
    "> p": {
      textTransform: 'uppercase',
      fontSize: '0.8em',
      textDecoration: 'none'
    }
  }

Even better, just wrap all your styles into a function like this :

import _ from 'lodash';
function mapNestedStyle(stylesheet) {
  _.each(stylesheet, (item, selectorKey) => {
    const selectors = item._definition;
    _.each(selectors, (selector, key) => {
      if (_.isPlainObject(selector) && key[0] !== '>' && key[0] !== ':' && key[0] !== '@') {
        delete selectors[key];
        selectors[`> ${key}`] = selector;
      }
    });
  });
  return stylesheet;
}

So that you can just write :

{
    color: "red",
    p: {
      textTransform: 'uppercase',
      fontSize: '0.8em',
      textDecoration: 'none'
    }
  }

This can be automated using a React HOC.

I hope nesting will become part of the core functionalities at some point :)

@Sibz
Copy link
Contributor

Sibz commented May 28, 2018

@nakwa Perfect!

I do think selectors for ' ' (space - all descendants) and >(direct child descendants) should be included - it seems wrong to have to add styles to every component that needs styling.
For example, every label in a form... I want to just put the styles on the form to apply to all the labels.

I modified as follows

const { StyleSheet, css } = Aphrodite.extend([{
  selectorHandler: (selector, baseSelector, generateSubtreeStyles) => {
    if (selector[0] === ' ') {
      const tag = selector.slice(1);
      const nestedTag = generateSubtreeStyles(`${baseSelector} ${tag}`);
      return nestedTag;
    } else if (selector[0] === '>') {
      const tag = selector.slice(1);
      const nestedTag = generateSubtreeStyles(`${baseSelector} > ${tag}`);
      return nestedTag;
    }
    return null;
  }
}]);

so now this works:

Form: {
      borderWidth: 1,
      borderStyle: "solid",
      borderColor: "black",
      margin: 5,
      padding: 5,
      borderRadius: 7,
      " label" :{
        backgroundColor: "pink",
      }
    }

and produces:

.Form_1pi38cr {
    border-width: 1px !important;
    border-style: solid !important;
    border-color: rgb(66, 139, 202) !important;
    margin: 5px !important;
    padding: 5px !important;
    border-radius: 7px !important;
}
.Form_1pi38cr label {
    background-color: rgb(91, 192, 222) !important;
}

Would be great for it to be core functionality. Might look at the code base.
I use typescript so had to modify the typings file slightly for linting to pass.

@Sibz
Copy link
Contributor

Sibz commented May 28, 2018

Wasn't so hard to implement in base library:
master...Sibz:nesting

I'll ponder over the code a bit longer and then I might make it a PR.

@nakwa
Copy link

nakwa commented Jun 1, 2018

Yea, @Sibz , actually I think using a (space) or > ends up being quite unreadable.
I've updated my script to only enable nesting when the first char of the selector is &, which I believe is the current proposal for nesting in CSS: https://tabatkins.github.io/specs/css-nesting/

@nakwa
Copy link

nakwa commented Jun 1, 2018

Also, in order to support the , selector (applying style to multiple selectors), and ensure it is still within the aphrodite scope :

import _ from 'lodash';
import { StyleSheet as Aphrodite } from 'aphrodite/no-important';

const { StyleSheet, css } = Aphrodite.extend([{
  selectorHandler: (selector, baseSelector, generateSubtreeStyles) => {
    const nestedTags = [];
    const selectors = selector.split(',');
    _.each(selectors, (subselector, key) => {
      if (selector[0] === '&') {
        const tag = key === 0 ? subselector.slice(1) : subselector;
        const nestedTag = generateSubtreeStyles(`${baseSelector} ${tag}`.replace(/ +(?= )/g, ''));
        nestedTags.push(nestedTag);
      }
    });
    return _.isEmpty(nestedTags) ? null : _.flattenDeep(nestedTags);
  }
}]);

export { StyleSheet, css };

so that :

    '& .container-fluid, .container-fluid > .row': {
      height: '100%',
      '& .woop': {
        backgroundColor: 'red'
      }
    },

generates :

[
  ".component_s3hzln .container-fluid{height:100%;}", 
  ".component_s3hzln .container-fluid .woop{background-color:red;}", 
  ".component_s3hzln .container-fluid > .row{height:100%;}", 
  ".component_s3hzln .container-fluid > .row .woop{background-color:red;}"
]

@kevinbarabash
Copy link
Contributor

This is really neat. I think it would be awesome if we could support nesting like this. If someone has time to put together a PR for this it would be greatly appreciated.

@Sibz
Copy link
Contributor

Sibz commented Apr 5, 2019

I can, I used the actual CSS selectors [space] and > to support multi selection. It's been a long time since that project but it worked a dream!

Skywalker13 added a commit to Xcraft-Inc/goblin-laboratory that referenced this issue Jul 11, 2019
Skywalker13 added a commit to Xcraft-Inc/xcraft-dev-frontend-deps that referenced this issue Aug 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants