@vetc-miniapp/ui-react 0.0.23 → 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 (146) hide show
  1. package/README.md +375 -56
  2. package/dist/bridge.d.ts +11 -0
  3. package/dist/bridge.js +20 -0
  4. package/dist/components/app.d.ts +6 -0
  5. package/dist/components/app.js +34 -0
  6. package/dist/components/avatar/Avatar.d.ts +21 -0
  7. package/dist/components/avatar/Avatar.js +33 -0
  8. package/dist/components/avatar/index.d.ts +2 -0
  9. package/dist/components/avatar/index.js +1 -0
  10. package/dist/components/bottom-sheet/BottomSheet.d.ts +19 -0
  11. package/dist/components/bottom-sheet/BottomSheet.js +70 -0
  12. package/dist/components/bottom-sheet/index.d.ts +2 -0
  13. package/dist/components/bottom-sheet/index.js +1 -0
  14. package/dist/components/button/Button.d.ts +32 -0
  15. package/dist/components/button/Button.js +165 -0
  16. package/dist/components/button/index.d.ts +2 -0
  17. package/dist/components/button/index.js +1 -0
  18. package/dist/components/button-group/ButtonGroup.d.ts +28 -0
  19. package/dist/components/button-group/ButtonGroup.js +21 -0
  20. package/dist/components/button-group/index.d.ts +2 -0
  21. package/dist/components/button-group/index.js +1 -0
  22. package/dist/components/card/Card.d.ts +18 -0
  23. package/dist/components/card/Card.js +35 -0
  24. package/dist/components/card/index.d.ts +2 -0
  25. package/dist/components/card/index.js +1 -0
  26. package/dist/components/checkbox/Checkbox.d.ts +41 -0
  27. package/dist/components/checkbox/Checkbox.js +94 -0
  28. package/dist/components/checkbox/index.d.ts +2 -0
  29. package/dist/components/checkbox/index.js +1 -0
  30. package/dist/components/chip/Chip.d.ts +24 -0
  31. package/dist/components/chip/Chip.js +83 -0
  32. package/dist/components/chip/index.d.ts +2 -0
  33. package/dist/components/chip/index.js +1 -0
  34. package/dist/components/dialog/Dialog.d.ts +19 -0
  35. package/dist/components/dialog/Dialog.js +51 -0
  36. package/dist/components/dialog/index.d.ts +2 -0
  37. package/dist/components/dialog/index.js +1 -0
  38. package/dist/components/divider/Divider.d.ts +16 -0
  39. package/dist/components/divider/Divider.js +18 -0
  40. package/dist/components/divider/index.d.ts +2 -0
  41. package/dist/components/divider/index.js +1 -0
  42. package/dist/components/input/Input.d.ts +40 -0
  43. package/dist/components/input/Input.js +51 -0
  44. package/dist/components/input/index.d.ts +2 -0
  45. package/dist/components/input/index.js +1 -0
  46. package/dist/components/list/List.d.ts +31 -0
  47. package/dist/components/list/List.js +72 -0
  48. package/dist/components/list/index.d.ts +2 -0
  49. package/dist/components/list/index.js +1 -0
  50. package/dist/components/loading/Loading.d.ts +28 -0
  51. package/dist/components/loading/Loading.js +33 -0
  52. package/dist/components/loading/index.d.ts +2 -0
  53. package/dist/components/loading/index.js +1 -0
  54. package/dist/components/modal/Modal.d.ts +38 -0
  55. package/dist/components/modal/Modal.js +50 -0
  56. package/dist/components/modal/index.d.ts +2 -0
  57. package/dist/components/modal/index.js +1 -0
  58. package/dist/components/navigation-bar/NavigationBar.d.ts +44 -0
  59. package/dist/components/navigation-bar/NavigationBar.js +70 -0
  60. package/dist/components/navigation-bar/index.d.ts +2 -0
  61. package/dist/components/navigation-bar/index.js +1 -0
  62. package/dist/components/radio/Radio.d.ts +40 -0
  63. package/dist/components/radio/Radio.js +88 -0
  64. package/dist/components/radio/index.d.ts +2 -0
  65. package/dist/components/radio/index.js +1 -0
  66. package/dist/components/select/Select.d.ts +29 -0
  67. package/dist/components/select/Select.js +30 -0
  68. package/dist/components/select/index.d.ts +2 -0
  69. package/dist/components/select/index.js +1 -0
  70. package/dist/components/switch/Switch.d.ts +23 -0
  71. package/dist/components/switch/Switch.js +81 -0
  72. package/dist/components/switch/index.d.ts +2 -0
  73. package/dist/components/switch/index.js +1 -0
  74. package/dist/components/tab-bar/TabBar.d.ts +28 -0
  75. package/dist/components/tab-bar/TabBar.js +60 -0
  76. package/dist/components/tab-bar/index.d.ts +2 -0
  77. package/dist/components/tab-bar/index.js +1 -0
  78. package/dist/components/textarea/Textarea.d.ts +31 -0
  79. package/dist/components/textarea/Textarea.js +33 -0
  80. package/dist/components/textarea/index.d.ts +2 -0
  81. package/dist/components/textarea/index.js +1 -0
  82. package/dist/components/toast/Toast.d.ts +41 -0
  83. package/dist/components/toast/Toast.js +61 -0
  84. package/dist/components/toast/index.d.ts +2 -0
  85. package/dist/components/toast/index.js +1 -0
  86. package/dist/components/typography/Typography.d.ts +45 -0
  87. package/dist/components/typography/Typography.js +143 -0
  88. package/dist/components/typography/index.d.ts +2 -0
  89. package/dist/components/typography/index.js +1 -0
  90. package/dist/hooks/use-app-pause.d.ts +6 -0
  91. package/dist/hooks/use-app-pause.js +29 -0
  92. package/dist/hooks/use-app-resume.d.ts +6 -0
  93. package/dist/hooks/use-app-resume.js +28 -0
  94. package/{src/ui-react/hooks/use-app-state.ts → dist/hooks/use-app-state.d.ts} +0 -1
  95. package/dist/hooks/use-app-state.js +1 -0
  96. package/dist/hooks/use-did-hide.d.ts +6 -0
  97. package/dist/hooks/use-did-hide.js +21 -0
  98. package/dist/hooks/use-did-show.d.ts +6 -0
  99. package/dist/hooks/use-did-show.js +21 -0
  100. package/dist/hooks/use-listener-scan-qr.d.ts +21 -0
  101. package/dist/hooks/use-listener-scan-qr.js +29 -0
  102. package/dist/hooks/use-navigate.d.ts +8 -0
  103. package/dist/hooks/use-navigate.js +23 -0
  104. package/dist/hooks/use-tap-app-bar.d.ts +6 -0
  105. package/dist/hooks/use-tap-app-bar.js +21 -0
  106. package/dist/index.d.ts +56 -0
  107. package/dist/index.js +41 -0
  108. package/dist/styles/VETCProvider.d.ts +114 -0
  109. package/dist/styles/VETCProvider.js +124 -0
  110. package/dist/styles/tokens.css +448 -0
  111. package/dist/tokens/colors.d.ts +127 -0
  112. package/dist/tokens/colors.js +75 -0
  113. package/dist/tokens/index.d.ts +3 -0
  114. package/dist/tokens/index.js +3 -0
  115. package/dist/tokens/spacing.d.ts +56 -0
  116. package/dist/tokens/spacing.js +56 -0
  117. package/dist/tokens/typography.d.ts +121 -0
  118. package/dist/tokens/typography.js +57 -0
  119. package/dist/types/app.d.ts +21 -0
  120. package/dist/types/app.js +1 -0
  121. package/package.json +26 -8
  122. package/src/dist/ui-react/index.js +0 -2
  123. package/src/dist/ui-react/index.js.LICENSE.txt +0 -11
  124. package/src/ui-react/bridge.js +0 -36
  125. package/src/ui-react/bridge.ts +0 -48
  126. package/src/ui-react/components/app.d.ts +0 -7
  127. package/src/ui-react/components/app.jsx +0 -80
  128. package/src/ui-react/components/app.tsx +0 -42
  129. package/src/ui-react/components/app1.js +0 -101
  130. package/src/ui-react/hooks/use-app-pause.js +0 -35
  131. package/src/ui-react/hooks/use-app-pause.ts +0 -33
  132. package/src/ui-react/hooks/use-app-resume.js +0 -37
  133. package/src/ui-react/hooks/use-app-resume.ts +0 -32
  134. package/src/ui-react/hooks/use-app-state.js +0 -35
  135. package/src/ui-react/hooks/use-did-hide.js +0 -25
  136. package/src/ui-react/hooks/use-did-hide.ts +0 -34
  137. package/src/ui-react/hooks/use-did-show.js +0 -26
  138. package/src/ui-react/hooks/use-did-show.ts +0 -34
  139. package/src/ui-react/hooks/use-listener-scan-qr.js +0 -33
  140. package/src/ui-react/hooks/use-listener-scan-qr.ts +0 -52
  141. package/src/ui-react/hooks/use-navigate.js +0 -15
  142. package/src/ui-react/hooks/use-navigate.ts +0 -41
  143. package/src/ui-react/index.js +0 -8
  144. package/src/ui-react/index.ts +0 -9
  145. package/src/ui-react/types/app.js +0 -30
  146. package/src/ui-react/types/app.ts +0 -32
