@korsolutions/ui 0.0.59 → 0.0.60

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 (61) hide show
  1. package/dist/module/components/menu/components/menu-checkbox-item.js +4 -4
  2. package/dist/module/components/menu/components/menu-checkbox-item.js.map +1 -1
  3. package/dist/module/components/menu/components/menu-content.js +1 -1
  4. package/dist/module/components/menu/components/menu-radio-item.js +4 -4
  5. package/dist/module/components/menu/components/menu-radio-item.js.map +1 -1
  6. package/dist/module/components/menu/components/menu-selection-indicator.js +29 -0
  7. package/dist/module/components/menu/components/menu-selection-indicator.js.map +1 -0
  8. package/dist/module/components/menu/variants/default.js +8 -17
  9. package/dist/module/components/menu/variants/default.js.map +1 -1
  10. package/dist/module/components/phone-input/components/country-picker.js +1 -1
  11. package/dist/module/components/popover/components/popover-content.js +2 -5
  12. package/dist/module/components/popover/components/popover-content.js.map +1 -1
  13. package/dist/module/components/portal/index.js +1 -0
  14. package/dist/module/components/portal/index.js.map +1 -1
  15. package/dist/module/components/portal/portal-offset.js +32 -0
  16. package/dist/module/components/portal/portal-offset.js.map +1 -0
  17. package/dist/module/components/portal/portal.js +39 -17
  18. package/dist/module/components/portal/portal.js.map +1 -1
  19. package/dist/module/components/select/components/select-content.js +3 -3
  20. package/dist/module/components/select/components/select-content.js.map +1 -1
  21. package/dist/module/components/textarea/variants/default.js +7 -0
  22. package/dist/module/components/textarea/variants/default.js.map +1 -1
  23. package/dist/module/hooks/use-relative-position.js +37 -28
  24. package/dist/module/hooks/use-relative-position.js.map +1 -1
  25. package/dist/module/themes/provider.js.map +1 -1
  26. package/dist/typescript/src/components/menu/components/menu-checkbox-item.d.ts.map +1 -1
  27. package/dist/typescript/src/components/menu/components/menu-radio-item.d.ts.map +1 -1
  28. package/dist/typescript/src/components/menu/components/menu-selection-indicator.d.ts +7 -0
  29. package/dist/typescript/src/components/menu/components/menu-selection-indicator.d.ts.map +1 -0
  30. package/dist/typescript/src/components/menu/types.d.ts +1 -3
  31. package/dist/typescript/src/components/menu/types.d.ts.map +1 -1
  32. package/dist/typescript/src/components/menu/variants/default.d.ts.map +1 -1
  33. package/dist/typescript/src/components/popover/components/popover-content.d.ts.map +1 -1
  34. package/dist/typescript/src/components/portal/index.d.ts +1 -0
  35. package/dist/typescript/src/components/portal/index.d.ts.map +1 -1
  36. package/dist/typescript/src/components/portal/portal-offset.d.ts +13 -0
  37. package/dist/typescript/src/components/portal/portal-offset.d.ts.map +1 -0
  38. package/dist/typescript/src/components/portal/portal.d.ts +3 -2
  39. package/dist/typescript/src/components/portal/portal.d.ts.map +1 -1
  40. package/dist/typescript/src/components/select/components/select-content.d.ts.map +1 -1
  41. package/dist/typescript/src/components/textarea/variants/default.d.ts.map +1 -1
  42. package/dist/typescript/src/hooks/use-relative-position.d.ts +4 -7
  43. package/dist/typescript/src/hooks/use-relative-position.d.ts.map +1 -1
  44. package/dist/typescript/src/themes/provider.d.ts +3 -0
  45. package/dist/typescript/src/themes/provider.d.ts.map +1 -1
  46. package/package.json +1 -1
  47. package/src/components/menu/components/menu-checkbox-item.tsx +3 -4
  48. package/src/components/menu/components/menu-content.tsx +1 -1
  49. package/src/components/menu/components/menu-radio-item.tsx +3 -7
  50. package/src/components/menu/components/menu-selection-indicator.tsx +26 -0
  51. package/src/components/menu/types.ts +1 -6
  52. package/src/components/menu/variants/default.tsx +7 -16
  53. package/src/components/phone-input/components/country-picker.tsx +1 -1
  54. package/src/components/popover/components/popover-content.tsx +1 -4
  55. package/src/components/portal/index.ts +1 -0
  56. package/src/components/portal/portal-offset.ts +28 -0
  57. package/src/components/portal/portal.tsx +54 -22
  58. package/src/components/select/components/select-content.tsx +14 -5
  59. package/src/components/textarea/variants/default.tsx +7 -0
  60. package/src/hooks/use-relative-position.ts +53 -41
  61. package/src/themes/provider.tsx +3 -0
@@ -5,29 +5,35 @@ import {
5
5
  type LayoutRectangle,
6
6
  type ViewStyle,
7
7
  } from "react-native";
