cronixui 1.0.5 → 1.1.0

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 (130) hide show
  1. package/README.md +35 -5
  2. package/package.json +21 -3
  3. package/packages/go/cronixui/cronixui.go +784 -237
  4. package/packages/go/cronixui/go.mod +34 -9
  5. package/packages/go/cronixui/go.sum +666 -0
  6. package/packages/python/cronixui/__init__.py +131 -1
  7. package/packages/python/cronixui/alert.py +61 -0
  8. package/packages/python/cronixui/avatar.py +50 -0
  9. package/packages/python/cronixui/badge.py +46 -0
  10. package/packages/python/cronixui/button.py +64 -0
  11. package/packages/python/cronixui/card.py +62 -0
  12. package/packages/python/cronixui/form.py +255 -0
  13. package/packages/python/cronixui/layout.py +143 -0
  14. package/packages/python/cronixui/list.py +51 -0
  15. package/packages/python/cronixui/loading.py +36 -0
  16. package/packages/python/cronixui/progress.py +90 -0
  17. package/packages/python/cronixui/table.py +48 -0
  18. package/packages/python/cronixui/tokens.py +200 -0
  19. package/packages/python/cronixui/tooltip.py +28 -0
  20. package/packages/react/src/components/Accordion.tsx +82 -0
  21. package/packages/react/src/components/Alert.tsx +80 -0
  22. package/packages/react/src/components/Avatar.tsx +54 -0
  23. package/packages/react/src/components/Badge.tsx +32 -0
  24. package/packages/react/src/components/Breadcrumb.tsx +50 -0
  25. package/packages/react/src/components/Button.tsx +47 -0
  26. package/packages/react/src/components/Card.tsx +69 -0
  27. package/packages/react/src/components/Checkbox.tsx +30 -0
  28. package/packages/react/src/components/CommandPalette.tsx +131 -0
  29. package/packages/react/src/components/Container.tsx +26 -0
  30. package/packages/react/src/components/Dropdown.tsx +88 -0
  31. package/packages/react/src/components/FileInput.tsx +86 -0
  32. package/packages/react/src/components/Footer.tsx +36 -0
  33. package/packages/react/src/components/FormGroup.tsx +36 -0
  34. package/packages/react/src/components/Header.tsx +29 -0
  35. package/packages/react/src/components/Input.tsx +54 -0
  36. package/packages/react/src/components/List.tsx +55 -0
  37. package/packages/react/src/components/Modal.tsx +89 -0
  38. package/packages/react/src/components/Nav.tsx +63 -0
  39. package/packages/react/src/components/Pagination.tsx +107 -0
  40. package/packages/react/src/components/Progress.tsx +49 -0
  41. package/packages/react/src/components/Radio.tsx +64 -0
  42. package/packages/react/src/components/Search.tsx +95 -0
  43. package/packages/react/src/components/Select.tsx +41 -0
  44. package/packages/react/src/components/Sidebar.tsx +64 -0
  45. package/packages/react/src/components/Skeleton.tsx +39 -0
  46. package/packages/react/src/components/Slider.tsx +32 -0
  47. package/packages/react/src/components/Spinner.tsx +24 -0
  48. package/packages/react/src/components/Stack.tsx +69 -0
  49. package/packages/react/src/components/Stat.tsx +35 -0
  50. package/packages/react/src/components/Table.tsx +90 -0
  51. package/packages/react/src/components/Tabs.tsx +85 -0
  52. package/packages/react/src/components/Tag.tsx +30 -0
  53. package/packages/react/src/components/Textarea.tsx +21 -0
  54. package/packages/react/src/components/Toast.tsx +134 -0
  55. package/packages/react/src/components/Toggle.tsx +58 -0
  56. package/packages/react/src/components/Tooltip.tsx +31 -0
  57. package/packages/react/src/components/Typography.tsx +66 -0
  58. package/packages/react/src/index.ts +40 -0
  59. package/packages/react/src/styles.css +2039 -0
  60. package/packages/react/src/tokens/index.ts +94 -0
  61. package/packages/rust/cronixui/src/colors.rs +135 -0
  62. package/packages/rust/cronixui/src/components/accordion.rs +47 -0
  63. package/packages/rust/cronixui/src/components/alert.rs +95 -0
  64. package/packages/rust/cronixui/src/components/avatar.rs +85 -0
  65. package/packages/rust/cronixui/src/components/badge.rs +35 -0
  66. package/packages/rust/cronixui/src/components/breadcrumb.rs +58 -0
  67. package/packages/rust/cronixui/src/components/button.rs +70 -0
  68. package/packages/rust/cronixui/src/components/card.rs +259 -0
  69. package/packages/rust/cronixui/src/components/command_palette.rs +254 -0
  70. package/packages/rust/cronixui/src/components/dropdown.rs +179 -0
  71. package/packages/rust/cronixui/src/components/file_input.rs +74 -0
  72. package/packages/rust/cronixui/src/components/input.rs +21 -0
  73. package/packages/rust/cronixui/src/components/list.rs +38 -0
  74. package/packages/rust/cronixui/src/components/mod.rs +51 -0
  75. package/packages/rust/cronixui/src/{modal.rs → components/modal.rs} +15 -1
  76. package/packages/rust/cronixui/src/components/nav.rs +19 -0
  77. package/packages/rust/cronixui/src/{pagination.rs → components/pagination.rs} +14 -13
  78. package/packages/rust/cronixui/src/components/progress.rs +50 -0
  79. package/packages/rust/cronixui/src/components/search.rs +185 -0
  80. package/packages/rust/cronixui/src/components/skeleton.rs +63 -0
  81. package/packages/rust/cronixui/src/components/spinner.rs +21 -0
  82. package/packages/rust/cronixui/src/components/table.rs +56 -0
  83. package/packages/rust/cronixui/src/components/tabs.rs +43 -0
  84. package/packages/rust/cronixui/src/components/toast.rs +69 -0
  85. package/packages/rust/cronixui/src/{toggle.rs → components/toggle.rs} +7 -5
  86. package/packages/rust/cronixui/src/components/tooltip.rs +11 -0
  87. package/packages/rust/cronixui/src/lib.rs +111 -62
  88. package/packages/rust/cronixui/src/tokens.rs +107 -0
  89. package/packages/web/src/tokens.ts +120 -0
  90. package/packages/web/src/variables.css +81 -81
  91. package/packages/python/cronixui/pyproject.toml +0 -11
  92. package/packages/react/src/components/Accordion.jsx +0 -50
  93. package/packages/react/src/components/Alert.jsx +0 -62
  94. package/packages/react/src/components/Avatar.jsx +0 -34
  95. package/packages/react/src/components/Badge.jsx +0 -15
  96. package/packages/react/src/components/Breadcrumb.jsx +0 -27
  97. package/packages/react/src/components/Button.jsx +0 -21
  98. package/packages/react/src/components/Card.jsx +0 -23
  99. package/packages/react/src/components/Checkbox.jsx +0 -27
  100. package/packages/react/src/components/CommandPalette.jsx +0 -93
  101. package/packages/react/src/components/Dropdown.jsx +0 -48
  102. package/packages/react/src/components/FileInput.jsx +0 -44
  103. package/packages/react/src/components/Input.jsx +0 -22
  104. package/packages/react/src/components/List.jsx +0 -29
  105. package/packages/react/src/components/Modal.jsx +0 -65
  106. package/packages/react/src/components/Nav.jsx +0 -50
  107. package/packages/react/src/components/Pagination.jsx +0 -81
  108. package/packages/react/src/components/Progress.jsx +0 -23
  109. package/packages/react/src/components/Radio.jsx +0 -50
  110. package/packages/react/src/components/Search.jsx +0 -70
  111. package/packages/react/src/components/Select.jsx +0 -33
  112. package/packages/react/src/components/Skeleton.jsx +0 -15
  113. package/packages/react/src/components/Slider.jsx +0 -29
  114. package/packages/react/src/components/Spinner.jsx +0 -5
  115. package/packages/react/src/components/Stat.jsx +0 -19
  116. package/packages/react/src/components/Table.jsx +0 -48
  117. package/packages/react/src/components/Tabs.jsx +0 -65
  118. package/packages/react/src/components/Tag.jsx +0 -19
  119. package/packages/react/src/components/Textarea.jsx +0 -17
  120. package/packages/react/src/components/Toast.jsx +0 -78
  121. package/packages/react/src/components/Toggle.jsx +0 -34
  122. package/packages/react/src/components/Tooltip.jsx +0 -12
  123. package/packages/react/src/index.d.ts +0 -103
  124. package/packages/react/src/index.js +0 -33
  125. package/packages/rust/cronixui/src/accordion.rs +0 -49
  126. package/packages/rust/cronixui/src/command_palette.rs +0 -62
  127. package/packages/rust/cronixui/src/dropdown.rs +0 -31
  128. package/packages/rust/cronixui/src/search.rs +0 -49
  129. package/packages/rust/cronixui/src/tabs.rs +0 -23
  130. package/packages/rust/cronixui/src/toast.rs +0 -70
