@hzblj/kara-components 0.1.0 → 0.2.1

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 (231) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/lib/module/atoms/badge/badge.android.js +8 -2
  3. package/lib/module/atoms/badge/badge.android.js.map +1 -1
  4. package/lib/module/atoms/button/button-tokens.js +39 -0
  5. package/lib/module/atoms/button/button-tokens.js.map +1 -1
  6. package/lib/module/atoms/button/button.android.js +40 -12
  7. package/lib/module/atoms/button/button.android.js.map +1 -1
  8. package/lib/module/atoms/button/button.ios.js +5 -2
  9. package/lib/module/atoms/button/button.ios.js.map +1 -1
  10. package/lib/module/atoms/checkbox/checkbox.android.js +17 -2
  11. package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
  12. package/lib/module/atoms/date-picker/date-picker.android.js +72 -0
  13. package/lib/module/atoms/date-picker/date-picker.android.js.map +1 -0
  14. package/lib/module/atoms/date-picker/{date-picker.js → date-picker.ios.js} +4 -7
  15. package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -0
  16. package/lib/module/atoms/date-picker/index.js +1 -1
  17. package/lib/module/atoms/date-picker/index.js.map +1 -1
  18. package/lib/module/atoms/fab/fab.android.js +5 -3
  19. package/lib/module/atoms/fab/fab.android.js.map +1 -1
  20. package/lib/module/atoms/glass-view/glass-fallback.js +5 -2
  21. package/lib/module/atoms/glass-view/glass-fallback.js.map +1 -1
  22. package/lib/module/atoms/icon/icon.android.js +10 -12
  23. package/lib/module/atoms/icon/icon.android.js.map +1 -1
  24. package/lib/module/atoms/icon/icon.ios.js +15 -3
  25. package/lib/module/atoms/icon/icon.ios.js.map +1 -1
  26. package/lib/module/atoms/icon/index.js +0 -1
  27. package/lib/module/atoms/icon/index.js.map +1 -1
  28. package/lib/module/atoms/index.js +1 -1
  29. package/lib/module/atoms/index.js.map +1 -1
  30. package/lib/module/atoms/list/list.android.js +8 -2
  31. package/lib/module/atoms/list/list.android.js.map +1 -1
  32. package/lib/module/atoms/picker/index.js +1 -1
  33. package/lib/module/atoms/picker/index.js.map +1 -1
  34. package/lib/module/atoms/picker/picker.android.js +87 -0
  35. package/lib/module/atoms/picker/picker.android.js.map +1 -0
  36. package/lib/module/atoms/picker/{picker.js → picker.ios.js} +1 -1
  37. package/lib/module/atoms/picker/picker.ios.js.map +1 -0
  38. package/lib/module/atoms/rn-icon/index.js +5 -0
  39. package/lib/module/atoms/rn-icon/index.js.map +1 -0
  40. package/lib/module/atoms/rn-icon/rn-icon.android.js +29 -0
  41. package/lib/module/atoms/rn-icon/rn-icon.android.js.map +1 -0
  42. package/lib/module/atoms/rn-icon/rn-icon.ios.js +29 -0
  43. package/lib/module/atoms/rn-icon/rn-icon.ios.js.map +1 -0
  44. package/lib/module/atoms/rn-icon/rn-icon.types.js +4 -0
  45. package/lib/module/atoms/rn-icon/rn-icon.types.js.map +1 -0
  46. package/lib/module/atoms/surface/surface.android.js +33 -5
  47. package/lib/module/atoms/surface/surface.android.js.map +1 -1
  48. package/lib/module/atoms/switch/switch.android.js +14 -1
  49. package/lib/module/atoms/switch/switch.android.js.map +1 -1
  50. package/lib/module/glyph/glyph-button.android.js +25 -0
  51. package/lib/module/glyph/glyph-button.android.js.map +1 -0
  52. package/lib/module/glyph/glyph-button.ios.js +33 -0
  53. package/lib/module/glyph/glyph-button.ios.js.map +1 -0
  54. package/lib/module/glyph/glyph-button.types.js +4 -0
  55. package/lib/module/glyph/glyph-button.types.js.map +1 -0
  56. package/lib/module/glyph/glyph.android.js +20 -0
  57. package/lib/module/glyph/glyph.android.js.map +1 -0
  58. package/lib/module/glyph/glyph.ios.js +24 -0
  59. package/lib/module/glyph/glyph.ios.js.map +1 -0
  60. package/lib/module/glyph/glyph.types.js +4 -0
  61. package/lib/module/glyph/glyph.types.js.map +1 -0
  62. package/lib/module/glyph/index.js +8 -0
  63. package/lib/module/glyph/index.js.map +1 -0
  64. package/lib/module/glyph/rn-glyph.android.js +25 -0
  65. package/lib/module/glyph/rn-glyph.android.js.map +1 -0
  66. package/lib/module/glyph/rn-glyph.ios.js +26 -0
  67. package/lib/module/glyph/rn-glyph.ios.js.map +1 -0
  68. package/lib/module/molecules/collapsible/collapsible.android.js +2 -2
  69. package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
  70. package/lib/module/molecules/labeled-content/labeled-content.js +2 -1
  71. package/lib/module/molecules/labeled-content/labeled-content.js.map +1 -1
  72. package/lib/module/molecules/list-item/list-item.js +2 -1
  73. package/lib/module/molecules/list-item/list-item.js.map +1 -1
  74. package/lib/module/molecules/navigation-bar/navigation-bar.android.js +8 -4
  75. package/lib/module/molecules/navigation-bar/navigation-bar.android.js.map +1 -1
  76. package/lib/module/molecules/radio-group/radio-group.android.js +5 -0
  77. package/lib/module/molecules/radio-group/radio-group.android.js.map +1 -1
  78. package/lib/module/molecules/radio-group/radio-group.ios.js +2 -2
  79. package/lib/module/molecules/radio-group/radio-group.ios.js.map +1 -1
  80. package/lib/module/molecules/search-bar/search-bar.js +10 -23
  81. package/lib/module/molecules/search-bar/search-bar.js.map +1 -1
  82. package/lib/module/molecules/stepper/stepper.android.js +7 -5
  83. package/lib/module/molecules/stepper/stepper.android.js.map +1 -1
  84. package/lib/module/organisms/alert/alert.android.js +42 -13
  85. package/lib/module/organisms/alert/alert.android.js.map +1 -1
  86. package/lib/module/organisms/context-menu/context-menu.android.js +82 -31
  87. package/lib/module/organisms/context-menu/context-menu.android.js.map +1 -1
  88. package/lib/module/organisms/context-menu/context-menu.ios.js +37 -16
  89. package/lib/module/organisms/context-menu/context-menu.ios.js.map +1 -1
  90. package/lib/module/organisms/fab-menu/fab-menu.android.js +6 -2
  91. package/lib/module/organisms/fab-menu/fab-menu.android.js.map +1 -1
  92. package/lib/module/organisms/fab-menu/fab-menu.ios.js +15 -6
  93. package/lib/module/organisms/fab-menu/fab-menu.ios.js.map +1 -1
  94. package/lib/module/organisms/form/form.android.js +1 -0
  95. package/lib/module/organisms/form/form.android.js.map +1 -1
  96. package/lib/module/organisms/index.js +0 -1
  97. package/lib/module/organisms/index.js.map +1 -1
  98. package/lib/module/organisms/menu/menu.android.js +51 -33
  99. package/lib/module/organisms/menu/menu.android.js.map +1 -1
  100. package/lib/typescript/atoms/button/button-tokens.d.ts +6 -1
  101. package/lib/typescript/atoms/date-picker/date-picker.d.ts +1 -2
  102. package/lib/typescript/atoms/date-picker/date-picker.ios.d.ts +3 -0
  103. package/lib/typescript/atoms/glass-view/glass-fallback.d.ts +4 -1
  104. package/lib/typescript/atoms/glass-view/glass-view.types.d.ts +1 -1
  105. package/lib/typescript/atoms/icon/icon.ios.d.ts +4 -0
  106. package/lib/typescript/atoms/icon/icon.types.d.ts +18 -1
  107. package/lib/typescript/atoms/icon/index.d.ts +0 -1
  108. package/lib/typescript/atoms/index.d.ts +1 -1
  109. package/lib/typescript/atoms/list/list.types.d.ts +9 -0
  110. package/lib/typescript/atoms/picker/picker.d.ts +1 -2
  111. package/lib/typescript/atoms/picker/picker.ios.d.ts +2 -0
  112. package/lib/typescript/atoms/rn-icon/index.d.ts +2 -0
  113. package/lib/typescript/atoms/rn-icon/rn-icon.d.ts +1 -0
  114. package/lib/typescript/atoms/rn-icon/rn-icon.ios.d.ts +3 -0
  115. package/lib/typescript/atoms/rn-icon/rn-icon.types.d.ts +5 -0
  116. package/lib/typescript/glyph/glyph-button.d.ts +1 -0
  117. package/lib/typescript/glyph/glyph-button.ios.d.ts +2 -0
  118. package/lib/typescript/glyph/glyph-button.types.d.ts +10 -0
  119. package/lib/typescript/glyph/glyph.d.ts +1 -0
  120. package/lib/typescript/glyph/glyph.ios.d.ts +3 -0
  121. package/lib/typescript/glyph/glyph.types.d.ts +13 -0
  122. package/lib/typescript/glyph/index.d.ts +5 -0
  123. package/lib/typescript/glyph/rn-glyph.d.ts +1 -0
  124. package/lib/typescript/glyph/rn-glyph.ios.d.ts +3 -0
  125. package/lib/typescript/molecules/scroll-edge-container/scroll-edge-container.types.d.ts +3 -0
  126. package/lib/typescript/organisms/context-menu/context-menu.types.d.ts +28 -2
  127. package/lib/typescript/organisms/fab-menu/fab-menu.types.d.ts +1 -1
  128. package/lib/typescript/organisms/form/form.types.d.ts +5 -0
  129. package/lib/typescript/organisms/index.d.ts +0 -1
  130. package/package.json +4 -6
  131. package/src/atoms/badge/badge.android.tsx +5 -2
  132. package/src/atoms/button/button-tokens.ts +49 -0
  133. package/src/atoms/button/button.android.tsx +46 -12
  134. package/src/atoms/button/button.ios.tsx +5 -2
  135. package/src/atoms/checkbox/checkbox.android.tsx +21 -4
  136. package/src/atoms/date-picker/date-picker.android.tsx +74 -0
  137. package/src/atoms/date-picker/date-picker.ios.tsx +27 -0
  138. package/src/atoms/fab/fab.android.tsx +4 -1
  139. package/src/atoms/glass-view/glass-fallback.tsx +5 -2
  140. package/src/atoms/glass-view/glass-view.types.ts +1 -1
  141. package/src/atoms/icon/icon.android.tsx +11 -7
  142. package/src/atoms/icon/icon.ios.tsx +25 -5
  143. package/src/atoms/icon/icon.types.ts +18 -24
  144. package/src/atoms/icon/index.ts +0 -1
  145. package/src/atoms/index.ts +1 -1
  146. package/src/atoms/list/list.android.tsx +12 -1
  147. package/src/atoms/list/list.types.ts +9 -0
  148. package/src/atoms/picker/picker.android.tsx +88 -0
  149. package/src/atoms/rn-icon/index.ts +2 -0
  150. package/src/atoms/rn-icon/rn-icon.android.tsx +28 -0
  151. package/src/atoms/rn-icon/rn-icon.ios.tsx +28 -0
  152. package/src/atoms/rn-icon/rn-icon.types.ts +8 -0
  153. package/src/atoms/surface/surface.android.tsx +28 -4
  154. package/src/atoms/switch/switch.android.tsx +24 -3
  155. package/src/glyph/glyph-button.android.tsx +25 -0
  156. package/src/glyph/glyph-button.ios.tsx +37 -0
  157. package/src/glyph/glyph-button.types.ts +12 -0
  158. package/src/glyph/glyph.android.tsx +12 -0
  159. package/src/glyph/glyph.ios.tsx +15 -0
  160. package/src/glyph/glyph.types.ts +15 -0
  161. package/src/glyph/index.ts +5 -0
  162. package/src/glyph/rn-glyph.android.tsx +14 -0
  163. package/src/glyph/rn-glyph.ios.tsx +16 -0
  164. package/src/molecules/collapsible/collapsible.android.tsx +2 -2
  165. package/src/molecules/labeled-content/labeled-content.tsx +2 -1
  166. package/src/molecules/list-item/list-item.tsx +2 -1
  167. package/src/molecules/navigation-bar/navigation-bar.android.tsx +11 -4
  168. package/src/molecules/radio-group/radio-group.android.tsx +11 -2
  169. package/src/molecules/radio-group/radio-group.ios.tsx +2 -2
  170. package/src/molecules/scroll-edge-container/scroll-edge-container.types.ts +3 -0
  171. package/src/molecules/search-bar/search-bar.tsx +4 -9
  172. package/src/molecules/stepper/stepper.android.tsx +3 -3
  173. package/src/organisms/alert/alert.android.tsx +44 -7
  174. package/src/organisms/context-menu/context-menu.android.tsx +71 -37
  175. package/src/organisms/context-menu/context-menu.ios.tsx +40 -18
  176. package/src/organisms/context-menu/context-menu.types.ts +29 -3
  177. package/src/organisms/fab-menu/fab-menu.android.tsx +3 -2
  178. package/src/organisms/fab-menu/fab-menu.ios.tsx +13 -12
  179. package/src/organisms/fab-menu/fab-menu.types.ts +1 -1
  180. package/src/organisms/form/form.android.tsx +1 -1
  181. package/src/organisms/form/form.types.ts +5 -0
  182. package/src/organisms/index.ts +0 -1
  183. package/src/organisms/menu/menu.android.tsx +36 -30
  184. package/lib/module/atoms/countdown/countdown-frame.js +0 -115
  185. package/lib/module/atoms/countdown/countdown-frame.js.map +0 -1
  186. package/lib/module/atoms/countdown/countdown-units.js +0 -25
  187. package/lib/module/atoms/countdown/countdown-units.js.map +0 -1
  188. package/lib/module/atoms/countdown/countdown.android.js +0 -53
  189. package/lib/module/atoms/countdown/countdown.android.js.map +0 -1
  190. package/lib/module/atoms/countdown/countdown.ios.js +0 -50
  191. package/lib/module/atoms/countdown/countdown.ios.js.map +0 -1
  192. package/lib/module/atoms/countdown/countdown.types.js +0 -4
  193. package/lib/module/atoms/countdown/countdown.types.js.map +0 -1
  194. package/lib/module/atoms/countdown/index.js +0 -5
  195. package/lib/module/atoms/countdown/index.js.map +0 -1
  196. package/lib/module/atoms/date-picker/date-picker.js.map +0 -1
  197. package/lib/module/atoms/icon/icon-names.js +0 -53
  198. package/lib/module/atoms/icon/icon-names.js.map +0 -1
  199. package/lib/module/atoms/picker/picker.js.map +0 -1
  200. package/lib/module/organisms/rn-context-menu/index.js +0 -5
  201. package/lib/module/organisms/rn-context-menu/index.js.map +0 -1
  202. package/lib/module/organisms/rn-context-menu/rn-context-menu.android.js +0 -101
  203. package/lib/module/organisms/rn-context-menu/rn-context-menu.android.js.map +0 -1
  204. package/lib/module/organisms/rn-context-menu/rn-context-menu.ios.js +0 -44
  205. package/lib/module/organisms/rn-context-menu/rn-context-menu.ios.js.map +0 -1
  206. package/lib/module/organisms/rn-context-menu/rn-context-menu.types.js +0 -4
  207. package/lib/module/organisms/rn-context-menu/rn-context-menu.types.js.map +0 -1
  208. package/lib/typescript/atoms/countdown/countdown-frame.d.ts +0 -17
  209. package/lib/typescript/atoms/countdown/countdown-units.d.ts +0 -5
  210. package/lib/typescript/atoms/countdown/countdown.d.ts +0 -1
  211. package/lib/typescript/atoms/countdown/countdown.ios.d.ts +0 -2
  212. package/lib/typescript/atoms/countdown/countdown.types.d.ts +0 -21
  213. package/lib/typescript/atoms/countdown/index.d.ts +0 -2
  214. package/lib/typescript/atoms/icon/icon-names.d.ts +0 -50
  215. package/lib/typescript/organisms/rn-context-menu/index.d.ts +0 -2
  216. package/lib/typescript/organisms/rn-context-menu/rn-context-menu.d.ts +0 -1
  217. package/lib/typescript/organisms/rn-context-menu/rn-context-menu.ios.d.ts +0 -2
  218. package/lib/typescript/organisms/rn-context-menu/rn-context-menu.types.d.ts +0 -33
  219. package/src/atoms/countdown/countdown-frame.tsx +0 -123
  220. package/src/atoms/countdown/countdown-units.ts +0 -37
  221. package/src/atoms/countdown/countdown.android.tsx +0 -49
  222. package/src/atoms/countdown/countdown.ios.tsx +0 -46
  223. package/src/atoms/countdown/countdown.types.ts +0 -24
  224. package/src/atoms/countdown/index.ts +0 -2
  225. package/src/atoms/date-picker/date-picker.tsx +0 -26
  226. package/src/atoms/icon/icon-names.ts +0 -53
  227. package/src/organisms/rn-context-menu/index.ts +0 -2
  228. package/src/organisms/rn-context-menu/rn-context-menu.android.tsx +0 -90
  229. package/src/organisms/rn-context-menu/rn-context-menu.ios.tsx +0 -47
  230. package/src/organisms/rn-context-menu/rn-context-menu.types.ts +0 -36
  231. /package/src/atoms/picker/{picker.tsx → picker.ios.tsx} +0 -0
