@vetc-miniapp/ui-react 0.0.24 → 0.0.25

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 (172) hide show
  1. package/dist/bridge.d.ts +11 -0
  2. package/dist/bridge.js +20 -0
  3. package/dist/components/app.d.ts +6 -0
  4. package/dist/components/app.js +34 -0
  5. package/dist/components/avatar/Avatar.d.ts +21 -0
  6. package/dist/components/avatar/Avatar.js +33 -0
  7. package/dist/components/avatar/index.js +1 -0
  8. package/dist/components/bottom-sheet/BottomSheet.d.ts +19 -0
  9. package/dist/components/bottom-sheet/BottomSheet.js +70 -0
  10. package/dist/components/bottom-sheet/index.js +1 -0
  11. package/dist/components/button/Button.d.ts +32 -0
  12. package/dist/components/button/Button.js +165 -0
  13. package/dist/components/button/index.js +1 -0
  14. package/dist/components/button-group/ButtonGroup.d.ts +28 -0
  15. package/dist/components/button-group/ButtonGroup.js +21 -0
  16. package/dist/components/button-group/index.js +1 -0
  17. package/dist/components/card/Card.d.ts +18 -0
  18. package/dist/components/card/Card.js +35 -0
  19. package/dist/components/card/index.js +1 -0
  20. package/dist/components/checkbox/Checkbox.d.ts +41 -0
  21. package/dist/components/checkbox/Checkbox.js +94 -0
  22. package/dist/components/checkbox/index.js +1 -0
  23. package/dist/components/chip/Chip.d.ts +24 -0
  24. package/dist/components/chip/Chip.js +83 -0
  25. package/dist/components/chip/index.js +1 -0
  26. package/dist/components/dialog/Dialog.d.ts +19 -0
  27. package/dist/components/dialog/Dialog.js +51 -0
  28. package/dist/components/dialog/index.js +1 -0
  29. package/dist/components/divider/Divider.d.ts +16 -0
  30. package/dist/components/divider/Divider.js +18 -0
  31. package/dist/components/divider/index.js +1 -0
  32. package/dist/components/input/Input.d.ts +40 -0
  33. package/dist/components/input/Input.js +51 -0
  34. package/dist/components/input/index.js +1 -0
  35. package/dist/components/list/List.d.ts +31 -0
  36. package/dist/components/list/List.js +72 -0
  37. package/dist/components/list/index.js +1 -0
  38. package/dist/components/loading/Loading.d.ts +28 -0
  39. package/dist/components/loading/Loading.js +33 -0
  40. package/dist/components/loading/index.js +1 -0
  41. package/dist/components/modal/Modal.d.ts +38 -0
  42. package/dist/components/modal/Modal.js +50 -0
  43. package/dist/components/modal/index.js +1 -0
  44. package/dist/components/navigation-bar/NavigationBar.d.ts +44 -0
  45. package/dist/components/navigation-bar/NavigationBar.js +70 -0
  46. package/dist/components/navigation-bar/index.js +1 -0
  47. package/dist/components/radio/Radio.d.ts +40 -0
  48. package/dist/components/radio/Radio.js +88 -0
  49. package/dist/components/radio/index.js +1 -0
  50. package/dist/components/select/Select.d.ts +29 -0
  51. package/dist/components/select/Select.js +30 -0
  52. package/dist/components/select/index.js +1 -0
  53. package/dist/components/switch/Switch.d.ts +23 -0
  54. package/dist/components/switch/Switch.js +81 -0
  55. package/dist/components/switch/index.js +1 -0
  56. package/dist/components/tab-bar/TabBar.d.ts +28 -0
  57. package/dist/components/tab-bar/TabBar.js +60 -0
  58. package/dist/components/tab-bar/index.js +1 -0
  59. package/dist/components/textarea/Textarea.d.ts +31 -0
  60. package/dist/components/textarea/Textarea.js +33 -0
  61. package/dist/components/textarea/index.js +1 -0
  62. package/dist/components/toast/Toast.d.ts +41 -0
  63. package/dist/components/toast/Toast.js +61 -0
  64. package/dist/components/toast/index.js +1 -0
  65. package/dist/components/typography/Typography.d.ts +45 -0
  66. package/dist/components/typography/Typography.js +143 -0
  67. package/dist/components/typography/index.js +1 -0
  68. package/dist/hooks/use-app-pause.d.ts +6 -0
  69. package/dist/hooks/use-app-pause.js +29 -0
  70. package/dist/hooks/use-app-resume.d.ts +6 -0
  71. package/dist/hooks/use-app-resume.js +28 -0
  72. package/{src/ui-react/hooks/use-app-state.ts → dist/hooks/use-app-state.d.ts} +0 -1
  73. package/dist/hooks/use-app-state.js +1 -0
  74. package/dist/hooks/use-did-hide.d.ts +6 -0
  75. package/dist/hooks/use-did-hide.js +21 -0
  76. package/dist/hooks/use-did-show.d.ts +6 -0
  77. package/dist/hooks/use-did-show.js +21 -0
  78. package/dist/hooks/use-listener-scan-qr.d.ts +21 -0
  79. package/dist/hooks/use-listener-scan-qr.js +29 -0
  80. package/dist/hooks/use-navigate.d.ts +8 -0
  81. package/dist/hooks/use-navigate.js +23 -0
  82. package/dist/hooks/use-tap-app-bar.d.ts +6 -0
  83. package/dist/hooks/use-tap-app-bar.js +21 -0
  84. package/{src/ui-react/index.ts → dist/index.d.ts} +1 -30
  85. package/dist/index.js +41 -0
  86. package/dist/styles/VETCProvider.d.ts +114 -0
  87. package/dist/styles/VETCProvider.js +124 -0
  88. package/{src/ui-react → dist}/styles/tokens.css +22 -1
  89. package/dist/tokens/colors.d.ts +127 -0
  90. package/dist/tokens/colors.js +75 -0
  91. package/dist/tokens/index.js +3 -0
  92. package/dist/tokens/spacing.d.ts +56 -0
  93. package/dist/tokens/spacing.js +56 -0
  94. package/dist/tokens/typography.d.ts +121 -0
  95. package/dist/tokens/typography.js +57 -0
  96. package/dist/types/app.d.ts +21 -0
  97. package/dist/types/app.js +1 -0
  98. package/package.json +13 -7
  99. package/src/dist/ui-react/index.js +0 -2
  100. package/src/dist/ui-react/index.js.LICENSE.txt +0 -11
  101. package/src/ui-react/bridge.js +0 -36
  102. package/src/ui-react/bridge.ts +0 -48
  103. package/src/ui-react/components/app.d.ts +0 -7
  104. package/src/ui-react/components/app.jsx +0 -80
  105. package/src/ui-react/components/app.tsx +0 -42
  106. package/src/ui-react/components/app1.js +0 -101
  107. package/src/ui-react/components/avatar/Avatar.tsx +0 -88
  108. package/src/ui-react/components/bottom-sheet/BottomSheet.tsx +0 -149
  109. package/src/ui-react/components/button/Button.tsx +0 -246
  110. package/src/ui-react/components/button-group/ButtonGroup.tsx +0 -108
  111. package/src/ui-react/components/card/Card.tsx +0 -77
  112. package/src/ui-react/components/checkbox/Checkbox.tsx +0 -232
  113. package/src/ui-react/components/chip/Chip.tsx +0 -137
  114. package/src/ui-react/components/dialog/Dialog.tsx +0 -135
  115. package/src/ui-react/components/divider/Divider.tsx +0 -54
  116. package/src/ui-react/components/input/Input.tsx +0 -195
  117. package/src/ui-react/components/list/List.tsx +0 -180
  118. package/src/ui-react/components/loading/Loading.tsx +0 -121
  119. package/src/ui-react/components/modal/Modal.tsx +0 -116
  120. package/src/ui-react/components/navigation-bar/NavigationBar.tsx +0 -188
  121. package/src/ui-react/components/radio/Radio.tsx +0 -216
  122. package/src/ui-react/components/select/Select.tsx +0 -109
  123. package/src/ui-react/components/switch/Switch.tsx +0 -164
  124. package/src/ui-react/components/tab-bar/TabBar.tsx +0 -137
  125. package/src/ui-react/components/textarea/Textarea.tsx +0 -109
  126. package/src/ui-react/components/toast/Toast.ts +0 -98
  127. package/src/ui-react/components/typography/Typography.tsx +0 -201
  128. package/src/ui-react/hooks/use-app-pause.js +0 -35
  129. package/src/ui-react/hooks/use-app-pause.ts +0 -33
  130. package/src/ui-react/hooks/use-app-resume.js +0 -37
  131. package/src/ui-react/hooks/use-app-resume.ts +0 -32
  132. package/src/ui-react/hooks/use-app-state.js +0 -35
  133. package/src/ui-react/hooks/use-did-hide.js +0 -25
  134. package/src/ui-react/hooks/use-did-hide.ts +0 -34
  135. package/src/ui-react/hooks/use-did-show.js +0 -26
  136. package/src/ui-react/hooks/use-did-show.ts +0 -34
  137. package/src/ui-react/hooks/use-listener-scan-qr.js +0 -33
  138. package/src/ui-react/hooks/use-listener-scan-qr.ts +0 -52
  139. package/src/ui-react/hooks/use-navigate.js +0 -15
  140. package/src/ui-react/hooks/use-navigate.ts +0 -41
  141. package/src/ui-react/hooks/use-tap-app-bar.js +0 -26
  142. package/src/ui-react/hooks/use-tap-app-bar.ts +0 -34
  143. package/src/ui-react/index.js +0 -9
  144. package/src/ui-react/styles/VETCProvider.tsx +0 -152
  145. package/src/ui-react/tokens/colors.ts +0 -91
  146. package/src/ui-react/tokens/spacing.ts +0 -59
  147. package/src/ui-react/tokens/typography.ts +0 -63
  148. package/src/ui-react/tokens_vetc.json +0 -1517
  149. package/src/ui-react/types/app.js +0 -30
  150. package/src/ui-react/types/app.ts +0 -32
  151. /package/{src/ui-react/components/avatar/index.ts → dist/components/avatar/index.d.ts} +0 -0
  152. /package/{src/ui-react/components/bottom-sheet/index.ts → dist/components/bottom-sheet/index.d.ts} +0 -0
  153. /package/{src/ui-react/components/button/index.ts → dist/components/button/index.d.ts} +0 -0
  154. /package/{src/ui-react/components/button-group/index.ts → dist/components/button-group/index.d.ts} +0 -0
  155. /package/{src/ui-react/components/card/index.ts → dist/components/card/index.d.ts} +0 -0
  156. /package/{src/ui-react/components/checkbox/index.ts → dist/components/checkbox/index.d.ts} +0 -0
  157. /package/{src/ui-react/components/chip/index.ts → dist/components/chip/index.d.ts} +0 -0
  158. /package/{src/ui-react/components/dialog/index.ts → dist/components/dialog/index.d.ts} +0 -0
  159. /package/{src/ui-react/components/divider/index.ts → dist/components/divider/index.d.ts} +0 -0
  160. /package/{src/ui-react/components/input/index.ts → dist/components/input/index.d.ts} +0 -0
  161. /package/{src/ui-react/components/list/index.ts → dist/components/list/index.d.ts} +0 -0
  162. /package/{src/ui-react/components/loading/index.ts → dist/components/loading/index.d.ts} +0 -0
  163. /package/{src/ui-react/components/modal/index.ts → dist/components/modal/index.d.ts} +0 -0
  164. /package/{src/ui-react/components/navigation-bar/index.ts → dist/components/navigation-bar/index.d.ts} +0 -0
  165. /package/{src/ui-react/components/radio/index.ts → dist/components/radio/index.d.ts} +0 -0
  166. /package/{src/ui-react/components/select/index.ts → dist/components/select/index.d.ts} +0 -0
  167. /package/{src/ui-react/components/switch/index.ts → dist/components/switch/index.d.ts} +0 -0
  168. /package/{src/ui-react/components/tab-bar/index.ts → dist/components/tab-bar/index.d.ts} +0 -0
  169. /package/{src/ui-react/components/textarea/index.ts → dist/components/textarea/index.d.ts} +0 -0
  170. /package/{src/ui-react/components/toast/index.ts → dist/components/toast/index.d.ts} +0 -0
  171. /package/{src/ui-react/components/typography/index.ts → dist/components/typography/index.d.ts} +0 -0
  172. /package/{src/ui-react/tokens/index.ts → dist/tokens/index.d.ts} +0 -0
