@vetc-miniapp/ui-react 0.0.26 → 0.0.27

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 (60) hide show
  1. package/dist/index.js +1 -41
  2. package/package.json +3 -2
  3. package/dist/bridge.js +0 -20
  4. package/dist/components/app.js +0 -34
  5. package/dist/components/avatar/Avatar.js +0 -33
  6. package/dist/components/avatar/index.js +0 -1
  7. package/dist/components/bottom-sheet/BottomSheet.js +0 -70
  8. package/dist/components/bottom-sheet/index.js +0 -1
  9. package/dist/components/button/Button.js +0 -165
  10. package/dist/components/button/index.js +0 -1
  11. package/dist/components/button-group/ButtonGroup.js +0 -21
  12. package/dist/components/button-group/index.js +0 -1
  13. package/dist/components/card/Card.js +0 -35
  14. package/dist/components/card/index.js +0 -1
  15. package/dist/components/checkbox/Checkbox.js +0 -94
  16. package/dist/components/checkbox/index.js +0 -1
  17. package/dist/components/chip/Chip.js +0 -83
  18. package/dist/components/chip/index.js +0 -1
  19. package/dist/components/dialog/Dialog.js +0 -51
  20. package/dist/components/dialog/index.js +0 -1
  21. package/dist/components/divider/Divider.js +0 -18
  22. package/dist/components/divider/index.js +0 -1
  23. package/dist/components/input/Input.js +0 -51
  24. package/dist/components/input/index.js +0 -1
  25. package/dist/components/list/List.js +0 -72
  26. package/dist/components/list/index.js +0 -1
  27. package/dist/components/loading/Loading.js +0 -33
  28. package/dist/components/loading/index.js +0 -1
  29. package/dist/components/modal/Modal.js +0 -50
  30. package/dist/components/modal/index.js +0 -1
  31. package/dist/components/navigation-bar/NavigationBar.js +0 -70
  32. package/dist/components/navigation-bar/index.js +0 -1
  33. package/dist/components/radio/Radio.js +0 -88
  34. package/dist/components/radio/index.js +0 -1
  35. package/dist/components/select/Select.js +0 -30
  36. package/dist/components/select/index.js +0 -1
  37. package/dist/components/switch/Switch.js +0 -81
  38. package/dist/components/switch/index.js +0 -1
  39. package/dist/components/tab-bar/TabBar.js +0 -60
  40. package/dist/components/tab-bar/index.js +0 -1
  41. package/dist/components/textarea/Textarea.js +0 -33
  42. package/dist/components/textarea/index.js +0 -1
  43. package/dist/components/toast/Toast.js +0 -61
  44. package/dist/components/toast/index.js +0 -1
  45. package/dist/components/typography/Typography.js +0 -143
  46. package/dist/components/typography/index.js +0 -1
  47. package/dist/hooks/use-app-pause.js +0 -25
  48. package/dist/hooks/use-app-resume.js +0 -25
  49. package/dist/hooks/use-app-state.js +0 -1
  50. package/dist/hooks/use-did-hide.js +0 -20
  51. package/dist/hooks/use-did-show.js +0 -20
  52. package/dist/hooks/use-listener-scan-qr.js +0 -25
  53. package/dist/hooks/use-navigate.js +0 -23
  54. package/dist/hooks/use-tap-app-bar.js +0 -20
  55. package/dist/styles/VETCProvider.js +0 -124
  56. package/dist/tokens/colors.js +0 -75
  57. package/dist/tokens/index.js +0 -3
  58. package/dist/tokens/spacing.js +0 -56
  59. package/dist/tokens/typography.js +0 -57
  60. package/dist/types/app.js +0 -1
