cronixui 1.0.6 → 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 (129) hide show
  1. package/README.md +35 -5
  2. package/package.json +19 -5
  3. package/packages/go/cronixui/cronixui.go +784 -237
  4. package/packages/go/cronixui/go.mod +32 -0
  5. package/packages/go/cronixui/go.sum +666 -0
  6. package/packages/python/cronixui/__init__.py +59 -3
  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/tooltip.py +28 -0
  19. package/packages/react/src/components/Accordion.tsx +82 -0
  20. package/packages/react/src/components/Alert.tsx +80 -0
  21. package/packages/react/src/components/Avatar.tsx +54 -0
  22. package/packages/react/src/components/Badge.tsx +32 -0
  23. package/packages/react/src/components/Breadcrumb.tsx +50 -0
  24. package/packages/react/src/components/Button.tsx +47 -0
  25. package/packages/react/src/components/Card.tsx +69 -0
  26. package/packages/react/src/components/Checkbox.tsx +30 -0
  27. package/packages/react/src/components/CommandPalette.tsx +131 -0
  28. package/packages/react/src/components/Container.tsx +26 -0
  29. package/packages/react/src/components/Dropdown.tsx +88 -0
  30. package/packages/react/src/components/FileInput.tsx +86 -0
  31. package/packages/react/src/components/Footer.tsx +36 -0
  32. package/packages/react/src/components/FormGroup.tsx +36 -0
  33. package/packages/react/src/components/Header.tsx +29 -0
  34. package/packages/react/src/components/Input.tsx +54 -0
  35. package/packages/react/src/components/List.tsx +55 -0
  36. package/packages/react/src/components/Modal.tsx +89 -0
  37. package/packages/react/src/components/Nav.tsx +63 -0
  38. package/packages/react/src/components/Pagination.tsx +107 -0
  39. package/packages/react/src/components/Progress.tsx +49 -0
  40. package/packages/react/src/components/Radio.tsx +64 -0
  41. package/packages/react/src/components/Search.tsx +95 -0
  42. package/packages/react/src/components/Select.tsx +41 -0
  43. package/packages/react/src/components/Sidebar.tsx +64 -0
  44. package/packages/react/src/components/Skeleton.tsx +39 -0
  45. package/packages/react/src/components/Slider.tsx +32 -0
  46. package/packages/react/src/components/Spinner.tsx +24 -0
  47. package/packages/react/src/components/Stack.tsx +69 -0
  48. package/packages/react/src/components/Stat.tsx +35 -0
  49. package/packages/react/src/components/Table.tsx +90 -0
  50. package/packages/react/src/components/Tabs.tsx +85 -0
  51. package/packages/react/src/components/Tag.tsx +30 -0
  52. package/packages/react/src/components/Textarea.tsx +21 -0
  53. package/packages/react/src/components/Toast.tsx +134 -0
  54. package/packages/react/src/components/Toggle.tsx +58 -0
  55. package/packages/react/src/components/Tooltip.tsx +31 -0
  56. package/packages/react/src/components/Typography.tsx +66 -0
  57. package/packages/react/src/index.ts +40 -0
  58. package/packages/react/src/styles.css +2039 -0
  59. package/packages/react/src/tokens/index.ts +94 -0
  60. package/packages/rust/cronixui/src/colors.rs +135 -0
  61. package/packages/rust/cronixui/src/components/accordion.rs +47 -0
  62. package/packages/rust/cronixui/src/components/alert.rs +95 -0
  63. package/packages/rust/cronixui/src/components/avatar.rs +85 -0
  64. package/packages/rust/cronixui/src/components/badge.rs +35 -0
  65. package/packages/rust/cronixui/src/components/breadcrumb.rs +58 -0
  66. package/packages/rust/cronixui/src/components/button.rs +70 -0
  67. package/packages/rust/cronixui/src/components/card.rs +259 -0
  68. package/packages/rust/cronixui/src/components/command_palette.rs +254 -0
  69. package/packages/rust/cronixui/src/components/dropdown.rs +179 -0
  70. package/packages/rust/cronixui/src/components/file_input.rs +74 -0
  71. package/packages/rust/cronixui/src/components/input.rs +21 -0
  72. package/packages/rust/cronixui/src/components/list.rs +38 -0
  73. package/packages/rust/cronixui/src/components/mod.rs +51 -0
  74. package/packages/rust/cronixui/src/{modal.rs → components/modal.rs} +15 -1
  75. package/packages/rust/cronixui/src/components/nav.rs +19 -0
  76. package/packages/rust/cronixui/src/{pagination.rs → components/pagination.rs} +14 -13
  77. package/packages/rust/cronixui/src/components/progress.rs +50 -0
  78. package/packages/rust/cronixui/src/components/search.rs +185 -0
  79. package/packages/rust/cronixui/src/components/skeleton.rs +63 -0
  80. package/packages/rust/cronixui/src/components/spinner.rs +21 -0
  81. package/packages/rust/cronixui/src/components/table.rs +56 -0
  82. package/packages/rust/cronixui/src/components/tabs.rs +43 -0
  83. package/packages/rust/cronixui/src/components/toast.rs +69 -0
  84. package/packages/rust/cronixui/src/{toggle.rs → components/toggle.rs} +7 -5
  85. package/packages/rust/cronixui/src/components/tooltip.rs +11 -0
  86. package/packages/rust/cronixui/src/lib.rs +111 -64
  87. package/packages/rust/cronixui/src/tokens.rs +97 -127
  88. package/packages/web/src/variables.css +81 -81
  89. package/packages/go/cronixui/tokens.go +0 -129
  90. package/packages/python/cronixui/pyproject.toml +0 -11
  91. package/packages/react/src/components/Accordion.jsx +0 -50
  92. package/packages/react/src/components/Alert.jsx +0 -62
  93. package/packages/react/src/components/Avatar.jsx +0 -34
  94. package/packages/react/src/components/Badge.jsx +0 -15
  95. package/packages/react/src/components/Breadcrumb.jsx +0 -27
  96. package/packages/react/src/components/Button.jsx +0 -21
  97. package/packages/react/src/components/Card.jsx +0 -23
  98. package/packages/react/src/components/Checkbox.jsx +0 -27
  99. package/packages/react/src/components/CommandPalette.jsx +0 -93
  100. package/packages/react/src/components/Dropdown.jsx +0 -48
  101. package/packages/react/src/components/FileInput.jsx +0 -44
  102. package/packages/react/src/components/Input.jsx +0 -22
  103. package/packages/react/src/components/List.jsx +0 -29
  104. package/packages/react/src/components/Modal.jsx +0 -65
  105. package/packages/react/src/components/Nav.jsx +0 -50
  106. package/packages/react/src/components/Pagination.jsx +0 -81
  107. package/packages/react/src/components/Progress.jsx +0 -23
  108. package/packages/react/src/components/Radio.jsx +0 -50
  109. package/packages/react/src/components/Search.jsx +0 -70
  110. package/packages/react/src/components/Select.jsx +0 -33
  111. package/packages/react/src/components/Skeleton.jsx +0 -15
  112. package/packages/react/src/components/Slider.jsx +0 -29
  113. package/packages/react/src/components/Spinner.jsx +0 -5
  114. package/packages/react/src/components/Stat.jsx +0 -19
  115. package/packages/react/src/components/Table.jsx +0 -48
  116. package/packages/react/src/components/Tabs.jsx +0 -65
  117. package/packages/react/src/components/Tag.jsx +0 -19
  118. package/packages/react/src/components/Textarea.jsx +0 -17
  119. package/packages/react/src/components/Toast.jsx +0 -78
  120. package/packages/react/src/components/Toggle.jsx +0 -34
  121. package/packages/react/src/components/Tooltip.jsx +0 -12
  122. package/packages/react/src/index.d.ts +0 -103
  123. package/packages/react/src/index.js +0 -33
  124. package/packages/rust/cronixui/src/accordion.rs +0 -49
  125. package/packages/rust/cronixui/src/command_palette.rs +0 -62
  126. package/packages/rust/cronixui/src/dropdown.rs +0 -31
  127. package/packages/rust/cronixui/src/search.rs +0 -49
  128. package/packages/rust/cronixui/src/tabs.rs +0 -23
  129. package/packages/rust/cronixui/src/toast.rs +0 -70
