@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,137 +0,0 @@
1
- /**
2
- * VETC TabBar (Bottom Navigation)
3
- * Figma: h=56px, label=12px/semibold, icon=24px
4
- * variant: neutral (gray-90 active) | brand (green-40 active)
5
- */
6
- import React from 'react';
7
-
8
- export interface TabBarItem {
9
- key: string;
10
- label: string;
11
- icon: React.ReactNode;
12
- activeIcon?: React.ReactNode;
13
- badge?: number | string;
14
- disabled?: boolean;
15
- }
16
-
17
- export type TabBarVariant = 'neutral' | 'brand';
18
-
19
- export interface TabBarProps {
20
- items: TabBarItem[];
21
- activeKey?: string;
22
- onChange?: (key: string) => void;
23
- variant?: TabBarVariant;
24
- backgroundColor?: string;
25
- divider?: boolean;
26
- className?: string;
27
- style?: React.CSSProperties;
28
- id?: string;
29
- }
30
-
31
- export function TabBar({
32
- items,
33
- activeKey,
34
- onChange,
35
- variant = 'brand',
36
- backgroundColor,
37
- divider = true,
38
- className = '',
39
- style,
40
- id,
41
- }: TabBarProps) {
42
- const activeColor = variant === 'brand'
43
- ? 'var(--vetc-tab-color-active)'
44
- : 'var(--vetc-color-text-primary)';
45
-
46
- return (
47
- <nav
48
- id={id}
49
- className={`vetc-tab-bar ${className}`}
50
- style={{
51
- display: 'flex',
52
- alignItems: 'stretch',
53
- height: 'var(--vetc-tab-height)',
54
- backgroundColor: backgroundColor ?? 'var(--vetc-tab-bg)',
55
- borderTop: divider ? `1px solid var(--vetc-tab-border)` : 'none',
56
- fontFamily: 'var(--vetc-font-family)',
57
- ...style,
58
- }}
59
- >
60
- {items.map((item) => {
61
- const isActive = item.key === activeKey;
62
- const color = item.disabled
63
- ? 'var(--vetc-tab-color-disabled)'
64
- : isActive
65
- ? activeColor
66
- : 'var(--vetc-tab-color-inactive)';
67
-
68
- return (
69
- <button
70
- key={item.key}
71
- disabled={item.disabled}
72
- onClick={() => !item.disabled && onChange?.(item.key)}
73
- aria-current={isActive ? 'page' : undefined}
74
- aria-label={item.label}
75
- className={`vetc-tab-bar__item${isActive ? ' vetc-tab-bar__item--active' : ''}`}
76
- style={{
77
- flex: 1,
78
- display: 'flex',
79
- flexDirection: 'column',
80
- alignItems: 'center',
81
- justifyContent: 'center',
82
- gap: 'var(--vetc-space-2)',
83
- background: 'none',
84
- border: 'none',
85
- cursor: item.disabled ? 'not-allowed' : 'pointer',
86
- padding: `var(--vetc-space-6) 0`,
87
- position: 'relative',
88
- opacity: item.disabled ? 0.5 : 1,
89
- transition: `color var(--vetc-transition-fast)`,
90
- color,
91
- }}
92
- >
93
- {/* Icon + Badge */}
94
- <div style={{ position: 'relative', display: 'inline-flex' }}>
95
- <span style={{ color, display: 'flex', fontSize: '24px', lineHeight: 1 }}>
96
- {isActive && item.activeIcon ? item.activeIcon : item.icon}
97
- </span>
98
- {item.badge !== undefined && (
99
- <span style={{
100
- position: 'absolute',
101
- top: 'calc(-1 * var(--vetc-space-4))',
102
- right: 'calc(-1 * var(--vetc-space-8))',
103
- minWidth: 'var(--vetc-tab-badge-size)',
104
- height: 'var(--vetc-tab-badge-size)',
105
- backgroundColor: 'var(--vetc-tab-badge-bg)',
106
- borderRadius: 'var(--vetc-radius-pill)',
107
- fontSize: 'var(--vetc-tab-badge-font-size)',
108
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
109
- color: 'var(--vetc-tab-badge-text)',
110
- display: 'flex',
111
- alignItems: 'center',
112
- justifyContent: 'center',
113
- padding: `0 var(--vetc-space-4)`,
114
- lineHeight: 1,
115
- }}>
116
- {item.badge}
117
- </span>
118
- )}
119
- </div>
120
-
121
- {/* Label */}
122
- <span style={{
123
- fontSize: 'var(--vetc-tab-label-size)',
124
- fontWeight: 'var(--vetc-tab-label-weight)' as any,
125
- color,
126
- lineHeight: 'var(--vetc-line-height-relaxed)',
127
- }}>
128
- {item.label}
129
- </span>
130
- </button>
131
- );
132
- })}
133
- </nav>
134
- );
135
- }
136
-
137
- export default TabBar;
@@ -1,109 +0,0 @@
1
- /**
2
- * VETC Textarea — tokenized
3
- */
4
- import React from 'react';
5
- import { Input as AntInput } from 'antd';
6
-
7
- const { TextArea: AntTextArea } = AntInput;
8
-
9
- function FieldLabel({ htmlFor, required, disabled, children }: {
10
- htmlFor?: string; required?: boolean; disabled?: boolean; children: React.ReactNode;
11
- }) {
12
- return (
13
- <label htmlFor={htmlFor} style={{
14
- fontFamily: 'var(--vetc-font-family)',
15
- fontSize: 'var(--vetc-input-label-font-size)',
16
- fontWeight: 'var(--vetc-input-label-font-weight)' as any,
17
- lineHeight: 'var(--vetc-line-height-relaxed)',
18
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-input-label-color)',
19
- display: 'block',
20
- }}>
21
- {children}
22
- {required && <span style={{ color: 'var(--vetc-color-negative)', marginLeft: 'var(--vetc-space-2)' }}>*</span>}
23
- </label>
24
- );
25
- }
26
-
27
- export interface TextareaProps {
28
- label?: string;
29
- placeholder?: string;
30
- value?: string;
31
- defaultValue?: string;
32
- helperText?: string;
33
- error?: string;
34
- disabled?: boolean;
35
- readOnly?: boolean;
36
- maxLength?: number;
37
- showCount?: boolean;
38
- rows?: number;
39
- autoSize?: boolean | { minRows?: number; maxRows?: number };
40
- onChange?: (value: string) => void;
41
- onFocus?: React.FocusEventHandler<HTMLTextAreaElement>;
42
- onBlur?: React.FocusEventHandler<HTMLTextAreaElement>;
43
- id?: string;
44
- name?: string;
45
- className?: string;
46
- style?: React.CSSProperties;
47
- required?: boolean;
48
- }
49
-
50
- export function Textarea({
51
- label, placeholder, value, defaultValue,
52
- helperText, error, disabled = false, readOnly = false,
53
- maxLength, showCount = false, rows = 4, autoSize,
54
- onChange, onFocus, onBlur,
55
- id, name, className = '', style, required = false,
56
- }: TextareaProps) {
57
- const hasError = !!error;
58
- const helperMsg = error ?? helperText;
59
- const helperColor = hasError
60
- ? 'var(--vetc-input-helper-color-error)'
61
- : 'var(--vetc-input-helper-color)';
62
-
63
- return (
64
- <div
65
- className={`vetc-textarea-wrapper ${className}`}
66
- style={{ display: 'flex', flexDirection: 'column', gap: 'var(--vetc-input-gap)', ...style }}
67
- >
68
- {label && <FieldLabel htmlFor={id} required={required} disabled={disabled}>{label}</FieldLabel>}
69
-
70
- <AntTextArea
71
- id={id}
72
- name={name}
73
- value={value}
74
- defaultValue={defaultValue}
75
- placeholder={placeholder}
76
- disabled={disabled}
77
- readOnly={readOnly}
78
- maxLength={maxLength}
79
- showCount={showCount}
80
- rows={rows}
81
- autoSize={autoSize}
82
- status={hasError ? 'error' : undefined}
83
- onChange={(e) => onChange?.(e.target.value)}
84
- onFocus={onFocus}
85
- onBlur={onBlur}
86
- style={{
87
- borderRadius: 'var(--vetc-input-radius)',
88
- padding: 'var(--vetc-input-padding-x)',
89
- fontSize: 'var(--vetc-input-font-size)',
90
- fontFamily: 'var(--vetc-font-family)',
91
- resize: 'vertical',
92
- }}
93
- />
94
-
95
- {helperMsg && (
96
- <span style={{
97
- fontFamily: 'var(--vetc-font-family)',
98
- fontSize: 'var(--vetc-input-helper-font-size)',
99
- lineHeight: 'var(--vetc-line-height-relaxed)',
100
- color: helperColor,
101
- }}>
102
- {helperMsg}
103
- </span>
104
- )}
105
- </div>
106
- );
107
- }
108
-
109
- export default Textarea;
@@ -1,98 +0,0 @@
1
- /**
2
- * VETC Toast — tokenized
3
- * Figma: Toast page (7 variants)
4
- */
5
- import React from 'react';
6
- import { notification, message } from 'antd';
7
-
8
- export type ToastType = 'default' | 'success' | 'error' | 'warning' | 'info';
9
-
10
- export interface ToastOptions {
11
- message: string;
12
- description?: string;
13
- type?: ToastType;
14
- placement?: 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight';
15
- /** Duration in seconds (0 = persistent) */
16
- duration?: number;
17
- icon?: React.ReactNode;
18
- onClose?: () => void;
19
- }
20
-
21
- /** Shared notification style — references CSS vars */
22
- const toastStyle: React.CSSProperties = {
23
- borderRadius: 'var(--vetc-toast-radius)',
24
- fontFamily: 'var(--vetc-font-family)',
25
- };
26
-
27
- /**
28
- * Static programmatic toast API
29
- * Usage: toast.success('Message')
30
- */
31
- export const toast = {
32
- show(opts: ToastOptions) {
33
- const {
34
- type = 'default', message: msg, description,
35
- duration = 3, placement = 'top', icon, onClose,
36
- } = opts;
37
-
38
- if (type === 'default') {
39
- message.open({ type: 'info', content: msg, duration, onClose });
40
- return;
41
- }
42
-
43
- notification[type]({
44
- message: msg,
45
- description,
46
- duration,
47
- placement,
48
- icon,
49
- onClose,
50
- style: toastStyle,
51
- });
52
- },
53
- success: (msg: string, description?: string) =>
54
- toast.show({ message: msg, description, type: 'success' }),
55
- error: (msg: string, description?: string) =>
56
- toast.show({ message: msg, description, type: 'error' }),
57
- warning: (msg: string, description?: string) =>
58
- toast.show({ message: msg, description, type: 'warning' }),
59
- info: (msg: string, description?: string) =>
60
- toast.show({ message: msg, description, type: 'info' }),
61
- };
62
-
63
- /**
64
- * Hook-based toast — requires VETCProvider (antd App wrapper)
65
- */
66
- export function useToast() {
67
- const [api, contextHolder] = notification.useNotification();
68
-
69
- const show = (opts: ToastOptions) => {
70
- const {
71
- type = 'info', message: msg, description,
72
- duration = 3, placement = 'top', icon, onClose,
73
- } = opts;
74
-
75
- api[type === 'default' ? 'info' : type]({
76
- message: msg,
77
- description,
78
- duration,
79
- placement,
80
- icon,
81
- onClose,
82
- style: toastStyle,
83
- });
84
- };
85
-
86
- return {
87
- contextHolder,
88
- toast: {
89
- show,
90
- success: (msg: string, description?: string) => show({ message: msg, description, type: 'success' }),
91
- error: (msg: string, description?: string) => show({ message: msg, description, type: 'error' }),
92
- warning: (msg: string, description?: string) => show({ message: msg, description, type: 'warning' }),
93
- info: (msg: string, description?: string) => show({ message: msg, description, type: 'info' }),
94
- },
95
- };
96
- }
97
-
98
- export default toast;
@@ -1,201 +0,0 @@
1
- /**
2
- * VETC Typography Components
3
- * All style values reference CSS custom properties from tokens.css
4
- */
5
- import React from 'react';
6
-
7
- // ── Typography style map — dùng CSS vars thay vì hardcode ──────────────────
8
- const styles: Record<string, React.CSSProperties> = {
9
- display4xl: {
10
- fontSize: 'var(--vetc-font-size-4xl)',
11
- fontWeight: 'var(--vetc-font-weight-bold)' as any,
12
- lineHeight: 'var(--vetc-line-height-tight)',
13
- },
14
- display3xl: {
15
- fontSize: 'var(--vetc-font-size-3xl)',
16
- fontWeight: 'var(--vetc-font-weight-bold)' as any,
17
- lineHeight: 'var(--vetc-line-height-tight)',
18
- },
19
- display2xl: {
20
- fontSize: 'var(--vetc-font-size-2xl)',
21
- fontWeight: 'var(--vetc-font-weight-bold)' as any,
22
- lineHeight: 'var(--vetc-line-height-tight)',
23
- },
24
- headlineXl: {
25
- fontSize: 'var(--vetc-font-size-xl)',
26
- fontWeight: 'var(--vetc-font-weight-bold)' as any,
27
- lineHeight: 'var(--vetc-line-height-normal)',
28
- },
29
- headlineLg: {
30
- fontSize: 'var(--vetc-font-size-lg)',
31
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
32
- lineHeight: 'var(--vetc-line-height-normal)',
33
- },
34
- titleBase: {
35
- fontSize: 'var(--vetc-font-size-base)',
36
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
37
- lineHeight: 'var(--vetc-line-height-relaxed)',
38
- letterSpacing: 'var(--vetc-letter-spacing-sm)',
39
- },
40
- titleSm: {
41
- fontSize: 'var(--vetc-font-size-sm)',
42
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
43
- lineHeight: 'var(--vetc-line-height-relaxed)',
44
- letterSpacing: 'var(--vetc-letter-spacing-sm)',
45
- },
46
- labelBase: {
47
- fontSize: 'var(--vetc-font-size-base)',
48
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
49
- lineHeight: 'var(--vetc-line-height-relaxed)',
50
- },
51
- labelSm: {
52
- fontSize: 'var(--vetc-font-size-sm)',
53
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
54
- lineHeight: 'var(--vetc-line-height-relaxed)',
55
- letterSpacing: 'var(--vetc-letter-spacing-lg)',
56
- },
57
- labelXs: {
58
- fontSize: 'var(--vetc-font-size-xs)',
59
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
60
- lineHeight: 'var(--vetc-line-height-relaxed)',
61
- letterSpacing: 'var(--vetc-letter-spacing-lg)',
62
- },
63
- labelXxs: {
64
- fontSize: 'var(--vetc-font-size-2xs)',
65
- fontWeight: 'var(--vetc-font-weight-semibold)' as any,
66
- lineHeight: 'var(--vetc-line-height-relaxed)',
67
- letterSpacing: 'var(--vetc-letter-spacing-lg)',
68
- },
69
- bodyBase: {
70
- fontSize: 'var(--vetc-font-size-base)',
71
- fontWeight: 'var(--vetc-font-weight-regular)' as any,
72
- lineHeight: 'var(--vetc-line-height-relaxed)',
73
- letterSpacing: 'var(--vetc-letter-spacing-sm)',
74
- },
75
- bodySm: {
76
- fontSize: 'var(--vetc-font-size-sm)',
77
- fontWeight: 'var(--vetc-font-weight-regular)' as any,
78
- lineHeight: 'var(--vetc-line-height-relaxed)',
79
- letterSpacing: 'var(--vetc-letter-spacing-md)',
80
- },
81
- bodyXs: {
82
- fontSize: 'var(--vetc-font-size-xs)',
83
- fontWeight: 'var(--vetc-font-weight-regular)' as any,
84
- lineHeight: 'var(--vetc-line-height-relaxed)',
85
- letterSpacing: 'var(--vetc-letter-spacing-lg)',
86
- },
87
- body2xs: {
88
- fontSize: 'var(--vetc-font-size-2xs)',
89
- fontWeight: 'var(--vetc-font-weight-regular)' as any,
90
- lineHeight: 'var(--vetc-line-height-relaxed)',
91
- letterSpacing: 'var(--vetc-letter-spacing-lg)',
92
- },
93
- };
94
-
95
- export type TypographyVariant = keyof typeof styles;
96
- export type TextColor =
97
- | 'primary' | 'secondary' | 'tertiary' | 'disabled'
98
- | 'brand' | 'error' | 'warning' | 'success' | 'inherit';
99
-
100
- const colorMap: Record<TextColor, string> = {
101
- primary: 'var(--vetc-color-text-primary)',
102
- secondary: 'var(--vetc-color-text-secondary)',
103
- tertiary: 'var(--vetc-color-text-tertiary)',
104
- disabled: 'var(--vetc-color-text-disabled)',
105
- brand: 'var(--vetc-color-brand)',
106
- error: 'var(--vetc-color-text-error)',
107
- warning: 'var(--vetc-color-warning)',
108
- success: 'var(--vetc-color-positive)',
109
- inherit: 'inherit',
110
- };
111
-
112
- export interface TypographyProps {
113
- /** Typography style variant */
114
- variant?: TypographyVariant;
115
- /** Text color */
116
- color?: TextColor;
117
- /** Strike-through */
118
- strikethrough?: boolean;
119
- /** Underline */
120
- underline?: boolean;
121
- /** Single-line truncate */
122
- truncate?: boolean;
123
- /** Clamp to N lines */
124
- lines?: number;
125
- /** Render tag */
126
- as?: keyof React.JSX.IntrinsicElements;
127
- children?: React.ReactNode;
128
- className?: string;
129
- style?: React.CSSProperties;
130
- id?: string;
131
- }
132
-
133
- export function Typography({
134
- variant = 'bodyBase',
135
- color = 'primary',
136
- strikethrough = false,
137
- underline = false,
138
- truncate = false,
139
- lines,
140
- as: Tag = 'span',
141
- children,
142
- className = '',
143
- style,
144
- id,
145
- }: TypographyProps) {
146
- const variantStyle = styles[variant] ?? styles.bodyBase;
147
- const colorValue = colorMap[color];
148
-
149
- const combinedStyle: React.CSSProperties = {
150
- fontFamily: 'var(--vetc-font-family)',
151
- margin: 0,
152
- padding: 0,
153
- ...variantStyle,
154
- color: colorValue,
155
- textDecoration: strikethrough ? 'line-through' : underline ? 'underline' : 'none',
156
- ...(truncate && !lines
157
- ? { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }
158
- : {}),
159
- ...(lines
160
- ? {
161
- display: '-webkit-box',
162
- WebkitLineClamp: lines,
163
- WebkitBoxOrient: 'vertical' as const,
164
- overflow: 'hidden',
165
- }
166
- : {}),
167
- ...style,
168
- };
169
-
170
- return (
171
- <Tag id={id} className={`vetc-text vetc-text--${variant} ${className}`} style={combinedStyle}>
172
- {children}
173
- </Tag>
174
- );
175
- }
176
-
177
- // ── Convenience shorthands ──────────────────────────────────────────────────
178
-
179
- export function Display({ level = '2xl', ...props }: Omit<TypographyProps, 'variant'> & { level?: '4xl' | '3xl' | '2xl' }) {
180
- return <Typography {...props} variant={`display${level}` as TypographyVariant} as={props.as ?? 'h1'} />;
181
- }
182
-
183
- export function Headline({ level = 'xl', ...props }: Omit<TypographyProps, 'variant'> & { level?: 'xl' | 'lg' }) {
184
- return <Typography {...props} variant={level === 'xl' ? 'headlineXl' : 'headlineLg'} as={props.as ?? 'h2'} />;
185
- }
186
-
187
- export function Title({ size = 'base', ...props }: Omit<TypographyProps, 'variant'> & { size?: 'base' | 'sm' }) {
188
- return <Typography {...props} variant={size === 'base' ? 'titleBase' : 'titleSm'} as={props.as ?? 'h3'} />;
189
- }
190
-
191
- export function Label({ size = 'base', ...props }: Omit<TypographyProps, 'variant'> & { size?: 'base' | 'sm' | 'xs' | 'xxs' }) {
192
- const variantMap = { base: 'labelBase', sm: 'labelSm', xs: 'labelXs', xxs: 'labelXxs' } as const;
193
- return <Typography {...props} variant={variantMap[size]} as={props.as ?? 'span'} />;
194
- }
195
-
196
- export function Body({ size = 'base', ...props }: Omit<TypographyProps, 'variant'> & { size?: 'base' | 'sm' | 'xs' | '2xs' }) {
197
- const variantMap = { base: 'bodyBase', sm: 'bodySm', xs: 'bodyXs', '2xs': 'body2xs' } as const;
198
- return <Typography {...props} variant={variantMap[size]} as={props.as ?? 'p'} />;
199
- }
200
-
201
- export default Typography;
@@ -1,35 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- /**
4
- * useAppPause
5
- *
6
- * Trigger khi Native App đi vào background.
7
- */
8
- export function useAppPause(callback) {
9
- const savedCallback = useRef(callback);
10
-
11
- // Luôn giữ callback mới nhất
12
- useEffect(() => {
13
- savedCallback.current = callback;
14
- }, [callback]);
15
-
16
- useEffect(() => {
17
- if (typeof window === "undefined" || !window.MiniApp) return;
18
-
19
- const handler = () => {
20
- try {
21
- if (savedCallback.current) {
22
- savedCallback.current();
23
- }
24
- } catch (err) {
25
- console.error("[useAppPause error]", err);
26
- }
27
- };
28
-
29
- window.MiniApp.on("appPause", handler);
30
-
31
- return () => {
32
- window.MiniApp.off("appPause", handler);
33
- };
34
- }, []);
35
- }
@@ -1,33 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- /**
4
- * useAppPause
5
- *
6
- * Trigger khi Native App đi vào background.
7
- */
8
- export function useAppPause(callback: () => void) {
9
- const savedCallback = useRef(callback);
10
-
11
- // giữ callback luôn mới nhất
12
- useEffect(() => {
13
- savedCallback.current = callback;
14
- }, [callback]);
15
-
16
- useEffect(() => {
17
- if (typeof window === "undefined" || !window.MiniApp) return;
18
-
19
- const handler = () => {
20
- try {
21
- savedCallback.current?.();
22
- } catch (err) {
23
- console.error("[useAppPause error]", err);
24
- }
25
- };
26
-
27
- window.MiniApp.on("appPause", handler);
28
-
29
- return () => {
30
- window.MiniApp.off("appPause", handler);
31
- };
32
- }, []);
33
- }
@@ -1,37 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- /**
4
- * useAppResume
5
- *
6
- * Trigger khi Native App quay lại foreground.
7
- */
8
- export function useAppResume(callback) {
9
- const savedCallback = useRef(callback);
10
-
11
- // Cập nhật callback mới nhất
12
- useEffect(() => {
13
- savedCallback.current = callback;
14
- }, [callback]);
15
-
16
- useEffect(() => {
17
- console.warn("MiniApp: useAppResume");
18
-
19
- if (typeof window === "undefined" || !window.MiniApp) return;
20
-
21
- const handler = () => {
22
- try {
23
- if (savedCallback.current) {
24
- savedCallback.current();
25
- }
26
- } catch (err) {
27
- console.error("[useAppResume error]", err);
28
- }
29
- };
30
-
31
- window.MiniApp.on("appResume", handler);
32
-
33
- return () => {
34
- window.MiniApp.off("appResume", handler);
35
- };
36
- }, []);
37
- }
@@ -1,32 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
-
3
- /**
4
- * useAppResume
5
- *
6
- * Trigger khi Native App quay lại foreground.
7
- */
8
- export function useAppResume(callback: () => void) {
9
- const savedCallback = useRef(callback);
10
-
11
- useEffect(() => {
12
- savedCallback.current = callback;
13
- }, [callback]);
14
-
15
- useEffect(() => {
16
- if (typeof window === "undefined" || !window.MiniApp) return;
17
-
18
- const handler = () => {
19
- try {
20
- savedCallback.current?.();
21
- } catch (err) {
22
- console.error("[useAppResume error]", err);
23
- }
24
- };
25
-
26
- window.MiniApp.on("appResume", handler);
27
-
28
- return () => {
29
- window.MiniApp.off("appResume", handler);
30
- };
31
- }, []);
32
- }