Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions libs/core/.storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,29 @@ const withTheme = (StoryFn, context) => {
return StoryFn();
};

// Direction decorator that applies `dir` on the preview iframe only.
// Unlike data-theme, `dir` is interpreted by the browser and must not be set on
// window.parent (that would flip Storybook manager/docs chrome).
// See Guides/RTL and localization for usage.
const withDirection = (StoryFn, context) => {
const direction = context.globals.direction || 'ltr';

document.documentElement.setAttribute('dir', direction);
document.body.setAttribute('dir', direction);

useEffect(() => {
document.documentElement.setAttribute('dir', direction);
document.body.setAttribute('dir', direction);
}, [direction]);

return StoryFn();
};

const preview = {
decorators: [
withCustomEventActions,
withTheme,
withDirection,
],

globalTypes: {
Expand All @@ -157,10 +176,23 @@ const preview = {
dynamicTitle: true,
},
},
direction: {
description: 'Document direction (LTR or RTL)',
toolbar: {
title: 'Direction',
icon: 'transfer',
items: [
{ value: 'ltr', title: 'LTR', right: 'Left-to-right' },
{ value: 'rtl', title: 'RTL', right: 'Right-to-left' },
],
dynamicTitle: true,
},
},
},

initialGlobals: {
theme: 'light',
direction: 'ltr',
},

parameters: {
Expand Down
18 changes: 12 additions & 6 deletions libs/core/src/components/pds-checkbox/pds-checkbox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
content: "";
display: block;
height: 7px;
left: 50%;
inset-block-start: 50%;
inset-inline-start: 50%;
position: absolute;
top: 50%;
transform: rotate(90deg) translate3d(-103%, 32%, 0);
transform: translate(-50%, -50%) rotate(90deg);
width: 4px;
}
}
Expand Down Expand Up @@ -90,10 +90,10 @@ input {
content: "";
display: block;
height: 7px;
left: 50%;
inset-block-start: 50%;
inset-inline-start: 50%;
position: absolute;
top: 50%;
transform: rotate(43deg) translate3d(-110%, -30%, 0);
transform: translate(-50%, -60%) rotate(43deg);
width: 4px;
}
}
Expand Down Expand Up @@ -126,6 +126,12 @@ input {
}
}

:host-context([dir='rtl']) {
input:checked::after {
transform: translate(-50%, -60%) rotate(-43deg);
}
}

