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

Manipulating multiple CSS values with the use of one SWITCHER Control's 'selectors' field. #6241

Closed
iamzenigma opened this issue Nov 21, 2018 · 3 comments

Comments

Projects
None yet
2 participants
@iamzenigma
Copy link

commented Nov 21, 2018

Hi,

I've been looking around and I can't find anything concerning this topic. I've been working on a widget and I'm trying to create a SWITCHER-control that lets, let's say, sibling B 'inherit' values from the controls that belong to sibling A.

A More In-Depth Sketch Of The Problem
Sibling A is a container that holds its own CSS-properties and values which the user can define. For example the font size, width, height, padding etc. Now I want sibling B to be able to inherit these properties and values which are defined in A to make it more time-efficient for the user if he or she is planning to style both the same way.

Now I've almost made this work with the use of the 'selectors' key. There's just one problem. I'm able to copy over all the values from multiple controls by defining selectors in the SWITCHER-control, but if one of the values that I'm copying over is 'undefined' the whole 'selector-rule' isn't applied anymore.

An Example Might Be A More Suitable Explainer
I'll try to make it more clear by sharing code.

Here are two controls for sibling A:

$this->add_responsive_control(
    'a_alignment',
    [
        'label' => __( 'Alignment', 'your-text-domain' ),
        'type' => Controls_Manager::CHOOSE,
        'options' => [
            'left' => [
                'title' => __( 'Left', 'your-text-domain' ),
                'icon' => 'fa fa-align-left',
            ],
            'center' => [
                'title' => __( 'Center', 'your-text-domain' ),
                'icon' => 'fa fa-align-center',
            ],
            'right' => [
                'title' => __( 'Right', 'your-text-domain' ),
                'icon' => 'fa fa-align-right',
            ],
        ],
        'default' => 'center',
        'selectors' => [
            '{{WRAPPER}} .sibling-a' => 'text-align: {{VALUE}};',
        ],
    ]
);

$this->add_responsive_control(
    'a_spacing',
    [
        'label' => __( 'Spacing', 'your-text-domain' ),
        'type' => Controls_Manager::SLIDER,
        'range' => [
            'px' => [
                'min' => 0,
                'max' => 100,
            ],
        ],
        'selectors' => [
            '{{WRAPPER}} .sibling-a' => 'margin-bottom: {{SIZE}}{{UNIT}};',
        ],
    ]
);

These controls both manage a different CSS rule, but notice that they both point towards the same element. Also, they are both responsive controls, so for Sibling B to correctly inherit the values in the next step requires 6 CSS rules in total (for tablet 2x, mobile 2x and desktop 2x).

Here's the SWITCHER control for sibling B's inheritance.

$this->add_control(
    'b_inherits_a',
    [
        'label' => __( 'Inherit From Sibling A?', 'your-text-domain' ),
        'type' => Controls_Manager::SWITCHER,
        'default' => 'yes',
        'selectors' => [
            '(desktop){{WRAPPER}} .sibling-b' => 'text-align: {{a_alignment.VALUE}}; margin-bottom: {{a_spacing.SIZE}}{{a_spacing.UNIT}};',
            '(tablet){{WRAPPER}} .sibling-b' => 'text-align: {{a_alignment_tablet.VALUE}}; margin-bottom: {{a_spacing_tablet.SIZE}}{{a_spacing_tablet.UNIT}};',
            '(mobile){{WRAPPER}} .sibling-b' => 'text-align: {{a_alignment_mobile.VALUE}}; margin-bottom: {{a_spacing_mobile.SIZE}}{{a_spacing_mobile.UNIT}};',
        ]
    ]
);

So the problem arises when one of the values of sibling A's controls is undefined (corresponding to the device we're in). So if no value is given for the 'tablet alignment' of A, but a value for A's 'tablet spacing' is given, then B won't inherit the intended A's 'tablet spacing', because 'tablet alignment' causes an error in the syntax of the given CSS rule. (I assume the CSS rule looks something like this: 'text-align: UNDEFINED; margin-bottom: 199px;', but the whole CSS rule gets discarded for the error that arises.

The question
Is there a way to tackle multiple CSS rules on the same element in one selector?

Maybe it's a nice idea to allow an array as a value.

Like so:

'selectors' => [
    '(desktop){{WRAPPER}}` .sibling-b' => [
        'text-align: {{a_alignment.VALUE}};', 
        'margin-bottom: {{a_spacing.SIZE}}{{a_spacing.UNIT}};'
    ]
]

I hope this was somewhat comprehensible!

Thanks in advance.

@iamzenigma

This comment has been minimized.

Copy link
Author

commented Nov 21, 2018

Hi again,

I've found a workaround for this. For anyone who bumps against the same issue, a solution is to let the individual controls of A (in my example: the controls with ID's 'a_spacing' and a_alignment') also manipulate sibling B's values. This lets the controls manipulate A and B's values. These controls are actually defining both siblings A and B, therefore for naming conventions it might be best to call them something like 'global_spacing' and 'global_alignment' now. After you have done this, create sibling B's controls: 'b_spacing' and 'b_alignment'. This way you could also change the values of B if there's a need for individual styling of Sibling A and B.

Now define a SWITCHER and for naming conventions name it something like 'b_style_unlock' and only show all of B's styling controls if this SWITCHER is set to true/yes.

