@hzblj/kara-components 0.17.0 → 0.19.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 (218) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/lib/module/atoms/badge/badge.android.js +5 -0
  3. package/lib/module/atoms/badge/badge.android.js.map +1 -1
  4. package/lib/module/atoms/badge/badge.ios.js +2 -0
  5. package/lib/module/atoms/badge/badge.ios.js.map +1 -1
  6. package/lib/module/atoms/button/button-tokens.js +8 -0
  7. package/lib/module/atoms/button/button-tokens.js.map +1 -1
  8. package/lib/module/atoms/button/button.android.js +43 -15
  9. package/lib/module/atoms/button/button.android.js.map +1 -1
  10. package/lib/module/atoms/button/button.ios.js +66 -10
  11. package/lib/module/atoms/button/button.ios.js.map +1 -1
  12. package/lib/module/atoms/checkbox/checkbox.android.js +5 -1
  13. package/lib/module/atoms/checkbox/checkbox.android.js.map +1 -1
  14. package/lib/module/atoms/checkbox/checkbox.ios.js +3 -1
  15. package/lib/module/atoms/checkbox/checkbox.ios.js.map +1 -1
  16. package/lib/module/atoms/color-picker/color-picker.android.js +4 -0
  17. package/lib/module/atoms/color-picker/color-picker.android.js.map +1 -1
  18. package/lib/module/atoms/color-picker/color-picker.ios.js +24 -2
  19. package/lib/module/atoms/color-picker/color-picker.ios.js.map +1 -1
  20. package/lib/module/atoms/date-picker/date-picker.android.js +85 -10
  21. package/lib/module/atoms/date-picker/date-picker.android.js.map +1 -1
  22. package/lib/module/atoms/date-picker/date-picker.ios.js +9 -7
  23. package/lib/module/atoms/date-picker/date-picker.ios.js.map +1 -1
  24. package/lib/module/atoms/divider/divider.android.js +17 -4
  25. package/lib/module/atoms/divider/divider.android.js.map +1 -1
  26. package/lib/module/atoms/divider/divider.ios.js +4 -0
  27. package/lib/module/atoms/divider/divider.ios.js.map +1 -1
  28. package/lib/module/atoms/fab/fab.android.js +64 -8
  29. package/lib/module/atoms/fab/fab.android.js.map +1 -1
  30. package/lib/module/atoms/fab/fab.ios.js +68 -12
  31. package/lib/module/atoms/fab/fab.ios.js.map +1 -1
  32. package/lib/module/atoms/icon-button/icon-button.android.js +48 -5
  33. package/lib/module/atoms/icon-button/icon-button.android.js.map +1 -1
  34. package/lib/module/atoms/icon-button/icon-button.ios.js +60 -10
  35. package/lib/module/atoms/icon-button/icon-button.ios.js.map +1 -1
  36. package/lib/module/atoms/list/list.android.js +32 -4
  37. package/lib/module/atoms/list/list.android.js.map +1 -1
  38. package/lib/module/atoms/list/list.ios.js +15 -5
  39. package/lib/module/atoms/list/list.ios.js.map +1 -1
  40. package/lib/module/atoms/progress/progress.android.js +21 -7
  41. package/lib/module/atoms/progress/progress.android.js.map +1 -1
  42. package/lib/module/atoms/progress/progress.ios.js +5 -1
  43. package/lib/module/atoms/progress/progress.ios.js.map +1 -1
  44. package/lib/module/atoms/segmented-control/segmented-control.android.js +66 -12
  45. package/lib/module/atoms/segmented-control/segmented-control.android.js.map +1 -1
  46. package/lib/module/atoms/segmented-control/segmented-control.ios.js +19 -8
  47. package/lib/module/atoms/segmented-control/segmented-control.ios.js.map +1 -1
  48. package/lib/module/atoms/segmented-control/segments.js +16 -0
  49. package/lib/module/atoms/segmented-control/segments.js.map +1 -0
  50. package/lib/module/atoms/slider/slider.android.js +16 -2
  51. package/lib/module/atoms/slider/slider.android.js.map +1 -1
  52. package/lib/module/atoms/slider/slider.ios.js +22 -2
  53. package/lib/module/atoms/slider/slider.ios.js.map +1 -1
  54. package/lib/module/atoms/switch/switch.android.js +5 -1
  55. package/lib/module/atoms/switch/switch.android.js.map +1 -1
  56. package/lib/module/atoms/switch/switch.ios.js +3 -1
  57. package/lib/module/atoms/switch/switch.ios.js.map +1 -1
  58. package/lib/module/atoms/text/spans.js +2 -1
  59. package/lib/module/atoms/text/spans.js.map +1 -1
  60. package/lib/module/atoms/text/text.android.js +18 -6
  61. package/lib/module/atoms/text/text.android.js.map +1 -1
  62. package/lib/module/atoms/text/text.ios.js +6 -2
  63. package/lib/module/atoms/text/text.ios.js.map +1 -1
  64. package/lib/module/atoms/text-field/text-field-input.js +14 -0
  65. package/lib/module/atoms/text-field/text-field-input.js.map +1 -0
  66. package/lib/module/atoms/text-field/text-field.android.js +113 -6
  67. package/lib/module/atoms/text-field/text-field.android.js.map +1 -1
  68. package/lib/module/atoms/text-field/text-field.ios.js +75 -3
  69. package/lib/module/atoms/text-field/text-field.ios.js.map +1 -1
  70. package/lib/module/molecules/collapsible/collapsible.android.js +4 -3
  71. package/lib/module/molecules/collapsible/collapsible.android.js.map +1 -1
  72. package/lib/module/molecules/collapsible/collapsible.ios.js +3 -1
  73. package/lib/module/molecules/collapsible/collapsible.ios.js.map +1 -1
  74. package/lib/module/molecules/radio-group/radio-group.android.js +10 -2
  75. package/lib/module/molecules/radio-group/radio-group.android.js.map +1 -1
  76. package/lib/module/molecules/radio-group/radio-group.ios.js +6 -2
  77. package/lib/module/molecules/radio-group/radio-group.ios.js.map +1 -1
  78. package/lib/module/molecules/search-bar/search-bar.js +9 -0
  79. package/lib/module/molecules/search-bar/search-bar.js.map +1 -1
  80. package/lib/module/molecules/stepper/stepper.android.js +25 -13
  81. package/lib/module/molecules/stepper/stepper.android.js.map +1 -1
  82. package/lib/module/molecules/stepper/stepper.ios.js +12 -6
  83. package/lib/module/molecules/stepper/stepper.ios.js.map +1 -1
  84. package/lib/module/organisms/action-sheet/action-sheet.android.js +16 -8
  85. package/lib/module/organisms/action-sheet/action-sheet.android.js.map +1 -1
  86. package/lib/module/organisms/action-sheet/action-sheet.ios.js +16 -3
  87. package/lib/module/organisms/action-sheet/action-sheet.ios.js.map +1 -1
  88. package/lib/module/organisms/alert/alert.android.js +4 -3
  89. package/lib/module/organisms/alert/alert.android.js.map +1 -1
  90. package/lib/module/organisms/alert/alert.ios.js +3 -2
  91. package/lib/module/organisms/alert/alert.ios.js.map +1 -1
  92. package/lib/module/organisms/context-menu/context-menu.android.js +3 -1
  93. package/lib/module/organisms/context-menu/context-menu.android.js.map +1 -1
  94. package/lib/module/organisms/context-menu/context-menu.ios.js +1 -0
  95. package/lib/module/organisms/context-menu/context-menu.ios.js.map +1 -1
  96. package/lib/module/organisms/fab-menu/fab-menu.android.js +3 -0
  97. package/lib/module/organisms/fab-menu/fab-menu.android.js.map +1 -1
  98. package/lib/module/organisms/fab-menu/fab-menu.ios.js +3 -0
  99. package/lib/module/organisms/fab-menu/fab-menu.ios.js.map +1 -1
  100. package/lib/module/organisms/menu/menu-parts.js +20 -0
  101. package/lib/module/organisms/menu/menu-parts.js.map +1 -0
  102. package/lib/module/organisms/menu/menu.android.js +51 -19
  103. package/lib/module/organisms/menu/menu.android.js.map +1 -1
  104. package/lib/module/organisms/menu/menu.ios.js +71 -8
  105. package/lib/module/organisms/menu/menu.ios.js.map +1 -1
  106. package/lib/module/organisms/sheet/sheet.js +2 -1
  107. package/lib/module/organisms/sheet/sheet.js.map +1 -1
  108. package/lib/typescript/atoms/badge/badge.types.d.ts +2 -0
  109. package/lib/typescript/atoms/button/button-tokens.d.ts +6 -2
  110. package/lib/typescript/atoms/button/button.types.d.ts +19 -5
  111. package/lib/typescript/atoms/checkbox/checkbox.types.d.ts +2 -0
  112. package/lib/typescript/atoms/color-picker/color-picker.types.d.ts +2 -0
  113. package/lib/typescript/atoms/date-picker/date-picker.ios.d.ts +5 -5
  114. package/lib/typescript/atoms/date-picker/date-picker.types.d.ts +30 -7
  115. package/lib/typescript/atoms/divider/divider.ios.d.ts +4 -0
  116. package/lib/typescript/atoms/divider/divider.types.d.ts +13 -2
  117. package/lib/typescript/atoms/fab/fab.types.d.ts +21 -4
  118. package/lib/typescript/atoms/icon-button/icon-button.types.d.ts +23 -4
  119. package/lib/typescript/atoms/list/list.types.d.ts +15 -1
  120. package/lib/typescript/atoms/progress/progress.types.d.ts +13 -2
  121. package/lib/typescript/atoms/segmented-control/segmented-control.ios.d.ts +3 -1
  122. package/lib/typescript/atoms/segmented-control/segmented-control.types.d.ts +26 -3
  123. package/lib/typescript/atoms/segmented-control/segments.d.ts +6 -0
  124. package/lib/typescript/atoms/slider/slider.ios.d.ts +4 -0
  125. package/lib/typescript/atoms/slider/slider.types.d.ts +7 -0
  126. package/lib/typescript/atoms/switch/switch.types.d.ts +2 -0
  127. package/lib/typescript/atoms/text/spans.d.ts +2 -0
  128. package/lib/typescript/atoms/text/text.types.d.ts +9 -1
  129. package/lib/typescript/atoms/text-field/text-field-input.d.ts +7 -0
  130. package/lib/typescript/atoms/text-field/text-field.types.d.ts +77 -4
  131. package/lib/typescript/molecules/collapsible/collapsible.types.d.ts +2 -0
  132. package/lib/typescript/molecules/radio-group/radio-group.types.d.ts +4 -0
  133. package/lib/typescript/molecules/search-bar/search-bar.types.d.ts +11 -1
  134. package/lib/typescript/molecules/stepper/stepper.ios.d.ts +2 -1
  135. package/lib/typescript/molecules/stepper/stepper.types.d.ts +17 -0
  136. package/lib/typescript/organisms/action-sheet/action-sheet.ios.d.ts +4 -0
  137. package/lib/typescript/organisms/action-sheet/action-sheet.types.d.ts +8 -3
  138. package/lib/typescript/organisms/alert/alert.types.d.ts +8 -3
  139. package/lib/typescript/organisms/menu/menu-parts.d.ts +14 -0
  140. package/lib/typescript/organisms/menu/menu.types.d.ts +35 -7
  141. package/lib/typescript/organisms/sheet/sheet.types.d.ts +7 -1
  142. package/package.json +4 -4
  143. package/src/atoms/badge/badge.android.tsx +9 -2
  144. package/src/atoms/badge/badge.ios.tsx +2 -2
  145. package/src/atoms/badge/badge.types.ts +2 -0
  146. package/src/atoms/button/button-tokens.ts +6 -3
  147. package/src/atoms/button/button.android.tsx +35 -11
  148. package/src/atoms/button/button.ios.tsx +72 -8
  149. package/src/atoms/button/button.types.ts +20 -5
  150. package/src/atoms/checkbox/checkbox.android.tsx +5 -2
  151. package/src/atoms/checkbox/checkbox.ios.tsx +2 -2
  152. package/src/atoms/checkbox/checkbox.types.ts +2 -0
  153. package/src/atoms/color-picker/color-picker.android.tsx +10 -2
  154. package/src/atoms/color-picker/color-picker.ios.tsx +24 -1
  155. package/src/atoms/color-picker/color-picker.types.ts +2 -0
  156. package/src/atoms/date-picker/date-picker.android.tsx +86 -16
  157. package/src/atoms/date-picker/date-picker.ios.tsx +20 -8
  158. package/src/atoms/date-picker/date-picker.types.ts +31 -7
  159. package/src/atoms/divider/divider.android.tsx +14 -3
  160. package/src/atoms/divider/divider.ios.tsx +4 -0
  161. package/src/atoms/divider/divider.types.ts +14 -2
  162. package/src/atoms/fab/fab.android.tsx +61 -11
  163. package/src/atoms/fab/fab.ios.tsx +48 -13
  164. package/src/atoms/fab/fab.types.ts +22 -4
  165. package/src/atoms/icon-button/icon-button.android.tsx +60 -10
  166. package/src/atoms/icon-button/icon-button.ios.tsx +77 -8
  167. package/src/atoms/icon-button/icon-button.types.ts +24 -4
  168. package/src/atoms/list/list.android.tsx +26 -4
  169. package/src/atoms/list/list.ios.tsx +20 -5
  170. package/src/atoms/list/list.types.ts +16 -1
  171. package/src/atoms/progress/progress.android.tsx +32 -6
  172. package/src/atoms/progress/progress.ios.tsx +11 -5
  173. package/src/atoms/progress/progress.types.ts +13 -2
  174. package/src/atoms/segmented-control/segmented-control.android.tsx +69 -10
  175. package/src/atoms/segmented-control/segmented-control.ios.tsx +24 -5
  176. package/src/atoms/segmented-control/segmented-control.types.ts +28 -3
  177. package/src/atoms/segmented-control/segments.ts +16 -0
  178. package/src/atoms/slider/slider.android.tsx +18 -2
  179. package/src/atoms/slider/slider.ios.tsx +38 -2
  180. package/src/atoms/slider/slider.types.ts +7 -0
  181. package/src/atoms/switch/switch.android.tsx +5 -2
  182. package/src/atoms/switch/switch.ios.tsx +2 -2
  183. package/src/atoms/switch/switch.types.ts +2 -0
  184. package/src/atoms/text/spans.ts +2 -1
  185. package/src/atoms/text/text.android.tsx +20 -7
  186. package/src/atoms/text/text.ios.tsx +7 -0
  187. package/src/atoms/text/text.types.ts +10 -1
  188. package/src/atoms/text-field/text-field-input.ts +17 -0
  189. package/src/atoms/text-field/text-field.android.tsx +132 -9
  190. package/src/atoms/text-field/text-field.ios.tsx +110 -6
  191. package/src/atoms/text-field/text-field.types.ts +98 -4
  192. package/src/molecules/collapsible/collapsible.android.tsx +10 -2
  193. package/src/molecules/collapsible/collapsible.ios.tsx +8 -2
  194. package/src/molecules/collapsible/collapsible.types.ts +2 -0
  195. package/src/molecules/radio-group/radio-group.android.tsx +18 -4
  196. package/src/molecules/radio-group/radio-group.ios.tsx +19 -5
  197. package/src/molecules/radio-group/radio-group.types.ts +4 -0
  198. package/src/molecules/search-bar/search-bar.tsx +9 -0
  199. package/src/molecules/search-bar/search-bar.types.ts +12 -1
  200. package/src/molecules/stepper/stepper.android.tsx +24 -4
  201. package/src/molecules/stepper/stepper.ios.tsx +23 -8
  202. package/src/molecules/stepper/stepper.types.ts +17 -0
  203. package/src/organisms/action-sheet/action-sheet.android.tsx +23 -11
  204. package/src/organisms/action-sheet/action-sheet.ios.tsx +20 -3
  205. package/src/organisms/action-sheet/action-sheet.types.ts +8 -3
  206. package/src/organisms/alert/alert.android.tsx +11 -8
  207. package/src/organisms/alert/alert.ios.tsx +2 -1
  208. package/src/organisms/alert/alert.types.ts +8 -3
  209. package/src/organisms/context-menu/context-menu.android.tsx +3 -2
  210. package/src/organisms/context-menu/context-menu.ios.tsx +1 -0
  211. package/src/organisms/fab-menu/fab-menu.android.tsx +3 -0
  212. package/src/organisms/fab-menu/fab-menu.ios.tsx +9 -1
  213. package/src/organisms/menu/menu-parts.ts +21 -0
  214. package/src/organisms/menu/menu.android.tsx +39 -10
  215. package/src/organisms/menu/menu.ios.tsx +55 -5
  216. package/src/organisms/menu/menu.types.ts +30 -7
  217. package/src/organisms/sheet/sheet.tsx +2 -1
  218. package/src/organisms/sheet/sheet.types.ts +7 -1
