@camtomlabs/malix-design-system 0.1.2 → 0.1.4

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 (58) hide show
  1. package/LICENSE +19 -5
  2. package/README.md +141 -10
  3. package/dist/index.cjs +2456 -0
  4. package/dist/index.d.cts +562 -0
  5. package/dist/index.d.ts +562 -0
  6. package/dist/index.js +2367 -0
  7. package/package.json +21 -8
  8. package/src/styles.css +186 -0
  9. package/src/components/Accordion.tsx +0 -52
  10. package/src/components/Avatar.tsx +0 -18
  11. package/src/components/Badge.tsx +0 -27
  12. package/src/components/Banner.tsx +0 -75
  13. package/src/components/Breadcrumb.tsx +0 -58
  14. package/src/components/Button.tsx +0 -47
  15. package/src/components/Card.tsx +0 -34
  16. package/src/components/ChatInput.tsx +0 -53
  17. package/src/components/Checkbox.tsx +0 -85
  18. package/src/components/CreditsIndicator.tsx +0 -41
  19. package/src/components/DataTable.tsx +0 -75
  20. package/src/components/DateInput.tsx +0 -57
  21. package/src/components/Divider.tsx +0 -12
  22. package/src/components/Dropzone.tsx +0 -94
  23. package/src/components/EmptyState.tsx +0 -65
  24. package/src/components/FileCard.tsx +0 -78
  25. package/src/components/FilterTabs.tsx +0 -49
  26. package/src/components/FlyoutMenu.tsx +0 -36
  27. package/src/components/GlassPopover.tsx +0 -38
  28. package/src/components/Header.tsx +0 -22
  29. package/src/components/Input.tsx +0 -18
  30. package/src/components/InputGroup.tsx +0 -37
  31. package/src/components/LanguageSelector.tsx +0 -81
  32. package/src/components/Modal.tsx +0 -104
  33. package/src/components/OnboardingPopover.tsx +0 -61
  34. package/src/components/OperationStatus.tsx +0 -73
  35. package/src/components/Overlay.tsx +0 -66
  36. package/src/components/Pagination.tsx +0 -89
  37. package/src/components/Pill.tsx +0 -19
  38. package/src/components/PricingCard.tsx +0 -74
  39. package/src/components/ProgressBar.tsx +0 -47
  40. package/src/components/Radio.tsx +0 -56
  41. package/src/components/SectionHeader.tsx +0 -32
  42. package/src/components/SegmentedControl.tsx +0 -42
  43. package/src/components/Select.tsx +0 -62
  44. package/src/components/SelectGroup.tsx +0 -32
  45. package/src/components/SelectionCard.tsx +0 -47
  46. package/src/components/SidebarItem.tsx +0 -27
  47. package/src/components/SidebarPanel.tsx +0 -84
  48. package/src/components/SplitPane.tsx +0 -85
  49. package/src/components/StatCard.tsx +0 -64
  50. package/src/components/StatusDot.tsx +0 -26
  51. package/src/components/Stepper.tsx +0 -40
  52. package/src/components/TabBar.tsx +0 -45
  53. package/src/components/Textarea.tsx +0 -43
  54. package/src/components/Toggle.tsx +0 -50
  55. package/src/components/Tooltip.tsx +0 -33
  56. package/src/components/UserProfilePopover.tsx +0 -100
  57. package/src/components/ValidationAlert.tsx +0 -72
  58. package/src/index.ts +0 -177