@@ -1,29 +0,0 @@
1
- export function List({ children, className = '' }) {
2
- return <div className={`cn-list ${className}`}>{children}</div>;
3
- }
4
-
5
- export function ListItem({
6
- children,
7
- icon,
8
- title,
9
- subtitle,
10
- actions,
11
- clickable = false,
12
- onClick,
13
- className = ''
14
- }) {
15
- return (
16
- <div
17
- className={`cn-list-item ${clickable ? 'cn-list-item-clickable' : ''} ${className}`}
18
- onClick={onClick}
19
- >
20
- {icon && <div className="cn-list-item-icon">{icon}</div>}
21
- <div className="cn-list-item-content">
22
- {title && <div className="cn-list-item-title">{title}</div>}
23
- {subtitle && <div className="cn-list-item-subtitle">{subtitle}</div>}
24
- {children}
25
- </div>
26
- {actions && <div className="cn-list-item-actions">{actions}</div>}
27
- </div>
28
- );
29
- }
@@ -1,65 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
-
3
- export default function Modal({
4
- isOpen = false,
5
- onClose,
6
- children,
7
- className = ''
8
- }) {
9
- const modalRef = useRef(null);
10
-
11
- useEffect(() => {
12
- const handleEscape = (e) => {
13
- if (e.key === 'Escape' && onClose) {
14
- onClose();
15
- }
16
- };
17
-
18
- if (isOpen) {
19
- document.addEventListener('keydown', handleEscape);
20
- document.body.style.overflow = 'hidden';
21
- }
22
-
23
- return () => {
24
- document.removeEventListener('keydown', handleEscape);
25
- document.body.style.overflow = '';
26
- };
27
- }, [isOpen, onClose]);
28
-
29
- if (!isOpen) return null;
30
-
31
- return (
32
- <div
33
- className={`cn-modal-backdrop cn-modal-open ${className}`}
34
- onClick={(e) => e.target === e.currentTarget && onClose?.()}
35
- >
36
- <div className="cn-modal" ref={modalRef}>
37
- {children}
38
- </div>
39
- </div>
40
- );
41
- }
42
-
43
- Modal.Header = function ModalHeader({ children, onClose, className = '' }) {
44
- return (
45
- <div className={`cn-modal-header ${className}`}>
46
- <div className="cn-modal-title">{children}</div>
47
- {onClose && (
48
- <button className="cn-modal-close" onClick={onClose}>
49
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
50
- <line x1="18" y1="6" x2="6" y2="18"></line>
51
- <line x1="6" y1="6" x2="18" y2="18"></line>
52
- </svg>
53
- </button>
54
- )}
55
- </div>
56
- );
57
- };
58
-
59
- Modal.Body = function ModalBody({ children, className = '' }) {
60
- return <div className={`cn-modal-body ${className}`}>{children}</div>;
61
- };
62
-
63
- Modal.Footer = function ModalFooter({ children, className = '' }) {
64
- return <div className={`cn-modal-footer ${className}`}>{children}</div>;
65
- };
@@ -1,50 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- export default function Nav({
4
- defaultActive,
5
- active: controlledActive,
6
- onChange,
7
- children,
8
- className = ''
9
- }) {
10
- const [internalActive, setInternalActive] = useState(defaultActive);
11
- const activeItem = controlledActive !== undefined ? controlledActive : internalActive;
12
-
13
- const handleClick = (item) => {
14
- if (onChange) {
15
- onChange(item);
16
- } else {
17
- setInternalActive(item);
18
- }
19
- };
20
-
21
- return (
22
- <nav className={`cn-nav ${className}`}>
23
- {children.map((child, idx) => {
24
- const isActive = child.props.active !== undefined
25
- ? child.props.active
26
- : child.props.id === activeItem;
27
-
28
- return {
29
- ...child,
30
- props: {
31
- ...child.props,
32
- active: isActive,
33
- onClick: () => handleClick(child.props.id),
34
- }
35
- };
36
- })}
37
- </nav>
38
- );
39
- }
40
-
41
- export function NavItem({ children, active = false, onClick, className = '' }) {
42
- return (
43
- <div
44
- className={`cn-nav-item ${active ? 'cn-nav-active' : ''} ${className}`}
45
- onClick={onClick}
46
- >
47
- {children}
48
- </div>
49
- );
50
- }
@@ -1,81 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- export default function Pagination({
4
- total,
5
- current = 1,
6
- onChange,
7
- className = ''
8
- }) {
9
- const [page, setPage] = useState(current);
10
-
11
- const activePage = onChange !== undefined ? current : page;
12
-
13
- const getPages = () => {
14
- const pages = [];
15
- const maxVisible = 5;
16
-
17
- if (total <= maxVisible) {
18
- for (let i = 1; i <= total; i++) pages.push(i);
19
- } else {
20
- if (activePage <= 3) {
21
- for (let i = 1; i <= 4; i++) pages.push(i);
22
- pages.push('...');
23
- pages.push(total);
24
- } else if (activePage >= total - 2) {
25
- pages.push(1);
26
- pages.push('...');
27
- for (let i = total - 3; i <= total; i++) pages.push(i);
28
- } else {
29
- pages.push(1);
30
- pages.push('...');
31
- for (let i = activePage - 1; i <= activePage + 1; i++) pages.push(i);
32
- pages.push('...');
33
- pages.push(total);
34
- }
35
- }
36
-
37
- return pages;
38
- };
39
-
40
- const goTo = (p) => {
41
- if (p < 1 || p > total || p === activePage) return;
42
- if (onChange) {
43
- onChange(p);
44
- } else {
45
- setPage(p);
46
- }
47
- };
48
-
49
- return (
50
- <div className={`cn-pagination ${className}`}>
51
- <button
52
- className="cn-pagination-item"
53
- onClick={() => goTo(activePage - 1)}
54
- disabled={activePage === 1}
55
- >
56
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="16" height="16">
57
- <polyline points="15 18 9 12 15 6"></polyline>
58
- </svg>
59
- </button>
60
- {getPages().map((p, idx) => (
61
- <button
62
- key={idx}
63
- className={`cn-pagination-item ${p === activePage ? 'cn-pagination-active' : ''}`}
64
- onClick={() => typeof p === 'number' && goTo(p)}
65
- disabled={p === '...'}
66
- >
67
- {p}
68
- </button>
69
- ))}
70
- <button
71
- className="cn-pagination-item"
72
- onClick={() => goTo(activePage + 1)}
73
- disabled={activePage === total}
74
- >
75
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" width="16" height="16">
76
- <polyline points="9 18 15 12 9 6"></polyline>
77
- </svg>
78
- </button>
79
- </div>
80
- );
81
- }
@@ -1,23 +0,0 @@
1
- export default function Progress({
2
- value = 0,
3
- max = 100,
4
- showLabel = false,
5
- variant = 'default',
6
- size = 'md',
7
- className = ''
8
- }) {
9
- const percentage = Math.min(Math.max((value / max) * 100, 0), 100);
10
-
11
- return (
12
- <div className={className}>
13
- {showLabel && (
14
- <div className="cn-progress-label">
15
- <span>{percentage.toFixed(0)}%</span>
16
- </div>
17
- )}
18
- <div className={`cn-progress ${size !== 'md' ? `cn-progress-${size}` : ''} ${variant !== 'default' ? `cn-progress-${variant}` : ''}`}>
19
- <div className="cn-progress-bar" style={{ width: `${percentage}%` }}></div>
20
- </div>
21
- </div>
22
- );
23
- }
@@ -1,50 +0,0 @@
1
- import { forwardRef } from 'react';
2
-
3
- const Radio = forwardRef(function Radio({
4
- checked = false,
5
- onChange,
6
- disabled = false,
7
- children,
8
- name,
9
- className = '',
10
- ...props
11
- }, ref) {
12
- return (
13
- <label className={`cn-radio ${disabled ? 'disabled' : ''} ${className}`}>
14
- <input
15
- ref={ref}
16
- type="radio"
17
- checked={checked}
18
- onChange={(e) => onChange?.(e.target.checked)}
19
- disabled={disabled}
20
- name={name}
21
- {...props}
22
- />
23
- <span className="cn-radio-box"></span>
24
- {children && <span className="cn-radio-label">{children}</span>}
25
- </label>
26
- );
27
- });
28
-
29
- export function RadioGroup({ children, name, value, onChange, className = '' }) {
30
- return (
31
- <div className={className} role="radiogroup">
32
- {children.map((child, idx) => {
33
- if (child.type?.displayName === 'Radio') {
34
- return {
35
- ...child,
36
- props: {
37
- ...child.props,
38
- name,
39
- checked: child.props.value === value,
40
- onChange: () => onChange?.(child.props.value),
41
- }
42
- };
43
- }
44
- return child;
45
- })}
46
- </div>
47
- );
48
- }
49
-
50
- export default Radio;
@@ -1,70 +0,0 @@
1
- import { useState, useEffect, useRef } from 'react';
2
-
3
- export default function Search({
4
- items = [],
5
- placeholder = 'Search...',
6
- onSelect,
7
- className = ''
8
- }) {
9
- const [query, setQuery] = useState('');
10
- const [isOpen, setIsOpen] = useState(false);
11
- const ref = useRef(null);
12
-
13
- const filtered = items.filter(item =>
14
- item.title.toLowerCase().includes(query.toLowerCase()) ||
15
- (item.subtitle && item.subtitle.toLowerCase().includes(query.toLowerCase()))
16
- );
17
-
18
- useEffect(() => {
19
- const handleClickOutside = (e) => {
20
- if (ref.current && !ref.current.contains(e.target)) {
21
- setIsOpen(false);
22
- }
23
- };
24
-
25
- document.addEventListener('click', handleClickOutside);
26
- return () => document.removeEventListener('click', handleClickOutside);
27
- }, []);
28
-
29
- const handleSelect = (item) => {
30
- setQuery(item.title);
31
- setIsOpen(false);
32
- onSelect?.(item);
33
- };
34
-
35
- return (
36
- <div ref={ref} className={`cn-search ${isOpen ? 'cn-search-open' : ''} ${className}`}>
37
- <svg className="cn-search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
38
- <circle cx="11" cy="11" r="8"></circle>
39
- <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
40
- </svg>
41
- <input
42
- type="text"
43
- className="cn-input cn-search-input"
44
- placeholder={placeholder}
45
- value={query}
46
- onChange={(e) => {
47
- setQuery(e.target.value);
48
- setIsOpen(true);
49
- }}
50
- onFocus={() => setIsOpen(true)}
51
- />
52
- {isOpen && filtered.length > 0 && (
53
- <div className="cn-search-results">
54
- {filtered.map((item, idx) => (
55
- <div
56
- key={idx}
57
- className="cn-search-result"
58
- onClick={() => handleSelect(item)}
59
- >
60
- <div className="cn-search-result-title">{item.title}</div>
61
- {item.subtitle && (
62
- <div className="cn-search-result-subtitle">{item.subtitle}</div>
63
- )}
64
- </div>
65
- ))}
66
- </div>
67
- )}
68
- </div>
69
- );
70
- }
@@ -1,33 +0,0 @@
1
- import { forwardRef } from 'react';
2
-
3
- const Select = forwardRef(function Select({
4
- options = [],
5
- value,
6
- onChange,
7
- placeholder = 'Select...',
8
- disabled = false,
9
- className = '',
10
- ...props
11
- }, ref) {
12
- return (
13
- <div className={`cn-select-wrapper ${className}`}>
14
- <select
15
- ref={ref}
16
- className="cn-select"
17
- value={value}
18
- onChange={(e) => onChange?.(e.target.value)}
19
- disabled={disabled}
20
- {...props}
21
- >
22
- {placeholder && <option value="" disabled>{placeholder}</option>}
23
- {options.map((opt, idx) => (
24
- <option key={idx} value={opt.value}>
25
- {opt.label}
26
- </option>
27
- ))}
28
- </select>
29
- </div>
30
- );
31
- });
32
-
33
- export default Select;
@@ -1,15 +0,0 @@
1
- export default function Skeleton({
2
- variant = 'text',
3
- width,
4
- height,
5
- className = ''
6
- }) {
7
- const variantClass = variant !== 'text' ? `cn-skeleton-${variant}` : 'cn-skeleton';
8
-
9
- const style = {
10
- width: width,
11
- height: height,
12
- };
13
-
14
- return <div className={`${variantClass} ${className}`} style={style}></div>;
15
- }
@@ -1,29 +0,0 @@
1
- import { forwardRef } from 'react';
2
-
3
- const Slider = forwardRef(function Slider({
4
- value = 0,
5
- min = 0,
6
- max = 100,
7
- step = 1,
8
- onChange,
9
- disabled = false,
10
- className = '',
11
- ...props
12
- }, ref) {
13
- return (
14
- <input
15
- ref={ref}
16
- type="range"
17
- className={`cn-slider ${className}`}
18
- value={value}
19
- min={min}
20
- max={max}
21
- step={step}
22
- onChange={(e) => onChange?.(Number(e.target.value))}
23
- disabled={disabled}
24
- {...props}
25
- />
26
- );
27
- });
28
-
29
- export default Slider;
@@ -1,5 +0,0 @@
1
- export default function Spinner({ size = 'md', className = '' }) {
2
- const sizeClass = size !== 'md' ? `cn-spinner-${size}` : '';
3
-
4
- return <div className={`cn-spinner ${sizeClass} ${className}`}></div>;
5
- }
@@ -1,19 +0,0 @@
1
- export default function Stat({
2
- value,
3
- label,
4
- delta,
5
- deltaType = 'up',
6
- className = ''
7
- }) {
8
- return (
9
- <div className={`cn-stat ${className}`}>
10
- <div className="cn-stat-value">{value}</div>
11
- {label && <div className="cn-stat-label">{label}</div>}
12
- {delta && (
13
- <div className={`cn-stat-delta cn-stat-delta-${deltaType}`}>
14
- {deltaType === 'up' ? '↑' : '↓'} {delta}
15
- </div>
16
- )}
17
- </div>
18
- );
19
- }
@@ -1,48 +0,0 @@
1
- import { forwardRef } from 'react';
2
-
3
- const Table = forwardRef(function Table({
4
- columns = [],
5
- data = [],
6
- sortable = false,
7
- onSort,
8
- className = ''
9
- }, ref) {
10
- const handleSort = (column) => {
11
- if (!sortable || !column.sortable) return;
12
- onSort?.(column.key);
13
- };
14
-
15
- return (
16
- <div className={`cn-table-wrapper ${sortable ? 'cn-table-sortable' : ''} ${className}`}>
17
- <table className="cn-table" ref={ref}>
18
- <thead>
19
- <tr>
20
- {columns.map((col, idx) => (
21
- <th
22
- key={idx}
23
- onClick={() => handleSort(col)}
24
- data-sort={col.key}
25
- >
26
- {col.header}
27
- {col.sortable && ' ↕'}
28
- </th>
29
- ))}
30
- </tr>
31
- </thead>
32
- <tbody>
33
- {data.map((row, idx) => (
34
- <tr key={idx}>
35
- {columns.map((col, colIdx) => (
36
- <td key={colIdx}>
37
- {col.render ? col.render(row) : row[col.key]}
38
- </td>
39
- ))}
40
- </tr>
41
- ))}
42
- </tbody>
43
- </table>
44
- </div>
45
- );
46
- });
47
-
48
- export default Table;
@@ -1,65 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- export default function Tabs({
4
- defaultIndex = 0,
5
- index: controlledIndex,
6
- onChange,
7
- children,
8
- className = ''
9
- }) {
10
- const [internalIndex, setInternalIndex] = useState(defaultIndex);
11
- const activeIndex = controlledIndex !== undefined ? controlledIndex : internalIndex;
12
-
13
- const handleTabClick = (idx) => {
14
- if (onChange) {
15
- onChange(idx);
16
- } else {
17
- setInternalIndex(idx);
18
- }
19
- };
20
-
21
- const tabs = [];
22
- const panels = [];
23
-
24
- children.forEach((child) => {
25
- if (child.type?.displayName === 'Tab') {
26
- tabs.push(child);
27
- } else if (child.type?.displayName === 'TabPanel') {
28
- panels.push(child);
29
- }
30
- });
31
-
32
- return (
33
- <div className={className}>
34
- <div className="cn-tabs">
35
- {tabs.map((tab, idx) => (
36
- <div
37
- key={idx}
38
- className={`cn-tab ${activeIndex === idx ? 'cn-tab-active' : ''}`}
39
- onClick={() => handleTabClick(idx)}
40
- >
41
- {tab.props.children}
42
- </div>
43
- ))}
44
- </div>
45
- {panels.map((panel, idx) => (
46
- <div
47
- key={idx}
48
- className={`cn-tab-panel ${activeIndex === idx ? 'cn-tab-panel-active' : ''}`}
49
- >
50
- {panel.props.children}
51
- </div>
52
- ))}
53
- </div>
54
- );
55
- }
56
-
57
- export function Tab({ children }) {
58
- return children;
59
- }
60
- Tab.displayName = 'Tab';
61
-
62
- export function TabPanel({ children }) {
63
- return children;
64
- }
65
- TabPanel.displayName = 'TabPanel';
@@ -1,19 +0,0 @@
1
- export default function Tag({
2
- children,
3
- onRemove,
4
- className = ''
5
- }) {
6
- return (
7
- <div className={`cn-tag ${className}`}>
8
- <span>{children}</span>
9
- {onRemove && (
10
- <button className="cn-tag-remove" onClick={onRemove}>
11
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
12
- <line x1="18" y1="6" x2="6" y2="18"></line>
13
- <line x1="6" y1="6" x2="18" y2="18"></line>
14
- </svg>
15
- </button>
16
- )}
17
- </div>
18
- );
19
- }
@@ -1,17 +0,0 @@
1
- import { forwardRef } from 'react';
2
-
3
- const Textarea = forwardRef(function Textarea({
4
- error = false,
5
- className = '',
6
- ...props
7
- }, ref) {
8
- return (
9
- <textarea
10
- ref={ref}
11
- className={`cn-input cn-textarea ${error ? 'cn-input-error' : ''} ${className}`}
12
- {...props}
13
- />
14
- );
15
- });
16
-
17
- export default Textarea;