@limonify/zest-ui 0.1.5 → 0.2.0

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 (141) hide show
  1. package/lib/avatar/root/AvatarRoot.d.ts.map +1 -1
  2. package/lib/avatar/root/AvatarRoot.js +1 -1
  3. package/lib/avatar/root/AvatarRoot.js.map +1 -1
  4. package/lib/button/Button.d.ts.map +1 -1
  5. package/lib/button/Button.js +1 -1
  6. package/lib/button/Button.js.map +1 -1
  7. package/lib/checkbox-group/CheckboxGroup.d.ts.map +1 -1
  8. package/lib/checkbox-group/CheckboxGroup.js +1 -1
  9. package/lib/checkbox-group/CheckboxGroup.js.map +1 -1
  10. package/lib/collapsible/panel/useCollapsiblePanel.d.ts.map +1 -1
  11. package/lib/collapsible/panel/useCollapsiblePanel.js +2 -0
  12. package/lib/collapsible/panel/useCollapsiblePanel.js.map +1 -1
  13. package/lib/dialog/popup/DialogPopup.d.ts +6 -0
  14. package/lib/dialog/popup/DialogPopup.d.ts.map +1 -1
  15. package/lib/dialog/popup/DialogPopup.js +2 -2
  16. package/lib/dialog/popup/DialogPopup.js.map +1 -1
  17. package/lib/dialog/popup/useDialogPopupProps.d.ts +1 -0
  18. package/lib/dialog/popup/useDialogPopupProps.d.ts.map +1 -1
  19. package/lib/dialog/popup/useDialogPopupProps.js +3 -0
  20. package/lib/dialog/popup/useDialogPopupProps.js.map +1 -1
  21. package/lib/dialog/root/DialogTransitionContext.d.ts +8 -0
  22. package/lib/dialog/root/DialogTransitionContext.d.ts.map +1 -0
  23. package/lib/dialog/root/DialogTransitionContext.js +44 -0
  24. package/lib/dialog/root/DialogTransitionContext.js.map +1 -0
  25. package/lib/dialog/root/useRenderDialogRoot.d.ts.map +1 -1
  26. package/lib/dialog/root/useRenderDialogRoot.js +40 -1
  27. package/lib/dialog/root/useRenderDialogRoot.js.map +1 -1
  28. package/lib/drawer/popup/DrawerPopup.d.ts +6 -0
  29. package/lib/drawer/popup/DrawerPopup.d.ts.map +1 -1
  30. package/lib/drawer/popup/DrawerPopup.js +2 -1
  31. package/lib/drawer/popup/DrawerPopup.js.map +1 -1
  32. package/lib/input/Input.d.ts +2 -1
  33. package/lib/input/Input.d.ts.map +1 -1
  34. package/lib/input/Input.js +35 -2
  35. package/lib/input/Input.js.map +1 -1
  36. package/lib/menu/item/MenuItem.d.ts.map +1 -1
  37. package/lib/menu/item/MenuItem.js +1 -1
  38. package/lib/menu/item/MenuItem.js.map +1 -1
  39. package/lib/menu/popup/MenuPopup.d.ts +15 -0
  40. package/lib/menu/popup/MenuPopup.d.ts.map +1 -1
  41. package/lib/menu/popup/MenuPopup.js +4 -1
  42. package/lib/menu/popup/MenuPopup.js.map +1 -1
  43. package/lib/menu/root/MenuRoot.d.ts +1 -1
  44. package/lib/menu/root/MenuRoot.d.ts.map +1 -1
  45. package/lib/menu/root/MenuRoot.js +40 -1
  46. package/lib/menu/root/MenuRoot.js.map +1 -1
  47. package/lib/menu/root/MenuTransitionContext.d.ts +8 -0
  48. package/lib/menu/root/MenuTransitionContext.d.ts.map +1 -0
  49. package/lib/menu/root/MenuTransitionContext.js +44 -0
  50. package/lib/menu/root/MenuTransitionContext.js.map +1 -0
  51. package/lib/popover/popup/PopoverPopup.d.ts +6 -0
  52. package/lib/popover/popup/PopoverPopup.d.ts.map +1 -1
  53. package/lib/popover/popup/PopoverPopup.js +4 -1
  54. package/lib/popover/popup/PopoverPopup.js.map +1 -1
  55. package/lib/popover/root/PopoverRoot.d.ts +1 -1
  56. package/lib/popover/root/PopoverRoot.d.ts.map +1 -1
  57. package/lib/popover/root/PopoverRoot.js +40 -1
  58. package/lib/popover/root/PopoverRoot.js.map +1 -1
  59. package/lib/popover/root/PopoverTransitionContext.d.ts +8 -0
  60. package/lib/popover/root/PopoverTransitionContext.d.ts.map +1 -0
  61. package/lib/popover/root/PopoverTransitionContext.js +44 -0
  62. package/lib/popover/root/PopoverTransitionContext.js.map +1 -0
  63. package/lib/radio-group/RadioGroup.d.ts.map +1 -1
  64. package/lib/radio-group/RadioGroup.js +1 -1
  65. package/lib/radio-group/RadioGroup.js.map +1 -1
  66. package/lib/select/list/SelectList.d.ts.map +1 -1
  67. package/lib/select/list/SelectList.js +9 -1
  68. package/lib/select/list/SelectList.js.map +1 -1
  69. package/lib/select/popup/SelectPopup.d.ts +15 -0
  70. package/lib/select/popup/SelectPopup.d.ts.map +1 -1
  71. package/lib/select/popup/SelectPopup.js +4 -1
  72. package/lib/select/popup/SelectPopup.js.map +1 -1
  73. package/lib/select/root/SelectRoot.d.ts +1 -1
  74. package/lib/select/root/SelectRoot.d.ts.map +1 -1
  75. package/lib/select/root/SelectRoot.js +40 -1
  76. package/lib/select/root/SelectRoot.js.map +1 -1
  77. package/lib/select/root/SelectTransitionContext.d.ts +8 -0
  78. package/lib/select/root/SelectTransitionContext.d.ts.map +1 -0
  79. package/lib/select/root/SelectTransitionContext.js +44 -0
  80. package/lib/select/root/SelectTransitionContext.js.map +1 -0
  81. package/lib/separator/Separator.d.ts +2 -1
  82. package/lib/separator/Separator.d.ts.map +1 -1
  83. package/lib/separator/Separator.js +35 -1
  84. package/lib/separator/Separator.js.map +1 -1
  85. package/lib/slider/thumb/SliderThumb.d.ts +4 -0
  86. package/lib/slider/thumb/SliderThumb.d.ts.map +1 -1
  87. package/lib/slider/thumb/SliderThumb.js +1 -1
  88. package/lib/slider/thumb/SliderThumb.js.map +1 -1
  89. package/lib/tabs/tab/TabsTab.d.ts.map +1 -1
  90. package/lib/tabs/tab/TabsTab.js +1 -1
  91. package/lib/tabs/tab/TabsTab.js.map +1 -1
  92. package/lib/toggle/Toggle.d.ts +2 -1
  93. package/lib/toggle/Toggle.d.ts.map +1 -1
  94. package/lib/toggle/Toggle.js +35 -1
  95. package/lib/toggle/Toggle.js.map +1 -1
  96. package/lib/toggle-group/ToggleGroup.d.ts.map +1 -1
  97. package/lib/toggle-group/ToggleGroup.js +1 -1
  98. package/lib/toggle-group/ToggleGroup.js.map +1 -1
  99. package/lib/tooltip/popup/TooltipPopup.d.ts +15 -0
  100. package/lib/tooltip/popup/TooltipPopup.d.ts.map +1 -1
  101. package/lib/tooltip/popup/TooltipPopup.js +5 -1
  102. package/lib/tooltip/popup/TooltipPopup.js.map +1 -1
  103. package/lib/tooltip/root/TooltipRoot.d.ts +1 -1
  104. package/lib/tooltip/root/TooltipRoot.d.ts.map +1 -1
  105. package/lib/tooltip/root/TooltipRoot.js +40 -1
  106. package/lib/tooltip/root/TooltipRoot.js.map +1 -1
  107. package/lib/tooltip/root/TooltipTransitionContext.d.ts +8 -0
  108. package/lib/tooltip/root/TooltipTransitionContext.d.ts.map +1 -0
  109. package/lib/tooltip/root/TooltipTransitionContext.js +44 -0
  110. package/lib/tooltip/root/TooltipTransitionContext.js.map +1 -0
  111. package/package.json +182 -2
  112. package/src/avatar/root/AvatarRoot.tsx +4 -1
  113. package/src/button/Button.tsx +4 -1
  114. package/src/checkbox-group/CheckboxGroup.tsx +4 -1
  115. package/src/collapsible/panel/useCollapsiblePanel.tsx +1 -0
  116. package/src/dialog/popup/DialogPopup.tsx +8 -2
  117. package/src/dialog/popup/useDialogPopupProps.ts +3 -0
  118. package/src/dialog/root/DialogTransitionContext.ts +15 -0
  119. package/src/dialog/root/useRenderDialogRoot.tsx +13 -1
  120. package/src/drawer/popup/DrawerPopup.tsx +8 -1
  121. package/src/input/Input.tsx +5 -1
  122. package/src/menu/item/MenuItem.tsx +4 -1
  123. package/src/menu/popup/MenuPopup.tsx +18 -1
  124. package/src/menu/root/MenuRoot.tsx +14 -2
  125. package/src/menu/root/MenuTransitionContext.ts +15 -0
  126. package/src/popover/popup/PopoverPopup.tsx +9 -1
  127. package/src/popover/root/PopoverRoot.tsx +14 -2
  128. package/src/popover/root/PopoverTransitionContext.ts +15 -0
  129. package/src/radio-group/RadioGroup.tsx +4 -1
  130. package/src/select/list/SelectList.tsx +9 -1
  131. package/src/select/popup/SelectPopup.tsx +18 -1
  132. package/src/select/root/SelectRoot.tsx +18 -2
  133. package/src/select/root/SelectTransitionContext.ts +15 -0
  134. package/src/separator/Separator.tsx +5 -1
  135. package/src/slider/thumb/SliderThumb.tsx +5 -1
  136. package/src/tabs/tab/TabsTab.tsx +4 -1
  137. package/src/toggle/Toggle.tsx +5 -1
  138. package/src/toggle-group/ToggleGroup.tsx +4 -1
  139. package/src/tooltip/popup/TooltipPopup.tsx +19 -1
  140. package/src/tooltip/root/TooltipRoot.tsx +18 -2
  141. package/src/tooltip/root/TooltipTransitionContext.ts +15 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limonify/zest-ui",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "Base UI for React Native — headless, unstyled, accessible primitive components.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,6 +21,176 @@