@@ -1,149 +0,0 @@
1
- /**
2
- * VETC BottomSheet — tokenized
3
- * Figma: Sheet page — 16px top radius, handle bar, slide-up animation
4
- */
5
- import React, { useEffect } from 'react';
6
-
7
- export interface BottomSheetProps {
8
- open: boolean;
9
- title?: React.ReactNode;
10
- children?: React.ReactNode;
11
- maskClosable?: boolean;
12
- showHandle?: boolean;
13
- maxHeight?: string;
14
- onClose?: () => void;
15
- className?: string;
16
- style?: React.CSSProperties;
17
- id?: string;
18
- }
19
-
20
- export function BottomSheet({
21
- open,
22
- title,
23
- children,
24
- maskClosable = true,
25
- showHandle = true,
26
- maxHeight = '90vh',
27
- onClose,
28
- className = '',
29
- style,
30
- id,
31
- }: BottomSheetProps) {
32
- // Lock body scroll when open
33
- useEffect(() => {
34
- document.body.style.overflow = open ? 'hidden' : '';
35
- return () => { document.body.style.overflow = ''; };
36
- }, [open]);
37
-
38
- if (!open) return null;
39
-
40
- return (
41
- <>
42
- {/* Overlay */}
43
- <div
44
- className="vetc-bottom-sheet-overlay"
45
- aria-hidden="true"
46
- onClick={() => maskClosable && onClose?.()}
47
- style={{
48
- position: 'fixed',
49
- inset: 0,
50
- backgroundColor: 'var(--vetc-sheet-overlay)',
51
- zIndex: 1000,
52
- animation: 'vetc-fade-in var(--vetc-transition-base)',
53
- }}
54
- />
55
-
56
- {/* Sheet panel */}
57
- <div
58
- id={id}
59
- role="dialog"
60
- aria-modal="true"
61
- className={`vetc-bottom-sheet ${className}`}
62
- style={{
63
- position: 'fixed',
64
- bottom: 0,
65
- left: 0,
66
- right: 0,
67
- backgroundColor: 'var(--vetc-sheet-bg)',
68
- borderRadius: `var(--vetc-sheet-radius) var(--vetc-sheet-radius) 0 0`,
69
- maxHeight,
70
- overflowY: 'auto',
71
- zIndex: 1001,
72
- fontFamily: 'var(--vetc-font-family)',
73
- animation: 'vetc-slide-up var(--vetc-transition-slow) cubic-bezier(0.32, 0.72, 0, 1)',
74
- ...style,
75
- }}
76
- >
77
- {/* Handle bar */}
78
- {showHandle && (
79
- <div style={{ display: 'flex', justifyContent: 'center', paddingTop: 'var(--vetc-space-8)', paddingBottom: 'var(--vetc-space-4)' }}>
80
- <div style={{
81
- width: 'var(--vetc-sheet-handle-width)',
82
- height: 'var(--vetc-sheet-handle-height)',
83
- borderRadius: 'var(--vetc-radius-pill)',
84
- backgroundColor: 'var(--vetc-sheet-handle-color)',
85
- }} />
86
- </div>
87
- )}
88
-
89
- {/* Header */}
90
- {title && (
91
- <div style={{
92
- padding: `var(--vetc-space-12) var(--vetc-sheet-padding)`,
93
- borderBottom: `1px solid var(--vetc-sheet-border)`,
94
- display: 'flex',
95
- alignItems: 'center',
96
- justifyContent: 'space-between',
97
- }}>
98
- <span style={{
99
- fontSize: 'var(--vetc-sheet-title-size)',
100
- fontWeight: 'var(--vetc-sheet-title-weight)' as any,
101
- color: 'var(--vetc-sheet-title-color)',
102
- lineHeight: 'var(--vetc-line-height-relaxed)',
103
- }}>
104
- {title}
105
- </span>
106
- {onClose && (
107
- <button
108
- onClick={onClose}
109
- aria-label="Đóng"
110
- style={{
111
- background: 'none',
112
- border: 'none',
113
- cursor: 'pointer',
114
- padding: 'var(--vetc-space-4)',
115
- display: 'flex',
116
- color: 'var(--vetc-color-icon-muted)',
117
- borderRadius:'var(--vetc-radius-pill)',
118
- transition: 'background-color var(--vetc-transition-fast)',
119
- }}
120
- >
121
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true">
122
- <path d="M18 6L6 18M6 6L18 18" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
123
- </svg>
124
- </button>
125
- )}
126
- </div>
127
- )}
128
-
129
- {/* Body */}
130
- <div style={{ padding: 'var(--vetc-sheet-padding)' }}>
131
- {children}
132
- </div>
133
- </div>
134
-
135
- <style>{`
136
- @keyframes vetc-fade-in {
137
- from { opacity: 0 }
138
- to { opacity: 1 }
139
- }
140
- @keyframes vetc-slide-up {
141
- from { transform: translateY(100%) }
142
- to { transform: translateY(0) }
143
- }
144
- `}</style>
145
- </>
146
- );
147
- }
148
-
149
- export default BottomSheet;
@@ -1,246 +0,0 @@
1
- /**
2
- * VETC Button Component
3
- * Figma: filled/outlined/elevated/ghost styles
4
- * Large: h=48px, radius=12px, font=16px/600
5
- * Small: h=32px, radius=8px, font=14px/600
6
- * style: filled | outlined | elevated | ghost | danger | danger-outlined
7
- * variant: brand (green) | neutral (gray-90)
8
- */
9
- import React, { useState } from 'react';
10
-
11
- export type ButtonStyle = 'filled' | 'outlined' | 'elevated' | 'ghost' | 'danger' | 'danger-outlined';
12
- export type ButtonVariant = 'brand' | 'neutral';
13
- export type ButtonSize = 'lg' | 'sm';
14
- export type ButtonShape = 'rounded' | 'pill';
15
-
16
- export interface ButtonProps {
17
- style?: ButtonStyle;
18
- variant?: ButtonVariant;
19
- size?: ButtonSize;
20
- shape?: ButtonShape;
21
- block?: boolean;
22
- disabled?: boolean;
23
- loading?: boolean;
24
- icon?: React.ReactNode;
25
- iconRight?: React.ReactNode;
26
- onClick?: React.MouseEventHandler<HTMLButtonElement>;
27
- children?: React.ReactNode;
28
- htmlType?: 'button' | 'submit' | 'reset';
29
- className?: string;
30
- css?: React.CSSProperties;
31
- id?: string;
32
- }
33
-
34
- type Tokens = {
35
- bg: string; bgHover: string; bgActive: string;
36
- text: string; border: string; shadow: string;
37
- };
38
-
39
- const BRAND: Record<ButtonStyle, Tokens> = {
40
- filled: {
41
- bg: 'var(--vetc-color-brand)',
42
- bgHover: 'var(--vetc-color-brand-hover)',
43
- bgActive: 'var(--vetc-color-brand-active)',
44
- text: 'var(--vetc-white)',
45
- border: 'transparent',
46
- shadow: 'none',
47
- },
48
- outlined: {
49
- bg: 'transparent',
50
- bgHover: 'var(--vetc-green-02)',
51
- bgActive: 'var(--vetc-green-05)',
52
- text: 'var(--vetc-color-brand)',
53
- border: 'var(--vetc-color-brand)',
54
- shadow: 'none',
55
- },
56
- elevated: {
57
- bg: 'var(--vetc-color-bg)',
58
- bgHover: 'var(--vetc-color-bg-hover)',
59
- bgActive: 'var(--vetc-color-bg-pressed)',
60
- text: 'var(--vetc-color-brand)',
61
- border: 'transparent',
62
- shadow: 'var(--vetc-shadow-md)',
63
- },
64
- ghost: {
65
- bg: 'transparent',
66
- bgHover: 'var(--vetc-color-bg-hover)',
67
- bgActive: 'var(--vetc-color-bg-pressed)',
68
- text: 'var(--vetc-color-brand)',
69
- border: 'transparent',
70
- shadow: 'none',
71
- },
72
- danger: {
73
- bg: 'var(--vetc-color-negative)',
74
- bgHover: 'var(--vetc-red-60)',
75
- bgActive: 'var(--vetc-red-60)',
76
- text: 'var(--vetc-white)',
77
- border: 'transparent',
78
- shadow: 'none',
79
- },
80
- 'danger-outlined': {
81
- bg: 'transparent',
82
- bgHover: 'var(--vetc-red-02)',
83
- bgActive: 'var(--vetc-red-05)',
84
- text: 'var(--vetc-color-negative)',
85
- border: 'var(--vetc-color-negative)',
86
- shadow: 'none',
87
- },
88
- };
89
-
90
- const NEUTRAL: Record<ButtonStyle, Tokens> = {
91
- filled: {
92
- bg: 'var(--vetc-gray-90)',
93
- bgHover: 'var(--vetc-gray-80)',
94
- bgActive: 'var(--vetc-gray-70)',
95
- text: 'var(--vetc-white)',
96
- border: 'transparent',
97
- shadow: 'none',
98
- },
99
- outlined: {
100
- bg: 'transparent',
101
- bgHover: 'var(--vetc-gray-05)',
102
- bgActive: 'var(--vetc-gray-10)',
103
- text: 'var(--vetc-gray-90)',
104
- border: 'var(--vetc-gray-90)',
105
- shadow: 'none',
106
- },
107
- elevated: {
108
- bg: 'var(--vetc-color-bg)',
109
- bgHover: 'var(--vetc-color-bg-hover)',
110
- bgActive: 'var(--vetc-color-bg-pressed)',
111
- text: 'var(--vetc-color-text-primary)',
112
- border: 'transparent',
113
- shadow: 'var(--vetc-shadow-md)',
114
- },
115
- ghost: {
116
- bg: 'transparent',
117
- bgHover: 'var(--vetc-color-bg-hover)',
118
- bgActive: 'var(--vetc-color-bg-pressed)',
119
- text: 'var(--vetc-color-text-primary)',
120
- border: 'transparent',
121
- shadow: 'none',
122
- },
123
- // danger variants are semantic — variant (brand/neutral) has no effect
124
- danger: {
125
- bg: 'var(--vetc-color-negative)',
126
- bgHover: 'var(--vetc-red-60)',
127
- bgActive: 'var(--vetc-red-60)',
128
- text: 'var(--vetc-white)',
129
- border: 'transparent',
130
- shadow: 'none',
131
- },
132
- 'danger-outlined': {
133
- bg: 'transparent',
134
- bgHover: 'var(--vetc-red-02)',
135
- bgActive: 'var(--vetc-red-05)',
136
- text: 'var(--vetc-color-negative)',
137
- border: 'var(--vetc-color-negative)',
138
- shadow: 'none',
139
- },
140
- };
141
-
142
- const LoadingSpinner = ({ color }: { color: string }) => (
143
- <svg
144
- width="16" height="16" viewBox="0 0 16 16" fill="none"
145
- style={{ animation: 'vetc-btn-spin 0.6s linear infinite', flexShrink: 0 }}
146
- aria-hidden="true"
147
- >
148
- <circle cx="8" cy="8" r="6" stroke={color} strokeOpacity="0.25" strokeWidth="2" />
149
- <path d="M8 2a6 6 0 016 6" stroke={color} strokeWidth="2" strokeLinecap="round" />
150
- </svg>
151
- );
152
-
153
- export function Button({
154
- style = 'filled',
155
- variant = 'brand',
156
- size = 'lg',
157
- shape = 'rounded',
158
- block = false,
159
- disabled = false,
160
- loading = false,
161
- icon,
162
- iconRight,
163
- onClick,
164
- children,
165
- htmlType = 'button',
166
- className = '',
167
- css,
168
- id,
169
- }: ButtonProps) {
170
- const [pressed, setPressed] = useState(false);
171
- const [hovered, setHovered] = useState(false);
172
-
173
- const tokens = (variant === 'neutral' ? NEUTRAL : BRAND)[style];
174
- const isDisabled = disabled || loading;
175
-
176
- const height = size === 'lg' ? 'var(--vetc-btn-height-lg)' : 'var(--vetc-btn-height-sm)';
177
- const fontSize = size === 'lg' ? 'var(--vetc-btn-font-size-lg)' : 'var(--vetc-btn-font-size-sm)';
178
- const paddingX = size === 'lg' ? 'var(--vetc-btn-padding-x-lg)' : 'var(--vetc-btn-padding-x-sm)';
179
- const radius = shape === 'pill' ? 'var(--vetc-btn-radius-pill)'
180
- : size === 'lg' ? 'var(--vetc-btn-radius-lg)'
181
- : 'var(--vetc-btn-radius-sm)';
182
-
183
- let bg = tokens.bg;
184
- let border = tokens.border;
185
- let shadow = tokens.shadow;
186
-
187
- if (isDisabled) {
188
- bg = 'var(--vetc-btn-disabled-bg)';
189
- border = 'var(--vetc-btn-disabled-border)';
190
- shadow = 'none';
191
- } else if (pressed) {
192
- bg = tokens.bgActive;
193
- } else if (hovered) {
194
- bg = tokens.bgHover;
195
- }
196
-
197
- const textColor = isDisabled ? 'var(--vetc-btn-disabled-text)' : tokens.text;
198
- const spinnerColor = isDisabled ? 'var(--vetc-gray-30)' : tokens.text;
199
-
200
- return (
201
- <>
202
- <button
203
- id={id}
204
- type={htmlType}
205
- disabled={isDisabled}
206
- onClick={onClick}
207
- onMouseEnter={() => setHovered(true)}
208
- onMouseLeave={() => { setHovered(false); setPressed(false); }}
209
- onMouseDown={() => setPressed(true)}
210
- onMouseUp={() => setPressed(false)}
211
- className={`vetc-btn vetc-btn--${style} vetc-btn--${variant} vetc-btn--${size} ${className}`}
212
- style={{
213
- display: 'inline-flex',
214
- alignItems: 'center',
215
- justifyContent: 'center',
216
- gap: 'var(--vetc-space-8)',
217
- height,
218
- width: block ? '100%' : undefined,
219
- paddingInline: paddingX,
220
- borderRadius: radius,
221
- border: `1px solid ${border}`,
222
- backgroundColor: bg,
223
- boxShadow: shadow,
224
- color: textColor,
225
- fontSize,
226
- fontWeight: 'var(--vetc-btn-font-weight)' as any,
227
- fontFamily: 'var(--vetc-font-family)',
228
- lineHeight: 'var(--vetc-line-height-relaxed)',
229
- cursor: isDisabled ? 'not-allowed' : 'pointer',
230
- transition: 'background-color var(--vetc-transition-fast), box-shadow var(--vetc-transition-fast)',
231
- userSelect: 'none',
232
- outline: 'none',
233
- whiteSpace: 'nowrap',
234
- ...css,
235
- }}
236
- >
237
- {loading ? <LoadingSpinner color={spinnerColor} /> : icon && <span style={{ display: 'inline-flex', flexShrink: 0 }}>{icon}</span>}
238
- {children && <span>{children}</span>}
239
- {iconRight && !loading && <span style={{ display: 'inline-flex', flexShrink: 0 }}>{iconRight}</span>}
240
- </button>
241
- <style>{`@keyframes vetc-btn-spin { to { transform: rotate(360deg); } }`}</style>
242
- </>
243
- );
244
- }
245
-
246
- export default Button;
@@ -1,108 +0,0 @@
1
- import React from 'react';
2
- import { Button } from '../button';
3
-
4
- export type ButtonGroupLayout = 'inline' | 'stacked';
5
- export type ButtonGroupVariant = 'bounding' | 'none';
6
-
7
- export interface ButtonGroupAction {
8
- label: React.ReactNode;
9
- onClick?: () => void;
10
- loading?: boolean;
11
- disabled?: boolean;
12
- }
13
-
14
- export interface ButtonGroupProps {
15
- /** Primary action — brand filled, rightmost (inline) or bottom (stacked). Required. */
16
- primary: ButtonGroupAction & {
17
- /** danger = red filled style */
18
- danger?: boolean;
19
- /** Override primary button color. Default: brand */
20
- variant?: 'brand' | 'neutral';
21
- };
22
- /** Secondary actions — neutral outlined. Max 2. */
23
- secondary?: ButtonGroupAction[];
24
- /** Optional content rendered above the button row */
25
- content?: React.ReactNode;
26
- /** inline: buttons side by side | stacked: buttons stacked vertically */
27
- layout?: ButtonGroupLayout;
28
- /** bounding: top border + background | none: no container styling */
29
- variant?: ButtonGroupVariant;
30
- }
31
-
32
- export function ButtonGroup({
33
- primary,
34
- secondary = [],
35
- content,
36
- layout = 'inline',
37
- variant = 'none',
38
- }: ButtonGroupProps) {
39
- const isInline = layout === 'inline';
40
- const isBounding = variant === 'bounding';
41
-
42
- const containerStyle: React.CSSProperties = isBounding
43
- ? {
44
- backgroundColor: 'var(--vetc-btn-group-bg)',
45
- borderTop: `1px solid var(--vetc-btn-group-border-color)`,
46
- padding: 'var(--vetc-btn-group-padding)',
47
- fontFamily: 'var(--vetc-font-family)',
48
- }
49
- : { fontFamily: 'var(--vetc-font-family)' };
50
-
51
- const rowStyle: React.CSSProperties = isInline
52
- ? { display: 'flex', gap: 'var(--vetc-btn-group-gap)', alignItems: 'stretch' }
53
- : { display: 'flex', flexDirection: 'column', gap: 'var(--vetc-btn-group-gap)' };
54
-
55
- const secondaryBtns = secondary.map((action, i) => (
56
- <Button
57
- key={i}
58
- style="outlined"
59
- variant="neutral"
60
- block={!isInline}
61
- loading={action.loading}
62
- disabled={action.disabled}
63
- onClick={action.onClick}
64
- css={isInline ? { flex: 1 } : undefined}
65
- >
66
- {action.label}
67
- </Button>
68
- ));
69
-
70
- const primaryBtn = (
71
- <Button
72
- style={primary.danger ? 'danger' : 'filled'}
73
- variant={primary.variant ?? 'brand'}
74
- block={!isInline}
75
- loading={primary.loading}
76
- disabled={primary.disabled}
77
- onClick={primary.onClick}
78
- css={isInline ? { flex: 1 } : undefined}
79
- >
80
- {primary.label}
81
- </Button>
82
- );
83
-
84
- return (
85
- <div style={containerStyle}>
86
- {content && (
87
- <div style={{ marginBottom: 'var(--vetc-btn-group-padding)' }}>
88
- {content}
89
- </div>
90
- )}
91
- <div style={rowStyle}>
92
- {isInline ? (
93
- <>
94
- {secondaryBtns}
95
- {primaryBtn}
96
- </>
97
- ) : (
98
- <>
99
- {primaryBtn}
100
- {secondaryBtns}
101
- </>
102
- )}
103
- </div>
104
- </div>
105
- );
106
- }
107
-
108
- export default ButtonGroup;
@@ -1,77 +0,0 @@
1
- /**
2
- * VETC Card — tokenized
3
- */
4
- import React from 'react';
5
- import { Card as AntCard } from 'antd';
6
-
7
- export type CardElevation = 'flat' | 'raised' | 'outlined';
8
-
9
- export interface CardProps {
10
- elevation?: CardElevation;
11
- title?: React.ReactNode;
12
- extra?: React.ReactNode;
13
- children?: React.ReactNode;
14
- onClick?: () => void;
15
- className?: string;
16
- style?: React.CSSProperties;
17
- bodyStyle?: React.CSSProperties;
18
- id?: string;
19
- }
20
-
21
- export function Card({
22
- elevation = 'outlined',
23
- title,
24
- extra,
25
- children,
26
- onClick,
27
- className = '',
28
- style,
29
- bodyStyle,
30
- id,
31
- }: CardProps) {
32
- const shadowMap: Record<CardElevation, string> = {
33
- flat: 'var(--vetc-shadow-none)',
34
- raised: 'var(--vetc-card-shadow-raised)',
35
- outlined: 'var(--vetc-shadow-none)',
36
- };
37
- const borderMap: Record<CardElevation, string> = {
38
- flat: 'none',
39
- raised: 'none',
40
- outlined: `1px solid var(--vetc-card-border)`,
41
- };
42
-
43
- return (
44
- <AntCard
45
- id={id}
46
- title={title}
47
- extra={extra}
48
- onClick={onClick}
49
- className={`vetc-card vetc-card--${elevation} ${className}`}
50
- styles={{
51
- body: {
52
- padding: 'var(--vetc-card-padding)',
53
- fontFamily: 'var(--vetc-font-family)',
54
- ...bodyStyle,
55
- },
56
- header: title
57
- ? { borderBottom: `1px solid var(--vetc-card-border)`, padding: `var(--vetc-space-12) var(--vetc-card-padding)` }
58
- : undefined,
59
- }}
60
- style={{
61
- borderRadius: 'var(--vetc-card-radius)',
62
- border: borderMap[elevation],
63
- boxShadow: shadowMap[elevation],
64
- cursor: onClick ? 'pointer' : 'default',
65
- overflow: 'hidden',
66
- backgroundColor: 'var(--vetc-card-bg)',
67
- fontFamily: 'var(--vetc-font-family)',
68
- transition: `box-shadow var(--vetc-transition-fast)`,
69
- ...style,
70
- }}
71
- >
72
- {children}
73
- </AntCard>
74
- );
75
- }
76
-
77
- export default Card;