@hzblj/kara-components 0.14.0 → 0.14.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 (42) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/lib/module/atoms/activity-indicator/activity-indicator.android.js +14 -7
  3. package/lib/module/atoms/activity-indicator/activity-indicator.android.js.map +1 -1
  4. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js +12 -2
  5. package/lib/module/atoms/activity-indicator/activity-indicator.ios.js.map +1 -1
  6. package/lib/module/atoms/date-picker/date-picker.android.js +13 -1
  7. package/lib/module/atoms/date-picker/date-picker.android.js.map +1 -1
  8. package/lib/module/atoms/date-picker/date-picker.ios.js +19 -12
  9. package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
  10. package/lib/module/atoms/picker/picker.android.js +48 -41
  11. package/lib/module/atoms/picker/picker.android.js.map +1 -1
  12. package/lib/module/atoms/picker/picker.ios.js +25 -2
  13. package/lib/module/atoms/picker/picker.ios.js.map +1 -1
  14. package/lib/module/atoms/segmented-control/segmented-control.android.js +25 -18
  15. package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
  16. package/lib/module/atoms/segmented-control/segmented-control.ios.js +15 -2
  17. package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
  18. package/lib/module/host-context.js +8 -0
  19. package/lib/module/host-context.js.map +1 -1
  20. package/lib/typescript/atoms/activity-indicator/activity-indicator.ios.d.ts +4 -1
  21. package/lib/typescript/atoms/activity-indicator/activity-indicator.types.d.ts +2 -1
  22. package/lib/typescript/atoms/date-picker/date-picker.ios.d.ts +2 -1
  23. package/lib/typescript/atoms/date-picker/date-picker.types.d.ts +1 -1
  24. package/lib/typescript/atoms/picker/picker.ios.d.ts +5 -0
  25. package/lib/typescript/atoms/picker/picker.types.d.ts +6 -2
  26. package/lib/typescript/atoms/segmented-control/segmented-control.ios.d.ts +2 -1
  27. package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +2 -1
  28. package/lib/typescript/host-context.d.ts +7 -0
  29. package/package.json +4 -4
  30. package/src/atoms/activity-indicator/activity-indicator.android.tsx +18 -5
  31. package/src/atoms/activity-indicator/activity-indicator.ios.tsx +10 -2
  32. package/src/atoms/activity-indicator/activity-indicator.types.ts +2 -1
  33. package/src/atoms/date-picker/date-picker.android.tsx +15 -1
  34. package/src/atoms/date-picker/date-picker.ios.tsx +20 -9
  35. package/src/atoms/date-picker/date-picker.types.ts +1 -1
  36. package/src/atoms/picker/picker.android.tsx +53 -44
  37. package/src/atoms/picker/picker.ios.tsx +28 -7
  38. package/src/atoms/picker/picker.types.ts +6 -2
  39. package/src/atoms/segmented-control/segmented-control.android.tsx +29 -18
  40. package/src/atoms/segmented-control/segmented-control.ios.tsx +10 -2
  41. package/src/atoms/segmented-control/segmented-control.types.ts +2 -1
  42. package/src/host-context.tsx +8 -0
@@ -4,6 +4,7 @@ import {haptics} from '@hzblj/expo-rich-haptics'
4
4
  import {useAppTheme} from '@hzblj/kara-theme'
5
5
  import {useCallback, useMemo} from 'react'
6
6
 
7
+ import {useInsideHost} from '../../host-context'
7
8
  import type {SegmentedControlComponent, SegmentedControlProps} from './segmented-control.types'
8
9
 
9
10
  const MATCH_HEIGHT_ONLY = {horizontal: false, vertical: true}
@@ -13,8 +14,9 @@ const SEGMENT = [weight(1)]
13
14
 
14
15
  /**
15
16
  * Material's single-choice segmented buttons, the selected one filled with the accent like a checked `Switch`. Its own
16
- * host, themed like an `Island`, so it drops into React Native layout the way the iOS control does. Material plays
17
- * nothing on a new segment, so this plays `selection`.
17
+ * host, themed like an `Island`, so it drops into React Native layout the way the iOS control does. Inside a `Screen`
18
+ * or `Island` it renders into that host instead (see `useInsideHost`). Material plays nothing on a new segment, so this
19
+ * plays `selection`.
18
20
  */