package/README.md CHANGED
@@ -1,102 +1,421 @@
1
- # MiniApp JS SDK
1
+ # @vetc-miniapp/ui-react
2
2
 
3
- SDK giúp Mini App giao tiếp với Super App host thông qua JS Bridge.
3
+ Bộ UI component React cho VETC MiniApp Platform, được xây dựng theo design system **Tasco DLS Mini App** từ Figma, sử dụng Ant Design v6 làm base và override hoàn toàn theo VETC design token.
4
+
5
+ ---
4
6
 
5
7
  ## Cài đặt
6
8
 
7
9
  ```bash
10
+ npm install antd @ant-design/icons
8
11
  npm install @vetc-miniapp/ui-react
9
12
  ```
10
- ## Sử dụng
11
- # useNavigate – Điều hướng Mini App qua Native (Flutter)
12
13
 
13
- `useNavigate` hook dùng trong **mini app React** để điều hướng **thông qua host app (Flutter)**, thay vì tự điều hướng nội bộ bằng `react-router-dom`.
14
+ ## Sử dụng bản
14
15
 
15
- Hook này giúp:
16
+ ```tsx
17
+ // 1. Import CSS tokens ở app root (1 lần duy nhất)
18
+ import '@vetc-miniapp/ui-react/src/ui-react/styles/tokens.css';
16
19
 
17
- - Mini app **không phụ thuộc URL thực**
18
- - Toàn bộ navigation được **native kiểm soát**
19
- - **Đồng bộ header, back stack, animation** giữa mini app & app chính
20
+ // 2. Wrap app với VETCProvider
21
+ import { VETCProvider } from '@vetc-miniapp/ui-react';
22
+
23
+ function App() {
24
+ return (
25
+ <VETCProvider>
26
+ <YourApp />
27
+ </VETCProvider>
28
+ );
29
+ }
30
+ ```
20
31
 
