@codeleap/mobile 3.25.0 → 3.25.2

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 (131) hide show
  1. package/package.json +4 -6
  2. package/src/components/ActionIcon/index.tsx +37 -55
  3. package/src/components/ActionIcon/styles.ts +4 -2
  4. package/src/components/ActivityIndicator/index.tsx +64 -42
  5. package/src/components/ActivityIndicator/styles.ts +10 -1
  6. package/src/components/Autocomplete/index.tsx +54 -46
  7. package/src/components/Autocomplete/styles.ts +5 -2
  8. package/src/components/Autocomplete/types.ts +23 -13
  9. package/src/components/Avatar/index.tsx +71 -59
  10. package/src/components/Avatar/styles.ts +9 -1
  11. package/src/components/AvatarGroup/index.tsx +44 -30
  12. package/src/components/AvatarGroup/styles.ts +6 -0
  13. package/src/components/Backdrop/index.tsx +34 -51
  14. package/src/components/Backdrop/styles.ts +10 -5
  15. package/src/components/Badge/index.tsx +62 -36
  16. package/src/components/Badge/styles.ts +11 -3
  17. package/src/components/Button/index.tsx +82 -55
  18. package/src/components/Button/styles.ts +14 -13
  19. package/src/components/Calendar/index.tsx +29 -35
  20. package/src/components/Calendar/style.ts +6 -0
  21. package/src/components/Checkbox/index.tsx +64 -43
  22. package/src/components/Checkbox/styles.ts +6 -1
  23. package/src/components/ContentView/index.tsx +63 -0
  24. package/src/components/ContentView/styles.ts +8 -0
  25. package/src/components/DatePickerModal/index.tsx +65 -50
  26. package/src/components/DatePickerModal/styles.ts +10 -9
  27. package/src/components/DatePickerModal/types.ts +54 -36
  28. package/src/components/Drawer/index.tsx +28 -0
  29. package/src/components/Drawer/styles.ts +8 -0
  30. package/src/components/EmptyPlaceholder/index.tsx +63 -40
  31. package/src/components/EmptyPlaceholder/styles.ts +5 -0
  32. package/src/components/FileInput/index.tsx +49 -11
  33. package/src/components/FileInput/styles.ts +8 -0
  34. package/src/components/Grid/index.tsx +116 -84
  35. package/src/components/Grid/styles.ts +5 -0
  36. package/src/components/Icon/index.tsx +79 -44
  37. package/src/components/Icon/styles.ts +6 -0
  38. package/src/components/Image/index.tsx +78 -58
  39. package/src/components/Image/styles.ts +6 -1
  40. package/src/components/ImageView/Spotlight.tsx +4 -1
  41. package/src/components/ImageView/component.tsx +2 -1
  42. package/src/components/InputBase/index.tsx +24 -33
  43. package/src/components/InputBase/styles.ts +75 -66
  44. package/src/components/InputBase/types.ts +4 -3
  45. package/src/components/InputBase/utils.ts +4 -6
  46. package/src/components/InputLabel/index.tsx +38 -0
  47. package/src/components/InputLabel/styles.ts +7 -0
  48. package/src/components/List/PaginationIndicator.tsx +54 -0
  49. package/src/components/List/index.tsx +151 -99
  50. package/src/components/List/styles.ts +6 -0
  51. package/src/components/LoadingOverlay/index.tsx +29 -42
  52. package/src/components/LoadingOverlay/styles.ts +7 -7
  53. package/src/components/Modal/index.tsx +127 -80
  54. package/src/components/Modal/styles.ts +8 -0
  55. package/src/components/Navigation/Navigation.tsx +0 -1
  56. package/src/components/Navigation/types.ts +9 -2
  57. package/src/components/NumberIncrement/index.tsx +60 -50
  58. package/src/components/NumberIncrement/styles.ts +5 -0
  59. package/src/components/NumberIncrement/types.ts +39 -32
  60. package/src/components/Pager/index.tsx +94 -42
  61. package/src/components/Pager/styles.ts +13 -1
  62. package/src/components/RadioInput/index.tsx +57 -32
  63. package/src/components/RadioInput/styles.ts +7 -5
  64. package/src/components/RefreshControl/index.tsx +19 -39
  65. package/src/components/RefreshControl/styles.ts +6 -1
  66. package/src/components/Scroll/index.tsx +105 -89
  67. package/src/components/Scroll/styles.ts +5 -0
  68. package/src/components/Sections/index.tsx +161 -111
  69. package/src/components/Sections/styles.ts +5 -0
  70. package/src/components/SegmentedControl/Option.tsx +31 -46
  71. package/src/components/SegmentedControl/index.tsx +121 -86
  72. package/src/components/SegmentedControl/styles.ts +22 -15
  73. package/src/components/Select/index.tsx +82 -71
  74. package/src/components/Select/styles.ts +5 -3
  75. package/src/components/Select/types.ts +25 -20
  76. package/src/components/Slider/index.tsx +43 -58
  77. package/src/components/Slider/styles.ts +6 -15
  78. package/src/components/Slider/types.ts +14 -9
  79. package/src/components/Switch/index.tsx +56 -43
  80. package/src/components/Switch/styles.ts +7 -1
  81. package/src/components/Text/index.tsx +52 -56
  82. package/src/components/Text/styles.ts +7 -1
  83. package/src/components/TextInput/index.tsx +162 -49
  84. package/src/components/TextInput/styles.ts +8 -2
  85. package/src/components/Touchable/index.tsx +87 -44
  86. package/src/components/Touchable/styles.ts +9 -0
  87. package/src/components/View/index.tsx +92 -23
  88. package/src/components/View/styles.ts +6 -0
  89. package/src/components/components.ts +6 -2
  90. package/src/components/defaultStyles.ts +77 -0
  91. package/src/index.ts +0 -2
  92. package/src/modules/PressableRipple/type.ts +0 -1
  93. package/src/utils/KeyboardAware/context.tsx +2 -0
  94. package/src/utils/KeyboardAware/keyboardHooks.ts +2 -1
  95. package/src/utils/ModalManager/components.tsx +30 -1
  96. package/src/utils/ModalManager/context.tsx +4 -4
  97. package/src/utils/ModalManager/index.ts +4 -1
  98. package/src/utils/hooks.ts +1 -12
  99. package/src/Registry.ts +0 -52
  100. package/src/components/ActionIcon/types.ts +0 -15
  101. package/src/components/ActivityIndicator/types.ts +0 -9
  102. package/src/components/Avatar/types.ts +0 -23
  103. package/src/components/AvatarGroup/types.ts +0 -10
  104. package/src/components/Backdrop/types.ts +0 -14
  105. package/src/components/Badge/types.ts +0 -27
  106. package/src/components/Button/types.ts +0 -20
  107. package/src/components/Checkbox/types.ts +0 -13
  108. package/src/components/EmptyPlaceholder/types.ts +0 -21
  109. package/src/components/FileInput/types.ts +0 -27
  110. package/src/components/Grid/types.ts +0 -20
  111. package/src/components/Icon/types.ts +0 -15
  112. package/src/components/Image/types.ts +0 -18
  113. package/src/components/List/types.ts +0 -41
  114. package/src/components/LoadingOverlay/types.ts +0 -9
  115. package/src/components/Modal/types.ts +0 -41
  116. package/src/components/Pager/types.ts +0 -37
  117. package/src/components/PaginationIndicator/index.tsx +0 -51
  118. package/src/components/PaginationIndicator/styles.ts +0 -3
  119. package/src/components/PaginationIndicator/types.ts +0 -10
  120. package/src/components/RadioInput/types.ts +0 -31
  121. package/src/components/RefreshControl/types.ts +0 -9
  122. package/src/components/Scroll/types.ts +0 -21
  123. package/src/components/SearchInput/index.tsx +0 -90
  124. package/src/components/Sections/types.ts +0 -39
  125. package/src/components/SegmentedControl/types.ts +0 -31
  126. package/src/components/Switch/types.ts +0 -12
  127. package/src/components/Text/types.ts +0 -18
  128. package/src/components/TextInput/types.ts +0 -23
  129. package/src/components/Touchable/types.ts +0 -27
  130. package/src/components/View/types.ts +0 -13
  131. package/src/hooks/index.ts +0 -13