@@ -1,11 +1,17 @@
1
- import {AlertDialog, Button, Column, Text as ComposeText} from '@expo/ui/jetpack-compose'
1
+ import {AlertDialog, Text as ComposeText, TextButton} from '@expo/ui/jetpack-compose'
2
2
  import {useAppTheme} from '@hzblj/kara-theme'
3
3
 
4
4
  import {useHostGuard} from '../../host-context'
5
5
  import type {AlertComponent, AlertProps} from './alert.types'
6
6
 
7
+ /**
8
+ * Material's `AlertDialog`, built from its slots — title, text, confirm and dismiss buttons. Content passed as plain
9
+ * children has no slot to land in, and the dialog draws empty. The actions mirror the iOS alert: the confirm label
10
+ * (in the destructive colour when asked) and Cancel.
11
+ */
7
12
  export const Alert: AlertComponent = ({
8
13
  confirmLabel = 'OK',
14
+ destructive = false,
9
15
  message,
10
16
  onConfirm,
11
17
  onDismiss,
@@ -20,18 +26,49 @@ export const Alert: AlertComponent = ({
20
26
  }
21
27
 
22
28
  return (
23
- <AlertDialog onDismissRequest={onDismiss}>
24
- <Column verticalArrangement={{spacedBy: 8}}>
29
+ <AlertDialog
30
+ colors={{
31
+ containerColor: theme.color('surface.primary'),
32
+ textContentColor: theme.color('content.secondary'),
33
+ titleContentColor: theme.color('content.primary'),
34
+ }}
35
+ onDismissRequest={onDismiss}
36
+ >
37
+ <AlertDialog.Title>
25
38
  <ComposeText color={theme.colorString('content.primary')} style={theme.typography.headingXs}>
26
39
  {title}
27
40
  </ComposeText>
28
- {message ? (
41
+ </AlertDialog.Title>
42
+ {message ? (
43
+ <AlertDialog.Text>
29
44
  <ComposeText color={theme.colorString('content.secondary')} style={theme.typography.body}>
30
45
  {message}
31
46
  </ComposeText>
32
- ) : null}
33
- <Button onClick={onConfirm ?? onDismiss}>{confirmLabel}</Button>
34
- </Column>
47
+ </AlertDialog.Text>
48
+ ) : null}
49
+ <AlertDialog.ConfirmButton>
50
+ <TextButton
51
+ onClick={() => {
52
+ // As on iOS, where any action closes the alert and reports it through `onDismiss`.
53
+ onConfirm?.()
54
+ onDismiss()
55
+ }}
56
+ >
57
+ <ComposeText
58
+ color={theme.colorString(destructive ? 'content.destructive' : 'content.accent')}
59
+ style={theme.typography.body}
60
+ >
61
+ {confirmLabel}
62
+ </ComposeText>
63
+ </TextButton>
64
+ </AlertDialog.ConfirmButton>
65
+ <AlertDialog.DismissButton>
66
+ <TextButton onClick={onDismiss}>
67
+ <ComposeText color={theme.colorString('content.accent')} style={theme.typography.body}>
68
+ Cancel
69
+ </ComposeText>
70
+ </TextButton>
71
+ </AlertDialog.DismissButton>
35
72
  </AlertDialog>
36
73
  )
37
74
  }
@@ -1,56 +1,90 @@
1
- import {Column, Text as ComposeText, DropdownMenu} from '@expo/ui/jetpack-compose'
2
- import {clickable, combinedClickable} from '@expo/ui/jetpack-compose/modifiers'
1
+ import {Text as ComposeText, DropdownMenu, DropdownMenuItem, Host} from '@expo/ui/jetpack-compose'
2
+ import {useSx} from '@hzblj/kara-sx'
3
3
  import {useAppTheme} from '@hzblj/kara-theme'
4
- import {useCallback, useMemo, useState} from 'react'
5
-
6
- import {useHostGuard} from '../../host-context'
4
+ import {useCallback, useState} from 'react'
5
+ import {Pressable, StyleSheet, View} from 'react-native'
7
6
  import type {MenuAction} from '../menu'
8
- import type {ContextMenuComponent, ContextMenuProps} from './context-menu.types'
7
+ import type {ContextMenuComponent, ContextMenuExclusionProps, ContextMenuProps} from './context-menu.types'
8
+
9
+ /**
10
+ * The menu's anchor: a point at the trigger's bottom start, so the dropdown opens just under it. The host lies outside
11
+ * the touch path — the dropdown is a popup window of its own, which takes its touches regardless.
12
+ */
13
+ const styles = StyleSheet.create({
14
+ anchor: {bottom: 0, height: 1, left: 0, position: 'absolute', width: 1},
15
+ })
9
16
 
10
- type MenuItemProps = {
17
+ type MenuRowProps = {
11
18
  action: MenuAction
12
19
  onDismiss: () => void
13
20
  }
14
21
 
15
- /** A row's `clickable` closes over its own action, so memoising it needs a component per row. */
16
- const MenuItem = ({action, onDismiss}: MenuItemProps) => {
22
+ /** A row's `onClick` closes over its own action, so memoising it needs a component per row. */
23
+ const MenuRow = ({action, onDismiss}: MenuRowProps) => {
17
24
  const theme = useAppTheme()
18
- const modifiers = useMemo(
19
- () => [
20
- clickable(() => {
21
- onDismiss()
22
- action.onPress()
23
- }),
24
- ],
25
- [action, onDismiss]
26
- )
25
+
26
+ const onClick = useCallback(() => {
27
+ onDismiss()
28
+ action.onPress()
29
+ }, [action, onDismiss])
27
30
 
28
31
  return (
29
- <ComposeText
30
- color={theme.colorString(action.destructive ? 'content.destructive' : 'content.primary')}
31
- modifiers={modifiers}
32
- style={theme.typography.body}
33
- >
34
- {action.label}
35
- </ComposeText>
32
+ <DropdownMenuItem onClick={onClick}>
33
+ <DropdownMenuItem.Text>
34
+ <ComposeText
35
+ color={theme.colorString(action.destructive ? 'content.destructive' : 'content.primary')}
36
+ style={theme.typography.body}
37
+ >
38
+ {action.label}
39
+ </ComposeText>
40
+ </DropdownMenuItem.Text>
41
+ </DropdownMenuItem>
36
42
  )
37
43
  }
38
44
 
39
- export const ContextMenu: ContextMenuComponent = ({actions, children}: ContextMenuProps) => {
40
- useHostGuard('ContextMenu')
41
-
45
+ /**
46
+ * The trigger stays a plain React Native subtree; a long press on it expands a Compose `DropdownMenu` anchored under
47
+ * it. The host behind the menu mounts on the first long press and stays — a list of triggers that are only scrolled
48
+ * past never builds one. `preview` and `onPreviewPress` are iOS-only: Material has no lifted preview.
49
+ */
50
+ const ContextMenuRoot = ({actions, children, testID, ...props}: ContextMenuProps) => {
51
+ const {onPreviewPress: _onPreviewPress, preview: _preview, ...sx} = props
52
+ const theme = useAppTheme()
53
+ const style = useSx(sx)
54
+ const [isHostMounted, setIsHostMounted] = useState(false)
42
55
  const [expanded, setExpanded] = useState(false)
56
+
57
+ const open = useCallback(() => {
58
+ setIsHostMounted(true)
59
+ setExpanded(true)
60
+ }, [])
61
+
43
62
  const dismiss = useCallback(() => setExpanded(false), [])
44
- const triggerModifiers = useMemo(() => [combinedClickable({onLongClick: () => setExpanded(true)})], [])
45
63
 
46
64
  return (
47
- <Column>
48
- <Column modifiers={triggerModifiers}>{children}</Column>
49
- <DropdownMenu expanded={expanded} onDismissRequest={dismiss}>
50
- {actions.map(action => (
51
- <MenuItem action={action} key={action.label} onDismiss={dismiss} />
52
- ))}
53
- </DropdownMenu>
54
- </Column>
65
+ <Pressable onLongPress={open} style={style} testID={testID}>
66
+ {children}
67
+ {isHostMounted ? (
68
+ <Host
69
+ colorScheme={theme.dark ? 'dark' : 'light'}
70
+ pointerEvents="none"
71
+ seedColor={theme.materialSeed}
72
+ style={styles.anchor}
73
+ >
74
+ <DropdownMenu expanded={expanded} onDismissRequest={dismiss}>
75
+ <DropdownMenu.Items>
76
+ {actions.map(action => (
77
+ <MenuRow action={action} key={action.label} onDismiss={dismiss} />
78
+ ))}
79
+ </DropdownMenu.Items>
80
+ </DropdownMenu>
81
+ </Host>
82
+ ) : null}
83
+ </Pressable>
55
84
  )
56
85
  }
86
+
87
+ /** A touchable inside the trigger already claims its own long press on Android, so this only has to group. */
88
+ const Exclusion = ({children}: ContextMenuExclusionProps) => <View>{children}</View>
89
+
90
+ export const ContextMenu: ContextMenuComponent = Object.assign(ContextMenuRoot, {Exclusion})
@@ -1,24 +1,46 @@
1
- import {Button, ContextMenu as SwiftUIContextMenu} from '@expo/ui/swift-ui'
1
+ import {iconResourceName} from '@hzblj/kara-native'
2
+ import {
3
+ type ContextMenuItem,
4
+ ContextMenu as NativeContextMenu,
5
+ ContextMenuExclusion as NativeContextMenuExclusion,
6
+ } from '@hzblj/kara-native/swift-ui'
7
+ import {useSx} from '@hzblj/kara-sx'
8
+ import {useCallback, useMemo} from 'react'
9
+ import type {ContextMenuComponent, ContextMenuExclusionProps, ContextMenuProps} from './context-menu.types'
2
10
 
3
- import {useHostGuard} from '../../host-context'
4
- import type {ContextMenuComponent, ContextMenuProps} from './context-menu.types'
11
+ const ContextMenuRoot = ({actions, children, onPreviewPress, preview, testID, ...sx}: ContextMenuProps) => {
12
+ const style = useSx(sx)
5
13
 
6
- export const ContextMenu: ContextMenuComponent = ({actions, children}: ContextMenuProps) => {
7
- useHostGuard('ContextMenu')
14
+ /** The native side hands back an id; the index is the one identity every action has. */
15
+ const items = useMemo<ContextMenuItem[]>(
16
+ () =>
17
+ actions.map((action, index) => ({
18
+ destructive: action.destructive,
19
+ id: String(index),
20
+ image: action.icon ? iconResourceName(action.icon) : undefined,
21
+ title: action.label,
22
+ })),
23
+ [actions]
24
+ )
25
+
26
+ const onSelectItem = useCallback((id: string) => actions[Number(id)]?.onPress(), [actions])
8
27
 
9
28
  return (
10
- <SwiftUIContextMenu>
11
- <SwiftUIContextMenu.Items>
12
- {actions.map(action => (
13
- <Button
14
- key={action.label}
15
- label={action.label}
16
- onPress={action.onPress}
17
- role={action.destructive ? 'destructive' : 'default'}
18
- />
19
- ))}
20
- </SwiftUIContextMenu.Items>
21
- <SwiftUIContextMenu.Trigger>{children}</SwiftUIContextMenu.Trigger>
22
- </SwiftUIContextMenu>
29
+ <NativeContextMenu
30
+ items={items}
31
+ onPreviewPress={onPreviewPress}
32
+ onSelectItem={onSelectItem}
33
+ preview={preview}
34
+ style={style}
35
+ testID={testID}
36
+ >
37
+ {children}
38
+ </NativeContextMenu>
23
39
  )
24
40
  }
41
+
42
+ const Exclusion = ({children}: ContextMenuExclusionProps) => (
43
+ <NativeContextMenuExclusion>{children}</NativeContextMenuExclusion>
44
+ )
45
+
46
+ export const ContextMenu: ContextMenuComponent = Object.assign(ContextMenuRoot, {Exclusion})
@@ -1,10 +1,36 @@
1
+ import type {ResponsiveViewSx} from '@hzblj/kara-sx'
1
2
  import type {ReactNode} from 'react'
2
-
3
3
  import type {MenuAction} from '../menu'
4
4
 
5
- export type ContextMenuProps = {
5
+ /**
6
+ * A context menu on a React Native subtree — a card in a list, say — opened by a long press. iOS: a
7
+ * `UIContextMenuInteraction`, with an optional React Native `preview` lifted while the menu is open and a callback for
8
+ * a tap on it. Android: a Material dropdown under the subtree. The trigger is ordinary React Native layout, so it
9
+ * needs no `Screen` or `Island`.
10
+ */
11
+ export type ContextMenuProps = ResponsiveViewSx & {
6
12
  actions: MenuAction[]
13
+ /** The trigger, laid out as usual. */
7
14
  children: ReactNode
15
+ /** iOS: the user tapped the lifted preview, committing to it — open the detail the card stands for. */
16
+ onPreviewPress?: () => void
17
+ /**
18
+ * iOS: lifted in place of the trigger while the menu is open — a larger version of the card, say. A snapshot of the
19
+ * trigger when unset. Mounted only once a finger has rested on the trigger, so a list of cards does not pay for
20
+ * every preview up front.
21
+ */
22
+ preview?: ReactNode
23
+ testID?: string
8
24
  }
9
25
 
10
- export type ContextMenuComponent = (props: ContextMenuProps) => ReactNode
26
+ export type ContextMenuExclusionProps = {
27
+ children: ReactNode
28
+ }
29
+
30
+ export type ContextMenuComponent = ((props: ContextMenuProps) => ReactNode) & {
31
+ /**
32
+ * A region of the trigger that must not open the menu — a button inside the card. On iOS the interaction covers the
33
+ * whole trigger, so a long press on that button would otherwise open the card's menu.
34
+ */
35
+ Exclusion: (props: ContextMenuExclusionProps) => ReactNode
36
+ }
@@ -7,6 +7,7 @@ import {
7
7
  import {useAppTheme} from '@hzblj/kara-theme'
8
8
  import {useCallback, useState} from 'react'
9
9
  import {Icon} from '../../atoms/icon'
10
+ import {Glyph} from '../../glyph'
10
11
  import {useHostGuard} from '../../host-context'
11
12
  import type {MenuAction} from '../menu'
12
13
  import type {FabMenuComponent, FabMenuProps} from './fab-menu.types'
@@ -46,7 +47,7 @@ const FabMenuRow = ({action, onSelect}: FabMenuRowProps) => {
46
47
  * content — rather than a row of its own. The pills take the button's colours, so the menu reads as the button
47
48
  * unfolding rather than a second surface.
48
49
  */
49
- export const FabMenu: FabMenuComponent = ({actions, icon = 'add'}: FabMenuProps) => {
50
+ export const FabMenu: FabMenuComponent = ({actions, icon}: FabMenuProps) => {
50
51
  useHostGuard('FabMenu')
51
52
 
52
53
  const theme = useAppTheme()
@@ -66,7 +67,7 @@ export const FabMenu: FabMenuComponent = ({actions, icon = 'add'}: FabMenuProps)
66
67
  onCheckedChange={setExpanded}
67
68
  >
68
69
  <ToggleFloatingActionButton.Icon>
69
- <Icon color="content.on.bg" name={icon} />
70
+ {icon ? <Icon color="content.on.bg" name={icon} /> : <Glyph color="content.on.bg" name="plus" />}
70
71
  </ToggleFloatingActionButton.Icon>
71
72
  </ToggleFloatingActionButton>
72
73
  </FloatingActionButtonMenu.Button>
@@ -1,15 +1,16 @@
1
- import {Button, Menu} from '@expo/ui/swift-ui'
2
- import type {SFSymbol} from 'sf-symbols-typescript'
1
+ import {Button, Image, Label, Menu} from '@expo/ui/swift-ui'
2
+ import {iconResourceName} from '@hzblj/kara-native'
3
3
 
4
- import {Icon, SF_SYMBOLS} from '../../atoms/icon'
4
+ import {Icon} from '../../atoms/icon'
5
5
  import {VStack} from '../../atoms/v-stack'
6
+ import {Glyph} from '../../glyph'
6
7
  import {useHostGuard} from '../../host-context'
7
8
  import type {FabMenuComponent, FabMenuProps} from './fab-menu.types'
8
9
 
9
10
  /** Matches `Fab`, so a screen can switch between the two without the button changing shape. */
10
11
  const FAB_SIZE = 56
11
12
 
12
- export const FabMenu: FabMenuComponent = ({actions, icon = 'add', testID}: FabMenuProps) => {
13
+ export const FabMenu: FabMenuComponent = ({actions, icon, testID}: FabMenuProps) => {
13
14
  useHostGuard('FabMenu')
14
15
 
15
16
  return (
@@ -24,19 +25,19 @@ export const FabMenu: FabMenuComponent = ({actions, icon = 'add', testID}: FabMe
24
25
  shadow="btn.accent"
25
26
  w={FAB_SIZE}
26
27
  >
27
- <Icon color="content.on.bg" name={icon} />
28
+ {icon ? <Icon color="content.on.bg" name={icon} /> : <Glyph color="content.on.bg" name="plus" />}
28
29
  </VStack>
29
30
  }
30
31
  testID={testID}
31
32
  >
32
33
  {actions.map(action => (
33
- <Button
34
- key={action.label}
35
- label={action.label}
36
- onPress={action.onPress}
37
- role={action.destructive ? 'destructive' : 'default'}
38
- systemImage={action.icon ? (SF_SYMBOLS[action.icon] as SFSymbol) : undefined}
39
- />
34
+ <Button key={action.label} onPress={action.onPress} role={action.destructive ? 'destructive' : 'default'}>
35
+ {/* A menu row is the system's Label: our icon is its image, from the asset catalog, and UIKit sizes it. */}
36
+ <Label
37
+ icon={action.icon ? <Image assetName={iconResourceName(action.icon)} /> : undefined}
38
+ title={action.label}
39
+ />
40
+ </Button>
40
41
  ))}
41
42
  </Menu>
42
43
  )
@@ -10,7 +10,7 @@ import type {MenuAction} from '../menu'
10
10
  */
11
11
  export type FabMenuProps = {
12
12
  actions: MenuAction[]
13
- /** The closed button's icon. `add` by default. */
13
+ /** The closed button's icon — one of the app's. A plus by default. */
14
14
  icon?: IconName
15
15
  testID?: string
16
16
  }
@@ -21,7 +21,7 @@ const FormSection = ({children, footer, title}: FormSectionProps) => (
21
21
  {title}
22
22
  </Text>
23
23
  ) : null}
24
- <Card>{children}</Card>
24
+ <Card wide>{children}</Card>
25
25
  {footer ? (
26
26
  <Text color="content.tertiary" variant="footnote">
27
27
  {footer}
@@ -1,5 +1,10 @@
1
1
  import type {ReactNode} from 'react'
2
2
 
3
+ /**
4
+ * A settings form — SwiftUI `Form` with its grouped sections, Material cards on Android. On iOS it scrolls like a
5
+ * `List`, so it needs a height from outside: a `Screen`, or an `Island` with `matchContents={false}` and a height. In
6
+ * an island that sizes itself to its content it has no height and shows nothing.
7
+ */
3
8
  export type FormProps = {
4
9
  children: ReactNode
5
10
  testID?: string
@@ -4,7 +4,6 @@ export * from './context-menu'
4
4
  export * from './fab-menu'
5
5
  export * from './form'
6
6
  export * from './menu'
7
- export * from './rn-context-menu'
8
7
  export * from './sheet'
9
8
  export * from './snackbar'
10
9
  export * from './swipe-actions'
@@ -1,12 +1,10 @@
1
- import {Text as ComposeText, DropdownMenu} from '@expo/ui/jetpack-compose'
2
- import {clickable} from '@expo/ui/jetpack-compose/modifiers'
1
+ import {Text as ComposeText, DropdownMenu, DropdownMenuItem} from '@expo/ui/jetpack-compose'
3
2
  import {useAppTheme} from '@hzblj/kara-theme'
4
- import {useCallback, useMemo, useState} from 'react'
3
+ import {useCallback, useState} from 'react'
5
4
  import {HStack} from '../../atoms/h-stack'
6
5
  import {Icon} from '../../atoms/icon'
7
- import {Spacer} from '../../atoms/spacer'
8
6
  import {Text} from '../../atoms/text'
9
- import {VStack} from '../../atoms/v-stack'
7
+ import {Glyph} from '../../glyph'
10
8
  import {useHostGuard} from '../../host-context'
11
9
  import type {MenuAction, MenuComponent, MenuProps} from './menu.types'
12
10
 
@@ -15,48 +13,56 @@ type MenuItemProps = {
15
13
  onDismiss: () => void
16
14
  }
17
15
 
18
- /** A row's `clickable` closes over its own action, so memoising it needs a component per row. */
19
16
  const MenuItem = ({action, onDismiss}: MenuItemProps) => {
20
17
  const theme = useAppTheme()
21
- const modifiers = useMemo(
22
- () => [
23
- clickable(() => {
24
- onDismiss()
25
- action.onPress()
26
- }),
27
- ],
28
- [action, onDismiss]
29
- )
18
+ const color = action.destructive ? 'content.destructive' : 'content.primary'
30
19
 
31
20
  return (
32
- <ComposeText
33
- color={theme.colorString(action.destructive ? 'content.destructive' : 'content.primary')}
34
- modifiers={modifiers}
35
- style={theme.typography.body}
21
+ <DropdownMenuItem
22
+ elementColors={{leadingIconColor: theme.color(color), textColor: theme.color(color)}}
23
+ onClick={() => {
24
+ onDismiss()
25
+ action.onPress()
26
+ }}
36
27
  >
37
- {action.label}
38
- </ComposeText>
28
+ <DropdownMenuItem.Text>
29
+ <ComposeText color={theme.colorString(color)} style={theme.typography.body}>
30
+ {action.label}
31
+ </ComposeText>
32
+ </DropdownMenuItem.Text>
33
+ {action.icon ? (
34
+ <DropdownMenuItem.LeadingIcon>
35
+ <Icon color={color} name={action.icon} size="s20" />
36
+ </DropdownMenuItem.LeadingIcon>
37
+ ) : null}
38
+ </DropdownMenuItem>
39
39
  )
40
40
  }
41
41
 
42
+ /**
43
+ * Material's dropdown menu, anchored to its trigger. The items live in the `Items` slot, which Compose presents in
44
+ * its own popup — anywhere else they would be laid out in place, on top of the trigger.
45
+ */
42
46
  export const Menu: MenuComponent = ({actions, label}: MenuProps) => {
43
47
  useHostGuard('Menu')
44
48
 
49
+ const theme = useAppTheme()
45
50
  const [expanded, setExpanded] = useState(false)
46
51
  const dismiss = useCallback(() => setExpanded(false), [])
47
52
 
48
53
  return (
49
- <VStack>
50
- <HStack align="center" onPress={() => setExpanded(true)} spacing="s4">
51
- <Text color="content.accent">{label}</Text>
52
- <Icon color="content.accent" name="chevronDown" size="s16" />
53
- </HStack>
54
- <DropdownMenu expanded={expanded} onDismissRequest={dismiss}>
54
+ <DropdownMenu color={theme.color('surface.primary')} expanded={expanded} onDismissRequest={dismiss}>
55
+ <DropdownMenu.Trigger>
56
+ <HStack align="center" onPress={() => setExpanded(true)} spacing="s4">
57
+ <Text color="content.accent">{label}</Text>
58
+ <Glyph color="content.accent" name="chevronDown" size="s16" />
59
+ </HStack>
60
+ </DropdownMenu.Trigger>
61
+ <DropdownMenu.Items>
55
62
  {actions.map(action => (
56
63
  <MenuItem action={action} key={action.label} onDismiss={dismiss} />
57
64
  ))}
58
- </DropdownMenu>
59
- <Spacer min="s0" />
60
- </VStack>
65
+ </DropdownMenu.Items>
66
+ </DropdownMenu>
61
67
  )
62
68
  }
@@ -1,115 +0,0 @@
1
- "use strict";
2
-
3
- import { MAX_FONT_SCALE, useAppTheme } from '@hzblj/kara-theme';
4
- import { useEffect, useMemo, useState } from 'react';
5
- import { StyleSheet, Text, useWindowDimensions, View } from 'react-native';
6
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
- const SECOND_MS = 1000;
8
- const HOUR = 3600;
9
- const DAY = 86_400;
10
- const DIVIDER = ' / ';
11
-
12
- /** Wakes a beat after the shape changes, so the frame never reads the boundary second twice. */
13
- const SHAPE_CHANGE_SLACK_MS = 50;
14
- const remainingSeconds = endsAt => Math.max(0, Math.round((endsAt - Date.now()) / SECOND_MS));
15
- const countdownShape = endsAt => {
16
- const remaining = remainingSeconds(endsAt);
17
- return {
18
- days: Math.floor(remaining / DAY),
19
- hasHours: remaining >= HOUR
20
- };
21
- };
22
-
23
- /** When the next segment drops out — the last day, then the last hour — or `null` once only minutes are left. */
24
- const nextShapeChange = (endsAt, {
25
- days,
26
- hasHours
27
- }) => {
28
- if (days > 0) {
29
- return endsAt - (days * DAY - 0.5) * SECOND_MS;
30
- }
31
- if (hasHours) {
32
- return endsAt - (HOUR - 0.5) * SECOND_MS;
33
- }
34
- return null;
35
- };
36
-
37
- /** The countdown's shape changes twice at most, so this re-renders twice at most — never once a second. */
38
- const useCountdownShape = endsAt => {
39
- const [shape, setShape] = useState(() => countdownShape(endsAt));
40
- useEffect(() => {
41
- let timeout;
42
- const update = () => {
43
- const next = countdownShape(endsAt);
44
- setShape(current => current.days === next.days && current.hasHours === next.hasHours ? current : next);
45
- const changesAt = nextShapeChange(endsAt, next);
46
- if (changesAt !== null) {
47
- timeout = setTimeout(update, Math.max(0, changesAt - Date.now()) + SHAPE_CHANGE_SLACK_MS);
48
- }
49
- };
50
- update();
51
- return () => clearTimeout(timeout);
52
- }, [endsAt]);
53
- return shape;
54
- };
55
-
56
- /** The widest string the countdown can show in its current shape, with zeros standing in for the digits. */
57
- const countdownTemplate = ({
58
- days,
59
- hasHours
60
- }, [day, hour, minute, second]) => {
61
- let template = '';
62
- if (days > 0) {
63
- template += `${'0'.repeat(String(days).length)}${day}${DIVIDER}`;
64
- }
65
- if (hasHours) {
66
- template += `00${hour}${DIVIDER}`;
67
- }
68
- return `${template}00${minute}${DIVIDER}00${second}`;
69
- };
70
- const styles = StyleSheet.create({
71
- template: {
72
- opacity: 0
73
- }
74
- });
75
- /**
76
- * What gives the native countdown its size: an invisible React Native text of exactly the shape it draws, set the way
77
- * `RnText` sets the same variant, so the countdown takes the space any text of that variant would — and the native view
78
- * laid over it has to match it to the point. Accessibility reads the native view, never this.
79
- */
80
- export const CountdownFrame = ({
81
- children,
82
- endsAt,
83
- testID,
84
- units,
85
- variant
86
- }) => {
87
- const theme = useAppTheme();
88
- const {
89
- fontScale
90
- } = useWindowDimensions();
91
- const shape = useCountdownShape(endsAt);
92
- const template = useMemo(() => countdownTemplate(shape, units), [shape, units]);
93
- const style = theme.typography[variant];
94
-
95
- /** As in `RnText`: React Native grows the glyphs but not the line, so the line grows here by the same capped factor. */
96
- const lineHeight = style.lineHeight * Math.min(fontScale, MAX_FONT_SCALE);
97
- return /*#__PURE__*/_jsxs(View, {
98
- testID: testID,
99
- children: [/*#__PURE__*/_jsx(View, {
100
- accessibilityElementsHidden: true,
101
- importantForAccessibility: "no-hide-descendants",
102
- style: styles.template,
103
- children: /*#__PURE__*/_jsx(Text, {
104
- maxFontSizeMultiplier: MAX_FONT_SCALE,
105
- numberOfLines: 1,
106
- style: [style, {
107
- fontVariant: ['tabular-nums'],
108
- lineHeight
109
- }],
110
- children: template
111
- })
112
- }), children]
113
- });
114
- };
115
- //# sourceMappingURL=countdown-frame.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["MAX_FONT_SCALE","useAppTheme","useEffect","useMemo","useState","StyleSheet","Text","useWindowDimensions","View","jsx","_jsx","jsxs","_jsxs","SECOND_MS","HOUR","DAY","DIVIDER","SHAPE_CHANGE_SLACK_MS","remainingSeconds","endsAt","Math","max","round","Date","now","countdownShape","remaining","days","floor","hasHours","nextShapeChange","useCountdownShape","shape","setShape","timeout","update","next","current","changesAt","setTimeout","clearTimeout","countdownTemplate","day","hour","minute","second","template","repeat","String","length","styles","create","opacity","CountdownFrame","children","testID","units","variant","theme","fontScale","style","typography","lineHeight","min","accessibilityElementsHidden","importantForAccessibility","maxFontSizeMultiplier","numberOfLines","fontVariant"],"sourceRoot":"../../../../src","sources":["atoms/countdown/countdown-frame.tsx"],"mappings":"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","ignoreList":[]}