@@ -1,23 +1,73 @@
1
- import {FloatingActionButton} from '@expo/ui/jetpack-compose'
1
+ import {
2
+ ExtendedFloatingActionButton,
3
+ FloatingActionButton,
4
+ LargeFloatingActionButton,
5
+ SmallFloatingActionButton,
6
+ } from '@expo/ui/jetpack-compose'
7
+ import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
2
8
  import {useAccessibilityModifiers} from '@hzblj/kara-sx'
3
- import {useAppTheme} from '@hzblj/kara-theme'
9
+ import {type SpaceSize, useAppTheme} from '@hzblj/kara-theme'
10
+ import {useMemo} from 'react'
4
11
 
5
12
  import {useHostGuard} from '../../host-context'
6
13
  import {Icon} from '../icon'
7
- import type {FabComponent, FabProps} from './fab.types'
14
+ import {Text} from '../text'
15
+ import type {FabComponent, FabProps, FabSize} from './fab.types'
8
16
 
9
- export const Fab: FabComponent = ({accessibilityHint, accessibilityLabel, name, onPress}: FabProps) => {
17
+ /** Material's three FABs, and the icon each draws (`FloatingActionButtonDefaults.LargeIconSize` is 36dp). */
18
+ const SIZES = {
19
+ large: {icon: 's36', Material: LargeFloatingActionButton},
20
+ regular: {icon: 's24', Material: FloatingActionButton},
21
+ small: {icon: 's24', Material: SmallFloatingActionButton},
22
+ } as const satisfies Record<FabSize, {Material: unknown; icon: SpaceSize}>
23
+
24
+ export const Fab: FabComponent = ({
25
+ accessibilityHint,
26
+ accessibilityLabel,
27
+ label,
28
+ name,
29
+ onPress,
30
+ size = 'regular',
31
+ testID,
32
+ }: FabProps) => {
10
33
  useHostGuard('Fab')
11
34
 
12
35
  const theme = useAppTheme()
13
- const modifiers = useAccessibilityModifiers({accessibilityHint}, accessibilityHint !== undefined)
36
+ const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint}, accessibilityHint !== undefined)
37
+ const modifiers = useMemo(
38
+ () => (testID ? [testIdModifier(testID), ...accessibilityModifiers] : accessibilityModifiers),
39
+ [accessibilityModifiers, testID]
40
+ )
41
+ const containerColor = theme.color('fill.accent.primary')
42
+
43
+ /*
44
+ * `@expo/ui`'s FABs are slot-based: content outside `.Icon` (and the extended one's `.Text`) is not composed at all.
45
+ * The label rides on the icon, as for every FAB, and the text beside it is hidden from TalkBack so the button is not
46
+ * read twice; a label on the FAB itself would clear the click inside it, which Material's FAB composes after our
47
+ * modifiers.
48
+ */
49
+ if (label) {
50
+ return (
51
+ <ExtendedFloatingActionButton containerColor={containerColor} modifiers={modifiers} onClick={onPress}>
52
+ <ExtendedFloatingActionButton.Icon>
53
+ <Icon accessibilityLabel={accessibilityLabel} color="content.on.accent" name={name} />
54
+ </ExtendedFloatingActionButton.Icon>
55
+ <ExtendedFloatingActionButton.Text>
56
+ <Text accessibilityHidden color="content.on.accent" variant="body">
57
+ {label}
58
+ </Text>
59
+ </ExtendedFloatingActionButton.Text>
60
+ </ExtendedFloatingActionButton>
61
+ )
62
+ }
63
+
64
+ const {icon, Material} = SIZES[size]
14
65
 
