@transferwise/components 46.160.0 → 46.160.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/build/Calendar/Calendar.js +9 -3
  2. package/build/Calendar/Calendar.js.map +1 -1
  3. package/build/Calendar/Calendar.mjs +9 -3
  4. package/build/Calendar/Calendar.mjs.map +1 -1
  5. package/build/Carousel/Carousel.js +17 -10
  6. package/build/Carousel/Carousel.js.map +1 -1
  7. package/build/Carousel/Carousel.mjs +17 -10
  8. package/build/Carousel/Carousel.mjs.map +1 -1
  9. package/build/ExpressiveMoneyInput/Chevron/Chevron.js +10 -2
  10. package/build/ExpressiveMoneyInput/Chevron/Chevron.js.map +1 -1
  11. package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs +11 -3
  12. package/build/ExpressiveMoneyInput/Chevron/Chevron.mjs.map +1 -1
  13. package/build/ProcessIndicator/ProcessIndicator.js +1 -1
  14. package/build/ProcessIndicator/ProcessIndicator.js.map +1 -1
  15. package/build/ProcessIndicator/ProcessIndicator.mjs +1 -1
  16. package/build/ProcessIndicator/ProcessIndicator.mjs.map +1 -1
  17. package/build/Prompt/CriticalBanner/CriticalBanner.js +10 -1
  18. package/build/Prompt/CriticalBanner/CriticalBanner.js.map +1 -1
  19. package/build/Prompt/CriticalBanner/CriticalBanner.mjs +10 -1
  20. package/build/Prompt/CriticalBanner/CriticalBanner.mjs.map +1 -1
  21. package/build/SegmentedControl/SegmentedControl.js +9 -2
  22. package/build/SegmentedControl/SegmentedControl.js.map +1 -1
  23. package/build/SegmentedControl/SegmentedControl.mjs +9 -2
  24. package/build/SegmentedControl/SegmentedControl.mjs.map +1 -1
  25. package/build/Tooltip/Tooltip.js +6 -1
  26. package/build/Tooltip/Tooltip.js.map +1 -1
  27. package/build/Tooltip/Tooltip.mjs +6 -1
  28. package/build/Tooltip/Tooltip.mjs.map +1 -1
  29. package/build/common/Panel/Panel.js +13 -25
  30. package/build/common/Panel/Panel.js.map +1 -1
  31. package/build/common/Panel/Panel.mjs +10 -22
  32. package/build/common/Panel/Panel.mjs.map +1 -1
  33. package/build/common/Panel/Panel.positionUtils.js +68 -0
  34. package/build/common/Panel/Panel.positionUtils.js.map +1 -0
  35. package/build/common/Panel/Panel.positionUtils.mjs +63 -0
  36. package/build/common/Panel/Panel.positionUtils.mjs.map +1 -0
  37. package/build/common/PropsValues/position.js +0 -25
  38. package/build/common/PropsValues/position.js.map +1 -1
  39. package/build/common/PropsValues/position.mjs +1 -24
  40. package/build/common/PropsValues/position.mjs.map +1 -1
  41. package/build/i18n/tr.json +5 -0
  42. package/build/i18n/tr.json.js +5 -0
  43. package/build/i18n/tr.json.js.map +1 -1
  44. package/build/i18n/tr.json.mjs +5 -0
  45. package/build/i18n/tr.json.mjs.map +1 -1
  46. package/build/i18n/zh-CN.json +5 -0
  47. package/build/i18n/zh-CN.json.js +5 -0
  48. package/build/i18n/zh-CN.json.js.map +1 -1
  49. package/build/i18n/zh-CN.json.mjs +5 -0
  50. package/build/i18n/zh-CN.json.mjs.map +1 -1
  51. package/build/main.css +134 -88
  52. package/build/styles/AvatarLayout/AvatarLayout.css +26 -9
  53. package/build/styles/AvatarView/AvatarView.css +8 -0
  54. package/build/styles/AvatarView/Dot/Dot.css +8 -0
  55. package/build/styles/CheckboxButton/CheckboxButton.css +3 -0
  56. package/build/styles/Nudge/Nudge.css +4 -38
  57. package/build/styles/Prompt/CriticalBanner/CriticalBanner.css +34 -0
  58. package/build/styles/SegmentedControl/SegmentedControl.css +2 -1
  59. package/build/styles/SlidingPanel/SlidingPanel.css +8 -0
  60. package/build/styles/Tooltip/Tooltip.css +0 -6
  61. package/build/styles/UploadInput/UploadItem/UploadItem.css +4 -5
  62. package/build/styles/_deprecated/Badge/Badge.css +4 -2
  63. package/build/styles/common/Panel/Panel.css +16 -8
  64. package/build/styles/css/accordion.css +3 -0
  65. package/build/styles/css/buttons.css +1 -7
  66. package/build/styles/css/chevron.css +3 -0
  67. package/build/styles/css/neptune.css +25 -19
  68. package/build/styles/css/process.css +15 -9
  69. package/build/styles/css/tick.css +6 -3
  70. package/build/styles/main.css +134 -88
  71. package/build/styles/styles/less/accordion.css +3 -0
  72. package/build/styles/styles/less/buttons.css +1 -7
  73. package/build/styles/styles/less/chevron.css +3 -0
  74. package/build/styles/styles/less/neptune.css +25 -19
  75. package/build/styles/styles/less/process.css +15 -9
  76. package/build/styles/styles/less/tick.css +6 -3
  77. package/build/types/Calendar/Calendar.d.ts.map +1 -1
  78. package/build/types/Carousel/Carousel.d.ts.map +1 -1
  79. package/build/types/Carousel/_stories/constants.d.ts +3 -0
  80. package/build/types/Carousel/_stories/constants.d.ts.map +1 -0
  81. package/build/types/ExpressiveMoneyInput/Chevron/Chevron.d.ts.map +1 -1
  82. package/build/types/Prompt/CriticalBanner/CriticalBanner.d.ts +6 -1
  83. package/build/types/Prompt/CriticalBanner/CriticalBanner.d.ts.map +1 -1
  84. package/build/types/SegmentedControl/SegmentedControl.d.ts.map +1 -1
  85. package/build/types/Tooltip/Tooltip.d.ts.map +1 -1
  86. package/build/types/common/Panel/Panel.d.ts +1 -1
  87. package/build/types/common/Panel/Panel.d.ts.map +1 -1
  88. package/build/types/common/Panel/Panel.positionUtils.d.ts +7 -0
  89. package/build/types/common/Panel/Panel.positionUtils.d.ts.map +1 -0
  90. package/build/types/common/PropsValues/position.d.ts +0 -13
  91. package/build/types/common/PropsValues/position.d.ts.map +1 -1
  92. package/build/types/common/ResponsivePanel/ResponsivePanel.d.ts +1 -1
  93. package/package.json +8 -8
  94. package/src/AvatarLayout/AvatarLayout.css +26 -9
  95. package/src/AvatarLayout/AvatarLayout.less +46 -22
  96. package/src/AvatarLayout/_stories/AvatarLayout.test.story.tsx +17 -42
  97. package/src/AvatarView/AvatarView.css +8 -0
  98. package/src/AvatarView/Dot/Dot.css +8 -0
  99. package/src/AvatarView/Dot/Dot.less +7 -1
  100. package/src/Button/_stories/Button.test.story.tsx +1 -1
  101. package/src/Calendar/Calendar.test.tsx +82 -0
  102. package/src/Calendar/Calendar.tsx +6 -3
  103. package/src/Carousel/Carousel.test.tsx +50 -2
  104. package/src/Carousel/Carousel.tsx +13 -10
  105. package/src/Carousel/_stories/Carousel.story.tsx +12 -52
  106. package/src/Carousel/_stories/Carousel.test.story.tsx +7 -43
  107. package/src/Carousel/_stories/constants.tsx +46 -0
  108. package/src/Checkbox/_stories/Checkbox.story.tsx +1 -1
  109. package/src/Checkbox/_stories/Checkbox.test.story.tsx +3 -2
  110. package/src/CheckboxButton/CheckboxButton.css +3 -0
  111. package/src/CheckboxButton/CheckboxButton.less +4 -0
  112. package/src/Chevron/_stories/Chevron.test.story.tsx +2 -2
  113. package/src/DateLookup/_stories/DateLookup.rtl.test.story.tsx +59 -48
  114. package/src/ExpressiveMoneyInput/Chevron/Chevron.tsx +8 -3
  115. package/src/Field/_stories/Field.test.story.tsx +48 -37
  116. package/src/FlowNavigation/_stories/FlowNavigation.rtl.test.story.tsx +78 -0
  117. package/src/FlowNavigation/_stories/FlowNavigation.test.story.tsx +0 -44
  118. package/src/Header/_stories/Header.test.story.tsx +1 -1
  119. package/src/Info/_stories/Info.rtl.test.story.tsx +35 -10
  120. package/src/Info/_stories/Info.story.tsx +1 -1
  121. package/src/Inputs/SelectInput/_stories/SelectInput.test.story.tsx +1 -1
  122. package/src/Label/_stories/Label.test.story.tsx +35 -22
  123. package/src/ListItem/_stories/subcomponents.tsx +1 -1
  124. package/src/Loader/_stories/Loader.test.story.tsx +23 -0
  125. package/src/MoneyInput/_stories/MoneyInput.rtl.test.story.tsx +1 -1
  126. package/src/Nudge/Nudge.css +4 -38
  127. package/src/Nudge/Nudge.less +4 -4
  128. package/src/Nudge/_stories/Nudge.test.story.tsx +29 -12
  129. package/src/PhoneNumberInput/_stories/{PhoneNumberInput.test.story.tsx → PhoneNumberInput.rtl.test.story.tsx} +18 -10
  130. package/src/Popover/_stories/Popover.rtl.test.story.tsx +40 -50
  131. package/src/ProcessIndicator/ProcessIndicator.tsx +1 -1
  132. package/src/ProcessIndicator/_stories/ProcessIndicator.test.story.tsx +32 -0
  133. package/src/ProgressBar/_stories/ProgressBar.test.story.tsx +33 -19
  134. package/src/Prompt/CriticalBanner/CriticalBanner.css +34 -0
  135. package/src/Prompt/CriticalBanner/CriticalBanner.less +45 -0
  136. package/src/Prompt/CriticalBanner/CriticalBanner.test.tsx +26 -3
  137. package/src/Prompt/CriticalBanner/CriticalBanner.tsx +15 -1
  138. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.accessibility.docs.mdx +4 -1
  139. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.story.tsx +52 -0
  140. package/src/Prompt/CriticalBanner/_stories/CriticalBanner.test.story.tsx +22 -1
  141. package/src/SegmentedControl/SegmentedControl.css +2 -1
  142. package/src/SegmentedControl/SegmentedControl.less +4 -2
  143. package/src/SegmentedControl/SegmentedControl.tsx +8 -2
  144. package/src/SegmentedControl/_stories/SegmentedControl.test.story.tsx +16 -8
  145. package/src/SlidingPanel/SlidingPanel.css +8 -0
  146. package/src/SlidingPanel/SlidingPanel.less +8 -0
  147. package/src/SlidingPanel/_stories/SlidingPanel.rtl.test.story.tsx +4 -3
  148. package/src/Snackbar/_stories/Snackbar.rtl.test.story.tsx +14 -16
  149. package/src/Snackbar/_stories/Snackbar.story.tsx +1 -1
  150. package/src/Snackbar/_stories/Snackbar.test.story.tsx +1 -1
  151. package/src/StatusIcon/_stories/StatusIcon.test.story.tsx +12 -4
  152. package/src/Tooltip/Tooltip.css +0 -6
  153. package/src/Tooltip/Tooltip.less +0 -12
  154. package/src/Tooltip/Tooltip.test.tsx +51 -0
  155. package/src/Tooltip/Tooltip.tsx +11 -1
  156. package/src/Tooltip/_stories/Tooltip.rtl.test.story.tsx +16 -34
  157. package/src/UploadInput/UploadItem/UploadItem.css +4 -5
  158. package/src/UploadInput/UploadItem/UploadItem.less +7 -6
  159. package/src/UploadInput/_stories/UploadInput.test.story.tsx +1 -1
  160. package/src/_deprecated/Badge/Badge.css +4 -2
  161. package/src/_deprecated/Badge/Badge.less +7 -14
  162. package/src/_deprecated/CriticalBanner/_stories/CriticalCommsBanner.test.story.tsx +0 -16
  163. package/src/_deprecated/OverlayHeader/_stories/OverlayHeader.test.story.tsx +15 -0
  164. package/src/common/Panel/Panel.css +16 -8
  165. package/src/common/Panel/Panel.less +31 -13
  166. package/src/common/Panel/Panel.positionUtils.ts +79 -0
  167. package/src/common/Panel/Panel.test.tsx +40 -0
  168. package/src/common/Panel/Panel.tsx +13 -24
  169. package/src/common/Panel/_stories/Panel.rtl.test.story.tsx +110 -0
  170. package/src/common/PropsValues/position.ts +0 -50
  171. package/src/i18n/tr.json +5 -0
  172. package/src/i18n/zh-CN.json +5 -0
  173. package/src/main.css +134 -88
  174. package/src/styles/less/accordion.css +3 -0
  175. package/src/styles/less/buttons.css +1 -7
  176. package/src/styles/less/buttons.less +3 -11
  177. package/src/styles/less/chevron.css +3 -0
  178. package/src/styles/less/chevron.less +4 -0
  179. package/src/styles/less/neptune.css +25 -19
  180. package/src/styles/less/process.css +15 -9
  181. package/src/styles/less/process.less +13 -6
  182. package/src/styles/less/tick.css +6 -3
  183. package/src/styles/less/tick.less +7 -3
