From cdd486ffa5017925b6808ac011e0d4088b37627d Mon Sep 17 00:00:00 2001 From: rdlabo Date: Wed, 16 Sep 2026 09:38:31 +0900 Subject: [PATCH] fix: support RTL structured list spacing --- src/popover/utils.spec.ts | 26 +++++++++++++++++++++++++- src/styles/structured-list.scss | 22 ++++++++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/popover/utils.spec.ts b/src/popover/utils.spec.ts index cbbf774..951714e 100644 --- a/src/popover/utils.spec.ts +++ b/src/popover/utils.spec.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest'; -import { calculateWindowAdjustment, getIndexOfItem, getNextItem, getPrevItem } from './utils'; +import { calculateWindowAdjustment, getIndexOfItem, getNextItem, getPopoverPosition, getPrevItem } from './utils'; describe('popover utilities', () => { it('navigates only relative to ion-item elements', () => { @@ -31,4 +31,28 @@ describe('popover utilities', () => { checkSafeAreaRight: true, }); }); + + it.each([ + { isRTL: false, side: 'start' as const, left: 60, originX: 'right' }, + { isRTL: false, side: 'end' as const, left: 140, originX: 'left' }, + { isRTL: true, side: 'start' as const, left: 140, originX: 'left' }, + { isRTL: true, side: 'end' as const, left: 60, originX: 'right' }, + ])('maps the logical $side side in RTL=$isRTL', ({ isRTL, side, left, originX }) => { + const trigger = document.createElement('button'); + trigger.getBoundingClientRect = () => ({ + top: 20, + left: 100, + width: 40, + height: 30, + right: 140, + bottom: 50, + x: 100, + y: 20, + toJSON: () => ({}), + }); + + expect( + getPopoverPosition(isRTL, 40, 20, 'trigger', side, 'start', { top: 0, left: 0, originX: 'left', originY: 'top' }, trigger), + ).toMatchObject({ left, originX }); + }); }); diff --git a/src/styles/structured-list.scss b/src/styles/structured-list.scss index fa862a8..0ad41f4 100644 --- a/src/styles/structured-list.scss +++ b/src/styles/structured-list.scss @@ -81,14 +81,16 @@ $groups: ion-item-group, ion-reorder-group, ion-accordion-group, ion-radio-group font-size: 0.76rem; line-height: 1.06rem; font-weight: 400; - margin: 8px 16px 4px 0; + margin-block: 8px 4px; + margin-inline: 0 16px; &:not(.ion-color) { --color: var(--ion-color-medium-tint); } } @else { font-size: 0.9rem; - margin: 8px 16px 4px 0; + margin-block: 8px 4px; + margin-inline: 0 16px; } } } @@ -158,6 +160,10 @@ $groups: ion-item-group, ion-reorder-group, ion-accordion-group, ion-radio-group padding: 32px 16px 16px; transform: translateX(5px); + &:dir(rtl) { + transform: translateX(-5px); + } + /** * ion-header.header-collapse-condenseを使って、スクロールによってHeaderを変化させるトリック */ @@ -255,7 +261,7 @@ $groups: ion-item-group, ion-reorder-group, ion-accordion-group, ion-radio-group } ion-text[slot='end']#{$d} { - padding-left: 8px; + padding-inline-start: 8px; } &.item-disabled { @@ -280,13 +286,18 @@ $groups: ion-item-group, ion-reorder-group, ion-accordion-group, ion-radio-group } & > ion-note#{$d} { + margin-block: 8px; + margin-inline: calc(var(--ion-safe-area-left, 0) + $outer-note-inset) calc(var(--ion-safe-area-right, 0) + $outer-note-inset); + + &:dir(rtl) { + margin-inline: calc(var(--ion-safe-area-right, 0) + $outer-note-inset) calc(var(--ion-safe-area-left, 0) + $outer-note-inset); + } + @if $is-ios27 { font-size: 0.76rem; line-height: 1.06rem; font-weight: 400; display: block; - margin: 8px calc(var(--ion-safe-area-right, 0) + $outer-note-inset) 8px calc(var(--ion-safe-area-left, 0) + $outer-note-inset); - &:not(.ion-color) { --color: var(--ion-color-medium-tint); } @@ -294,7 +305,6 @@ $groups: ion-item-group, ion-reorder-group, ion-accordion-group, ion-radio-group --color: var(--ion-color-medium-tint); font-size: 0.9rem; display: block; - margin: 8px calc(var(--ion-safe-area-right, 0) + $outer-note-inset) 8px calc(var(--ion-safe-area-left, 0) + $outer-note-inset); } } }