Skip to content

Commit

Permalink
fix(item-sliding): options display on rtl (#27203)
Browse files Browse the repository at this point in the history
Issue URL: resolves #26103, resolves #25285

---------

<!-- Please refer to our contributing documentation for any questions on
submitting a pull request, or let us know here if you need any help:
https://ionicframework.com/docs/building/contributing -->

<!-- Some docs updates need to be made in the `ionic-docs` repo, in a
separate PR. See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#modifying-documentation
for details. -->

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Options in `item-sliding` will not display when using RTL with Firefox
and Safari.

<!-- Issues are required for both bug fixes and features. -->


## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

Issue was coming from `:host-context`. Firefox would keep [removing the
entire](https://www.w3.org/TR/selectors-3/#grouping) compiled style when
using this unsupported style. This would led to the RTL styles to not
being applied to the component.

- Split the CSS group from `add-root-selector()`
- Added comments to make it easier to navigate through
`add-root-selector()`
- Added `:dir()`

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Co-authored-by: brandyscarney <brandyscarney@users.noreply.github.com>

- Updating `add-root-selector()` would also fix another
[issue](#25285)
unintentionally

---------

Co-authored-by: ionitron <hi@ionicframework.com>
Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
  • Loading branch information
3 people authored May 9, 2023
1 parent 2bec25e commit b16fd1d
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 29 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 53 additions & 27 deletions core/src/themes/ionic.functions.string.scss
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,7 @@

// Add Root Selector
// --------------------------------------------------------------------------------
// Adds a root selector using host-context based on the selector passed
//
// Examples
// --------------------------------------------------------------------------------
// @include add-root-selector("[dir=rtl]", ":host")
// --> :host-context([dir=rtl])
//
// @include add-root-selector("[dir=rtl]", ":host(.fixed)")
// --> :host-context([dir=rtl]):host(.fixed)
// --> :host-context([dir=rtl]).fixed
//
// @include add-root-selector("[dir=rtl]", ":host(.tab-layout-icon-hide) ::slotted(ion-badge)")
// --> :host-context([dir=rtl]).tab-layout-icon-hide ::slotted(ion-badge)
//
// @include add-root-selector("[dir=rtl]", ".shadow")
// --> [dir=rtl] .shadow
// --> :host-context([dir=rtl]) .shadow
// Adds a root selector using host based on the selector passed
// --------------------------------------------------------------------------------

@function add-root-selector($root, $addHostSelector) {
Expand All @@ -110,8 +94,13 @@

@each $selector in $selectors {
// If the selector contains :host( it means it is targeting a class on the host
// element so we need to change how we target it
// element so we need to change how we target it:
// @include add-root-selector(":host(.fixed)", "[dir=rtl]")
// --> :host-context([dir=rtl]):host(.fixed)
// --> :host-context([dir=rtl]).fixed
@if str-contains($selector, ":host(") {
// @include add-root-selector(":host(.fixed)", "[dir=rtl]")
// --> :host-context([dir=rtl]):host(.fixed)
$shadow-element: str-replace($selector, ":host(", ":host-context(#{$addHostSelector}):host(");
$list: append($list, $shadow-element, comma);

Expand All @@ -122,26 +111,63 @@
@if str-contains($element, ":host(") {
$scoped-element: $element;

@if str-contains($element, "))") {
$scoped-element: str-replace($scoped-element, "))", ")");
} @else {
$scoped-element: str-replace($scoped-element, ")", "");
}
$scoped-element: str-replace($scoped-element, ":host(", ":host-context(#{$addHostSelector})");
// Replace the :host( and ) so all we have left is the class
// inside of it:
// :host(.fixed) -> .fixed
$scoped-element: str-replace($scoped-element, ")", "");
$scoped-element: str-replace($scoped-element, ":host(", "");

// Add the class back inside of host with the rtl selector:
// .fixed -> :host-context([dir=rtl]).fixed
$scoped-element: str-replace($scoped-element, $scoped-element, ":host-context(#{$addHostSelector})#{$scoped-element}");

// @include add-root-selector(":host(.fixed)", "[dir=rtl]")
// --> :host-context([dir=rtl]).fixed
$new-element: append($new-element, $scoped-element, space);
} @else {
// Add back any selectors that followed the host after transforming the
// first selector:
// :host(.fixed) ::slotted(ion-icon)
// --> :host-context([dir=rtl]):host(.fixed) ::slotted(ion-icon)
// --> :host-context([dir=rtl]).fixed ::slotted(ion-icon)
$new-element: append($new-element, $element, space);
}
}

$list: append($list, $new-element, comma);
// If the selector contains :host it means it is targeting just the host
// If the selector contains :host without a parantheses
// it means it is targeting just the host
// element so we can change it to look for host-context
// @include add-root-selector(":host", "[dir=rtl]")
// --> :host-context([dir=rtl])
// --> :host:dir(rtl)
} @else if str-contains($selector, ":host") {
$list: append($list, ":host-context(#{$addHostSelector})", comma);
$new-element: ();
$elements: str-split($selector, " ");

@each $element in $elements {
@if str-contains($element, ":host") {
// Replace the :host with the addHostSelector:
// :host -> :host-context([dir=rtl])
$updated-element: str-replace($element, ":host", ":host-context(#{$addHostSelector})");

// Add the final selector after all transformations:
// :host -> :host-context([dir=rtl])
$new-element: append($new-element, $updated-element, space);
} @else {
// Add back any selectors that followed the host after transforming the
// first selector:
// :host ::slotted(ion-icon) -> :host-context([dir=rtl]) ::slotted(ion-icon)
$new-element: append($new-element, $element, space);
}
}

$list: append($list, $new-element, comma);
// If the selector does not contain host at all it is either a shadow
// or normal element so append both the dir check and host-context
// or normal element so append both the addHostSelector and host-context
// @include add-root-selector("ion-component", "[dir=rtl]")
// --> :host-context([dir=rtl]) ion-component
// --> [dir=rtl] ion-component
} @else {
$list: append($list, "#{$addHostSelector} #{$selector}", comma);
$list: append($list, ":host-context(#{$addHostSelector}) #{$selector}", comma);
Expand Down
62 changes: 60 additions & 2 deletions core/src/themes/ionic.mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,66 @@
@mixin rtl() {
$root: #{&};

@at-root #{add-root-selector($root, "[dir=rtl]")} {
@content;
$rootSplit: str-split($root, ",");
$selectors: #{add-root-selector($root, "[dir=rtl]")};
$selectorsSplit: str-split($selectors, ",");

$hostContextSelectors: ();
$restSelectors: ();
$dirSelectors: ();

// Selectors must be split into individual selectors in case the browser
// doesn't support a specific selector.
// For example, Firefox and Safari doesn't support `:host-context()`.
// If an invalid selector is used, then the entire group of selectors
// will be ignored.
// @link https://www.w3.org/TR/selectors-3/#grouping
@each $selector in $selectorsSplit {
// Group the selectors back into a single selector to optimize the output.
@if str-index($selector, ":host-context") {
$hostContextSelectors: append($hostContextSelectors, $selector, comma);
} @else {
// Group the selectors back into a single selector to optimize the output.
$restSelectors: append($restSelectors, $selector, comma);
}
}

// Supported by Chrome.
@if length($hostContextSelectors) > 0 {
@at-root #{$hostContextSelectors} {
@content;
}
}

// Supported by all browsers.
@if length($restSelectors) > 0 {
@at-root #{$restSelectors} {
@content;
}
}

// If browser can support `:dir()`, then add the `:dir()` selectors.
@supports selector(:dir(rtl)) {
// Adding :dir() in case the browser doesn't support `:host-context()` and does support `:dir()`.
// `:host-context()` is added:
// - through the `add-root-selector()` function.
// - first so that it takes precedence over `:dir()`.
// For example,
// - Firefox doesn't support `:host-context()`, but does support `:dir()`.
// - Safari doesn't support `:host-context()`, but Safari 16.4+ supports `:dir()`
// @link https://webkit.org/blog/13966/webkit-features-in-safari-16-4/
@each $selector in $rootSplit {
$dirSelector: "#{$selector}:dir(rtl)";
// Group the selectors back into a single selector to optimize the output.
$dirSelectors: append($dirSelectors, $dirSelector, comma);
}

// Supported by Firefox.
@if length($dirSelectors) > 0 {
@at-root #{$dirSelectors} {
@content;
}
}
}
}

Expand Down

0 comments on commit b16fd1d

Please sign in to comment.