@@ -1,37 +0,0 @@
1
- import React from 'react';
2
-
3
- export type InputGroupProps = React.InputHTMLAttributes<HTMLInputElement> & {
4
- label?: string;
5
- helperText?: string;
6
- leadingIcon?: React.ReactNode;
7
- error?: boolean;
8
- };
9
-
10
- export function InputGroup({
11
- label,
12
- helperText,
13
- leadingIcon,
14
- error,
15
- id,
16
- className,
17
- ...props
18
- }: InputGroupProps) {
19
- const inputId = id || `input-${React.useId()}`;
20
-
21
- return (
22
- <div className={`malix-input-group${className ? ` ${className}` : ''}`} data-error={error || undefined}>
23
- {label ? (
24
- <label htmlFor={inputId} className="malix-input-group__label">
25
- {label}
26
- </label>
27
- ) : null}
28
- <div className="malix-input-group__field">
29
- {leadingIcon ? <span className="malix-input-group__icon">{leadingIcon}</span> : null}
30
- <input id={inputId} className="malix-input" {...props} />
31
- </div>
32
- {helperText ? (
33
- <span className="malix-input-group__helper">{helperText}</span>
34
- ) : null}
35
- </div>
36
- );
37
- }
@@ -1,81 +0,0 @@
1
- import React from 'react';
2
-
3
- export type LanguageSelectorOption = {
4
- value: string;
5
- label: string;
6
- };
7
-
8
- export type LanguageSelectorProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> & {
9
- value: string;
10
- options?: LanguageSelectorOption[];
11
- onChange?: (value: string) => void;
12
- };
13
-
14
- export function LanguageSelector({
15
- value,
16
- options,
17
- onChange,
18
- className,
19
- ...props
20
- }: LanguageSelectorProps) {
21
- const selectedOption = options?.find((opt) => opt.value === value);
22
- const displayLabel = selectedOption?.label ?? value;
23
-
24
- return (
25
- <div
26
- className={`malix-language-selector${className ? ` ${className}` : ''}`}
27
- {...props}
28
- >
29
- <span className="malix-language-selector__icon" aria-hidden="true">
30
- <svg
31
- width="16"
32
- height="16"
33
- viewBox="0 0 24 24"
34
- fill="none"
35
- stroke="currentColor"
36
- strokeWidth="2"
37
- strokeLinecap="round"
38
- strokeLinejoin="round"
39
- >
40
- <circle cx="12" cy="12" r="10" />
41
- <line x1="2" y1="12" x2="22" y2="12" />
42
- <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10A15.3 15.3 0 0 1 12 2z" />
43
- </svg>
44
- </span>
45
-
46
- <select
47
- className="malix-language-selector__select"
48
- value={value}
49
- onChange={(e) => onChange?.(e.target.value)}
50
- aria-label="Select language"
51
- >
52
- {options ? (
53
- options.map((opt) => (
54
- <option key={opt.value} value={opt.value}>
55
- {opt.label}
56
- </option>
57
- ))
58
- ) : (
59
- <option value={value}>{displayLabel}</option>
60
- )}
61
- </select>
62
-
63
- <span className="malix-language-selector__label">{displayLabel}</span>
64
-
65
- <span className="malix-language-selector__chevron" aria-hidden="true">
66
- <svg
67
- width="14"
68
- height="14"
69
- viewBox="0 0 24 24"
70
- fill="none"
71
- stroke="currentColor"
72
- strokeWidth="2"
73
- strokeLinecap="round"
74
- strokeLinejoin="round"
75
- >
76
- <polyline points="6 9 12 15 18 9" />
77
- </svg>
78
- </span>
79
- </div>
80
- );
81
- }
@@ -1,104 +0,0 @@
1
- import React, { useEffect, useRef } from 'react';
2
-
3
- const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
4
-
5
- export type ModalProps = {
6
- open: boolean;
7
- title: string;
8
- onClose: () => void;
9
- onConfirm?: () => void;
10
- confirmLabel?: string;
11
- cancelLabel?: string;
12
- children: React.ReactNode;
13
- };
14
-
15
- export function Modal({
16
- open,
17
- title,
18
- onClose,
19
- onConfirm,
20
- confirmLabel = 'Confirm',
21
- cancelLabel = 'Cancel',
22
- children,
23
- }: ModalProps) {
24
- const panelRef = useRef<HTMLDivElement | null>(null);
25
-
26
- useEffect(() => {
27
- if (!open || !panelRef.current) return;
28
-
29
- const panel = panelRef.current;
30
- const focusables = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
31
- const first = focusables[0];
32
- const last = focusables[focusables.length - 1];
33
-
34
- first?.focus();
35
-
36
- function onKeyDown(event: KeyboardEvent) {
37
- if (event.key === 'Escape') {
38
- event.preventDefault();
39
- onClose();
40
- }
41
-
42
- if (event.key === 'Tab' && focusables.length > 0) {
43
- if (event.shiftKey && document.activeElement === first) {
44
- event.preventDefault();
45
- last?.focus();
46
- } else if (!event.shiftKey && document.activeElement === last) {
47
- event.preventDefault();
48
- first?.focus();
49
- }
50
- }
51
- }
52
-
53
- document.addEventListener('keydown', onKeyDown);
54
- return () => document.removeEventListener('keydown', onKeyDown);
55
- }, [open, onClose]);
56
-
57
- if (!open) return null;
58
-
59
- return (
60
- <div className="malix-overlay-backdrop" onMouseDown={onClose}>
61
- <div
62
- ref={panelRef}
63
- className="malix-modal"
64
- role="dialog"
65
- aria-modal="true"
66
- aria-label={title}
67
- onMouseDown={(event) => event.stopPropagation()}
68
- >
69
- <div className="malix-modal__header">
70
- <h2 className="malix-modal__title">{title}</h2>
71
- <button
72
- type="button"
73
- className="malix-modal__close"
74
- onClick={onClose}
75
- aria-label="Close"
76
- >
77
- <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg>
78
- </button>
79
- </div>
80
- <div className="malix-modal__body">{children}</div>
81
- <div className="malix-modal__footer">
82
- <button
83
- type="button"
84
- className="malix-button"
85
- data-hierarchy="secondary"
86
- onClick={onClose}
87
- >
88
- <span>{cancelLabel}</span>
89
- </button>
90
- {onConfirm ? (
91
- <button
92
- type="button"
93
- className="malix-button"
94
- data-hierarchy="primary"
95
- onClick={onConfirm}
96
- >
97
- <span>{confirmLabel}</span>
98
- </button>
99
- ) : null}
100
- </div>
101
- </div>
102
- </div>
103
- );
104
- }
@@ -1,61 +0,0 @@
1
- import React from 'react';
2
-
3
- export type OnboardingPopoverProps = React.HTMLAttributes<HTMLDivElement> & {
4
- step: number;
5
- totalSteps: number;
6
- title: string;
7
- description: string;
8
- onNext?: () => void;
9
- onSkip?: () => void;
10
- nextLabel?: string;
11
- };
12
-
13
- export function OnboardingPopover({
14
- step,
15
- totalSteps,
16
- title,
17
- description,
18
- onNext,
19
- onSkip,
20
- nextLabel = 'Next',
21
- className,
22
- ...props
23
- }: OnboardingPopoverProps) {
24
- return (
25
- <div
26
- className={`malix-onboarding-popover${className ? ` ${className}` : ''}`}
27
- role="dialog"
28
- aria-label={`Step ${step} of ${totalSteps}: ${title}`}
29
- {...props}
30
- >
31
- <span className="malix-onboarding-popover__step">
32
- Step {step} of {totalSteps}
33
- </span>
34
-
35
- <h3 className="malix-onboarding-popover__title">{title}</h3>
36
-
37
- <p className="malix-onboarding-popover__description">{description}</p>
38
-
39
- <div className="malix-onboarding-popover__actions">
40
- {onSkip ? (
41
- <button
42
- type="button"
43
- className="malix-onboarding-popover__skip"
44
- onClick={onSkip}
45
- >
46
- Skip
47
- </button>
48
- ) : null}
49
- {onNext ? (
50
- <button
51
- type="button"
52
- className="malix-onboarding-popover__next-btn"
53
- onClick={onNext}
54
- >
55
- {nextLabel}
56
- </button>
57
- ) : null}
58
- </div>
59
- </div>
60
- );
61
- }
@@ -1,73 +0,0 @@
1
- import React from 'react';
2
-
3
- export type OperationStatusType = 'active' | 'completed' | 'failed' | 'pending';
4
-
5
- export type OperationStatusProps = React.HTMLAttributes<HTMLSpanElement> & {
6
- status: OperationStatusType;
7
- label?: string;
8
- };
9
-
10
- const DEFAULT_LABELS: Record<OperationStatusType, string> = {
11
- active: 'Active',
12
- completed: 'Completed',
13
- failed: 'Failed',
14
- pending: 'Pending',
15
- };
16
-
17
- function StatusIcon({ status }: { status: OperationStatusType }) {
18
- const shared = { width: 14, height: 14, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const };
19
-
20
- switch (status) {
21
- case 'active':
22
- // Blinking dot - filled circle, no stroke
23
- return (
24
- <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
25
- <circle cx="7" cy="7" r="4" />
26
- </svg>
27
- );
28
- case 'completed':
29
- return (
30
- <svg {...shared}>
31
- <circle cx="12" cy="12" r="10" />
32
- <path d="m9 12 2 2 4-4" />
33
- </svg>
34
- );
35
- case 'failed':
36
- return (
37
- <svg {...shared}>
38
- <circle cx="12" cy="12" r="10" />
39
- <path d="m15 9-6 6" />
40
- <path d="m9 9 6 6" />
41
- </svg>
42
- );
43
- case 'pending':
44
- return (
45
- <svg {...shared}>
46
- <circle cx="12" cy="12" r="10" />
47
- <polyline points="12 6 12 12 16 14" />
48
- </svg>
49
- );
50
- }
51
- }
52
-
53
- export function OperationStatus({
54
- status,
55
- label,
56
- className,
57
- ...props
58
- }: OperationStatusProps) {
59
- return (
60
- <span
61
- className={`malix-op-status${className ? ` ${className}` : ''}`}
62
- data-status={status}
63
- {...props}
64
- >
65
- <span className="malix-op-status__icon">
66
- <StatusIcon status={status} />
67
- </span>
68
- <span className="malix-op-status__label">
69
- {label ?? DEFAULT_LABELS[status]}
70
- </span>
71
- </span>
72
- );
73
- }
@@ -1,66 +0,0 @@
1
- import React, { useEffect, useRef } from 'react';
2
-
3
- const FOCUSABLE_SELECTOR = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
4
-
5
- export type OverlayProps = {
6
- open: boolean;
7
- title: string;
8
- onClose: () => void;
9
- children: React.ReactNode;
10
- };
11
-
12
- export function Overlay({ open, title, onClose, children }: OverlayProps) {
13
- const panelRef = useRef<HTMLDivElement | null>(null);
14
-
15
- useEffect(() => {
16
- if (!open || !panelRef.current) return;
17
-
18
- const panel = panelRef.current;
19
- const focusables = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));
20
- const first = focusables[0];
21
- const last = focusables[focusables.length - 1];
22
-
23
- first?.focus();
24
-
25
- function onKeyDown(event: KeyboardEvent) {
26
- if (event.key === 'Escape') {
27
- event.preventDefault();
28
- onClose();
29
- }
30
-
31
- if (event.key === 'Tab' && focusables.length > 0) {
32
- if (event.shiftKey && document.activeElement === first) {
33
- event.preventDefault();
34
- last?.focus();
35
- } else if (!event.shiftKey && document.activeElement === last) {
36
- event.preventDefault();
37
- first?.focus();
38
- }
39
- }
40
- }
41
-
42
- document.addEventListener('keydown', onKeyDown);
43
- return () => document.removeEventListener('keydown', onKeyDown);
44
- }, [open, onClose]);
45
-
46
- if (!open) return null;
47
-
48
- return (
49
- <div className="malix-overlay-backdrop" onMouseDown={onClose}>
50
- <div
51
- ref={panelRef}
52
- className="malix-overlay-panel"
53
- role="dialog"
54
- aria-modal="true"
55
- aria-label={title}
56
- onMouseDown={(event) => event.stopPropagation()}
57
- >
58
- <h2 className="malix-overlay-title">{title}</h2>
59
- {children}
60
- <button type="button" className="malix-button malix-overlay-close" data-hierarchy="secondary" onClick={onClose}>
61
- Close
62
- </button>
63
- </div>
64
- </div>
65
- );
66
- }
@@ -1,89 +0,0 @@
1
- import React from 'react';
2
-
3
- export type PaginationVariant = 'full' | 'mini';
4
-
5
- export type PaginationProps = {
6
- currentPage: number;
7
- totalPages: number;
8
- onPageChange: (page: number) => void;
9
- variant?: PaginationVariant;
10
- className?: string;
11
- };
12
-
13
- function ChevronLeft() {
14
- return (
15
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
16
- <path d="M10 12L6 8L10 4" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
17
- </svg>
18
- );
19
- }
20
-
21
- function ChevronRight() {
22
- return (
23
- <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
24
- <path d="M6 4L10 8L6 12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
25
- </svg>
26
- );
27
- }
28
-
29
- export function Pagination({
30
- currentPage,
31
- totalPages,
32
- onPageChange,
33
- variant = 'full',
34
- className,
35
- }: PaginationProps) {
36
- const isFirstPage = currentPage <= 1;
37
- const isLastPage = currentPage >= totalPages;
38
-
39
- const pages = Array.from({ length: totalPages }, (_, i) => i + 1);
40
-
41
- return (
42
- <nav
43
- className={`malix-pagination${className ? ` ${className}` : ''}`}
44
- data-variant={variant}
45
- aria-label="Pagination"
46
- >
47
- <button
48
- type="button"
49
- className="malix-pagination__arrow"
50
- data-disabled={isFirstPage || undefined}
51
- disabled={isFirstPage}
52
- onClick={() => onPageChange(currentPage - 1)}
53
- aria-label="Previous page"
54
- >
55
- <ChevronLeft />
56
- </button>
57
-
58
- {variant === 'full' ? (
59
- pages.map((page) => (
60
- <button
61
- key={page}
62
- type="button"
63
- className="malix-pagination__item"
64
- data-active={page === currentPage || undefined}
65
- aria-current={page === currentPage ? 'page' : undefined}
66
- onClick={() => onPageChange(page)}
67
- >
68
- {page}
69
- </button>
70
- ))
71
- ) : (
72
- <span className="malix-pagination__label">
73
- {currentPage} of {totalPages}
74
- </span>
75
- )}
76
-
77
- <button
78
- type="button"
79
- className="malix-pagination__arrow"
80
- data-disabled={isLastPage || undefined}
81
- disabled={isLastPage}
82
- onClick={() => onPageChange(currentPage + 1)}
83
- aria-label="Next page"
84
- >
85
- <ChevronRight />
86
- </button>
87
- </nav>
88
- );
89
- }
@@ -1,19 +0,0 @@
1
- import React from 'react';
2
-
3
- export type PillVariant = 'default' | 'primary' | 'success' | 'error' | 'warning' | 'info';
4
-
5
- export type PillProps = React.HTMLAttributes<HTMLSpanElement> & {
6
- variant?: PillVariant;
7
- };
8
-
9
- export function Pill({ variant = 'default', children, className, ...props }: PillProps) {
10
- return (
11
- <span
12
- className={`malix-pill${className ? ` ${className}` : ''}`}
13
- data-variant={variant}
14
- {...props}
15
- >
16
- {children}
17
- </span>
18
- );
19
- }
@@ -1,74 +0,0 @@
1
- import React from 'react';
2
-
3
- export type PricingCardProps = React.HTMLAttributes<HTMLDivElement> & {
4
- planName: string;
5
- price: string;
6
- period?: string;
7
- description?: string;
8
- features: string[];
9
- ctaLabel?: string;
10
- onCtaClick?: () => void;
11
- highlighted?: boolean;
12
- };
13
-
14
- export function PricingCard({
15
- planName,
16
- price,
17
- period = '/month',
18
- description,
19
- features,
20
- ctaLabel = 'Get Started',
21
- onCtaClick,
22
- highlighted = false,
23
- className,
24
- ...props
25
- }: PricingCardProps) {
26
- return (
27
- <div
28
- className={`malix-pricing-card${className ? ` ${className}` : ''}`}
29
- data-highlighted={highlighted || undefined}
30
- {...props}
31
- >
32
- <span className="malix-pricing-card__badge">{planName}</span>
33
-
34
- <div className="malix-pricing-card__price-row">
35
- <span className="malix-pricing-card__price">{price}</span>
36
- <span className="malix-pricing-card__period">{period}</span>
37
- </div>
38
-
39
- {description ? (
40
- <p className="malix-pricing-card__description">{description}</p>
41
- ) : null}
42
-
43
- <ul className="malix-pricing-card__features">
44
- {features.map((feature, i) => (
45
- <li key={i} className="malix-pricing-card__feature-item">
46
- <svg
47
- className="malix-pricing-card__check-icon"
48
- width="16"
49
- height="16"
50
- viewBox="0 0 24 24"
51
- fill="none"
52
- stroke="currentColor"
53
- strokeWidth="2"
54
- strokeLinecap="round"
55
- strokeLinejoin="round"
56
- aria-hidden="true"
57
- >
58
- <polyline points="20 6 9 17 4 12" />
59
- </svg>
60
- <span>{feature}</span>
61
- </li>
62
- ))}
63
- </ul>
64
-
65
- <button
66
- type="button"
67
- className="malix-pricing-card__cta"
68
- onClick={onCtaClick}
69
- >
70
- {ctaLabel}
71
- </button>
72
- </div>
73
- );
74
- }
@@ -1,47 +0,0 @@
1
- import React from 'react';
2
-
3
- export type ProgressBarVariant = 'default' | 'success';
4
-
5
- export type ProgressBarProps = React.HTMLAttributes<HTMLDivElement> & {
6
- value: number;
7
- label?: string;
8
- showPercent?: boolean;
9
- variant?: ProgressBarVariant;
10
- };
11
-
12
- export function ProgressBar({
13
- value,
14
- label,
15
- showPercent = true,
16
- variant = 'default',
17
- className,
18
- ...props
19
- }: ProgressBarProps) {
20
- const clampedValue = Math.max(0, Math.min(100, value));
21
-
22
- return (
23
- <div
24
- className={`malix-progress-bar${className ? ` ${className}` : ''}`}
25
- data-variant={variant}
26
- role="progressbar"
27
- aria-valuenow={clampedValue}
28
- aria-valuemin={0}
29
- aria-valuemax={100}
30
- aria-label={label}
31
- {...props}
32
- >
33
- {(label || showPercent) ? (
34
- <div className="malix-progress-bar__label-row">
35
- {label ? <span className="malix-progress-bar__label">{label}</span> : null}
36
- {showPercent ? <span className="malix-progress-bar__percent">{clampedValue}%</span> : null}
37
- </div>
38
- ) : null}
39
- <div className="malix-progress-bar__track">
40
- <div
41
- className="malix-progress-bar__fill"
42
- style={{ width: `${clampedValue}%` }}
43
- />
44
- </div>
45
- </div>
46
- );
47
- }