21
21
  "types": "./lib/index.d.ts",
22
22
  "react-native": "./src/index.ts",
23
23
  "default": "./lib/index.js"
24
+ },
25
+ "./accordion": {
26
+ "types": "./lib/accordion/index.d.ts",
27
+ "react-native": "./src/accordion/index.ts",
28
+ "default": "./lib/accordion/index.js"
29
+ },
30
+ "./alert-dialog": {
31
+ "types": "./lib/alert-dialog/index.d.ts",
32
+ "react-native": "./src/alert-dialog/index.ts",
33
+ "default": "./lib/alert-dialog/index.js"
34
+ },
35
+ "./autocomplete": {
36
+ "types": "./lib/autocomplete/index.d.ts",
37
+ "react-native": "./src/autocomplete/index.ts",
38
+ "default": "./lib/autocomplete/index.js"
39
+ },
40
+ "./avatar": {
41
+ "types": "./lib/avatar/index.d.ts",
42
+ "react-native": "./src/avatar/index.ts",
43
+ "default": "./lib/avatar/index.js"
44
+ },
45
+ "./button": {
46
+ "types": "./lib/button/index.d.ts",
47
+ "react-native": "./src/button/index.ts",
48
+ "default": "./lib/button/index.js"
49
+ },
50
+ "./checkbox": {
51
+ "types": "./lib/checkbox/index.d.ts",
52
+ "react-native": "./src/checkbox/index.ts",
53
+ "default": "./lib/checkbox/index.js"
54
+ },
55
+ "./checkbox-group": {
56
+ "types": "./lib/checkbox-group/index.d.ts",
57
+ "react-native": "./src/checkbox-group/index.ts",
58
+ "default": "./lib/checkbox-group/index.js"
59
+ },
60
+ "./collapsible": {
61
+ "types": "./lib/collapsible/index.d.ts",
62
+ "react-native": "./src/collapsible/index.ts",
63
+ "default": "./lib/collapsible/index.js"
64
+ },
65
+ "./combobox": {
66
+ "types": "./lib/combobox/index.d.ts",
67
+ "react-native": "./src/combobox/index.ts",
68
+ "default": "./lib/combobox/index.js"
69
+ },
70
+ "./context-menu": {
71
+ "types": "./lib/context-menu/index.d.ts",
72
+ "react-native": "./src/context-menu/index.ts",
73
+ "default": "./lib/context-menu/index.js"
74
+ },
75
+ "./dialog": {
76
+ "types": "./lib/dialog/index.d.ts",
77
+ "react-native": "./src/dialog/index.ts",
78
+ "default": "./lib/dialog/index.js"
79
+ },
80
+ "./drawer": {
81
+ "types": "./lib/drawer/index.d.ts",
82
+ "react-native": "./src/drawer/index.ts",
83
+ "default": "./lib/drawer/index.js"
84
+ },
85
+ "./field": {
86
+ "types": "./lib/field/index.d.ts",
87
+ "react-native": "./src/field/index.ts",
88
+ "default": "./lib/field/index.js"
89
+ },
90
+ "./fieldset": {
91
+ "types": "./lib/fieldset/index.d.ts",
92
+ "react-native": "./src/fieldset/index.ts",
93
+ "default": "./lib/fieldset/index.js"
94
+ },
95
+ "./input": {
96
+ "types": "./lib/input/index.d.ts",
97
+ "react-native": "./src/input/index.ts",
98
+ "default": "./lib/input/index.js"
99
+ },
100
+ "./menu": {
101
+ "types": "./lib/menu/index.d.ts",
102
+ "react-native": "./src/menu/index.ts",
103
+ "default": "./lib/menu/index.js"
104
+ },
105
+ "./meter": {
106
+ "types": "./lib/meter/index.d.ts",
107
+ "react-native": "./src/meter/index.ts",
108
+ "default": "./lib/meter/index.js"
109
+ },
110
+ "./number-field": {
111
+ "types": "./lib/number-field/index.d.ts",
112
+ "react-native": "./src/number-field/index.ts",
113
+ "default": "./lib/number-field/index.js"
114
+ },
115
+ "./otp-field": {
116
+ "types": "./lib/otp-field/index.d.ts",
117
+ "react-native": "./src/otp-field/index.ts",
118
+ "default": "./lib/otp-field/index.js"
119
+ },
120
+ "./popover": {
121
+ "types": "./lib/popover/index.d.ts",
122
+ "react-native": "./src/popover/index.ts",
123
+ "default": "./lib/popover/index.js"
124
+ },
125
+ "./progress": {
126
+ "types": "./lib/progress/index.d.ts",
127
+ "react-native": "./src/progress/index.ts",
128
+ "default": "./lib/progress/index.js"
129
+ },
130
+ "./radio": {
131
+ "types": "./lib/radio/index.d.ts",
132
+ "react-native": "./src/radio/index.ts",
133
+ "default": "./lib/radio/index.js"
134
+ },
135
+ "./radio-group": {
136
+ "types": "./lib/radio-group/index.d.ts",
137
+ "react-native": "./src/radio-group/index.ts",
138
+ "default": "./lib/radio-group/index.js"
139
+ },
140
+ "./select": {
141
+ "types": "./lib/select/index.d.ts",
142
+ "react-native": "./src/select/index.ts",
143
+ "default": "./lib/select/index.js"
144
+ },
145
+ "./separator": {
146
+ "types": "./lib/separator/index.d.ts",
147
+ "react-native": "./src/separator/index.ts",
148
+ "default": "./lib/separator/index.js"
149
+ },
150
+ "./slider": {
151
+ "types": "./lib/slider/index.d.ts",
152
+ "react-native": "./src/slider/index.ts",
153
+ "default": "./lib/slider/index.js"
154
+ },
155
+ "./switch": {
156
+ "types": "./lib/switch/index.d.ts",
157
+ "react-native": "./src/switch/index.ts",
158
+ "default": "./lib/switch/index.js"
159
+ },
160
+ "./tabs": {
161
+ "types": "./lib/tabs/index.d.ts",
162
+ "react-native": "./src/tabs/index.ts",
163
+ "default": "./lib/tabs/index.js"
164
+ },
165
+ "./toast": {
166
+ "types": "./lib/toast/index.d.ts",
167
+ "react-native": "./src/toast/index.ts",
168
+ "default": "./lib/toast/index.js"
169
+ },
170
+ "./toggle": {
171
+ "types": "./lib/toggle/index.d.ts",
172
+ "react-native": "./src/toggle/index.ts",
173
+ "default": "./lib/toggle/index.js"
174
+ },
175
+ "./toggle-group": {
176
+ "types": "./lib/toggle-group/index.d.ts",
177
+ "react-native": "./src/toggle-group/index.ts",
178
+ "default": "./lib/toggle-group/index.js"
179
+ },
180
+ "./tooltip": {
181
+ "types": "./lib/tooltip/index.d.ts",
182
+ "react-native": "./src/tooltip/index.ts",
183
+ "default": "./lib/tooltip/index.js"
184
+ },
185
+ "./hooks": {
186
+ "types": "./lib/hooks/index.d.ts",
187
+ "react-native": "./src/hooks/index.ts",
188
+ "default": "./lib/hooks/index.js"
189
+ },
190
+ "./store": {
191
+ "types": "./lib/store/index.d.ts",
192
+ "react-native": "./src/store/index.ts",
193
+ "default": "./lib/store/index.js"
24
194
  }
