@xsolla/xui-table 0.151.0-pr273.1778117489

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,716 @@
1
+ // src/Table.tsx
2
+ import React, {
3
+ createContext,
4
+ forwardRef,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useState
10
+ } from "react";
11
+
12
+ // ../../foundation/primitives-native/src/Box.tsx
13
+ import {
14
+ View,
15
+ Pressable,
16
+ Image
17
+ } from "react-native";
18
+ import { jsx } from "react/jsx-runtime";
19
+ var Box = ({
20
+ children,
21
+ onPress,
22
+ onLayout,
23
+ onMoveShouldSetResponder,
24
+ onResponderGrant,
25
+ onResponderMove,
26
+ onResponderRelease,
27
+ onResponderTerminate,
28
+ backgroundColor,
29
+ borderColor,
30
+ borderWidth,
31
+ borderBottomWidth,
32
+ borderBottomColor,
33
+ borderTopWidth,
34
+ borderTopColor,
35
+ borderLeftWidth,
36
+ borderLeftColor,
37
+ borderRightWidth,
38
+ borderRightColor,
39
+ borderRadius,
40
+ borderStyle,
41
+ height,
42
+ padding,
43
+ paddingHorizontal,
44
+ paddingVertical,
45
+ margin,
46
+ marginTop,
47
+ marginBottom,
48
+ marginLeft,
49
+ marginRight,
50
+ flexDirection,
51
+ alignItems,
52
+ justifyContent,
53
+ position,
54
+ top,
55
+ bottom,
56
+ left,
57
+ right,
58
+ width,
59
+ minWidth,
60
+ minHeight,
61
+ maxWidth,
62
+ maxHeight,
63
+ flex,
64
+ overflow,
65
+ zIndex,
66
+ hoverStyle,
67
+ pressStyle,
68
+ style,
69
+ "data-testid": dataTestId,
70
+ testID,
71
+ as,
72
+ src,
73
+ alt,
74
+ ...rest
75
+ }) => {
76
+ const getContainerStyle = (pressed) => ({
77
+ backgroundColor: pressed && pressStyle?.backgroundColor ? pressStyle.backgroundColor : backgroundColor,
78
+ borderColor,
79
+ borderWidth,
80
+ borderBottomWidth,
81
+ borderBottomColor,
82
+ borderTopWidth,
83
+ borderTopColor,
84
+ borderLeftWidth,
85
+ borderLeftColor,
86
+ borderRightWidth,
87
+ borderRightColor,
88
+ borderRadius,
89
+ borderStyle,
90
+ overflow,
91
+ zIndex,
92
+ height,
93
+ width,
94
+ minWidth,
95
+ minHeight,
96
+ maxWidth,
97
+ maxHeight,
98
+ padding,
99
+ paddingHorizontal,
100
+ paddingVertical,
101
+ margin,
102
+ marginTop,
103
+ marginBottom,
104
+ marginLeft,
105
+ marginRight,
106
+ flexDirection,
107
+ alignItems,
108
+ justifyContent,
109
+ position,
110
+ top,
111
+ bottom,
112
+ left,
113
+ right,
114
+ flex,
115
+ ...style
116
+ });
117
+ const finalTestID = dataTestId || testID;
118
+ const {
119
+ role,
120
+ tabIndex,
121
+ onKeyDown,
122
+ onKeyUp,
123
+ "aria-label": _ariaLabel,
124
+ "aria-labelledby": _ariaLabelledBy,
125
+ "aria-current": _ariaCurrent,
126
+ "aria-disabled": _ariaDisabled,
127
+ "aria-live": _ariaLive,
128
+ className,
129
+ "data-testid": _dataTestId,
130
+ ...nativeRest
131
+ } = rest;
132
+ if (as === "img" && src) {
133
+ const imageStyle = {
134
+ width,
135
+ height,
136
+ borderRadius,
137
+ position,
138
+ top,
139
+ bottom,
140
+ left,
141
+ right,
142
+ ...style
143
+ };
144
+ return /* @__PURE__ */ jsx(
145
+ Image,
146
+ {
147
+ source: { uri: src },
148
+ style: imageStyle,
149
+ testID: finalTestID,
150
+ resizeMode: "cover",
151
+ ...nativeRest
152
+ }
153
+ );
154
+ }
155
+ if (onPress) {
156
+ return /* @__PURE__ */ jsx(
157
+ Pressable,
158
+ {
159
+ onPress,
160
+ onLayout,
161
+ onMoveShouldSetResponder,
162
+ onResponderGrant,
163
+ onResponderMove,
164
+ onResponderRelease,
165
+ onResponderTerminate,
166
+ style: ({ pressed }) => getContainerStyle(pressed),
167
+ testID: finalTestID,
168
+ ...nativeRest,
169
+ children
170
+ }
171
+ );
172
+ }
173
+ return /* @__PURE__ */ jsx(
174
+ View,
175
+ {
176
+ style: getContainerStyle(),
177
+ testID: finalTestID,
178
+ onLayout,
179
+ onMoveShouldSetResponder,
180
+ onResponderGrant,
181
+ onResponderMove,
182
+ onResponderRelease,
183
+ onResponderTerminate,
184
+ ...nativeRest,
185
+ children
186
+ }
187
+ );
188
+ };
189
+
190
+ // ../../foundation/primitives-native/src/Text.tsx
191
+ import {
192
+ Text as RNText,
193
+ StyleSheet
194
+ } from "react-native";
195
+ import { jsx as jsx2 } from "react/jsx-runtime";
196
+ var roleMap = {
197
+ alert: "alert",
198
+ heading: "header",
199
+ button: "button",
200
+ link: "link",
201
+ text: "text"
202
+ };
203
+ var parseNumericValue = (value) => {
204
+ if (value === void 0) return void 0;
205
+ if (typeof value === "number") return value;
206
+ const parsed = parseFloat(value);
207
+ return isNaN(parsed) ? void 0 : parsed;
208
+ };
209
+ var Text = ({
210
+ children,
211
+ color,
212
+ fontSize,
213
+ fontWeight,
214
+ fontFamily,
215
+ textAlign,
216
+ lineHeight,
217
+ numberOfLines,
218
+ id,
219
+ role,
220
+ style: styleProp,
221
+ ...props
222
+ }) => {
223
+ let resolvedFontFamily = fontFamily ? fontFamily.split(",")[0].replace(/['"]/g, "").trim() : void 0;
224
+ if (resolvedFontFamily === "Pilat Wide" || resolvedFontFamily === "Pilat Wide Bold" || resolvedFontFamily === "Aktiv Grotesk") {
225
+ resolvedFontFamily = void 0;
226
+ }
227
+ const incomingStyle = StyleSheet.flatten(styleProp);
228
+ const baseStyle = {
229
+ color: color ?? incomingStyle?.color,
230
+ fontSize: typeof fontSize === "number" ? fontSize : void 0,
231
+ fontWeight,
232
+ fontFamily: resolvedFontFamily,
233
+ textDecorationLine: props.textDecoration,
234
+ textAlign: textAlign ?? incomingStyle?.textAlign,
235
+ lineHeight: parseNumericValue(lineHeight ?? incomingStyle?.lineHeight),
236
+ marginTop: parseNumericValue(
237
+ incomingStyle?.marginTop
238
+ ),
239
+ marginBottom: parseNumericValue(
240
+ incomingStyle?.marginBottom
241
+ )
242
+ };
243
+ const accessibilityRole = role ? roleMap[role] : void 0;
244
+ return /* @__PURE__ */ jsx2(
245
+ RNText,
246
+ {
247
+ style: baseStyle,
248
+ numberOfLines,
249
+ testID: id,
250
+ accessibilityRole,
251
+ children
252
+ }
253
+ );
254
+ };
255
+
256
+ // src/Table.tsx
257
+ import { useResolvedTheme, isNative } from "@xsolla/xui-core";
258
+ import { Sort } from "@xsolla/xui-icons-base";
259
+ import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
260
+ var TableRowGroupContext = createContext("body");
261
+ var RowRevealContext = createContext({
262
+ revealed: true
263
+ });
264
+ var useHoverCapable = () => {
265
+ const [hoverCapable, setHoverCapable] = useState(true);
266
+ useEffect(() => {
267
+ if (isNative) return;
268
+ if (typeof window === "undefined" || !window.matchMedia) return;
269
+ const mq = window.matchMedia("(hover: hover)");
270
+ setHoverCapable(mq.matches);
271
+ const onChange = (e) => setHoverCapable(e.matches);
272
+ mq.addEventListener?.("change", onChange);
273
+ return () => mq.removeEventListener?.("change", onChange);
274
+ }, []);
275
+ return hoverCapable;
276
+ };
277
+ var Divider = ({ color }) => /* @__PURE__ */ jsx3(
278
+ Box,
279
+ {
280
+ width: "100%",
281
+ height: 1,
282
+ backgroundColor: color,
283
+ style: { flexShrink: 0 }
284
+ }
285
+ );
286
+ var TableRoot = forwardRef(
287
+ ({ children, themeMode, themeProductContext }, ref) => {
288
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
289
+ const sizing = theme.sizing.table;
290
+ return /* @__PURE__ */ jsx3(
291
+ Box,
292
+ {
293
+ ref,
294
+ flexDirection: "column",
295
+ alignItems: "stretch",
296
+ gap: sizing.containerGap,
297
+ paddingVertical: sizing.containerPaddingVertical,
298
+ backgroundColor: theme.colors.background.primary,
299
+ borderRadius: sizing.containerRadius,
300
+ width: "100%",
301
+ role: "table",
302
+ style: {
303
+ color: theme.colors.content.primary,
304
+ overflow: "clip"
305
+ },
306
+ children
307
+ }
308
+ );
309
+ }
310
+ );
311
+ TableRoot.displayName = "Table";
312
+ var TableCaption = forwardRef(
313
+ ({ children, themeMode, themeProductContext, style, ...props }, ref) => {
314
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
315
+ const sizing = theme.sizing.table;
316
+ return /* @__PURE__ */ jsx3(
317
+ Box,
318
+ {
319
+ ref,
320
+ flexDirection: "row",
321
+ alignItems: "center",
322
+ paddingHorizontal: sizing.headerRowPaddingHorizontal,
323
+ width: "100%",
324
+ ...props,
325
+ style,
326
+ children: typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsx3(
327
+ Text,
328
+ {
329
+ fontSize: sizing.captionFontSize,
330
+ lineHeight: sizing.captionLineHeight,
331
+ color: theme.colors.content.secondary,
332
+ children
333
+ }
334
+ ) : children
335
+ }
336
+ );
337
+ }
338
+ );
339
+ TableCaption.displayName = "Table.Caption";
340
+ var TableHeader = forwardRef(
341
+ ({ children, themeMode, themeProductContext, style, ...props }, ref) => {
342
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
343
+ return /* @__PURE__ */ jsx3(TableRowGroupContext.Provider, { value: "header", children: /* @__PURE__ */ jsxs(
344
+ Box,
345
+ {
346
+ ref,
347
+ flexDirection: "column",
348
+ alignItems: "stretch",
349
+ width: "100%",
350
+ role: "rowgroup",
351
+ ...props,
352
+ style: {
353
+ position: "sticky",
354
+ top: 0,
355
+ zIndex: 1,
356
+ backgroundColor: theme.colors.background.primary,
357
+ ...style
358
+ },
359
+ children: [
360
+ children,
361
+ /* @__PURE__ */ jsx3(Divider, { color: theme.colors.border.secondary })
362
+ ]
363
+ }
364
+ ) });
365
+ }
366
+ );
367
+ TableHeader.displayName = "Table.Header";
368
+ var TableBody = forwardRef(
369
+ ({ children, style, minRows, themeMode, themeProductContext, ...props }, ref) => {
370
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
371
+ const sizing = theme.sizing.table;
372
+ const DIVIDER_HEIGHT = 1;
373
+ const computedMinHeight = minRows ? minRows * sizing.rowHeight + (minRows - 1) * DIVIDER_HEIGHT : void 0;
374
+ let renderedChildren = children;
375
+ if (minRows) {
376
+ const childArray = React.Children.toArray(children);
377
+ const hasOnlyRowChildren = childArray.length > 0 && childArray.every((c) => React.isValidElement(c) && c.type === TableRow);
378
+ const realRowCount = hasOnlyRowChildren ? childArray.length : 0;
379
+ const placeholderCount = hasOnlyRowChildren ? Math.max(0, minRows - realRowCount) : 0;
380
+ if (placeholderCount > 0) {
381
+ const patchedRows = childArray.map((child, i) => {
382
+ if (!React.isValidElement(child)) return child;
383
+ const isLastRealRow = i === realRowCount - 1;
384
+ return React.cloneElement(child, {
385
+ hideDivider: isLastRealRow ? false : child.props.hideDivider
386
+ });
387
+ });
388
+ const placeholders = Array.from({ length: placeholderCount }).map(
389
+ (_, i) => {
390
+ const isLast = i === placeholderCount - 1;
391
+ return /* @__PURE__ */ jsx3(
392
+ Box,
393
+ {
394
+ "aria-hidden": true,
395
+ style: {
396
+ height: isLast ? sizing.rowHeight : sizing.rowHeight + DIVIDER_HEIGHT,
397
+ flexShrink: 0
398
+ }
399
+ },
400
+ `__table-body-placeholder-${i}`
401
+ );
402
+ }
403
+ );
404
+ renderedChildren = /* @__PURE__ */ jsxs(Fragment, { children: [
405
+ patchedRows,
406
+ placeholders
407
+ ] });
408
+ }
409
+ }
410
+ return /* @__PURE__ */ jsx3(TableRowGroupContext.Provider, { value: "body", children: /* @__PURE__ */ jsx3(
411
+ Box,
412
+ {
413
+ ref,
414
+ flexDirection: "column",
415
+ alignItems: "stretch",
416
+ width: "100%",
417
+ role: "rowgroup",
418
+ ...props,
419
+ style: {
420
+ ...computedMinHeight !== void 0 && {
421
+ minHeight: computedMinHeight
422
+ },
423
+ ...style
424
+ },
425
+ children: renderedChildren
426
+ }
427
+ ) });
428
+ }
429
+ );
430
+ TableBody.displayName = "Table.Body";
431
+ var TableRow = forwardRef(
432
+ ({
433
+ children,
434
+ hoverable: hoverableProp,
435
+ selected,
436
+ hideDivider: hideDividerProp,
437
+ onPress,
438
+ themeMode,
439
+ themeProductContext,
440
+ style,
441
+ onMouseEnter,
442
+ onMouseLeave,
443
+ onFocus,
444
+ onBlur,
445
+ ...props
446
+ }, ref) => {
447
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
448
+ const sizing = theme.sizing.table;
449
+ const rowGroup = useContext(TableRowGroupContext);
450
+ const hoverCapable = useHoverCapable();
451
+ const isHeaderRow = rowGroup === "header";
452
+ const hoverable = hoverableProp ?? !isHeaderRow;
453
+ const hideDivider = hideDividerProp ?? isHeaderRow;
454
+ const [hovered, setHovered] = useState(false);
455
+ const [focusedWithin, setFocusedWithin] = useState(false);
456
+ const revealed = hoverCapable ? hovered || focusedWithin : true;
457
+ const handleMouseEnter = useCallback(
458
+ (e) => {
459
+ setHovered(true);
460
+ onMouseEnter?.(e);
461
+ },
462
+ [onMouseEnter]
463
+ );
464
+ const handleMouseLeave = useCallback(
465
+ (e) => {
466
+ setHovered(false);
467
+ onMouseLeave?.(e);
468
+ },
469
+ [onMouseLeave]
470
+ );
471
+ const handleFocus = useCallback(
472
+ (e) => {
473
+ setFocusedWithin(true);
474
+ onFocus?.(e);
475
+ },
476
+ [onFocus]
477
+ );
478
+ const handleBlur = useCallback(
479
+ (e) => {
480
+ if (e.relatedTarget instanceof Node && e.currentTarget.contains(e.relatedTarget)) {
481
+ onBlur?.(e);
482
+ return;
483
+ }
484
+ setFocusedWithin(false);
485
+ onBlur?.(e);
486
+ },
487
+ [onBlur]
488
+ );
489
+ const baseBg = selected ? theme.colors.background.secondary : theme.colors.background.primary;
490
+ const rowHeight = isHeaderRow ? sizing.headerRowHeight : sizing.rowHeight;
491
+ const rowPaddingHorizontal = isHeaderRow ? sizing.headerRowPaddingHorizontal : sizing.rowPaddingHorizontal;
492
+ const ctxValue = useMemo(() => ({ revealed }), [revealed]);
493
+ return /* @__PURE__ */ jsxs(RowRevealContext.Provider, { value: ctxValue, children: [
494
+ /* @__PURE__ */ jsx3(
495
+ Box,
496
+ {
497
+ ref,
498
+ flexDirection: "row",
499
+ alignItems: "center",
500
+ height: rowHeight,
501
+ paddingHorizontal: rowPaddingHorizontal,
502
+ width: "100%",
503
+ gap: sizing.cellGap,
504
+ backgroundColor: baseBg,
505
+ cursor: onPress ? "pointer" : void 0,
506
+ hoverStyle: hoverable ? { backgroundColor: theme.colors.background.secondary } : void 0,
507
+ onPress,
508
+ role: "row",
509
+ "aria-selected": selected || void 0,
510
+ onMouseEnter: handleMouseEnter,
511
+ onMouseLeave: handleMouseLeave,
512
+ onFocus: handleFocus,
513
+ onBlur: handleBlur,
514
+ ...props,
515
+ style,
516
+ children
517
+ }
518
+ ),
519
+ !hideDivider && /* @__PURE__ */ jsx3(Divider, { color: theme.colors.border.secondary })
520
+ ] });
521
+ }
522
+ );
523
+ TableRow.displayName = "Table.Row";
524
+ var positionToFlex = (position) => {
525
+ switch (position) {
526
+ case "left":
527
+ return { flexShrink: 0 };
528
+ case "right":
529
+ return { flexShrink: 0, marginLeft: "auto" };
530
+ default:
531
+ return {};
532
+ }
533
+ };
534
+ var alignToJustify = (align) => {
535
+ switch (align) {
536
+ case "right":
537
+ return "flex-end";
538
+ case "center":
539
+ return "center";
540
+ default:
541
+ return "flex-start";
542
+ }
543
+ };
544
+ var TableCell = forwardRef(
545
+ ({
546
+ children,
547
+ align = "left",
548
+ position = "default",
549
+ width,
550
+ grow,
551
+ revealOnHover,
552
+ themeMode,
553
+ themeProductContext,
554
+ style,
555
+ ...props
556
+ }, ref) => {
557
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
558
+ const sizing = theme.sizing.table;
559
+ const { revealed } = useContext(RowRevealContext);
560
+ const positionStyle = useMemo(() => positionToFlex(position), [position]);
561
+ const revealStyle = revealOnHover && !revealed ? { opacity: 0, pointerEvents: "none" } : revealOnHover ? { opacity: 1, transition: "opacity 0.15s ease" } : {};
562
+ return /* @__PURE__ */ jsx3(
563
+ Box,
564
+ {
565
+ ref,
566
+ flexDirection: "row",
567
+ alignItems: "center",
568
+ justifyContent: alignToJustify(align),
569
+ gap: 8,
570
+ height: "100%",
571
+ width,
572
+ role: "cell",
573
+ ...props,
574
+ style: {
575
+ flex: width != null ? "0 0 auto" : grow ?? 1,
576
+ minWidth: 0,
577
+ ...positionStyle,
578
+ ...revealStyle,
579
+ ...style
580
+ },
581
+ children: typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsx3(
582
+ Text,
583
+ {
584
+ fontSize: sizing.cellFontSize,
585
+ lineHeight: sizing.cellLineHeight,
586
+ color: theme.colors.content.primary,
587
+ style: {
588
+ overflow: "hidden",
589
+ textOverflow: "ellipsis",
590
+ whiteSpace: "nowrap",
591
+ maxWidth: "100%"
592
+ },
593
+ children
594
+ }
595
+ ) : children
596
+ }
597
+ );
598
+ }
599
+ );
600
+ TableCell.displayName = "Table.Cell";
601
+ var SortIcon = ({ direction, color, size }) => /* @__PURE__ */ jsx3(
602
+ Box,
603
+ {
604
+ style: {
605
+ flexShrink: 0,
606
+ opacity: direction === "none" ? 0.4 : 1,
607
+ transition: "opacity 0.15s ease"
608
+ },
609
+ children: /* @__PURE__ */ jsx3(Sort, { variant: "line", size, color, "aria-hidden": true })
610
+ }
611
+ );
612
+ var TableHead = forwardRef(
613
+ ({
614
+ children,
615
+ align = "left",
616
+ position = "default",
617
+ width,
618
+ grow,
619
+ sort,
620
+ onSortToggle,
621
+ themeMode,
622
+ themeProductContext,
623
+ style,
624
+ ...props
625
+ }, ref) => {
626
+ const { theme } = useResolvedTheme({ themeMode, themeProductContext });
627
+ const sizing = theme.sizing.table;
628
+ const positionStyle = useMemo(() => positionToFlex(position), [position]);
629
+ const sortable = sort != null;
630
+ const ariaSort = sortable ? sort : void 0;
631
+ return /* @__PURE__ */ jsxs(
632
+ Box,
633
+ {
634
+ ref,
635
+ flexDirection: "row",
636
+ alignItems: "center",
637
+ justifyContent: alignToJustify(align),
638
+ gap: sizing.headerCellGap,
639
+ height: "100%",
640
+ width,
641
+ role: "columnheader",
642
+ "aria-sort": ariaSort,
643
+ cursor: sortable ? "pointer" : void 0,
644
+ onPress: sortable ? onSortToggle : void 0,
645
+ ...props,
646
+ style: {
647
+ flex: width != null ? "0 0 auto" : grow ?? 1,
648
+ minWidth: 0,
649
+ ...positionStyle,
650
+ ...style
651
+ },
652
+ children: [
653
+ sortable && /* @__PURE__ */ jsx3(
654
+ SortIcon,
655
+ {
656
+ direction: sort,
657
+ color: theme.colors.content.secondary,
658
+ size: sizing.headerCellFontSize + 2
659
+ }
660
+ ),
661
+ typeof children === "string" || typeof children === "number" ? /* @__PURE__ */ jsx3(
662
+ Text,
663
+ {
664
+ fontSize: sizing.headerCellFontSize,
665
+ lineHeight: sizing.headerCellLineHeight,
666
+ color: theme.colors.content.secondary,
667
+ fontWeight: "500",
668
+ style: {
669
+ overflow: "hidden",
670
+ textOverflow: "ellipsis",
671
+ whiteSpace: "nowrap"
672
+ },
673
+ children
674
+ }
675
+ ) : children
676
+ ]
677
+ }
678
+ );
679
+ }
680
+ );
681
+ TableHead.displayName = "Table.Head";
682
+ var TableFooter = forwardRef(
683
+ ({ children, style, ...props }, ref) => {
684
+ const { theme } = useResolvedTheme();
685
+ const sizing = theme.sizing.table;
686
+ return /* @__PURE__ */ jsx3(
687
+ Box,
688
+ {
689
+ ref,
690
+ flexDirection: "row",
691
+ alignItems: "center",
692
+ justifyContent: "center",
693
+ gap: sizing.paginationGap,
694
+ paddingHorizontal: sizing.paginationPaddingHorizontal,
695
+ width: "100%",
696
+ ...props,
697
+ style,
698
+ children
699
+ }
700
+ );
701
+ }
702
+ );
703
+ TableFooter.displayName = "Table.Footer";
704
+ var Table = Object.assign(TableRoot, {
705
+ Caption: TableCaption,
706
+ Header: TableHeader,
707
+ Body: TableBody,
708
+ Footer: TableFooter,
709
+ Row: TableRow,
710
+ Head: TableHead,
711
+ Cell: TableCell
712
+ });
713
+ export {
714
+ Table
715
+ };
716
+ //# sourceMappingURL=index.mjs.map