@tallyui/components 1.0.0 → 3.0.0-next.0

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.
Files changed (137) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +1113 -83
  3. package/dist/index.js +4936 -353
  4. package/package.json +27 -5
  5. package/src/cart/cart-line-actions.tsx +40 -0
  6. package/src/cart/cart-line.tsx +18 -32
  7. package/src/cart/cart-note-input.tsx +46 -0
  8. package/src/cart/cart-panel.tsx +39 -26
  9. package/src/cart/cart-total.tsx +43 -47
  10. package/src/cart/discount-badge.tsx +19 -0
  11. package/src/cart/index.ts +4 -1
  12. package/src/checkout/cash-tendered.tsx +101 -0
  13. package/src/checkout/change-display.tsx +19 -0
  14. package/src/checkout/index.ts +3 -0
  15. package/src/checkout/order-summary-line.tsx +7 -8
  16. package/src/checkout/order-summary.tsx +8 -7
  17. package/src/checkout/payment-method-card.tsx +10 -9
  18. package/src/checkout/payment-selector.tsx +5 -5
  19. package/src/checkout/receipt-preview.tsx +78 -0
  20. package/src/customer/customer-card.tsx +16 -12
  21. package/src/customer/customer-form.tsx +72 -0
  22. package/src/customer/customer-order-history.tsx +34 -0
  23. package/src/customer/customer-picker.tsx +124 -0
  24. package/src/customer/customer-select.tsx +80 -31
  25. package/src/customer/index.ts +3 -0
  26. package/src/index.ts +114 -1
  27. package/src/input/filter-chip.tsx +85 -27
  28. package/src/input/index.ts +1 -0
  29. package/src/input/quantity-stepper.tsx +64 -0
  30. package/src/input/search-input.tsx +30 -7
  31. package/src/layout/checkout-layout.tsx +46 -0
  32. package/src/layout/index.ts +5 -0
  33. package/src/layout/live-tab-screen.tsx +54 -0
  34. package/src/layout/pos-layout.tsx +64 -0
  35. package/src/layout/settings-layout.tsx +58 -0
  36. package/src/layout/store-settings-choice-screen.tsx +142 -0
  37. package/src/order/index.ts +4 -0
  38. package/src/order/order-card.tsx +48 -0
  39. package/src/order/order-detail.tsx +82 -0
  40. package/src/order/order-list.tsx +39 -0
  41. package/src/order/order-status-badge.tsx +32 -0
  42. package/src/product/category-nav.tsx +70 -0
  43. package/src/product/index.ts +3 -0
  44. package/src/product/product-card.tsx +36 -8
  45. package/src/product/product-grid.tsx +46 -13
  46. package/src/product/product-image.tsx +28 -7
  47. package/src/product/product-list.tsx +44 -0
  48. package/src/product/product-price.tsx +77 -22
  49. package/src/product/product-sku.tsx +5 -5
  50. package/src/product/product-stock-badge.tsx +41 -28
  51. package/src/product/product-title.tsx +2 -3
  52. package/src/product/product-variant-picker.tsx +61 -0
  53. package/src/register/cash-count-input.tsx +85 -0
  54. package/src/register/closure-sheet.tsx +161 -0
  55. package/src/register/index.ts +12 -0
  56. package/src/register/movement-sheet.tsx +118 -0
  57. package/src/register/open-register-card.tsx +109 -0
  58. package/src/register/register-bar.helpers.ts +57 -0
  59. package/src/register/register-bar.tsx +85 -0
  60. package/src/register/register-column.tsx +117 -0
  61. package/src/register/register-count.tsx +330 -0
  62. package/src/register/register-open-close.tsx +52 -0
  63. package/src/register/register-panel.tsx +151 -0
  64. package/src/register/register-picker.tsx +60 -0
  65. package/src/register/register-summary.tsx +72 -0
  66. package/src/sale/cart-bar.tsx +18 -0
  67. package/src/sale/cart.tsx +66 -0
  68. package/src/sale/catalogue.tsx +139 -0
  69. package/src/sale/discount-form.tsx +85 -0
  70. package/src/sale/index.ts +9 -0
  71. package/src/sale/orders-list.tsx +108 -0
  72. package/src/sale/print-style.ts +11 -0
  73. package/src/sale/receipt.tsx +62 -0
  74. package/src/sale/sync-status.tsx +141 -0
  75. package/src/sale/tender.tsx +38 -0
  76. package/src/settings/connector-status.tsx +65 -0
  77. package/src/settings/index.ts +3 -0
  78. package/src/settings/settings-group.tsx +23 -0
  79. package/src/settings/settings-row.tsx +23 -0
  80. package/src/ui/accordion/index.tsx +59 -0
  81. package/src/ui/alert-dialog/index.tsx +91 -0
  82. package/src/ui/avatar/index.tsx +50 -0
  83. package/src/ui/badge/index.tsx +60 -0
  84. package/src/ui/button/index.tsx +90 -0
  85. package/src/ui/card/index.tsx +60 -0
  86. package/src/ui/checkbox/index.tsx +29 -0
  87. package/src/ui/collapsible/index.tsx +24 -0
  88. package/src/ui/combobox/index.tsx +98 -0
  89. package/src/ui/context-menu/index.tsx +159 -0
  90. package/src/ui/dialog/index.tsx +92 -0
  91. package/src/ui/dropdown-menu/index.tsx +161 -0
  92. package/src/ui/hover-card/index.tsx +31 -0
  93. package/src/ui/hstack/index.tsx +43 -0
  94. package/src/ui/icon/index.tsx +44 -0
  95. package/src/ui/icon/search-icon.tsx +9 -0
  96. package/src/ui/icon/svg-icon.tsx +36 -0
  97. package/src/ui/icon-button/index.tsx +58 -0
  98. package/src/ui/index.ts +99 -0
  99. package/src/ui/input/index.tsx +100 -0
  100. package/src/ui/label/index.tsx +26 -0
  101. package/src/ui/loader/index.tsx +37 -0
  102. package/src/ui/popover/index.tsx +31 -0
  103. package/src/ui/progress/index.tsx +28 -0
  104. package/src/ui/radio-group/index.tsx +38 -0
  105. package/src/ui/select/index.tsx +96 -0
  106. package/src/ui/separator/index.tsx +29 -0
  107. package/src/ui/slider/index.tsx +28 -0
  108. package/src/ui/switch/index.tsx +31 -0
  109. package/src/ui/table/index.tsx +59 -0
  110. package/src/ui/tabs/index.tsx +53 -0
  111. package/src/ui/text/index.tsx +36 -0
  112. package/src/ui/textarea/index.tsx +29 -0
  113. package/src/ui/toast/index.tsx +52 -0
  114. package/src/ui/toggle/index.tsx +69 -0
  115. package/src/ui/toggle-group/index.tsx +66 -0
  116. package/src/ui/tooltip/index.tsx +30 -0
  117. package/src/ui/vstack/index.tsx +43 -0
  118. package/src/uniwind-env.d.ts +9 -1
  119. package/src/__tests__/cart-line.test.tsx +0 -53
  120. package/src/__tests__/cart-panel.test.tsx +0 -55
  121. package/src/__tests__/cart-total.test.tsx +0 -60
  122. package/src/__tests__/customer-card.test.tsx +0 -47
  123. package/src/__tests__/customer-select.test.tsx +0 -33
  124. package/src/__tests__/filter-chip.test.tsx +0 -40
  125. package/src/__tests__/helpers.ts +0 -111
  126. package/src/__tests__/order-summary-line.test.tsx +0 -18
  127. package/src/__tests__/order-summary.test.tsx +0 -32
  128. package/src/__tests__/payment-method-card.test.tsx +0 -24
  129. package/src/__tests__/payment-selector.test.tsx +0 -23
  130. package/src/__tests__/product-card.test.tsx +0 -31
  131. package/src/__tests__/product-grid.test.tsx +0 -37
  132. package/src/__tests__/product-image.test.tsx +0 -54
  133. package/src/__tests__/product-price.test.tsx +0 -68
  134. package/src/__tests__/product-sku.test.tsx +0 -47
  135. package/src/__tests__/product-stock-badge.test.tsx +0 -58
  136. package/src/__tests__/product-title.test.tsx +0 -38
  137. package/src/__tests__/search-input.test.tsx +0 -22