@@ -39,7 +39,7 @@
39
39
  .np-upload-input__title,
40
40
  .np-upload-input__text {
41
41
  margin: 0;
42
- text-align-last: left;
42
+ text-align: left;
43
43
  color: var(--color-content-secondary);
44
44
  }
45
45
 
@@ -64,7 +64,7 @@
64
64
  --buttonTopRightOffset: var(--size-16);
65
65
  // Offset towards the parent's top-right edge
66
66
  // ensuring no overflow on smaller screens
67
- --clickAreaTopRightOffset: calc((var(--clickAreaSize) - var(--iconSize)) * -.5);
67
+ --clickAreaTopRightOffset: calc((var(--clickAreaSize) - var(--iconSize)) * -0.5);
68
68
 
69
69
  position: absolute;
70
70
  right: var(--buttonTopRightOffset);
@@ -84,14 +84,16 @@
84
84
  border: 0;
85
85
  background-color: var(--color-background-neutral);
86
86
  color: var(--color-interactive-primary);
87
- transition: color, background-color 0.15s ease-in-out;
87
+ transition:
88
+ color,
89
+ background-color 0.15s ease-in-out;
88
90
  outline-offset: 0;
89
91
  display: flex;
90
92
  align-items: center;
91
93
  justify-content: center;