15
66
  return (
16
- <FloatingActionButton containerColor={theme.color('fill.accent.primary')} modifiers={modifiers} onClick={onPress}>
17
- {/* `@expo/ui`'s FAB is slot-based: content outside `.Icon` is not composed at all. */}
18
- <FloatingActionButton.Icon>
19
- <Icon accessibilityLabel={accessibilityLabel} color="content.on.accent" name={name} />
20
- </FloatingActionButton.Icon>
21
- </FloatingActionButton>
67
+ <Material containerColor={containerColor} modifiers={modifiers} onClick={onPress}>
68
+ <Material.Icon>
69
+ <Icon accessibilityLabel={accessibilityLabel} color="content.on.accent" name={name} size={icon} />
70
+ </Material.Icon>
71
+ </Material>
22
72
  )
23
73
  }
@@ -1,23 +1,49 @@
1
1
  import {Button} from '@expo/ui/swift-ui'
2
2
  import {buttonStyle} from '@expo/ui/swift-ui/modifiers'
3
- import {useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
3
+ import {type ResponsiveSx, useAccessibilityModifiers, useSxModifiers} from '@hzblj/kara-sx'
4
+ import type {SpaceSize} from '@hzblj/kara-theme'
4
5
  import {useMemo} from 'react'
5
6
  import {useHostGuard} from '../../host-context'
7
+ import {HStack} from '../h-stack'
6
8
  import {Icon} from '../icon'
7
- import type {FabComponent, FabProps} from './fab.types'
9
+ import {Text} from '../text'
10
+ import type {FabComponent, FabProps, FabSize} from './fab.types'
8
11
 
9
- export const Fab: FabComponent = ({accessibilityHint, accessibilityLabel, name, onPress, testID}: FabProps) => {
12
+ /** Material's sizes, in points: the circle across and the icon in it. */
13
+ const SIZES = {
14
+ large: {icon: 's36', side: 96},
15
+ regular: {icon: 's24', side: 56},
16
+ small: {icon: 's24', side: 40},
17
+ } as const satisfies Record<FabSize, {icon: SpaceSize; side: number}>
18
+
19
+ const SURFACE = {bg: 'fill.accent.primary', borderRadius: 'full', shadow: 'btn.accent'} as const satisfies ResponsiveSx
20
+
21
+ /**
22
+ * Material's extended FAB in kara's iOS language: the regular FAB's height, accent and shadow, drawn as a capsule (the
23
+ * shape a round button takes once it has words), 16 before the icon and 20 after the text, as Material pads it.
24
+ */
25
+ const EXTENDED = {...SURFACE, h: SIZES.regular.side, pl: 's16', pr: 's20'} as const satisfies ResponsiveSx
26
+
27
+ export const Fab: FabComponent = ({
28
+ accessibilityHint,
29
+ accessibilityLabel,
30
+ label,
31
+ name,
32
+ onPress,
33
+ size = 'regular',
34
+ testID,
35
+ }: FabProps) => {
10
36
  useHostGuard('Fab')
11
- const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint})
37
+ const extended = Boolean(label)
38
+ const {icon, side} = SIZES[size]
39
+
40
+ // An icon alone carries the label; beside text, the button does, in place of what the text would say.
41
+ const accessibilityModifiers = useAccessibilityModifiers(
42
+ extended ? {accessibilityHint, accessibilityLabel} : {accessibilityHint}
43
+ )
12
44
 
13
- const sxModifiers = useSxModifiers({
14
- bg: 'fill.accent.primary',
15
- borderRadius: 'full',
16
- h: 56,
17
- p: 's16',
18
- shadow: 'btn.accent',
19
- w: 56,
20
- })
45
+ // The frame centres the icon, so one circle serves every size: `regular` is the 56pt one it always was.
46
+ const sxModifiers = useSxModifiers(extended ? EXTENDED : {...SURFACE, h: side, w: side}, 'center')
21
47
  const modifiers = useMemo(
22
48
  () => [buttonStyle('plain'), ...sxModifiers, ...accessibilityModifiers],
23
49
  [accessibilityModifiers, sxModifiers]
@@ -25,7 +51,16 @@ export const Fab: FabComponent = ({accessibilityHint, accessibilityLabel, name,
25
51
 
26
52
  return (
27
53
  <Button modifiers={modifiers} onPress={onPress} testID={testID}>
28
- <Icon accessibilityLabel={accessibilityLabel} color="content.on.accent" name={name} />
54
+ {extended ? (
55
+ <HStack align="center" spacing="s12">
56
+ <Icon color="content.on.accent" name={name} />
57
+ <Text color="content.on.accent" variant="body">
58
+ {label}
59
+ </Text>
60
+ </HStack>
61
+ ) : (
62
+ <Icon accessibilityLabel={accessibilityLabel} color="content.on.accent" name={name} size={icon} />
63
+ )}
29
64
  </Button>
30
65
  )
31
66
  }
@@ -2,23 +2,41 @@ import type {ReactNode} from 'react'
2
2
 
3
3
  import type {IconName} from '../icon'
4
4
 
5
+ /** Material's three floating action buttons: 40, 56 and 96 across. */
6
+ export type FabSize = 'large' | 'regular' | 'small'
7
+
5
8
  /**
6
- * A floating action button: Material's `FloatingActionButton` on Android, a 56pt accent circle (a plain SwiftUI
7
- * `Button`) on iOS. It renders inline; pinning it to a corner is up to the screen.
9
+ * A floating action button: Material's `FloatingActionButton` on Android, an accent circle (a plain SwiftUI `Button`)
10
+ * on iOS. With a `label` it is the extended one, the icon and the text side by side: Material's
11
+ * `ExtendedFloatingActionButton`, an accent capsule on iOS. It renders inline; pinning it to a corner is up to the
12
+ * screen.
8
13
  */
9
14
  export type FabProps = {
10
15
  /**
11
16
  * What VoiceOver and TalkBack say for the button, since an icon has no words of its own: "Share", "Add to
12
- * favourites". Required for that reason.
17
+ * favourites". Required for that reason. With a `label` it is still what they say, in place of the label, so give
18
+ * it the label's words (or words that start with them), and Voice Control finds the button by what it shows.
13
19
  */
14
20
  accessibilityLabel: string
15
21
  /** What pressing it does, when the label alone does not say. See `AccessibilityProps`. */
16
22
  accessibilityHint?: string
23
+ /**
24
+ * Text beside the icon, which makes it an extended FAB: "Compose", "New post". For a screen whose main action needs
25
+ * its words. Material's `ExtendedFloatingActionButton` on Android; on iOS the same accent, 56pt tall, as a capsule.
26
+ */
27
+ label?: string
17
28
  /** The icon, one of the app's, drawn in `content.on.accent`. */
18
29
  name: IconName
19
30
  /** Called when the button is pressed. */
20
31
  onPress?: () => void
21
- /** Test identifier, forwarded to the native view on iOS. Android ignores it. */
32
+ /**
33
+ * `regular` by default, 56 across. `small` is 40, for a secondary action beside a regular one; `large` is 96, with a
34
+ * 36 icon, for a screen built around one action. Material's `SmallFloatingActionButton` and
35
+ * `LargeFloatingActionButton` on Android, the same sizes of circle on iOS. A FAB with a `label` has one size and
36
+ * ignores it.
37
+ */
38
+ size?: FabSize
39
+ /** Test identifier, forwarded to the native view. */
22
40
  testID?: string
23
41
  }
24
42
 
@@ -1,28 +1,78 @@
1
- import {IconButton as ComposeIconButton} from '@expo/ui/jetpack-compose'
1
+ import {
2
+ IconButton as ComposeIconButton,
3
+ FilledIconButton,
4
+ FilledTonalIconButton,
5
+ OutlinedIconButton,
6
+ } from '@expo/ui/jetpack-compose'
7
+ import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
2
8
  import {useAccessibilityModifiers} from '@hzblj/kara-sx'
9
+ import {type ColorToken, useAppTheme} from '@hzblj/kara-theme'
10
+ import {useMemo} from 'react'
3
11
 
4
12
  import {useHostGuard} from '../../host-context'
13
+ import {buttonTokens, contentOnTint} from '../button/button-tokens'
5
14
  import {Icon} from '../icon'
6
- import type {IconButtonComponent, IconButtonProps} from './icon-button.types'
15
+ import type {IconButtonComponent, IconButtonProps, IconButtonVariant} from './icon-button.types'
16
+
17
+ /** Material has no glass: `glass` is the tonal button, a contained one that still reads over a photo or a map. */
18
+ const MATERIAL_BY_VARIANT = {
19
+ filled: FilledIconButton,
20
+ glass: FilledTonalIconButton,
21
+ outlined: OutlinedIconButton,
22
+ plain: ComposeIconButton,
23
+ tonal: FilledTonalIconButton,
24
+ } as const satisfies Record<IconButtonVariant, unknown>
25
+
26
+ /** Which `Button` variant's tokens fill the container: the same accent and secondary fills a `Button` draws. */
27
+ const FILL_BY_VARIANT = {
28
+ filled: 'accent',
29
+ glass: 'secondary',
30
+ outlined: undefined,
31
+ plain: undefined,
32
+ tonal: 'secondary',
33
+ } as const satisfies Record<IconButtonVariant, 'accent' | 'secondary' | undefined>
7
34
 
8
35
  export const IconButton: IconButtonComponent = ({
9
36
  accessibilityHint,
10
37
  accessibilityLabel,
38
+ color,
11
39
  disabled = false,
12
40
  name,
13
41
  onPress,
42
+ testID,
43
+ variant = 'plain',
14
44
  }: IconButtonProps) => {
15
45
  useHostGuard('IconButton')
46
+ const theme = useAppTheme()
16
47
  // The label rides on the icon, as Material's own icon buttons carry it; the hint labels the click.
17
- const modifiers = useAccessibilityModifiers({accessibilityHint}, accessibilityHint !== undefined)
48
+ const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint}, accessibilityHint !== undefined)
49
+ const modifiers = useMemo(
50
+ () => (testID ? [testIdModifier(testID), ...accessibilityModifiers] : accessibilityModifiers),
51
+ [accessibilityModifiers, testID]
52
+ )
53
+ const MaterialIconButton = MATERIAL_BY_VARIANT[variant]
54
+ const fillVariant = FILL_BY_VARIANT[variant]
55
+ const tokens = fillVariant ? buttonTokens(fillVariant, disabled, theme) : undefined
56
+
57
+ // `filled` takes the colour as its fill and picks the icon's on it, as a tinted `Button` does; the rest colour the icon.
58
+ const filled = variant === 'filled'
59
+ const fill = filled && color && !disabled ? color : tokens?.fill
60
+ let iconColor: ColorToken = color ?? 'content.primary'
61
+
62
+ if (disabled) {
63
+ iconColor = 'btn.content.disabled'
64
+ } else if (filled) {
65
+ iconColor = color ? contentOnTint(color, theme) : 'content.on.accent'
66
+ }
18
67
 
19
68
  return (
20
- <ComposeIconButton enabled={!disabled} modifiers={modifiers} onClick={onPress}>
21
- <Icon
22
- accessibilityLabel={accessibilityLabel}
23
- color={disabled ? 'btn.content.disabled' : 'content.primary'}
24
- name={name}
25
- />
26
- </ComposeIconButton>
69
+ <MaterialIconButton
70
+ colors={fill ? {containerColor: theme.color(fill), disabledContainerColor: theme.color(fill)} : undefined}
71
+ enabled={!disabled}
72
+ modifiers={modifiers}
73
+ onClick={onPress}
74
+ >
75
+ <Icon accessibilityLabel={accessibilityLabel} color={iconColor} name={name} />
76
+ </MaterialIconButton>
27
77
  )
28
78
  }
@@ -1,46 +1,115 @@
1
1
  import {Button} from '@expo/ui/swift-ui'
2
- import {buttonStyle, contentShape, disabled as disabledModifier, shapes} from '@expo/ui/swift-ui/modifiers'
2
+ import {
3
+ buttonBorderShape,
4
+ buttonStyle,
5
+ contentShape,
6
+ disabled as disabledModifier,
7
+ shapes,
8
+ tint as tintModifier,
9
+ } from '@expo/ui/swift-ui/modifiers'
10
+ import {isLiquidGlassAvailable} from '@hzblj/kara-native'
3
11
  import {useAccessibilityModifiers} from '@hzblj/kara-sx'
12
+ import {type ColorToken, useAppTheme} from '@hzblj/kara-theme'
4
13
  import {useMemo} from 'react'
5
14
 
6
15
  import {useHostGuard} from '../../host-context'
16
+ import type {ButtonStyleName} from '../button/button.types'
17
+ import {contentOnTint} from '../button/button-tokens'
7
18
  import {Icon} from '../icon'
8
19
  import {ZStack} from '../z-stack'
9
- import type {IconButtonComponent, IconButtonProps} from './icon-button.types'
20
+ import type {IconButtonComponent, IconButtonProps, IconButtonVariant} from './icon-button.types'
10
21
 
11
22
  /** Apple's least tap target. Android's `IconButton` is Material's 48dp; this is the same idea in points. */
12
23
  const TARGET = 44
13
24
 
25
+ /** Material's 40dp container, the ring an `outlined` button draws inside its tap target. */
26
+ const RING = 40
27
+
14
28
  /** The whole square takes the tap, not only the glyph's own pixels. */
15
29
  const WHOLE_TARGET = {modifiers: [contentShape(shapes.circle())]}
16
30
 
31
+ /**
32
+ * The SwiftUI style behind each variant. `outlined` has none (SwiftUI draws no ring around a button), so it is a plain
33
+ * button whose label draws the ring.
34
+ */
35
+ const STYLE_BY_VARIANT = {
36
+ filled: 'borderedProminent',
37
+ glass: 'glass',
38
+ outlined: 'plain',
39
+ plain: 'plain',
40
+ tonal: 'bordered',
41
+ } as const satisfies Record<IconButtonVariant, ButtonStyleName>
42
+
17
43
  export const IconButton: IconButtonComponent = ({
18
44
  accessibilityHint,
19
45
  accessibilityLabel,
46
+ color,
20
47
  disabled = false,
21
48
  name,
22
49
  onPress,
23
50
  testID,
51
+ variant = 'plain',
24
52
  }: IconButtonProps) => {
25
53
  useHostGuard('IconButton')
54
+ const theme = useAppTheme()
26
55
  const accessibilityModifiers = useAccessibilityModifiers({accessibilityHint})
56
+ // Below iOS 26 `@expo/ui` turns `.glass` into `.automatic`, a bare icon; the tonal circle is what Android draws too.
57
+ const style = variant === 'glass' && !isLiquidGlassAvailable() ? 'bordered' : STYLE_BY_VARIANT[variant]
58
+ const bezel = style !== 'plain'
59
+
60
+ /**
61
+ * `filled` takes the colour as its fill, and the icon a colour that reads on it. `tonal` passes it as the tint too,
62
+ * which SwiftUI's bordered style draws its pale circle in. SwiftUI dims a disabled button's label itself, and greys a
63
+ * prominent one's fill, so a disabled filled icon goes back to the primary content colour rather than white on grey.
64
+ */
65
+ const fill: ColorToken | undefined = variant === 'filled' ? (color ?? 'fill.accent.primary') : undefined
66
+ const tint = fill ?? (variant === 'tonal' ? color : undefined)
67
+ const iconColor: ColorToken = fill
68
+ ? disabled
69
+ ? 'content.primary'
70
+ : color
71
+ ? contentOnTint(color, theme)
72
+ : 'content.on.accent'
73
+ : (color ?? 'content.primary')
27
74
 
28
75
  const modifiers = useMemo(() => {
29
- const list = [buttonStyle('plain'), ...accessibilityModifiers]
76
+ const list = [buttonStyle(style)]
77
+
78
+ if (bezel) {
79
+ list.push(buttonBorderShape('circle'))
80
+ }
81
+
82
+ if (tint) {
83
+ list.push(tintModifier(theme.color(tint)))
84
+ }
85
+
86
+ list.push(...accessibilityModifiers)
30
87
 
31
88
  if (disabled) {
32
89
  list.push(disabledModifier(true))
33
90
  }
34
91
 
35
92
  return list
36
- }, [accessibilityModifiers, disabled])
93
+ }, [accessibilityModifiers, bezel, disabled, style, theme, tint])
94
+
95
+ // A bezel style sizes its circle around the icon itself; a plain button's label is the whole target.
96
+ const icon = <Icon accessibilityLabel={accessibilityLabel} color={iconColor} name={name} />
37
97
 