19
21
  export const SegmentedControl: SegmentedControlComponent = ({
20
22
  disabled = false,
@@ -24,6 +26,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
24
26
  values,
25
27
  }: SegmentedControlProps) => {
26
28
  const theme = useAppTheme()
29
+ const insideHost = useInsideHost()
27
30
 
28
31
  // Every segment keeps the same outline, the selected one too: an accent border would run its fill out over the line
29
32
  // the others draw inside, and make it look taller than its neighbours.
@@ -58,24 +61,32 @@ export const SegmentedControl: SegmentedControlComponent = ({
58
61
  [onChange, selectedIndex, values]
59
62
  )
60
63
 
64
+ const row = (
65
+ <SingleChoiceSegmentedButtonRow modifiers={rowModifiers}>
66
+ {values.map((label, index) => (
67
+ <SegmentedButton
68
+ colors={colors}
69
+ enabled={!disabled}
70
+ key={index}
71
+ modifiers={SEGMENT}
72
+ onClick={() => select(index)}
73
+ selected={index === selectedIndex}
74
+ >
75
+ <SegmentedButton.Label>
76
+ <Text style={theme.typography.subheadlineMedium}>{label}</Text>
77
+ </SegmentedButton.Label>
78
+ </SegmentedButton>
79
+ ))}
80
+ </SingleChoiceSegmentedButtonRow>
81
+ )
82
+
83
+ if (insideHost) {
84
+ return row
85
+ }
86
+
61
87
  return (
62
88
  <Host colorScheme={theme.dark ? 'dark' : 'light'} matchContents={MATCH_HEIGHT_ONLY} seedColor={theme.materialSeed}>
63
- <SingleChoiceSegmentedButtonRow modifiers={rowModifiers}>
64
- {values.map((label, index) => (
65
- <SegmentedButton
66
- colors={colors}
67
- enabled={!disabled}
68
- key={index}
69
- modifiers={SEGMENT}
70
- onClick={() => select(index)}
71
- selected={index === selectedIndex}
72
- >
73
- <SegmentedButton.Label>
74
- <Text style={theme.typography.subheadlineMedium}>{label}</Text>
75
- </SegmentedButton.Label>
76
- </SegmentedButton>
77
- ))}
78
- </SingleChoiceSegmentedButtonRow>
89
+ {row}
79
90
  </Host>
80
91
  )
81
92
  }
@@ -1,13 +1,18 @@
1
+ import {RNHostView} from '@expo/ui/swift-ui'
1
2
  import {SegmentedControl as NativeSegmentedControl} from '@hzblj/kara-native/swift-ui'
2
3
  import {useAppTheme} from '@hzblj/kara-theme'
3
4
  import {useCallback, useMemo} from 'react'
4
5
 
6
+ import {useInsideHost} from '../../host-context'
5
7
  import type {SegmentedControlComponent, SegmentedControlProps} from './segmented-control.types'
6
8
 
9
+ const MATCH_HEIGHT_ONLY = {horizontal: false, vertical: true}
10
+
7
11
  /**
8
12
  * kara's own `UISegmentedControl`, with the pill in the accent and white text on it — the system control tints only the
9
13
  * pill, and SwiftUI's segmented picker not even that. A plain `UIView` in React Native layout: it needs no host, takes
10
- * the width it is given and UIKit's height. UIKit plays the control's own feedback.
14
+ * the width it is given and UIKit's height. Inside a `Screen` or `Island` it is carried into that host by `RNHostView`
15
+ * (see `useInsideHost`), the host's width and its own height. UIKit plays the control's own feedback.
11
16
  */
12
17
  export const SegmentedControl: SegmentedControlComponent = ({
13
18
  disabled = false,
@@ -17,6 +22,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
17
22
  values,
18
23
  }: SegmentedControlProps) => {
19
24
  const theme = useAppTheme()
25
+ const insideHost = useInsideHost()
20
26
  const face = theme.typography.footnoteMedium
21
27
 
22
28
  // The native control speaks in values; a segment's is its index, so two labels may read the same.
@@ -30,7 +36,7 @@ export const SegmentedControl: SegmentedControlComponent = ({
30
36
  [onChange, values]
31
37
  )
32
38
 
33
- return (
39
+ const control = (
34
40
  <NativeSegmentedControl
35
41
  activePillColor={theme.color('fill.accent.primary')}
36
42
  activeTextColor={theme.color('content.on.accent')}
@@ -44,4 +50,6 @@ export const SegmentedControl: SegmentedControlComponent = ({
44
50
  testID={testID}
45
51
  />
46
52
  )
53
+
54
+ return insideHost ? <RNHostView matchContents={MATCH_HEIGHT_ONLY}>{control}</RNHostView> : control
47
55
  }
@@ -3,7 +3,8 @@ import type {ReactNode} from 'react'
3
3
  /**
4
4
  * A row of mutually exclusive segments, the selected one in the accent: kara's own `UISegmentedControl` on iOS,
5
5
  * Material's single-choice segmented buttons on Android. It needs no `Screen` or `Island`: iOS is a plain view, Android
6
- * brings its own host. It takes the width it is given; the height is the platform's.
6
+ * brings its own host. Inside one it joins that host, so it can share a stack with other native views. It takes the
7
+ * width it is given; the height is the platform's.
7
8
  */
8
9
  export type SegmentedControlProps = {
9
10
  /** Stops the user changing the segment, and dims the control. */
@@ -6,6 +6,14 @@ export const HostBoundary = ({children}: {children: ReactNode}) => (
6
6
  <HostContext.Provider value>{children}</HostContext.Provider>
7
7
  )
8
8
 
9
+ /**
10
+ * Whether this renders inside a `Screen` or `Island` already. An atom that brings its own host renders straight into
11
+ * that one instead: on Android a host nested in another is a real view beside the outer host's own `ComposeView`, and
12
+ * the outer host (a horizontal `LinearLayout`) measures its `ComposeView` in the width the nested one leaves over,
13
+ * which for a full-width nested host is none.
14
+ */
15
+ export const useInsideHost = () => useContext(HostContext)
16
+
9
17
  export const useHostGuard = (atom: string) => {
10
18
  const insideHost = useContext(HostContext)
11
19