92
94
 
93
95
  &:before {
94
- content: '';
96
+ content: "";
95
97
  display: block;
96
98
  width: var(--clickAreaSize);
97
99
  height: var(--clickAreaSize);
@@ -116,7 +118,6 @@
116
118
  padding: 0;
117
119
 
118
120
  &:hover:not(:has(.np-upload-input__item-button:hover)) {
119
-
120
121
  &:before,
121
122
  & + .np-upload-input__item:before {
122
123
  width: 100%;
@@ -158,7 +159,7 @@
158
159
  padding-left: var(--size-16);
159
160
 
160
161
  &::before {
161
- content: '' / '';
162
+ content: "" / "";
162
163
  position: absolute;
163
164
  display: block;
164
165
  left: 0;
@@ -345,5 +345,5 @@ export const RTLDiff: Story = {
345
345
  />
346
346
  </>
347
347
  ),
348
- ...withRTLDiff(),
348
+ ...withRTLDiff({ renderAs: 'iframe' }),
349
349
  };
@@ -15,12 +15,14 @@
15
15
  --badge-border-color: rgba(0, 0, 0, 0.08);
16
16
  }
17
17
  .tw-badge > .tw-badge__children {
18
+ --badge-h-side: left;
18
19
  -webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
19
20
  mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
21
+ -webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) var(--badge-h-side) calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
22
+ mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) var(--badge-h-side) calc(100% - var(--badge-mask-offset) - var(--badge-content-position)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
20
23
  }
21
24
  [dir="rtl"] .tw-badge > .tw-badge__children {
22
- -webkit-mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
23
- mask-image: radial-gradient(circle at top calc(100% - var(--badge-mask-offset)) right calc(100% - var(--badge-mask-offset)), transparent 0, transparent calc(var(--badge-size) / 2 + var(--badge-mask)), black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px));
25
+ --badge-h-side: right;
24
26
  }