25
195
  },
26
196
  "files": [
@@ -34,6 +204,7 @@
34
204
  "scripts": {
35
205
  "typecheck": "tsc --noEmit",
36
206
  "test": "jest",
207
+ "test:coverage": "jest --coverage",
37
208
  "lint": "eslint src",
38
209
  "build": "tsc -p tsconfig.build.json",
39
210
  "clean": "rm -rf lib",
@@ -46,6 +217,7 @@
46
217
  },
47
218
  "devDependencies": {
48
219
  "@babel/runtime": "^8.0.0",
220
+ "@size-limit/preset-small-lib": "^12.1.0",
49
221
  "@testing-library/react-native": "^14.0.1",
50
222
  "@types/jest": "^29.5.14",
51
223
  "@types/react": "~19.2.2",
@@ -55,6 +227,7 @@
55
227
  "react": "19.2.3",
56
228
  "react-native": "0.86.0",
57
229
  "react-native-gesture-handler": "~3.1.0",
230
+ "size-limit": "^12.1.0",
58
231
  "typescript": "~6.0.3"
59
232
  },
60
233
  "dependencies": {
@@ -64,5 +237,12 @@
64
237
  "react-native-gesture-handler": {
65
238
  "optional": true
66
239
  }
67
- }
240
+ },
241
+ "size-limit": [
242
+ {
243
+ "name": "full",
244
+ "path": "lib/index.js",
245
+ "limit": "500 KB"
246
+ }
247
+ ]
68
248
  }