8
- import type { SafeAreaInsets } from "../safe-area";
8
+ import { usePortalOffset } from "../components/portal";
9
+ import { useSafeAreaInsets, type SafeAreaInsets } from "../safe-area";
9
10
 
10
11
  type UseRelativePositionArgs = Omit<
11
12
  GetContentStyleArgs,
12
- "triggerPosition" | "contentLayout" | "dimensions"
13
- > & {
14
- triggerPosition: LayoutPosition | null;
15
- contentLayout: LayoutRectangle | null;
16
- };
13
+ "dimensions" | "insets"
14
+ >;
17
15
 
18
16
  export function useRelativePosition({
19
17
  align,
20
18
  triggerPosition,
21
19
  contentLayout,
22
20
  alignOffset,
23
- insets,
24
21
  sideOffset,
25
22
  preferredSide,
26
23
  }: UseRelativePositionArgs): ViewStyle {
27
24
  const dimensions = useWindowDimensions();
25
+ const insets = useSafeAreaInsets();
26
+ const portalOffset = usePortalOffset();
28
27
 
29
28
  return useMemo(() => {
30
- if (!triggerPosition || !contentLayout) {
29
+ const hasLayout =
30
+ triggerPosition.width > 0 &&
31
+ triggerPosition.height > 0 &&
32
+ contentLayout.width > 0 &&
33
+ contentLayout.height > 0 &&
34
+ portalOffset.isLoaded;
35
+
36
+ if (!hasLayout) {
31
37
  return {
32
38
  position: "absolute",
33
39
  opacity: 0,
@@ -36,11 +42,22 @@ export function useRelativePosition({
36
42
  };
37
43
  }
38
44
 
45
+ // Adjust trigger position to account for the portal container's
46
+ // offset from the window origin. measureInWindow returns window-relative
47
+ // coordinates, but the portal content is positioned relative to its
48
+ // container which may not be at the window origin (e.g. on Android
49
+ // where the status bar offsets the view hierarchy).
50
+ const adjustedTriggerPosition: LayoutPosition = {
51
+ ...triggerPosition,
52
+ pageX: triggerPosition.pageX - portalOffset.value.x,
53
+ pageY: triggerPosition.pageY - portalOffset.value.y,
54
+ };
55
+
39
56
  const style = getContentStyle({
40
57
  align,
41
58
  contentLayout,
42
59
  preferredSide,
43
- triggerPosition,
60
+ triggerPosition: adjustedTriggerPosition,
44
61
  alignOffset,
45
62
  insets,
46
63
  sideOffset,
@@ -58,6 +75,7 @@ export function useRelativePosition({
58
75
  dimensions.width,
59
76
  dimensions.height,
60
77
  sideOffset,
78
+ portalOffset,
61
79
  ]);
62
80
  }
63
81
 
@@ -72,7 +90,7 @@ interface GetPositionArgs {
72
90
  dimensions: DisplayMetrics;
73
91
  triggerPosition: LayoutPosition;
74
92
  contentLayout: LayoutRectangle;
75
- insets?: SafeAreaInsets;
93
+ insets: SafeAreaInsets;
76
94
  }
77
95
 
78
96
  interface GetSidePositionArgs extends GetPositionArgs {
@@ -95,8 +113,7 @@ export const DEFAULT_POSITION: LayoutPosition = {
95
113
 
96
114
  interface GetSideArgs {
97
115
  preferredSide: "top" | "bottom";
98
- insetTop: number;
99
- insetBottom: number;
116
+ insets: SafeAreaInsets;
100
117
  positionTop: number;
101
118
  positionBottom: number;
102
119
  contentLayout: LayoutRectangle;
@@ -105,29 +122,28 @@ interface GetSideArgs {
105
122
 
106
123
  function getSide({
107
124
  preferredSide,
108
- insetTop,
109
- insetBottom,
125
+ insets,
110
126
  positionTop,
111
127
  positionBottom,
112
128
  contentLayout,
113
129
  dimensions,
114
130
  }: GetSideArgs) {
115
131
  if (preferredSide === "bottom") {
116
- const spaceBelow = dimensions.height - insetBottom - positionBottom;
132
+ const spaceBelow = dimensions.height - insets.bottom - positionBottom;
117
133
  if (spaceBelow >= contentLayout.height) {
118
134
  return "bottom";
119
135
  }
120
- const spaceAbove = positionTop - insetTop;
136
+ const spaceAbove = positionTop - insets.top;
121
137
  if (spaceAbove > spaceBelow) {
122
138
  return "top";
123
139
  }
124
140
  return "bottom";
125
141
  }
126
- const spaceAbove = positionTop - insetTop;
142
+ const spaceAbove = positionTop - insets.top;
127
143
  if (spaceAbove >= contentLayout.height) {
128
144
  return "top";
129
145
  }
130
- const spaceBelow = dimensions.height - insetBottom - positionBottom;
146
+ const spaceBelow = dimensions.height - insets.bottom - positionBottom;
131
147
  if (spaceBelow > spaceAbove) {
132
148
  return "bottom";
133
149
  }
@@ -142,8 +158,6 @@ function getSidePosition({
142
158
  insets,
143
159
  dimensions,
144
160
  }: GetSidePositionArgs) {
145
- const insetTop = insets?.top ?? 0;
146
- const insetBottom = insets?.bottom ?? 0;
147
161
  const positionTop =
148
162
  triggerPosition?.pageY - sideOffset - contentLayout.height;
149
163
  const positionBottom =
@@ -151,8 +165,7 @@ function getSidePosition({
151
165
 
152
166
  const side = getSide({
153
167
  preferredSide,
154
- insetTop,
155
- insetBottom,
168
+ insets,
156
169
  positionTop,
157
170
  positionBottom,
158
171
  contentLayout,
@@ -162,15 +175,15 @@ function getSidePosition({
162
175
  if (side === "top") {
163
176
  return {
164
177
  top: Math.min(
165
- Math.max(insetTop, positionTop),
166
- dimensions.height - insetBottom - contentLayout.height,
178
+ Math.max(insets.top, positionTop),
179
+ dimensions.height - insets.bottom - contentLayout.height,
167
180
  ),
168
181
  };
169
182
  }
170
183
 
171
184
  return {
172
185
  top: Math.min(
173
- dimensions.height - insetBottom - contentLayout.height,
186
+ dimensions.height - insets.bottom - contentLayout.height,
174
187
  positionBottom,
175
188
  ),
176
189
  };
@@ -189,9 +202,7 @@ function getAlignPosition({
189
202
  insets,
190
203
  dimensions,
191
204
  }: GetAlignPositionArgs) {
192
- const insetLeft = insets?.left ?? 0;
193
- const insetRight = insets?.right ?? 0;
194
- const maxContentWidth = dimensions.width - insetLeft - insetRight;
205
+ const maxContentWidth = dimensions.width - insets.left - insets.right;
195
206
 
196
207
  const contentWidth = Math.min(contentLayout.width, maxContentWidth);
197
208
 
@@ -201,25 +212,24 @@ function getAlignPosition({
201
212
  triggerPosition.width,
202
213
  contentWidth,
203
214
  alignOffset,
204
- insetLeft,
205
- insetRight,
215
+ insets,
206
216
  dimensions,
207
217
  );
208
218
 
209
219
  const doesCollide =
210
- left < insetLeft || left + contentWidth > dimensions.width - insetRight;
220
+ left < insets.left || left + contentWidth > dimensions.width - insets.right;
211
221
  if (doesCollide) {
212
- const spaceLeft = left - insetLeft;
213
- const spaceRight = dimensions.width - insetRight - (left + contentWidth);
222
+ const spaceLeft = left - insets.left;
223
+ const spaceRight = dimensions.width - insets.right - (left + contentWidth);
214
224
 
215
225
  if (spaceLeft > spaceRight && spaceLeft >= contentWidth) {
216
- left = insetLeft;
226
+ left = insets.left;
217
227
  } else if (spaceRight >= contentWidth) {
218
- left = dimensions.width - insetRight - contentWidth;
228
+ left = dimensions.width - insets.right - contentWidth;
219
229
  } else {
220
230
  const centeredPosition = Math.max(
221
- insetLeft,
222
- (dimensions.width - contentWidth - insetRight) / 2,
231
+ insets.left,
232
+ (dimensions.width - contentWidth - insets.right) / 2,
223
233
  );
224
234
  left = centeredPosition;
225
235
  }
@@ -234,8 +244,7 @@ function getLeftPosition(
234
244
  triggerWidth: number,
235
245
  contentWidth: number,
236
246
  alignOffset: number,
237
- insetLeft: number,
238
- insetRight: number,
247
+ insets: SafeAreaInsets,
239
248
  dimensions: DisplayMetrics,
240
249
  ) {
241
250
  let left = 0;
@@ -249,8 +258,11 @@ function getLeftPosition(
249
258
  left = triggerPageX + triggerWidth - contentWidth;
250
259
  }
251
260
  return Math.max(
252
- insetLeft,
253
- Math.min(left + alignOffset, dimensions.width - contentWidth - insetRight),
261
+ insets.left,
262
+ Math.min(
263
+ left + alignOffset,
264
+ dimensions.width - contentWidth - insets.right,
265
+ ),
254
266
  );
255
267
  }
256
268
 
@@ -36,6 +36,9 @@ export interface ComponentsConfig {
36
36
  toast?: {
37
37
  icons?: Partial<Record<keyof typeof ToastVariants, IconComponent>>;
38
38
  };
39
+ menu?: {
40
+ selectionIcon?: IconComponent;
41
+ };
39
42
  }
40
43
 
41
44
  const ThemeContext = createContext<ThemeContext | null>(null);