38
98
  return (
39
99
  <Button modifiers={modifiers} onPress={onPress} testID={testID}>
40
- <ZStack h={TARGET} ios={WHOLE_TARGET} w={TARGET}>
41
- {/* SwiftUI dims a disabled button's label itself; a disabled colour on top of that dimmed it twice. */}
42
- <Icon accessibilityLabel={accessibilityLabel} color="content.primary" name={name} />
43
- </ZStack>
100
+ {bezel ? (
101
+ icon
102
+ ) : (
103
+ <ZStack h={TARGET} ios={WHOLE_TARGET} w={TARGET}>
104
+ {variant === 'outlined' ? (
105
+ <ZStack borderColor="border.secondary.base" borderRadius="full" borderWidth={1} h={RING} w={RING}>
106
+ {icon}
107
+ </ZStack>
108
+ ) : (
109
+ icon
110
+ )}
111
+ </ZStack>
112
+ )}
44
113
  </Button>
45
114
  )
46
115
  }
@@ -1,11 +1,18 @@
1
+ import type {ColorToken} from '@hzblj/kara-theme'
1
2
  import type {ReactNode} from 'react'
2
3
 
3
4
  import type {IconName} from '../icon'
4
5
 
5
6
  /**
6
- * A button that is just an icon: a plain SwiftUI `Button` on iOS, Material's `IconButton` on Android. The icon sits
7
- * centred in a tap target the size each platform asks for, 44pt on iOS and 48dp on Android, so a row of them spaces
8
- * itself the same way on both.
7
+ * How much the button stands out: just the icon (`plain`), on a fill in the accent (`filled`), on a muted fill
8
+ * (`tonal`), in a ring (`outlined`) or on Liquid Glass (`glass`).
9
+ */
10
+ export type IconButtonVariant = 'filled' | 'glass' | 'outlined' | 'plain' | 'tonal'
11
+
12
+ /**
13
+ * A button that is just an icon: a SwiftUI `Button` on iOS, one of Material's icon buttons on Android. A `plain` one
14
+ * sits centred in a tap target the size each platform asks for, 44pt on iOS and 48dp on Android, so a row of them
15
+ * spaces itself the same way on both; the other variants draw a circle around the icon.
9
16
  */