@@ -14,7 +14,10 @@ export function AvatarRoot(componentProps: AvatarRoot.Props) {
14
14
 
15
15
  const [imageLoadingStatus, setImageLoadingStatus] = React.useState<ImageLoadingStatus>('idle');
16
16
 
17
- const state: AvatarRootState = { imageLoadingStatus };
17
+ const state: AvatarRootState = React.useMemo(
18
+ () => ({ imageLoadingStatus }),
19
+ [imageLoadingStatus],
20
+ );
18
21
 
19
22
  const contextValue: AvatarRootContext = React.useMemo(
20
23
  () => ({ imageLoadingStatus, setImageLoadingStatus }),
@@ -19,7 +19,10 @@ export function Button(componentProps: Button.Props) {
19
19
 
20
20
  const { getButtonProps } = useButton({ disabled });
21
21
 
22
- const state: ButtonState = { disabled, pressed };
22
+ const state: ButtonState = React.useMemo(
23
+ () => ({ disabled, pressed }),
24
+ [disabled, pressed],
25
+ );
23
26
 
24
27
  return useRenderElement(Pressable, componentProps, {
25
28
  state,
@@ -52,7 +52,10 @@ export function CheckboxGroup(componentProps: CheckboxGroup.Props) {
52
52
 
53
53
  const parent = useCheckboxGroupParent({ allValues, value, onValueChange: setValue });
54
54
 
55
- const state: CheckboxGroupState = { disabled };
55
+ const state: CheckboxGroupState = React.useMemo(
56
+ () => ({ disabled }),
57
+ [disabled],
58
+ );
56
59
 
57
60
  const contextValue: CheckboxGroupContext = React.useMemo(
58
61
  () => ({
@@ -35,6 +35,7 @@ export function useCollapsiblePanel(parameters: UseCollapsiblePanelParameters) {
35
35
 
36
36
  const handleContentLayout = React.useCallback((event: LayoutChangeEvent) => {
37
37
  const { height, width } = event.nativeEvent.layout;
38
+ if (height <= 0) return;
38
39
  setDimensions((previous) =>
39
40
  previous.height === height && previous.width === width ? previous : { height, width },
40
41
  );
@@ -2,6 +2,7 @@
2
2
  import { View } from 'react-native';
3
3
  import { useRenderElement } from '../../use-render/useRenderElement';
4
4
  import type { ZestUIComponentProps } from '../../types';
5
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
5
6
  import { useDialogPopupProps } from './useDialogPopupProps';
6
7
 
7
8
  /**
@@ -11,9 +12,9 @@ import { useDialogPopupProps } from './useDialogPopupProps';
11
12
  export function DialogPopup(componentProps: DialogPopup.Props) {
12
13
  const { render, className, style, ref, ...elementProps } = componentProps;
13
14
 
14
- const { open, props } = useDialogPopupProps();
15
+ const { open, transitionStatus, props } = useDialogPopupProps();
15
16
 
16
- const state: DialogPopupState = { open };
17
+ const state: DialogPopupState = { open, transitionStatus };
17
18
 
18
19
  return useRenderElement(View, componentProps, {
19
20
  state,
@@ -27,6 +28,11 @@ export interface DialogPopupState {
27
28
  * Whether the dialog is currently open.
28
29
  */
29
30
  open: boolean;
31
+ /**
32
+ * The transition status of the dialog: `'starting'` as it opens (auto-clears
33
+ * to `undefined` after one frame), `'ending'` once it is closing.
34
+ */
35
+ transitionStatus: TransitionStatus;
30
36
  }
31
37
 
32
38
  export interface DialogPopupProps extends ZestUIComponentProps<typeof View, DialogPopupState> {}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  import { useDialogRootContext } from '../root/DialogRootContext';
3
3
  import { useDialogPortalContext } from '../portal/DialogPortalContext';
4
+ import { useDialogTransitionContext } from '../root/DialogTransitionContext';
4
5
 
5
6
  /**
6
7
  * The state and element props shared by every popup built on the dialog store —
@@ -9,6 +10,7 @@ import { useDialogPortalContext } from '../portal/DialogPortalContext';
9
10
  export function useDialogPopupProps() {
10
11
  useDialogPortalContext();
11
12
  const store = useDialogRootContext();
13
+ const transitionContext = useDialogTransitionContext();
12
14
 
13
15
  const open = store.useState('open');
14
16
  const titleElementId = store.useState('titleElementId');
@@ -18,6 +20,7 @@ export function useDialogPopupProps() {
18
20
  return {
19
21
  store,
20
22
  open,
23
+ transitionStatus: transitionContext?.transitionStatus,
21
24
  props: {
22
25
  role,
23
26
  accessibilityViewIsModal: true,
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
4
+
5
+ export interface DialogTransitionContext {
6
+ transitionStatus: TransitionStatus;
7
+ }
8
+
9
+ export const DialogTransitionContext = React.createContext<DialogTransitionContext | undefined>(
10
+ undefined,
11
+ );
12
+
13
+ export function useDialogTransitionContext() {
14
+ return React.useContext(DialogTransitionContext);
15
+ }
@@ -1,10 +1,12 @@
1
1
  'use client';
2
2
  import * as React from 'react';
3
3
  import { useRefWithInit } from '../../hooks/useRefWithInit';
4
+ import { useTransitionStatus } from '../../internals/useTransitionStatus';
4
5
  import { usePopupRootHandle } from '../../utils/popups/usePopupRootHandle';
5
6
  import { DialogStore } from '../store/DialogStore';
6
7
  import type { DialogRootProps } from './DialogRoot';
7
8
  import { DialogRootContext } from './DialogRootContext';
9
+ import { DialogTransitionContext } from './DialogTransitionContext';
8
10
 
9
11
  export type DialogRootMode = 'dialog' | 'alert-dialog';
10
12
 
@@ -53,11 +55,21 @@ export function useRenderDialogRoot<Payload = unknown>(
53
55
 
54
56
  usePopupRootHandle({ store, handle, actionsRef });
55
57
 
58
+ const resolvedOpen = store.useState('open');
59
+ const { transitionStatus } = useTransitionStatus(resolvedOpen, false, true);
60
+
56
61
  const payload = store.useState('payload') as Payload;
57
62
 
63
+ const transitionContextValue = React.useMemo(
64
+ () => ({ transitionStatus }),
65
+ [transitionStatus],
66
+ );
67
+
58
68
  return (
59
69
  <DialogRootContext.Provider value={store}>
60
- {typeof children === 'function' ? children(payload) : children}
70
+ <DialogTransitionContext.Provider value={transitionContextValue}>
71
+ {typeof children === 'function' ? children(payload) : children}
72
+ </DialogTransitionContext.Provider>
61
73
  </DialogRootContext.Provider>
62
74
  );
63
75
  }
@@ -6,6 +6,7 @@ import { useDialogPopupProps } from '../../dialog/popup/useDialogPopupProps';
6
6
  import { useRenderElement } from '../../use-render/useRenderElement';
7
7
  import { useStableCallback } from '../../hooks/useStableCallback';
8
8
  import type { ZestUIComponentProps } from '../../types';
9
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
9
10
  import { createChangeEventDetails } from '../../utils/createChangeEventDetails';
10
11
  import { REASONS } from '../../utils/reasons';
11
12
  import { useDrawerRootContext, type DrawerSnapPoint, type DrawerSwipeDirection } from '../root/DrawerRootContext';
@@ -59,7 +60,7 @@ export function DrawerPopup(componentProps: DrawerPopup.Props) {
59
60
  setActiveSnapPoint,
60
61
  snapToSequentialPoints,
61
62
  } = useDrawerSnapPoints();
62
- const { store, open, props } = useDialogPopupProps();
63
+ const { store, open, transitionStatus, props } = useDialogPopupProps();
63
64
 
64
65
  const [swiping, setSwiping] = React.useState(false);
65
66
  const [swipeMovement, setSwipeMovement] = React.useState(0);
@@ -157,6 +158,7 @@ export function DrawerPopup(componentProps: DrawerPopup.Props) {
157
158
 
158
159
  const state: DrawerPopupState = {
159
160
  open,
161
+ transitionStatus,
160
162
  swiping,
161
163
  swipeMovement,
162
164
  swipeDirection,
@@ -192,6 +194,11 @@ export interface DrawerPopupState {
192
194
  * Whether the drawer is currently open.
193
195
  */
194
196
  open: boolean;
197
+ /**
198
+ * The transition status of the drawer: `'starting'` as it opens (auto-clears
199
+ * to `undefined` after one frame), `'ending'` once it is closing.
200
+ */
201
+ transitionStatus: TransitionStatus;
195
202
  /**
196
203
  * Whether a swipe is currently in progress.
197
204
  */
@@ -1,4 +1,5 @@
1
1
  'use client';
2
+ import * as React from 'react';
2
3
  import { TextInput } from 'react-native';
3
4
  import { useRenderElement } from '../use-render/useRenderElement';
4
5
  import { useFieldControl } from '../field/control/useFieldControl';
@@ -32,7 +33,10 @@ export function Input(componentProps: Input.Props) {
32
33
  requireField: false,
33
34
  });
34
35
 
35
- const state: Input.State = { disabled: field?.disabled ?? false };
36
+ const state: Input.State = React.useMemo(
37
+ () => ({ disabled: field?.disabled ?? false }),
38
+ [field?.disabled],
39
+ );
36
40
 
37
41
  return useRenderElement(TextInput, componentProps, {
38
42
  state,
@@ -43,7 +43,10 @@ export function MenuItem(componentProps: MenuItem.Props) {
43
43
 
44
44
  const { getButtonProps } = useButton({ disabled });
45
45
 
46
- const state: MenuItemState = { disabled, pressed, index };
46
+ const state: MenuItemState = React.useMemo(
47
+ () => ({ disabled, pressed, index }),
48
+ [disabled, pressed, index],
49
+ );
47
50
 
48
51
  return useRenderElement(Pressable, componentProps, {
49
52
  state,
@@ -2,9 +2,11 @@
2
2
  import { View } from 'react-native';
3
3
  import { useMenuRootContext } from '../root/MenuRootContext';
4
4
  import { useMenuPositionerContext } from '../positioner/MenuPositionerContext';
5
+ import { useMenuTransitionContext } from '../root/MenuTransitionContext';
5
6
  import { useRenderElement } from '../../use-render/useRenderElement';
6
7
  import { CompositeList } from '../../internals/composite/list/CompositeList';
7
8
  import type { Align, Side } from '../../utils/useAnchorPositioning';
9
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
8
10
  import type { ZestUIComponentProps } from '../../types';
9
11
 
10
12
  /**
@@ -16,10 +18,11 @@ export function MenuPopup(componentProps: MenuPopup.Props) {
16
18
 
17
19
  const store = useMenuRootContext();
18
20
  const { side, align } = useMenuPositionerContext();
21
+ const { transitionStatus } = useMenuTransitionContext() ?? { transitionStatus: undefined };
19
22
 
20
23
  const open = store.useState('open');
21
24
 
22
- const state: MenuPopupState = { open, side, align };
25
+ const state: MenuPopupState = { open, transitionStatus, side, align };
23
26
 
24
27
  const element = useRenderElement(View, componentProps, {
25
28
  state,
@@ -40,8 +43,22 @@ export function MenuPopup(componentProps: MenuPopup.Props) {
40
43
  }
41
44
 
42
45
  export interface MenuPopupState {
46
+ /**
47
+ * Whether the menu is currently open.
48
+ */
43
49
  open: boolean;
50
+ /**
51
+ * The transition status of the menu: `'starting'` as it opens (auto-clears
52
+ * to `undefined` after one frame), `'ending'` once it is closing.
53
+ */
54
+ transitionStatus: TransitionStatus;
55
+ /**
56
+ * The side the popup was actually placed on, after collision handling.
57
+ */
44
58
  side: Side;
59
+ /**
60
+ * The alignment the popup was actually placed with.
61
+ */
45
62
  align: Align;
46
63
  }
47
64
 
@@ -1,12 +1,14 @@
1
1
  'use client';
2
- import type * as React from 'react';
2
+ import * as React from 'react';
3
3
  import { useRefWithInit } from '../../hooks/useRefWithInit';
4
+ import { useTransitionStatus } from '../../internals/useTransitionStatus';
4
5
  import { usePopupRootHandle } from '../../utils/popups/usePopupRootHandle';
5
6
  import type { ZestChangeEventDetails } from '../../utils/createChangeEventDetails';
6
7
  import type { REASONS } from '../../utils/reasons';
7
8
  import { MenuStore } from '../store/MenuStore';
8
9
  import type { MenuHandle } from '../store/MenuHandle';
9
10
  import { MenuRootContext } from './MenuRootContext';
11
+ import { MenuTransitionContext } from './MenuTransitionContext';
10
12
 
11
13
  /**
12
14
  * Groups all parts of the menu.
@@ -49,11 +51,21 @@ export function MenuRoot<Payload = unknown>(props: MenuRoot.Props<Payload>) {
49
51
 
50
52
  usePopupRootHandle({ store, handle, actionsRef });
51
53
 
54
+ const resolvedOpen = store.useState('open');
55
+ const { transitionStatus } = useTransitionStatus(resolvedOpen, false, true);
56
+
52
57
  const payload = store.useState('payload') as Payload;
53
58
 
59
+ const transitionContextValue = React.useMemo(
60
+ () => ({ transitionStatus }),
61
+ [transitionStatus],
62
+ );
63
+
54
64
  return (
55
65
  <MenuRootContext.Provider value={store}>
56
- {typeof children === 'function' ? children(payload) : children}
66
+ <MenuTransitionContext.Provider value={transitionContextValue}>
67
+ {typeof children === 'function' ? children(payload) : children}
68
+ </MenuTransitionContext.Provider>
57
69
  </MenuRootContext.Provider>
58
70
  );
59
71
  }
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
4
+
5
+ export interface MenuTransitionContext {
6
+ transitionStatus: TransitionStatus;
7
+ }
8
+
9
+ export const MenuTransitionContext = React.createContext<MenuTransitionContext | undefined>(
10
+ undefined,
11
+ );
12
+
13
+ export function useMenuTransitionContext() {
14
+ return React.useContext(MenuTransitionContext);
15
+ }
@@ -2,8 +2,10 @@
2
2
  import { View } from 'react-native';
3
3
  import { usePopoverRootContext } from '../root/PopoverRootContext';
4
4
  import { usePopoverPositionerContext } from '../positioner/PopoverPositionerContext';
5
+ import { usePopoverTransitionContext } from '../root/PopoverTransitionContext';
5
6
  import { useRenderElement } from '../../use-render/useRenderElement';
6
7
  import type { Align, Side } from '../../utils/useAnchorPositioning';
8
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
7
9
  import type { ZestUIComponentProps } from '../../types';
8
10
 
9
11
  /**
@@ -15,12 +17,13 @@ export function PopoverPopup(componentProps: PopoverPopup.Props) {
15
17
 
16
18
  const store = usePopoverRootContext();
17
19
  const { side, align } = usePopoverPositionerContext();
20
+ const { transitionStatus } = usePopoverTransitionContext() ?? { transitionStatus: undefined };
18
21
 
19
22
  const open = store.useState('open');
20
23
  const titleElementId = store.useState('titleElementId');
21
24
  const descriptionElementId = store.useState('descriptionElementId');
22
25
 
23
- const state: PopoverPopupState = { open, side, align };
26
+ const state: PopoverPopupState = { open, transitionStatus, side, align };
24
27
 
25
28
  return useRenderElement(View, componentProps, {
26
29
  state,
@@ -45,6 +48,11 @@ export interface PopoverPopupState {
45
48
  * Whether the popover is currently open.
46
49
  */
47
50
  open: boolean;
51
+ /**
52
+ * The transition status of the popover: `'starting'` as it opens (auto-clears
53
+ * to `undefined` after one frame), `'ending'` once it is closing.
54
+ */
55
+ transitionStatus: TransitionStatus;
48
56
  /**
49
57
  * The side the popup was actually placed on, after collision handling.
50
58
  */
@@ -1,12 +1,14 @@
1
1
  'use client';
2
- import type * as React from 'react';
2
+ import * as React from 'react';
3
3
  import { useRefWithInit } from '../../hooks/useRefWithInit';
4
+ import { useTransitionStatus } from '../../internals/useTransitionStatus';
4
5
  import { usePopupRootHandle } from '../../utils/popups/usePopupRootHandle';
5
6
  import type { ZestChangeEventDetails } from '../../utils/createChangeEventDetails';
6
7
  import type { REASONS } from '../../utils/reasons';
7
8
  import { PopoverStore } from '../store/PopoverStore';
8
9
  import type { PopoverHandle } from '../store/PopoverHandle';
9
10
  import { PopoverRootContext } from './PopoverRootContext';
11
+ import { PopoverTransitionContext } from './PopoverTransitionContext';
10
12
 
11
13
  /**
12
14
  * Groups all parts of the popover.
@@ -43,11 +45,21 @@ export function PopoverRoot<Payload = unknown>(props: PopoverRoot.Props<Payload>
43
45
 
44
46
  usePopupRootHandle({ store, handle, actionsRef });
45
47
 
48
+ const resolvedOpen = store.useState('open');
49
+ const { transitionStatus } = useTransitionStatus(resolvedOpen, false, true);
50
+
46
51
  const payload = store.useState('payload') as Payload;
47
52
 
53
+ const transitionContextValue = React.useMemo(
54
+ () => ({ transitionStatus }),
55
+ [transitionStatus],
56
+ );
57
+
48
58
  return (
49
59
  <PopoverRootContext.Provider value={store}>
50
- {typeof children === 'function' ? children(payload) : children}
60
+ <PopoverTransitionContext.Provider value={transitionContextValue}>
61
+ {typeof children === 'function' ? children(payload) : children}
62
+ </PopoverTransitionContext.Provider>
51
63
  </PopoverRootContext.Provider>
52
64
  );
53
65
  }
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+ import * as React from 'react';
3
+ import type { TransitionStatus } from '../../internals/useTransitionStatus';
4
+
5
+ export interface PopoverTransitionContext {
6
+ transitionStatus: TransitionStatus;
7
+ }
8
+
9
+ export const PopoverTransitionContext = React.createContext<PopoverTransitionContext | undefined>(
10
+ undefined,
11
+ );
12
+
13
+ export function usePopoverTransitionContext() {
14
+ return React.useContext(PopoverTransitionContext);
15
+ }
@@ -53,7 +53,10 @@ export function RadioGroup<Value = any>(componentProps: RadioGroup.Props<Value>)
53
53
  },
54
54
  );
55
55
 
56
- const state: RadioGroupState = { disabled, readOnly, required };
56
+ const state: RadioGroupState = React.useMemo(
57
+ () => ({ disabled, readOnly, required }),
58
+ [disabled, readOnly, required],
59
+ );
57
60
 
58
61
  const contextValue: RadioGroupContext<Value> = React.useMemo(
59
62
  () => ({