@@ -1,94 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * VETC Checkbox & CheckboxGroup
4
- * Figma: size=20px, touch-area=48px, states: default/hover/pressed/selected/indeterminate/disabled
5
- * variant: neutral (gray-90) | brand (green-40)
6
- */
7
- import { useState } from 'react';
8
- const CheckIcon = () => (_jsx("svg", { width: "12", height: "9", viewBox: "0 0 12 9", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M1 4L4.5 7.5L11 1", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
9
- const IndeterminateIcon = () => (_jsx("svg", { width: "10", height: "2", viewBox: "0 0 10 2", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M1 1H9", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }));
10
- export function Checkbox({ label, description, checked, defaultChecked = false, indeterminate = false, disabled = false, variant = 'brand', onChange, value, id, className = '', style, }) {
11
- const [internalChecked, setInternalChecked] = useState(defaultChecked);
12
- const [hovered, setHovered] = useState(false);
13
- const isControlled = checked !== undefined;
14
- const isChecked = isControlled ? checked : internalChecked;
15
- const isActive = isChecked || indeterminate;
16
- const handleChange = () => {
17
- if (disabled)
18
- return;
19
- const next = !isChecked;
20
- if (!isControlled)
21
- setInternalChecked(next);
22
- onChange?.(next);
23
- };
24
- const accentColor = variant === 'brand'
25
- ? 'var(--vetc-color-brand)'
26
- : 'var(--vetc-gray-90)';
27
- const accentHover = variant === 'brand'
28
- ? 'var(--vetc-color-brand-hover)'
29
- : 'var(--vetc-gray-70)';
30
- let boxBg = 'transparent';
31
- let boxBorder = disabled ? 'var(--vetc-color-border-disabled)' : hovered ? accentColor : 'var(--vetc-color-border)';
32
- let iconColor = 'var(--vetc-white)';
33
- if (isActive) {
34
- boxBg = disabled ? 'var(--vetc-color-bg-disabled)' : accentColor;
35
- boxBorder = disabled ? 'var(--vetc-color-border-disabled)' : hovered ? accentHover : accentColor;
36
- iconColor = disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-white)';
37
- if (disabled)
38
- boxBg = 'var(--vetc-color-bg-disabled)';
39
- }
40
- return (_jsxs("label", { htmlFor: id, className: `vetc-checkbox ${className}`, onMouseEnter: () => !disabled && setHovered(true), onMouseLeave: () => setHovered(false), style: {
41
- display: 'inline-flex',
42
- alignItems: description ? 'flex-start' : 'center',
43
- gap: 'var(--vetc-space-12)',
44
- cursor: disabled ? 'not-allowed' : 'pointer',
45
- userSelect: 'none',
46
- fontFamily: 'var(--vetc-font-family)',
47
- ...style,
48
- }, children: [_jsx("input", { id: id, type: "checkbox", checked: isChecked, disabled: disabled, value: value, onChange: handleChange, style: { position: 'absolute', opacity: 0, width: 0, height: 0, pointerEvents: 'none' }, "aria-checked": indeterminate ? 'mixed' : isChecked }), _jsxs("span", { "aria-hidden": "true", style: {
49
- width: '20px',
50
- height: '20px',
51
- flexShrink: 0,
52
- borderRadius: 'var(--vetc-radius-xs)',
53
- border: `1.5px solid ${boxBorder}`,
54
- backgroundColor: boxBg,
55
- display: 'flex',
56
- alignItems: 'center',
57
- justifyContent: 'center',
58
- transition: 'background-color var(--vetc-transition-fast), border-color var(--vetc-transition-fast)',
59
- color: iconColor,
60
- marginTop: description ? '2px' : 0,
61
- }, children: [isChecked && !indeterminate && _jsx(CheckIcon, {}), indeterminate && _jsx(IndeterminateIcon, {})] }), (label || description) && (_jsxs("span", { style: { display: 'flex', flexDirection: 'column', gap: 'var(--vetc-space-2)' }, children: [label && (_jsx("span", { style: {
62
- fontSize: 'var(--vetc-font-size-base)',
63
- fontWeight: 'var(--vetc-font-weight-regular)',
64
- lineHeight: 'var(--vetc-line-height-relaxed)',
65
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-color-text-primary)',
66
- }, children: label })), description && (_jsx("span", { style: {
67
- fontSize: 'var(--vetc-font-size-sm)',
68
- fontWeight: 'var(--vetc-font-weight-regular)',
69
- lineHeight: 'var(--vetc-line-height-relaxed)',
70
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-color-text-secondary)',
71
- }, children: description }))] }))] }));
72
- }
73
- export function CheckboxGroup({ options, value, defaultValue = [], direction = 'vertical', disabled = false, variant = 'brand', onChange, className = '', style, }) {
74
- const [internalValue, setInternalValue] = useState(defaultValue);
75
- const isControlled = value !== undefined;
76
- const currentValue = isControlled ? value : internalValue;
77
- const handleChange = (optValue, checked) => {
78
- const next = checked
79
- ? [...currentValue, optValue]
80
- : currentValue.filter((v) => v !== optValue);
81
- if (!isControlled)
82
- setInternalValue(next);
83
- onChange?.(next);
84
- };
85
- return (_jsx("div", { className: `vetc-checkbox-group ${className}`, style: {
86
- display: 'flex',
87
- flexDirection: direction === 'vertical' ? 'column' : 'row',
88
- flexWrap: direction === 'horizontal' ? 'wrap' : undefined,
89
- gap: direction === 'vertical' ? 'var(--vetc-space-12)' : 'var(--vetc-space-16)',
90
- fontFamily: 'var(--vetc-font-family)',
91
- ...style,
92
- }, children: options.map((opt) => (_jsx(Checkbox, { label: opt.label, description: opt.description, checked: currentValue.includes(opt.value), disabled: opt.disabled ?? disabled, variant: variant, value: opt.value, onChange: (chk) => handleChange(opt.value, chk) }, String(opt.value)))) }));
93
- }
94
- export default Checkbox;
@@ -1 +0,0 @@
1
- export { Checkbox, CheckboxGroup } from './Checkbox';
@@ -1,83 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Tag as AntTag } from 'antd';
3
- // Map type → CSS vars (from tokens.css --vetc-chip-*)
4
- const typeVarMap = {
5
- default: {
6
- bg: 'var(--vetc-chip-default-bg)',
7
- border: 'var(--vetc-chip-default-border)',
8
- text: 'var(--vetc-chip-default-text)',
9
- filledBg: 'var(--vetc-chip-default-filled-bg)',
10
- },
11
- brand: {
12
- bg: 'var(--vetc-chip-brand-bg)',
13
- border: 'var(--vetc-chip-brand-border)',
14
- text: 'var(--vetc-chip-brand-text)',
15
- filledBg: 'var(--vetc-chip-brand-filled-bg)',
16
- },
17
- positive: {
18
- bg: 'var(--vetc-chip-positive-bg)',
19
- border: 'var(--vetc-chip-positive-border)',
20
- text: 'var(--vetc-chip-positive-text)',
21
- filledBg: 'var(--vetc-chip-positive-filled-bg)',
22
- },
23
- negative: {
24
- bg: 'var(--vetc-chip-negative-bg)',
25
- border: 'var(--vetc-chip-negative-border)',
26
- text: 'var(--vetc-chip-negative-text)',
27
- filledBg: 'var(--vetc-chip-negative-filled-bg)',
28
- },
29
- warning: {
30
- bg: 'var(--vetc-chip-warning-bg)',
31
- border: 'var(--vetc-chip-warning-border)',
32
- text: 'var(--vetc-chip-warning-text)',
33
- filledBg: 'var(--vetc-chip-warning-filled-bg)',
34
- },
35
- info: {
36
- bg: 'var(--vetc-chip-info-bg)',
37
- border: 'var(--vetc-chip-info-border)',
38
- text: 'var(--vetc-chip-info-text)',
39
- filledBg: 'var(--vetc-chip-info-filled-bg)',
40
- },
41
- };
42
- export function Chip({ style = 'tinted', type = 'default', shape = 'pill', size = 'md', icon, closable = false, onClose, onClick, children, className = '', css, id, }) {
43
- const vars = typeVarMap[type];
44
- const height = size === 'md' ? 'var(--vetc-chip-height-md)' : 'var(--vetc-chip-height-sm)';
45
- const paddingX = size === 'md' ? 'var(--vetc-chip-padding-x-md)' : 'var(--vetc-chip-padding-x-sm)';
46
- const fontSize = size === 'md' ? 'var(--vetc-chip-font-size-md)' : 'var(--vetc-chip-font-size-sm)';
47
- const fontWeight = 'var(--vetc-chip-font-weight)';
48
- const borderRadius = shape === 'pill' ? 'var(--vetc-chip-radius-pill)' : 'var(--vetc-chip-radius-rounded)';
49
- let bg = vars.bg;
50
- let border = `1px solid ${vars.border}`;
51
- let textColor = vars.text;
52
- if (style === 'filled') {
53
- bg = vars.filledBg;
54
- border = 'none';
55
- textColor = 'var(--vetc-white)';
56
- }
57
- else if (style === 'outlined') {
58
- bg = 'transparent';
59
- border = `1px solid ${vars.border}`;
60
- textColor = vars.text;
61
- }
62
- // tinted: bg + border (default)
63
- return (_jsx(AntTag, { id: id, closable: closable, onClose: (e) => { e.preventDefault(); onClose?.(); }, onClick: onClick, icon: icon, className: `vetc-chip vetc-chip--${style} vetc-chip--${type} ${className}`, style: {
64
- display: 'inline-flex',
65
- alignItems: 'center',
66
- height,
67
- padding: `0 ${paddingX}`,
68
- borderRadius,
69
- background: bg,
70
- border,
71
- color: textColor,
72
- fontSize,
73
- fontWeight: fontWeight,
74
- fontFamily: 'var(--vetc-font-family)',
75
- cursor: onClick ? 'pointer' : 'default',
76
- userSelect: 'none',
77
- lineHeight: 1,
78
- gap: 'var(--vetc-space-4)',
79
- transition: 'var(--vetc-transition-fast)',
80
- ...css,
81
- }, children: children }));
82
- }
83
- export default Chip;
@@ -1 +0,0 @@
1
- export { Chip } from './Chip';
@@ -1,51 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect } from 'react';
3
- import { createPortal } from 'react-dom';
4
- import { Button } from '../button';
5
- export function Dialog({ open, image, title, description, okText = 'Xác nhận', cancelText = 'Hủy', okDisabled = false, okLoading = false, okDanger = false, onOk, onCancel, maskClosable = true, }) {
6
- useEffect(() => {
7
- if (open)
8
- document.body.style.overflow = 'hidden';
9
- else
10
- document.body.style.overflow = '';
11
- return () => { document.body.style.overflow = ''; };
12
- }, [open]);
13
- if (!open)
14
- return null;
15
- const hasContent = title || description;
16
- return _jsx(_Fragment, { children: createPortal(_jsx("div", { style: {
17
- position: 'fixed',
18
- inset: 0,
19
- backgroundColor: 'var(--vetc-dialog-overlay)',
20
- display: 'flex',
21
- alignItems: 'center',
22
- justifyContent: 'center',
23
- zIndex: 1000,
24
- padding: '0 var(--vetc-space-24)',
25
- }, onClick: maskClosable ? onCancel : undefined, children: _jsxs("div", { style: {
26
- width: '100%',
27
- maxWidth: 'var(--vetc-dialog-width)',
28
- backgroundColor: 'var(--vetc-dialog-bg)',
29
- borderRadius: 'var(--vetc-dialog-radius)',
30
- overflow: 'hidden',
31
- boxShadow: 'var(--vetc-shadow-lg)',
32
- fontFamily: 'var(--vetc-font-family)',
33
- }, onClick: e => e.stopPropagation(), children: [image && (_jsx("div", { style: { width: '100%', height: 'var(--vetc-dialog-image-height)', overflow: 'hidden', flexShrink: 0 }, children: _jsx("img", { src: image, alt: "", style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } }) })), hasContent && (_jsxs("div", { style: { padding: 'var(--vetc-dialog-padding)' }, children: [title && (_jsx("div", { style: {
34
- fontSize: 'var(--vetc-dialog-title-size)',
35
- fontWeight: 'var(--vetc-dialog-title-weight)',
36
- color: 'var(--vetc-color-text-primary)',
37
- lineHeight: 'var(--vetc-line-height-relaxed)',
38
- marginBottom: description ? 'var(--vetc-space-8)' : 0,
39
- }, children: title })), description && (_jsx("div", { style: {
40
- fontSize: 'var(--vetc-dialog-desc-size)',
41
- color: 'var(--vetc-dialog-desc-color)',
42
- lineHeight: 'var(--vetc-line-height-relaxed)',
43
- }, children: description }))] })), _jsxs("div", { style: {
44
- display: 'flex',
45
- gap: 'var(--vetc-dialog-btn-gap)',
46
- padding: hasContent
47
- ? `0 var(--vetc-dialog-padding) var(--vetc-dialog-padding)`
48
- : 'var(--vetc-dialog-padding)',
49
- }, children: [onCancel && (_jsx(Button, { block: true, style: "outlined", variant: "neutral", onClick: onCancel, children: cancelText })), _jsx(Button, { block: true, style: okDanger ? 'danger' : 'filled', loading: okLoading, disabled: okDisabled, onClick: onOk, children: okText })] })] }) }), document.body) });
50
- }
51
- export default Dialog;
@@ -1 +0,0 @@
1
- export { Dialog } from './Dialog';
@@ -1,18 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Divider as AntDivider } from 'antd';
3
- export function Divider({ orientation = 'horizontal', label, labelAlign = 'center', thickness = 1, color, margin, className = '', style, }) {
4
- const isVertical = orientation === 'vertical';
5
- return (_jsx(AntDivider, { type: isVertical ? 'vertical' : 'horizontal', orientationMargin: labelAlign !== 'center' ? 0 : undefined, orientation: labelAlign !== 'center' ? labelAlign : undefined, className: `vetc-divider ${className}`, style: {
6
- borderColor: color ?? 'var(--vetc-divider-color)',
7
- borderTopWidth: !isVertical ? thickness : undefined,
8
- borderLeftWidth: isVertical ? thickness : undefined,
9
- margin: margin ?? (isVertical
10
- ? `0 var(--vetc-divider-margin-v)`
11
- : `var(--vetc-divider-margin-h) 0`),
12
- fontFamily: 'var(--vetc-font-family)',
13
- fontSize: 'var(--vetc-divider-label-size)',
14
- color: 'var(--vetc-divider-label-color)',
15
- ...style,
16
- }, children: label }));
17
- }
18
- export default Divider;
@@ -1 +0,0 @@
1
- export { Divider } from './Divider';
@@ -1,51 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Input as AntInput } from 'antd';
3
- // ── Shared label/helper sub-components ───────────────────────────────────────
4
- function FieldLabel({ htmlFor, required, disabled, children }) {
5
- return (_jsxs("label", { htmlFor: htmlFor, style: {
6
- fontFamily: 'var(--vetc-font-family)',
7
- fontSize: 'var(--vetc-input-label-font-size)',
8
- fontWeight: 'var(--vetc-input-label-font-weight)',
9
- lineHeight: 'var(--vetc-line-height-relaxed)',
10
- color: disabled
11
- ? 'var(--vetc-color-text-disabled)'
12
- : 'var(--vetc-input-label-color)',
13
- display: 'block',
14
- }, children: [children, required && (_jsx("span", { style: { color: 'var(--vetc-color-negative)', marginLeft: 'var(--vetc-space-2)' }, children: "*" }))] }));
15
- }
16
- function HelperText({ color, children }) {
17
- return (_jsx("span", { style: {
18
- fontFamily: 'var(--vetc-font-family)',
19
- fontSize: 'var(--vetc-input-helper-font-size)',
20
- lineHeight: 'var(--vetc-line-height-relaxed)',
21
- color,
22
- }, children: children }));
23
- }
24
- const inputFieldStyle = {
25
- height: 'var(--vetc-input-height)',
26
- borderRadius: 'var(--vetc-input-radius)',
27
- paddingInline: 'var(--vetc-input-padding-x)',
28
- fontSize: 'var(--vetc-input-font-size)',
29
- fontFamily: 'var(--vetc-font-family)',
30
- };
31
- export function Input({ label, placeholder, value, defaultValue, helperText, status = 'default', error, disabled = false, readOnly = false, maxLength, showCount = false, prefix, suffix, allowClear = false, onChange, onFocus, onBlur, onPressEnter, type = 'text', id, name, className = '', style, required = false, }) {
32
- const hasError = !!error;
33
- const resolvedStatus = hasError ? 'error' : status;
34
- const antStatus = resolvedStatus === 'error' ? 'error' : undefined;
35
- const helperMsg = error ?? helperText;
36
- const helperColor = resolvedStatus === 'error'
37
- ? 'var(--vetc-input-helper-color-error)'
38
- : 'var(--vetc-input-helper-color)';
39
- return (_jsxs("div", { className: `vetc-input-wrapper ${className}`, style: { display: 'flex', flexDirection: 'column', gap: 'var(--vetc-input-gap)', ...style }, children: [label && _jsx(FieldLabel, { htmlFor: id, required: required, disabled: disabled, children: label }), _jsx(AntInput, { id: id, name: name, type: type, value: value, defaultValue: defaultValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, maxLength: maxLength, showCount: showCount, prefix: prefix, suffix: suffix, allowClear: allowClear, status: antStatus, onChange: (e) => onChange?.(e.target.value, e), onFocus: onFocus, onBlur: onBlur, onPressEnter: onPressEnter, style: inputFieldStyle }), helperMsg && _jsx(HelperText, { color: helperColor, children: helperMsg })] }));
40
- }
41
- export function PasswordInput({ label, placeholder, value, defaultValue, helperText, status = 'default', error, disabled = false, readOnly = false, maxLength, onChange, onFocus, onBlur, id, name, className = '', style, required = false, }) {
42
- const hasError = !!error;
43
- const resolvedStatus = hasError ? 'error' : status;
44
- const antStatus = resolvedStatus === 'error' ? 'error' : undefined;
45
- const helperMsg = error ?? helperText;
46
- const helperColor = hasError
47
- ? 'var(--vetc-input-helper-color-error)'
48
- : 'var(--vetc-input-helper-color)';
49
- return (_jsxs("div", { className: `vetc-input-wrapper ${className ?? ''}`, style: { display: 'flex', flexDirection: 'column', gap: 'var(--vetc-input-gap)', ...style }, children: [label && _jsx(FieldLabel, { htmlFor: id, required: required, disabled: disabled, children: label }), _jsx(AntInput.Password, { id: id, name: name, value: value, defaultValue: defaultValue, placeholder: placeholder, disabled: disabled, readOnly: readOnly, maxLength: maxLength, status: antStatus, onChange: (e) => onChange?.(e.target.value, e), onFocus: onFocus, onBlur: onBlur, style: inputFieldStyle }), helperMsg && _jsx(HelperText, { color: helperColor, children: helperMsg })] }));
50
- }
51
- export default Input;
@@ -1 +0,0 @@
1
- export { Input, PasswordInput } from './Input';
@@ -1,72 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * VETC List & ListItem — tokenized
4
- * Figma: List item page — height 56px, padding-x 16px
5
- */
6
- import React from 'react';
7
- // ── ChevronRight Icon ─────────────────────────────────────────────────────────
8
- function ChevronRightIcon({ disabled }) {
9
- return (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M7.5 5L12.5 10L7.5 15", stroke: disabled ? 'var(--vetc-color-icon-disabled)' : 'var(--vetc-color-icon-muted)', strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
10
- }
11
- export function ListItem({ leading, title, description, trailing, arrow = false, divider = true, onClick, disabled = false, className = '', style, id, }) {
12
- const isClickable = !!onClick && !disabled;
13
- return (_jsxs("div", { id: id, role: isClickable ? 'button' : undefined, tabIndex: isClickable ? 0 : undefined, className: `vetc-list-item ${className}`, onClick: !disabled ? onClick : undefined, onKeyDown: isClickable ? (e) => { if (e.key === 'Enter' || e.key === ' ')
14
- onClick?.(); } : undefined, style: {
15
- display: 'flex',
16
- alignItems: 'center',
17
- minHeight: 'var(--vetc-list-item-height)',
18
- padding: `0 var(--vetc-list-item-padding-x)`,
19
- gap: 'var(--vetc-list-item-gap)',
20
- cursor: isClickable ? 'pointer' : 'default',
21
- borderBottom: divider ? `1px solid var(--vetc-list-item-border)` : 'none',
22
- backgroundColor: 'var(--vetc-list-item-bg)',
23
- opacity: disabled ? 0.5 : 1,
24
- fontFamily: 'var(--vetc-font-family)',
25
- transition: `background-color var(--vetc-transition-fast)`,
26
- outline: 'none',
27
- ...style,
28
- }, onMouseEnter: (e) => {
29
- if (isClickable)
30
- e.currentTarget.style.backgroundColor = 'var(--vetc-list-item-bg-hover)';
31
- }, onMouseLeave: (e) => {
32
- if (isClickable)
33
- e.currentTarget.style.backgroundColor = 'var(--vetc-list-item-bg)';
34
- }, children: [leading && (_jsx("div", { style: {
35
- flexShrink: 0,
36
- display: 'flex',
37
- alignItems: 'center',
38
- color: 'var(--vetc-list-item-icon-color)',
39
- }, children: leading })), _jsxs("div", { style: { flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 'var(--vetc-space-2)' }, children: [_jsx("span", { style: {
40
- fontSize: 'var(--vetc-list-item-title-size)',
41
- fontWeight: 'var(--vetc-list-item-title-weight)',
42
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-list-item-title-color)',
43
- lineHeight: 'var(--vetc-line-height-relaxed)',
44
- overflow: 'hidden',
45
- textOverflow: 'ellipsis',
46
- whiteSpace: 'nowrap',
47
- }, children: title }), description && (_jsx("span", { style: {
48
- fontSize: 'var(--vetc-list-item-desc-size)',
49
- fontWeight: 'var(--vetc-font-weight-regular)',
50
- color: disabled ? 'var(--vetc-color-text-disabled)' : 'var(--vetc-list-item-desc-color)',
51
- lineHeight: 'var(--vetc-line-height-relaxed)',
52
- overflow: 'hidden',
53
- textOverflow: 'ellipsis',
54
- whiteSpace: 'nowrap',
55
- }, children: description }))] }), (trailing || arrow) && (_jsxs("div", { style: {
56
- flexShrink: 0,
57
- display: 'flex',
58
- alignItems: 'center',
59
- gap: 'var(--vetc-space-8)',
60
- color: 'var(--vetc-list-item-icon-color)',
61
- }, children: [trailing, arrow && _jsx(ChevronRightIcon, { disabled: disabled })] }))] }));
62
- }
63
- export function List({ items, renderItem, bordered = false, className = '', style, id, }) {
64
- return (_jsx("div", { id: id, className: `vetc-list ${className}`, style: {
65
- border: bordered ? `1px solid var(--vetc-color-border-variant)` : 'none',
66
- borderRadius: bordered ? 'var(--vetc-card-radius)' : 0,
67
- overflow: 'hidden',
68
- backgroundColor: 'var(--vetc-color-bg)',
69
- ...style,
70
- }, children: items.map((item, index) => (_jsx(React.Fragment, { children: renderItem(item, index) }, index))) }));
71
- }
72
- export default ListItem;
@@ -1 +0,0 @@
1
- export { List, ListItem } from './List';
@@ -1,33 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Spin, Skeleton } from 'antd';
3
- import { LoadingOutlined } from '@ant-design/icons';
4
- const spinnerSizeVarMap = {
5
- sm: 'var(--vetc-spinner-size-sm)',
6
- md: 'var(--vetc-spinner-size-md)',
7
- lg: 'var(--vetc-spinner-size-lg)',
8
- };
9
- // antd icon needs numeric fontSize — CSS var won't work directly
10
- const spinnerPxMap = { sm: 16, md: 24, lg: 40 };
11
- export function Spinner({ size = 'md', color, fullscreen = false, tip, children, spinning = true, className = '', style, }) {
12
- const icon = (_jsx(LoadingOutlined, { style: { fontSize: spinnerPxMap[size], color: color ?? 'var(--vetc-spinner-color)' }, spin: true }));
13
- if (children) {
14
- return (_jsx(Spin, { spinning: spinning, indicator: icon, tip: tip, className: `vetc-spinner ${className}`, style: style, children: children }));
15
- }
16
- if (fullscreen) {
17
- return (_jsx("div", { className: `vetc-spinner-overlay ${className}`, style: {
18
- position: 'fixed',
19
- inset: 0,
20
- display: 'flex',
21
- alignItems: 'center',
22
- justifyContent: 'center',
23
- backgroundColor: 'var(--vetc-color-overlay)',
24
- zIndex: 9999,
25
- ...style,
26
- }, children: _jsx(Spin, { spinning: spinning, indicator: icon, tip: tip }) }));
27
- }
28
- return (_jsx("div", { className: `vetc-spinner ${className}`, style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', ...style }, children: _jsx(Spin, { spinning: spinning, indicator: icon, tip: tip }) }));
29
- }
30
- export function SkeletonLoader({ rows = 3, avatar = false, avatarSize = 40, title = true, loading = true, className = '', style, children, }) {
31
- return (_jsx(Skeleton, { active: true, loading: loading, avatar: avatar ? { size: avatarSize, shape: 'circle' } : false, title: title, paragraph: { rows }, className: `vetc-skeleton ${className}`, style: style, children: children }));
32
- }
33
- export default Spinner;
@@ -1 +0,0 @@
1
- export { Spinner, SkeletonLoader } from './Loading';
@@ -1,50 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Modal as AntModal } from 'antd';
3
- /** Shared button style for modal actions */
4
- const modalBtnStyle = {
5
- height: 'var(--vetc-btn-height-lg)',
6
- borderRadius: 'var(--vetc-btn-radius-lg)',
7
- fontWeight: 'var(--vetc-btn-font-weight)',
8
- fontSize: 'var(--vetc-btn-font-size-lg)',
9
- fontFamily: 'var(--vetc-font-family)',
10
- };
11
- export function Modal({ open, title, children, footer, okText = 'Xác nhận', cancelText = 'Hủy', okDisabled = false, okLoading = false, okDanger = false, onOk, onCancel, maskClosable = true, closable = true, width, className = '', style, id, }) {
12
- return (_jsx(AntModal, { open: open, title: title, okText: okText, cancelText: cancelText, okButtonProps: {
13
- disabled: okDisabled,
14
- loading: okLoading,
15
- danger: okDanger,
16
- style: modalBtnStyle,
17
- }, cancelButtonProps: { style: modalBtnStyle }, footer: footer === null ? null : footer, onOk: onOk, onCancel: onCancel, maskClosable: maskClosable, closable: closable, width: width ?? 'var(--vetc-modal-width)', centered: true, className: `vetc-modal ${className}`, style: { fontFamily: 'var(--vetc-font-family)', ...style }, styles: {
18
- header: {
19
- padding: `var(--vetc-modal-padding) var(--vetc-modal-padding) 0`,
20
- borderBottom: 'none',
21
- },
22
- body: {
23
- padding: `var(--vetc-space-8) var(--vetc-modal-padding) var(--vetc-modal-padding)`,
24
- },
25
- footer: {
26
- padding: `0 var(--vetc-modal-padding) var(--vetc-modal-padding)`,
27
- borderTop: 'none',
28
- },
29
- mask: { backgroundColor: 'var(--vetc-modal-overlay)' },
30
- }, children: children }));
31
- }
32
- // ── Confirm hook ──────────────────────────────────────────────────────────────
33
- export function useConfirm() {
34
- const confirm = (opts) => {
35
- AntModal.confirm({
36
- title: opts.title,
37
- content: opts.content,
38
- okText: opts.okText ?? 'Xác nhận',
39
- cancelText: opts.cancelText ?? 'Hủy',
40
- okButtonProps: { danger: opts.okDanger, style: { ...modalBtnStyle, height: 40 } },
41
- cancelButtonProps: { style: { ...modalBtnStyle, height: 40 } },
42
- centered: true,
43
- onOk: opts.onOk,
44
- onCancel: opts.onCancel,
45
- styles: { mask: { backgroundColor: 'var(--vetc-modal-overlay)' } },
46
- });
47
- };
48
- return { confirm };
49
- }
50
- export default Modal;
@@ -1 +0,0 @@
1
- export { Modal, useConfirm } from './Modal';
@@ -1,70 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- function BackIcon() {
3
- return (_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: _jsx("path", { d: "M15 19L8 12L15 5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }));
4
- }
5
- function CloseIcon() {
6
- return (_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" }) }));
7
- }
8
- const iconBtnStyle = {
9
- background: 'none',
10
- border: 'none',
11
- cursor: 'pointer',
12
- width: 'var(--vetc-nav-icon-btn-size)',
13
- height: 'var(--vetc-nav-icon-btn-size)',
14
- display: 'flex',
15
- alignItems: 'center',
16
- justifyContent: 'center',
17
- borderRadius: 'var(--vetc-radius-pill)',
18
- color: 'var(--vetc-nav-icon-color)',
19
- flexShrink: 0,
20
- padding: 0,
21
- transition: 'background-color var(--vetc-transition-fast)',
22
- };
23
- export function NavigationBar({ title, leading = 'none', leadingIcon, onLeadingPress, actions = [], leftSlot, rightSlot, backgroundColor, divider = true, transparent = false, className = '', style, id,
24
- // deprecated props
25
- showBack, backIcon, onBack, left, right, }) {
26
- // Backward compat
27
- const resolvedLeading = showBack ? 'back' : leading;
28
- const resolvedLeadingIcon = leadingIcon ?? backIcon;
29
- const resolvedOnLeading = onLeadingPress ?? onBack;
30
- const renderLeadingButton = () => {
31
- if (leftSlot !== undefined || left !== undefined)
32
- return leftSlot ?? left;
33
- if (resolvedLeading === 'none')
34
- return null;
35
- const icon = resolvedLeadingIcon ?? (resolvedLeading === 'close' ? _jsx(CloseIcon, {}) : _jsx(BackIcon, {}));
36
- const label = resolvedLeading === 'close' ? 'Đóng' : 'Quay lại';
37
- return (_jsx("button", { onClick: resolvedOnLeading, style: iconBtnStyle, "aria-label": label, children: icon }));
38
- };
39
- return (_jsxs("header", { id: id, className: `vetc-nav-bar ${className}`, style: {
40
- display: 'flex',
41
- alignItems: 'center',
42
- height: 'var(--vetc-nav-height)',
43
- padding: `0 var(--vetc-nav-padding-x)`,
44
- backgroundColor: transparent ? 'transparent' : (backgroundColor ?? 'var(--vetc-nav-bg)'),
45
- borderBottom: divider && !transparent ? `1px solid var(--vetc-nav-border)` : 'none',
46
- fontFamily: 'var(--vetc-font-family)',
47
- position: 'relative',
48
- ...style,
49
- }, children: [_jsx("div", { style: { display: 'flex', alignItems: 'center', minWidth: 'var(--vetc-nav-icon-btn-size)', zIndex: 1 }, children: renderLeadingButton() }), title && (_jsx("div", { style: {
50
- position: 'absolute',
51
- left: 0,
52
- right: 0,
53
- display: 'flex',
54
- alignItems: 'center',
55
- justifyContent: 'center',
56
- pointerEvents: 'none',
57
- }, children: _jsx("span", { style: {
58
- fontSize: 'var(--vetc-nav-title-size)',
59
- fontWeight: 'var(--vetc-nav-title-weight)',
60
- color: 'var(--vetc-nav-title-color)',
61
- lineHeight: 'var(--vetc-line-height-normal)',
62
- maxWidth: '60%',
63
- overflow: 'hidden',
64
- textOverflow: 'ellipsis',
65
- whiteSpace: 'nowrap',
66
- }, children: title }) })), _jsx("div", { style: { display: 'flex', alignItems: 'center', gap: 'var(--vetc-space-4)', marginLeft: 'auto', zIndex: 1 }, children: rightSlot !== undefined || right !== undefined
67
- ? (rightSlot ?? right)
68
- : actions.slice(0, 2).map((action, i) => (_jsx("button", { id: action.id, onClick: action.onClick, disabled: action.disabled, "aria-label": action.label, style: { ...iconBtnStyle, opacity: action.disabled ? 0.4 : 1 }, children: action.icon }, i))) })] }));
69
- }
70
- export default NavigationBar;
@@ -1 +0,0 @@
1
- export { NavigationBar } from './NavigationBar';