automoby-kit 1.0.12 → 1.0.13
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.
- package/README.md +7 -71
- package/dist/cjs/Accordion.js +59 -1
- package/dist/cjs/Backdrop.js +26 -1
- package/dist/cjs/Breadcrumb.js +61 -1
- package/dist/cjs/Button.js +48 -1
- package/dist/cjs/Chips.js +117 -1
- package/dist/cjs/Divider.js +23 -1
- package/dist/cjs/Drawer.js +112 -1
- package/dist/cjs/Input.js +84 -1
- package/dist/cjs/Menu.js +122 -1
- package/dist/cjs/Pagination.js +188 -1
- package/dist/cjs/RadioGroup.js +73 -1
- package/dist/cjs/Tabs.js +54 -1
- package/dist/cjs/Typography.js +63 -1
- package/dist/cjs/chevron-left-COj1qGVo.js +16 -0
- package/dist/{types → cjs}/components/Accordion/Accordion.d.ts +2 -2
- package/dist/{types → cjs}/components/Breadcrumb/Breadcrumb.d.ts +2 -2
- package/dist/{types → cjs}/components/Chips/Chips.d.ts +5 -5
- package/dist/{types → cjs}/components/Drawer/Drawer.d.ts +2 -2
- package/dist/{types → cjs}/components/Input/Input.d.ts +4 -4
- package/dist/{types → cjs}/components/Input/Input.stories.d.ts +1 -1
- package/dist/{types → cjs}/components/Pagination/Pagination.d.ts +2 -2
- package/dist/{types → cjs}/components/RadioGroup/RadioGroup.d.ts +2 -2
- package/dist/cjs/components/Select/Select.d.ts +17 -0
- package/dist/cjs/components/Select/Select.stories.d.ts +104 -0
- package/dist/{types → cjs}/components/Tabs/Tabs.d.ts +2 -2
- package/dist/{types → cjs}/contexts/MobileContext.d.ts +1 -0
- package/dist/cjs/contexts.js +1491 -1
- package/dist/cjs/createLucideIcon-BTjFFtDc.js +114 -0
- package/dist/cjs/index.d.ts +34 -0
- package/dist/cjs/index.js +52 -1
- package/dist/cjs/utils.js +9 -1
- package/dist/esm/Accordion.js +57 -1
- package/dist/esm/Backdrop.js +24 -1
- package/dist/esm/Breadcrumb.js +59 -1
- package/dist/esm/Button.js +46 -1
- package/dist/esm/Chips.js +112 -1
- package/dist/esm/Divider.js +21 -1
- package/dist/esm/Drawer.js +110 -1
- package/dist/esm/Input.js +82 -1
- package/dist/esm/Menu.js +120 -1
- package/dist/esm/Pagination.js +186 -1
- package/dist/esm/RadioGroup.js +71 -1
- package/dist/esm/Tabs.js +52 -1
- package/dist/esm/Typography.js +60 -1
- package/dist/esm/components/Accordion/Accordion.d.ts +25 -0
- package/dist/esm/components/Accordion/Accordion.stories.d.ts +175 -0
- package/dist/esm/components/Backdrop/Backdrop.d.ts +20 -0
- package/dist/esm/components/Backdrop/Backdrop.stories.d.ts +9 -0
- package/dist/esm/components/Breadcrumb/Breadcrumb.d.ts +16 -0
- package/dist/esm/components/Breadcrumb/Breadcrumb.stories.d.ts +177 -0
- package/dist/esm/components/Button/Button.d.ts +13 -0
- package/dist/esm/components/Button/Button.stories.d.ts +100 -0
- package/dist/esm/components/Chips/Chips.d.ts +37 -0
- package/dist/esm/components/Chips/Chips.stories.d.ts +90 -0
- package/dist/esm/components/Divider/Divider.d.ts +25 -0
- package/dist/esm/components/Divider/Divider.stories.d.ts +88 -0
- package/dist/esm/components/Drawer/Drawer.d.ts +16 -0
- package/dist/esm/components/Drawer/Drawer.stories.d.ts +128 -0
- package/dist/esm/components/Input/Input.d.ts +24 -0
- package/dist/esm/components/Input/Input.stories.d.ts +131 -0
- package/dist/esm/components/Menu/Menu.d.ts +39 -0
- package/dist/esm/components/Menu/Menu.stories.d.ts +89 -0
- package/dist/esm/components/Pagination/Pagination.d.ts +12 -0
- package/dist/esm/components/Pagination/Pagination.stories.d.ts +76 -0
- package/dist/esm/components/RadioGroup/RadioGroup.d.ts +55 -0
- package/dist/esm/components/RadioGroup/RadioGroup.stories.d.ts +86 -0
- package/dist/esm/components/Select/Select.d.ts +17 -0
- package/dist/esm/components/Select/Select.stories.d.ts +104 -0
- package/dist/esm/components/Tabs/Tabs.d.ts +43 -0
- package/dist/esm/components/Tabs/Tabs.stories.d.ts +66 -0
- package/dist/esm/components/Typography/Typography.d.ts +9 -0
- package/dist/esm/components/Typography/Typography.stories.d.ts +57 -0
- package/dist/esm/contexts/MobileContext.d.ts +13 -0
- package/dist/esm/contexts.js +1485 -1
- package/dist/esm/index.d.ts +34 -0
- package/dist/esm/index.js +37 -1
- package/dist/esm/utils/cn.d.ts +2 -0
- package/dist/esm/utils.js +7 -1
- package/package.json +2 -7
- package/dist/cjs/ProtectedComponent.js +0 -1
- package/dist/cjs/licensing.js +0 -1
- package/dist/esm/ProtectedComponent.js +0 -1
- package/dist/esm/licensing.js +0 -1
- package/dist/types/Accordion.js +0 -54
- package/dist/types/Backdrop.js +0 -24
- package/dist/types/Breadcrumb.js +0 -56
- package/dist/types/Button.js +0 -46
- package/dist/types/Chips.js +0 -109
- package/dist/types/Divider.js +0 -21
- package/dist/types/Drawer.js +0 -107
- package/dist/types/Input.js +0 -78
- package/dist/types/Menu.js +0 -120
- package/dist/types/Pagination.js +0 -183
- package/dist/types/ProtectedComponent.js +0 -33
- package/dist/types/RadioGroup.js +0 -68
- package/dist/types/Tabs.js +0 -49
- package/dist/types/Typography.js +0 -60
- package/dist/types/components/ProtectedComponent.d.ts +0 -5
- package/dist/types/contexts.js +0 -1478
- package/dist/types/index.d.ts +0 -36
- package/dist/types/index.js +0 -38
- package/dist/types/licensing/LicenseManager.d.ts +0 -41
- package/dist/types/licensing/index.d.ts +0 -30
- package/dist/types/licensing.js +0 -125
- package/dist/types/utils.js +0 -7
- /package/dist/{types → cjs}/components/Accordion/Accordion.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Backdrop/Backdrop.d.ts +0 -0
- /package/dist/{types → cjs}/components/Backdrop/Backdrop.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Breadcrumb/Breadcrumb.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Button/Button.d.ts +0 -0
- /package/dist/{types → cjs}/components/Button/Button.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Chips/Chips.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Divider/Divider.d.ts +0 -0
- /package/dist/{types → cjs}/components/Divider/Divider.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Drawer/Drawer.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Menu/Menu.d.ts +0 -0
- /package/dist/{types → cjs}/components/Menu/Menu.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Pagination/Pagination.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/RadioGroup/RadioGroup.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Tabs/Tabs.stories.d.ts +0 -0
- /package/dist/{types → cjs}/components/Typography/Typography.d.ts +0 -0
- /package/dist/{types → cjs}/components/Typography/Typography.stories.d.ts +0 -0
- /package/dist/{types → cjs}/utils/cn.d.ts +0 -0
- /package/dist/{types → esm}/chevron-left-Ck6O99eF.js +0 -0
- /package/dist/{types → esm}/createLucideIcon-D-q73LTT.js +0 -0
package/dist/esm/Drawer.js
CHANGED
|
@@ -1 +1,110 @@
|
|
|
1
|
-
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import React, { useRef, useEffect } from 'react';
|
|
3
|
+
import cn from './utils.js';
|
|
4
|
+
import { useMobile } from './contexts.js';
|
|
5
|
+
|
|
6
|
+
const Drawer = React.forwardRef(({ children, direction = 'bottom', fullScreen = false, isOpen = false, onClose, className, isMobile, ...props }, ref) => {
|
|
7
|
+
const detectedIsMobile = useMobile();
|
|
8
|
+
const actualIsMobile = isMobile ?? detectedIsMobile;
|
|
9
|
+
const overlayRef = useRef(null);
|
|
10
|
+
const drawerRef = useRef(null);
|
|
11
|
+
// Handle escape key
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const handleEscape = (event) => {
|
|
14
|
+
if (event.key === 'Escape' && isOpen && onClose) {
|
|
15
|
+
onClose();
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
if (isOpen) {
|
|
19
|
+
document.addEventListener('keydown', handleEscape);
|
|
20
|
+
// Prevent body scroll when drawer is open
|
|
21
|
+
document.body.style.overflow = 'hidden';
|
|
22
|
+
}
|
|
23
|
+
return () => {
|
|
24
|
+
document.removeEventListener('keydown', handleEscape);
|
|
25
|
+
document.body.style.overflow = 'auto';
|
|
26
|
+
};
|
|
27
|
+
}, [isOpen, onClose]);
|
|
28
|
+
// Handle click outside
|
|
29
|
+
const handleOverlayClick = (event) => {
|
|
30
|
+
if (!fullScreen &&
|
|
31
|
+
onClose &&
|
|
32
|
+
overlayRef.current &&
|
|
33
|
+
event.target === overlayRef.current) {
|
|
34
|
+
onClose();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
// Handle keyboard events on overlay
|
|
38
|
+
const handleOverlayKeyDown = (event) => {
|
|
39
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
40
|
+
if (!fullScreen &&
|
|
41
|
+
onClose &&
|
|
42
|
+
overlayRef.current &&
|
|
43
|
+
event.target === overlayRef.current) {
|
|
44
|
+
onClose();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const getTranslateClasses = () => {
|
|
49
|
+
if (!isOpen) {
|
|
50
|
+
switch (direction) {
|
|
51
|
+
case 'top':
|
|
52
|
+
return '-translate-y-full';
|
|
53
|
+
case 'bottom':
|
|
54
|
+
return 'translate-y-full';
|
|
55
|
+
case 'left':
|
|
56
|
+
return '-translate-x-full';
|
|
57
|
+
case 'right':
|
|
58
|
+
return 'translate-x-full';
|
|
59
|
+
default:
|
|
60
|
+
return 'translate-y-full';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return 'translate-x-0 translate-y-0';
|
|
64
|
+
};
|
|
65
|
+
const getPositionClasses = () => {
|
|
66
|
+
switch (direction) {
|
|
67
|
+
case 'top':
|
|
68
|
+
return 'top-0 left-0 right-0';
|
|
69
|
+
case 'bottom':
|
|
70
|
+
return 'bottom-0 left-0 right-0';
|
|
71
|
+
case 'left':
|
|
72
|
+
return 'top-0 left-0 bottom-0';
|
|
73
|
+
case 'right':
|
|
74
|
+
return 'top-0 right-0 bottom-0';
|
|
75
|
+
default:
|
|
76
|
+
return 'bottom-0 left-0 right-0';
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const getSizeClasses = () => {
|
|
80
|
+
const isVertical = direction === 'top' || direction === 'bottom';
|
|
81
|
+
if (fullScreen) {
|
|
82
|
+
return isVertical ? 'w-full h-full' : 'w-full h-full';
|
|
83
|
+
}
|
|
84
|
+
if (isVertical) {
|
|
85
|
+
return 'w-full max-h-[90vh]';
|
|
86
|
+
}
|
|
87
|
+
return 'h-full max-w-[90vw]';
|
|
88
|
+
};
|
|
89
|
+
const baseOverlayClasses = 'fixed inset-0 z-50 transition-all duration-300';
|
|
90
|
+
const baseDrawerClasses = 'fixed bg-white shadow-2xl transition-all duration-300 ease-out overflow-auto';
|
|
91
|
+
const overlayClasses = cn(baseOverlayClasses, {
|
|
92
|
+
'bg-neutral-darker/50 backdrop-blur-sm': isOpen,
|
|
93
|
+
'bg-transparent pointer-events-none': !isOpen,
|
|
94
|
+
});
|
|
95
|
+
const drawerClasses = cn(baseDrawerClasses, getPositionClasses(), getSizeClasses(), getTranslateClasses(), {
|
|
96
|
+
'rounded-t-2xl': direction === 'bottom' && !fullScreen,
|
|
97
|
+
'rounded-b-2xl': direction === 'top' && !fullScreen,
|
|
98
|
+
'rounded-r-2xl': direction === 'left' && !fullScreen,
|
|
99
|
+
'rounded-l-2xl': direction === 'right' && !fullScreen,
|
|
100
|
+
'p-6': !actualIsMobile,
|
|
101
|
+
'p-4': actualIsMobile,
|
|
102
|
+
}, className);
|
|
103
|
+
if (!isOpen) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
return (jsx("div", { ref: overlayRef, className: overlayClasses, onClick: handleOverlayClick, onKeyDown: handleOverlayKeyDown, tabIndex: -1, role: "button", "aria-label": "Close drawer", children: jsx("div", { ref: ref || drawerRef, className: drawerClasses, role: "dialog", "aria-modal": "true", "aria-label": "Drawer", ...props, children: children }) }));
|
|
107
|
+
});
|
|
108
|
+
Drawer.displayName = 'Drawer';
|
|
109
|
+
|
|
110
|
+
export { Drawer };
|
package/dist/esm/Input.js
CHANGED
|
@@ -1 +1,82 @@
|
|
|
1
|
-
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import React, { useState, useId } from 'react';
|
|
3
|
+
import cn from './utils.js';
|
|
4
|
+
import { useMobile } from './contexts.js';
|
|
5
|
+
|
|
6
|
+
const Input = React.forwardRef(({ state = 'default', label, value, onChange, helperText, startIcon, endIcon, type = 'text', isMobile, ...props }, ref) => {
|
|
7
|
+
const [isFocused, setIsFocused] = useState(false);
|
|
8
|
+
const id = useId();
|
|
9
|
+
const detectedIsMobile = useMobile();
|
|
10
|
+
const actualIsMobile = isMobile ?? detectedIsMobile;
|
|
11
|
+
const hasContent = value !== '' && value !== null && value !== undefined;
|
|
12
|
+
const isLabelFloated = isFocused || hasContent;
|
|
13
|
+
const isDisabled = state === 'disabled';
|
|
14
|
+
const handleFocus = (e) => {
|
|
15
|
+
if (!isDisabled) {
|
|
16
|
+
setIsFocused(true);
|
|
17
|
+
props.onFocus?.(e);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const handleBlur = (e) => {
|
|
21
|
+
if (!isDisabled) {
|
|
22
|
+
setIsFocused(false);
|
|
23
|
+
props.onBlur?.(e);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const baseContainerClasses = 'relative flex items-center border rounded-lg transition-all duration-300 w-[360px]';
|
|
27
|
+
const baseLabelClasses = 'absolute pointer-events-none transition-all duration-300';
|
|
28
|
+
const baseInputClasses = 'peer w-full h-full bg-transparent outline-none text-m font-medium disabled:text-neutral-light';
|
|
29
|
+
const baseIconClasses = 'absolute h-5 w-5 transition-colors duration-300';
|
|
30
|
+
const containerClasses = cn(baseContainerClasses, {
|
|
31
|
+
'h-[54px]': !actualIsMobile,
|
|
32
|
+
'h-12': actualIsMobile,
|
|
33
|
+
'border-neutral-light': state === 'default' && !isFocused,
|
|
34
|
+
'border-primary': state === 'default' && isFocused,
|
|
35
|
+
'border-error': state === 'error',
|
|
36
|
+
'bg-white border-neutral-light cursor-not-allowed': isDisabled,
|
|
37
|
+
});
|
|
38
|
+
const labelClasses = cn(baseLabelClasses, {
|
|
39
|
+
'top-[-10px] bg-white px-1 mx-3 font-medium': isLabelFloated,
|
|
40
|
+
'text-s': (!actualIsMobile && isLabelFloated) ||
|
|
41
|
+
(actualIsMobile && !isLabelFloated),
|
|
42
|
+
'text-xs': actualIsMobile && isLabelFloated,
|
|
43
|
+
'text-m': !actualIsMobile && !isLabelFloated,
|
|
44
|
+
'right-1': startIcon,
|
|
45
|
+
'right-3': !startIcon,
|
|
46
|
+
'top-1/2 -translate-y-1/2 text-m font-medium': !isLabelFloated,
|
|
47
|
+
'right-11': !isLabelFloated && startIcon,
|
|
48
|
+
'right-4': !isLabelFloated && !startIcon,
|
|
49
|
+
'text-neutral-main': !isFocused && state === 'default',
|
|
50
|
+
'text-neutral-light': isDisabled,
|
|
51
|
+
'text-primary': isFocused && state === 'default',
|
|
52
|
+
'text-error': isLabelFloated && state === 'error',
|
|
53
|
+
});
|
|
54
|
+
const inputClasses = cn(baseInputClasses, {
|
|
55
|
+
'pr-12': startIcon,
|
|
56
|
+
'pl-12': endIcon,
|
|
57
|
+
'px-4': !startIcon && !endIcon,
|
|
58
|
+
'pr-4 pl-12': !startIcon && endIcon,
|
|
59
|
+
'pl-4 pr-12': startIcon && !endIcon,
|
|
60
|
+
'cursor-not-allowed text-red-500': isDisabled,
|
|
61
|
+
'text-neutral-dark': !isFocused,
|
|
62
|
+
'text-neutral-darker': isFocused,
|
|
63
|
+
});
|
|
64
|
+
const iconClasses = cn(baseIconClasses, {
|
|
65
|
+
'text-neutral-main': state !== 'error' && !isFocused,
|
|
66
|
+
'text-primary': state === 'default' && isFocused,
|
|
67
|
+
'text-error': state === 'error',
|
|
68
|
+
'text-neutral-light': isDisabled,
|
|
69
|
+
});
|
|
70
|
+
const helperTextClasses = cn('font-light mt-1 px-2 h-4', {
|
|
71
|
+
'text-s': !actualIsMobile,
|
|
72
|
+
'text-xs': actualIsMobile,
|
|
73
|
+
'text-neutral-main': state === 'default',
|
|
74
|
+
'text-primary': state === 'default' && isFocused,
|
|
75
|
+
'text-error': state === 'error',
|
|
76
|
+
'text-neutral-light': isDisabled,
|
|
77
|
+
});
|
|
78
|
+
return (jsxs("div", { children: [jsxs("div", { className: containerClasses, children: [endIcon && (jsx("span", { className: cn(iconClasses, 'left-4'), children: endIcon })), jsx("label", { htmlFor: id, className: labelClasses, children: label }), jsx("input", { ref: ref, id: id, type: type, value: value, onChange: onChange, onFocus: handleFocus, onBlur: handleBlur, disabled: isDisabled, className: inputClasses, ...props }), startIcon && (jsx("span", { className: cn(iconClasses, 'right-4'), children: startIcon }))] }), helperText && jsx("p", { className: helperTextClasses, children: helperText })] }));
|
|
79
|
+
});
|
|
80
|
+
Input.displayName = 'Input';
|
|
81
|
+
|
|
82
|
+
export { Input };
|
package/dist/esm/Menu.js
CHANGED
|
@@ -1 +1,120 @@
|
|
|
1
|
-
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import cn from './utils.js';
|
|
4
|
+
import { Typography } from './Typography.js';
|
|
5
|
+
|
|
6
|
+
const Menu = React.forwardRef(({ buttonText, items, disabled = false, className, onOpenChange, isOpen: controlledIsOpen, 'aria-label': ariaLabel, ...props }, ref) => {
|
|
7
|
+
const [internalIsOpen, setInternalIsOpen] = useState(false);
|
|
8
|
+
const menuRef = useRef(null);
|
|
9
|
+
const buttonRef = useRef(null);
|
|
10
|
+
// Use controlled or uncontrolled state
|
|
11
|
+
const isOpen = controlledIsOpen !== undefined ? controlledIsOpen : internalIsOpen;
|
|
12
|
+
const handleToggle = () => {
|
|
13
|
+
if (disabled)
|
|
14
|
+
return;
|
|
15
|
+
const newIsOpen = !isOpen;
|
|
16
|
+
if (controlledIsOpen === undefined) {
|
|
17
|
+
setInternalIsOpen(newIsOpen);
|
|
18
|
+
}
|
|
19
|
+
onOpenChange?.(newIsOpen);
|
|
20
|
+
};
|
|
21
|
+
const handleItemClick = (item) => {
|
|
22
|
+
if (item.disabled)
|
|
23
|
+
return;
|
|
24
|
+
// Close menu after item click
|
|
25
|
+
if (controlledIsOpen === undefined) {
|
|
26
|
+
setInternalIsOpen(false);
|
|
27
|
+
}
|
|
28
|
+
onOpenChange?.(false);
|
|
29
|
+
// Execute item's onClick if provided
|
|
30
|
+
item.onClick?.();
|
|
31
|
+
};
|
|
32
|
+
// Close menu when clicking outside
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const handleClickOutside = (event) => {
|
|
35
|
+
if (menuRef.current &&
|
|
36
|
+
!menuRef.current.contains(event.target)) {
|
|
37
|
+
if (controlledIsOpen === undefined) {
|
|
38
|
+
setInternalIsOpen(false);
|
|
39
|
+
}
|
|
40
|
+
onOpenChange?.(false);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
if (isOpen) {
|
|
44
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
45
|
+
return () => {
|
|
46
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}, [isOpen, controlledIsOpen, onOpenChange]);
|
|
51
|
+
// Handle keyboard navigation
|
|
52
|
+
const handleKeyDown = (event) => {
|
|
53
|
+
if (disabled)
|
|
54
|
+
return;
|
|
55
|
+
switch (event.key) {
|
|
56
|
+
case 'Escape':
|
|
57
|
+
if (isOpen) {
|
|
58
|
+
event.preventDefault();
|
|
59
|
+
if (controlledIsOpen === undefined) {
|
|
60
|
+
setInternalIsOpen(false);
|
|
61
|
+
}
|
|
62
|
+
onOpenChange?.(false);
|
|
63
|
+
buttonRef.current?.focus();
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case 'ArrowDown':
|
|
67
|
+
if (!isOpen) {
|
|
68
|
+
event.preventDefault();
|
|
69
|
+
handleToggle();
|
|
70
|
+
}
|
|
71
|
+
break;
|
|
72
|
+
case 'Enter':
|
|
73
|
+
case ' ':
|
|
74
|
+
event.preventDefault();
|
|
75
|
+
handleToggle();
|
|
76
|
+
break;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const buttonClasses = cn(
|
|
80
|
+
// Base button styles matching Figma design
|
|
81
|
+
'inline-flex items-center justify-center', 'bg-white border border-[var(--color-neutral-light)]', 'rounded-[6px] px-[16px] py-[13px]', 'transition-colors duration-200',
|
|
82
|
+
// Interactive states
|
|
83
|
+
'hover:bg-[var(--color-neutral-lighter)]', 'focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:ring-offset-2',
|
|
84
|
+
// Disabled state
|
|
85
|
+
{
|
|
86
|
+
'opacity-50 cursor-not-allowed': disabled,
|
|
87
|
+
'cursor-pointer': !disabled,
|
|
88
|
+
});
|
|
89
|
+
const dropdownClasses = cn(
|
|
90
|
+
// Base dropdown styles
|
|
91
|
+
'absolute top-full left-0 right-0 z-10 mt-[4px]', 'bg-white border border-[var(--color-neutral-light)]', 'rounded-[6px] py-[13px] px-[16px]', 'shadow-lg',
|
|
92
|
+
// Animation
|
|
93
|
+
'transition-all duration-200', {
|
|
94
|
+
'opacity-100 translate-y-0 pointer-events-auto': isOpen,
|
|
95
|
+
'opacity-0 -translate-y-2 pointer-events-none': !isOpen,
|
|
96
|
+
});
|
|
97
|
+
const itemClasses = cn('block w-full text-right', 'transition-colors duration-200', 'hover:bg-[var(--color-neutral-lighter)] rounded-[4px] px-[8px] py-[4px] -mx-[8px]', 'focus:outline-none focus:bg-[var(--color-neutral-lighter)]');
|
|
98
|
+
return (jsx("div", { ref: ref, className: cn('relative inline-block', className), ...props, children: jsxs("div", { ref: menuRef, children: [jsx("button", { ref: buttonRef, type: "button", className: buttonClasses, onClick: handleToggle, onKeyDown: handleKeyDown, disabled: disabled, "aria-expanded": isOpen, "aria-haspopup": "true", "aria-label": ariaLabel || `منوی ${buttonText}`, children: jsx(Typography, { variant: "body-s-heavy", color: "neutral-darker", className: "text-right", children: buttonText }) }), jsx("div", { className: dropdownClasses, children: jsx("div", { className: "flex flex-col gap-[16px]", children: items.map((item) => {
|
|
99
|
+
const content = (jsx(Typography, { variant: "body-s-heavy", color: item.disabled ? 'neutral-main' : 'neutral-darker', className: "text-right", children: item.label }));
|
|
100
|
+
const commonProps = {
|
|
101
|
+
className: cn(itemClasses, {
|
|
102
|
+
'opacity-50 cursor-not-allowed': item.disabled,
|
|
103
|
+
'cursor-pointer': !item.disabled,
|
|
104
|
+
}),
|
|
105
|
+
onClick: item.disabled
|
|
106
|
+
? undefined
|
|
107
|
+
: () => handleItemClick(item),
|
|
108
|
+
'aria-label': `منوی ${item.label}`,
|
|
109
|
+
};
|
|
110
|
+
// Render as link if href is provided
|
|
111
|
+
if (item.href && !item.disabled) {
|
|
112
|
+
return (jsx("a", { ...commonProps, href: item.href, role: "menuitem", tabIndex: isOpen ? 0 : -1, children: content }, item.id));
|
|
113
|
+
}
|
|
114
|
+
// Render as button
|
|
115
|
+
return (jsx("button", { ...commonProps, type: "button", role: "menuitem", tabIndex: isOpen ? 0 : -1, disabled: item.disabled, children: content }, item.id));
|
|
116
|
+
}) }) })] }) }));
|
|
117
|
+
});
|
|
118
|
+
Menu.displayName = 'Menu';
|
|
119
|
+
|
|
120
|
+
export { Menu };
|
package/dist/esm/Pagination.js
CHANGED
|
@@ -1 +1,186 @@
|
|
|
1
|
-
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import cn from './utils.js';
|
|
4
|
+
import { useMobile } from './contexts.js';
|
|
5
|
+
import { c as createLucideIcon } from './createLucideIcon-D-q73LTT.js';
|
|
6
|
+
import { C as ChevronLeft } from './chevron-left-Ck6O99eF.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @license lucide-react v0.522.0 - ISC
|
|
10
|
+
*
|
|
11
|
+
* This source code is licensed under the ISC license.
|
|
12
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
const __iconNode$3 = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
|
|
17
|
+
const ChevronRight = createLucideIcon("chevron-right", __iconNode$3);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @license lucide-react v0.522.0 - ISC
|
|
21
|
+
*
|
|
22
|
+
* This source code is licensed under the ISC license.
|
|
23
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
const __iconNode$2 = [
|
|
28
|
+
["path", { d: "m11 17-5-5 5-5", key: "13zhaf" }],
|
|
29
|
+
["path", { d: "m18 17-5-5 5-5", key: "h8a8et" }]
|
|
30
|
+
];
|
|
31
|
+
const ChevronsLeft = createLucideIcon("chevrons-left", __iconNode$2);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @license lucide-react v0.522.0 - ISC
|
|
35
|
+
*
|
|
36
|
+
* This source code is licensed under the ISC license.
|
|
37
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
const __iconNode$1 = [
|
|
42
|
+
["path", { d: "m6 17 5-5-5-5", key: "xnjwq" }],
|
|
43
|
+
["path", { d: "m13 17 5-5-5-5", key: "17xmmf" }]
|
|
44
|
+
];
|
|
45
|
+
const ChevronsRight = createLucideIcon("chevrons-right", __iconNode$1);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @license lucide-react v0.522.0 - ISC
|
|
49
|
+
*
|
|
50
|
+
* This source code is licensed under the ISC license.
|
|
51
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
const __iconNode = [
|
|
56
|
+
["circle", { cx: "12", cy: "12", r: "1", key: "41hilf" }],
|
|
57
|
+
["circle", { cx: "19", cy: "12", r: "1", key: "1wjl8i" }],
|
|
58
|
+
["circle", { cx: "5", cy: "12", r: "1", key: "1pcz8c" }]
|
|
59
|
+
];
|
|
60
|
+
const Ellipsis = createLucideIcon("ellipsis", __iconNode);
|
|
61
|
+
|
|
62
|
+
function PaginationRoot({ className, ...props }) {
|
|
63
|
+
return (jsx("nav", { role: "navigation", "aria-label": "pagination", "data-slot": "pagination", className: cn('mx-auto flex w-full justify-center', className), ...props }));
|
|
64
|
+
}
|
|
65
|
+
function PaginationContent({ className, device, ...props }) {
|
|
66
|
+
return (jsx("ul", { "data-slot": "pagination-content", className: cn('flex flex-row items-center', device === 'mobile' ? 'gap-[6px]' : 'gap-2', className), ...props }));
|
|
67
|
+
}
|
|
68
|
+
function PaginationItem({ ...props }) {
|
|
69
|
+
return jsx("li", { "data-slot": "pagination-item", ...props });
|
|
70
|
+
}
|
|
71
|
+
function PaginationLink({ className, device, variant, isActive, ...props }) {
|
|
72
|
+
return (jsx("a", { "aria-current": isActive ? 'page' : undefined, "data-slot": "pagination-link", "data-active": isActive, className: cn(buttonVariants({
|
|
73
|
+
variant: variant === 'nextPrev' ? variant : isActive ? 'active' : 'outline',
|
|
74
|
+
device,
|
|
75
|
+
}), className), tabIndex: props['aria-disabled'] ? -1 : 0, ...props }));
|
|
76
|
+
}
|
|
77
|
+
function PaginationPrevious({ className, device, variant, ...props }) {
|
|
78
|
+
const isMobile = device === 'mobile';
|
|
79
|
+
const content = {
|
|
80
|
+
mobile: jsx(ChevronRight, { size: 18 }),
|
|
81
|
+
desktop: (jsxs(Fragment, { children: [jsx(ChevronsRight, { size: 20 }), " ", jsx("span", { children: "\u0642\u0628\u0644\u06CC" })] })),
|
|
82
|
+
};
|
|
83
|
+
return (jsx(PaginationLink, { "aria-label": "Go to previous page", className: !isMobile
|
|
84
|
+
? 'flex justify-center items-center gap-2 w-[89px] h-[48px] ml-4'
|
|
85
|
+
: 'ml-2.5', device: device, variant: "nextPrev", ...props, children: device === 'mobile' ? content.mobile : content.desktop }));
|
|
86
|
+
}
|
|
87
|
+
function PaginationNext({ className, device, variant, ...props }) {
|
|
88
|
+
const isMobile = device === 'mobile';
|
|
89
|
+
const content = {
|
|
90
|
+
mobile: jsx(ChevronLeft, { size: 18 }),
|
|
91
|
+
desktop: (jsxs(Fragment, { children: [jsx("span", { children: "\u0628\u0639\u062F\u06CC" }), jsx(ChevronsLeft, { size: 20 })] })),
|
|
92
|
+
};
|
|
93
|
+
return (jsx(PaginationLink, { "aria-label": "Go to next page", className: !isMobile
|
|
94
|
+
? 'flex justify-center items-center gap-2 w-[89px] h-[48px] mr-4'
|
|
95
|
+
: 'mr-2.5', device: device, variant: "nextPrev", ...props, children: device === 'mobile' ? content.mobile : content.desktop }));
|
|
96
|
+
}
|
|
97
|
+
function PaginationEllipsis({ className, ...props }) {
|
|
98
|
+
return (jsx("span", { "aria-hidden": true, "data-slot": "pagination-ellipsis", className: cn('flex size-9 items-center justify-center', className), ...props, children: jsx(Ellipsis, { className: "size-4" }) }));
|
|
99
|
+
}
|
|
100
|
+
function buttonVariants({ variant, device, }) {
|
|
101
|
+
const isMobile = device === 'mobile';
|
|
102
|
+
const base = cn({
|
|
103
|
+
'flex items-center justify-center rounded-[6px]': true,
|
|
104
|
+
'w-[40px] h-[40px] text-s font-heavy': isMobile,
|
|
105
|
+
'w-[48px] h-[48px] text-l font-heavy': !isMobile,
|
|
106
|
+
});
|
|
107
|
+
const variants = {
|
|
108
|
+
outline: 'border border-neutral-light bg-white text-neutral-darker',
|
|
109
|
+
active: 'bg-primary text-white',
|
|
110
|
+
nextPrev: 'border border-neutral-light bg-white text-neutral-darker',
|
|
111
|
+
};
|
|
112
|
+
return [base, variants[variant]].join(' ');
|
|
113
|
+
}
|
|
114
|
+
const Pagination = ({ pageCount, page: controlledPage, defaultPage = 1, onPageChange, className, isMobile, ...navProps }) => {
|
|
115
|
+
const detectedIsMobile = useMobile();
|
|
116
|
+
const actualIsMobile = isMobile ?? detectedIsMobile;
|
|
117
|
+
const isControlled = controlledPage !== undefined;
|
|
118
|
+
const [internalPage, setInternalPage] = useState(defaultPage);
|
|
119
|
+
const page = isControlled ? controlledPage : internalPage;
|
|
120
|
+
const device = actualIsMobile ? 'mobile' : 'desktop';
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (!isControlled)
|
|
123
|
+
setInternalPage(defaultPage);
|
|
124
|
+
}, [defaultPage, isControlled]);
|
|
125
|
+
const changePage = (newPage) => {
|
|
126
|
+
if (!isControlled)
|
|
127
|
+
setInternalPage(newPage);
|
|
128
|
+
onPageChange?.(newPage);
|
|
129
|
+
};
|
|
130
|
+
function renderPages() {
|
|
131
|
+
const items = [];
|
|
132
|
+
if (pageCount <= 5) {
|
|
133
|
+
for (let i = 1; i <= pageCount; i++) {
|
|
134
|
+
items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", isActive: page === i, variant: "main", device: device, onClick: (e) => {
|
|
135
|
+
e.preventDefault();
|
|
136
|
+
if (page !== i)
|
|
137
|
+
changePage(i);
|
|
138
|
+
}, "aria-disabled": page === i, tabIndex: page === i ? -1 : 0, children: i }) }, i));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// First page
|
|
143
|
+
items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", isActive: page === 1, device: device, variant: "main", onClick: (e) => {
|
|
144
|
+
e.preventDefault();
|
|
145
|
+
if (page !== 1)
|
|
146
|
+
changePage(1);
|
|
147
|
+
}, "aria-disabled": page === 1, tabIndex: page === 1 ? -1 : 0, children: "1" }) }, 1));
|
|
148
|
+
// Ellipsis start
|
|
149
|
+
if (page > 3) {
|
|
150
|
+
items.push(jsx(PaginationItem, { children: jsx(PaginationEllipsis, {}) }, "start-ellipsis"));
|
|
151
|
+
}
|
|
152
|
+
// Middle pages
|
|
153
|
+
const start = Math.max(2, page - 1);
|
|
154
|
+
const end = Math.min(pageCount - 1, page + 1);
|
|
155
|
+
for (let i = start; i <= end; i++) {
|
|
156
|
+
items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", isActive: page === i, device: device, variant: "main", onClick: (e) => {
|
|
157
|
+
e.preventDefault();
|
|
158
|
+
if (page !== i)
|
|
159
|
+
changePage(i);
|
|
160
|
+
}, "aria-disabled": page === i, tabIndex: page === i ? -1 : 0, children: i }) }, i));
|
|
161
|
+
}
|
|
162
|
+
// Ellipsis end
|
|
163
|
+
if (page < pageCount - 2) {
|
|
164
|
+
items.push(jsx(PaginationItem, { children: jsx(PaginationEllipsis, {}) }, "end-ellipsis"));
|
|
165
|
+
}
|
|
166
|
+
// Last page
|
|
167
|
+
items.push(jsx(PaginationItem, { children: jsx(PaginationLink, { href: "#", isActive: page === pageCount, device: device, variant: "main", onClick: (e) => {
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
if (page !== pageCount)
|
|
170
|
+
changePage(pageCount);
|
|
171
|
+
}, "aria-disabled": page === pageCount, tabIndex: page === pageCount ? -1 : 0, children: pageCount }) }, pageCount));
|
|
172
|
+
}
|
|
173
|
+
return items;
|
|
174
|
+
}
|
|
175
|
+
return (jsx(PaginationRoot, { className: className, ...navProps, children: jsxs(PaginationContent, { device: "mobile", children: [jsx(PaginationItem, { children: jsx(PaginationPrevious, { href: "#", onClick: (e) => {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
if (page > 1)
|
|
178
|
+
changePage(page - 1);
|
|
179
|
+
}, variant: "nextPrev", device: device, "aria-disabled": page === 1, tabIndex: page === 1 ? -1 : 0 }) }), renderPages(), jsx(PaginationItem, { children: jsx(PaginationNext, { href: "#", onClick: (e) => {
|
|
180
|
+
e.preventDefault();
|
|
181
|
+
if (page < pageCount)
|
|
182
|
+
changePage(page + 1);
|
|
183
|
+
}, variant: "nextPrev", device: device, "aria-disabled": page === pageCount, tabIndex: page === pageCount ? -1 : 0 }) })] }) }));
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export { Pagination };
|
package/dist/esm/RadioGroup.js
CHANGED
|
@@ -1 +1,71 @@
|
|
|
1
|
-
|
|
1
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import cn from './utils.js';
|
|
4
|
+
import { useMobile } from './contexts.js';
|
|
5
|
+
|
|
6
|
+
const RadioButton = ({ selected, disabled, isMobile, }) => {
|
|
7
|
+
const size = isMobile ? 'size-5' : 'size-6';
|
|
8
|
+
if (selected) {
|
|
9
|
+
return (jsxs("div", { className: cn('relative flex items-center justify-center', size), children: [jsx("div", { className: cn('absolute inset-0 rounded-full border-2', disabled
|
|
10
|
+
? 'bg-neutral-light border-neutral-light'
|
|
11
|
+
: 'bg-primary border-primary') }), jsx("div", { className: cn('absolute rounded-full bg-white', isMobile ? 'inset-[5px]' : 'inset-1.5') })] }));
|
|
12
|
+
}
|
|
13
|
+
return (jsx("div", { className: cn('relative flex items-center justify-center', size), children: jsx("div", { className: cn('absolute inset-0 rounded-full border', isMobile ? 'border' : 'border-[1.5px]', disabled ? 'border-neutral-light' : 'border-midnight') }) }));
|
|
14
|
+
};
|
|
15
|
+
const RadioOptionComponent = ({ option, selected, disabled, onClick, name, isMobile = false, }) => {
|
|
16
|
+
const [isHovered, setIsHovered] = React.useState(false);
|
|
17
|
+
const handleKeyDown = (e) => {
|
|
18
|
+
if ((e.key === 'Enter' || e.key === ' ') && !disabled) {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
onClick();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const getBackgroundColor = () => {
|
|
24
|
+
if (disabled)
|
|
25
|
+
return 'bg-neutral-lighter';
|
|
26
|
+
if (selected)
|
|
27
|
+
return 'bg-primary-lightest';
|
|
28
|
+
if (isHovered)
|
|
29
|
+
return 'bg-primary-lightest/20';
|
|
30
|
+
return 'bg-white';
|
|
31
|
+
};
|
|
32
|
+
const getBorderColor = () => {
|
|
33
|
+
if (selected)
|
|
34
|
+
return 'border-primary';
|
|
35
|
+
return 'border-transparent';
|
|
36
|
+
};
|
|
37
|
+
const getTextColor = () => {
|
|
38
|
+
if (disabled)
|
|
39
|
+
return 'text-midnight';
|
|
40
|
+
if (selected)
|
|
41
|
+
return 'text-primary';
|
|
42
|
+
return 'text-neutral-dark';
|
|
43
|
+
};
|
|
44
|
+
const getShadow = () => {
|
|
45
|
+
if (disabled)
|
|
46
|
+
return '';
|
|
47
|
+
return 'shadow-[0px_2px_4px_0px_rgba(0,0,0,0.08)]';
|
|
48
|
+
};
|
|
49
|
+
return (jsx("div", { className: cn('relative rounded-lg border-[1.5px] cursor-pointer transition-all duration-200', getBackgroundColor(), getBorderColor(), getShadow(), disabled && 'cursor-not-allowed'), onClick: disabled ? undefined : onClick, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), onKeyDown: handleKeyDown, tabIndex: disabled ? -1 : 0, role: "radio", "aria-checked": selected, "aria-disabled": disabled, children: jsx("div", { className: "flex flex-row items-center justify-end relative w-full", children: jsx("div", { className: cn('box-border flex flex-row items-center justify-end relative w-full', isMobile ? 'gap-2 px-4 py-3' : 'gap-4 px-6 py-4'), children: jsxs("div", { className: cn('flex flex-row items-center justify-end p-0 relative shrink-0', isMobile ? 'gap-2' : 'gap-3'), children: [jsx("input", { type: "radio", name: name, value: option.id, checked: selected, disabled: disabled, onChange: onClick, className: "sr-only", "aria-label": option.label }), jsx("div", { className: "flex flex-col gap-0.5 items-end justify-start p-0 relative shrink-0", children: jsx("div", { className: cn('font-sans font-bold text-nowrap text-right transition-colors duration-200', isMobile
|
|
50
|
+
? 'text-[14px] leading-[24px]'
|
|
51
|
+
: 'text-[16px] leading-normal', getTextColor()), children: jsx("p", { className: "whitespace-pre", dir: "auto", children: option.label }) }) }), option.icon && (jsx("div", { className: cn('flex items-center justify-center shrink-0 transition-colors duration-200', isMobile ? 'size-6' : 'size-7', getTextColor()), children: option.icon })), jsx("div", { className: "flex flex-col items-start justify-start p-0 relative shrink-0", children: jsx(RadioButton, { selected: selected, disabled: disabled, isMobile: isMobile }) })] }) }) }) }));
|
|
52
|
+
};
|
|
53
|
+
const RadioGroup = React.forwardRef(({ options, value, onChange, name, disabled = false, className, isMobile, direction = 'vertical', ...props }, ref) => {
|
|
54
|
+
const detectedIsMobile = useMobile();
|
|
55
|
+
const actualIsMobile = isMobile ?? detectedIsMobile;
|
|
56
|
+
const handleOptionClick = (optionId) => {
|
|
57
|
+
if (!disabled && onChange) {
|
|
58
|
+
onChange(optionId);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const getContainerClasses = () => {
|
|
62
|
+
if (direction === 'horizontal') {
|
|
63
|
+
return cn('flex flex-row gap-2', actualIsMobile ? 'flex-wrap' : 'flex-nowrap', className);
|
|
64
|
+
}
|
|
65
|
+
return cn('flex flex-col gap-2', className);
|
|
66
|
+
};
|
|
67
|
+
return (jsx("div", { ref: ref, className: getContainerClasses(), role: "radiogroup", ...props, children: options.map((option) => (jsx(RadioOptionComponent, { option: option, selected: value === option.id, disabled: disabled || option.disabled, onClick: () => handleOptionClick(option.id), name: name, isMobile: actualIsMobile }, option.id))) }));
|
|
68
|
+
});
|
|
69
|
+
RadioGroup.displayName = 'RadioGroup';
|
|
70
|
+
|
|
71
|
+
export { RadioGroup };
|