@helpwave/hightide-native 0.0.8 → 0.1.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 (125) hide show
  1. package/README.md +4 -4
  2. package/package.json +8 -19
  3. package/src/components/card/Card.tsx +41 -0
  4. package/src/components/card/index.ts +1 -0
  5. package/src/components/chat/ChatAttachmentMessageBubble.tsx +246 -0
  6. package/src/components/chat/ChatConversationRow.tsx +21 -16
  7. package/src/components/chat/ChatDateDivider.tsx +2 -2
  8. package/src/components/chat/ChatMessageBubble.tsx +62 -52
  9. package/src/components/chat/ChatMessageComposer.tsx +16 -14
  10. package/src/components/chat/ChatQuickReplyChip.tsx +41 -43
  11. package/src/components/chat/ChatSystemLine.tsx +14 -7
  12. package/src/components/chat/ChatThreadHeader.tsx +60 -6
  13. package/src/components/chat/index.ts +1 -2
  14. package/src/components/index.ts +3 -1
  15. package/src/components/layout/Divider.tsx +60 -0
  16. package/src/components/layout/index.ts +1 -0
  17. package/src/components/list/ListActionItem.tsx +99 -0
  18. package/src/components/list/ListItem.tsx +96 -0
  19. package/src/components/list/ListNavigationItem.tsx +102 -0
  20. package/src/components/list/index.ts +3 -0
  21. package/src/components/user-interaction/Button.tsx +80 -31
  22. package/src/components/user-interaction/Checkbox.tsx +72 -32
  23. package/src/components/user-interaction/IconButton.tsx +68 -30
  24. package/src/components/user-interaction/Input.tsx +61 -14
  25. package/src/components/user-interaction/MultiSelect.tsx +142 -75
  26. package/src/components/user-interaction/SearchBar.tsx +222 -0
  27. package/src/components/user-interaction/Select.tsx +129 -58
  28. package/src/components/user-interaction/Switch.tsx +110 -80
  29. package/src/components/user-interaction/ThemedPressable.tsx +136 -0
  30. package/src/components/user-interaction/index.ts +2 -0
  31. package/src/components/visualization-and-display/Avatar.tsx +112 -93
  32. package/src/components/visualization-and-display/Chip.tsx +22 -22
  33. package/src/components/visualization-and-display/ThemedIcon.tsx +72 -0
  34. package/src/components/visualization-and-display/ThemedText.tsx +38 -0
  35. package/src/components/visualization-and-display/index.ts +2 -1
  36. package/src/global-contexts/HightideProvider.tsx +19 -10
  37. package/src/global-contexts/content-theme/ContentThemeContext.ts +22 -0
  38. package/src/global-contexts/content-theme/ContentThemeProvider.tsx +32 -0
  39. package/src/global-contexts/content-theme/index.ts +2 -0
  40. package/src/global-contexts/debug/forward-exports.ts +10 -0
  41. package/src/global-contexts/debug/index.ts +1 -0
  42. package/src/global-contexts/index.ts +2 -0
  43. package/src/global-contexts/theme/ThemeProvider.tsx +7 -1
  44. package/src/icons/HightideIconRegistry.ts +33 -0
  45. package/src/icons/index.ts +2 -0
  46. package/src/icons/types.ts +11 -0
  47. package/src/theme/adapters/container-adapter.ts +323 -0
  48. package/src/theme/adapters/defined.ts +11 -0
  49. package/src/theme/adapters/icon-style-adapter.ts +10 -0
  50. package/src/theme/adapters/index.ts +4 -0
  51. package/src/theme/adapters/text-style-adapter.ts +16 -0
  52. package/src/theme/index.ts +1 -0
  53. package/src/theme/resolvers/avatar.ts +385 -186
  54. package/src/theme/resolvers/button.ts +50 -82
  55. package/src/theme/resolvers/card.ts +27 -0
  56. package/src/theme/resolvers/chat/attachment-message-bubble.ts +163 -0
  57. package/src/theme/resolvers/chat/chat-theme.ts +69 -0
  58. package/src/theme/resolvers/chat/conversation-list.ts +35 -0
  59. package/src/theme/resolvers/chat/conversation-row.ts +64 -0
  60. package/src/theme/resolvers/chat/date-divider.ts +31 -0
  61. package/src/theme/resolvers/chat/index.ts +11 -0
  62. package/src/theme/resolvers/chat/message-bubble.ts +57 -0
  63. package/src/theme/resolvers/chat/message-composer.ts +38 -0
  64. package/src/theme/resolvers/chat/message-list.ts +27 -0
  65. package/src/theme/resolvers/chat/quick-reply-chip.ts +80 -0
  66. package/src/theme/resolvers/chat/system-line.ts +41 -0
  67. package/src/theme/resolvers/chat/thread-header.ts +85 -0
  68. package/src/theme/resolvers/checkbox.ts +87 -74
  69. package/src/theme/resolvers/chip.ts +30 -67
  70. package/src/theme/resolvers/colorScheme.ts +136 -0
  71. package/src/theme/resolvers/divider.ts +34 -0
  72. package/src/theme/resolvers/icon.ts +21 -0
  73. package/src/theme/resolvers/iconButton.ts +45 -61
  74. package/src/theme/resolvers/index.ts +7 -2
  75. package/src/theme/resolvers/input.ts +64 -42
  76. package/src/theme/resolvers/listItem.ts +115 -0
  77. package/src/theme/resolvers/multiSelect.ts +159 -84
  78. package/src/theme/resolvers/searchBar.ts +121 -0
  79. package/src/theme/resolvers/select.ts +141 -66
  80. package/src/theme/resolvers/switch.ts +59 -53
  81. package/src/theme/resolvers/themedPressable.ts +54 -0
  82. package/src/theme/themes/createHightideTheme.ts +214 -51
  83. package/src/theme/themes/hightideThemes.ts +10 -6
  84. package/src/theme/types/color.ts +2 -59
  85. package/src/theme/types/components/avatar.ts +37 -36
  86. package/src/theme/types/components/button.ts +13 -14
  87. package/src/theme/types/components/card.ts +9 -0
  88. package/src/theme/types/components/chat.ts +124 -103
  89. package/src/theme/types/components/checkbox.ts +11 -12
  90. package/src/theme/types/components/chip.ts +8 -14
  91. package/src/theme/types/components/divider.ts +18 -0
  92. package/src/theme/types/components/hightide.ts +41 -42
  93. package/src/theme/types/components/iconButton.ts +19 -14
  94. package/src/theme/types/components/index.ts +5 -1
  95. package/src/theme/types/components/input.ts +17 -8
  96. package/src/theme/types/components/listItem.ts +86 -0
  97. package/src/theme/types/components/multiSelect.ts +14 -12
  98. package/src/theme/types/components/searchBar.ts +28 -0
  99. package/src/theme/types/components/select.ts +21 -9
  100. package/src/theme/types/components/switch.ts +12 -5
  101. package/src/theme/types/components/themedPressable.ts +31 -0
  102. package/src/theme/types/decoration.ts +1 -11
  103. package/src/theme/types/icongraphy.ts +8 -0
  104. package/src/theme/types/index.ts +2 -0
  105. package/src/theme/types/layout.ts +25 -57
  106. package/src/theme/types/resolver.ts +54 -1
  107. package/src/theme/types/semantics.ts +72 -0
  108. package/src/theme/types/theme.ts +26 -13
  109. package/src/theme/types/typography.ts +16 -17
  110. package/src/utils/hitBoxOverlay.ts +44 -0
  111. package/src/utils/index.ts +2 -0
  112. package/src/utils/minimumTouchTargetHitSlop.ts +68 -0
  113. package/src/components/chat/ChatAttachmentCard.tsx +0 -110
  114. package/src/components/chat/ChatMessageCard.tsx +0 -118
  115. package/src/components/menu/Menu.tsx +0 -61
  116. package/src/components/menu/MenuActionItem.tsx +0 -90
  117. package/src/components/menu/MenuItem.tsx +0 -73
  118. package/src/components/menu/MenuNavigationItem.tsx +0 -90
  119. package/src/components/menu/index.ts +0 -4
  120. package/src/components/visualization-and-display/Icon.tsx +0 -27
  121. package/src/storybook/helper.tsx +0 -36
  122. package/src/theme/resolvers/chat.ts +0 -418
  123. package/src/theme/resolvers/coloring.ts +0 -94
  124. package/src/theme/resolvers/menu.ts +0 -120
  125. package/src/theme/types/components/menu.ts +0 -57