@@ -0,0 +1,28 @@
1
+ import * as React from 'react';
2
+ import { View } from 'react-native';
3
+ import { Slider as SliderPrimitive } from '@tallyui/primitives';
4
+ import { cn } from '@tallyui/theme';
5
+
6
+ type SliderProps = React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>;
7
+
8
+ const Slider = React.forwardRef<
9
+ React.ComponentRef<typeof SliderPrimitive.Root>,
10
+ SliderProps
11
+ >(function Slider({ className, ...props }, ref) {
12
+ return (
13
+ <SliderPrimitive.Root
14
+ ref={ref}
15
+ className={cn('relative flex w-full touch-none select-none items-center', className)}
16
+ {...props}
17
+ >
18
+ <SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
19
+ <SliderPrimitive.Range className="absolute h-full bg-primary" />
20
+ </SliderPrimitive.Track>
21
+ <SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background web:ring-offset-background web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2" />
22
+ </SliderPrimitive.Root>
23
+ );
24
+ });
25
+ Slider.displayName = 'Slider';
26
+
27
+ export { Slider };
28
+ export type { SliderProps };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { Switch as SwitchPrimitive } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+
5
+ const Switch = React.forwardRef<
6
+ React.ComponentRef<typeof SwitchPrimitive.Root>,
7
+ React.ComponentPropsWithoutRef<typeof SwitchPrimitive.Root>
8
+ >(function Switch({ className, ...props }, ref) {
9
+ return (
10
+ <SwitchPrimitive.Root
11
+ ref={ref}
12
+ className={cn(
13
+ 'web:peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent web:transition-colors web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2 web:focus-visible:ring-offset-background',
14
+ props.checked ? 'bg-primary' : 'bg-input',
15
+ props.disabled && 'web:cursor-not-allowed opacity-50',
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ <SwitchPrimitive.Thumb
21
+ className={cn(
22
+ 'pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 web:transition-transform',
23
+ props.checked ? 'translate-x-5' : 'translate-x-0',
24
+ )}
25
+ />
26
+ </SwitchPrimitive.Root>
27
+ );
28
+ });
29
+ Switch.displayName = 'Switch';
30
+
31
+ export { Switch };
@@ -0,0 +1,59 @@
1
+ import * as React from 'react';
2
+ import { View, Text, Pressable, type ViewProps, type TextProps } from 'react-native';
3
+ import { TextClassContext } from '@tallyui/primitives';
4
+ import { cn } from '@tallyui/theme';
5
+
6
+ const Table = React.forwardRef<View, ViewProps>(function Table({ className, ...props }, ref) {
7
+ return <View ref={ref} role="table" className={cn('w-full', className)} {...props} />;
8
+ });
9
+ Table.displayName = 'Table';
10
+
11
+ const TableHeader = React.forwardRef<View, ViewProps>(function TableHeader({ className, ...props }, ref) {
12
+ return <View ref={ref} role="rowgroup" className={cn('border-b border-border', className)} {...props} />;
13
+ });
14
+ TableHeader.displayName = 'TableHeader';
15
+
16
+ const TableBody = React.forwardRef<View, ViewProps>(function TableBody({ className, ...props }, ref) {
17
+ return <View ref={ref} role="rowgroup" className={cn('[&>*:last-child]:border-0', className)} {...props} />;
18
+ });
19
+ TableBody.displayName = 'TableBody';
20
+
21
+ const TableFooter = React.forwardRef<View, ViewProps>(function TableFooter({ className, ...props }, ref) {
22
+ return <View ref={ref} role="rowgroup" className={cn('border-t border-border bg-muted/50 font-medium', className)} {...props} />;
23
+ });
24
+ TableFooter.displayName = 'TableFooter';
25
+
26
+ const TableRow = React.forwardRef<View, ViewProps>(function TableRow({ className, ...props }, ref) {
27
+ return (
28
+ <View
29
+ ref={ref}
30
+ role="row"
31
+ className={cn('flex flex-row border-b border-border web:transition-colors web:hover:bg-muted/50', className)}
32
+ {...props}
33
+ />
34
+ );
35
+ });
36
+ TableRow.displayName = 'TableRow';
37
+
38
+ const TableHead = React.forwardRef<View, ViewProps>(function TableHead({ className, ...props }, ref) {
39
+ return (
40
+ <TextClassContext.Provider value="text-muted-foreground text-xs uppercase text-left font-medium">
41
+ <View
42
+ ref={ref}
43
+ role="columnheader"
44
+ className={cn('flex-1 px-4 py-3 text-left font-medium text-muted-foreground', className)}
45
+ {...props}
46
+ />
47
+ </TextClassContext.Provider>
48
+ );
49
+ });
50
+ TableHead.displayName = 'TableHead';
51
+
52
+ const TableCell = React.forwardRef<View, ViewProps>(function TableCell({ className, ...props }, ref) {
53
+ return (
54
+ <View ref={ref} role="cell" className={cn('flex-1 px-4 py-4', className)} {...props} />
55
+ );
56
+ });
57
+ TableCell.displayName = 'TableCell';
58
+
59
+ export { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell };
@@ -0,0 +1,53 @@
1
+ import * as React from 'react';
2
+ import { Tabs as TabsPrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+
5
+ const Tabs = TabsPrimitive.Root;
6
+
7
+ function TabsList({ className, ...props }: React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>) {
8
+ return (
9
+ <TabsPrimitive.List
10
+ className={cn('inline-flex h-10 items-center justify-center rounded-md bg-muted p-1', className)}
11
+ {...props}
12
+ />
13
+ );
14
+ }
15
+
16
+ const TabsTrigger = React.forwardRef<
17
+ React.ComponentRef<typeof TabsPrimitive.Trigger>,
18
+ React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
19
+ >(function TabsTrigger({ className, ...props }, ref) {
20
+ const { value } = TabsPrimitive.useRootContext();
21
+ const isActive = props.value === value;
22
+
23
+ return (
24
+ <TextClassContext.Provider
25
+ value={cn(
26
+ 'text-sm font-medium',
27
+ isActive ? 'text-foreground' : 'text-muted-foreground'
28
+ )}
29
+ >
30
+ <TabsPrimitive.Trigger
31
+ ref={ref}
32
+ className={cn(
33
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium web:ring-offset-background web:transition-all web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
34
+ isActive && 'bg-background shadow-sm',
35
+ className,
36
+ )}
37
+ {...props}
38
+ />
39
+ </TextClassContext.Provider>
40
+ );
41
+ });
42
+ TabsTrigger.displayName = 'TabsTrigger';
43
+
44
+ function TabsContent({ className, ...props }: React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>) {
45
+ return (
46
+ <TabsPrimitive.Content
47
+ className={cn('mt-2 web:ring-offset-background web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2', className)}
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ export { Tabs, TabsList, TabsTrigger, TabsContent };
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { Text as RNText } from 'react-native';
3
+ import { Slot, TextClassContext } from '@tallyui/primitives';
4
+ import { cn } from '@tallyui/theme';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+
7
+ const textVariants = cva('text-foreground web:select-text text-base', {
8
+ variants: {
9
+ variant: {
10
+ default: '',
11
+ link: 'web:hover:underline web:focus:underline web:hover:cursor-pointer group-active:underline',
12
+ },
13
+ },
14
+ defaultVariants: {
15
+ variant: 'default',
16
+ },
17
+ });
18
+
19
+ type TextProps = React.ComponentPropsWithoutRef<typeof RNText> &
20
+ VariantProps<typeof textVariants> & {
21
+ asChild?: boolean;
22
+ };
23
+
24
+ function Text({ className, variant, asChild = false, children, ...props }: TextProps) {
25
+ const textClass = React.useContext(TextClassContext);
26
+ const Component = asChild ? Slot : RNText;
27
+
28
+ return (
29
+ <Component className={cn(textVariants({ variant }), textClass, className)} {...props}>
30
+ {children}
31
+ </Component>
32
+ );
33
+ }
34
+
35
+ export { Text, TextClassContext, textVariants };
36
+ export type { TextProps };
@@ -0,0 +1,29 @@
1
+ import * as React from 'react';
2
+ import { TextInput, type TextInputProps } from 'react-native';
3
+ import { cn } from '@tallyui/theme';
4
+
5
+ type TextareaProps = TextInputProps & {
6
+ className?: string;
7
+ };
8
+
9
+ const Textarea = React.forwardRef<TextInput, TextareaProps>(
10
+ function Textarea({ className, ...props }, ref) {
11
+ return (
12
+ <TextInput
13
+ ref={ref}
14
+ multiline
15
+ textAlignVertical="top"
16
+ className={cn(
17
+ 'min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground web:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
18
+ className,
19
+ )}
20
+ {...props}
21
+ />
22
+ );
23
+ }
24
+ );
25
+
26
+ Textarea.displayName = 'Textarea';
27
+
28
+ export { Textarea };
29
+ export type { TextareaProps };
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { View, Text, Pressable } from 'react-native';
3
+ import { Toast as ToastPrimitive } from '@tallyui/primitives';
4
+ import { cn } from '@tallyui/theme';
5
+ import { cva, type VariantProps } from 'class-variance-authority';
6
+
7
+ const toastVariants = cva(
8
+ 'group pointer-events-auto relative flex w-full flex-row items-center justify-between gap-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg',
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'border-border bg-background text-foreground',
13
+ destructive: 'border-destructive bg-destructive text-destructive-foreground',
14
+ success: 'border-success bg-success text-success-foreground',
15
+ },
16
+ },
17
+ defaultVariants: {
18
+ variant: 'default',
19
+ },
20
+ }
21
+ );
22
+
23
+ type ToastProps = React.ComponentPropsWithoutRef<typeof View> & VariantProps<typeof toastVariants>;
24
+
25
+ function Toast({ className, variant, ...props }: ToastProps) {
26
+ return <View className={cn(toastVariants({ variant }), className)} {...props} />;
27
+ }
28
+
29
+ function ToastTitle({ className, ...props }: React.ComponentPropsWithoutRef<typeof Text>) {
30
+ return <Text className={cn('text-sm font-semibold', className)} {...props} />;
31
+ }
32
+
33
+ function ToastDescription({ className, ...props }: React.ComponentPropsWithoutRef<typeof Text>) {
34
+ return <Text className={cn('text-sm opacity-90', className)} {...props} />;
35
+ }
36
+
37
+ function ToastClose({ className, ...props }: React.ComponentPropsWithoutRef<typeof Pressable>) {
38
+ return (
39
+ <Pressable
40
+ className={cn(
41
+ 'absolute right-2 top-2 rounded-md p-1 opacity-0 web:transition-opacity web:hover:opacity-100 web:focus:opacity-100 web:group-hover:opacity-100',
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ <Text className="text-sm">✕</Text>
47
+ </Pressable>
48
+ );
49
+ }
50
+
51
+ export { Toast, ToastTitle, ToastDescription, ToastClose, toastVariants };
52
+ export type { ToastProps };
@@ -0,0 +1,69 @@
1
+ import * as React from 'react';
2
+ import { Toggle as TogglePrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+
6
+ const toggleVariants = cva(
7
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium web:ring-offset-background web:transition-colors web:hover:bg-muted web:hover:text-muted-foreground web:focus-visible:outline-none web:focus-visible:ring-2 web:focus-visible:ring-ring web:focus-visible:ring-offset-2',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-transparent',
12
+ outline: 'border border-input bg-transparent web:hover:bg-accent web:hover:text-accent-foreground',
13
+ },
14
+ size: {
15
+ default: 'h-10 px-3',
16
+ sm: 'h-9 px-2.5',
17
+ lg: 'h-11 px-5',
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: 'default',
22
+ size: 'default',
23
+ },
24
+ }
25
+ );
26
+
27
+ const toggleTextVariants = cva('text-sm font-medium', {
28
+ variants: {
29
+ variant: {
30
+ default: '',
31
+ outline: '',
32
+ },
33
+ size: {
34
+ default: '',
35
+ sm: '',
36
+ lg: '',
37
+ },
38
+ },
39
+ defaultVariants: {
40
+ variant: 'default',
41
+ size: 'default',
42
+ },
43
+ });
44
+
45
+ type ToggleProps = React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
46
+ VariantProps<typeof toggleVariants>;
47
+
48
+ const Toggle = React.forwardRef<
49
+ React.ComponentRef<typeof TogglePrimitive.Root>,
50
+ ToggleProps
51
+ >(function Toggle({ className, variant, size, ...props }, ref) {
52
+ return (
53
+ <TextClassContext.Provider value={toggleTextVariants({ variant, size })}>
54
+ <TogglePrimitive.Root
55
+ ref={ref}
56
+ className={cn(
57
+ toggleVariants({ variant, size }),
58
+ props.pressed && 'bg-accent text-accent-foreground',
59
+ className,
60
+ )}
61
+ {...props}
62
+ />
63
+ </TextClassContext.Provider>
64
+ );
65
+ });
66
+ Toggle.displayName = 'Toggle';
67
+
68
+ export { Toggle, toggleVariants, toggleTextVariants };
69
+ export type { ToggleProps };
@@ -0,0 +1,66 @@
1
+ import * as React from 'react';
2
+ import { ToggleGroup as ToggleGroupPrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+ import { type VariantProps } from 'class-variance-authority';
5
+ import { toggleVariants, toggleTextVariants } from '../toggle';
6
+
7
+ type ToggleGroupProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
8
+ VariantProps<typeof toggleVariants>;
9
+
10
+ const ToggleGroupContext = React.createContext<VariantProps<typeof toggleVariants>>({
11
+ size: 'default',
12
+ variant: 'default',
13
+ });
14
+
15
+ const ToggleGroup = React.forwardRef<
16
+ React.ComponentRef<typeof ToggleGroupPrimitive.Root>,
17
+ ToggleGroupProps
18
+ >(function ToggleGroup({ className, variant, size, children, ...props }, ref) {
19
+ return (
20
+ <ToggleGroupPrimitive.Root
21
+ ref={ref}
22
+ className={cn('flex flex-row items-center justify-center gap-1', className)}
23
+ {...props}
24
+ >
25
+ <ToggleGroupContext.Provider value={{ variant, size }}>
26
+ {children}
27
+ </ToggleGroupContext.Provider>
28
+ </ToggleGroupPrimitive.Root>
29
+ );
30
+ });
31
+ ToggleGroup.displayName = 'ToggleGroup';
32
+
33
+ type ToggleGroupItemProps = React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
34
+ VariantProps<typeof toggleVariants>;
35
+
36
+ const ToggleGroupItem = React.forwardRef<
37
+ React.ComponentRef<typeof ToggleGroupPrimitive.Item>,
38
+ ToggleGroupItemProps
39
+ >(function ToggleGroupItem({ className, variant, size, ...props }, ref) {
40
+ const context = React.useContext(ToggleGroupContext);
41
+
42
+ return (
43
+ <TextClassContext.Provider
44
+ value={toggleTextVariants({
45
+ variant: variant || context.variant,
46
+ size: size || context.size,
47
+ })}
48
+ >
49
+ <ToggleGroupPrimitive.Item
50
+ ref={ref}
51
+ className={cn(
52
+ toggleVariants({
53
+ variant: variant || context.variant,
54
+ size: size || context.size,
55
+ }),
56
+ className,
57
+ )}
58
+ {...props}
59
+ />
60
+ </TextClassContext.Provider>
61
+ );
62
+ });
63
+ ToggleGroupItem.displayName = 'ToggleGroupItem';
64
+
65
+ export { ToggleGroup, ToggleGroupItem };
66
+ export type { ToggleGroupProps, ToggleGroupItemProps };
@@ -0,0 +1,30 @@
1
+ import * as React from 'react';
2
+ import { Tooltip as TooltipPrimitive, TextClassContext } from '@tallyui/primitives';
3
+ import { cn } from '@tallyui/theme';
4
+
5
+ const Tooltip = TooltipPrimitive.Root;
6
+ const TooltipTrigger = TooltipPrimitive.Trigger;
7
+
8
+ const TooltipContent = React.forwardRef<
9
+ React.ComponentRef<typeof TooltipPrimitive.Content>,
10
+ React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
11
+ >(function TooltipContent({ className, sideOffset = 4, ...props }, ref) {
12
+ return (
13
+ <TooltipPrimitive.Portal>
14
+ <TextClassContext.Provider value="text-sm text-popover-foreground">
15
+ <TooltipPrimitive.Content
16
+ ref={ref}
17
+ sideOffset={sideOffset}
18
+ className={cn(
19
+ 'z-50 overflow-hidden rounded-md border border-border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md',
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ </TextClassContext.Provider>
25
+ </TooltipPrimitive.Portal>
26
+ );
27
+ });
28
+ TooltipContent.displayName = 'TooltipContent';
29
+
30
+ export { Tooltip, TooltipTrigger, TooltipContent };
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { View, type ViewProps } from 'react-native';
3
+ import { cn } from '@tallyui/theme';
4
+ import { cva, type VariantProps } from 'class-variance-authority';
5
+
6
+ const vstackVariants = cva('flex-col', {
7
+ variants: {
8
+ space: {
9
+ none: 'gap-0',
10
+ xs: 'gap-1',
11
+ sm: 'gap-2',
12
+ md: 'gap-3',
13
+ lg: 'gap-4',
14
+ xl: 'gap-6',
15
+ },
16
+ reversed: {
17
+ true: 'flex-col-reverse',
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ space: 'md',
22
+ },
23
+ });
24
+
25
+ type VStackProps = ViewProps & VariantProps<typeof vstackVariants>;
26
+
27
+ const VStack = React.forwardRef<View, VStackProps>(function VStack(
28
+ { className, space, reversed, ...props },
29
+ ref
30
+ ) {
31
+ return (
32
+ <View
33
+ ref={ref}
34
+ className={cn(vstackVariants({ space, reversed }), className)}
35
+ {...props}
36
+ />
37
+ );
38
+ });
39
+
40
+ VStack.displayName = 'VStack';
41
+
42
+ export { VStack, vstackVariants };
43
+ export type { VStackProps };
@@ -2,17 +2,25 @@
2
2
  * Type augmentations for React Native components to support className prop.
3
3
  * Uniwind provides these at runtime — this file ensures TypeScript accepts them.
4
4
  */
5
- export {};
5
+ import 'react-native';
6
6
 
7
7
  declare module 'react-native' {
8
8
  interface ViewProps {
9
9
  className?: string;
10
+ // react-native-web turns each entry into a data-* DOM attribute (e.g. { print: 'hide' } → data-print="hide").
11
+ // TV6a's first use: the print-style hook (`dataSet={{ print: 'hide' }}`) that TV6b lifts.
12
+ dataSet?: { [key: string]: string | number };
10
13
  }
11
14
 
12
15
  interface TextProps {
13
16
  className?: string;
14
17
  }
15
18
 
19
+ interface TextInputProps {
20
+ // Uniwind reads an accent-* class for the placeholder colour.
21
+ placeholderTextColorClassName?: string;
22
+ }
23
+
16
24
  interface ImagePropsBase {
17
25
  className?: string;
18
26
  }
@@ -1,53 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { ConnectorProvider } from '@tallyui/core';
4
- import { CartLine } from '../cart/cart-line';
5
- import { createTestConnector, wooDoc, medusaDoc } from './helpers';
6
-
7
- describe('CartLine', () => {
8
- it('renders product name and line total for WooCommerce', () => {
9
- const connector = createTestConnector('woo');
10
- render(
11
- <ConnectorProvider connector={connector}>
12
- <CartLine item={{ doc: wooDoc, quantity: 2 }} />
13
- </ConnectorProvider>
14
- );
15
- expect(screen.getByText('Espresso Machine Pro')).toBeDefined();
16
- expect(screen.getByText('$1299.00 × 2')).toBeDefined();
17
- expect(screen.getByText('$2598.00')).toBeDefined();
18
- });
19
-
20
- it('renders product name and line total for Medusa', () => {
21
- const connector = createTestConnector('medusa');
22
- render(
23
- <ConnectorProvider connector={connector}>
24
- <CartLine item={{ doc: medusaDoc, quantity: 1 }} />
25
- </ConnectorProvider>
26
- );
27
- expect(screen.getByText('Espresso Machine Pro')).toBeDefined();
28
- expect(screen.getByText('$1299.00 × 1')).toBeDefined();
29
- expect(screen.getByText('$1299.00')).toBeDefined();
30
- });
31
-
32
- it('renders custom currency symbol', () => {
33
- const connector = createTestConnector('woo');
34
- render(
35
- <ConnectorProvider connector={connector}>
36
- <CartLine item={{ doc: wooDoc, quantity: 1 }} currencySymbol="€" />
37
- </ConnectorProvider>
38
- );
39
- expect(screen.getByText('€1299.00 × 1')).toBeDefined();
40
- expect(screen.getByText('€1299.00')).toBeDefined();
41
- });
42
-
43
- it('handles missing price gracefully', () => {
44
- const connector = createTestConnector('woo');
45
- render(
46
- <ConnectorProvider connector={connector}>
47
- <CartLine item={{ doc: {}, quantity: 3 }} />
48
- </ConnectorProvider>
49
- );
50
- expect(screen.getByText('× 3')).toBeDefined();
51
- expect(screen.getByText('—')).toBeDefined();
52
- });
53
- });
@@ -1,55 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render, screen } from '@testing-library/react';
3
- import { Text } from 'react-native';
4
- import { ConnectorProvider } from '@tallyui/core';
5
- import { CartPanel } from '../cart/cart-panel';
6
- import { CartLine } from '../cart/cart-line';
7
- import { createTestConnector, wooDoc } from './helpers';
8
-
9
- describe('CartPanel', () => {
10
- const items = [
11
- { doc: wooDoc, quantity: 2 },
12
- { doc: { ...wooDoc, id: 2, name: 'Grinder', price: '199.99' }, quantity: 1 },
13
- ];
14
-
15
- it('renders cart line items', () => {
16
- const connector = createTestConnector('woo');
17
- render(
18
- <ConnectorProvider connector={connector}>
19
- <CartPanel items={items} renderItem={(item) => <CartLine item={item} />} />
20
- </ConnectorProvider>
21
- );
22
- expect(screen.getByText('Espresso Machine Pro')).toBeDefined();
23
- expect(screen.getByText('Grinder')).toBeDefined();
24
- });
25
-
26
- it('renders header and footer slots', () => {
27
- const connector = createTestConnector('woo');
28
- render(
29
- <ConnectorProvider connector={connector}>
30
- <CartPanel
31
- items={items}
32
- renderItem={(item) => <CartLine item={item} />}
33
- header={<Text>Customer: Jane</Text>}
34
- footer={<Text>Total: $1399.97</Text>}
35
- />
36
- </ConnectorProvider>
37
- );
38
- expect(screen.getByText('Customer: Jane')).toBeDefined();
39
- expect(screen.getByText('Total: $1399.97')).toBeDefined();
40
- });
41
-
42
- it('renders emptyState when no items', () => {
43
- const connector = createTestConnector('woo');
44
- render(
45
- <ConnectorProvider connector={connector}>
46
- <CartPanel
47
- items={[]}
48
- renderItem={() => null}
49
- emptyState={<Text>Cart is empty</Text>}
50
- />
51
- </ConnectorProvider>
52
- );
53
- expect(screen.getByText('Cart is empty')).toBeDefined();
54
- });
55
- });