@nativetail/ui 0.2.4 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nativetail/ui",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {},
@@ -2,7 +2,7 @@ import { cn, Text, useTw, View } from "@nativetail/core";
2
2
  import { Pressable } from "react-native";
3
3
  import { Button, ButtonProps } from "../button";
4
4
 
5
- type CardProps = {
5
+ export type CardProps = {
6
6
  renderHeader?: () => React.ReactNode;
7
7
  title?: string;
8
8
  subtitle?: string;
@@ -7,7 +7,7 @@ import {
7
7
  ActionSheetRef,
8
8
  } from "../actions-sheet";
9
9
 
10
- type InfoCardProps = {
10
+ export type InfoCardProps = {
11
11
  containerClassname?: string;
12
12
  renderIcon?: () => React.ReactNode;
13
13
  renderContent?: () => React.ReactNode;
@@ -1,6 +1,6 @@
1
1
  import { cn, View } from "@nativetail/core";
2
2
 
3
- type StatCardProps = {
3
+ export type StatCardProps = {
4
4
  renderIcon?: () => React.ReactNode;
5
5
  renderTitle?: () => React.ReactNode;
6
6
  renderValue?: () => React.ReactNode;
@@ -37,7 +37,7 @@ type DropdownState = {
37
37
  toggle: () => void;
38
38
  close: () => void;
39
39
  open: () => void;
40
- position: PositionType | null;
40
+ position: React.MutableRefObject<PositionType | null>;
41
41
  setPosition: (position: PositionType | null) => void;
42
42
  };
43
43
  const DropdownContext = React.createContext<DropdownState | null>(null);
@@ -57,7 +57,7 @@ const DropdownRoot = ({
57
57
  children: ReactNode;
58
58
  }) => {
59
59
  const [open, setOpen] = useState(false);
60
- const [position, setPosition] = useState<PositionType | null>(null);
60
+ const position = useRef<PositionType | null>(null);
61
61
  return (
62
62
  <DropdownContext.Provider
63
63
  value={{
@@ -66,7 +66,9 @@ const DropdownRoot = ({
66
66
  close: () => setOpen(false),
67
67
  open: () => setOpen(true),
68
68
  position,
69
- setPosition,
69
+ setPosition: (pos) => {
70
+ position.current = pos;
71
+ },
70
72
  }}
71
73
  >
72
74
  <View className={className} animated>
@@ -118,6 +120,11 @@ const DropdownTrigger = ({
118
120
  }
119
121
  toggle();
120
122
  }, [toggle, _measure]);
123
+
124
+ useEffect(() => {
125
+ _measure();
126
+ }, [_measure]);
127
+
121
128
  return (
122
129
  <Pressable
123
130
  className={className}
@@ -141,28 +148,57 @@ const DropdownMenu = ({
141
148
  useBlur?: boolean;
142
149
  }) => {
143
150
  const { isOpen, close } = useDropdownContext();
144
- const position = useDropdownContext().position;
151
+ const position = useDropdownContext().position.current;
152
+
153
+ const [modalOpen, setModalOpen] = useState(isOpen);
154
+ const [layout, setLayout] = useState<PositionType>({
155
+ bottom: 0,
156
+ height: 0,
157
+ left: 0,
158
+ right: 0,
159
+ top: 0,
160
+ width: 0,
161
+ });
162
+ const itemRef = useRef<NativeView>(null);
163
+ const screen = useWindowDimensions();
164
+ const tw = useTw();
145
165
  const left = position?.left || 0;
146
166
  const top = position?.top || 0;
147
167
  const menuX = left;
148
- const menuY = top;
149
168
  const width = position?.width || 0;
150
- const screen = useWindowDimensions();
151
- const tw = useTw();
152
169
  const style = tw`${className}`;
153
170
  const styleWidth = !isNaN(Number(style?.width)) ? Number(style?.width) : 0;
154
171
  const menuWidth = styleWidth || width || 0;
155
- const isEndOfScreen = screen.width - menuX < menuWidth;
172
+ const isEndOfXScreen = screen.width - menuX < menuWidth;
173
+ const isEndOfYScreen = top + layout.height > screen.height;
156
174
  const menuStyle: ViewStyle = {
157
- top: menuY,
158
175
  left: menuX,
159
176
  transformOrigin: "top left",
160
177
  };
161
- if (isEndOfScreen) {
178
+ menuStyle.top = top;
179
+
180
+ if (isEndOfXScreen) {
162
181
  menuStyle.left = menuX - menuWidth + width;
163
182
  menuStyle.transformOrigin = "top right";
164
183
  }
165
- const [modalOpen, setModalOpen] = useState(isOpen);
184
+ if (isEndOfYScreen) {
185
+ menuStyle.top = top - layout.height - position.height;
186
+ menuStyle.transformOrigin = "bottom left";
187
+ }
188
+ const measureLayout = useCallback(() => {
189
+ if (itemRef.current) {
190
+ itemRef.current.measure((x, y, width, height, pageX, pageY) => {
191
+ setLayout({
192
+ width,
193
+ height,
194
+ top: pageY,
195
+ left: pageX,
196
+ right: screen.width - pageX - width,
197
+ bottom: screen.height - pageY - height,
198
+ });
199
+ });
200
+ }
201
+ }, []);
166
202
 
167
203
  useEffect(() => {
168
204
  if (isOpen) {
@@ -205,6 +241,7 @@ const DropdownMenu = ({
205
241
  <AnimatePresence exitBeforeEnter>
206
242
  {isOpen && (
207
243
  <View
244
+ ref={itemRef}
208
245
  className={mergeClasses(
209
246
  "absolute in:scale-0 scale-100 out:scale-0 overflow-hidden z-10 bg-card/95 rounded-xl border border-muted/15",
210
247
  width ? `w-[${width}px]` : "",
@@ -212,6 +249,7 @@ const DropdownMenu = ({
212
249
  )}
213
250
  onDidAnimate={onDidAnimate}
214
251
  style={menuStyle}
252
+ onLayout={measureLayout}
215
253
  animated
216
254
  print
217
255
  >