label {
display: flex;
gap: var(--pine-dimension-xs);
Expand Down
2 changes: 1 addition & 1 deletion libs/core/src/components/pds-combobox/pds-combobox.scss
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@

.pds-combobox__input-icon {
color: var(--pine-color-text-secondary);
inset-inline-end: var(--pine-dimension-150);
pointer-events: none;
position: absolute;
right: var(--pine-dimension-150);
z-index: var(--pine-z-index-raised);
}

Expand Down
20 changes: 11 additions & 9 deletions libs/core/src/components/pds-filters/pds-filter/pds-filter.scss
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
/* Modern browsers: CSS anchor positioning with JavaScript-controlled flipping */
@supports (anchor-name: --test) {
inset-block-start: calc(anchor(--filter-trigger bottom) + var(--pine-dimension-100));
inset-inline-start: anchor(--filter-trigger left);
inset-inline-start: anchor(--filter-trigger start);
position: fixed; /* Anchor positioning requires fixed positioning */
/* stylelint-disable-next-line property-no-unknown */
position-anchor: --filter-trigger;
Expand All @@ -188,8 +188,8 @@

/* Flipping classes applied by JavaScript for precise control */
&.popover-flip-horizontal {
inset-inline-start: anchor(--filter-trigger right);
transform: translateX(-100%);
inset-inline-end: anchor(--filter-trigger end);
inset-inline-start: auto;
}

&.popover-flip-vertical {
Expand All @@ -199,14 +199,15 @@

&.popover-flip-horizontal.popover-flip-vertical {
inset-block-start: anchor(--filter-trigger top);
inset-inline-start: anchor(--filter-trigger right);
transform: translate(-100%, calc(-100% - var(--pine-dimension-100)));
inset-inline-end: anchor(--filter-trigger end);
inset-inline-start: auto;
transform: translateY(calc(-100% - var(--pine-dimension-100)));
}

/* Ensure fallback class works with flipping in modern browsers */
&.is-open.popover-flip-horizontal {
inset-inline-start: anchor(--filter-trigger right);
transform: translateX(-100%);
inset-inline-end: anchor(--filter-trigger end);
inset-inline-start: auto;
}

&.is-open.popover-flip-vertical {
Expand All @@ -216,8 +217,9 @@

&.is-open.popover-flip-horizontal.popover-flip-vertical {
inset-block-start: anchor(--filter-trigger top);
inset-inline-start: anchor(--filter-trigger right);
transform: translate(-100%, calc(-100% - var(--pine-dimension-100)));
inset-inline-end: anchor(--filter-trigger end);
inset-inline-start: auto;
transform: translateY(calc(-100% - var(--pine-dimension-100)));
}

}
Expand Down
20 changes: 12 additions & 8 deletions libs/core/src/components/pds-filters/pds-filter/pds-filter.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, Element, Event, EventEmitter, Host, h, Prop, State, Method, Listen } from '@stencil/core';
import type { BasePdsProps } from '@utils/interfaces';
import { isRtlDirection } from '@utils/scroll';
import type { PdsFilterOpenEventDetail, PdsFilterCloseEventDetail, PdsFilterClearEventDetail, PdsFilterVariant } from './filter-interface';

import { enlarge, trash } from '@pine-ds/icons/icons';
Expand Down Expand Up @@ -254,16 +255,19 @@ export class PdsFilter implements BasePdsProps {
const popoverWidth = 228;
const popoverHeight = this.popoverEl.getBoundingClientRect().height || 200;

// Boundary detection for flipping
// Boundary detection for flipping (inline-end overflow respects document direction)
const bufferSpace = 20;
const wouldOverflowRight = (triggerRect.left + popoverWidth + bufferSpace) > viewportWidth;
const isRtl = isRtlDirection(this.el);
const wouldOverflowInlineEnd = isRtl
? (triggerRect.right - popoverWidth - bufferSpace) < 0
: (triggerRect.left + popoverWidth + bufferSpace) > viewportWidth;
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
const wouldOverflowBottom = (triggerRect.bottom + 8 + popoverHeight + bufferSpace) > viewportHeight;

if (supportsAnchorPositioning) {
// Modern browsers: CSS anchor positioning + JavaScript-controlled flipping
this.popoverEl.classList.remove('popover-flip-horizontal', 'popover-flip-vertical');

if (wouldOverflowRight) {
if (wouldOverflowInlineEnd) {
this.popoverEl.classList.add('popover-flip-horizontal');
}

Expand All @@ -273,15 +277,15 @@ export class PdsFilter implements BasePdsProps {

} else {
// Fallback browsers: JavaScript positioning with boundary detection
let left = triggerRect.left;
let left = isRtl ? triggerRect.right - popoverWidth : triggerRect.left;
let top = triggerRect.bottom + 8;
let transformOrigin = 'top left';
let transformOrigin = isRtl ? 'top right' : 'top left';

// Apply horizontal flipping if needed
if (wouldOverflowRight) {
if (wouldOverflowInlineEnd) {
const actualPopoverWidth = this.popoverEl.getBoundingClientRect().width || popoverWidth;
left = triggerRect.right - actualPopoverWidth;
transformOrigin = 'top right';
left = isRtl ? triggerRect.left : triggerRect.right - actualPopoverWidth;
transformOrigin = isRtl ? 'top left' : 'top right';
}

// Apply vertical flipping if needed
Expand Down
8 changes: 6 additions & 2 deletions libs/core/src/components/pds-input/pds-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,14 @@
border-end-start-radius: var(--pds-input-border-radius);
border-inline-end: var(--pine-border-width-none);
border-start-start-radius: var(--pds-input-border-radius);
left: var(--pds-input-padding-x);
inset-inline-start: var(--pds-input-padding-x);
}

&::part(suffix) {
border-end-end-radius: var(--pds-input-border-radius);
border-inline-start: var(--pine-border-width-none);
border-start-end-radius: var(--pds-input-border-radius);
right: var(--pds-input-padding-x);
inset-inline-end: var(--pds-input-padding-x);
}

&::part(prepend),
Expand Down Expand Up @@ -114,15 +114,19 @@
--pds-select-background: var(--pds-input-addon-background);
--pds-select-border: var(--pine-border-width-none);
--pds-select-border-radius-end-end: var(--pine-dimension-none);
--pds-select-border-radius-end-start: var(--pds-input-border-radius);
--pds-select-border-radius-start-end: var(--pine-dimension-none);
--pds-select-border-radius-start-start: var(--pds-input-border-radius);
--pds-select-min-height: calc(var(--pine-dimension-450) - calc(2 * var(--pine-border-width)));
}

/* stylelint-disable-next-line */
:host([has-append]) ::slotted(pds-select[slot="append"]) {
--pds-select-background: var(--pds-input-addon-background);
--pds-select-border: var(--pine-border-width-none);
--pds-select-border-radius-end-end: var(--pds-input-border-radius);
--pds-select-border-radius-end-start: var(--pine-dimension-none);
--pds-select-border-radius-start-end: var(--pds-input-border-radius);
--pds-select-border-radius-start-start: var(--pine-dimension-none);
--pds-select-min-height: calc(var(--pine-dimension-450) - calc(2 * var(--pine-border-width)));
}
Expand Down
3 changes: 2 additions & 1 deletion libs/core/src/components/pds-modal/pds-modal.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
opacity: 0;
padding: 0;
position: fixed;
transition: opacity 0.2s ease, visibility 0.2s ease;
transition: opacity var(--pine-motion-duration-base) var(--pine-motion-easing-in), visibility var(--pine-motion-duration-base) var(--pine-motion-easing-in);
visibility: hidden;
width: 100%;
z-index: var(--pine-z-index-modal);
Expand All @@ -33,6 +33,7 @@

&.open {
opacity: 1;
transition-timing-function: var(--pine-motion-easing-out);
visibility: visible;
}
}
Expand Down
15 changes: 10 additions & 5 deletions libs/core/src/components/pds-select/pds-select.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,10 @@ select {
appearance: none;
background-color: var(--pds-select-background);
border: var(--pds-select-border);
border-radius: var(--pds-select-border-radius);
border-bottom-left-radius: var(--pds-select-border-radius-end-start, var(--pds-select-border-radius));
border-bottom-right-radius: var(--pds-select-border-radius-end-end, var(--pds-select-border-radius));
border-top-left-radius: var(--pds-select-border-radius-start-start, var(--pds-select-border-radius));
border-top-right-radius: var(--pds-select-border-radius-start-end, var(--pds-select-border-radius));
border-end-end-radius: var(--pds-select-border-radius-end-end, var(--pds-select-border-radius));
border-end-start-radius: var(--pds-select-border-radius-end-start, var(--pds-select-border-radius));
border-start-end-radius: var(--pds-select-border-radius-start-end, var(--pds-select-border-radius));
border-start-start-radius: var(--pds-select-border-radius-start-start, var(--pds-select-border-radius));
Comment thread
cursor[bot] marked this conversation as resolved.
color: var(--pine-color-text);
font: var(--pine-typography-body);
grid-area: field;
Expand Down Expand Up @@ -104,6 +103,12 @@ select {
}
}

:host(.is-disabled) {
.pds-select__select-icon {
color: var(--pine-color-text-disabled);
}
}

:host(.is-invalid) {
select {
background-color: var(--pine-select-color-background-danger);
Expand Down
8 changes: 4 additions & 4 deletions libs/core/src/components/pds-switch/pds-switch.scss
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ input {
display: block;
height: var(--sizing-input-toggle-size);
inset-block-start: 50%;
inset-inline-start: 50%;
inset-inline-start: var(--pine-dimension-025);
position: absolute;
transform: translate3d(-100%, -50%, 0);
transition: transform var(--number-transition-timing);
transform: translateY(-50%);
transition: inset-inline-start var(--number-transition-timing);
width: var(--sizing-input-toggle-size);
}
}
Expand Down Expand Up @@ -141,7 +141,7 @@ input:focus-visible:not(:disabled) {
// 'Checked' state
// switch toggle
input:checked::after {
transform: translate3d(0, -50%, 0);
inset-inline-start: calc(100% - var(--sizing-input-toggle-size) - var(--pine-dimension-025));
}

// switch container
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@

:host(.is-fixed) {
background: var(--pine-color-background-container);
left: 0;
inset-inline-start: 0;
Comment thread
cursor[bot] marked this conversation as resolved.
position: sticky;
z-index: var(--pine-z-index-raised);
}

// Flip shadow toward scrollable content when fixed column is on the inline-end edge.
:host-context([dir='rtl']) {
/* stylelint-disable-next-line pine-design-system/no-hardcoded-colors */
--box-shadow-fixed: -3px 3px 6px -2px rgba(0, 0, 0, 0.2);
}

// box shadow when table has scrolled and cell is fixed
:host(.has-scrolled.is-fixed) {
box-shadow: var(--box-shadow-fixed);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component, Element, Host, Prop, State, Watch, h } from '@stencil/core';
import { isScrolledFromInlineStart } from '../../../utils/scroll';
import { setupTruncationTooltip } from '../../../utils/truncation-tooltip';

@Component({
Expand Down Expand Up @@ -152,7 +153,7 @@ export class PdsTableCell {
}

try {
this.tableScrolling = this.scrollContainer.scrollLeft > 0;
this.tableScrolling = isScrolledFromInlineStart(this.scrollContainer);
} catch (error) {
console.warn('Scroll handler error:', error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

:host(.is-fixed) {
background: var(--pine-color-background-container);
left: var(--pine-dimension-none);
inset-inline-start: var(--pine-dimension-none);
position: sticky;
z-index: var(--pine-z-index-raised);
}
Expand All @@ -39,6 +39,12 @@
border-block-start: var(--border-head-cell-default);
}

// Flip shadow toward scrollable content when fixed column is on the inline-end edge.
:host-context([dir='rtl']) {
/* stylelint-disable-next-line pine-design-system/no-hardcoded-colors */
--box-shadow-default: -3px 3px 6px -2px rgba(0, 0, 0, 0.1);
}

// box shadow when table has scrolled and cell is fixed
:host(.has-scrolled.is-fixed) {
box-shadow: var(--box-shadow-default);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, Element, Host, Prop, h, Event, EventEmitter, State, Method } from '@stencil/core';

import { downSmall, upSmall } from '@pine-ds/icons/icons';
import { isScrolledFromInlineStart } from '../../../utils/scroll';

@Component({
tag: 'pds-table-head-cell',
Expand Down Expand Up @@ -188,7 +189,7 @@ export class PdsTableHeadCell {
}

try {
this.tableScrolling = this.scrollContainer.scrollLeft > 0;
this.tableScrolling = isScrolledFromInlineStart(this.scrollContainer);
} catch (error) {
console.warn('Scroll handler error:', error);
}
Expand Down
Loading
Loading