10
17
  export type IconButtonProps = {
11
18
  /**
@@ -15,14 +22,27 @@ export type IconButtonProps = {
15
22
  accessibilityLabel: string
16
23
  /** What pressing it does, when the label alone does not say. See `AccessibilityProps`. */
17
24
  accessibilityHint?: string
25
+ /**
26
+ * The icon's colour, `content.primary` by default. On `filled` it is the fill instead (`fill.accent.primary` by
27
+ * default) and the icon picks a colour that reads on it, as a `Button`'s `tint` does on a prominent style. On iOS a
28
+ * `tonal` button's circle takes a pale shade of it too.
29
+ */
30
+ color?: ColorToken
18
31
  /** Dims the icon (iOS's own disabled look, `btn.content.disabled` on Android) and stops the button responding. */
19
32
  disabled?: boolean
20
33
  /** The icon, one of the app's. */
21
34
  name: IconName
22
35
  /** Called when the button is pressed. */
23
36
  onPress?: () => void
24
- /** Test identifier, forwarded to the native view on iOS. Android ignores it. */
37
+ /** Test identifier, forwarded to the native view. */
25
38
  testID?: string
39
+ /**
40
+ * The look. `plain` by default. iOS draws the others as SwiftUI button styles in a circle: `borderedProminent` for
41
+ * `filled`, `bordered` for `tonal`, `glass` for `glass` (`bordered` below iOS 26), and `outlined` as a plain button
42
+ * in a hairline ring. Android draws Material's `IconButton`, `FilledIconButton`, `FilledTonalIconButton` and
43
+ * `OutlinedIconButton`; Material has no glass, so there `glass` is the tonal one.
44
+ */
45
+ variant?: IconButtonVariant
26
46
  }