21
32
  ---
22
33
 
23
- ## 📦 Import
34
+ ## Components
24
35
 
25
- ```ts
26
- import { useNavigate } from '@vetc-miniapp/ui-react';
36
+ ### Button
27
37
 
28
- const navigate = useNavigate();
38
+ ```tsx
39
+ import { Button } from '@vetc-miniapp/ui-react';
29
40
 
30
- navigate(
31
- path: string,
32
- title: string,
33
- params?: Record<string, any>,
34
- options?: {
35
- replace?: boolean;
36
- popTo?: string;
37
- }
38
- ): void
41
+ // Primary (default) — brand green
42
+ <Button variant="primary" size="lg">Xác nhận</Button>
39
43
 
44
+ // Secondary — outlined
45
+ <Button variant="secondary" size="lg">Hủy</Button>
46
+
47
+ // Tertiary — ghost/text
48
+ <Button variant="tertiary">Xem thêm</Button>
49
+
50
+ // Danger
51
+ <Button variant="danger">Xóa</Button>
52
+
53
+ // Pill shape
54
+ <Button variant="primary" shape="pill">Tìm kiếm</Button>
55
+
56
+ // Small size (32px)
57
+ <Button variant="primary" size="sm">OK</Button>
58
+
59
+ // Loading & disabled
60
+ <Button loading>Đang xử lý...</Button>
61
+ <Button disabled>Không khả dụng</Button>
40
62
  ```
41
63
 
42
- ## 🚀 Cách sử dụng bản
43
- # Điều hướng sang page khác
44
- ```ts
45
- const navigate = useNavigate();
64
+ **Props:** `variant` (primary|secondary|tertiary|danger), `size` (lg|sm), `shape` (rounded|pill), `block`, `disabled`, `loading`, `icon`, `iconRight`
65
+
66
+ ---
67
+
68
+ ### Typography
69
+
70
+ ```tsx
71
+ import { Display, Headline, Title, Label, Body, Typography } from '@vetc-miniapp/ui-react';
72
+
73
+ <Display level="2xl">Tiêu đề lớn</Display>
74
+ <Headline level="xl">Headline</Headline>
75
+ <Title size="base">Tiêu đề phần</Title>
76
+ <Label size="sm" color="secondary">Nhãn</Label>
77
+ <Body size="base" color="primary">Nội dung văn bản</Body>
78
+
79
+ // Màu sắc: primary | secondary | disabled | brand | error | warning | success | inherit
80
+ <Body color="error">Lỗi rồi!</Body>
81
+
82
+ // Clamp lines
83
+ <Body lines={2}>Nội dung dài sẽ bị cắt sau 2 dòng...</Body>
84
+ ```
85
+
86
+ ---
87
+
88
+ ### Input / TextField
89
+
90
+ ```tsx
91
+ import { Input, PasswordInput } from '@vetc-miniapp/ui-react';
92
+
93
+ // Default
94
+ <Input label="Số điện thoại" placeholder="0901 234 567" />
95
+
96
+ // Required + error
97
+ <Input
98
+ label="Email"
99
+ required
100
+ error="Email không hợp lệ"
101
+ value={email}
102
+ onChange={setEmail}
103
+ />
104
+
105
+ // With icons
106
+ <Input prefix={<PhoneIcon />} suffix={<ClearIcon />} allowClear />
107
+
108
+ // Password
109
+ <PasswordInput label="Mật khẩu" placeholder="Nhập mật khẩu" />
110
+ ```
111
+
112
+ ---
113
+
114
+ ### Textarea
115
+
116
+ ```tsx
117
+ import { Textarea } from '@vetc-miniapp/ui-react';
118
+
119
+ <Textarea
120
+ label="Ghi chú"
121
+ placeholder="Nhập nội dung..."
122
+ rows={4}
123
+ maxLength={500}
124
+ showCount
125
+ />
126
+ ```
127
+
128
+ ---
129
+
130
+ ### Select
131
+
132
+ ```tsx
133
+ import { Select } from '@vetc-miniapp/ui-react';
134
+
135
+ <Select
136
+ label="Tỉnh/Thành phố"
137
+ placeholder="Chọn tỉnh thành"
138
+ options={[
139
+ { label: 'Hà Nội', value: 'HN' },
140
+ { label: 'TP.HCM', value: 'HCM' },
141
+ ]}
142
+ onChange={(val) => console.log(val)}
143
+ />
144
+ ```
145
+
146
+ ---
147
+
148
+ ### Checkbox / Radio / Switch
149
+
150
+ ```tsx
151
+ import { Checkbox, CheckboxGroup, Radio, RadioGroup, Switch } from '@vetc-miniapp/ui-react';
152
+
153
+ // Checkbox
154
+ <Checkbox label="Đồng ý điều khoản" checked={agreed} onChange={setAgreed} />
155
+
156
+ // Checkbox Group
157
+ <CheckboxGroup
158
+ options={[
159
+ { label: 'Tùy chọn A', value: 'a' },
160
+ { label: 'Tùy chọn B', value: 'b' },
161
+ ]}
162
+ value={selected}
163
+ onChange={setSelected}
164
+ direction="vertical"
165
+ />
166
+
167
+ // Radio Group
168
+ <RadioGroup
169
+ options={[{ label: 'Nam', value: 'male' }, { label: 'Nữ', value: 'female' }]}
170
+ value={gender}
171
+ onChange={setGender}
172
+ />
173
+
174
+ // Switch
175
+ <Switch label="Bật thông báo" checked={notif} onChange={setNotif} />
176
+ ```
177
+
178
+ ---
179
+
180
+ ### Chip / Tag
181
+
182
+ ```tsx
183
+ import { Chip } from '@vetc-miniapp/ui-react';
184
+
185
+ // Types: default | brand | positive | negative | warning | info
186
+ // Variants: tinted | outlined | filled
187
+ <Chip type="positive" variant="tinted">Hoàn thành</Chip>
188
+ <Chip type="negative" variant="filled">Lỗi</Chip>
189
+ <Chip type="warning" variant="outlined" closable onClose={...}>Cảnh báo</Chip>
190
+
191
+ // Small
192
+ <Chip size="sm" type="info">Mới</Chip>
193
+ ```
194
+
195
+ ---
196
+
197
+ ### Avatar
46
198
 