@@ -1,20 +1,50 @@
1
1
  import React, { forwardRef, useImperativeHandle, useRef } from 'react'
2
- import { parseFilePathData, MobileFile } from '@codeleap/common'
2
+ import {
3
+ parseFilePathData,
4
+ useCodeleapContext,
5
+ MobileFile,
6
+ AnyRef,
7
+ FormTypes,
8
+ } from '@codeleap/common'
3
9
  import { OSAlert } from '../../utils'
4
10
  import ImageCropPicker, { ImageOrVideo, Options } from 'react-native-image-crop-picker'
5
- import DocumentPicker from 'react-native-document-picker'
6
- import { FileInputImageSource, FileInputProps, FileInputRef, FileResult } from './types'
11
+ import DocumentPicker, { DocumentPickerOptions } from 'react-native-document-picker'
12
+ export * from './styles'
7
13
 
8
- export * from './types'
14
+ export const useSomething = useImperativeHandle
15
+
16
+ type FileInputImageSource = 'camera' | 'library' | 'fs'
17
+
18
+ type FileResult = FormTypes.AnyFile
19
+
20
+ export type FileInputRef = {
21
+ openFilePicker: (string?: FileInputImageSource) => Promise<FileResult[]>
22
+ }
23
+ export type FileInputProps = {
24
+ mode: 'hidden' | 'button'
25
+ onFileSelect?: (files: FileResult[]) => void
26
+ options?: DocumentPickerOptions<any>
27
+
28
+ ref?: AnyRef<FileInputRef>
29
+
30
+ type?: 'image' | 'anyFile'
31
+ alertProps?: Parameters<typeof OSAlert.ask>[0]
32
+ pickerOptions?: Partial<Options>
33
+ required?: boolean
34
+ onOpenCamera?: (resolve: (() => void)) => Promise<void>
35
+ onOpenFileSystem?: (resolve: (() => void)) => Promise<void>
36
+ onOpenGallery?: (resolve: (() => void)) => Promise<void>
37
+ onError?: (error: any) => void
38
+ }
9
39
 