25
27
  .tw-badge > .tw-badge__content {
26
28
  position: absolute;
@@ -29,26 +29,19 @@
29
29
  }
30
30
 
31
31
  & > &__children {
32
- // Not possible to add a logical property (inset-inline-start) to replace left position inside a gradient.
33
- // RTL direction sorted out with conditional attributes.
32
+ --badge-h-side: left;
33
+
34
+ [dir="rtl"] & {
35
+ --badge-h-side: right;
36
+ }
34
37
 
35
38
  mask-image: radial-gradient(
36
- circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position)) left
37
- calc(100% - var(--badge-mask-offset) - var(--badge-content-position)),
39
+ circle at top calc(100% - var(--badge-mask-offset) - var(--badge-content-position))
40
+ var(--badge-h-side) calc(100% - var(--badge-mask-offset) - var(--badge-content-position)),
38
41
  transparent 0,
39
42
  transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
40
43
  black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
41
44
  );
42
-
43
- [dir="rtl"] & {
44
- mask-image: radial-gradient(
45
- circle at top calc(100% - var(--badge-mask-offset)) right
46
- calc(100% - var(--badge-mask-offset)),
47
- transparent 0,
48
- transparent calc(var(--badge-size) / 2 + var(--badge-mask)),
49
- black calc(var(--badge-size) / 2 + var(--badge-mask) + 0.5px)
50
- );
51
- }
52
45
  }
53
46
 
54
47
  & > &__content {
@@ -36,22 +36,6 @@ export const Sentiments: Story = {
36
36
  ]),
37
37
  };
38
38
 
