@idealyst/components 1.2.124 → 1.2.126

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/components",
3
- "version": "1.2.124",
3
+ "version": "1.2.126",
4
4
  "description": "Shared component library for React and React Native",
5
5
  "documentation": "https://github.com/IdealystIO/idealyst-framework/tree/main/packages/components#readme",
6
6
  "readme": "README.md",
@@ -56,7 +56,7 @@
56
56
  "publish:npm": "npm publish"
57
57
  },
58
58
  "peerDependencies": {
59
- "@idealyst/theme": "^1.2.124",
59
+ "@idealyst/theme": "^1.2.126",
60
60
  "@mdi/js": ">=7.0.0",
61
61
  "@mdi/react": ">=1.0.0",
62
62
  "@react-native-vector-icons/common": ">=12.0.0",
@@ -111,8 +111,8 @@
111
111
  },
112
112
  "devDependencies": {
113
113
  "@idealyst/blur": "^1.2.40",
114
- "@idealyst/theme": "^1.2.124",
115
- "@idealyst/tooling": "^1.2.124",
114
+ "@idealyst/theme": "^1.2.126",
115
+ "@idealyst/tooling": "^1.2.126",
116
116
  "@mdi/react": "^1.6.1",
117
117
  "@types/react": "^19.1.0",
118
118
  "react": "^19.1.0",
@@ -125,17 +125,13 @@ export const tabBarStyles = defineStyle('TabBar', (theme: Theme) => ({
125
125
  opacity: disabled ? 0.5 : 1,
126
126
  ...tabPadding,
127
127
  variants: {
128
- size: type === 'pills'
129
- ? {
130
- fontSize: theme.sizes.$tabBar.fontSize,
131
- lineHeight: theme.sizes.$tabBar.lineHeight,
132
- }
133
- : {
134
- fontSize: theme.sizes.$tabBar.fontSize,
135
- paddingVertical: theme.sizes.$tabBar.padding,
136
- paddingHorizontal: theme.sizes.$tabBar.padding,
137
- lineHeight: theme.sizes.$tabBar.lineHeight,
138
- },
128
+ size: {
129
+ fontSize: theme.sizes.$tabBar.fontSize,
130
+ lineHeight: theme.sizes.$tabBar.lineHeight,
131
+ paddingTop: type === 'pills' ? undefined : theme.sizes.$tabBar.padding,
132
+ paddingBottom: type === 'pills' ? undefined : theme.sizes.$tabBar.paddingBottom,
133
+ paddingHorizontal: type === 'pills' ? undefined : theme.sizes.$tabBar.padding,
134
+ },
139
135
  },
140
136
  _web: {
141
137
  border: 'none',
@@ -66,6 +66,16 @@ const Tab: React.FC<TabProps> = ({
66
66
  }) => {
67
67
  const iconSize = ICON_SIZES[size || 'md'] || 18;
68
68
 
69
+ // Apply tab variants (size) for variant expansion (padding, fontSize, lineHeight)
70
+ tabBarTabStyles.useVariants({
71
+ size,
72
+ });
73
+
74
+ // Apply label variants (size) for variant expansion
75
+ tabBarLabelStyles.useVariants({
76
+ size,
77
+ });
78
+
69
79
  // Apply icon variants (size, disabled, iconPosition)
70
80
  tabBarIconStyles.useVariants({
71
81
  size,
@@ -93,9 +93,11 @@ function TH({
93
93
  { width, flex: width ? undefined : 1 },
94
94
  ]}
95
95
  >
96
- <Text style={headerCellStyle}>
97
- {children}
98
- </Text>
96
+ {typeof children === 'string' ? (
97
+ <Text style={headerCellStyle}>{children}</Text>
98
+ ) : (
99
+ children
100
+ )}
99
101
  </View>
100
102
  );
101
103
  }
@@ -131,6 +133,49 @@ function TD({
131
133
  );
132
134
  }
133
135
 
136
+ // ============================================================================
137
+ // TF Component (Footer Cell)
138
+ // ============================================================================
139
+
140
+ interface TFProps {
141
+ children: ReactNode;
142
+ size?: TableSizeVariant;
143
+ type?: TableType;
144
+ align?: TableAlignVariant;
145
+ width?: number | string;
146
+ }
147
+
148
+ function TF({
149
+ children,
150
+ size = 'md',
151
+ type = 'standard',
152
+ align = 'left',
153
+ width,
154
+ }: TFProps) {
155
+ tableStyles.useVariants({
156
+ size,
157
+ type,
158
+ align,
159
+ });
160
+
161
+ const footerCellStyle = (tableStyles.footerCell as any)({});
162
+
163
+ return (
164
+ <View
165
+ style={[
166
+ footerCellStyle,
167
+ { width, flex: width ? undefined : 1 },
168
+ ]}
169
+ >
170
+ {typeof children === 'string' ? (
171
+ <Text style={footerCellStyle}>{children}</Text>
172
+ ) : (
173
+ children
174
+ )}
175
+ </View>
176
+ );
177
+ }
178
+
134
179
  // ============================================================================
135
180
  // Main Table Component
136
181
  // ============================================================================
@@ -200,6 +245,15 @@ function TableInner<T = any>({
200
245
  };
201
246
 
202
247
  const isClickable = !!onRowPress;
248
+ const hasFooter = columns.some((col) => col.footer !== undefined);
249
+
250
+ // Helper to resolve footer content
251
+ const getFooterContent = (column: TableColumn<T>) => {
252
+ if (typeof column.footer === 'function') {
253
+ return column.footer(data);
254
+ }
255
+ return column.footer;
256
+ };
203
257
 
204
258
  return (
205
259
  <ScrollView
@@ -253,6 +307,25 @@ function TableInner<T = any>({
253
307
  </TR>
254
308
  ))}
255
309
  </View>
310
+
311
+ {/* Footer */}
312
+ {hasFooter && (
313
+ <View style={(tableStyles.tfoot as any)({})}>
314
+ <View style={{ flexDirection: 'row' }}>
315
+ {columns.map((column) => (
316
+ <TF
317
+ key={column.key}
318
+ size={size}
319
+ type={type}
320
+ align={column.align}
321
+ width={column.width}
322
+ >
323
+ {getFooterContent(column) ?? null}
324
+ </TF>
325
+ ))}
326
+ </View>
327
+ </View>
328
+ )}
256
329
  </View>
257
330
  </ScrollView>
258
331
  );
@@ -88,6 +88,46 @@ export const tableStyles = defineStyle('Table', (theme: Theme) => ({
88
88
 
89
89
  tbody: (_props: TableDynamicProps) => ({}),
90
90
 
91
+ tfoot: (_props: TableDynamicProps) => ({
92
+ backgroundColor: theme.colors.surface.secondary,
93
+ }),
94
+
95
+ footerCell: ({ type = 'standard', align = 'left' }: TableDynamicProps) => {
96
+ const alignStyles = {
97
+ left: { textAlign: 'left' as const, justifyContent: 'flex-start' as const },
98
+ center: { textAlign: 'center' as const, justifyContent: 'center' as const },
99
+ right: { textAlign: 'right' as const, justifyContent: 'flex-end' as const },
100
+ }[align];
101
+
102
+ const borderStyles = type === 'bordered' ? {
103
+ borderRightWidth: 1,
104
+ borderRightColor: theme.colors.border.primary,
105
+ } : {};
106
+
107
+ return {
108
+ flexDirection: 'row' as const,
109
+ alignItems: 'center' as const,
110
+ fontWeight: '600' as const,
111
+ color: theme.colors.text.primary,
112
+ borderTopWidth: 2,
113
+ borderTopColor: theme.colors.border.primary,
114
+ ...alignStyles,
115
+ ...borderStyles,
116
+ variants: {
117
+ size: {
118
+ padding: theme.sizes.$table.padding,
119
+ fontSize: theme.sizes.$table.fontSize,
120
+ lineHeight: theme.sizes.$table.lineHeight,
121
+ },
122
+ },
123
+ _web: {
124
+ borderTop: `2px solid ${theme.colors.border.primary}`,
125
+ borderRight: type === 'bordered' ? `1px solid ${theme.colors.border.primary}` : undefined,
126
+ ':last-child': type === 'bordered' ? { borderRight: 'none' } : {},
127
+ },
128
+ } as const;
129
+ },
130
+
91
131
  row: ({ type = 'standard', clickable = false }: TableDynamicProps) => {
92
132
  const typeStyles = type === 'bordered' || type === 'striped' ? {
93
133
  borderBottomWidth: 1,
@@ -126,6 +126,43 @@ function TD({
126
126
  );
127
127
  }
128
128
 
129
+ // ============================================================================
130
+ // TF Component (Footer Cell)
131
+ // ============================================================================
132
+
133
+ interface TFProps {
134
+ children: ReactNode;
135
+ size?: TableSizeVariant;
136
+ type?: TableType;
137
+ align?: TableAlignVariant;
138
+ width?: number | string;
139
+ }
140
+
141
+ function TF({
142
+ children,
143
+ size = 'md',
144
+ type = 'standard',
145
+ align = 'left',
146
+ width,
147
+ }: TFProps) {
148
+ tableStyles.useVariants({
149
+ size,
150
+ type,
151
+ align,
152
+ });
153
+
154
+ const footerCellProps = getWebProps([(tableStyles.footerCell as any)({})]);
155
+
156
+ return (
157
+ <td
158
+ {...footerCellProps}
159
+ style={{ width }}
160
+ >
161
+ {children}
162
+ </td>
163
+ );
164
+ }
165
+
129
166
  // ============================================================================
130
167
  // Main Table Component
131
168
  // ============================================================================
@@ -194,6 +231,15 @@ function Table<T = any>({
194
231
  };
195
232
 
196
233
  const isClickable = !!onRowPress;
234
+ const hasFooter = columns.some((col) => col.footer !== undefined);
235
+
236
+ // Helper to resolve footer content
237
+ const getFooterContent = (column: TableColumn<T>) => {
238
+ if (typeof column.footer === 'function') {
239
+ return column.footer(data);
240
+ }
241
+ return column.footer;
242
+ };
197
243
 
198
244
  return (
199
245
  <div {...containerProps} {...ariaProps} id={id} data-testid={testID}>
@@ -238,6 +284,23 @@ function Table<T = any>({
238
284
  </TR>
239
285
  ))}
240
286
  </tbody>
287
+ {hasFooter && (
288
+ <tfoot {...getWebProps([(tableStyles.tfoot as any)({})])}>
289
+ <tr>
290
+ {columns.map((column) => (
291
+ <TF
292
+ key={column.key}
293
+ size={size}
294
+ type={type}
295
+ align={column.align}
296
+ width={column.width}
297
+ >
298
+ {getFooterContent(column) ?? null}
299
+ </TF>
300
+ ))}
301
+ </tr>
302
+ </tfoot>
303
+ )}
241
304
  </table>
242
305
  </div>
243
306
  );
@@ -11,9 +11,10 @@ export type TableAlignVariant = 'left' | 'center' | 'right';
11
11
 
12
12
  export interface TableColumn<T = any> extends SortableAccessibilityProps {
13
13
  key: string;
14
- title: string;
14
+ title: ReactNode;
15
15
  dataIndex?: string;
16
16
  render?: (value: any, row: T, index: number) => ReactNode;
17
+ footer?: ReactNode | ((data: T[]) => ReactNode);
17
18
  width?: number | string;
18
19
  align?: TableAlignVariant;
19
20
  }