@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
@@ -1,195 +0,0 @@
1
- /**
2
- * VETC Input / TextField & PasswordInput Components
3
- * All style values use CSS custom properties from tokens.css
4
- * Figma: Text field page — height 48px, radius 8px, padding-x 12px
5
- */
6
- import React from 'react';
7
- import { Input as AntInput } from 'antd';
8
-
9
- export type InputStatus = 'default' | 'error' | 'success';
10
-
11
- // ── Shared label/helper sub-components ───────────────────────────────────────
12
-
13
- function FieldLabel({ htmlFor, required, disabled, children }: {
14
- htmlFor?: string;
15
- required?: boolean;
16
- disabled?: boolean;
17
- children: React.ReactNode;
18
- }) {
19
- return (
20
- <label
21
- htmlFor={htmlFor}
22
- style={{
23
- fontFamily: 'var(--vetc-font-family)',
24
- fontSize: 'var(--vetc-input-label-font-size)',
25
- fontWeight: 'var(--vetc-input-label-font-weight)' as any,
26
- lineHeight: 'var(--vetc-line-height-relaxed)',
27
- color: disabled
28
- ? 'var(--vetc-color-text-disabled)'
29
- : 'var(--vetc-input-label-color)',
30
- display: 'block',
31
- }}
32
- >
33
- {children}
34
- {required && (
35
- <span style={{ color: 'var(--vetc-color-negative)', marginLeft: 'var(--vetc-space-2)' }}>*</span>
36
- )}
37
- </label>
38
- );
39
- }
40
-
41
- function HelperText({ color, children }: { color: string; children: React.ReactNode }) {
42
- return (
43
- <span
44
- style={{
45
- fontFamily: 'var(--vetc-font-family)',
46
- fontSize: 'var(--vetc-input-helper-font-size)',
47
- lineHeight: 'var(--vetc-line-height-relaxed)',
48
- color,
49
- }}
50
- >
51
- {children}
52
- </span>
53
- );
54
- }
55
-
56
- const inputFieldStyle: React.CSSProperties = {
57
- height: 'var(--vetc-input-height)',
58
- borderRadius: 'var(--vetc-input-radius)',
59
- paddingInline:'var(--vetc-input-padding-x)',
60
- fontSize: 'var(--vetc-input-font-size)',
61
- fontFamily: 'var(--vetc-font-family)',
62
- };
63
-
64
- // ── Input ─────────────────────────────────────────────────────────────────────
65
-
66
- export interface InputProps {
67
- label?: string;
68
- placeholder?: string;
69
- value?: string;
70
- defaultValue?: string;
71
- helperText?: string;
72
- /** Status affecting border color */
73
- status?: InputStatus;
74
- /** Error message — sets status=error automatically */
75
- error?: string;
76
- disabled?: boolean;
77
- readOnly?: boolean;
78
- maxLength?: number;
79
- showCount?: boolean;
80
- prefix?: React.ReactNode;
81
- suffix?: React.ReactNode;
82
- allowClear?: boolean;
83
- onChange?: (value: string, event: React.ChangeEvent<HTMLInputElement>) => void;
84
- onFocus?: React.FocusEventHandler<HTMLInputElement>;
85
- onBlur?: React.FocusEventHandler<HTMLInputElement>;
86
- onPressEnter?: React.KeyboardEventHandler<HTMLInputElement>;
87
- type?: string;
88
- id?: string;
89
- name?: string;
90
- className?: string;
91
- style?: React.CSSProperties;
92
- required?: boolean;
93
- }
94
-
95
- export function Input({
96
- label, placeholder, value, defaultValue,
97
- helperText, status = 'default', error,
98
- disabled = false, readOnly = false,
99
- maxLength, showCount = false,
100
- prefix, suffix, allowClear = false,
101
- onChange, onFocus, onBlur, onPressEnter,
102
- type = 'text', id, name,
103
- className = '', style, required = false,
104
- }: InputProps) {
105
- const hasError = !!error;
106
- const resolvedStatus: InputStatus = hasError ? 'error' : status;
107
- const antStatus = resolvedStatus === 'error' ? 'error' : undefined;
108
-
109
- const helperMsg = error ?? helperText;
110
- const helperColor = resolvedStatus === 'error'
111
- ? 'var(--vetc-input-helper-color-error)'
112
- : 'var(--vetc-input-helper-color)';
113
-
114
- return (
115
- <div
116
- className={`vetc-input-wrapper ${className}`}
117
- style={{ display: 'flex', flexDirection: 'column', gap: 'var(--vetc-input-gap)', ...style }}
118
- >
119
- {label && <FieldLabel htmlFor={id} required={required} disabled={disabled}>{label}</FieldLabel>}
120
-
121
- <AntInput
122
- id={id}
123
- name={name}
124
- type={type}
125
- value={value}
126
- defaultValue={defaultValue}
127
- placeholder={placeholder}
128
- disabled={disabled}
129
- readOnly={readOnly}
130
- maxLength={maxLength}
131
- showCount={showCount}
132
- prefix={prefix}
133
- suffix={suffix}
134
- allowClear={allowClear}
135
- status={antStatus}
136
- onChange={(e) => onChange?.(e.target.value, e)}
137
- onFocus={onFocus}
138
- onBlur={onBlur}
139
- onPressEnter={onPressEnter}
140
- style={inputFieldStyle}
141
- />
142
-
143
- {helperMsg && <HelperText color={helperColor}>{helperMsg}</HelperText>}
144
- </div>
145
- );
146
- }
147
-
148
- // ── Password Input ────────────────────────────────────────────────────────────
149
-
150
- export interface PasswordInputProps extends Omit<InputProps, 'type' | 'suffix' | 'allowClear'> {}
151
-
152
- export function PasswordInput({
153
- label, placeholder, value, defaultValue,
154
- helperText, status = 'default', error,
155
- disabled = false, readOnly = false, maxLength,
156
- onChange, onFocus, onBlur,
157
- id, name, className = '', style, required = false,
158
- }: PasswordInputProps) {
159
- const hasError = !!error;
160
- const resolvedStatus: InputStatus = hasError ? 'error' : status;
161
- const antStatus = resolvedStatus === 'error' ? 'error' : undefined;
162
- const helperMsg = error ?? helperText;
163
- const helperColor = hasError
164
- ? 'var(--vetc-input-helper-color-error)'
165
- : 'var(--vetc-input-helper-color)';
166
-
167
- return (
168
- <div
169
- className={`vetc-input-wrapper ${className ?? ''}`}
170
- style={{ display: 'flex', flexDirection: 'column', gap: 'var(--vetc-input-gap)', ...style }}
171
- >
172
- {label && <FieldLabel htmlFor={id} required={required} disabled={disabled}>{label}</FieldLabel>}
173
-
174
- <AntInput.Password
175
- id={id}
176
- name={name}
177
- value={value}
178
- defaultValue={defaultValue}
179
- placeholder={placeholder}
180
- disabled={disabled}
181
- readOnly={readOnly}
182
- maxLength={maxLength}
183
- status={antStatus}
184
- onChange={(e) => onChange?.(e.target.value, e as any)}
185
- onFocus={onFocus as any}
186
- onBlur={onBlur as any}
187
- style={inputFieldStyle}
188
- />
189
-
190
- {helperMsg && <HelperText color={helperColor}>{helperMsg}</HelperText>}
191
- </div>
192
- );
193
- }
194
-
195
- export default Input;
@@ -1,180 +0,0 @@
1
- /**
2
- * VETC List & ListItem — tokenized
3
- * Figma: List item page — height 56px, padding-x 16px
4
- */
5
- import React from 'react';
6
-
7
- // ── ChevronRight Icon ─────────────────────────────────────────────────────────
8
- function ChevronRightIcon({ disabled }: { disabled?: boolean }) {
9
- return (
10
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
11
- <path
12
- d="M7.5 5L12.5 10L7.5 15"
13
- stroke={disabled ? 'var(--vetc-color-icon-disabled)' : 'var(--vetc-color-icon-muted)'}
14
- strokeWidth="1.5"
15
- strokeLinecap="round"
16
- strokeLinejoin="round"
17
- />
18
- </svg>
19
- );
20
- }
21
-
22
- // ── ListItem ──────────────────────────────────────────────────────────────────
23
- export interface ListItemProps {
24
- leading?: React.ReactNode;
25
- title: React.ReactNode;
26
- description?: React.ReactNode;
27
- trailing?: React.ReactNode;
28
- /** Show right chevron */
29
- arrow?: boolean;
30
- /** Bottom divider line */
31
- divider?: boolean;
32
- onClick?: () => void;
33
- disabled?: boolean;
34
- className?: string;
35
- style?: React.CSSProperties;
36
- id?: string;
37
- }
38
-
39
- export function ListItem({
40
- leading,
41
- title,
42
- description,
43
- trailing,
44
- arrow = false,
45
- divider = true,
46
- onClick,
47
- disabled = false,
48
- className = '',
49
- style,
50
- id,
51
- }: ListItemProps) {
52
- const isClickable = !!onClick && !disabled;
53
-
54
- return (
55
- <div
56
- id={id}
57
- role={isClickable ? 'button' : undefined}
58
- tabIndex={isClickable ? 0 : undefined}
59
- className={`vetc-list-item ${className}`}
60
- onClick={!disabled ? onClick : undefined}
61
- onKeyDown={isClickable ? (e) => { if (e.key === 'Enter' || e.key === ' ') onClick?.(); } : undefined}
62
- style={{
63
- display: 'flex',
64
- alignItems: 'center',
65
- minHeight: 'var(--vetc-list-item-height)',
66
- padding: `0 var(--vetc-list-item-padding-x)`,
67
- gap: 'var(--vetc-list-item-gap)',
68
- cursor: isClickable ? 'pointer' : 'default',
69
- borderBottom: divider ? `1px solid var(--vetc-list-item-border)` : 'none',
70
- backgroundColor: 'var(--vetc-list-item-bg)',
71
- opacity: disabled ? 0.5 : 1,
72
- fontFamily: 'var(--vetc-font-family)',
73
- transition: `background-color var(--vetc-transition-fast)`,
74
- outline: 'none',
75
- ...style,
76
- }}
77
- onMouseEnter={(e) => {
78
- if (isClickable) e.currentTarget.style.backgroundColor = 'var(--vetc-list-item-bg-hover)';
79
- }}
80
- onMouseLeave={(e) => {
81
- if (isClickable) e.currentTarget.style.backgroundColor = 'var(--vetc-list-item-bg)';
82
- }}
83
- >
84
- {/* Leading slot */}
85
- {leading && (
86
- <div style={{
87
- flexShrink: 0,
88
- display: 'flex',
89
- alignItems: 'center',
90
- color: 'var(--vetc-list-item-icon-color)',
91
- }}>
92
- {leading}
93
- </div>
94
- )}
95
-
96
- {/* Content */}
97
- <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 'var(--vetc-space-2)' }}>
98
- <span style={{
99
- fontSize: 'var(--vetc-list-item-title-size)',
100
- fontWeight: 'var(--vetc-list-item-title-weight)' as any,
101
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-list-item-title-color)',
102
- lineHeight: 'var(--vetc-line-height-relaxed)',
103
- overflow: 'hidden',
104
- textOverflow: 'ellipsis',
105
- whiteSpace: 'nowrap',
106
- }}>
107
- {title}
108
- </span>
109
- {description && (
110
- <span style={{
111
- fontSize: 'var(--vetc-list-item-desc-size)',
112
- fontWeight: 'var(--vetc-font-weight-regular)' as any,
113
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-list-item-desc-color)',
114
- lineHeight: 'var(--vetc-line-height-relaxed)',
115
- overflow: 'hidden',
116
- textOverflow: 'ellipsis',
117
- whiteSpace: 'nowrap',
118
- }}>
119
- {description}
120
- </span>
121
- )}
122
- </div>
123
-
124
- {/* Trailing slot */}
125
- {(trailing || arrow) && (
126
- <div style={{
127
- flexShrink: 0,
128
- display: 'flex',
129
- alignItems: 'center',
130
- gap: 'var(--vetc-space-8)',
131
- color: 'var(--vetc-list-item-icon-color)',
132
- }}>
133
- {trailing}
134
- {arrow && <ChevronRightIcon disabled={disabled} />}
135
- </div>
136
- )}
137
- </div>
138
- );
139
- }
140
-
141
- // ── List ──────────────────────────────────────────────────────────────────────
142
- export interface ListProps<T = any> {
143
- items: T[];
144
- renderItem: (item: T, index: number) => React.ReactNode;
145
- bordered?: boolean;
146
- className?: string;
147
- style?: React.CSSProperties;
148
- id?: string;
149
- }
150
-
151
- export function List<T = any>({
152
- items,
153
- renderItem,
154
- bordered = false,
155
- className = '',
156
- style,
157
- id,
158
- }: ListProps<T>) {
159
- return (
160
- <div
161
- id={id}
162
- className={`vetc-list ${className}`}
163
- style={{
164
- border: bordered ? `1px solid var(--vetc-color-border-variant)` : 'none',
165
- borderRadius: bordered ? 'var(--vetc-card-radius)' : 0,
166
- overflow: 'hidden',
167
- backgroundColor: 'var(--vetc-color-bg)',
168
- ...style,
169
- }}
170
- >
171
- {items.map((item, index) => (
172
- <React.Fragment key={index}>
173
- {renderItem(item, index)}
174
- </React.Fragment>
175
- ))}
176
- </div>
177
- );
178
- }
179
-
180
- export default ListItem;
@@ -1,121 +0,0 @@
1
- /**
2
- * VETC Loading — tokenized
3
- */
4
- import React from 'react';
5
- import { Spin, Skeleton } from 'antd';
6
- import { LoadingOutlined } from '@ant-design/icons';
7
-
8
- export type SpinnerSize = 'sm' | 'md' | 'lg';
9
-
10
- const spinnerSizeVarMap: Record<SpinnerSize, string> = {
11
- sm: 'var(--vetc-spinner-size-sm)',
12
- md: 'var(--vetc-spinner-size-md)',
13
- lg: 'var(--vetc-spinner-size-lg)',
14
- };
15
- // antd icon needs numeric fontSize — CSS var won't work directly
16
- const spinnerPxMap: Record<SpinnerSize, number> = { sm: 16, md: 24, lg: 40 };
17
-
18
- export interface SpinnerProps {
19
- size?: SpinnerSize;
20
- color?: string;
21
- fullscreen?: boolean;
22
- tip?: string;
23
- children?: React.ReactNode;
24
- spinning?: boolean;
25
- className?: string;
26
- style?: React.CSSProperties;
27
- }
28
-
29
- export function Spinner({
30
- size = 'md',
31
- color,
32
- fullscreen = false,
33
- tip,
34
- children,
35
- spinning = true,
36
- className = '',
37
- style,
38
- }: SpinnerProps) {
39
- const icon = (
40
- <LoadingOutlined
41
- style={{ fontSize: spinnerPxMap[size], color: color ?? 'var(--vetc-spinner-color)' }}
42
- spin
43
- />
44
- );
45
-
46
- if (children) {
47
- return (
48
- <Spin spinning={spinning} indicator={icon} tip={tip} className={`vetc-spinner ${className}`} style={style}>
49
- {children}
50
- </Spin>
51
- );
52
- }
53
-
54
- if (fullscreen) {
55
- return (
56
- <div
57
- className={`vetc-spinner-overlay ${className}`}
58
- style={{
59
- position: 'fixed',
60
- inset: 0,
61
- display: 'flex',
62
- alignItems: 'center',
63
- justifyContent: 'center',
64
- backgroundColor: 'var(--vetc-color-overlay)',
65
- zIndex: 9999,
66
- ...style,
67
- }}
68
- >
69
- <Spin spinning={spinning} indicator={icon} tip={tip} />
70
- </div>
71
- );
72
- }
73
-
74
- return (
75
- <div
76
- className={`vetc-spinner ${className}`}
77
- style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', ...style }}
78
- >
79
- <Spin spinning={spinning} indicator={icon} tip={tip} />
80
- </div>
81
- );
82
- }
83
-
84
- // ── Skeleton ──────────────────────────────────────────────────────────────────
85
- export interface SkeletonProps {
86
- rows?: number;
87
- avatar?: boolean;
88
- avatarSize?: number;
89
- title?: boolean;
90
- loading?: boolean;
91
- className?: string;
92
- style?: React.CSSProperties;
93
- children?: React.ReactNode;
94
- }
95
-
96
- export function SkeletonLoader({
97
- rows = 3,
98
- avatar = false,
99
- avatarSize = 40,
100
- title = true,
101
- loading = true,
102
- className = '',
103
- style,
104
- children,
105
- }: SkeletonProps) {
106
- return (
107
- <Skeleton
108
- active
109
- loading={loading}
110
- avatar={avatar ? { size: avatarSize, shape: 'circle' } : false}
111
- title={title}
112
- paragraph={{ rows }}
113
- className={`vetc-skeleton ${className}`}
114
- style={style}
115
- >
116
- {children}
117
- </Skeleton>
118
- );
119
- }
120
-
121
- export default Spinner;
@@ -1,116 +0,0 @@
1
- /**
2
- * VETC Modal / Dialog — tokenized
3
- * Figma: Dialog page — width 344px, radius 16px, overlay rgba(0,0,0,0.4)
4
- */
5
- import React from 'react';
6
- import { Modal as AntModal } from 'antd';
7
-
8
- export interface ModalProps {
9
- open: boolean;
10
- title?: React.ReactNode;
11
- children?: React.ReactNode;
12
- /** null = hide footer, undefined = default buttons, ReactNode = custom */
13
- footer?: React.ReactNode | null;
14
- okText?: string;
15
- cancelText?: string;
16
- okDisabled?: boolean;
17
- okLoading?: boolean;
18
- okDanger?: boolean;
19
- onOk?: () => void;
20
- onCancel?: () => void;
21
- maskClosable?: boolean;
22
- closable?: boolean;
23
- width?: number | string;
24
- className?: string;
25
- style?: React.CSSProperties;
26
- id?: string;
27
- }
28
-
29
- /** Shared button style for modal actions */
30
- const modalBtnStyle: React.CSSProperties = {
31
- height: 'var(--vetc-btn-height-lg)',
32
- borderRadius: 'var(--vetc-btn-radius-lg)',
33
- fontWeight: 'var(--vetc-btn-font-weight)' as any,
34
- fontSize: 'var(--vetc-btn-font-size-lg)',
35
- fontFamily: 'var(--vetc-font-family)',
36
- };
37
-
38
- export function Modal({
39
- open, title, children, footer,
40
- okText = 'Xác nhận', cancelText = 'Hủy',
41
- okDisabled = false, okLoading = false, okDanger = false,
42
- onOk, onCancel,
43
- maskClosable = true, closable = true,
44
- width,
45
- className = '', style, id,
46
- }: ModalProps) {
47
- return (
48
- <AntModal
49
- open={open}
50
- title={title}
51
- okText={okText}
52
- cancelText={cancelText}
53
- okButtonProps={{
54
- disabled: okDisabled,
55
- loading: okLoading,
56
- danger: okDanger,
57
- style: modalBtnStyle,
58
- }}
59
- cancelButtonProps={{ style: modalBtnStyle }}
60
- footer={footer === null ? null : footer}
61
- onOk={onOk}
62
- onCancel={onCancel}
63
- maskClosable={maskClosable}
64
- closable={closable}
65
- width={width ?? 'var(--vetc-modal-width)'}
66
- centered
67
- className={`vetc-modal ${className}`}
68
- style={{ fontFamily: 'var(--vetc-font-family)', ...style }}
69
- styles={{
70
- header: {
71
- padding: `var(--vetc-modal-padding) var(--vetc-modal-padding) 0`,
72
- borderBottom: 'none',
73
- },
74
- body: {
75
- padding: `var(--vetc-space-8) var(--vetc-modal-padding) var(--vetc-modal-padding)`,
76
- },
77
- footer: {
78
- padding: `0 var(--vetc-modal-padding) var(--vetc-modal-padding)`,
79
- borderTop: 'none',
80
- },
81
- mask: { backgroundColor: 'var(--vetc-modal-overlay)' },
82
- } as any}
83
- >
84
- {children}
85
- </AntModal>
86
- );
87
- }
88
-
89
- // ── Confirm hook ──────────────────────────────────────────────────────────────
90
- export function useConfirm() {
91
- const confirm = (opts: {
92
- title: string;
93
- content?: React.ReactNode;
94
- okText?: string;
95
- cancelText?: string;
96
- okDanger?: boolean;
97
- onOk?: () => void | Promise<void>;
98
- onCancel?: () => void;
99
- }) => {
100
- AntModal.confirm({
101
- title: opts.title,
102
- content: opts.content,
103
- okText: opts.okText ?? 'Xác nhận',
104
- cancelText: opts.cancelText ?? 'Hủy',
105
- okButtonProps: { danger: opts.okDanger, style: { ...modalBtnStyle, height: 40 } },
106
- cancelButtonProps: { style: { ...modalBtnStyle, height: 40 } },
107
- centered: true,
108
- onOk: opts.onOk,
109
- onCancel: opts.onCancel,
110
- styles: { mask: { backgroundColor: 'var(--vetc-modal-overlay)' } } as any,
111
- });
112
- };
113
- return { confirm };
114
- }
115
-
116
- export default Modal;