Skip to content

Commit 150258f

Browse files
committed
Provide initial tables component. DataTable will be provided later.
1 parent d19ac85 commit 150258f

44 files changed

Lines changed: 2174 additions & 391 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/carbon-react-native/carbon-icons.d.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ declare module "@carbon/icons/svg/*.svg" {
77
SvgProps,
88
} from "react-native-svg"
99

10-
const Component: React.FunctionComponent<SvgProps>
10+
const Component: React.ForwardRefExoticComponent<
11+
& SvgProps
12+
& React.RefAttributes<Svg>
13+
>
1114
export default Component
1215
}

packages/carbon-react-native/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@audira/carbon-react-native",
3-
"version": "1.0.3",
3+
"version": "1.0.4",
44
"license": "MIT",
55
"homepage": "https://rakadoank.github.io/carbon-react-native",
66
"repository": "https://github.com/RakaDoank/carbon-react-native",

packages/carbon-react-native/src/components/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ export * from "./radio-button"
3030
export * from "./radio-button-group"
3131
export * from "./radio-button-input"
3232
export * from "./switch"
33+
export * from "./table"
34+
export * from "./table-cell"
35+
export * from "./table-cell-header"
36+
export * from "./table-cell-icon"
37+
export * from "./table-cell-text"
38+
export * from "./table-row"
39+
export * from "./table-row-header"
3340
export * from "./text"
3441
export * from "./text-area"
3542
export * from "./text-area-field"
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import {
2+
forwardRef,
3+
useCallback,
4+
useContext,
5+
useEffect,
6+
useRef,
7+
useState,
8+
} from "react"
9+
10+
import {
11+
Pressable,
12+
type ViewStyle,
13+
} from "react-native"
14+
15+
import type {
16+
ColorLayerLevel,
17+
} from "@audira/carbon-react-native-elements"
18+
19+
import * as CarbonStyleSheet from "../../carbon-style-sheet"
20+
21+
import {
22+
LayerContext,
23+
} from "../layer/LayerContext"
24+
25+
import {
26+
TableCellText,
27+
} from "../table-cell-text/TableCellText"
28+
29+
import {
30+
TableRowHeaderContext,
31+
} from "../table-row-header/_TableRowHeaderContext"
32+
33+
import type {
34+
TableCellHeaderProps,
35+
} from "./TableCellHeaderProps"
36+
37+
import type {
38+
TableCellHeaderRef,
39+
} from "./TableCellHeaderRef"
40+
41+
import {
42+
SortIcon,
43+
type SortIconRef,
44+
} from "./_sort-icon"
45+
46+
/**
47+
* Similar as the `TableCell`. `TableCellHeader` is also a View to render
48+
* container with a correct horizontal padding, with additional
49+
* sorting icon option and its coloring.
50+
*
51+
* It's better to use the `TableCell` instead of `TableCellHeader`
52+
* if a header cell is not using the sorting icon at all to save a bit of memory
53+
* because `TableCellHeader` has bunch of Pressable logics and two icons loaded.
54+
*
55+
* To ensure consistent column alignment across rows,
56+
* you need to provide fixed `width` of the cell.
57+
* Sets an explicit width for the cell (in pixels) and
58+
* acts as a column constraint. This helps simulate
59+
* table-like behavior, since React Native relies on
60+
* flexbox and does not provide native table layout.
61+
* Without this, cells in the same column may render
62+
* with inconsistent widths across different rows.
63+
*/
64+
export const TableCellHeader = forwardRef<TableCellHeaderRef, TableCellHeaderProps>(
65+
function TableCellHeader(
66+
{
67+
defaultSort,
68+
sort: sortProp,
69+
width,
70+
children,
71+
text,
72+
textProps,
73+
accessibilityLabel,
74+
onChangeSort,
75+
disabled,
76+
onHoverIn: onHoverInProp,
77+
onHoverOut: onHoverOutProp,
78+
onPress: onPressProp,
79+
style,
80+
...props
81+
},
82+
ref,
83+
) {
84+
85+
const
86+
layerContext =
87+
useContext(LayerContext),
88+
89+
tableRowHeaderContext =
90+
useContext(TableRowHeaderContext),
91+
92+
sortIconRef =
93+
useRef<SortIconRef>(null),
94+
95+
allowOnChangeSortSelfEffect =
96+
useRef(false),
97+
98+
[sortSelf, setSortSelf] =
99+
useState(defaultSort),
100+
101+
sort =
102+
sortProp ?? sortSelf,
103+
104+
[hovered, setHovered] =
105+
useState(false)
106+
107+
const
108+
onHoverIn: NonNullable<typeof onHoverInProp> =
109+
useCallback(event => {
110+
if(sortIconRef.current) {
111+
setHovered(true)
112+
}
113+
114+
onHoverInProp?.(event)
115+
}, [
116+
onHoverInProp,
117+
]),
118+
119+
onHoverOut: NonNullable<typeof onHoverOutProp> =
120+
useCallback(event => {
121+
if(sortIconRef.current) {
122+
setHovered(false)
123+
}
124+
125+
onHoverOutProp?.(event)
126+
}, [
127+
onHoverOutProp,
128+
]),
129+
130+
onPress: NonNullable<typeof onPressProp> =
131+
useCallback(event => {
132+
onPressProp?.(event)
133+
134+
if(defaultSort) {
135+
allowOnChangeSortSelfEffect.current = true
136+
setSortSelf(s => {
137+
if(s == "none") {
138+
return "asc"
139+
} else if(s == "asc") {
140+
return "desc"
141+
} else {
142+
return "none"
143+
}
144+
})
145+
} else if(sortProp && onChangeSort) {
146+
let nextSort: NonNullable<typeof sort> = "none"
147+
if(sortProp == "none") {
148+
nextSort = "asc"
149+
} else if(sortProp == "asc") {
150+
nextSort = "desc"
151+
} else {
152+
nextSort = "none"
153+
}
154+
onChangeSort(nextSort)
155+
}
156+
}, [
157+
sortProp,
158+
defaultSort,
159+
onChangeSort,
160+
onPressProp,
161+
])
162+
163+
useEffect(() => {
164+
if(sort) {
165+
if(sort == "none") {
166+
if(hovered) {
167+
sortIconRef.current?.setOpacity(1)
168+
} else {
169+
sortIconRef.current?.setOpacity(0)
170+
}
171+
} else {
172+
sortIconRef.current?.setOpacity(1)
173+
}
174+
}
175+
}, [
176+
hovered,
177+
sort,
178+
])
179+
180+
useEffect(() => {
181+
if(allowOnChangeSortSelfEffect.current && sort) {
182+
allowOnChangeSortSelfEffect.current = false
183+
onChangeSort?.(sort)
184+
}
185+
}, [
186+
sort,
187+
onChangeSort,
188+
])
189+
190+
CarbonStyleSheet.use()
191+
192+
return (
193+
<Pressable
194+
ref={ ref }
195+
{ ...props }
196+
accessibilityLabel={ accessibilityLabel || text }
197+
disabled={ disabled ?? !sort }
198+
onHoverIn={ onHoverIn }
199+
onHoverOut={ onHoverOut }
200+
onPress={ onPress }
201+
style={ [
202+
CarbonStyleSheet.g.flex_auto,
203+
CarbonStyleSheet.g.px_05,
204+
205+
typeof width === "number" ? {
206+
width,
207+
} : undefined,
208+
209+
hovered || sort === "asc" || sort === "desc"
210+
? styleSheetBG[`hovered_${layerContext}`]
211+
: styleSheetBG[`normal_${layerContext}`],
212+
213+
tableRowHeaderContext.size == "extra_large"
214+
? CarbonStyleSheet.g.pt_05
215+
: sort
216+
? CarbonStyleSheet.g.items_center
217+
: CarbonStyleSheet.g.justify_center,
218+
sort
219+
? [
220+
CarbonStyleSheet.g.flex_row,
221+
CarbonStyleSheet.g.justify_between,
222+
]
223+
: undefined,
224+
style,
225+
] }
226+
>
227+
{ typeof children === "undefined" && !!text ? (
228+
<TableCellText
229+
{ ...textProps }
230+
>
231+
{ text }
232+
</TableCellText>
233+
) : children }
234+
235+
{ !!sort && (
236+
<SortIcon
237+
type={ sort }
238+
ref={ sortIconRef }
239+
/>
240+
) }
241+
</Pressable>
242+
)
243+
244+
},
245+
)
246+
247+
const
248+
styleSheetBG =
249+
CarbonStyleSheet.create({
250+
normal_1: {
251+
backgroundColor: CarbonStyleSheet.color.layer_accent_01,
252+
},
253+
normal_2: {
254+
backgroundColor: CarbonStyleSheet.color.layer_accent_02,
255+
},
256+
normal_3: {
257+
backgroundColor: CarbonStyleSheet.color.layer_accent_03,
258+
},
259+
hovered_1: {
260+
backgroundColor: CarbonStyleSheet.color.layer_accent_hover_01,
261+
},
262+
hovered_2: {
263+
backgroundColor: CarbonStyleSheet.color.layer_accent_hover_02,
264+
},
265+
hovered_3: {
266+
backgroundColor: CarbonStyleSheet.color.layer_accent_hover_03,
267+
},
268+
} as const satisfies {
269+
[Level in `${StateColor}_${ColorLayerLevel}`]: NonNullable<Pick<ViewStyle, "backgroundColor">>
270+
})
271+
272+
type StateColor =
273+
| "normal"
274+
| "hovered"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type {
2+
PressableProps,
3+
} from "react-native"
4+
5+
import type {
6+
TableCellProps,
7+
} from "../table-cell/TableCellProps"
8+
9+
import type {
10+
TableCellHeaderSort,
11+
} from "./TableCellHeaderSort"
12+
13+
export interface TableCellHeaderProps extends
14+
Omit<
15+
PressableProps,
16+
| "children"
17+
| "style"
18+
>,
19+
TableCellProps
20+
{
21+
/**
22+
* Render the sort icon either ascending (arrow up), or descending (arrow up rotated), or none (arrows vertical).
23+
*
24+
* This is just an icon rendering, you have to sort your actual data manually.
25+
*/
26+
defaultSort?: TableCellHeaderSort,
27+
/**
28+
* Render the sort icon either ascending (arrow up), or descending (arrow up rotated), or none (arrows vertical).
29+
*
30+
* This is just an icon rendering, you have to sort your actual data manually.
31+
*/
32+
sort?: TableCellHeaderSort,
33+
onChangeSort?: (sort: TableCellHeaderSort) => void,
34+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type {
2+
TableCellRef,
3+
} from "../table-cell/TableCellRef"
4+
5+
export interface TableCellHeaderRef extends TableCellRef {
6+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type TableCellHeaderSort =
2+
| "none"
3+
| "asc"
4+
| "desc"

0 commit comments

Comments
 (0)