@@ -1,48 +0,0 @@
1
- import { useState, useEffect, useRef } from 'react';
2
-
3
- export default function Dropdown({
4
- trigger,
5
- children,
6
- className = ''
7
- }) {
8
- const [isOpen, setIsOpen] = useState(false);
9
- const dropdownRef = useRef(null);
10
-
11
- const toggle = () => setIsOpen((prev) => !prev);
12
- const close = () => setIsOpen(false);
13
-
14
- useEffect(() => {
15
- const handleClickOutside = (e) => {
16
- if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
17
- close();
18
- }
19
- };
20
-
21
- if (isOpen) {
22
- document.addEventListener('click', handleClickOutside);
23
- }
24
-
25
- return () => {
26
- document.removeEventListener('click', handleClickOutside);
27
- };
28
- }, [isOpen]);
29
-
30
- return (
31
- <div ref={dropdownRef} className={`cn-dropdown ${isOpen ? 'cn-dropdown-open' : ''} ${className}`}>
32
- <div className="cn-dropdown-trigger" onClick={toggle}>
33
- {trigger}
34
- </div>
35
- <div className="cn-dropdown-menu" onClick={close}>
36
- {children}
37
- </div>
38
- </div>
39
- );
40
- }
41
-
42
- export function DropdownItem({ children, onClick, className = '', ...props }) {
43
- return (
44
- <div className={`cn-dropdown-item ${className}`} onClick={onClick} {...props}>
45
- {children}
46
- </div>
47
- );
48
- }
@@ -1,44 +0,0 @@
1
- import { useState, useRef } from 'react';
2
-
3
- export default function FileInput({
4
- onFileSelect,
5
- accept,
6
- multiple = false,
7
- label = 'Drag and drop files here, or click to browse',
8
- className = ''
9
- }) {
10
- const [fileName, setFileName] = useState('');
11
- const inputRef = useRef(null);
12
-
13
- const handleChange = (e) => {
14
- const files = Array.from(e.target.files);
15
- if (files.length > 0) {
16
- setFileName(files.map(f => f.name).join(', '));
17
- onFileSelect?.(multiple ? files : files[0]);
18
- }
19
- };
20
-
21
- return (
22
- <div className={`cn-file-input ${fileName ? 'cn-file-input-has-file' : ''} ${className}`}>
23
- <input
24
- ref={inputRef}
25
- type="file"
26
- accept={accept}
27
- multiple={multiple}
28
- onChange={handleChange}
29
- />
30
- <div className="cn-file-input-label">
31
- <div className="cn-file-input-icon">
32
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5">
33
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
34
- <polyline points="17 8 12 3 7 8"></polyline>
35
- <line x1="12" y1="3" x2="12" y2="15"></line>
36
- </svg>
37
- </div>
38
- <div className="cn-file-input-text">
39
- {fileName ? <span>{fileName}</span> : label}
40
- </div>
41
- </div>
42
- </div>
43
- );
44
- }
@@ -1,22 +0,0 @@
1
- import { forwardRef } from 'react';
2
-
3
- const Input = forwardRef(function Input({
4
- type = 'text',
5
- size = 'md',
6
- error = false,
7
- className = '',
8
- ...props
9
- }, ref) {
10
- const sizeClass = size !== 'md' ? `cn-input-${size}` : '';
11
-
12
- return (
13
- <input
14
- ref={ref}
15
- type={type}
16
- className={`cn-input ${sizeClass} ${error ? 'cn-input-error' : ''} ${className}`}
17
- {...props}
18
- />
19
- );
20
- });
21
-
22
- export default Input;
@@ -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;