@vetc-miniapp/ui-react 0.0.24 → 0.0.26

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 +17 -0
  2. package/dist/bridge.js +20 -0
  3. package/dist/components/app.d.ts +17 -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 +4 -0
  69. package/dist/hooks/use-app-pause.js +25 -0
  70. package/dist/hooks/use-app-resume.d.ts +4 -0
  71. package/dist/hooks/use-app-resume.js +25 -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 +20 -0
  76. package/dist/hooks/use-did-show.d.ts +6 -0
  77. package/dist/hooks/use-did-show.js +20 -0
  78. package/dist/hooks/use-listener-scan-qr.d.ts +10 -0
  79. package/dist/hooks/use-listener-scan-qr.js +25 -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 +20 -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
@@ -0,0 +1,17 @@
1
+ export interface FlutterInAppWebViewBridge {
2
+ callHandler(handlerName: string, args?: unknown): Promise<unknown>;
3
+ }
4
+ export interface MiniAppBridge {
5
+ on(event: string, callback: (data: any) => void): void;
6
+ off(event: string, callback: (data: any) => void): void;
7
+ _emit?: (event: string, data?: any) => void;
8
+ }
9
+ declare global {
10
+ interface Window {
11
+ flutter_inappwebview?: FlutterInAppWebViewBridge;
12
+ MiniApp?: MiniAppBridge;
13
+ }
14
+ }
15
+ export declare const isBrowser: boolean;
16
+ export declare const getBridge: () => FlutterInAppWebViewBridge | null;
17
+ 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,17 @@
1
+ import React from "react";
2
+ interface PageConfig {
3
+ pathname: string;
4
+ Component: React.ComponentType<any>;
5
+ [key: string]: any;
6
+ }
7
+ interface AppConfig {
8
+ pages: PageConfig[];
9
+ [key: string]: any;
10
+ }
11
+ export declare function initRouter(config: AppConfig): void;
12
+ export declare function getCurrentPage(): PageConfig | undefined;
13
+ export declare function setCurrentPath(path: string): void;
14
+ export declare function App({ config }: {
15
+ config: AppConfig;
16
+ }): import("react/jsx-runtime").JSX.Element;
17
+ export {};
@@ -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() ?? null;
23
+ if (!current) {
24
+ setCurrentPath("/");
25
+ current = getCurrentPage() ?? null;
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 @@
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;
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * VETC BottomSheet — tokenized
4
+ * Figma: Sheet page — 16px top radius, handle bar, slide-up animation
5
+ */
6
+ import { useEffect } from 'react';
7
+ export function BottomSheet({ open, title, children, maskClosable = true, showHandle = true, maxHeight = '90vh', onClose, className = '', style, id, }) {
8
+ // Lock body scroll when open
9
+ useEffect(() => {
10
+ document.body.style.overflow = open ? 'hidden' : '';
11
+ return () => { document.body.style.overflow = ''; };
12
+ }, [open]);
13
+ if (!open)
14
+ return null;
15
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "vetc-bottom-sheet-overlay", "aria-hidden": "true", onClick: () => maskClosable && onClose?.(), style: {
16
+ position: 'fixed',
17
+ inset: 0,
18
+ backgroundColor: 'var(--vetc-sheet-overlay)',
19
+ zIndex: 1000,
20
+ animation: 'vetc-fade-in var(--vetc-transition-base)',
21
+ } }), _jsxs("div", { id: id, role: "dialog", "aria-modal": "true", className: `vetc-bottom-sheet ${className}`, style: {
22
+ position: 'fixed',
23
+ bottom: 0,
24
+ left: 0,
25
+ right: 0,
26
+ backgroundColor: 'var(--vetc-sheet-bg)',
27
+ borderRadius: `var(--vetc-sheet-radius) var(--vetc-sheet-radius) 0 0`,
28
+ maxHeight,
29
+ overflowY: 'auto',
30
+ zIndex: 1001,
31
+ fontFamily: 'var(--vetc-font-family)',
32
+ animation: 'vetc-slide-up var(--vetc-transition-slow) cubic-bezier(0.32, 0.72, 0, 1)',
33
+ ...style,
34
+ }, children: [showHandle && (_jsx("div", { style: { display: 'flex', justifyContent: 'center', paddingTop: 'var(--vetc-space-8)', paddingBottom: 'var(--vetc-space-4)' }, children: _jsx("div", { style: {
35
+ width: 'var(--vetc-sheet-handle-width)',
36
+ height: 'var(--vetc-sheet-handle-height)',
37
+ borderRadius: 'var(--vetc-radius-pill)',
38
+ backgroundColor: 'var(--vetc-sheet-handle-color)',
39
+ } }) })), title && (_jsxs("div", { style: {
40
+ padding: `var(--vetc-space-12) var(--vetc-sheet-padding)`,
41
+ borderBottom: `1px solid var(--vetc-sheet-border)`,
42
+ display: 'flex',
43
+ alignItems: 'center',
44
+ justifyContent: 'space-between',
45
+ }, children: [_jsx("span", { style: {
46
+ fontSize: 'var(--vetc-sheet-title-size)',
47
+ fontWeight: 'var(--vetc-sheet-title-weight)',
48
+ color: 'var(--vetc-sheet-title-color)',
49
+ lineHeight: 'var(--vetc-line-height-relaxed)',
50
+ }, children: title }), onClose && (_jsx("button", { onClick: onClose, "aria-label": "\u0110\u00F3ng", style: {
51
+ background: 'none',
52
+ border: 'none',
53
+ cursor: 'pointer',
54
+ padding: 'var(--vetc-space-4)',
55
+ display: 'flex',
56
+ color: 'var(--vetc-color-icon-muted)',
57
+ borderRadius: 'var(--vetc-radius-pill)',
58
+ transition: 'background-color var(--vetc-transition-fast)',
59
+ }, children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) }) }))] })), _jsx("div", { style: { padding: 'var(--vetc-sheet-padding)' }, children: children })] }), _jsx("style", { children: `
60
+ @keyframes vetc-fade-in {
61
+ from { opacity: 0 }
62
+ to { opacity: 1 }
63
+ }
64
+ @keyframes vetc-slide-up {
65
+ from { transform: translateY(100%) }
66
+ to { transform: translateY(0) }
67
+ }
68
+ ` })] }));
69
+ }
70
+ export default BottomSheet;
@@ -0,0 +1 @@
1
+ export { BottomSheet } from './BottomSheet';
@@ -0,0 +1,32 @@
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 from 'react';
10
+ export type ButtonStyle = 'filled' | 'outlined' | 'elevated' | 'ghost' | 'danger' | 'danger-outlined';
11
+ export type ButtonVariant = 'brand' | 'neutral';
12
+ export type ButtonSize = 'lg' | 'sm';
13
+ export type ButtonShape = 'rounded' | 'pill';
14
+ export interface ButtonProps {
15
+ style?: ButtonStyle;
16
+ variant?: ButtonVariant;
17
+ size?: ButtonSize;
18
+ shape?: ButtonShape;
19
+ block?: boolean;
20
+ disabled?: boolean;
21
+ loading?: boolean;
22
+ icon?: React.ReactNode;
23
+ iconRight?: React.ReactNode;
24
+ onClick?: React.MouseEventHandler<HTMLButtonElement>;
25
+ children?: React.ReactNode;
26
+ htmlType?: 'button' | 'submit' | 'reset';
27
+ className?: string;
28
+ css?: React.CSSProperties;
29
+ id?: string;
30
+ }
31
+ export declare function Button({ style, variant, size, shape, block, disabled, loading, icon, iconRight, onClick, children, htmlType, className, css, id, }: ButtonProps): import("react/jsx-runtime").JSX.Element;
32
+ export default Button;
@@ -0,0 +1,165 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * VETC Button Component
4
+ * Figma: filled/outlined/elevated/ghost styles
5
+ * Large: h=48px, radius=12px, font=16px/600
6
+ * Small: h=32px, radius=8px, font=14px/600
7
+ * style: filled | outlined | elevated | ghost | danger | danger-outlined
8
+ * variant: brand (green) | neutral (gray-90)
9
+ */
10
+ import { useState } from 'react';
11
+ const BRAND = {
12
+ filled: {
13
+ bg: 'var(--vetc-color-brand)',
14
+ bgHover: 'var(--vetc-color-brand-hover)',
15
+ bgActive: 'var(--vetc-color-brand-active)',
16
+ text: 'var(--vetc-white)',
17
+ border: 'transparent',
18
+ shadow: 'none',
19
+ },
20
+ outlined: {
21
+ bg: 'transparent',
22
+ bgHover: 'var(--vetc-green-02)',
23
+ bgActive: 'var(--vetc-green-05)',
24
+ text: 'var(--vetc-color-brand)',
25
+ border: 'var(--vetc-color-brand)',
26
+ shadow: 'none',
27
+ },
28
+ elevated: {
29
+ bg: 'var(--vetc-color-bg)',
30
+ bgHover: 'var(--vetc-color-bg-hover)',
31
+ bgActive: 'var(--vetc-color-bg-pressed)',
32
+ text: 'var(--vetc-color-brand)',
33
+ border: 'transparent',
34
+ shadow: 'var(--vetc-shadow-md)',
35
+ },
36
+ ghost: {
37
+ bg: 'transparent',
38
+ bgHover: 'var(--vetc-color-bg-hover)',
39
+ bgActive: 'var(--vetc-color-bg-pressed)',
40
+ text: 'var(--vetc-color-brand)',
41
+ border: 'transparent',
42
+ shadow: 'none',
43
+ },
44
+ danger: {
45
+ bg: 'var(--vetc-color-negative)',
46
+ bgHover: 'var(--vetc-red-60)',
47
+ bgActive: 'var(--vetc-red-60)',
48
+ text: 'var(--vetc-white)',
49
+ border: 'transparent',
50
+ shadow: 'none',
51
+ },
52
+ 'danger-outlined': {
53
+ bg: 'transparent',
54
+ bgHover: 'var(--vetc-red-02)',
55
+ bgActive: 'var(--vetc-red-05)',
56
+ text: 'var(--vetc-color-negative)',
57
+ border: 'var(--vetc-color-negative)',
58
+ shadow: 'none',
59
+ },
60
+ };
61
+ const NEUTRAL = {
62
+ filled: {
63
+ bg: 'var(--vetc-gray-90)',
64
+ bgHover: 'var(--vetc-gray-80)',
65
+ bgActive: 'var(--vetc-gray-70)',
66
+ text: 'var(--vetc-white)',
67
+ border: 'transparent',
68
+ shadow: 'none',
69
+ },
70
+ outlined: {
71
+ bg: 'transparent',
72
+ bgHover: 'var(--vetc-gray-05)',
73
+ bgActive: 'var(--vetc-gray-10)',
74
+ text: 'var(--vetc-gray-90)',
75
+ border: 'var(--vetc-gray-90)',
76
+ shadow: 'none',
77
+ },
78
+ elevated: {
79
+ bg: 'var(--vetc-color-bg)',
80
+ bgHover: 'var(--vetc-color-bg-hover)',
81
+ bgActive: 'var(--vetc-color-bg-pressed)',
82
+ text: 'var(--vetc-color-text-primary)',
83
+ border: 'transparent',
84
+ shadow: 'var(--vetc-shadow-md)',
85
+ },
86
+ ghost: {
87
+ bg: 'transparent',
88
+ bgHover: 'var(--vetc-color-bg-hover)',
89
+ bgActive: 'var(--vetc-color-bg-pressed)',
90
+ text: 'var(--vetc-color-text-primary)',
91
+ border: 'transparent',
92
+ shadow: 'none',
93
+ },
94
+ // danger variants are semantic — variant (brand/neutral) has no effect
95
+ danger: {
96
+ bg: 'var(--vetc-color-negative)',
97
+ bgHover: 'var(--vetc-red-60)',
98
+ bgActive: 'var(--vetc-red-60)',
99
+ text: 'var(--vetc-white)',
100
+ border: 'transparent',
101
+ shadow: 'none',
102
+ },
103
+ 'danger-outlined': {
104
+ bg: 'transparent',
105
+ bgHover: 'var(--vetc-red-02)',
106
+ bgActive: 'var(--vetc-red-05)',
107
+ text: 'var(--vetc-color-negative)',
108
+ border: 'var(--vetc-color-negative)',
109
+ shadow: 'none',
110
+ },
111
+ };
112
+ const LoadingSpinner = ({ color }) => (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", style: { animation: 'vetc-btn-spin 0.6s linear infinite', flexShrink: 0 }, "aria-hidden": "true", children: [_jsx("circle", { cx: "8", cy: "8", r: "6", stroke: color, strokeOpacity: "0.25", strokeWidth: "2" }), _jsx("path", { d: "M8 2a6 6 0 016 6", stroke: color, strokeWidth: "2", strokeLinecap: "round" })] }));
113
+ export function Button({ style = 'filled', variant = 'brand', size = 'lg', shape = 'rounded', block = false, disabled = false, loading = false, icon, iconRight, onClick, children, htmlType = 'button', className = '', css, id, }) {
114
+ const [pressed, setPressed] = useState(false);
115
+ const [hovered, setHovered] = useState(false);
116
+ const tokens = (variant === 'neutral' ? NEUTRAL : BRAND)[style];
117
+ const isDisabled = disabled || loading;
118
+ const height = size === 'lg' ? 'var(--vetc-btn-height-lg)' : 'var(--vetc-btn-height-sm)';
119
+ const fontSize = size === 'lg' ? 'var(--vetc-btn-font-size-lg)' : 'var(--vetc-btn-font-size-sm)';
120
+ const paddingX = size === 'lg' ? 'var(--vetc-btn-padding-x-lg)' : 'var(--vetc-btn-padding-x-sm)';
121
+ const radius = shape === 'pill' ? 'var(--vetc-btn-radius-pill)'
122
+ : size === 'lg' ? 'var(--vetc-btn-radius-lg)'
123
+ : 'var(--vetc-btn-radius-sm)';
124
+ let bg = tokens.bg;
125
+ let border = tokens.border;
126
+ let shadow = tokens.shadow;
127
+ if (isDisabled) {
128
+ bg = 'var(--vetc-btn-disabled-bg)';
129
+ border = 'var(--vetc-btn-disabled-border)';
130
+ shadow = 'none';
131
+ }
132
+ else if (pressed) {
133
+ bg = tokens.bgActive;
134
+ }
135
+ else if (hovered) {
136
+ bg = tokens.bgHover;
137
+ }
138
+ const textColor = isDisabled ? 'var(--vetc-btn-disabled-text)' : tokens.text;
139
+ const spinnerColor = isDisabled ? 'var(--vetc-gray-30)' : tokens.text;
140
+ return (_jsxs(_Fragment, { children: [_jsxs("button", { id: id, type: htmlType, disabled: isDisabled, onClick: onClick, onMouseEnter: () => setHovered(true), onMouseLeave: () => { setHovered(false); setPressed(false); }, onMouseDown: () => setPressed(true), onMouseUp: () => setPressed(false), className: `vetc-btn vetc-btn--${style} vetc-btn--${variant} vetc-btn--${size} ${className}`, style: {
141
+ display: 'inline-flex',
142
+ alignItems: 'center',
143
+ justifyContent: 'center',
144
+ gap: 'var(--vetc-space-8)',
145
+ height,
146
+ width: block ? '100%' : undefined,
147
+ paddingInline: paddingX,
148
+ borderRadius: radius,
149
+ border: `1px solid ${border}`,
150
+ backgroundColor: bg,
151
+ boxShadow: shadow,
152
+ color: textColor,
153
+ fontSize,
154
+ fontWeight: 'var(--vetc-btn-font-weight)',
155
+ fontFamily: 'var(--vetc-font-family)',
156
+ lineHeight: 'var(--vetc-line-height-relaxed)',
157
+ cursor: isDisabled ? 'not-allowed' : 'pointer',
158
+ transition: 'background-color var(--vetc-transition-fast), box-shadow var(--vetc-transition-fast)',
159
+ userSelect: 'none',
160
+ outline: 'none',
161
+ whiteSpace: 'nowrap',
162
+ ...css,
163
+ }, children: [loading ? _jsx(LoadingSpinner, { color: spinnerColor }) : icon && _jsx("span", { style: { display: 'inline-flex', flexShrink: 0 }, children: icon }), children && _jsx("span", { children: children }), iconRight && !loading && _jsx("span", { style: { display: 'inline-flex', flexShrink: 0 }, children: iconRight })] }), _jsx("style", { children: `@keyframes vetc-btn-spin { to { transform: rotate(360deg); } }` })] }));
164
+ }
165
+ export default Button;
@@ -0,0 +1 @@
1
+ export { Button } from './Button';
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ export type ButtonGroupLayout = 'inline' | 'stacked';
3
+ export type ButtonGroupVariant = 'bounding' | 'none';
4
+ export interface ButtonGroupAction {
5
+ label: React.ReactNode;
6
+ onClick?: () => void;
7
+ loading?: boolean;
8
+ disabled?: boolean;
9
+ }
10
+ export interface ButtonGroupProps {
11
+ /** Primary action — brand filled, rightmost (inline) or bottom (stacked). Required. */
12
+ primary: ButtonGroupAction & {
13
+ /** danger = red filled style */
14
+ danger?: boolean;
15
+ /** Override primary button color. Default: brand */
16
+ variant?: 'brand' | 'neutral';
17
+ };
18
+ /** Secondary actions — neutral outlined. Max 2. */
19
+ secondary?: ButtonGroupAction[];
20
+ /** Optional content rendered above the button row */
21
+ content?: React.ReactNode;
22
+ /** inline: buttons side by side | stacked: buttons stacked vertically */
23
+ layout?: ButtonGroupLayout;
24
+ /** bounding: top border + background | none: no container styling */
25
+ variant?: ButtonGroupVariant;
26
+ }
27
+ export declare function ButtonGroup({ primary, secondary, content, layout, variant, }: ButtonGroupProps): import("react/jsx-runtime").JSX.Element;
28
+ export default ButtonGroup;
@@ -0,0 +1,21 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Button } from '../button';
3
+ export function ButtonGroup({ primary, secondary = [], content, layout = 'inline', variant = 'none', }) {
4
+ const isInline = layout === 'inline';
5
+ const isBounding = variant === 'bounding';
6
+ const containerStyle = isBounding
7
+ ? {
8
+ backgroundColor: 'var(--vetc-btn-group-bg)',
9
+ borderTop: `1px solid var(--vetc-btn-group-border-color)`,
10
+ padding: 'var(--vetc-btn-group-padding)',
11
+ fontFamily: 'var(--vetc-font-family)',
12
+ }
13
+ : { fontFamily: 'var(--vetc-font-family)' };
14
+ const rowStyle = isInline
15
+ ? { display: 'flex', gap: 'var(--vetc-btn-group-gap)', alignItems: 'stretch' }
16
+ : { display: 'flex', flexDirection: 'column', gap: 'var(--vetc-btn-group-gap)' };
17
+ const secondaryBtns = secondary.map((action, i) => (_jsx(Button, { style: "outlined", variant: "neutral", block: !isInline, loading: action.loading, disabled: action.disabled, onClick: action.onClick, css: isInline ? { flex: 1 } : undefined, children: action.label }, i)));
18
+ const primaryBtn = (_jsx(Button, { style: primary.danger ? 'danger' : 'filled', variant: primary.variant ?? 'brand', block: !isInline, loading: primary.loading, disabled: primary.disabled, onClick: primary.onClick, css: isInline ? { flex: 1 } : undefined, children: primary.label }));
19
+ return (_jsxs("div", { style: containerStyle, children: [content && (_jsx("div", { style: { marginBottom: 'var(--vetc-btn-group-padding)' }, children: content })), _jsx("div", { style: rowStyle, children: isInline ? (_jsxs(_Fragment, { children: [secondaryBtns, primaryBtn] })) : (_jsxs(_Fragment, { children: [primaryBtn, secondaryBtns] })) })] }));
20
+ }
21
+ export default ButtonGroup;
@@ -0,0 +1 @@
1
+ export { ButtonGroup } from './ButtonGroup';
@@ -0,0 +1,18 @@
1
+ /**
2
+ * VETC Card — tokenized
3
+ */
4
+ import React from 'react';
5
+ export type CardElevation = 'flat' | 'raised' | 'outlined';
6
+ export interface CardProps {
7
+ elevation?: CardElevation;
8
+ title?: React.ReactNode;
9
+ extra?: React.ReactNode;
10
+ children?: React.ReactNode;
11
+ onClick?: () => void;
12
+ className?: string;
13
+ style?: React.CSSProperties;
14
+ bodyStyle?: React.CSSProperties;
15
+ id?: string;
16
+ }
17
+ export declare function Card({ elevation, title, extra, children, onClick, className, style, bodyStyle, id, }: CardProps): import("react/jsx-runtime").JSX.Element;
18
+ export default Card;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Card as AntCard } from 'antd';
3
+ export function Card({ elevation = 'outlined', title, extra, children, onClick, className = '', style, bodyStyle, id, }) {
4
+ const shadowMap = {
5
+ flat: 'var(--vetc-shadow-none)',
6
+ raised: 'var(--vetc-card-shadow-raised)',
7
+ outlined: 'var(--vetc-shadow-none)',
8
+ };
9
+ const borderMap = {
10
+ flat: 'none',
11
+ raised: 'none',
12
+ outlined: `1px solid var(--vetc-card-border)`,
13
+ };
14
+ return (_jsx(AntCard, { id: id, title: title, extra: extra, onClick: onClick, className: `vetc-card vetc-card--${elevation} ${className}`, styles: {
15
+ body: {
16
+ padding: 'var(--vetc-card-padding)',
17
+ fontFamily: 'var(--vetc-font-family)',
18
+ ...bodyStyle,
19
+ },
20
+ header: title
21
+ ? { borderBottom: `1px solid var(--vetc-card-border)`, padding: `var(--vetc-space-12) var(--vetc-card-padding)` }
22
+ : undefined,
23
+ }, style: {
24
+ borderRadius: 'var(--vetc-card-radius)',
25
+ border: borderMap[elevation],
26
+ boxShadow: shadowMap[elevation],
27
+ cursor: onClick ? 'pointer' : 'default',
28
+ overflow: 'hidden',
29
+ backgroundColor: 'var(--vetc-card-bg)',
30
+ fontFamily: 'var(--vetc-font-family)',
31
+ transition: `box-shadow var(--vetc-transition-fast)`,
32
+ ...style,
33
+ }, children: children }));
34
+ }
35
+ export default Card;
@@ -0,0 +1 @@
1
+ export { Card } from './Card';
@@ -0,0 +1,41 @@
1
+ /**
2
+ * VETC Checkbox & CheckboxGroup
3
+ * Figma: size=20px, touch-area=48px, states: default/hover/pressed/selected/indeterminate/disabled
4
+ * variant: neutral (gray-90) | brand (green-40)
5
+ */
6
+ import React from 'react';
7
+ export type CheckboxVariant = 'neutral' | 'brand';
8
+ export interface CheckboxProps {
9
+ label?: React.ReactNode;
10
+ description?: React.ReactNode;
11
+ checked?: boolean;
12
+ defaultChecked?: boolean;
13
+ indeterminate?: boolean;
14
+ disabled?: boolean;
15
+ variant?: CheckboxVariant;
16
+ onChange?: (checked: boolean) => void;
17
+ value?: any;
18
+ id?: string;
19
+ className?: string;
20
+ style?: React.CSSProperties;
21
+ }
22
+ export declare function Checkbox({ label, description, checked, defaultChecked, indeterminate, disabled, variant, onChange, value, id, className, style, }: CheckboxProps): import("react/jsx-runtime").JSX.Element;
23
+ export interface CheckboxOption {
24
+ label: React.ReactNode;
25
+ description?: React.ReactNode;
26
+ value: any;
27
+ disabled?: boolean;
28
+ }
29
+ export interface CheckboxGroupProps {
30
+ options: CheckboxOption[];
31
+ value?: any[];
32
+ defaultValue?: any[];
33
+ direction?: 'horizontal' | 'vertical';
34
+ disabled?: boolean;
35
+ variant?: CheckboxVariant;
36
+ onChange?: (values: any[]) => void;
37
+ className?: string;
38
+ style?: React.CSSProperties;
39
+ }
40
+ export declare function CheckboxGroup({ options, value, defaultValue, direction, disabled, variant, onChange, className, style, }: CheckboxGroupProps): import("react/jsx-runtime").JSX.Element;
41
+ export default Checkbox;