@camtomlabs/malix-design-system 0.1.2
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/LICENSE +7 -0
- package/README.md +38 -0
- package/package.json +49 -0
- package/src/components/Accordion.tsx +52 -0
- package/src/components/Avatar.tsx +18 -0
- package/src/components/Badge.tsx +27 -0
- package/src/components/Banner.tsx +75 -0
- package/src/components/Breadcrumb.tsx +58 -0
- package/src/components/Button.tsx +47 -0
- package/src/components/Card.tsx +34 -0
- package/src/components/ChatInput.tsx +53 -0
- package/src/components/Checkbox.tsx +85 -0
- package/src/components/CreditsIndicator.tsx +41 -0
- package/src/components/DataTable.tsx +75 -0
- package/src/components/DateInput.tsx +57 -0
- package/src/components/Divider.tsx +12 -0
- package/src/components/Dropzone.tsx +94 -0
- package/src/components/EmptyState.tsx +65 -0
- package/src/components/FileCard.tsx +78 -0
- package/src/components/FilterTabs.tsx +49 -0
- package/src/components/FlyoutMenu.tsx +36 -0
- package/src/components/GlassPopover.tsx +38 -0
- package/src/components/Header.tsx +22 -0
- package/src/components/Input.tsx +18 -0
- package/src/components/InputGroup.tsx +37 -0
- package/src/components/LanguageSelector.tsx +81 -0
- package/src/components/Modal.tsx +104 -0
- package/src/components/OnboardingPopover.tsx +61 -0
- package/src/components/OperationStatus.tsx +73 -0
- package/src/components/Overlay.tsx +66 -0
- package/src/components/Pagination.tsx +89 -0
- package/src/components/Pill.tsx +19 -0
- package/src/components/PricingCard.tsx +74 -0
- package/src/components/ProgressBar.tsx +47 -0
- package/src/components/Radio.tsx +56 -0
- package/src/components/SectionHeader.tsx +32 -0
- package/src/components/SegmentedControl.tsx +42 -0
- package/src/components/Select.tsx +62 -0
- package/src/components/SelectGroup.tsx +32 -0
- package/src/components/SelectionCard.tsx +47 -0
- package/src/components/SidebarItem.tsx +27 -0
- package/src/components/SidebarPanel.tsx +84 -0
- package/src/components/SplitPane.tsx +85 -0
- package/src/components/StatCard.tsx +64 -0
- package/src/components/StatusDot.tsx +26 -0
- package/src/components/Stepper.tsx +40 -0
- package/src/components/TabBar.tsx +45 -0
- package/src/components/Textarea.tsx +43 -0
- package/src/components/Toggle.tsx +50 -0
- package/src/components/Tooltip.tsx +33 -0
- package/src/components/UserProfilePopover.tsx +100 -0
- package/src/components/ValidationAlert.tsx +72 -0
- package/src/index.ts +177 -0
- package/src/styles.css +3237 -0
- package/src/tokens.css +165 -0
- package/src/tokens.registry.json +75 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# @camtomlabs/malix-design-system
|
|
2
|
+
|
|
3
|
+
Malix Design System combined package.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @camtomlabs/malix-design-system
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Import the bundled styles once:
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import '@camtomlabs/malix-design-system/styles.css';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Import components from the combined entrypoint:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { Button, Input, Card } from '@camtomlabs/malix-design-system';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You can also read the token registry:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { tokenRegistry } from '@camtomlabs/malix-design-system';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Note
|
|
32
|
+
|
|
33
|
+
This package publishes raw TypeScript source. Configure your bundler to transpile the package in
|
|
34
|
+
frameworks like Next.js.
|
|
35
|
+
|
|
36
|
+
## License
|
|
37
|
+
|
|
38
|
+
`UNLICENSED`
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@camtomlabs/malix-design-system",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Malix Design System combined package with components, tokens, and bundled styles.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"sideEffects": [
|
|
9
|
+
"./src/styles.css",
|
|
10
|
+
"./src/tokens.css"
|
|
11
|
+
],
|
|
12
|
+
"files": [
|
|
13
|
+
"src"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/carloscamtom/malix-design-system.git",
|
|
18
|
+
"directory": "packages/malix"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/carloscamtom/malix-design-system#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/carloscamtom/malix-design-system/issues"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"malix",
|
|
26
|
+
"design-system",
|
|
27
|
+
"react",
|
|
28
|
+
"components",
|
|
29
|
+
"tokens"
|
|
30
|
+
],
|
|
31
|
+
"license": "UNLICENSED",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": "./src/index.ts",
|
|
34
|
+
"./styles.css": "./src/styles.css",
|
|
35
|
+
"./tokens.css": "./src/tokens.css",
|
|
36
|
+
"./tokens.registry.json": "./src/tokens.registry.json"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
40
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"lint": "eslint src",
|
|
47
|
+
"build": "node -e \"console.log('malix build: source package ready')\""
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
export type AccordionProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
title: string;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
defaultOpen?: boolean;
|
|
7
|
+
icon?: React.ReactNode;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function Accordion({
|
|
11
|
+
title,
|
|
12
|
+
children,
|
|
13
|
+
defaultOpen = false,
|
|
14
|
+
icon,
|
|
15
|
+
className,
|
|
16
|
+
...props
|
|
17
|
+
}: AccordionProps) {
|
|
18
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div
|
|
22
|
+
className={`malix-accordion${className ? ` ${className}` : ''}`}
|
|
23
|
+
data-open={open}
|
|
24
|
+
{...props}
|
|
25
|
+
>
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
className="malix-accordion__header"
|
|
29
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
30
|
+
aria-expanded={open}
|
|
31
|
+
>
|
|
32
|
+
{icon ? <span className="malix-accordion__icon">{icon}</span> : null}
|
|
33
|
+
<span className="malix-accordion__title">{title}</span>
|
|
34
|
+
<svg
|
|
35
|
+
className="malix-accordion__chevron"
|
|
36
|
+
width="16"
|
|
37
|
+
height="16"
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
fill="none"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
strokeWidth="2"
|
|
42
|
+
strokeLinecap="round"
|
|
43
|
+
strokeLinejoin="round"
|
|
44
|
+
aria-hidden="true"
|
|
45
|
+
>
|
|
46
|
+
<polyline points="6 9 12 15 18 9" />
|
|
47
|
+
</svg>
|
|
48
|
+
</button>
|
|
49
|
+
<div className="malix-accordion__body">{children}</div>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type AvatarProps = React.HTMLAttributes<HTMLSpanElement> & {
|
|
4
|
+
initials: string;
|
|
5
|
+
size?: number;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function Avatar({ initials, size = 40, className, style, ...props }: AvatarProps) {
|
|
9
|
+
return (
|
|
10
|
+
<span
|
|
11
|
+
className={`malix-avatar${className ? ` ${className}` : ''}`}
|
|
12
|
+
style={{ width: size, height: size, borderRadius: size * 0.35, ...style }}
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<span className="malix-avatar__text">{initials}</span>
|
|
16
|
+
</span>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type BadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'error';
|
|
4
|
+
|
|
5
|
+
export type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
|
|
6
|
+
variant?: BadgeVariant;
|
|
7
|
+
dot?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function Badge({
|
|
11
|
+
variant = 'default',
|
|
12
|
+
dot = false,
|
|
13
|
+
children,
|
|
14
|
+
className,
|
|
15
|
+
...props
|
|
16
|
+
}: BadgeProps) {
|
|
17
|
+
return (
|
|
18
|
+
<span
|
|
19
|
+
className={`malix-badge${className ? ` ${className}` : ''}`}
|
|
20
|
+
data-variant={variant}
|
|
21
|
+
{...props}
|
|
22
|
+
>
|
|
23
|
+
{dot ? <span className="malix-badge__dot" /> : null}
|
|
24
|
+
<span className="malix-badge__label">{children}</span>
|
|
25
|
+
</span>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type BannerVariant = 'info' | 'success' | 'warning' | 'error';
|
|
4
|
+
|
|
5
|
+
export type BannerProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
variant?: BannerVariant;
|
|
7
|
+
onClose?: () => void;
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const defaultIcons: Record<BannerVariant, React.ReactNode> = {
|
|
12
|
+
info: (
|
|
13
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
14
|
+
<circle cx="12" cy="12" r="10" />
|
|
15
|
+
<line x1="12" y1="16" x2="12" y2="12" />
|
|
16
|
+
<line x1="12" y1="8" x2="12.01" y2="8" />
|
|
17
|
+
</svg>
|
|
18
|
+
),
|
|
19
|
+
success: (
|
|
20
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
21
|
+
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
22
|
+
<polyline points="22 4 12 14.01 9 11.01" />
|
|
23
|
+
</svg>
|
|
24
|
+
),
|
|
25
|
+
warning: (
|
|
26
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
27
|
+
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
|
|
28
|
+
<line x1="12" y1="9" x2="12" y2="13" />
|
|
29
|
+
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
30
|
+
</svg>
|
|
31
|
+
),
|
|
32
|
+
error: (
|
|
33
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
34
|
+
<circle cx="12" cy="12" r="10" />
|
|
35
|
+
<line x1="15" y1="9" x2="9" y2="15" />
|
|
36
|
+
<line x1="9" y1="9" x2="15" y2="15" />
|
|
37
|
+
</svg>
|
|
38
|
+
),
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function Banner({
|
|
42
|
+
variant = 'info',
|
|
43
|
+
onClose,
|
|
44
|
+
icon,
|
|
45
|
+
children,
|
|
46
|
+
className,
|
|
47
|
+
...props
|
|
48
|
+
}: BannerProps) {
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
className={`malix-banner${className ? ` ${className}` : ''}`}
|
|
52
|
+
data-variant={variant}
|
|
53
|
+
role="alert"
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
<span className="malix-banner__icon">
|
|
57
|
+
{icon ?? defaultIcons[variant]}
|
|
58
|
+
</span>
|
|
59
|
+
<span className="malix-banner__content">{children}</span>
|
|
60
|
+
{onClose ? (
|
|
61
|
+
<button
|
|
62
|
+
type="button"
|
|
63
|
+
className="malix-banner__close"
|
|
64
|
+
onClick={onClose}
|
|
65
|
+
aria-label="Dismiss"
|
|
66
|
+
>
|
|
67
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
68
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
69
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
70
|
+
</svg>
|
|
71
|
+
</button>
|
|
72
|
+
) : null}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type BreadcrumbItem = {
|
|
4
|
+
label: string;
|
|
5
|
+
href?: string;
|
|
6
|
+
icon?: React.ReactNode;
|
|
7
|
+
active?: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type BreadcrumbProps = React.HTMLAttributes<HTMLElement> & {
|
|
11
|
+
items: BreadcrumbItem[];
|
|
12
|
+
separator?: React.ReactNode;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const DefaultSeparator = () => (
|
|
16
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
17
|
+
<path d="M6 3L11 8L6 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
|
18
|
+
</svg>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export function Breadcrumb({ items, separator, className, ...props }: BreadcrumbProps) {
|
|
22
|
+
return (
|
|
23
|
+
<nav
|
|
24
|
+
className={`malix-breadcrumb${className ? ` ${className}` : ''}`}
|
|
25
|
+
aria-label="Breadcrumb"
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
{items.map((item, index) => {
|
|
29
|
+
const isLast = index === items.length - 1;
|
|
30
|
+
const isActive = item.active ?? isLast;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<React.Fragment key={index}>
|
|
34
|
+
<span className="malix-breadcrumb__item" data-active={isActive || undefined}>
|
|
35
|
+
{item.icon ? (
|
|
36
|
+
<span className="malix-breadcrumb__icon">{item.icon}</span>
|
|
37
|
+
) : null}
|
|
38
|
+
{isActive || !item.href ? (
|
|
39
|
+
<span className="malix-breadcrumb__label" aria-current={isActive ? 'page' : undefined}>
|
|
40
|
+
{item.label}
|
|
41
|
+
</span>
|
|
42
|
+
) : (
|
|
43
|
+
<a className="malix-breadcrumb__label" href={item.href}>
|
|
44
|
+
{item.label}
|
|
45
|
+
</a>
|
|
46
|
+
)}
|
|
47
|
+
</span>
|
|
48
|
+
{!isLast ? (
|
|
49
|
+
<span className="malix-breadcrumb__separator" aria-hidden="true">
|
|
50
|
+
{separator ?? <DefaultSeparator />}
|
|
51
|
+
</span>
|
|
52
|
+
) : null}
|
|
53
|
+
</React.Fragment>
|
|
54
|
+
);
|
|
55
|
+
})}
|
|
56
|
+
</nav>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type ButtonHierarchy = 'primary' | 'secondary' | 'tertiary' | 'ghost';
|
|
4
|
+
export type ButtonVariant = 'text' | 'leading-icon-text' | 'icon-only' | 'icon-badge';
|
|
5
|
+
|
|
6
|
+
export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
7
|
+
hierarchy?: ButtonHierarchy;
|
|
8
|
+
variant?: ButtonVariant;
|
|
9
|
+
icon?: React.ReactNode;
|
|
10
|
+
badge?: React.ReactNode;
|
|
11
|
+
loading?: boolean;
|
|
12
|
+
label?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function Button({
|
|
16
|
+
hierarchy = 'primary',
|
|
17
|
+
variant = 'text',
|
|
18
|
+
icon,
|
|
19
|
+
badge,
|
|
20
|
+
loading = false,
|
|
21
|
+
disabled,
|
|
22
|
+
children,
|
|
23
|
+
label,
|
|
24
|
+
...props
|
|
25
|
+
}: ButtonProps) {
|
|
26
|
+
const isDisabled = disabled || loading;
|
|
27
|
+
const isIconOnly = variant === 'icon-only';
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
className="malix-button"
|
|
33
|
+
data-hierarchy={hierarchy}
|
|
34
|
+
data-variant={variant}
|
|
35
|
+
data-loading={loading}
|
|
36
|
+
disabled={isDisabled}
|
|
37
|
+
aria-busy={loading || undefined}
|
|
38
|
+
aria-label={isIconOnly ? label : props['aria-label']}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
{icon ? <span className="malix-button__icon">{icon}</span> : null}
|
|
42
|
+
{!isIconOnly ? <span>{loading ? 'Loading...' : children}</span> : null}
|
|
43
|
+
{variant === 'icon-badge' && badge ? <span className="malix-button__badge">{badge}</span> : null}
|
|
44
|
+
{isIconOnly && !icon ? <span>{loading ? '...' : label}</span> : null}
|
|
45
|
+
</button>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type CardLevel = 1 | 2 | 3;
|
|
4
|
+
|
|
5
|
+
export type CardProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
6
|
+
level?: CardLevel;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function Card({
|
|
12
|
+
level = 1,
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
children,
|
|
16
|
+
className,
|
|
17
|
+
...props
|
|
18
|
+
}: CardProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={`malix-card${className ? ` ${className}` : ''}`}
|
|
22
|
+
data-level={level}
|
|
23
|
+
{...props}
|
|
24
|
+
>
|
|
25
|
+
{(title || description) ? (
|
|
26
|
+
<div className="malix-card__header">
|
|
27
|
+
{title ? <h3 className="malix-card__title">{title}</h3> : null}
|
|
28
|
+
{description ? <p className="malix-card__desc">{description}</p> : null}
|
|
29
|
+
</div>
|
|
30
|
+
) : null}
|
|
31
|
+
{children ? <div className="malix-card__body">{children}</div> : null}
|
|
32
|
+
</div>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type ChatInputProps = {
|
|
4
|
+
value: string;
|
|
5
|
+
onChange: (value: string) => void;
|
|
6
|
+
onSend: () => void;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
className?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function ChatInput({
|
|
13
|
+
value,
|
|
14
|
+
onChange,
|
|
15
|
+
onSend,
|
|
16
|
+
placeholder = 'Type a message...',
|
|
17
|
+
disabled,
|
|
18
|
+
className,
|
|
19
|
+
}: ChatInputProps) {
|
|
20
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
21
|
+
if (e.key === 'Enter' && !e.shiftKey && value.trim()) {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
onSend();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className={`malix-chat-input${className ? ` ${className}` : ''}`}>
|
|
29
|
+
<input
|
|
30
|
+
type="text"
|
|
31
|
+
className="malix-chat-input__field"
|
|
32
|
+
value={value}
|
|
33
|
+
onChange={(e) => onChange(e.target.value)}
|
|
34
|
+
onKeyDown={handleKeyDown}
|
|
35
|
+
placeholder={placeholder}
|
|
36
|
+
disabled={disabled}
|
|
37
|
+
aria-label={placeholder}
|
|
38
|
+
/>
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
className="malix-chat-input__send-btn"
|
|
42
|
+
onClick={onSend}
|
|
43
|
+
disabled={disabled || !value.trim()}
|
|
44
|
+
aria-label="Send message"
|
|
45
|
+
>
|
|
46
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
47
|
+
<path d="M12 19V5" />
|
|
48
|
+
<path d="m5 12 7-7 7 7" />
|
|
49
|
+
</svg>
|
|
50
|
+
</button>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type CheckboxProps = Omit<React.HTMLAttributes<HTMLButtonElement>, 'onChange'> & {
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
onChange?: (checked: boolean) => void;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
label?: string;
|
|
8
|
+
indeterminate?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function Checkbox({
|
|
12
|
+
checked = false,
|
|
13
|
+
onChange,
|
|
14
|
+
disabled = false,
|
|
15
|
+
label,
|
|
16
|
+
indeterminate = false,
|
|
17
|
+
className,
|
|
18
|
+
...props
|
|
19
|
+
}: CheckboxProps) {
|
|
20
|
+
const handleClick = () => {
|
|
21
|
+
if (!disabled && onChange) {
|
|
22
|
+
onChange(!checked);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const checkbox = (
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
role="checkbox"
|
|
30
|
+
className={`malix-checkbox${className ? ` ${className}` : ''}`}
|
|
31
|
+
data-checked={checked}
|
|
32
|
+
data-disabled={disabled}
|
|
33
|
+
data-indeterminate={indeterminate}
|
|
34
|
+
aria-checked={indeterminate ? 'mixed' : checked}
|
|
35
|
+
disabled={disabled}
|
|
36
|
+
onClick={handleClick}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
{checked && !indeterminate ? (
|
|
40
|
+
<svg
|
|
41
|
+
className="malix-checkbox__icon"
|
|
42
|
+
viewBox="0 0 12 12"
|
|
43
|
+
fill="none"
|
|
44
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
45
|
+
aria-hidden="true"
|
|
46
|
+
>
|
|
47
|
+
<path
|
|
48
|
+
d="M2 6l3 3 5-5"
|
|
49
|
+
stroke="currentColor"
|
|
50
|
+
strokeWidth="2"
|
|
51
|
+
strokeLinecap="round"
|
|
52
|
+
strokeLinejoin="round"
|
|
53
|
+
/>
|
|
54
|
+
</svg>
|
|
55
|
+
) : null}
|
|
56
|
+
{indeterminate ? (
|
|
57
|
+
<svg
|
|
58
|
+
className="malix-checkbox__icon"
|
|
59
|
+
viewBox="0 0 12 12"
|
|
60
|
+
fill="none"
|
|
61
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
62
|
+
aria-hidden="true"
|
|
63
|
+
>
|
|
64
|
+
<path
|
|
65
|
+
d="M2 6h8"
|
|
66
|
+
stroke="currentColor"
|
|
67
|
+
strokeWidth="2"
|
|
68
|
+
strokeLinecap="round"
|
|
69
|
+
/>
|
|
70
|
+
</svg>
|
|
71
|
+
) : null}
|
|
72
|
+
</button>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (label) {
|
|
76
|
+
return (
|
|
77
|
+
<label className="malix-checkbox-row">
|
|
78
|
+
{checkbox}
|
|
79
|
+
<span className="malix-checkbox-row__label">{label}</span>
|
|
80
|
+
</label>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return checkbox;
|
|
85
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type CreditsIndicatorProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
remaining: number;
|
|
5
|
+
label?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function CreditsIndicator({
|
|
9
|
+
remaining,
|
|
10
|
+
label = 'Credits Remaining',
|
|
11
|
+
className,
|
|
12
|
+
...props
|
|
13
|
+
}: CreditsIndicatorProps) {
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={`malix-credits-indicator${className ? ` ${className}` : ''}`}
|
|
17
|
+
{...props}
|
|
18
|
+
>
|
|
19
|
+
<span className="malix-credits-indicator__icon" aria-hidden="true">
|
|
20
|
+
<svg
|
|
21
|
+
width="20"
|
|
22
|
+
height="20"
|
|
23
|
+
viewBox="0 0 24 24"
|
|
24
|
+
fill="none"
|
|
25
|
+
stroke="currentColor"
|
|
26
|
+
strokeWidth="2"
|
|
27
|
+
strokeLinecap="round"
|
|
28
|
+
strokeLinejoin="round"
|
|
29
|
+
>
|
|
30
|
+
<ellipse cx="12" cy="6" rx="8" ry="3" />
|
|
31
|
+
<path d="M4 6v6c0 1.66 3.58 3 8 3s8-1.34 8-3V6" />
|
|
32
|
+
<path d="M4 12v6c0 1.66 3.58 3 8 3s8-1.34 8-3v-6" />
|
|
33
|
+
</svg>
|
|
34
|
+
</span>
|
|
35
|
+
<div className="malix-credits-indicator__info">
|
|
36
|
+
<span className="malix-credits-indicator__label">{label}</span>
|
|
37
|
+
<span className="malix-credits-indicator__value">{remaining}</span>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type TableColumn = {
|
|
4
|
+
key: string;
|
|
5
|
+
header: string;
|
|
6
|
+
width?: string;
|
|
7
|
+
render?: (value: any, row: TableRow) => React.ReactNode;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type TableRow = Record<string, any>;
|
|
11
|
+
|
|
12
|
+
export type DataTableProps = React.HTMLAttributes<HTMLTableElement> & {
|
|
13
|
+
columns: TableColumn[];
|
|
14
|
+
data: TableRow[];
|
|
15
|
+
onRowClick?: (row: TableRow) => void;
|
|
16
|
+
emptyMessage?: string;
|
|
17
|
+
className?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function DataTable({
|
|
21
|
+
columns,
|
|
22
|
+
data,
|
|
23
|
+
onRowClick,
|
|
24
|
+
emptyMessage,
|
|
25
|
+
className,
|
|
26
|
+
...props
|
|
27
|
+
}: DataTableProps) {
|
|
28
|
+
return (
|
|
29
|
+
<table
|
|
30
|
+
className={`malix-data-table${className ? ` ${className}` : ''}`}
|
|
31
|
+
{...props}
|
|
32
|
+
>
|
|
33
|
+
<thead>
|
|
34
|
+
<tr className="malix-data-table__header-row">
|
|
35
|
+
{columns.map((col) => (
|
|
36
|
+
<th
|
|
37
|
+
key={col.key}
|
|
38
|
+
className="malix-data-table__header-cell"
|
|
39
|
+
style={col.width ? { width: col.width } : undefined}
|
|
40
|
+
>
|
|
41
|
+
{col.header}
|
|
42
|
+
</th>
|
|
43
|
+
))}
|
|
44
|
+
</tr>
|
|
45
|
+
</thead>
|
|
46
|
+
<tbody className="malix-data-table__body">
|
|
47
|
+
{data.length > 0 ? (
|
|
48
|
+
data.map((row, rowIndex) => (
|
|
49
|
+
<tr
|
|
50
|
+
key={rowIndex}
|
|
51
|
+
className="malix-data-table__data-row"
|
|
52
|
+
data-clickable={onRowClick ? true : undefined}
|
|
53
|
+
onClick={onRowClick ? () => onRowClick(row) : undefined}
|
|
54
|
+
>
|
|
55
|
+
{columns.map((col) => (
|
|
56
|
+
<td key={col.key} className="malix-data-table__cell">
|
|
57
|
+
{col.render ? col.render(row[col.key], row) : row[col.key]}
|
|
58
|
+
</td>
|
|
59
|
+
))}
|
|
60
|
+
</tr>
|
|
61
|
+
))
|
|
62
|
+
) : (
|
|
63
|
+
<tr className="malix-data-table__data-row">
|
|
64
|
+
<td
|
|
65
|
+
className="malix-data-table__cell"
|
|
66
|
+
colSpan={columns.length}
|
|
67
|
+
>
|
|
68
|
+
{emptyMessage || 'No data available'}
|
|
69
|
+
</td>
|
|
70
|
+
</tr>
|
|
71
|
+
)}
|
|
72
|
+
</tbody>
|
|
73
|
+
</table>
|
|
74
|
+
);
|
|
75
|
+
}
|