@xaui/native 0.0.20 → 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,470 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/components/list/index.ts
31
+ var list_exports = {};
32
+ __export(list_exports, {
33
+ List: () => List,
34
+ ListBuilder: () => ListBuilder,
35
+ ListItem: () => ListItem
36
+ });
37
+ module.exports = __toCommonJS(list_exports);
38
+
39
+ // src/components/list/list.tsx
40
+ var import_react8 = __toESM(require("react"), 1);
41
+ var import_react_native6 = require("react-native");
42
+
43
+ // src/components/list/list-context.ts
44
+ var import_react = require("react");
45
+ var ListContext = (0, import_react.createContext)(null);
46
+
47
+ // src/components/list/list.style.ts
48
+ var import_react_native = require("react-native");
49
+ var styles = import_react_native.StyleSheet.create({
50
+ list: {
51
+ flex: 1
52
+ },
53
+ item: {
54
+ flexDirection: "row",
55
+ alignItems: "center",
56
+ gap: 12
57
+ },
58
+ content: {
59
+ flex: 1,
60
+ gap: 2
61
+ },
62
+ title: {
63
+ fontWeight: "500"
64
+ },
65
+ description: {
66
+ opacity: 0.7
67
+ },
68
+ disabled: {
69
+ opacity: 0.5
70
+ },
71
+ divider: {
72
+ height: 1,
73
+ marginLeft: 10
74
+ }
75
+ });
76
+
77
+ // src/components/list/list-divider.tsx
78
+ var import_react7 = __toESM(require("react"), 1);
79
+ var import_react_native5 = require("react-native");
80
+
81
+ // src/core/theme-context.tsx
82
+ var import_react5 = __toESM(require("react"), 1);
83
+ var import_react_native3 = require("react-native");
84
+ var import_theme = require("@xaui/core/theme");
85
+ var import_palette = require("@xaui/core/palette");
86
+
87
+ // src/core/portal/portal.tsx
88
+ var import_react3 = require("react");
89
+
90
+ // src/core/portal/portal-context.ts
91
+ var import_react2 = require("react");
92
+ var PortalContext = (0, import_react2.createContext)(null);
93
+
94
+ // src/core/portal/portal-host.tsx
95
+ var import_react4 = __toESM(require("react"), 1);
96
+ var import_react_native2 = require("react-native");
97
+ var hostStyles = import_react_native2.StyleSheet.create({
98
+ container: {
99
+ flex: 1
100
+ }
101
+ });
102
+
103
+ // src/core/theme-context.tsx
104
+ var XUIThemeContext = (0, import_react5.createContext)(null);
105
+
106
+ // src/core/theme-hooks.ts
107
+ var import_react6 = require("react");
108
+ var import_react_native4 = require("react-native");
109
+ function useXUITheme() {
110
+ const theme = (0, import_react6.useContext)(XUIThemeContext);
111
+ if (!theme) {
112
+ throw new Error("useXUITheme must be used within XUIProvider");
113
+ }
114
+ return theme;
115
+ }
116
+
117
+ // src/core/index.ts
118
+ var import_theme2 = require("@xaui/core/theme");
119
+
120
+ // src/components/list/list-divider.tsx
121
+ var ListDivider = () => {
122
+ const theme = useXUITheme();
123
+ return /* @__PURE__ */ import_react7.default.createElement(
124
+ import_react_native5.View,
125
+ {
126
+ style: [styles.divider, { backgroundColor: theme.colors.foreground + "30" }]
127
+ }
128
+ );
129
+ };
130
+ var ListChildren = ({ children, showDivider }) => {
131
+ if (!showDivider) {
132
+ return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, children);
133
+ }
134
+ const childArray = import_react7.Children.toArray(children);
135
+ return /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, null, childArray.map((child, index) => /* @__PURE__ */ import_react7.default.createElement(import_react7.default.Fragment, { key: index }, child, index < childArray.length - 1 && /* @__PURE__ */ import_react7.default.createElement(ListDivider, null))));
136
+ };
137
+
138
+ // src/components/list/list.tsx
139
+ var List = ({
140
+ children,
141
+ selectionMode = "none",
142
+ selectedKeys: controlledSelectedKeys,
143
+ defaultSelectedKeys = [],
144
+ showDivider = false,
145
+ isPressable = true,
146
+ isSelectable = false,
147
+ themeColor = "primary",
148
+ size = "md",
149
+ onSelectionChange,
150
+ style
151
+ }) => {
152
+ const isControlled = controlledSelectedKeys !== void 0;
153
+ const [internalSelectedKeys, setInternalSelectedKeys] = (0, import_react8.useState)(defaultSelectedKeys);
154
+ const selectedKeys = isControlled ? controlledSelectedKeys : internalSelectedKeys;
155
+ const isSelected = (0, import_react8.useCallback)(
156
+ (key) => {
157
+ return selectedKeys.includes(key);
158
+ },
159
+ [selectedKeys]
160
+ );
161
+ const toggleSelection = (0, import_react8.useCallback)(
162
+ (key) => {
163
+ if (selectionMode === "none" || !isSelectable) {
164
+ return;
165
+ }
166
+ let newSelectedKeys;
167
+ if (selectionMode === "single") {
168
+ newSelectedKeys = isSelected(key) ? [] : [key];
169
+ } else {
170
+ newSelectedKeys = isSelected(key) ? selectedKeys.filter((k) => k !== key) : [...selectedKeys, key];
171
+ }
172
+ if (!isControlled) {
173
+ setInternalSelectedKeys(newSelectedKeys);
174
+ }
175
+ onSelectionChange?.(newSelectedKeys);
176
+ },
177
+ [
178
+ selectionMode,
179
+ isSelectable,
180
+ isSelected,
181
+ selectedKeys,
182
+ isControlled,
183
+ onSelectionChange
184
+ ]
185
+ );
186
+ const contextValue = (0, import_react8.useMemo)(
187
+ () => ({
188
+ selectionMode,
189
+ selectedKeys,
190
+ isPressable,
191
+ isSelectable,
192
+ themeColor,
193
+ size,
194
+ showDivider,
195
+ toggleSelection,
196
+ isSelected
197
+ }),
198
+ [
199
+ selectionMode,
200
+ selectedKeys,
201
+ isPressable,
202
+ isSelectable,
203
+ themeColor,
204
+ size,
205
+ showDivider,
206
+ toggleSelection,
207
+ isSelected
208
+ ]
209
+ );
210
+ return /* @__PURE__ */ import_react8.default.createElement(ListContext.Provider, { value: contextValue }, /* @__PURE__ */ import_react8.default.createElement(import_react_native6.View, { style: [styles.list, style] }, /* @__PURE__ */ import_react8.default.createElement(ListChildren, { showDivider }, children)));
211
+ };
212
+
213
+ // src/components/list/list-item.tsx
214
+ var import_react10 = __toESM(require("react"), 1);
215
+ var import_react_native7 = require("react-native");
216
+
217
+ // src/components/list/list.hook.ts
218
+ var import_react9 = require("react");
219
+ var useListItemSizeStyles = (size) => {
220
+ const theme = useXUITheme();
221
+ return (0, import_react9.useMemo)(() => {
222
+ const sizes = {
223
+ xs: {
224
+ paddingVertical: theme.spacing.sm,
225
+ paddingHorizontal: theme.spacing.sm,
226
+ titleSize: theme.fontSizes.xs,
227
+ descriptionSize: theme.fontSizes.xs
228
+ },
229
+ sm: {
230
+ paddingVertical: theme.spacing.sm,
231
+ paddingHorizontal: theme.spacing.md,
232
+ titleSize: theme.fontSizes.sm,
233
+ descriptionSize: theme.fontSizes.xs
234
+ },
235
+ md: {
236
+ paddingVertical: theme.spacing.md,
237
+ paddingHorizontal: theme.spacing.md,
238
+ titleSize: theme.fontSizes.md,
239
+ descriptionSize: theme.fontSizes.sm
240
+ },
241
+ lg: {
242
+ paddingVertical: theme.spacing.lg,
243
+ paddingHorizontal: theme.spacing.lg,
244
+ titleSize: theme.fontSizes.lg,
245
+ descriptionSize: theme.fontSizes.md
246
+ }
247
+ };
248
+ return sizes[size];
249
+ }, [size, theme]);
250
+ };
251
+
252
+ // src/components/list/list-item.tsx
253
+ var import_core3 = require("@xaui/core");
254
+ var ListItem = ({
255
+ itemKey,
256
+ title,
257
+ description,
258
+ startContent,
259
+ endContent,
260
+ isDisabled = false,
261
+ isSelected: propIsSelected,
262
+ customAppearance,
263
+ onPress
264
+ }) => {
265
+ const context = (0, import_react10.useContext)(ListContext);
266
+ const theme = useXUITheme();
267
+ const selectionMode = context?.selectionMode ?? "none";
268
+ const isPressable = context?.isPressable ?? true;
269
+ const isSelectable = context?.isSelectable ?? false;
270
+ const themeColor = context?.themeColor ?? "primary";
271
+ const size = context?.size ?? "md";
272
+ const isSelected = propIsSelected ?? (context ? context.isSelected(itemKey) : false);
273
+ const sizeStyles = useListItemSizeStyles(size);
274
+ const safeThemeColor = (0, import_core3.getSafeThemeColor)(themeColor);
275
+ const colorScheme = theme.colors[safeThemeColor];
276
+ const backgroundColor = isSelected ? colorScheme.background : "transparent";
277
+ const titleColor = isDisabled ? theme.colors.foreground + "50" : theme.colors.foreground;
278
+ const descriptionColor = isDisabled ? theme.colors.foreground + "50" : theme.colors.foreground + "99";
279
+ const handlePress = () => {
280
+ if (isDisabled) {
281
+ return;
282
+ }
283
+ if (isSelectable && selectionMode !== "none") {
284
+ context?.toggleSelection(itemKey);
285
+ }
286
+ onPress?.({});
287
+ };
288
+ const renderTitle = () => {
289
+ if (typeof title === "string" || typeof title === "number") {
290
+ return /* @__PURE__ */ import_react10.default.createElement(
291
+ import_react_native7.Text,
292
+ {
293
+ style: [
294
+ styles.title,
295
+ { fontSize: sizeStyles.titleSize, color: titleColor },
296
+ customAppearance?.title
297
+ ],
298
+ numberOfLines: 1
299
+ },
300
+ title
301
+ );
302
+ }
303
+ return title;
304
+ };
305
+ const renderDescription = () => {
306
+ if (!description) return null;
307
+ if (typeof description === "string" || typeof description === "number") {
308
+ return /* @__PURE__ */ import_react10.default.createElement(
309
+ import_react_native7.Text,
310
+ {
311
+ style: [
312
+ styles.description,
313
+ { fontSize: sizeStyles.descriptionSize, color: descriptionColor },
314
+ customAppearance?.description
315
+ ],
316
+ numberOfLines: 2
317
+ },
318
+ description
319
+ );
320
+ }
321
+ return description;
322
+ };
323
+ const content = /* @__PURE__ */ import_react10.default.createElement(
324
+ import_react_native7.View,
325
+ {
326
+ style: [
327
+ styles.item,
328
+ {
329
+ paddingVertical: sizeStyles.paddingVertical,
330
+ paddingHorizontal: sizeStyles.paddingHorizontal,
331
+ backgroundColor
332
+ },
333
+ isDisabled && styles.disabled,
334
+ customAppearance?.container
335
+ ]
336
+ },
337
+ startContent && /* @__PURE__ */ import_react10.default.createElement(import_react_native7.View, { style: customAppearance?.content }, startContent),
338
+ /* @__PURE__ */ import_react10.default.createElement(import_react_native7.View, { style: [styles.content, customAppearance?.content] }, renderTitle(), renderDescription()),
339
+ endContent && /* @__PURE__ */ import_react10.default.createElement(import_react_native7.View, { style: customAppearance?.content }, endContent)
340
+ );
341
+ if (!isPressable) {
342
+ return content;
343
+ }
344
+ return /* @__PURE__ */ import_react10.default.createElement(
345
+ import_react_native7.Pressable,
346
+ {
347
+ onPress: handlePress,
348
+ disabled: isDisabled,
349
+ style: ({ pressed }) => [
350
+ pressed && !isDisabled && {
351
+ backgroundColor: theme.colors.foreground + "10"
352
+ }
353
+ ]
354
+ },
355
+ content
356
+ );
357
+ };
358
+
359
+ // src/components/list/list-builder.tsx
360
+ var import_react11 = __toESM(require("react"), 1);
361
+ var import_react_native8 = require("react-native");
362
+ function ListBuilder({
363
+ data,
364
+ keyExtractor,
365
+ renderItem,
366
+ selectionMode = "none",
367
+ selectedKeys: controlledSelectedKeys,
368
+ defaultSelectedKeys = [],
369
+ showDivider = false,
370
+ isPressable = true,
371
+ isSelectable = false,
372
+ themeColor = "primary",
373
+ size = "md",
374
+ onSelectionChange,
375
+ style,
376
+ flatListProps
377
+ }) {
378
+ const theme = useXUITheme();
379
+ const isControlled = controlledSelectedKeys !== void 0;
380
+ const [internalSelectedKeys, setInternalSelectedKeys] = (0, import_react11.useState)(defaultSelectedKeys);
381
+ const selectedKeys = isControlled ? controlledSelectedKeys : internalSelectedKeys;
382
+ const isSelected = (0, import_react11.useCallback)(
383
+ (key) => {
384
+ return selectedKeys.includes(key);
385
+ },
386
+ [selectedKeys]
387
+ );
388
+ const toggleSelection = (0, import_react11.useCallback)(
389
+ (key) => {
390
+ if (selectionMode === "none" || !isSelectable) {
391
+ return;
392
+ }
393
+ let newSelectedKeys;
394
+ if (selectionMode === "single") {
395
+ newSelectedKeys = isSelected(key) ? [] : [key];
396
+ } else {
397
+ newSelectedKeys = isSelected(key) ? selectedKeys.filter((k) => k !== key) : [...selectedKeys, key];
398
+ }
399
+ if (!isControlled) {
400
+ setInternalSelectedKeys(newSelectedKeys);
401
+ }
402
+ onSelectionChange?.(newSelectedKeys);
403
+ },
404
+ [
405
+ selectionMode,
406
+ isSelectable,
407
+ isSelected,
408
+ selectedKeys,
409
+ isControlled,
410
+ onSelectionChange
411
+ ]
412
+ );
413
+ const contextValue = (0, import_react11.useMemo)(
414
+ () => ({
415
+ selectionMode,
416
+ selectedKeys,
417
+ isPressable,
418
+ isSelectable,
419
+ themeColor,
420
+ size,
421
+ showDivider,
422
+ toggleSelection,
423
+ isSelected
424
+ }),
425
+ [
426
+ selectionMode,
427
+ selectedKeys,
428
+ isPressable,
429
+ isSelectable,
430
+ themeColor,
431
+ size,
432
+ showDivider,
433
+ toggleSelection,
434
+ isSelected
435
+ ]
436
+ );
437
+ const renderListItem = (0, import_react11.useCallback)(
438
+ (item, index) => {
439
+ const element = renderItem(item, index);
440
+ if (!showDivider || index === data.length - 1) {
441
+ return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, { key: keyExtractor(item, index) }, element);
442
+ }
443
+ return /* @__PURE__ */ import_react11.default.createElement(import_react_native8.View, { key: keyExtractor(item, index) }, element, /* @__PURE__ */ import_react11.default.createElement(
444
+ import_react_native8.View,
445
+ {
446
+ style: [
447
+ styles.divider,
448
+ { backgroundColor: theme.colors.foreground + "15" }
449
+ ]
450
+ }
451
+ ));
452
+ },
453
+ [data.length, keyExtractor, renderItem, showDivider, theme.colors.foreground]
454
+ );
455
+ return /* @__PURE__ */ import_react11.default.createElement(ListContext.Provider, { value: contextValue }, /* @__PURE__ */ import_react11.default.createElement(import_react_native8.View, { style: [styles.list, style] }, /* @__PURE__ */ import_react11.default.createElement(
456
+ import_react_native8.FlatList,
457
+ {
458
+ data,
459
+ keyExtractor,
460
+ renderItem: ({ item, index }) => renderListItem(item, index),
461
+ ...flatListProps
462
+ }
463
+ )));
464
+ }
465
+ // Annotate the CommonJS export names for ESM import in node:
466
+ 0 && (module.exports = {
467
+ List,
468
+ ListBuilder,
469
+ ListItem
470
+ });
@@ -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.cjs';
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,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.20",
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",
@@ -151,6 +151,11 @@
151
151
  "types": "./dist/input/index.d.ts",
152
152
  "import": "./dist/input/index.js",
153
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"
154
159
  }
155
160
  },
156
161
  "files": [