27
47
 
28
48
  export type IconButtonComponent = (props: IconButtonProps) => ReactNode
@@ -1,8 +1,11 @@
1
1
  import {LazyColumn, PullToRefreshBox} from '@expo/ui/jetpack-compose'
2
+ import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
2
3
  import {useSxModifiers} from '@hzblj/kara-sx'
3
- import {Children, Fragment, isValidElement} from 'react'
4
+ import {spacing} from '@hzblj/kara-theme'
5
+ import {Children, Fragment, isValidElement, useMemo} from 'react'
4
6
  import {useHostGuard} from '../../host-context'
5
7
  import {useRefreshState} from '../../use-refresh-state'
8
+ import {Card} from '../card'
6
9
  import {Divider} from '../divider'
7
10
  import type {ListComponent, ListProps} from './list.types'
8
11
 
@@ -15,12 +18,31 @@ const withSeparators = (children: ListProps['children']) =>
15
18
  </Fragment>
16
19
  ))
17
20
 
18
- export const List: ListComponent = ({children, onRefresh}: ListProps) => {
21
+ /** The card's inset from the column's edges, as `Form` keeps its cards off the screen's. */
22
+ const INSET = {bottom: spacing.s16, end: spacing.s16, start: spacing.s16, top: spacing.s16}
23
+
24
+ /**
25
+ * `plain` is the column as it always was. `insetGrouped` sets the rows in one `Card`, the way `Form` draws a section,
26
+ * inset by the column's content padding: the card is a single item of the column, so its rows are composed together
27
+ * rather than as they scroll in. A long list that wants laziness stays `plain`.
28
+ */
29
+ export const List: ListComponent = ({children, onRefresh, separators = true, testID, variant = 'plain'}: ListProps) => {
19
30
  useHostGuard('List')
20
- const modifiers = useSxModifiers({wide: true})
31
+ const sxModifiers = useSxModifiers({wide: true})
32
+ // On the column, not the refresh box around it: the column is what scrolls.
33
+ const modifiers = useMemo(
34
+ () => (testID ? [...sxModifiers, testIdModifier(testID)] : sxModifiers),
35
+ [sxModifiers, testID]
36
+ )
21
37
  const {isRefreshing, refresh} = useRefreshState(onRefresh)
38
+ const rows = separators ? withSeparators(children) : children
39
+ const grouped = variant === 'insetGrouped'
22
40
 
23
- const column = <LazyColumn modifiers={modifiers}>{withSeparators(children)}</LazyColumn>
41
+ const column = (
42
+ <LazyColumn contentPadding={grouped ? INSET : undefined} modifiers={modifiers}>
43
+ {grouped && Children.toArray(children).length > 0 ? <Card wide>{rows}</Card> : rows}
44
+ </LazyColumn>
45
+ )
24
46
 
25
47
  if (!onRefresh) {
26
48
  return column
@@ -1,18 +1,33 @@
1
- import {List as SwiftUIList} from '@expo/ui/swift-ui'
2
- import {type ModifierConfig, refreshable} from '@expo/ui/swift-ui/modifiers'
1
+ import {Group, List as SwiftUIList} from '@expo/ui/swift-ui'
2
+ import {listRowSeparator, listStyle, type ModifierConfig, refreshable} from '@expo/ui/swift-ui/modifiers'
3
3
  import {useMemo} from 'react'
4
4
 
5
5
  import {useHostGuard} from '../../host-context'
6
6
  import type {ListComponent, ListProps} from './list.types'
7
7
 
8
- export const List: ListComponent = ({children, onRefresh, testID}: ListProps) => {
8
+ /**
9
+ * `listRowSeparator` is a row's preference, not the list's: set on the `List` it reaches no row. A `Group` hands a
10
+ * modifier to each of its children, so the rows go in one, and each of them hides its own separator.
11
+ */
12
+ const NO_SEPARATORS = [listRowSeparator('hidden')]
13
+
14
+ export const List: ListComponent = ({
15
+ children,
16
+ onRefresh,
17
+ separators = true,
18
+ testID,
19
+ variant = 'insetGrouped',
20
+ }: ListProps) => {
9
21
  useHostGuard('List')
10
22
 
11
- const modifiers = useMemo<ModifierConfig[]>(() => (onRefresh ? [refreshable(onRefresh)] : []), [onRefresh])
23
+ const modifiers = useMemo<ModifierConfig[]>(
24
+ () => [listStyle(variant), ...(onRefresh ? [refreshable(onRefresh)] : [])],
25
+ [onRefresh, variant]
26
+ )
12
27
 
13
28
  return (
14
29
  <SwiftUIList modifiers={modifiers} testID={testID}>
15
- {children}
30
+ {separators ? children : <Group modifiers={NO_SEPARATORS}>{children}</Group>}
16
31
  </SwiftUIList>
17
32
  )
18
33
  }
@@ -1,5 +1,8 @@
1
1
  import type {ReactNode} from 'react'
2
2
 
3
+ /** How the rows are laid out: in a rounded card inset from the edges, or edge to edge. */
4
+ export type ListVariant = 'insetGrouped' | 'plain'
5
+
3
6
  /**
4
7
  * A scrolling list: SwiftUI `List`, Compose `LazyColumn`. It has no height of its own, so it needs one from outside:
5
8
  * a `Screen`, or an `Island` with `matchContents={false}` and a height. In an island that sizes itself to its content
@@ -17,8 +20,20 @@ export type ListProps = {
17
20
  * until the promise settles, so neither platform needs a `refreshing` flag kept in sync by hand.
18
21
  */
19
22
  onRefresh?: () => Promise<void>
20
- /** Test identifier, forwarded to the native view on iOS. Android ignores it. */
23
+ /**
24
+ * Draws a line between rows. On by default. Off, iOS hides SwiftUI's row separators and Android draws no `Divider`,
25
+ * for rows that carry their own spacing (cards, media).
26
+ */
27
+ separators?: boolean
28
+ /** Test identifier, forwarded to the native view. */
21
29
  testID?: string
30
+ /**
31
+ * The layout. Each platform's own list by default: `insetGrouped` on iOS, `plain` on Android. `insetGrouped` sets
32
+ * the rows in a rounded card inset from the edges (SwiftUI's `insetGrouped` list style; on Android a `Card` inside the
33
+ * column, the way `Form` draws its sections). `plain` runs the rows edge to edge (SwiftUI's `plain` style; Android's
34
+ * column as it is).
35
+ */
36
+ variant?: ListVariant
22
37
  }
23
38
 
24
39
  export type ListComponent = (props: ListProps) => ReactNode
@@ -1,19 +1,45 @@
1
1
  import {CircularProgressIndicator, LinearProgressIndicator} from '@expo/ui/jetpack-compose'
2
+ import {testID as testIdModifier} from '@expo/ui/jetpack-compose/modifiers'
2
3
  import {useAccessibilityModifiers} from '@hzblj/kara-sx'
3
- import {useAppTheme} from '@hzblj/kara-theme'
4
+ import {useAppTheme, withOpacity} from '@hzblj/kara-theme'
5
+ import {useMemo} from 'react'
4
6
 
5
7
  import {useHostGuard} from '../../host-context'
6
8
  import type {ProgressComponent, ProgressProps} from './progress.types'
7
9
 
8
- export const Progress: ProgressComponent = ({accessibilityLabel, circular = false, value}: ProgressProps) => {
10
+ /**
11
+ * Material draws the track in the seeded scheme's `secondaryContainer`, a pale accent: under a red or a green bar it
12
+ * would read as a second colour. Any other colour gets its own faded track instead, the way the accent's is pale; the
13
+ * accent keeps Material's. An indeterminate ring has no track in Material, so it is left without one.
14
+ */
15
+ export const Progress: ProgressComponent = ({
16
+ accessibilityLabel,
17
+ circular = false,
18
+ color = 'fill.accent.primary',
19
+ testID,
20
+ value,
21
+ }: ProgressProps) => {
9
22
  useHostGuard('Progress')
10
23
  const theme = useAppTheme()
11
- const color = theme.color('fill.accent.primary')
12
- const modifiers = useAccessibilityModifiers({accessibilityLabel})
24
+ const indicator = theme.color(color)
25
+ // The website reads the default above from the source, so it stays a literal rather than a shared constant.
26
+ const track = color === 'fill.accent.primary' ? undefined : withOpacity(String(indicator), 'o20')
27
+ const accessibilityModifiers = useAccessibilityModifiers({accessibilityLabel})
28
+ const modifiers = useMemo(
29
+ () => (testID ? [testIdModifier(testID), ...accessibilityModifiers] : accessibilityModifiers),
30
+ [accessibilityModifiers, testID]
31
+ )
13
32
 
14
33
  if (circular) {
15
- return <CircularProgressIndicator color={color} modifiers={modifiers} progress={value} />
34
+ return (
35
+ <CircularProgressIndicator
36
+ color={indicator}
37
+ modifiers={modifiers}
38
+ progress={value}
39
+ trackColor={value === undefined ? undefined : track}
40
+ />
41
+ )
16
42
  }
17
43
 
18
- return <LinearProgressIndicator color={color} modifiers={modifiers} progress={value} />
44
+ return <LinearProgressIndicator color={indicator} modifiers={modifiers} progress={value} trackColor={track} />
19
45
  }