47
- <button onClick={() => navigate('/about', { title: "About", params: {}})}>
48
- Go to About
49
- </button>
199
+ ```tsx
200
+ import { Avatar } from '@vetc-miniapp/ui-react';
50
201
 
51
- Truyền params
52
- navigate('/vehicle-detail', {
53
- title: "About",
54
- params: {
55
- id: 'abc123',
56
- source: 'homepage',
57
- }
58
- });
202
+ // Image
203
+ <Avatar src="https://..." alt="User" size="md" />
204
+
205
+ // Initials
206
+ <Avatar initials="Nguyễn Văn A" size="lg" color="#25a45e" />
207
+
208
+ // Sizes: xl(56) | lg(48) | md(40) | sm(32) | xs(24)
59
209
  ```
60
210
 
61
- # ➡ Flutter sẽ nhận được URL dạng:
211
+ ---
212
+
213
+ ### Card
62
214
 
63
- /miniapp/vehicle-detail?id=abc123&source=homepage
215
+ ```tsx
216
+ import { Card } from '@vetc-miniapp/ui-react';
64
217
 
65
- ## 🔁 Replace route (không giữ back stack)
218
+ // Elevations: outlined | raised | flat
219
+ <Card elevation="outlined" title="Thông tin xe" extra={<Button size="sm">Sửa</Button>}>
220
+ Nội dung card...
221
+ </Card>
66
222
  ```
