@xaui/native 0.0.19 → 0.0.21

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.
@@ -0,0 +1,188 @@
1
+ import React, { ReactNode } from 'react';
2
+ import * as react_native from 'react-native';
3
+ import { ViewStyle, TextStyle, GestureResponderEvent } from 'react-native';
4
+ import { T as ThemeColor, S as Size } from '../index-BOw6tbkc.js';
5
+
6
+ type ListSelectionMode = 'single' | 'multiple' | 'none';
7
+ type ListItemCustomAppearance = {
8
+ /**
9
+ * Custom styles for the item container
10
+ */
11
+ container?: ViewStyle;
12
+ /**
13
+ * Custom styles for the content container
14
+ */
15
+ content?: ViewStyle;
16
+ /**
17
+ * Custom styles for the title text
18
+ */
19
+ title?: TextStyle;
20
+ /**
21
+ * Custom styles for the description text
22
+ */
23
+ description?: TextStyle;
24
+ };
25
+ type ListItemProps = {
26
+ /**
27
+ * Unique key for the item (used for selection)
28
+ */
29
+ itemKey: string;
30
+ /**
31
+ * Title text for the ListItem
32
+ */
33
+ title: ReactNode;
34
+ /**
35
+ * Optional description text
36
+ */
37
+ description?: ReactNode;
38
+ /**
39
+ * Content to display at the start of the item
40
+ */
41
+ startContent?: ReactNode;
42
+ /**
43
+ * Content to display at the end of the item
44
+ */
45
+ endContent?: ReactNode;
46
+ /**
47
+ * Whether the item is disabled
48
+ * @default false
49
+ */
50
+ isDisabled?: boolean;
51
+ /**
52
+ * Whether the item is selected
53
+ * @default false
54
+ */
55
+ isSelected?: boolean;
56
+ /**
57
+ * Custom appearance styles for item parts
58
+ */
59
+ customAppearance?: ListItemCustomAppearance;
60
+ /**
61
+ * Callback fired when the item is pressed
62
+ */
63
+ onPress?: (event: GestureResponderEvent) => void;
64
+ };
65
+ type ListProps = {
66
+ /**
67
+ * List items
68
+ */
69
+ children: ReactNode;
70
+ /**
71
+ * Selection mode for the list
72
+ * @default 'none'
73
+ */
74
+ selectionMode?: ListSelectionMode;
75
+ /**
76
+ * Controlled selected keys
77
+ */
78
+ selectedKeys?: string[];
79
+ /**
80
+ * Default selected keys (uncontrolled)
81
+ */
82
+ defaultSelectedKeys?: string[];
83
+ /**
84
+ * Whether to show dividers between items
85
+ * @default false
86
+ */
87
+ showDivider?: boolean;
88
+ /**
89
+ * Whether items are pressable
90
+ * @default true
91
+ */
92
+ isPressable?: boolean;
93
+ /**
94
+ * Whether items are selectable
95
+ * @default false
96
+ */
97
+ isSelectable?: boolean;
98
+ /**
99
+ * Theme color for selected items
100
+ * @default 'primary'
101
+ */
102
+ themeColor?: ThemeColor;
103
+ /**
104
+ * Size of the list items
105
+ * @default 'md'
106
+ */
107
+ size?: Size;
108
+ /**
109
+ * Callback fired when selection changes
110
+ */
111
+ onSelectionChange?: (keys: string[]) => void;
112
+ /**
113
+ * Custom styles for the list container
114
+ */
115
+ style?: ViewStyle;
116
+ };
117
+ type ListBuilderProps<T> = {
118
+ /**
119
+ * Data array to render
120
+ */
121
+ data: T[];
122
+ /**
123
+ * Function to extract unique key from item
124
+ */
125
+ keyExtractor: (item: T, index: number) => string;
126
+ /**
127
+ * Function to render each item
128
+ */
129
+ renderItem: (item: T, index: number) => ReactNode;
130
+ /**
131
+ * Selection mode for the list
132
+ * @default 'none'
133
+ */
134
+ selectionMode?: ListSelectionMode;
135
+ /**
136
+ * Controlled selected keys
137
+ */
138
+ selectedKeys?: string[];
139
+ /**
140
+ * Default selected keys (uncontrolled)
141
+ */
142
+ defaultSelectedKeys?: string[];
143
+ /**
144
+ * Whether to show dividers between items
145
+ * @default false
146
+ */
147
+ showDivider?: boolean;
148
+ /**
149
+ * Whether items are pressable
150
+ * @default true
151
+ */
152
+ isPressable?: boolean;
153
+ /**
154
+ * Whether items are selectable
155
+ * @default false
156
+ */
157
+ isSelectable?: boolean;
158
+ /**
159
+ * Theme color for selected items
160
+ * @default 'primary'
161
+ */
162
+ themeColor?: ThemeColor;
163
+ /**
164
+ * Size of the list items
165
+ * @default 'md'
166
+ */
167
+ size?: Size;
168
+ /**
169
+ * Callback fired when selection changes
170
+ */
171
+ onSelectionChange?: (keys: string[]) => void;
172
+ /**
173
+ * Custom styles for the list container
174
+ */
175
+ style?: ViewStyle;
176
+ /**
177
+ * FlatList props to pass through
178
+ */
179
+ flatListProps?: Omit<React.ComponentProps<typeof react_native.FlatList<T>>, 'data' | 'renderItem' | 'keyExtractor'>;
180
+ };
181
+
182
+ declare const List: React.FC<ListProps>;
183
+
184
+ declare const ListItem: React.FC<ListItemProps>;
185
+
186
+ declare function ListBuilder<T>({ data, keyExtractor, renderItem, selectionMode, selectedKeys: controlledSelectedKeys, defaultSelectedKeys, showDivider, isPressable, isSelectable, themeColor, size, onSelectionChange, style, flatListProps, }: ListBuilderProps<T>): React.JSX.Element;
187
+
188
+ export { List, ListBuilder, type ListBuilderProps, ListItem, type ListItemCustomAppearance, type ListItemProps, type ListProps, type ListSelectionMode };
@@ -0,0 +1,395 @@
1
+ import "../chunk-DXXNBF5P.js";
2
+ import {
3
+ useXUITheme
4
+ } from "../chunk-LTKYHG5V.js";
5
+
6
+ // src/components/list/list.tsx
7
+ import React2, { useCallback, useMemo, useState } from "react";
8
+ import { View as View2 } from "react-native";
9
+
10
+ // src/components/list/list-context.ts
11
+ import { createContext, useContext } from "react";
12
+ var ListContext = createContext(null);
13
+
14
+ // src/components/list/list.style.ts
15
+ import { StyleSheet } from "react-native";
16
+ var styles = StyleSheet.create({
17
+ list: {
18
+ flex: 1
19
+ },
20
+ item: {
21
+ flexDirection: "row",
22
+ alignItems: "center",
23
+ gap: 12
24
+ },
25
+ content: {
26
+ flex: 1,
27
+ gap: 2
28
+ },
29
+ title: {
30
+ fontWeight: "500"
31
+ },
32
+ description: {
33
+ opacity: 0.7
34
+ },
35
+ disabled: {
36
+ opacity: 0.5
37
+ },
38
+ divider: {
39
+ height: 1,
40
+ marginLeft: 10
41
+ }
42
+ });
43
+
44
+ // src/components/list/list-divider.tsx
45
+ import React, { Children } from "react";
46
+ import { View } from "react-native";
47
+ var ListDivider = () => {
48
+ const theme = useXUITheme();
49
+ return /* @__PURE__ */ React.createElement(
50
+ View,
51
+ {
52
+ style: [styles.divider, { backgroundColor: theme.colors.foreground + "30" }]
53
+ }
54
+ );
55
+ };
56
+ var ListChildren = ({ children, showDivider }) => {
57
+ if (!showDivider) {
58
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, children);
59
+ }
60
+ const childArray = Children.toArray(children);
61
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, childArray.map((child, index) => /* @__PURE__ */ React.createElement(React.Fragment, { key: index }, child, index < childArray.length - 1 && /* @__PURE__ */ React.createElement(ListDivider, null))));
62
+ };
63
+
64
+ // src/components/list/list.tsx
65
+ var List = ({
66
+ children,
67
+ selectionMode = "none",
68
+ selectedKeys: controlledSelectedKeys,
69
+ defaultSelectedKeys = [],
70
+ showDivider = false,
71
+ isPressable = true,
72
+ isSelectable = false,
73
+ themeColor = "primary",
74
+ size = "md",
75
+ onSelectionChange,
76
+ style
77
+ }) => {
78
+ const isControlled = controlledSelectedKeys !== void 0;
79
+ const [internalSelectedKeys, setInternalSelectedKeys] = useState(defaultSelectedKeys);
80
+ const selectedKeys = isControlled ? controlledSelectedKeys : internalSelectedKeys;
81
+ const isSelected = useCallback(
82
+ (key) => {
83
+ return selectedKeys.includes(key);
84
+ },
85
+ [selectedKeys]
86
+ );
87
+ const toggleSelection = useCallback(
88
+ (key) => {
89
+ if (selectionMode === "none" || !isSelectable) {
90
+ return;
91
+ }
92
+ let newSelectedKeys;
93
+ if (selectionMode === "single") {
94
+ newSelectedKeys = isSelected(key) ? [] : [key];
95
+ } else {
96
+ newSelectedKeys = isSelected(key) ? selectedKeys.filter((k) => k !== key) : [...selectedKeys, key];
97
+ }
98
+ if (!isControlled) {
99
+ setInternalSelectedKeys(newSelectedKeys);
100
+ }
101
+ onSelectionChange?.(newSelectedKeys);
102
+ },
103
+ [
104
+ selectionMode,
105
+ isSelectable,
106
+ isSelected,
107
+ selectedKeys,
108
+ isControlled,
109
+ onSelectionChange
110
+ ]
111
+ );
112
+ const contextValue = useMemo(
113
+ () => ({
114
+ selectionMode,
115
+ selectedKeys,
116
+ isPressable,
117
+ isSelectable,
118
+ themeColor,
119
+ size,
120
+ showDivider,
121
+ toggleSelection,
122
+ isSelected
123
+ }),
124
+ [
125
+ selectionMode,
126
+ selectedKeys,
127
+ isPressable,
128
+ isSelectable,
129
+ themeColor,
130
+ size,
131
+ showDivider,
132
+ toggleSelection,
133
+ isSelected
134
+ ]
135
+ );
136
+ return /* @__PURE__ */ React2.createElement(ListContext.Provider, { value: contextValue }, /* @__PURE__ */ React2.createElement(View2, { style: [styles.list, style] }, /* @__PURE__ */ React2.createElement(ListChildren, { showDivider }, children)));
137
+ };
138
+
139
+ // src/components/list/list-item.tsx
140
+ import React3, { useContext as useContext2 } from "react";
141
+ import { Pressable, Text, View as View3 } from "react-native";
142
+
143
+ // src/components/list/list.hook.ts
144
+ import { useMemo as useMemo2 } from "react";
145
+ var useListItemSizeStyles = (size) => {
146
+ const theme = useXUITheme();
147
+ return useMemo2(() => {
148
+ const sizes = {
149
+ xs: {
150
+ paddingVertical: theme.spacing.sm,
151
+ paddingHorizontal: theme.spacing.sm,
152
+ titleSize: theme.fontSizes.xs,
153
+ descriptionSize: theme.fontSizes.xs
154
+ },
155
+ sm: {
156
+ paddingVertical: theme.spacing.sm,
157
+ paddingHorizontal: theme.spacing.md,
158
+ titleSize: theme.fontSizes.sm,
159
+ descriptionSize: theme.fontSizes.xs
160
+ },
161
+ md: {
162
+ paddingVertical: theme.spacing.md,
163
+ paddingHorizontal: theme.spacing.md,
164
+ titleSize: theme.fontSizes.md,
165
+ descriptionSize: theme.fontSizes.sm
166
+ },
167
+ lg: {
168
+ paddingVertical: theme.spacing.lg,
169
+ paddingHorizontal: theme.spacing.lg,
170
+ titleSize: theme.fontSizes.lg,
171
+ descriptionSize: theme.fontSizes.md
172
+ }
173
+ };
174
+ return sizes[size];
175
+ }, [size, theme]);
176
+ };
177
+
178
+ // src/components/list/list-item.tsx
179
+ import { getSafeThemeColor } from "@xaui/core";
180
+ var ListItem = ({
181
+ itemKey,
182
+ title,
183
+ description,
184
+ startContent,
185
+ endContent,
186
+ isDisabled = false,
187
+ isSelected: propIsSelected,
188
+ customAppearance,
189
+ onPress
190
+ }) => {
191
+ const context = useContext2(ListContext);
192
+ const theme = useXUITheme();
193
+ const selectionMode = context?.selectionMode ?? "none";
194
+ const isPressable = context?.isPressable ?? true;
195
+ const isSelectable = context?.isSelectable ?? false;
196
+ const themeColor = context?.themeColor ?? "primary";
197
+ const size = context?.size ?? "md";
198
+ const isSelected = propIsSelected ?? (context ? context.isSelected(itemKey) : false);
199
+ const sizeStyles = useListItemSizeStyles(size);
200
+ const safeThemeColor = getSafeThemeColor(themeColor);
201
+ const colorScheme = theme.colors[safeThemeColor];
202
+ const backgroundColor = isSelected ? colorScheme.background : "transparent";
203
+ const titleColor = isDisabled ? theme.colors.foreground + "50" : theme.colors.foreground;
204
+ const descriptionColor = isDisabled ? theme.colors.foreground + "50" : theme.colors.foreground + "99";
205
+ const handlePress = () => {
206
+ if (isDisabled) {
207
+ return;
208
+ }
209
+ if (isSelectable && selectionMode !== "none") {
210
+ context?.toggleSelection(itemKey);
211
+ }
212
+ onPress?.({});
213
+ };
214
+ const renderTitle = () => {
215
+ if (typeof title === "string" || typeof title === "number") {
216
+ return /* @__PURE__ */ React3.createElement(
217
+ Text,
218
+ {
219
+ style: [
220
+ styles.title,
221
+ { fontSize: sizeStyles.titleSize, color: titleColor },
222
+ customAppearance?.title
223
+ ],
224
+ numberOfLines: 1
225
+ },
226
+ title
227
+ );
228
+ }
229
+ return title;
230
+ };
231
+ const renderDescription = () => {
232
+ if (!description) return null;
233
+ if (typeof description === "string" || typeof description === "number") {
234
+ return /* @__PURE__ */ React3.createElement(
235
+ Text,
236
+ {
237
+ style: [
238
+ styles.description,
239
+ { fontSize: sizeStyles.descriptionSize, color: descriptionColor },
240
+ customAppearance?.description
241
+ ],
242
+ numberOfLines: 2
243
+ },
244
+ description
245
+ );
246
+ }
247
+ return description;
248
+ };
249
+ const content = /* @__PURE__ */ React3.createElement(
250
+ View3,
251
+ {
252
+ style: [
253
+ styles.item,
254
+ {
255
+ paddingVertical: sizeStyles.paddingVertical,
256
+ paddingHorizontal: sizeStyles.paddingHorizontal,
257
+ backgroundColor
258
+ },
259
+ isDisabled && styles.disabled,
260
+ customAppearance?.container
261
+ ]
262
+ },
263
+ startContent && /* @__PURE__ */ React3.createElement(View3, { style: customAppearance?.content }, startContent),
264
+ /* @__PURE__ */ React3.createElement(View3, { style: [styles.content, customAppearance?.content] }, renderTitle(), renderDescription()),
265
+ endContent && /* @__PURE__ */ React3.createElement(View3, { style: customAppearance?.content }, endContent)
266
+ );
267
+ if (!isPressable) {
268
+ return content;
269
+ }
270
+ return /* @__PURE__ */ React3.createElement(
271
+ Pressable,
272
+ {
273
+ onPress: handlePress,
274
+ disabled: isDisabled,
275
+ style: ({ pressed }) => [
276
+ pressed && !isDisabled && {
277
+ backgroundColor: theme.colors.foreground + "10"
278
+ }
279
+ ]
280
+ },
281
+ content
282
+ );
283
+ };
284
+
285
+ // src/components/list/list-builder.tsx
286
+ import React4, { useCallback as useCallback2, useMemo as useMemo3, useState as useState2 } from "react";
287
+ import { FlatList, View as View4 } from "react-native";
288
+ function ListBuilder({
289
+ data,
290
+ keyExtractor,
291
+ renderItem,
292
+ selectionMode = "none",
293
+ selectedKeys: controlledSelectedKeys,
294
+ defaultSelectedKeys = [],
295
+ showDivider = false,
296
+ isPressable = true,
297
+ isSelectable = false,
298
+ themeColor = "primary",
299
+ size = "md",
300
+ onSelectionChange,
301
+ style,
302
+ flatListProps
303
+ }) {
304
+ const theme = useXUITheme();
305
+ const isControlled = controlledSelectedKeys !== void 0;
306
+ const [internalSelectedKeys, setInternalSelectedKeys] = useState2(defaultSelectedKeys);
307
+ const selectedKeys = isControlled ? controlledSelectedKeys : internalSelectedKeys;
308
+ const isSelected = useCallback2(
309
+ (key) => {
310
+ return selectedKeys.includes(key);
311
+ },
312
+ [selectedKeys]
313
+ );
314
+ const toggleSelection = useCallback2(
315
+ (key) => {
316
+ if (selectionMode === "none" || !isSelectable) {
317
+ return;
318
+ }
319
+ let newSelectedKeys;
320
+ if (selectionMode === "single") {
321
+ newSelectedKeys = isSelected(key) ? [] : [key];
322
+ } else {
323
+ newSelectedKeys = isSelected(key) ? selectedKeys.filter((k) => k !== key) : [...selectedKeys, key];
324
+ }
325
+ if (!isControlled) {
326
+ setInternalSelectedKeys(newSelectedKeys);
327
+ }
328
+ onSelectionChange?.(newSelectedKeys);
329
+ },
330
+ [
331
+ selectionMode,
332
+ isSelectable,
333
+ isSelected,
334
+ selectedKeys,
335
+ isControlled,
336
+ onSelectionChange
337
+ ]
338
+ );
339
+ const contextValue = useMemo3(
340
+ () => ({
341
+ selectionMode,
342
+ selectedKeys,
343
+ isPressable,
344
+ isSelectable,
345
+ themeColor,
346
+ size,
347
+ showDivider,
348
+ toggleSelection,
349
+ isSelected
350
+ }),
351
+ [
352
+ selectionMode,
353
+ selectedKeys,
354
+ isPressable,
355
+ isSelectable,
356
+ themeColor,
357
+ size,
358
+ showDivider,
359
+ toggleSelection,
360
+ isSelected
361
+ ]
362
+ );
363
+ const renderListItem = useCallback2(
364
+ (item, index) => {
365
+ const element = renderItem(item, index);
366
+ if (!showDivider || index === data.length - 1) {
367
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, { key: keyExtractor(item, index) }, element);
368
+ }
369
+ return /* @__PURE__ */ React4.createElement(View4, { key: keyExtractor(item, index) }, element, /* @__PURE__ */ React4.createElement(
370
+ View4,
371
+ {
372
+ style: [
373
+ styles.divider,
374
+ { backgroundColor: theme.colors.foreground + "15" }
375
+ ]
376
+ }
377
+ ));
378
+ },
379
+ [data.length, keyExtractor, renderItem, showDivider, theme.colors.foreground]
380
+ );
381
+ return /* @__PURE__ */ React4.createElement(ListContext.Provider, { value: contextValue }, /* @__PURE__ */ React4.createElement(View4, { style: [styles.list, style] }, /* @__PURE__ */ React4.createElement(
382
+ FlatList,
383
+ {
384
+ data,
385
+ keyExtractor,
386
+ renderItem: ({ item, index }) => renderListItem(item, index),
387
+ ...flatListProps
388
+ }
389
+ )));
390
+ }
391
+ export {
392
+ List,
393
+ ListBuilder,
394
+ ListItem
395
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xaui/native",
3
- "version": "0.0.19",
3
+ "version": "0.0.21",
4
4
  "description": "Flutter-inspired React Native UI components with native animations powered by React Native Reanimated",
5
5
  "keywords": [
6
6
  "react-native",
@@ -146,6 +146,16 @@
146
146
  "types": "./dist/card/index.d.ts",
147
147
  "import": "./dist/card/index.js",
148
148
  "require": "./dist/card/index.js"
149
+ },
150
+ "./input": {
151
+ "types": "./dist/input/index.d.ts",
152
+ "import": "./dist/input/index.js",
153
+ "require": "./dist/input/index.js"
154
+ },
155
+ "./list": {
156
+ "types": "./dist/list/index.d.ts",
157
+ "import": "./dist/list/index.js",
158
+ "require": "./dist/list/index.js"
149
159
  }
150
160
  },
151
161
  "files": [