@@ -0,0 +1,44 @@
1
+ import type { Insets, PressableProps, ViewStyle } from 'react-native'
2
+
3
+ export const resolveHitSlopInsets = (hitSlop: PressableProps['hitSlop']): Insets => {
4
+ if (hitSlop === undefined || hitSlop === null) {
5
+ return {
6
+ top: 0,
7
+ right: 0,
8
+ bottom: 0,
9
+ left: 0,
10
+ }
11
+ }
12
+
13
+ if (typeof hitSlop === 'number') {
14
+ return {
15
+ top: hitSlop,
16
+ right: hitSlop,
17
+ bottom: hitSlop,
18
+ left: hitSlop,
19
+ }
20
+ }
21
+
22
+ return {
23
+ top: hitSlop.top ?? 0,
24
+ right: hitSlop.right ?? 0,
25
+ bottom: hitSlop.bottom ?? 0,
26
+ left: hitSlop.left ?? 0,
27
+ }
28
+ }
29
+
30
+ export const createHitBoxOverlayStyle = (
31
+ hitSlop: PressableProps['hitSlop'],
32
+ color: string
33
+ ): ViewStyle => {
34
+ const insets = resolveHitSlopInsets(hitSlop)
35
+
36
+ return {
37
+ position: 'absolute',
38
+ top: -(insets.top ?? 0),
39
+ right: -(insets.right ?? 0),
40
+ bottom: -(insets.bottom ?? 0),
41
+ left: -(insets.left ?? 0),
42
+ backgroundColor: color,
43
+ }
44
+ }
@@ -0,0 +1,2 @@
1
+ export * from './hitBoxOverlay'
2
+ export * from './minimumTouchTargetHitSlop'
@@ -0,0 +1,68 @@
1
+ import { useCallback, useState } from 'react'
2
+ import type {
3
+ Insets,
4
+ LayoutChangeEvent,
5
+ PressableProps
6
+ } from 'react-native'
7
+
8
+ export const computeMinimumTouchTargetHitSlop = (
9
+ layout: { width: number, height: number },
10
+ touchTargetSize: number
11
+ ): Insets => {
12
+ const horizontal = Math.max(0, touchTargetSize - layout.width) / 2
13
+ const vertical = Math.max(0, touchTargetSize - layout.height) / 2
14
+
15
+ return {
16
+ top: vertical,
17
+ bottom: vertical,
18
+ left: horizontal,
19
+ right: horizontal,
20
+ }
21
+ }
22
+
23
+ const areInsetsEqual = (a: Insets | undefined, b: Insets): boolean => (
24
+ a !== undefined
25
+ && a.top === b.top
26
+ && a.bottom === b.bottom
27
+ && a.left === b.left
28
+ && a.right === b.right
29
+ )
30
+
31
+ export type UseMinimumTouchTargetHitSlopOptions = {
32
+ touchTargetSize: number,
33
+ hitSlop?: PressableProps['hitSlop'],
34
+ onLayout?: PressableProps['onLayout'],
35
+ }
36
+
37
+ export type UseMinimumTouchTargetHitSlopResult = {
38
+ hitSlop: PressableProps['hitSlop'],
39
+ onLayout: NonNullable<PressableProps['onLayout']>,
40
+ }
41
+
42
+ export const useMinimumTouchTargetHitSlop = ({
43
+ touchTargetSize,
44
+ hitSlop: providedHitSlop,
45
+ onLayout: providedOnLayout,
46
+ }: UseMinimumTouchTargetHitSlopOptions): UseMinimumTouchTargetHitSlopResult => {
47
+ const [autoHitSlop, setAutoHitSlop] = useState<Insets | undefined>()
48
+
49
+ const onLayout = useCallback((event: LayoutChangeEvent) => {
50
+ providedOnLayout?.(event)
51
+
52
+ if (providedHitSlop !== undefined) {
53
+ return
54
+ }
55
+
56
+ const { width, height } = event.nativeEvent.layout
57
+ const nextHitSlop = computeMinimumTouchTargetHitSlop({ width, height }, touchTargetSize)
58
+
59
+ setAutoHitSlop((prev) => (
60
+ areInsetsEqual(prev, nextHitSlop) ? prev : nextHitSlop
61
+ ))
62
+ }, [providedHitSlop, providedOnLayout, touchTargetSize])
63
+
64
+ return {
65
+ hitSlop: providedHitSlop !== undefined ? providedHitSlop : autoHitSlop,
66
+ onLayout,
67
+ }
68
+ }
@@ -1,110 +0,0 @@
1
- import {
2
- useMemo,
3
- type ReactNode
4
- } from 'react'
5
- import {
6
- Text,
7
- View,
8
- type StyleProp,
9
- type ViewProps,
10
- type ViewStyle
11
- } from 'react-native'
12
- import {
13
- Download,
14
- FileText
15
- } from 'lucide-react-native'
16
-
17
- import { IconButton } from '../user-interaction/IconButton'
18
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
19
- import type {
20
- ChatAttachmentCardMetadataStyle,
21
- ChatAttachmentCardNameStyle,
22
- ChatAttachmentCardState,
23
- ChatAttachmentCardStyle,
24
- ChatMessageDirection
25
- } from '../../theme/types/components/chat'
26
- import type { StyleOverwrite } from '../../theme/types/resolver'
27
-
28
- export type ChatAttachmentCardProps = Omit<ViewProps, 'style'> & {
29
- name: ReactNode,
30
- metadata?: ReactNode,
31
- icon?: ReactNode,
32
- direction?: ChatMessageDirection,
33
- downloadLabel?: string,
34
- onDownload?: () => void,
35
- style?: StyleProp<ViewStyle>,
36
- cardStyle?: StyleOverwrite<ChatAttachmentCardState, ChatAttachmentCardStyle>,
37
- nameStyle?: StyleOverwrite<Record<string, never>, ChatAttachmentCardNameStyle>,
38
- metadataStyle?: StyleOverwrite<Record<string, never>, ChatAttachmentCardMetadataStyle>,
39
- }
40
-
41
- export const ChatAttachmentCard = ({
42
- name,
43
- metadata,
44
- icon,
45
- direction = 'incoming',
46
- downloadLabel = 'Download',
47
- onDownload,
48
- style,
49
- cardStyle,
50
- nameStyle,
51
- metadataStyle,
52
- ...props
53
- }: ChatAttachmentCardProps) => {
54
- const { theme } = useTheme()
55
- const state = useMemo(() => ({ direction }), [direction])
56
- const staticState = useMemo(() => ({}), [])
57
-
58
- const resolvedCardStyle = useMemo(
59
- () => theme.components.chat.attachmentCard.container(state, cardStyle),
60
- [theme, state, cardStyle]
61
- )
62
- const resolvedIconStyle = useMemo(
63
- () => theme.components.chat.attachmentCard.icon(staticState),
64
- [theme, staticState]
65
- )
66
- const resolvedIconColor = useMemo(
67
- () => theme.components.chat.attachmentCard.iconColor(staticState),
68
- [theme, staticState]
69
- )
70
- const resolvedNameStyle = useMemo(
71
- () => theme.components.chat.attachmentCard.name(staticState, nameStyle),
72
- [theme, staticState, nameStyle]
73
- )
74
- const resolvedMetadataStyle = useMemo(
75
- () => theme.components.chat.attachmentCard.metadata(staticState, metadataStyle),
76
- [theme, staticState, metadataStyle]
77
- )
78
-
79
- return (
80
- <View {...props} style={[resolvedCardStyle, style]}>
81
- <View style={resolvedIconStyle}>
82
- {icon ?? <FileText size={22} color={resolvedIconColor.color} />}
83
- </View>
84
- <View style={{ flex: 1, minWidth: 0, gap: 2 }}>
85
- {typeof name === 'string' || typeof name === 'number' ? (
86
- <Text style={resolvedNameStyle} numberOfLines={1}>{name}</Text>
87
- ) : (
88
- name
89
- )}
90
- {metadata != null && (
91
- typeof metadata === 'string' || typeof metadata === 'number' ? (
92
- <Text style={resolvedMetadataStyle}>{metadata}</Text>
93
- ) : (
94
- metadata
95
- )
96
- )}
97
- </View>
98
- {onDownload && (
99
- <IconButton
100
- accessibilityLabel={downloadLabel}
101
- size="sm"
102
- color="primary"
103
- coloringStyle="text"
104
- onPress={onDownload}
105
- icon={Download}
106
- />
107
- )}
108
- </View>
109
- )
110
- }
@@ -1,118 +0,0 @@
1
- import {
2
- useMemo,
3
- type ReactNode
4
- } from 'react'
5
- import {
6
- Text,
7
- View,
8
- type StyleProp,
9
- type ViewProps,
10
- type ViewStyle
11
- } from 'react-native'
12
-
13
- import type { ColoringType } from '@helpwave/hightide-design/utils'
14
-
15
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
16
- import type {
17
- ChatMessageCardState,
18
- ChatMessageCardStyle,
19
- ChatMessageCardSubtitleStyle,
20
- ChatMessageCardTitleStyle,
21
- ChatMessageDirection
22
- } from '../../theme/types/components/chat'
23
- import type { StyleOverwrite } from '../../theme/types/resolver'
24
-
25
- export type ChatMessageCardProps = Omit<ViewProps, 'children' | 'style'> & {
26
- icon?: ReactNode,
27
- title: ReactNode,
28
- subtitle?: ReactNode,
29
- badge?: ReactNode,
30
- actions?: ReactNode,
31
- color?: ColoringType,
32
- direction?: ChatMessageDirection,
33
- children?: ReactNode,
34
- style?: StyleProp<ViewStyle>,
35
- cardStyle?: StyleOverwrite<ChatMessageCardState, ChatMessageCardStyle>,
36
- titleStyle?: StyleOverwrite<ChatMessageCardState, ChatMessageCardTitleStyle>,
37
- subtitleStyle?: StyleOverwrite<Record<string, never>, ChatMessageCardSubtitleStyle>,
38
- }
39
-
40
- export const ChatMessageCard = ({
41
- icon,
42
- title,
43
- subtitle,
44
- badge,
45
- actions,
46
- color = 'primary',
47
- direction = 'incoming',
48
- children,
49
- style,
50
- cardStyle,
51
- titleStyle,
52
- subtitleStyle,
53
- ...props
54
- }: ChatMessageCardProps) => {
55
- const { theme } = useTheme()
56
- const state = useMemo(() => ({ direction, color }), [direction, color])
57
- const staticState = useMemo(() => ({}), [])
58
-
59
- const resolvedCardStyle = useMemo(
60
- () => theme.components.chat.messageCard.container(state, cardStyle),
61
- [theme, state, cardStyle]
62
- )
63
- const resolvedHeaderStyle = useMemo(
64
- () => theme.components.chat.messageCard.header(staticState),
65
- [theme, staticState]
66
- )
67
- const resolvedIconStyle = useMemo(
68
- () => theme.components.chat.messageCard.icon(state),
69
- [theme, state]
70
- )
71
- const resolvedTitleStyle = useMemo(
72
- () => theme.components.chat.messageCard.title(state, titleStyle),
73
- [theme, state, titleStyle]
74
- )
75
- const resolvedSubtitleStyle = useMemo(
76
- () => theme.components.chat.messageCard.subtitle(staticState, subtitleStyle),
77
- [theme, staticState, subtitleStyle]
78
- )
79
- const resolvedBodyStyle = useMemo(
80
- () => theme.components.chat.messageCard.body(staticState),
81
- [theme, staticState]
82
- )
83
- const resolvedActionsStyle = useMemo(
84
- () => theme.components.chat.messageCard.actions(staticState),
85
- [theme, staticState]
86
- )
87
-
88
- return (
89
- <View {...props} style={[resolvedCardStyle, style]}>
90
- <View style={resolvedHeaderStyle}>
91
- {icon != null && (
92
- <View style={resolvedIconStyle}>{icon}</View>
93
- )}
94
- <View style={{ flex: 1, minWidth: 0, gap: 2 }}>
95
- {typeof title === 'string' || typeof title === 'number' ? (
96
- <Text style={resolvedTitleStyle}>{title}</Text>
97
- ) : (
98
- title
99
- )}
100
- {subtitle != null && (
101
- typeof subtitle === 'string' || typeof subtitle === 'number' ? (
102
- <Text style={resolvedSubtitleStyle}>{subtitle}</Text>
103
- ) : (
104
- subtitle
105
- )
106
- )}
107
- </View>
108
- {badge}
109
- </View>
110
- {children != null && (
111
- <View style={resolvedBodyStyle}>{children}</View>
112
- )}
113
- {actions != null && (
114
- <View style={resolvedActionsStyle}>{actions}</View>
115
- )}
116
- </View>
117
- )
118
- }
@@ -1,61 +0,0 @@
1
- import {
2
- useMemo,
3
- type ReactNode
4
- } from 'react'
5
- import {
6
- Text,
7
- View,
8
- type StyleProp,
9
- type ViewProps,
10
- type ViewStyle
11
- } from 'react-native'
12
-
13
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
14
- import type {
15
- MenuCardStyle,
16
- MenuSectionStyle,
17
- MenuSectionTitleStyle
18
- } from '../../theme/types/components/menu'
19
- import type { StyleOverwrite } from '../../theme/types/resolver'
20
-
21
- export type MenuProps = Omit<ViewProps, 'children' | 'style'> & {
22
- title: string,
23
- children?: ReactNode,
24
- style?: StyleProp<ViewStyle>,
25
- sectionStyle?: StyleOverwrite<Record<string, never>, MenuSectionStyle>,
26
- titleStyle?: StyleOverwrite<Record<string, never>, MenuSectionTitleStyle>,
27
- cardStyle?: StyleOverwrite<Record<string, never>, MenuCardStyle>,
28
- }
29
-
30
- export const Menu = ({
31
- title,
32
- children,
33
- style,
34
- sectionStyle,
35
- titleStyle,
36
- cardStyle,
37
- ...props
38
- }: MenuProps) => {
39
- const { theme } = useTheme()
40
- const state = useMemo(() => ({}), [])
41
-
42
- const resolvedSectionStyle = useMemo(
43
- () => theme.components.menu.section(state, sectionStyle),
44
- [theme, state, sectionStyle]
45
- )
46
- const resolvedTitleStyle = useMemo(
47
- () => theme.components.menu.sectionTitle(state, titleStyle),
48
- [theme, state, titleStyle]
49
- )
50
- const resolvedCardStyle = useMemo(
51
- () => theme.components.menu.card(state, cardStyle),
52
- [theme, state, cardStyle]
53
- )
54
-
55
- return (
56
- <View {...props} style={[resolvedSectionStyle, style]}>
57
- <Text style={resolvedTitleStyle}>{title}</Text>
58
- <View style={resolvedCardStyle}>{children}</View>
59
- </View>
60
- )
61
- }
@@ -1,90 +0,0 @@
1
- import {
2
- Fragment,
3
- useMemo,
4
- type ReactNode
5
- } from 'react'
6
- import {
7
- Pressable,
8
- Text,
9
- View,
10
- type PressableProps,
11
- type StyleProp,
12
- type ViewStyle
13
- } from 'react-native'
14
-
15
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
16
- import type {
17
- MenuActionItemLabelStyle,
18
- MenuActionItemState,
19
- MenuActionItemStyle
20
- } from '../../theme/types/components/menu'
21
- import type { StyleOverwrite } from '../../theme/types/resolver'
22
-
23
- export type MenuActionItemProps = Omit<PressableProps, 'children' | 'style'> & {
24
- label: string,
25
- leading?: ReactNode,
26
- trailing?: ReactNode,
27
- danger?: boolean,
28
- style?: StyleProp<ViewStyle>,
29
- itemStyle?: StyleOverwrite<MenuActionItemState, MenuActionItemStyle>,
30
- labelStyle?: StyleOverwrite<MenuActionItemState, MenuActionItemLabelStyle>,
31
- }
32
-
33
- type PressableInteraction = {
34
- pressed: boolean,
35
- hovered?: boolean,
36
- focused?: boolean,
37
- }
38
-
39
- export const MenuActionItem = ({
40
- label,
41
- leading,
42
- trailing,
43
- danger = false,
44
- disabled,
45
- style,
46
- itemStyle,
47
- labelStyle,
48
- ...props
49
- }: MenuActionItemProps) => {
50
- const { theme } = useTheme()
51
-
52
- const resolveState = (interaction: PressableInteraction): MenuActionItemState => ({
53
- isDanger: danger,
54
- isDisabled: !!disabled,
55
- isPressed: interaction.pressed,
56
- isHovered: !!interaction.hovered,
57
- isFocused: !!interaction.focused,
58
- })
59
-
60
- const resolvedContentStyle = useMemo(
61
- () => theme.components.menu.actionItemContent({}),
62
- [theme]
63
- )
64
-
65
- return (
66
- <Pressable
67
- {...props}
68
- disabled={disabled}
69
- style={(pressableState) => {
70
- const state = resolveState(pressableState as PressableInteraction)
71
- return [theme.components.menu.actionItem(state, itemStyle), style]
72
- }}
73
- >
74
- {(pressableState) => {
75
- const state = resolveState(pressableState as PressableInteraction)
76
- const resolvedLabelStyle = theme.components.menu.actionItemLabel(state, labelStyle)
77
-
78
- return (
79
- <Fragment>
80
- {leading}
81
- <View style={resolvedContentStyle}>
82
- <Text style={resolvedLabelStyle}>{label}</Text>
83
- </View>
84
- {trailing}
85
- </Fragment>
86
- )
87
- }}
88
- </Pressable>
89
- )
90
- }
@@ -1,73 +0,0 @@
1
- import {
2
- useMemo,
3
- type ReactNode
4
- } from 'react'
5
- import {
6
- Text,
7
- View,
8
- type StyleProp,
9
- type ViewProps,
10
- type ViewStyle
11
- } from 'react-native'
12
-
13
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
14
- import type {
15
- MenuItemLabelStyle,
16
- MenuItemStyle,
17
- MenuItemValueStyle
18
- } from '../../theme/types/components/menu'
19
- import type { StyleOverwrite } from '../../theme/types/resolver'
20
-
21
- export type MenuItemProps = Omit<ViewProps, 'style'> & {
22
- label: string,
23
- value: string,
24
- leading?: ReactNode,
25
- trailing?: ReactNode,
26
- style?: StyleProp<ViewStyle>,
27
- itemStyle?: StyleOverwrite<Record<string, never>, MenuItemStyle>,
28
- labelStyle?: StyleOverwrite<Record<string, never>, MenuItemLabelStyle>,
29
- valueStyle?: StyleOverwrite<Record<string, never>, MenuItemValueStyle>,
30
- }
31
-
32
- export const MenuItem = ({
33
- label,
34
- value,
35
- leading,
36
- trailing,
37
- style,
38
- itemStyle,
39
- labelStyle,
40
- valueStyle,
41
- ...props
42
- }: MenuItemProps) => {
43
- const { theme } = useTheme()
44
- const state = useMemo(() => ({}), [])
45
-
46
- const resolvedItemStyle = useMemo(
47
- () => theme.components.menu.item(state, itemStyle),
48
- [theme, state, itemStyle]
49
- )
50
- const resolvedContentStyle = useMemo(
51
- () => theme.components.menu.itemContent(state),
52
- [theme, state]
53
- )
54
- const resolvedLabelStyle = useMemo(
55
- () => theme.components.menu.itemLabel(state, labelStyle),
56
- [theme, state, labelStyle]
57
- )
58
- const resolvedValueStyle = useMemo(
59
- () => theme.components.menu.itemValue(state, valueStyle),
60
- [theme, state, valueStyle]
61
- )
62
-
63
- return (
64
- <View {...props} style={[resolvedItemStyle, style]}>
65
- {leading}
66
- <View style={resolvedContentStyle}>
67
- <Text style={resolvedLabelStyle}>{label}</Text>
68
- <Text style={resolvedValueStyle}>{value}</Text>
69
- </View>
70
- {trailing}
71
- </View>
72
- )
73
- }
@@ -1,90 +0,0 @@
1
- import {
2
- Fragment,
3
- useMemo,
4
- type ReactNode
5
- } from 'react'
6
- import {
7
- Pressable,
8
- Text,
9
- View,
10
- type PressableProps,
11
- type StyleProp,
12
- type ViewStyle
13
- } from 'react-native'
14
- import { ChevronRight } from 'lucide-react-native'
15
-
16
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
17
- import type {
18
- MenuActionItemLabelStyle,
19
- MenuActionItemState,
20
- MenuActionItemStyle
21
- } from '../../theme/types/components/menu'
22
- import type { StyleOverwrite } from '../../theme/types/resolver'
23
-
24
- export type MenuNavigationItemProps = Omit<PressableProps, 'children' | 'style'> & {
25
- label: string,
26
- leading?: ReactNode,
27
- style?: StyleProp<ViewStyle>,
28
- itemStyle?: StyleOverwrite<MenuActionItemState, MenuActionItemStyle>,
29
- labelStyle?: StyleOverwrite<MenuActionItemState, MenuActionItemLabelStyle>,
30
- }
31
-
32
- type PressableInteraction = {
33
- pressed: boolean,
34
- hovered?: boolean,
35
- focused?: boolean,
36
- }
37
-
38
- export const MenuNavigationItem = ({
39
- label,
40
- leading,
41
- disabled,
42
- style,
43
- itemStyle,
44
- labelStyle,
45
- ...props
46
- }: MenuNavigationItemProps) => {
47
- const { theme } = useTheme()
48
-
49
- const resolveState = (interaction: PressableInteraction): MenuActionItemState => ({
50
- isDisabled: !!disabled,
51
- isPressed: interaction.pressed,
52
- isHovered: !!interaction.hovered,
53
- isFocused: !!interaction.focused,
54
- })
55
-
56
- const resolvedContentStyle = useMemo(
57
- () => theme.components.menu.navigationItemContent({}),
58
- [theme]
59
- )
60
- const trailingColor = useMemo(
61
- () => theme.components.menu.navigationItemTrailing({}).color,
62
- [theme]
63
- )
64
-
65
- return (
66
- <Pressable
67
- {...props}
68
- disabled={disabled}
69
- style={(pressableState) => {
70
- const state = resolveState(pressableState as PressableInteraction)
71
- return [theme.components.menu.navigationItem(state, itemStyle), style]
72
- }}
73
- >
74
- {(pressableState) => {
75
- const state = resolveState(pressableState as PressableInteraction)
76
- const resolvedLabelStyle = theme.components.menu.navigationItemLabel(state, labelStyle)
77
-
78
- return (
79
- <Fragment>
80
- {leading}
81
- <View style={resolvedContentStyle}>
82
- <Text style={resolvedLabelStyle}>{label}</Text>
83
- </View>
84
- <ChevronRight size={16} color={trailingColor} />
85
- </Fragment>
86
- )
87
- }}
88
- </Pressable>
89
- )
90
- }
@@ -1,4 +0,0 @@
1
- export * from './Menu'
2
- export * from './MenuActionItem'
3
- export * from './MenuItem'
4
- export * from './MenuNavigationItem'
@@ -1,27 +0,0 @@
1
- import type { LucideIcon } from 'lucide-react-native'
2
-
3
- import type { ElementSize } from '@helpwave/hightide-design/types'
4
-
5
- import { useTheme } from '../../global-contexts/theme/ThemeContext'
6
-
7
- export type IconProps = {
8
- icon: LucideIcon,
9
- size?: ElementSize,
10
- color?: string,
11
- }
12
-
13
- export const Icon = ({
14
- icon: IconComponent,
15
- size = 'md',
16
- color,
17
- }: IconProps) => {
18
- const { theme } = useTheme()
19
-
20
- return (
21
- <IconComponent
22
- size={theme.layout.icon[size].size}
23
- strokeWidth={theme.layout.icon[size].strokeWidth}
24
- color={color}
25
- />
26
- )
27
- }