67
- navigate('/login', { mode: 'replace' });
68
223
 
224
+ ---
225
+
226
+ ### List / ListItem
69
227
 
70
- navigate('/home', { popTo: '/home' });
228
+ ```tsx
229
+ import { List, ListItem } from '@vetc-miniapp/ui-react';
230
+
231
+ <List
232
+ bordered
233
+ items={vehicles}
234
+ renderItem={(v) => (
235
+ <ListItem
236
+ leading={<CarIcon />}
237
+ title={v.plate}
238
+ description={v.type}
239
+ arrow
240
+ onClick={() => navigate(v.id)}
241
+ />
242
+ )}
243
+ />
71
244
  ```
72
- ## 🧩 Ví dụ đầy đủ trong Component
245
+
246
+ ---
247
+
248
+ ### Loading
249
+
250
+ ```tsx
251
+ import { Spinner, SkeletonLoader } from '@vetc-miniapp/ui-react';
252
+
253
+ // Spinner inline
254
+ <Spinner size="md" />
255
+
256
+ // Full-screen overlay
257
+ <Spinner fullscreen spinning={isLoading} />
258
+
259
+ // Spinner wrapping content
260
+ <Spinner spinning={loading}>
261
+ <Content />
262
+ </Spinner>
263
+
264
+ // Skeleton
265
+ <SkeletonLoader loading={loading} rows={3} avatar>
266
+ <ActualContent />
267
+ </SkeletonLoader>
268
+ ```
269
+
270
+ ---
271
+
272
+ ### Toast
273
+
274
+ ```tsx
275
+ import { toast, useToast } from '@vetc-miniapp/ui-react';
276
+
277
+ // Static (simple)
278
+ toast.success('Đăng ký thành công!');
279
+ toast.error('Có lỗi xảy ra', 'Vui lòng thử lại');
280
+ toast.warning('Cảnh báo');
281
+ toast.info('Thông tin');
282
+
283
+ // Hook-based (recommended)
284
+ function MyComponent() {
285
+ const { toast, contextHolder } = useToast();
286
+ return (
287
+ <>
288
+ {contextHolder}
289
+ <Button onClick={() => toast.success('OK!')}>Thông báo</Button>
290
+ </>
291
+ );
292
+ }
73
293
  ```