To wrap things up, here's an example on how I made it work, continuing on the example I gave in the previous comment.

//a_alignment became global_alignment and now changes the text-align for sibling A and B.
$this->add_responsive_control(
    'global_alignment',
    [
        'label' => __( 'Alignment', 'your-text-domain' ),
        'type' => Controls_Manager::CHOOSE,
        'options' => [
            'left' => [
                'title' => __( 'Left', 'your-text-domain' ),
                'icon' => 'fa fa-align-left',
            ],
            'center' => [
                'title' => __( 'Center', 'your-text-domain' ),
                'icon' => 'fa fa-align-center',
            ],
            'right' => [
                'title' => __( 'Right', 'your-text-domain' ),
                'icon' => 'fa fa-align-right',
            ],
        ],
        'default' => 'center',
        'selectors' => [
            '{{WRAPPER}} .sibling-a, {{WRAPPER}} .sibling-b' => 'text-align: {{VALUE}};',
        ],
    ]
);

//a_spacing became global_spacing and now changes the margin-bottom for sibling A and B
$this->add_responsive_control(
    'global_spacing',
    [
        'label' => __( 'Spacing', 'your-text-domain' ),
        'type' => Controls_Manager::SLIDER,
        'range' => [
            'px' => [
                'min' => 0,
                'max' => 100,
            ],
        ],
        'selectors' => [
            '{{WRAPPER}} .sibling-a, {{WRAPPER}} .sibling-b' => 'margin-bottom: {{SIZE}}{{UNIT}};',
        ],
    ]
);

//b_inherits_a became b_style_unlock and no selectors are necessary anymore.
$this->add_control(
    'b_style_unlock',
    [
        'label' => __( 'Unlock Styling For B?', 'your-text-domain' ),
        'type' => Controls_Manager::SWITCHER,
        'default' => 'no',
    ]
);

// The controls for b will only be visible if the SWITCHER is turned on.

//this is the control for b alignment, as this is declared later on it will overwrite the global alignments value (that are set for B) if a value is set.
$this->add_responsive_control(
    'b_alignment',
    [
        'label' => __( 'Alignment', 'your-text-domain' ),
        'type' => Controls_Manager::CHOOSE,
        'options' => [
            'left' => [
                'title' => __( 'Left', 'your-text-domain' ),
                'icon' => 'fa fa-align-left',
            ],
            'center' => [
                'title' => __( 'Center', 'your-text-domain' ),
                'icon' => 'fa fa-align-center',
            ],
            'right' => [
                'title' => __( 'Right', 'your-text-domain' ),
                'icon' => 'fa fa-align-right',
            ],
        ],
        'default' => 'center',
        'selectors' => [
            '{{WRAPPER}} .sibling-b' => 'text-align: {{VALUE}};',
        ],
        'conditions' => [
            'b_style_unlock' => 'yes'
        ]
    ]
);

//the spacing for b.
$this->add_responsive_control(
    'b_spacing',
    [
        'label' => __( 'Spacing', 'your-text-domain' ),
        'type' => Controls_Manager::SLIDER,
        'range' => [
            'px' => [
                'min' => 0,
                'max' => 100,
            ],
        ],
        'selectors' => [
            '{{WRAPPER}} .sibling-b' => 'margin-bottom: {{SIZE}}{{UNIT}};',
        ],
        'conditions' => [
            'b_style_unlock' => 'yes'
        ]
    ]
);

So I only had to change my approach. I feel quite stupid that I didn't come up with this earlier on, but yeah, my problem is solved. The only setback I can come up with is that B now always inherits the values of A. In my case this was actually a +, but this might not be the case for others, therefore I'm leaving this issue open as I think that a fix to the problem I described above might be a potential feature or improvement in Elementor's infrastructure!

@iamzenigma iamzenigma changed the title Changing multiple CSS values in the 'selectors' field when adding a control to a widget. Manipulating multiple CSS values with the use of one SWITCHER Control's 'selectors' field. Nov 21, 2018

@bainternet

This comment has been minimized.

Copy link
Collaborator

commented Nov 22, 2018

@iamzenigma

A Workaround is a nice approach, But I'm happy to tell you that in the next version we have added a built-in way to overcome this by defining a fallback to the CSS value which can either by a static (hardcoded value) or a controls default value. So something like:

$this->add_control(
	'b_inherits_a',
	[
		'label' => __( 'Inherit From Sibling A?', 'your-text-domain' ),
		'type' => Controls_Manager::SWITCHER,
		'default' => 'yes',
		'selectors' => [
			'(desktop){{WRAPPER}} .sibling-b' => 'text-align: {{a_alignment.VALUE || a_alignment.DEFAULT}};', //controls default value
			'(tablet){{WRAPPER}} .sibling-b' => 'text-align: {{a_alignment_tablet.VALUE || "center" }};', //hard coded value
			'(mobile){{WRAPPER}} .sibling-b' => 'text-align: {{a_alignment_mobile.VALUE || "left" }};',
		],
	]
);

The only limitation is that the DEFAULT token won't work for multiple value controls.

Thanks.

@bainternet bainternet closed this Nov 22, 2018

@iamzenigma

This comment has been minimized.

Copy link
Author

commented Nov 23, 2018

@bainternet
That sounds like a great idea! Love to see this come to fruition in the next version.

Thanks for taking the time to read and reply!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.