10
40
  const pickerDefaults = {
11
41
  cropping: true,
12
42
  }
13
43
 
14
44
  function parsePickerData(data: ImageOrVideo): FileResult {
15
- const filePathData = parseFilePathData(data.path)
16
45
 
17
- const file: MobileFile = {
46
+ const filePathData = parseFilePathData(data.path)
47
+ const d: MobileFile = {
18
48
  ...data,
19
49
  name: filePathData.name,
20
50
  size: data.size,
@@ -24,25 +54,32 @@ function parsePickerData(data: ImageOrVideo): FileResult {
24
54
  }
25
55
 
26
56
  return {
27
- file,
57
+ file: d,
28
58
  preview: data.path,
29
59
  }
30
60
  }
31
61
 
32
- const _FileInput = forwardRef<FileInputRef, FileInputProps>((fileInputProps, ref) => {
62
+ const _FileInput = forwardRef<
63
+ FileInputRef,
64
+ FileInputProps
65
+ >((fileInputProps, ref) => {
33
66
  const {
67
+
34
68
  onFileSelect,
69
+
35
70
  options,
36
71
  type = 'image',
37
72
  alertProps,
73
+
38
74
  pickerOptions,
75
+
39
76
  onOpenCamera = null,
40
77
  onOpenGallery = null,
41
78
  onOpenFileSystem = null,
42
79
  onError,
43
80
  } = fileInputProps
44
-
45
81
  const resolveWithFile = useRef<(file: FileResult[]) => any>()
82
+ const { logger } = useCodeleapContext()
46
83
 
47
84
  const handleResolve = (files: Array<FileResult>) => {
48
85
  if (resolveWithFile.current) {
@@ -68,7 +105,10 @@ const _FileInput = forwardRef<FileInputRef, FileInputProps>((fileInputProps, ref
68
105
  }
69
106
 
70
107
  function handleError(err) {
108
+
109
+ logger.warn('File Input Error', err, 'FILE INPUT')
71
110
  onError?.(err)
111
+
72
112
  }
73
113
 
74
114
  const mergedOptions = {
@@ -85,7 +125,6 @@ const _FileInput = forwardRef<FileInputRef, FileInputProps>((fileInputProps, ref
85
125
  }
86
126
  handleResolve?.(imageArray.map(parsePickerData))
87
127
  }
88
-
89
128
  const onPress = (open?: FileInputImageSource, options?: Options) => {
90
129
  if (open == 'fs') {
91
130
  openFileSystem()
@@ -94,7 +133,6 @@ const _FileInput = forwardRef<FileInputRef, FileInputProps>((fileInputProps, ref
94
133
  ImageCropPicker[call]({ ...mergedOptions, ...(options || {}) }).then(handlePickerResolution)
95
134
  }
96
135
  }
97
-
98
136
  const openFilePicker = async (imageSource = null) => {
99
137
  if (type === 'image') {
100
138
  if (imageSource === 'camera') {
@@ -0,0 +1,8 @@
1
+ import { createDefaultVariantFactory, includePresets } from '@codeleap/common'
2
+
3
+ export type FileInputComposition = 'label' | 'wrapper' | 'input'
4
+
5
+ const createFileInputStyle =
6
+ createDefaultVariantFactory<FileInputComposition>()
7
+
8
+ export const FileInputPresets = includePresets((styles) => createFileInputStyle(() => ({ wrapper: styles })))
@@ -1,103 +1,135 @@
1
- import React, { useCallback, forwardRef, ComponentType } from 'react'
2
- import { ListRenderItemInfo } from 'react-native'
1
+ import * as React from 'react'
2
+ import { forwardRef } from 'react'
3
+ import {
4
+ useDefaultComponentStyle,
5
+ ComponentVariants,
6
+ useCallback,
7
+ useCodeleapContext,
8
+ } from '@codeleap/common'
9
+
10
+ import { StyleSheet, ListRenderItemInfo } from 'react-native'
3
11
  import { View, ViewProps } from '../View'
4
12
  import { EmptyPlaceholder } from '../EmptyPlaceholder'
5
13
  import { RefreshControl } from '../RefreshControl'
6
- import { List, ListItem } from '../List'
14
+ import { List } from '../List'
15
+ import { GridPresets } from './styles'
16
+ import { FlatListProps, AugmentedRenderItemInfo } from '../List'
7
17
  import { KeyboardAwareFlatList } from 'react-native-keyboard-aware-scroll-view'
8
- import { GridProps } from './types'
9
- import { AnyRecord, AppTheme, IJSX, StyledComponentProps, StyledComponentWithProps, Theme, useTheme } from '@codeleap/styles'
10
- import { MobileStyleRegistry } from '../../Registry'
11
- import { useStylesFor } from '../../hooks'
12
18
 
13
19
  export * from './styles'
14
- export * from './types'
15
20
 
16
- const RenderSeparator = (props: { separatorStyles: ViewProps['style'] }) => {
17
- return <View style={props.separatorStyles} />
18
- }
19
-
20
- export const Grid = forwardRef<KeyboardAwareFlatList, GridProps>((flatGridProps, ref) => {
21
- const {
22
- style,
23
- onRefresh,
24
- refreshing,
25
- placeholder,
26
- refreshControlProps = {},
27
- spacing,
28
- numColumns,
29
- renderItem: RenderItem,
30
- ...props
31
- } = {
32
- ...Grid.defaultProps,
33
- ...flatGridProps,
34
- }
35
-
36
- const themeSpacing = useTheme(store => (store.current as AppTheme<Theme>)?.spacing)
37
-
38
- const styles = useStylesFor(Grid.styleRegistryName, style)
39
-
40
- const renderItem = useCallback((data: ListRenderItemInfo<any>) => {
41
- if (!RenderItem) return null
21
+ export type DataboundFlatGridPropsTypes = 'data' | 'renderItem' | 'keyExtractor' | 'getItemLayout'
42
22
 
43
- const listLength = props?.data?.length || 0
23
+ export type GridAugmentedRenderItemInfo<T> = AugmentedRenderItemInfo<T> & {
24
+ isFirstInRow: boolean
25
+ isLastInRow: boolean
26
+ isOnlyInRow: boolean
27
+ }
44
28
 
45
- const isFirst = data.index === 0
46
- const isLast = data.index === listLength - 1
47
- const isOnly = isFirst && isLast
29
+ export type GridProps<
30
+ T = any[],
31
+ Data = T extends Array<infer D> ? D : never
32
+ > = Omit<FlatListProps<T, Data>, 'variants' | 'renderItem'> & ComponentVariants<typeof GridPresets> & {
33
+ spacing?: number
34
+ itemDimension?: number
35
+ renderItem: (data: GridAugmentedRenderItemInfo<T>) => React.ReactElement
36
+ }
48
37
 
49
- const idx = data.index + 1
50
- const isLastInRow = !isFirst && idx % (numColumns) === 0
51
- const isFirstInRow = isFirst || data.index % numColumns === 0
52
- const isOnlyInRow = !isFirstInRow && !isLastInRow
38
+ const RenderSeparator = (props: { separatorStyles: ViewProps['style'] }) => {
39
+ return (
40
+ <View style={props.separatorStyles}></View>
41
+ )
42
+ }
53
43
 
54
- let gap = themeSpacing?.marginRight?.(spacing / 2)
55
- if (isLastInRow) gap = themeSpacing?.marginLeft?.(spacing / 2)
56
- else if (isOnlyInRow) gap = themeSpacing?.marginHorizontal?.(spacing / 2)
44
+ const defaultProps: Partial<GridProps> = {
45
+ keyboardShouldPersistTaps: 'handled',
46
+ refreshControlProps: {},
47
+ }
57
48
 
58
- const _itemProps = { isFirst, isLast, isOnly, isFirstInRow, isLastInRow, isOnlyInRow }
49
+ const GridCP = forwardRef<KeyboardAwareFlatList, GridProps>(
50
+ (flatGridProps, ref) => {
51
+ const {
52
+ variants = [],
53
+ style,
54
+ styles = {},
55
+ onRefresh,
56
+ refreshing,
57
+ placeholder,
58
+ refreshControlProps = {},
59
+ spacing,
60
+ numColumns,
61
+ ...props
62
+ } = {
63
+ ...defaultProps,
64
+ ...flatGridProps,
65
+ }
66
+
67
+ const { Theme } = useCodeleapContext()
68
+ const variantStyles = useDefaultComponentStyle<'u:Grid', typeof GridPresets>('u:Grid', {
69
+ variants,
70
+ styles,
71
+ transform: StyleSheet.flatten,
72
+ })
73
+
74
+ const renderItem = useCallback((data: ListRenderItemInfo<any>) => {
75
+ if (!props?.renderItem) return null
76
+ const listLength = props?.data?.length || 0
77
+
78
+ const isFirst = data.index === 0
79
+ const isLast = data.index === listLength - 1
80
+ const isOnly = isFirst && isLast
81
+
82
+ const idx = data.index + 1
83
+ const isLastInRow = !isFirst && idx % (numColumns) === 0
84
+ const isFirstInRow = isFirst || data.index % numColumns === 0
85
+ const isOnlyInRow = !isFirstInRow && !isLastInRow
86
+
87
+ let gap = Theme.spacing.marginRight(spacing / 2)
88
+ if (isLastInRow) gap = Theme.spacing.marginLeft(spacing / 2)
89
+ else if (isOnlyInRow) gap = Theme.spacing.marginHorizontal(spacing / 2)
90
+
91
+ const _itemProps = { isFirst, isLast, isOnly, isFirstInRow, isLastInRow, isOnlyInRow }
92
+ const RenderItem = props?.renderItem
93
+
94
+ return (
95
+ <View style={{ ...variantStyles.itemWrapper, ...gap }}>
96
+ <RenderItem {...data} {..._itemProps} />
97
+ </View>
98
+ )
99
+ }, [props?.renderItem, props?.data?.length])
100
+
101
+ const separatorStyles = { height: Theme.spacing.value(spacing), ...variantStyles.separator }
102
+ const separator = props?.separators || (!!spacing ? <RenderSeparator separatorStyles={separatorStyles} /> : null)
103
+ const refreshControl = !!onRefresh && <RefreshControl refreshing={refreshing} onRefresh={onRefresh} {...refreshControlProps} />
104
+ const _gridProps = {
105
+ ...props,
106
+ ref: ref,
107
+ ListEmptyComponent: <EmptyPlaceholder {...placeholder} />,
108
+ ListHeaderComponentStyle: variantStyles.header,
109
+ ListFooterComponentStyle: variantStyles.footer,
110
+ ItemSeparatorComponent: separator,
111
+ refreshControl,
112
+ style: [variantStyles.wrapper, style],
113
+ contentContainerStyle: [variantStyles.content, props?.contentContainerStyle],
114
+ showsVerticalScrollIndicator: false,
115
+ numColumns,
116
+ renderItem,
117
+ }
59
118
 
60
119
  return (
61
- <View style={[styles.itemWrapper, gap]}>
62
- <RenderItem {...data} {..._itemProps} />
63
- </View>
120
+ // @ts-ignore
121
+ <List
122
+ {..._gridProps}
123
+ />
64
124
  )
65
- }, [RenderItem, props?.data?.length])
125
+ },
126
+ )
66
127
 
67
- const separatorStyles = { height: themeSpacing?.value?.(spacing), ...styles.separator }
68
- const separator = props?.separators || (!!spacing ? <RenderSeparator separatorStyles={separatorStyles} /> : null)
69
- const refreshControl = !!onRefresh && <RefreshControl refreshing={refreshing} onRefresh={onRefresh} {...refreshControlProps} />
70
-
71
- return (
72
- <List
73
- // @ts-expect-error
74
- ref={ref}
75
- {...props}
76
- ListEmptyComponent={<EmptyPlaceholder {...placeholder} />}
77
- ListHeaderComponentStyle={styles.header}
78
- ListFooterComponentStyle={styles.footer}
79
- ItemSeparatorComponent={separator as unknown as ComponentType}
80
- refreshControl={refreshControl}
81
- style={styles.wrapper}
82
- contentContainerStyle={[styles.content, props?.contentContainerStyle]}
83
- showsVerticalScrollIndicator={false}
84
- numColumns={numColumns}
85
- renderItem={renderItem}
86
- />
87
- )
88
- }) as StyledComponentWithProps<GridProps>
89
-
90
- Grid.styleRegistryName = 'Grid'
91
- Grid.elements = ['wrapper', 'content', 'separator', 'header', 'refreshControl', 'itemWrapper', 'footer']
92
- Grid.rootElement = 'wrapper'
93
-
94
- Grid.withVariantTypes = <S extends AnyRecord>(styles: S) => {
95
- return Grid as (<T extends ListItem>(props: StyledComponentProps<GridProps<T>, typeof styles>) => IJSX)
128
+ export type GridComponentType = (<T extends any[] = any[]>(props: GridProps<T>) => JSX.Element) & {
129
+ defaultProps?: Partial<GridProps>
96
130
  }
97
131
 
98
- Grid.defaultProps = {
99
- keyboardShouldPersistTaps: 'handled',
100
- refreshControlProps: {},
101
- } as Partial<GridProps>
132
+ export const Grid = GridCP as unknown as GridComponentType
133
+
134
+ Grid.defaultProps = defaultProps
102
135
 
103
- MobileStyleRegistry.registerComponent(Grid)
@@ -1,3 +1,8 @@
1
+ import { createDefaultVariantFactory, includePresets } from '@codeleap/common'
1
2
  import { ListComposition } from '../List'
2
3
 
3
4
  export type GridComposition = ListComposition | 'itemWrapper' | 'footer'
5
+
6
+ const createGridStyle = createDefaultVariantFactory<GridComposition>()
7
+
8
+ export const GridPresets = includePresets(style => createGridStyle(() => ({ content: style })))
@@ -1,34 +1,79 @@
1
- import React from 'react'
2
- import { arePropsEqual, TypeGuards } from '@codeleap/common'
3
- import { Badge } from '../Badge'
1
+ import * as React from 'react'
2
+ import {
3
+ ComponentVariants,
4
+
5
+ useDefaultComponentStyle,
6
+ useCodeleapContext,
7
+ arePropsEqual,
8
+ IconPlaceholder,
9
+ onUpdate,
10
+ PropsOf,
11
+ StylesOf,
12
+ TypeGuards,
13
+ getNestedStylesByKey,
14
+ } from '@codeleap/common'
15
+ import { StyleSheet } from 'react-native'
16
+ export * from './styles'
17
+
18
+ import {
19
+ IconComposition,
20
+ IconPresets,
21
+ } from './styles'
22
+ import { Badge, BadgeComponentProps } from '../Badge'
4
23
  import { View } from '../View'
5
- import { IconProps } from './types'
6
- import { useNestedStylesByKey, AnyRecord, StyledComponentProps, IJSX, useTheme, StyledComponentWithProps, AppTheme, Theme } from '@codeleap/styles'
7
- import { MobileStyleRegistry } from '../../Registry'
8
- import { useStylesFor } from '../../hooks'
9
24
 
10
- export * from './styles'
11
- export * from './types'
25
+ export type IconProps = {
26
+ name: IconPlaceholder
27
+ style?: any
28
+ color?: string
29
+ variants?: ComponentVariants<typeof IconPresets>['variants']
30
+ wrapperProps?: Partial<PropsOf<typeof View>>
31
+ size?: number
32
+ styles?: StylesOf<IconComposition>
33
+ source?: string
34
+ } & BadgeComponentProps
12
35
 
13
36
  export const IconComponent = (props: IconProps) => {
14
37
  const {
15
38
  name,
16
39
  style,
17
- badge,
40
+ variants,
41
+ badge = false,
18
42
  badgeProps = {},
19
43
  wrapperProps = {},
44
+ styles = {},
20
45
  source,
21
46
  ...otherProps
22
- } = {
23
- ...Icon.defaultProps,
24
- ...props,
25
- }
26
-
27
- const icons = useTheme(store => (store.current as AppTheme<Theme>)?.icons)
28
-
29
- const styles = useStylesFor(Icon.styleRegistryName, style)
30
-
31
- const Component = icons?.[name]
47
+ } = props
48
+
49
+ const { Theme, logger } = useCodeleapContext()
50
+
51
+ const variantStyles = useDefaultComponentStyle<'u:Icon', typeof IconPresets>('u:Icon', {
52
+ variants,
53
+ transform: StyleSheet.flatten,
54
+ styles: {
55
+ icon: style,
56
+ ...styles,
57
+ },
58
+ rootElement: 'icon',
59
+ })
60
+ const Component = Theme?.icons?.[name] || (source && Theme.icons.RenderSource)
61
+
62
+ onUpdate(() => {
63
+ if (!Component && !!name) {
64
+ logger.warn(
65
+ `Icon: No icon found in theme for name "${name}".`,
66
+ { props: { style, name, variants, variantStyles } },
67
+ 'Component',
68
+ )
69
+ } else if (!Component && !!source) {
70
+ logger.warn('Icon: Cannot render source, no RenderSource component in Theme.icons', {
71
+ source,
72
+ props: { style, name, variants, variantStyles },
73
+ Component,
74
+ }, 'Component')
75
+ }
76
+ }, [name])
32
77
 
33
78
  if (!name && !source) {
34
79
  return null
@@ -39,42 +84,32 @@ export const IconComponent = (props: IconProps) => {
39
84
  }
40
85
 
41
86
  if (badge || TypeGuards.isNumber(badge)) {
42
- const badgeStyles = useNestedStylesByKey('badge', styles)
87
+ const badgeStyles = getNestedStylesByKey('badge', variantStyles)
43
88
 
44
89
  const sized = {
45
- // @ts-expect-error
46
- height: styles.icon?.size || styles.icon?.height || props?.size,
47
- // @ts-expect-error
48
- width: styles.icon?.size || styles.icon?.width || props?.size,
90
+ height: variantStyles.icon?.size || variantStyles.icon?.height || props?.size,
91
+ width: variantStyles.icon?.size || variantStyles.icon?.width || props?.size,
49
92
  }
50
93
 
51
- return <View {...wrapperProps} style={[sized, styles.iconBadgeWrapper]}>
52
- <Component {...otherProps} style={styles.icon} source={source} />
53
- <Badge style={badgeStyles} badge={badge} {...badgeProps} />
94
+ const wrapperStyle = [
95
+ sized,
96
+ (variantStyles.iconBadgeWrapper ?? {}),
97
+ ]
98
+
99
+ return <View {...wrapperProps} style={wrapperStyle}>
100
+ <Component {...otherProps} style={variantStyles.icon} source={source} />
101
+ <Badge styles={badgeStyles} badge={badge} {...badgeProps} />
54
102
  </View>
55
103
  }
56
104
 
57
- return <Component {...otherProps} style={styles.icon} source={source} />
105
+ return <Component {...otherProps} style={variantStyles.icon} source={source} />
58
106
  }
59
107
 
60
108
  function areEqual(prevProps, nextProps) {
61
- const check = ['name', 'style', 'renderEmptySpace', 'badgeProps', 'badge']
109
+ const check = ['name', 'style', 'variants', 'renderEmptySpace', 'badgeProps', 'badge']
62
110
  const res = arePropsEqual(prevProps, nextProps, { check })
63
111
  return res
64
112
  }
65
113
 
66
- export const Icon = React.memo(IconComponent, areEqual) as StyledComponentWithProps<IconProps>
67
-
68
- Icon.styleRegistryName = 'Icon'
69
- Icon.elements = ['icon', 'iconBadgeWrapper', 'badge']
70
- Icon.rootElement = 'icon'
71
-
72
- Icon.defaultProps = {
73
- badge: false,
74
- } as Partial<IconProps>
75
-
76
- Icon.withVariantTypes = <S extends AnyRecord>(styles: S) => {
77
- return Icon as (props: StyledComponentProps<IconProps, typeof styles>) => IJSX
78
- }
114
+ export const Icon = React.memo(IconComponent, areEqual) as typeof IconComponent
79
115
 
80
- MobileStyleRegistry.registerComponent(Icon)
@@ -1,3 +1,9 @@
1
+ import { createDefaultVariantFactory, includePresets } from '@codeleap/common'
1
2
  import { BadgeComposition } from '../Badge'
2
3
 
3
4
  export type IconComposition = 'icon' | 'iconBadgeWrapper' | `badge${Capitalize<BadgeComposition>}`
5
+
6
+ const createIconStyle = createDefaultVariantFactory<IconComposition>()
7
+
8
+ export const IconPresets = includePresets((styles) => createIconStyle(() => ({ icon: styles })))
9
+