39
- export const RTL: Story = {
40
- render: (args) => (
41
- <>
42
- <CriticalCommsBanner {...args} sentiment="negative" />
43
- <CriticalCommsBanner {...args} sentiment="warning" />
44
- <CriticalCommsBanner {...args} sentiment="neutral" />
45
- </>
46
- ),
47
- args: {
48
- subtitle: 'Add money within the next 30 days',
49
- action: { label: 'Take action', href: 'https://wise.com' },
50
- className: 'm-b-1',
51
- },
52
- ...withVariantConfig(['rtl']),
53
- };
54
-
55
39
  export const Mobile: Story = {
56
40
  args: {
57
41
  subtitle: 'Add money within the next 30 days',
@@ -40,3 +40,18 @@ export const RTLDiff: Story = {
40
40
  ),
41
41
  ...withRTLDiff(),
42
42
  };
43
+
44
+ export const RTLDiffMobile: Story = {
45
+ render: () => (
46
+ <OverlayHeader
47
+ logo={<Logo />}
48
+ avatar={
49
+ <AvatarView>
50
+ <Person size="24" />
51
+ </AvatarView>
52
+ }
53
+ onClose={() => {}}
54
+ />
55
+ ),
56
+ ...withRTLDiff({ viewport: 'largeMobile' }),
57
+ };
@@ -39,26 +39,34 @@
39
39
  .np-panel[data-popper-placement^="top"] > .np-panel__content {
40
40
  transform: translateY(10px) scale(0.95);
41
41
  }
42
- .np-panel[data-popper-placement^="top"] > .np-panel__content .np-panel__arrow {
43
- inset-block-end: -6px;
42
+ .np-panel[data-popper-placement^="top"] .np-panel__arrow {
43
+ inset-block-end: -7px;
44
44
  }
45
45
  .np-panel[data-popper-placement^="right"] > .np-panel__content {
46
46
  transform: translateX(-10px) scale(0.95);
47
47
  }
48
- .np-panel[data-popper-placement^="right"] > .np-panel__content .np-panel__arrow {
49
- inset-inline-start: -6px;
48
+ .np-panel[data-popper-placement^="right"] .np-panel__arrow {
49
+ inset-inline-start: -7px;
50
+ }
51
+ [dir="rtl"] .np-panel[data-popper-placement^="right"] .np-panel__arrow {
52
+ inset-inline-start: unset;
53
+ inset-inline-end: -7px;
50
54
  }
51
55
  .np-panel[data-popper-placement^="bottom"] > .np-panel__content {
52
56
  transform: translateY(-10px) scale(0.95);
53
57
  }
54
- .np-panel[data-popper-placement^="bottom"] > .np-panel__content .np-panel__arrow {
55
- inset-block-start: -6px;
58
+ .np-panel[data-popper-placement^="bottom"] .np-panel__arrow {
59
+ inset-block-start: -7px;
56
60
  }
57
61
  .np-panel[data-popper-placement^="left"] > .np-panel__content {
58
62
  transform: translateX(10px) scale(0.95);
59
63
  }
60
- .np-panel[data-popper-placement^="left"] > .np-panel__content .np-panel__arrow {
61
- inset-inline-end: -6px;
64
+ .np-panel[data-popper-placement^="left"] .np-panel__arrow {
65
+ inset-inline-end: -7px;
66
+ }
67
+ [dir="rtl"] .np-panel[data-popper-placement^="left"] .np-panel__arrow {
68
+ inset-inline-end: unset;
69
+ inset-inline-start: -7px;
62
70
  }
63
71
  .np-theme-personal .np-panel__content {
64
72
  border-radius: 16px;
@@ -11,7 +11,7 @@
11
11
  opacity 0.2s cubic-bezier(0.6, 0.2, 0.1, 1),
12
12
  transform 0.2s cubic-bezier(0.6, 0.2, 0.1, 1),
13
13
  visibility 0s linear 0.2s;
14
-
14
+
15
15
  .np-theme-personal & {
16
16
  border-radius: var(--radius-small);
17
17
  }
@@ -45,35 +45,53 @@
45
45
  background: var(--color-background-elevated);
46
46
  }
47
47
 
48
- &[data-popper-placement^="top"] > &__content {
49
- transform: translateY(10px) scale(0.95);
48
+ &[data-popper-placement^="top"] {
49
+ > .np-panel__content {
50
+ transform: translateY(10px) scale(0.95);
51
+ }
50
52
 
51
53
  .np-panel__arrow {
52
- bottom: -6px;
54
+ bottom: -7px;
53
55
  }
54
56
  }
55
57
 
56
- &[data-popper-placement^="right"] > &__content {
57
- transform: translateX(-10px) scale(0.95);
58
+ &[data-popper-placement^="right"] {
59
+ > .np-panel__content {
60
+ transform: translateX(-10px) scale(0.95);
61
+ }
58
62
 
59
63
  .np-panel__arrow {
60
- left: -6px;
64
+ left: -7px;
65
+
66
+ [dir="rtl"] & {
67
+ left: unset;
68
+ right: -7px;
69
+ }
61
70
  }
62
71
  }
63
72
 
64
- &[data-popper-placement^="bottom"] > &__content {
65
- transform: translateY(-10px) scale(0.95);
73
+ &[data-popper-placement^="bottom"] {
74
+ > .np-panel__content {
75
+ transform: translateY(-10px) scale(0.95);
76
+ }
66
77
 
67
78
  .np-panel__arrow {
68
- top: -6px;
79
+ top: -7px;
69
80
  }
70
81
  }
71
82
 
72
- &[data-popper-placement^="left"] > &__content {
73
- transform: translateX(10px) scale(0.95);
83
+ &[data-popper-placement^="left"] {
84
+ > .np-panel__content {
85
+ transform: translateX(10px) scale(0.95);
86
+ }
74
87
 
75
88
  .np-panel__arrow {
76
- right: -6px;
89
+ right: -7px;
90
+
91
+ [dir="rtl"] & {
92
+ right: unset;
93
+ left: -7px;
94
+ }
77
95
  }
78
96
  }
79
97
  }
@@ -0,0 +1,79 @@
1
+ import type { Placement } from '@floating-ui/react';
2
+
3
+ export type FloatingPlacement =
4
+ | 'top'
5
+ | 'right'
6
+ | 'bottom'
7
+ | 'left'
8
+ | 'top-left'
9
+ | 'top-right'
10
+ | 'right-top'
11
+ | 'right-bottom'
12
+ | 'bottom-left'
13
+ | 'bottom-right'
14
+ | 'left-top'
15
+ | 'left-bottom';
16
+
17
+ export const POPOVER_OFFSET = 16;
18
+
19
+ // By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
20
+ // the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
21
+ // This mapping forces popover to try the four available positions before going back to the initial chosen one.
22
+ export const fallbackPlacements: Record<Placement, Placement[]> = {
23
+ top: ['bottom', 'right', 'left'],
24
+ bottom: ['top', 'right', 'left'],
25
+ left: ['right', 'top', 'bottom'],
26
+ right: ['left', 'top', 'bottom'],
27
+ 'top-start': ['bottom-start', 'top-end', 'bottom-end'],
28
+ 'top-end': ['bottom-end', 'top-start', 'bottom-start'],
29
+ 'bottom-start': ['top-start', 'bottom-end', 'top-end'],
30
+ 'bottom-end': ['top-end', 'bottom-start', 'top-start'],
31
+ 'left-start': ['right-start', 'left-end', 'right-end'],
32
+ 'left-end': ['right-end', 'left-start', 'right-start'],
33
+ 'right-start': ['left-start', 'right-end', 'left-end'],
34
+ 'right-end': ['left-end', 'right-start', 'left-start'],
35
+ };
36
+
37
+ export const positionToPlacement = (position: FloatingPlacement): Placement => {
38
+ switch (position) {
39
+ case 'top-left':
40
+ return 'top-start';
41
+ case 'top-right':
42
+ return 'top-end';
43
+ case 'bottom-left':
44
+ return 'bottom-start';
45
+ case 'bottom-right':
46
+ return 'bottom-end';
47
+ case 'left-top':
48
+ return 'left-start';
49
+ case 'left-bottom':
50
+ return 'left-end';
51
+ case 'right-top':
52
+ return 'right-start';
53
+ case 'right-bottom':
54
+ return 'right-end';
55
+ default:
56
+ return position;
57
+ }
58
+ };
59
+
60
+ export const mirrorPositionForRtl = (position: FloatingPlacement): FloatingPlacement => {
61
+ switch (position) {
62
+ case 'left':
63
+ return 'right';
64
+ case 'right':
65
+ return 'left';
66
+ case 'left-top':
67
+ return 'right-top';
68
+ case 'left-bottom':
69
+ return 'right-bottom';
70
+ case 'right-top':
71
+ return 'left-top';
72
+ case 'right-bottom':
73
+ return 'left-bottom';
74
+ default:
75
+ // top-left/top-right/bottom-left/bottom-right map to start/end via
76
+ // positionToPlacement, and floating-ui already flips start/end in RTL.
77
+ return position;
78
+ }
79
+ };
@@ -1,6 +1,7 @@
1
1
  import { createRef } from 'react';
2
2
 
3
3
  import { Position } from '..';
4
+ import { DirectionProvider } from '../../Provider/Direction/DirectionProvider';
4
5
  import { render } from '../../test-utils';
5
6
 
6
7
  import Panel from './Panel';
@@ -93,4 +94,43 @@ describe('Panel', () => {
93
94
  expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
94
95
  });
95
96
  });
97
+
98
+ describe('RTL position mirroring', () => {
99
+ it.each<[PanelProps['position'], string]>([
100
+ [Position.LEFT, 'right'],
101
+ [Position.RIGHT, 'left'],
102
+ [Position.LEFT_TOP, 'right-start'],
103
+ [Position.LEFT_BOTTOM, 'right-end'],
104
+ [Position.RIGHT_TOP, 'left-start'],
105
+ [Position.RIGHT_BOTTOM, 'left-end'],
106
+ ])('mirrors %s in RTL to floating-ui placement %s', (position, expectedPlacement) => {
107
+ render(
108
+ <DirectionProvider direction="rtl">
109
+ <Panel {...props} position={position} />
110
+ </DirectionProvider>,
111
+ );
112
+
113
+ expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
114
+ });
115
+
116
+ it.each<[PanelProps['position'], string]>([
117
+ [Position.TOP, 'top'],
118
+ [Position.BOTTOM, 'bottom'],
119
+ [Position.TOP_LEFT, 'top-start'],
120
+ [Position.TOP_RIGHT, 'top-end'],
121
+ [Position.BOTTOM_LEFT, 'bottom-start'],
122
+ [Position.BOTTOM_RIGHT, 'bottom-end'],
123
+ ])(
124
+ 'does not mirror %s in RTL (floating-ui handles start/end flip)',
125
+ (position, expectedPlacement) => {
126
+ render(
127
+ <DirectionProvider direction="rtl">
128
+ <Panel {...props} position={position} />
129
+ </DirectionProvider>,
130
+ );
131
+
132
+ expect(getPanel()).toHaveAttribute('data-popper-placement', expectedPlacement);
133
+ },
134
+ );
135
+ });
96
136
  });
@@ -3,7 +3,6 @@ import {
3
3
  autoUpdate,
4
4
  flip as flipMiddleware,
5
5
  offset,
6
- type Placement,
7
6
  shift,
8
7
  size as sizeMiddleware,
9
8
  useFloating,
@@ -20,29 +19,17 @@ import {
20
19
  useRef,
21
20
  } from 'react';
22
21
 
23
- import { Position, type FloatingPlacement, positionToPlacement } from '..';
24
- import Dimmer from '../../Dimmer';
25
22
  import { OverlayIdContext } from '../../Provider/overlay/OverlayIdProvider';
26
-
27
- const POPOVER_OFFSET = 16;
28
-
29
- // By default the flip positioning explores only the opposite alternative. So if left is passed and there's no enough space
30
- // the right one gets chosen. If there's no space on both sides popover goes back to the initially chosen one left.
31
- // This mapping forces popover to try the four available positions before going back to the initial chosen one.
32
- const fallbackPlacements: Record<Placement, Placement[]> = {
33
- [Position.TOP]: [Position.BOTTOM, Position.RIGHT, Position.LEFT],
34
- [Position.BOTTOM]: [Position.TOP, Position.RIGHT, Position.LEFT],
35
- [Position.LEFT]: [Position.RIGHT, Position.TOP, Position.BOTTOM],
36
- [Position.RIGHT]: [Position.LEFT, Position.TOP, Position.BOTTOM],
37
- 'top-start': ['bottom-start', 'top-end', 'bottom-end'],
38
- 'top-end': ['bottom-end', 'top-start', 'bottom-start'],
39
- 'bottom-start': ['top-start', 'bottom-end', 'top-end'],
40
- 'bottom-end': ['top-end', 'bottom-start', 'top-start'],
41
- 'left-start': ['right-start', 'left-end', 'right-end'],
42
- 'left-end': ['right-end', 'left-start', 'right-start'],
43
- 'right-start': ['left-start', 'right-end', 'left-end'],
44
- 'right-end': ['left-end', 'right-start', 'left-start'],
45
- };
23
+ import Dimmer from '../../Dimmer';
24
+ import useDirection from '../hooks/useDirection';
25
+ import { Position } from '..';
26
+ import {
27
+ type FloatingPlacement,
28
+ POPOVER_OFFSET,
29
+ fallbackPlacements,
30
+ mirrorPositionForRtl,
31
+ positionToPlacement,
32
+ } from './Panel.positionUtils';
46
33
 
47
34
  export type PanelProps = PropsWithChildren<{
48
35
  arrow?: boolean;
@@ -74,7 +61,9 @@ const Panel = forwardRef<HTMLDivElement, PanelProps>(function Panel(
74
61
  reference,
75
62
  ) {
76
63
  const arrowRef = useRef<HTMLDivElement | null>(null);
77
- const floatingPlacement = positionToPlacement(position);
64
+ const { isRTL } = useDirection();
65
+ const resolvedPosition = isRTL ? mirrorPositionForRtl(position) : position;
66
+ const floatingPlacement = positionToPlacement(resolvedPosition);
78
67
 
79
68
  const middleware = [];
80
69
 
@@ -0,0 +1,110 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { useRef } from 'react';
3
+ import { withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
4
+ import Button from '../../../Button';
5
+ import { Position } from '../../PropsValues/position';
6
+ import Panel from '../Panel';
7
+
8
+ export default {
9
+ component: Panel,
10
+ title: 'Dialogs/Panel/Tests/RTL Diff',
11
+ tags: ['!autodocs', '!manifest'],
12
+ } satisfies Meta<typeof Panel>;
13
+
14
+ type Story = StoryObj<typeof Panel>;
15
+
16
+ function PanelStory({
17
+ position,
18
+ label,
19
+ }: {
20
+ position: Parameters<typeof Panel>[0]['position'];
21
+ label: string;
22
+ }) {
23
+ const anchorRef = useRef<HTMLButtonElement>(null);
24
+ const margin =
25
+ position?.startsWith('bottom') || position?.startsWith('top') ? '3.5rem 0' : '2.5rem 0';
26
+
27
+ return (
28
+ <>
29
+ <Button ref={anchorRef} style={{ margin }}>
30
+ {label}
31
+ </Button>
32
+ <Panel
33
+ open
34
+ position={position}
35
+ anchorRef={anchorRef}
36
+ arrow
37
+ aria-label={`Panel positioned ${label}`}
38
+ >
39
+ <div style={{ padding: '16px', maxWidth: '200px' }}>
40
+ Panel content positioned at <strong>{label}</strong>
41
+ </div>
42
+ </Panel>
43
+ </>
44
+ );
45
+ }
46
+
47
+ export const Top: Story = {
48
+ render: () => (
49
+ <>
50
+ <PanelStory position={Position.TOP_LEFT} label="top-left" />
51
+ <PanelStory position={Position.TOP} label="top" />
52
+ <PanelStory position={Position.TOP_RIGHT} label="top-right" />
53
+ </>
54
+ ),
55
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'center', vAlign: 'bottom' }),
56
+ };
57
+
58
+ export const Right: Story = {
59
+ render: () => (
60
+ <>
61
+ <PanelStory position={Position.RIGHT_TOP} label="right-top" />
62
+ <PanelStory position={Position.RIGHT} label="right" />
63
+ <PanelStory position={Position.RIGHT_BOTTOM} label="right-bottom" />
64
+ </>
65
+ ),
66
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'left', vAlign: 'center' }),
67
+ };
68
+ export const RightFlipped: Story = {
69
+ render: () => (
70
+ <>
71
+ <PanelStory position={Position.RIGHT_TOP} label="right-top" />
72
+ <PanelStory position={Position.RIGHT} label="right" />
73
+ <PanelStory position={Position.RIGHT_BOTTOM} label="right-bottom" />
74
+ </>
75
+ ),
76
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'right', vAlign: 'center' }),
77
+ };
78
+
79
+ export const Bottom: Story = {
80
+ render: () => (
81
+ <>
82
+ <PanelStory position={Position.BOTTOM_LEFT} label="bottom-left" />
83
+ <PanelStory position={Position.BOTTOM} label="bottom" />
84
+ <PanelStory position={Position.BOTTOM_RIGHT} label="bottom-right" />
85
+ </>
86
+ ),
87
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'center' }),
88
+ };
89
+
90
+ export const Left: Story = {
91
+ render: () => (
92
+ <>
93
+ <PanelStory position={Position.LEFT_TOP} label="left-top" />
94
+ <PanelStory position={Position.LEFT} label="left" />
95
+ <PanelStory position={Position.LEFT_BOTTOM} label="left-bottom" />
96
+ </>
97
+ ),
98
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'right', vAlign: 'center' }),
99
+ };
100
+
101
+ export const LeftFlipped: Story = {
102
+ render: () => (
103
+ <>
104
+ <PanelStory position={Position.LEFT_TOP} label="left-top" />
105
+ <PanelStory position={Position.LEFT} label="left" />
106
+ <PanelStory position={Position.LEFT_BOTTOM} label="left-bottom" />
107
+ </>
108
+ ),
109
+ ...withRTLDiff({ renderAs: 'iframe', hAlign: 'left', vAlign: 'center' }),
110
+ };
@@ -1,5 +1,3 @@
1
- import type { Placement } from '@floating-ui/react';
2
-
3
1
  export enum Position {
4
2
  BOTTOM = 'bottom',
5
3
  BOTTOM_LEFT = 'bottom-left',
@@ -21,51 +19,3 @@ export type PositionTop = 'top';
21
19
  export type PositionRight = 'right';
22
20
  export type PositionBottom = 'bottom';
23
21
  export type PositionLeft = 'left';
24
-
25
- export type PositionTopLeft = 'top-left';
26
- export type PositionTopRight = 'top-right';
27
- export type PositionBottomLeft = 'bottom-left';
28
- export type PositionBottomRight = 'bottom-right';
29
- export type PositionLeftTop = 'left-top';
30
- export type PositionLeftBottom = 'left-bottom';
31
- export type PositionRightTop = 'right-top';
32
- export type PositionRightBottom = 'right-bottom';
33
-
34
- /** Neptune position values that can be converted to floating-ui Placement */
35
- export type FloatingPlacement =
36
- | PositionTop
37
- | PositionRight
38
- | PositionBottom
39
- | PositionLeft
40
- | PositionTopLeft
41
- | PositionTopRight
42
- | PositionRightTop
43
- | PositionRightBottom
44
- | PositionBottomLeft
45
- | PositionBottomRight
46
- | PositionLeftTop
47
- | PositionLeftBottom;
48
-
49
- /** Maps Neptune position values to floating-ui Placement */
50
- export const positionToPlacement = (position: FloatingPlacement): Placement => {
51
- switch (position) {
52
- case 'top-left':
53
- return 'top-start';
54
- case 'top-right':
55
- return 'top-end';
56
- case 'bottom-left':
57
- return 'bottom-start';
58
- case 'bottom-right':
59
- return 'bottom-end';
60
- case 'left-top':
61
- return 'left-start';
62
- case 'left-bottom':
63
- return 'left-end';
64
- case 'right-top':
65
- return 'right-start';
66
- case 'right-bottom':
67
- return 'right-end';
68
- default:
69
- return position;
70
- }
71
- };
package/src/i18n/tr.json CHANGED
@@ -12,6 +12,10 @@
12
12
  "neptune.DateLookup.goTo20YearView": "20 yıl görünümüne git",
13
13
  "neptune.DateLookup.month": "ay",
14
14
  "neptune.DateLookup.next": "devam et",
15
+ "neptune.DateLookup.notAvailable": "kullanılamaz",
16
+ "neptune.DateLookup.outOfRangeAfter": "Lütfen {min} veya daha sonraki bir tarih girin.",
17
+ "neptune.DateLookup.outOfRangeBefore": "Lütfen {max} veya daha önceki bir tarih girin.",
18
+ "neptune.DateLookup.outOfRangeBetween": "Lütfen {min} ile {max} arasında bir tarih girin.",
15
19
  "neptune.DateLookup.previous": "önceki",
16
20
  "neptune.DateLookup.selected": "seçili",
17
21
  "neptune.DateLookup.twentyYears": "20 yıl",
@@ -46,6 +50,7 @@
46
50
  "neptune.Table.loaded": "Tablo verileri yüklendi",
47
51
  "neptune.Table.loading": "Tablo verileri yükleniyor",
48
52
  "neptune.Table.refreshPage": "Sayfayı yenile",
53
+ "neptune.Typeahead.suggestionsLabel": "Öneriler",
49
54
  "neptune.Upload.csButtonText": "Başka bir dosya yükleyin?",
50
55
  "neptune.Upload.csFailureText": "Yükleme başarısız. Lütfen yine deneyin",
51
56
  "neptune.Upload.csSuccessText": "Yükleme tamamlandı!",
@@ -12,6 +12,10 @@
12
12
  "neptune.DateLookup.goTo20YearView": "转到 20 年视图",
13
13
  "neptune.DateLookup.month": "月",
14
14
  "neptune.DateLookup.next": "下一页",
15
+ "neptune.DateLookup.notAvailable": "不可用",
16
+ "neptune.DateLookup.outOfRangeAfter": "请输入 {min}或之后的日期。",
17
+ "neptune.DateLookup.outOfRangeBefore": "请输入 {max}或之前的日期。",
18
+ "neptune.DateLookup.outOfRangeBetween": "请输入 {min}与 {max}之间的日期。",
15
19
  "neptune.DateLookup.previous": "上一页",
16
20
  "neptune.DateLookup.selected": "已选",
17
21
  "neptune.DateLookup.twentyYears": "20 年",
@@ -46,6 +50,7 @@
46
50
  "neptune.Table.loaded": "表数据已加载",
47
51
  "neptune.Table.loading": "表数据正在加载",
48
52
  "neptune.Table.refreshPage": "刷新页面",
53
+ "neptune.Typeahead.suggestionsLabel": "建议",
49
54
  "neptune.Upload.csButtonText": "上传另一个文件?",
50
55
  "neptune.Upload.csFailureText": "上传失败。请重试",
51
56
  "neptune.Upload.csSuccessText": "上传完毕!",