74
294
 
75
- import { useNavigate } from '@vetc-miniapp/ui-react';
295
+ ---
296
+
297
+ ### Modal / Dialog
76
298
 
77
- function VehicleItem({ vehicle }) {
78
- const navigate = useNavigate();
299
+ ```tsx
300
+ import { Modal, useConfirm } from '@vetc-miniapp/ui-react';
79
301
 
302
+ // Controlled modal
303
+ <Modal
304
+ open={isOpen}
305
+ title="Xác nhận thanh toán"
306
+ okText="Thanh toán"
307
+ cancelText="Hủy"
308
+ onOk={handlePay}
309
+ onCancel={() => setOpen(false)}
310
+ >
311
+ Bạn có chắc muốn thanh toán 50,000đ?
312
+ </Modal>
313
+
314
+ // Confirm dialog
315
+ function MyComp() {
316
+ const { confirm } = useConfirm();
80
317
  return (
81
- <div onClick={() => navigate('/vehicle-detail', { title: "Chi tiết xe"}, { mode: "replace" })}>
82
- {vehicle.name}
83
- </div>
318
+ <Button onClick={() => confirm({
319
+ title: 'Xóa phương tiện?',
320
+ content: 'Hành động này không thể hoàn tác.',
321
+ okDanger: true,
322
+ onOk: handleDelete,
323
+ })}>
324
+ Xóa
325
+ </Button>
84
326
  );
85
327
  }
86
328
  ```
87
329
 
88
- ## ⚠️ Lưu ý quan trọng
89
- # ❌ Không dùng react-router-dom
330
+ ---
331
+
332
+ ### Bottom Sheet
333
+
334
+ ```tsx
335
+ import { BottomSheet } from '@vetc-miniapp/ui-react';
336
+
337
+ <BottomSheet
338
+ open={isOpen}
339
+ title="Chọn phương thức"
340
+ onClose={() => setOpen(false)}
341
+ >
342
+ <ListItem title="Thẻ ngân hàng" arrow onClick={...} />
343
+ <ListItem title="Ví điện tử" arrow onClick={...} />
344
+ </BottomSheet>
90
345
  ```
91
346
 
92
- // KHÔNG dùng
93
- import { useNavigate } from 'react-router-dom';
347
+ ---
94
348
 
349
+ ### NavigationBar (Top Nav)
95
350
 
96
- Mini app không quản lý history nội bộ.
351
+ ```tsx
352
+ import { NavigationBar } from '@vetc-miniapp/ui-react';
353
+
354
+ <NavigationBar
355
+ title="Chi tiết giao dịch"
356
+ showBack
357
+ onBack={() => history.back()}
358
+ actions={[
359
+ { icon: <ShareIcon />, label: 'Chia sẻ', onClick: handleShare },
360
+ { icon: <MoreIcon />, label: 'Thêm', onClick: handleMore },
361
+ ]}
362
+ />
97
363
  ```
98
364
 
99
- # ❌ Không dùng window.location
365
+ ---
366
+
367
+ ### TabBar (Bottom Nav)
368
+
369
+ ```tsx
370
+ import { TabBar } from '@vetc-miniapp/ui-react';
371
+
372
+ <TabBar
373
+ activeKey={activeTab}
374
+ onChange={setActiveTab}
375
+ items={[
376
+ { key: 'home', label: 'Trang chủ', icon: <HomeIcon /> },
377
+ { key: 'history', label: 'Lịch sử', icon: <HistoryIcon />, badge: 3 },
378
+ { key: 'profile', label: 'Tài khoản', icon: <UserIcon /> },
379
+ ]}
380
+ />
100
381
  ```
101
- window.location.href = '/about'; // ❌
382
+
383
+ ---
384
+
385
+ ## Design Tokens
386
+
387
+ ```ts
388
+ import { colorAlias, colorGlobal, fontSize, spacing, borderRadius } from '@vetc-miniapp/ui-react';
389
+
390
+ // Colors
391
+ colorAlias.brand // #25a45e
392
+ colorAlias.negative // #da2c39
393
+ colorAlias.warning // #ff8c2f
394
+
395
+ // Typography
396
+ fontSize.base // '16px'
397
+ fontSize.sm // '13px'
398
+
399
+ // Spacing
400
+ spacing[16] // '16px'
401
+
402
+ // Border radius
403
+ borderRadius.md // '8px'
404
+ borderRadius.xl // '16px'
405
+ borderRadius.pill // '1000px'
406
+ ```
407
+
408
+ ---
409
+
410
+ ## Theming
411
+
412
+ `VETCProvider` tự động inject Ant Design ConfigProvider với toàn bộ VETC token. Bạn có thể extend theme:
413
+
414
+ ```tsx
415
+ import { VETCProvider, vetcAntdTheme } from '@vetc-miniapp/ui-react';
416
+ import { ConfigProvider } from 'antd';
417
+
418
+ <ConfigProvider theme={{ ...vetcAntdTheme, token: { ...vetcAntdTheme.token, colorPrimary: '#custom' } }}>
419
+ <App />
420
+ </ConfigProvider>
102
421
  ```
@@ -0,0 +1,11 @@
1
+ export interface FlutterInAppWebViewBridge {
2
+ callHandler(handlerName: string, args?: unknown): Promise<unknown>;
3
+ }
4
+ declare global {
5
+ interface Window {
6
+ flutter_inappwebview?: FlutterInAppWebViewBridge;
7
+ }
8
+ }
9
+ export declare const isBrowser: boolean;
10
+ export declare const getBridge: () => FlutterInAppWebViewBridge | null;
11
+ export declare const callHost: <T = unknown>(action: string, payload?: Record<string, unknown>) => Promise<T>;
package/dist/bridge.js ADDED
@@ -0,0 +1,20 @@
1
+ /* ================= Types ================= */
2
+ /* ================= Runtime helpers ================= */
3
+ export const isBrowser = typeof window !== "undefined";
4
+ export const getBridge = () => {
5
+ if (!isBrowser || !window.flutter_inappwebview || !window.flutter_inappwebview.callHandler || typeof window.flutter_inappwebview.callHandler != "function") {
6
+ return null;
7
+ }
8
+ return window.flutter_inappwebview;
9
+ };
10
+ /* ================= Core bridge call ================= */
11
+ export const callHost = (action, payload = {}) => {
12
+ const bridge = getBridge();
13
+ if (!bridge) {
14
+ return Promise.resolve({ ok: false, error: "BRIDGE_CALL_FAILED" });
15
+ }
16
+ return bridge.callHandler("MiniAppBridge", {
17
+ action,
18
+ payload,
19
+ });
20
+ };
@@ -0,0 +1,6 @@
1
+ export declare function initRouter(config: any): void;
2
+ export declare function getCurrentPage(): any;
3
+ export declare function setCurrentPath(path: any): void;
4
+ export declare function App({ config }: {
5
+ config: any;
6
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { callHost } from "../bridge";
4
+ let routeMap = new Map();
5
+ let currentPath = "/";
6
+ export function initRouter(config) {
7
+ routeMap = new Map(config.pages.map(p => [p.pathname, p]));
8
+ }
9
+ export function getCurrentPage() {
10
+ return routeMap.get(currentPath);
11
+ }
12
+ export function setCurrentPath(path) {
13
+ currentPath = path;
14
+ }
15
+ export function App({ config }) {
16
+ const [page, setPage] = useState(null);
17
+ useEffect(() => {
18
+ initRouter(config);
19
+ const rawPath = window.location.pathname || "/";
20
+ const path = rawPath.replace("/miniapp", "") || "/";
21
+ setCurrentPath(path);
22
+ let current = getCurrentPage();
23
+ if (!current) {
24
+ setCurrentPath("/");
25
+ current = getCurrentPage();
26
+ }
27
+ setPage(current);
28
+ callHost("registerAppConfig", { config });
29
+ }, [config]);
30
+ if (!page)
31
+ return _jsx("div", {});
32
+ const Component = page.Component;
33
+ return _jsx(Component, {});
34
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * VETC Avatar — tokenized
3
+ */
4
+ import React from 'react';
5
+ export type AvatarSize = 'xl' | 'lg' | 'md' | 'sm' | 'xs';
6
+ export type AvatarShape = 'circle' | 'square';
7
+ export interface AvatarProps {
8
+ src?: string;
9
+ alt?: string;
10
+ initials?: string;
11
+ icon?: React.ReactNode;
12
+ size?: AvatarSize;
13
+ shape?: AvatarShape;
14
+ color?: string;
15
+ className?: string;
16
+ style?: React.CSSProperties;
17
+ onClick?: () => void;
18
+ id?: string;
19
+ }
20
+ export declare function Avatar({ src, alt, initials, icon, size, shape, color, className, style, onClick, id, }: AvatarProps): import("react/jsx-runtime").JSX.Element;
21
+ export default Avatar;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Avatar as AntAvatar } from 'antd';
3
+ // Map size token name to CSS var
4
+ const sizeVarMap = {
5
+ xl: 'var(--vetc-avatar-size-xl)',
6
+ lg: 'var(--vetc-avatar-size-lg)',
7
+ md: 'var(--vetc-avatar-size-md)',
8
+ sm: 'var(--vetc-avatar-size-sm)',
9
+ xs: 'var(--vetc-avatar-size-xs)',
10
+ };
11
+ // Pixel values needed for antd size prop (numeric)
12
+ const sizePxMap = { xl: 56, lg: 48, md: 40, sm: 32, xs: 24 };
13
+ // Font size scales with avatar size (from Figma)
14
+ const fontSizeMap = {
15
+ xl: 'var(--vetc-font-size-2xl)',
16
+ lg: 'var(--vetc-font-size-xl)',
17
+ md: 'var(--vetc-font-size-base)',
18
+ sm: 'var(--vetc-font-size-sm)',
19
+ xs: 'var(--vetc-font-size-2xs)',
20
+ };
21
+ export function Avatar({ src, alt, initials, icon, size = 'md', shape = 'circle', color, className = '', style, onClick, id, }) {
22
+ return (_jsx("span", { id: id, style: { display: 'inline-flex', flexShrink: 0, width: sizeVarMap[size], height: sizeVarMap[size] }, children: _jsx(AntAvatar, { src: src, alt: alt, icon: !src && !initials ? icon : undefined, size: sizePxMap[size], shape: shape, onClick: onClick, className: `vetc-avatar vetc-avatar--${size} ${className}`, style: {
23
+ backgroundColor: !src ? (color ?? 'var(--vetc-avatar-bg-default)') : undefined,
24
+ fontSize: fontSizeMap[size],
25
+ fontWeight: 'var(--vetc-font-weight-bold)',
26
+ fontFamily: 'var(--vetc-font-family)',
27
+ color: 'var(--vetc-avatar-text-color)',
28
+ cursor: onClick ? 'pointer' : 'default',
29
+ flexShrink: 0,
30
+ ...style,
31
+ }, children: !src && initials ? initials.slice(0, 2).toUpperCase() : undefined }) }));
32
+ }
33
+ export default Avatar;
@@ -0,0 +1,2 @@
1
+ export { Avatar } from './Avatar';
2
+ export type { AvatarProps, AvatarSize, AvatarShape } from './Avatar';
@@ -0,0 +1 @@
1
+ export { Avatar } from './Avatar';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * VETC BottomSheet — tokenized
3
+ * Figma: Sheet page — 16px top radius, handle bar, slide-up animation
4
+ */
5
+ import React from 'react';
6
+ export interface BottomSheetProps {
7
+ open: boolean;
8
+ title?: React.ReactNode;
9
+ children?: React.ReactNode;
10
+ maskClosable?: boolean;
11
+ showHandle?: boolean;
12
+ maxHeight?: string;
13
+ onClose?: () => void;
14
+ className?: string;
15
+ style?: React.CSSProperties;
16
+ id?: string;
17
+ }
18
+ export declare function BottomSheet({ open, title, children, maskClosable, showHandle, maxHeight, onClose, className, style, id, }: BottomSheetProps): import("react/jsx-runtime").JSX.Element;
19
+ export default BottomSheet;