@nukleas/cyberdesign 0.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.
- package/LICENSE +21 -0
- package/README.md +38 -0
- package/build.ts +286 -0
- package/dist/cyberdesign-vars-only.css +116 -0
- package/dist/cyberdesign.css +8377 -0
- package/dist/fonts/JetBrainsMono-Variable.woff2 +0 -0
- package/dist/fonts/OFL.txt +93 -0
- package/dist/react/Accordion.d.ts +34 -0
- package/dist/react/Accordion.js +13 -0
- package/dist/react/Alert.d.ts +19 -0
- package/dist/react/Alert.js +6 -0
- package/dist/react/AugButton.d.ts +12 -0
- package/dist/react/AugButton.js +6 -0
- package/dist/react/AugPanel.d.ts +10 -0
- package/dist/react/AugPanel.js +6 -0
- package/dist/react/Avatar.d.ts +28 -0
- package/dist/react/Avatar.js +11 -0
- package/dist/react/Badge.d.ts +10 -0
- package/dist/react/Badge.js +6 -0
- package/dist/react/Breadcrumb.d.ts +19 -0
- package/dist/react/Breadcrumb.js +10 -0
- package/dist/react/Button.d.ts +12 -0
- package/dist/react/Button.js +6 -0
- package/dist/react/Checkbox.d.ts +9 -0
- package/dist/react/Checkbox.js +9 -0
- package/dist/react/Dropdown.d.ts +35 -0
- package/dist/react/Dropdown.js +20 -0
- package/dist/react/Field.d.ts +13 -0
- package/dist/react/Field.js +6 -0
- package/dist/react/Gauge.d.ts +19 -0
- package/dist/react/Gauge.js +6 -0
- package/dist/react/GlowCard.d.ts +12 -0
- package/dist/react/GlowCard.js +4 -0
- package/dist/react/HudBar.d.ts +34 -0
- package/dist/react/HudBar.js +10 -0
- package/dist/react/HudFrame.d.ts +19 -0
- package/dist/react/HudFrame.js +6 -0
- package/dist/react/Input.d.ts +11 -0
- package/dist/react/Input.js +6 -0
- package/dist/react/Interference.d.ts +18 -0
- package/dist/react/Interference.js +6 -0
- package/dist/react/Kbd.d.ts +18 -0
- package/dist/react/Kbd.js +11 -0
- package/dist/react/Label.d.ts +8 -0
- package/dist/react/Label.js +6 -0
- package/dist/react/Modal.d.ts +19 -0
- package/dist/react/Modal.js +9 -0
- package/dist/react/Pagination.d.ts +23 -0
- package/dist/react/Pagination.js +28 -0
- package/dist/react/PanelHeader.d.ts +9 -0
- package/dist/react/PanelHeader.js +6 -0
- package/dist/react/Progress.d.ts +13 -0
- package/dist/react/Progress.js +6 -0
- package/dist/react/Radio.d.ts +9 -0
- package/dist/react/Radio.js +9 -0
- package/dist/react/Scanner.d.ts +11 -0
- package/dist/react/Scanner.js +6 -0
- package/dist/react/Select.d.ts +13 -0
- package/dist/react/Select.js +6 -0
- package/dist/react/Separator.d.ts +11 -0
- package/dist/react/Separator.js +7 -0
- package/dist/react/Skeleton.d.ts +11 -0
- package/dist/react/Skeleton.js +13 -0
- package/dist/react/Slider.d.ts +9 -0
- package/dist/react/Slider.js +9 -0
- package/dist/react/Spinner.d.ts +11 -0
- package/dist/react/Spinner.js +6 -0
- package/dist/react/Stat.d.ts +20 -0
- package/dist/react/Stat.js +11 -0
- package/dist/react/Switch.d.ts +9 -0
- package/dist/react/Switch.js +6 -0
- package/dist/react/Table.d.ts +55 -0
- package/dist/react/Table.js +36 -0
- package/dist/react/Tabs.d.ts +28 -0
- package/dist/react/Tabs.js +16 -0
- package/dist/react/Terminal.d.ts +33 -0
- package/dist/react/Terminal.js +6 -0
- package/dist/react/Textarea.d.ts +7 -0
- package/dist/react/Textarea.js +6 -0
- package/dist/react/Ticker.d.ts +13 -0
- package/dist/react/Ticker.js +7 -0
- package/dist/react/Toast.d.ts +15 -0
- package/dist/react/Toast.js +6 -0
- package/dist/react/Tooltip.d.ts +11 -0
- package/dist/react/Tooltip.js +6 -0
- package/dist/react/index.d.ts +39 -0
- package/dist/react/index.js +46 -0
- package/dist/tailwind-theme.css +171 -0
- package/dist/tokens/base.d.ts +33 -0
- package/dist/tokens/base.js +41 -0
- package/dist/tokens/index.d.ts +7 -0
- package/dist/tokens/index.js +7 -0
- package/dist/tokens/semantic.d.ts +1 -0
- package/dist/tokens/semantic.js +54 -0
- package/dist/tokens/themes/cyan.d.ts +1 -0
- package/dist/tokens/themes/cyan.js +15 -0
- package/dist/tokens/themes/green.d.ts +1 -0
- package/dist/tokens/themes/green.js +15 -0
- package/dist/tokens/themes/orange.d.ts +1 -0
- package/dist/tokens/themes/orange.js +15 -0
- package/dist/tokens/themes/red.d.ts +1 -0
- package/dist/tokens/themes/red.js +15 -0
- package/dist/tokens/themes/steel.d.ts +1 -0
- package/dist/tokens/themes/steel.js +15 -0
- package/package.json +73 -0
- package/src/css/a11y.css +67 -0
- package/src/css/animations.css +111 -0
- package/src/css/base.css +34 -0
- package/src/css/components/accordion.css +148 -0
- package/src/css/components/alert.css +116 -0
- package/src/css/components/app-patterns.css +894 -0
- package/src/css/components/augmented.css +474 -0
- package/src/css/components/avatar.css +105 -0
- package/src/css/components/badge.css +46 -0
- package/src/css/components/breadcrumb.css +75 -0
- package/src/css/components/button.css +130 -0
- package/src/css/components/calendar.css +135 -0
- package/src/css/components/card.css +68 -0
- package/src/css/components/carousel.css +69 -0
- package/src/css/components/charts.css +464 -0
- package/src/css/components/checkbox.css +136 -0
- package/src/css/components/command.css +162 -0
- package/src/css/components/dropdown.css +187 -0
- package/src/css/components/form.css +280 -0
- package/src/css/components/gauge.css +156 -0
- package/src/css/components/glow-card.css +32 -0
- package/src/css/components/hud.css +165 -0
- package/src/css/components/image.css +164 -0
- package/src/css/components/input-otp.css +53 -0
- package/src/css/components/input.css +182 -0
- package/src/css/components/interference.css +217 -0
- package/src/css/components/kbd.css +54 -0
- package/src/css/components/layout-extras.css +256 -0
- package/src/css/components/modal.css +146 -0
- package/src/css/components/navigation-extras.css +280 -0
- package/src/css/components/pagination.css +122 -0
- package/src/css/components/panel-header.css +55 -0
- package/src/css/components/popover.css +112 -0
- package/src/css/components/scanner.css +125 -0
- package/src/css/components/select.css +99 -0
- package/src/css/components/sheet.css +149 -0
- package/src/css/components/skeleton.css +77 -0
- package/src/css/components/slider.css +116 -0
- package/src/css/components/spinner.css +101 -0
- package/src/css/components/spreadsheet.css +314 -0
- package/src/css/components/svg.css +104 -0
- package/src/css/components/switch.css +114 -0
- package/src/css/components/table.css +115 -0
- package/src/css/components/tabs.css +159 -0
- package/src/css/components/terminal.css +259 -0
- package/src/css/components/ticker.css +100 -0
- package/src/css/components/toast.css +137 -0
- package/src/css/components/toaster.css +48 -0
- package/src/css/components/toggle.css +99 -0
- package/src/css/components/typography.css +122 -0
- package/src/css/components/vectorbg.css +57 -0
- package/src/css/utilities.css +165 -0
- package/src/fonts/JetBrainsMono-Variable.woff2 +0 -0
- package/src/fonts/OFL.txt +93 -0
- package/src/js/charts.js +533 -0
- package/src/react/Accordion.tsx +85 -0
- package/src/react/Alert.tsx +58 -0
- package/src/react/AugButton.tsx +40 -0
- package/src/react/AugPanel.tsx +36 -0
- package/src/react/Avatar.tsx +69 -0
- package/src/react/Badge.tsx +28 -0
- package/src/react/Breadcrumb.tsx +63 -0
- package/src/react/Button.tsx +39 -0
- package/src/react/Checkbox.tsx +31 -0
- package/src/react/Dropdown.tsx +104 -0
- package/src/react/Field.tsx +36 -0
- package/src/react/Gauge.tsx +57 -0
- package/src/react/GlowCard.tsx +25 -0
- package/src/react/HudBar.tsx +86 -0
- package/src/react/HudFrame.tsx +52 -0
- package/src/react/Input.tsx +38 -0
- package/src/react/Interference.tsx +53 -0
- package/src/react/Kbd.tsx +62 -0
- package/src/react/Label.tsx +31 -0
- package/src/react/Modal.tsx +63 -0
- package/src/react/Pagination.tsx +113 -0
- package/src/react/PanelHeader.tsx +28 -0
- package/src/react/Progress.tsx +47 -0
- package/src/react/Radio.tsx +31 -0
- package/src/react/Scanner.tsx +37 -0
- package/src/react/Select.tsx +43 -0
- package/src/react/Separator.tsx +40 -0
- package/src/react/Skeleton.tsx +50 -0
- package/src/react/Slider.tsx +28 -0
- package/src/react/Spinner.tsx +39 -0
- package/src/react/Stat.tsx +52 -0
- package/src/react/Switch.tsx +25 -0
- package/src/react/Table.tsx +144 -0
- package/src/react/Tabs.tsx +97 -0
- package/src/react/Terminal.tsx +95 -0
- package/src/react/Textarea.tsx +20 -0
- package/src/react/Ticker.tsx +46 -0
- package/src/react/Toast.tsx +43 -0
- package/src/react/Tooltip.tsx +32 -0
- package/src/react/index.ts +52 -0
- package/src/tokens/base.ts +48 -0
- package/src/tokens/index.ts +7 -0
- package/src/tokens/semantic.ts +63 -0
- package/src/tokens/themes/cyan.ts +16 -0
- package/src/tokens/themes/green.ts +16 -0
- package/src/tokens/themes/orange.ts +16 -0
- package/src/tokens/themes/red.ts +16 -0
- package/src/tokens/themes/steel.ts +16 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
export interface StatProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/** Uppercase caption above the value. */
|
|
8
|
+
label: ReactNode;
|
|
9
|
+
/** The headline metric. */
|
|
10
|
+
value: ReactNode;
|
|
11
|
+
/** Secondary change/context line below the value. */
|
|
12
|
+
delta?: ReactNode;
|
|
13
|
+
/** Tints the delta green (`up`) or red (`down`). */
|
|
14
|
+
trend?: 'up' | 'down';
|
|
15
|
+
/** Color treatment for the value. Default is the neon-green metric look. */
|
|
16
|
+
variant?: 'default' | 'accent' | 'red' | 'yellow' | 'blue' | 'magenta';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Metric block — uppercase label, neon value and optional trend delta. */
|
|
20
|
+
export function Stat({
|
|
21
|
+
label,
|
|
22
|
+
value,
|
|
23
|
+
delta,
|
|
24
|
+
trend,
|
|
25
|
+
variant = 'default',
|
|
26
|
+
className = '',
|
|
27
|
+
...rest
|
|
28
|
+
}: StatProps) {
|
|
29
|
+
return (
|
|
30
|
+
<div
|
|
31
|
+
className={cx('cd-stat', variant !== 'default' && `cd-stat--${variant}`, className)}
|
|
32
|
+
{...rest}
|
|
33
|
+
>
|
|
34
|
+
<span className="cd-stat__label">{label}</span>
|
|
35
|
+
<span className="cd-stat__value">{value}</span>
|
|
36
|
+
{delta != null && (
|
|
37
|
+
<span className={cx('cd-stat__delta', trend && `cd-stat__delta--${trend}`)}>{delta}</span>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Grid container that separates `Stat` cards with 1px border lines. */
|
|
44
|
+
export function StatGrid({ className = '', children, ...rest }: HTMLAttributes<HTMLDivElement>) {
|
|
45
|
+
return (
|
|
46
|
+
<div className={cx('cd-stats', className)} {...rest}>
|
|
47
|
+
{children}
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Stat.Grid = StatGrid;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
export interface SwitchProps
|
|
7
|
+
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
|
|
8
|
+
/** `sm` is the compact track; default is the standard size. */
|
|
9
|
+
size?: 'sm' | 'md';
|
|
10
|
+
/** Text shown beside the toggle. */
|
|
11
|
+
label?: ReactNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Toggle switch — visually-hidden checkbox driving a neon track + thumb. */
|
|
15
|
+
export function Switch({ size = 'md', label, className = '', ...rest }: SwitchProps) {
|
|
16
|
+
return (
|
|
17
|
+
<label className={cx('cd-switch', size === 'sm' && 'cd-switch--sm', className)}>
|
|
18
|
+
<input type="checkbox" className="cd-switch__input" {...rest} />
|
|
19
|
+
<span className="cd-switch__track">
|
|
20
|
+
<span className="cd-switch__thumb" />
|
|
21
|
+
</span>
|
|
22
|
+
{label != null && <span className="cd-switch__label">{label}</span>}
|
|
23
|
+
</label>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HTMLAttributes,
|
|
3
|
+
TableHTMLAttributes,
|
|
4
|
+
ThHTMLAttributes,
|
|
5
|
+
TdHTMLAttributes,
|
|
6
|
+
ReactNode,
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
10
|
+
parts.filter(Boolean).join(' ');
|
|
11
|
+
|
|
12
|
+
export interface TableProps extends TableHTMLAttributes<HTMLTableElement> {
|
|
13
|
+
/** Tighter padding and smaller type. */
|
|
14
|
+
compact?: boolean;
|
|
15
|
+
/** Draw cell borders. */
|
|
16
|
+
bordered?: boolean;
|
|
17
|
+
/** Wrap the table in a horizontally-scrollable `cd-table-wrap` container. */
|
|
18
|
+
scroll?: boolean;
|
|
19
|
+
/** Props forwarded to the wrapping `cd-table-wrap` div when `scroll` is set. */
|
|
20
|
+
wrapProps?: HTMLAttributes<HTMLDivElement>;
|
|
21
|
+
children?: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Cyber data table — accent uppercase headers, status dots, numeric cells. Compose with Table.Head/Body/Row/HeaderCell/Cell. */
|
|
25
|
+
export function Table({
|
|
26
|
+
compact = false,
|
|
27
|
+
bordered = false,
|
|
28
|
+
scroll = false,
|
|
29
|
+
wrapProps,
|
|
30
|
+
className = '',
|
|
31
|
+
children,
|
|
32
|
+
...rest
|
|
33
|
+
}: TableProps) {
|
|
34
|
+
const table = (
|
|
35
|
+
<table
|
|
36
|
+
className={cx(
|
|
37
|
+
'cd-table',
|
|
38
|
+
compact && 'cd-table--compact',
|
|
39
|
+
bordered && 'cd-table--bordered',
|
|
40
|
+
className,
|
|
41
|
+
)}
|
|
42
|
+
{...rest}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</table>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (!scroll) return table;
|
|
49
|
+
|
|
50
|
+
const { className: wrapClassName = '', ...wrapRest } = wrapProps ?? {};
|
|
51
|
+
return (
|
|
52
|
+
<div className={cx('cd-table-wrap', wrapClassName)} {...wrapRest}>
|
|
53
|
+
{table}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface TableHeadProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|
59
|
+
children?: ReactNode;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** `<thead>` group for the table header row(s). */
|
|
63
|
+
function TableHead({ children, ...rest }: TableHeadProps) {
|
|
64
|
+
return <thead {...rest}>{children}</thead>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface TableBodyProps extends HTMLAttributes<HTMLTableSectionElement> {
|
|
68
|
+
children?: ReactNode;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** `<tbody>` group for the table data rows. */
|
|
72
|
+
function TableBody({ children, ...rest }: TableBodyProps) {
|
|
73
|
+
return <tbody {...rest}>{children}</tbody>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {
|
|
77
|
+
children?: ReactNode;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** `<tr>` table row. */
|
|
81
|
+
function TableRow({ children, ...rest }: TableRowProps) {
|
|
82
|
+
return <tr {...rest}>{children}</tr>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface TableHeaderCellProps extends ThHTMLAttributes<HTMLTableCellElement> {
|
|
86
|
+
/** Right-aligned tabular-numeric header cell (`cd-table__num`). */
|
|
87
|
+
numeric?: boolean;
|
|
88
|
+
/** Mark column sortable; `'asc'`/`'desc'` show a direction arrow (sets `data-sort`). */
|
|
89
|
+
sort?: boolean | 'asc' | 'desc';
|
|
90
|
+
children?: ReactNode;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** `<th>` header cell — supports numeric alignment and a sortable `data-sort` indicator. */
|
|
94
|
+
function TableHeaderCell({
|
|
95
|
+
numeric = false,
|
|
96
|
+
sort,
|
|
97
|
+
className = '',
|
|
98
|
+
children,
|
|
99
|
+
...rest
|
|
100
|
+
}: TableHeaderCellProps) {
|
|
101
|
+
const dataSort = sort === true ? '' : sort || undefined;
|
|
102
|
+
return (
|
|
103
|
+
<th
|
|
104
|
+
className={cx(numeric && 'cd-table__num', className)}
|
|
105
|
+
data-sort={dataSort}
|
|
106
|
+
{...rest}
|
|
107
|
+
>
|
|
108
|
+
{children}
|
|
109
|
+
</th>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface TableCellProps extends TdHTMLAttributes<HTMLTableCellElement> {
|
|
114
|
+
/** Right-aligned tabular-numeric, accent-colored cell (`cd-table__num`). */
|
|
115
|
+
numeric?: boolean;
|
|
116
|
+
/** Status dot prefix (sets `data-status`). */
|
|
117
|
+
status?: 'ok' | 'warn' | 'err' | 'idle';
|
|
118
|
+
children?: ReactNode;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** `<td>` data cell — supports numeric alignment and a leading status dot. */
|
|
122
|
+
function TableCell({
|
|
123
|
+
numeric = false,
|
|
124
|
+
status,
|
|
125
|
+
className = '',
|
|
126
|
+
children,
|
|
127
|
+
...rest
|
|
128
|
+
}: TableCellProps) {
|
|
129
|
+
return (
|
|
130
|
+
<td
|
|
131
|
+
className={cx(numeric && 'cd-table__num', className)}
|
|
132
|
+
data-status={status}
|
|
133
|
+
{...rest}
|
|
134
|
+
>
|
|
135
|
+
{children}
|
|
136
|
+
</td>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Table.Head = TableHead;
|
|
141
|
+
Table.Body = TableBody;
|
|
142
|
+
Table.Row = TableRow;
|
|
143
|
+
Table.HeaderCell = TableHeaderCell;
|
|
144
|
+
Table.Cell = TableCell;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
|
|
4
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
5
|
+
parts.filter(Boolean).join(' ');
|
|
6
|
+
|
|
7
|
+
export interface TabItem {
|
|
8
|
+
/** Tab label (also the trigger text). */
|
|
9
|
+
label: ReactNode;
|
|
10
|
+
/** Panel content shown when this tab is active. */
|
|
11
|
+
content?: ReactNode;
|
|
12
|
+
/** Optional trailing badge node in the trigger (e.g. a count). */
|
|
13
|
+
badge?: ReactNode;
|
|
14
|
+
/** Disable selection of this tab. */
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TabsProps {
|
|
19
|
+
/** The tabs to render, in order. */
|
|
20
|
+
items: TabItem[];
|
|
21
|
+
/** Layout style: underline (default), pills, or vertical. */
|
|
22
|
+
variant?: 'underline' | 'pills' | 'vertical';
|
|
23
|
+
/** Index of the initially-active tab (uncontrolled). */
|
|
24
|
+
defaultActive?: number;
|
|
25
|
+
/** Controlled active index. When set, `onTabChange` should update it. */
|
|
26
|
+
active?: number;
|
|
27
|
+
/** Fires with the new index when a tab is selected. */
|
|
28
|
+
onTabChange?: (index: number) => void;
|
|
29
|
+
/** Hide the panels region (trigger-only, e.g. a filter bar). */
|
|
30
|
+
hidePanels?: boolean;
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Tabbed navigation — underline / pills / vertical, with optional badges and panels. */
|
|
35
|
+
export function Tabs({
|
|
36
|
+
items,
|
|
37
|
+
variant = 'underline',
|
|
38
|
+
defaultActive = 0,
|
|
39
|
+
active,
|
|
40
|
+
onTabChange,
|
|
41
|
+
hidePanels = false,
|
|
42
|
+
className = '',
|
|
43
|
+
}: TabsProps) {
|
|
44
|
+
const [internal, setInternal] = useState(defaultActive);
|
|
45
|
+
const current = active ?? internal;
|
|
46
|
+
|
|
47
|
+
const select = (i: number) => {
|
|
48
|
+
if (items[i]?.disabled) return;
|
|
49
|
+
if (active == null) setInternal(i);
|
|
50
|
+
onTabChange?.(i);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
className={cx(
|
|
56
|
+
'cd-tabs',
|
|
57
|
+
variant === 'pills' && 'cd-tabs--pills',
|
|
58
|
+
variant === 'vertical' && 'cd-tabs--vertical',
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
<div className="cd-tabs__list" role="tablist">
|
|
63
|
+
{items.map((item, i) => (
|
|
64
|
+
<button
|
|
65
|
+
key={i}
|
|
66
|
+
type="button"
|
|
67
|
+
role="tab"
|
|
68
|
+
aria-selected={i === current}
|
|
69
|
+
disabled={item.disabled}
|
|
70
|
+
className={cx('cd-tabs__trigger', i === current && 'is-active')}
|
|
71
|
+
onClick={() => select(i)}
|
|
72
|
+
>
|
|
73
|
+
{item.label}
|
|
74
|
+
{item.badge != null && (
|
|
75
|
+
<span className="cd-badge cd-badge--orange" style={{ marginLeft: 4 }}>
|
|
76
|
+
{item.badge}
|
|
77
|
+
</span>
|
|
78
|
+
)}
|
|
79
|
+
</button>
|
|
80
|
+
))}
|
|
81
|
+
</div>
|
|
82
|
+
{!hidePanels && (
|
|
83
|
+
<div className="cd-tabs__panels">
|
|
84
|
+
{items.map((item, i) => (
|
|
85
|
+
<div
|
|
86
|
+
key={i}
|
|
87
|
+
role="tabpanel"
|
|
88
|
+
className={cx('cd-tabs__panel', i === current && 'is-active')}
|
|
89
|
+
>
|
|
90
|
+
{item.content}
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { HTMLAttributes, InputHTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
/** Semantic line kinds — map to `cd-terminal__line--*` modifiers. */
|
|
7
|
+
export type TerminalLineType = 'system' | 'prompt' | 'command' | 'output' | 'error';
|
|
8
|
+
|
|
9
|
+
export interface TerminalLine {
|
|
10
|
+
/** Line text content. */
|
|
11
|
+
text: ReactNode;
|
|
12
|
+
/** Line kind; omit for the default (plain) line. */
|
|
13
|
+
type?: TerminalLineType;
|
|
14
|
+
/** Prompt glyph shown before the text (for `prompt` lines, e.g. `root@net> `). */
|
|
15
|
+
prompt?: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface TerminalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
19
|
+
/** Header title text (theme-accent colored). Omit to hide the header. */
|
|
20
|
+
title?: ReactNode;
|
|
21
|
+
/** Show the min/max/close window control dots in the header. */
|
|
22
|
+
controls?: boolean;
|
|
23
|
+
/** The log lines to render in the body. */
|
|
24
|
+
lines?: TerminalLine[];
|
|
25
|
+
/** Tighter spacing / smaller type. */
|
|
26
|
+
compact?: boolean;
|
|
27
|
+
/** Extra CRT inner scanline overlay. */
|
|
28
|
+
crt?: boolean;
|
|
29
|
+
/** Render a bottom command input row. */
|
|
30
|
+
input?: boolean;
|
|
31
|
+
/** Prompt glyph shown before the input field. */
|
|
32
|
+
inputPrompt?: ReactNode;
|
|
33
|
+
/** Props forwarded to the input field element. */
|
|
34
|
+
inputProps?: InputHTMLAttributes<HTMLInputElement>;
|
|
35
|
+
/** Extra body content rendered after `lines`. */
|
|
36
|
+
children?: ReactNode;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Cyberpunk terminal/console window — header, scrollable log body and optional command input. */
|
|
40
|
+
export function Terminal({
|
|
41
|
+
title,
|
|
42
|
+
controls = false,
|
|
43
|
+
lines = [],
|
|
44
|
+
compact = false,
|
|
45
|
+
crt = false,
|
|
46
|
+
input = false,
|
|
47
|
+
inputPrompt = 'root@net> ',
|
|
48
|
+
inputProps,
|
|
49
|
+
className = '',
|
|
50
|
+
children,
|
|
51
|
+
...rest
|
|
52
|
+
}: TerminalProps) {
|
|
53
|
+
return (
|
|
54
|
+
<div
|
|
55
|
+
className={cx(
|
|
56
|
+
'cd-terminal',
|
|
57
|
+
compact && 'cd-terminal--compact',
|
|
58
|
+
crt && 'cd-terminal--crt',
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...rest}
|
|
62
|
+
>
|
|
63
|
+
{(title != null || controls) && (
|
|
64
|
+
<div className="cd-terminal__header">
|
|
65
|
+
{title != null && <span className="cd-terminal__title">{title}</span>}
|
|
66
|
+
{controls && (
|
|
67
|
+
<div className="cd-terminal__controls">
|
|
68
|
+
<span className="cd-terminal__dot cd-terminal__dot--min" title="Minimize" />
|
|
69
|
+
<span className="cd-terminal__dot cd-terminal__dot--max" title="Maximize" />
|
|
70
|
+
<span className="cd-terminal__dot cd-terminal__dot--close" title="Close" />
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
<div className="cd-terminal__body cd-scrollbar">
|
|
76
|
+
{lines.map((line, i) => (
|
|
77
|
+
<div
|
|
78
|
+
key={i}
|
|
79
|
+
className={cx('cd-terminal__line', line.type && `cd-terminal__line--${line.type}`)}
|
|
80
|
+
>
|
|
81
|
+
{line.prompt != null && <span className="cd-terminal__prompt">{line.prompt}</span>}
|
|
82
|
+
{line.text}
|
|
83
|
+
</div>
|
|
84
|
+
))}
|
|
85
|
+
{children}
|
|
86
|
+
</div>
|
|
87
|
+
{input && (
|
|
88
|
+
<div className="cd-terminal__input">
|
|
89
|
+
{inputPrompt != null && <span className="cd-terminal__prompt">{inputPrompt}</span>}
|
|
90
|
+
<input className="cd-terminal__field" placeholder="type command..." {...inputProps} />
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TextareaHTMLAttributes } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
export interface TextareaProps
|
|
7
|
+
extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
8
|
+
/** Error state — red border. */
|
|
9
|
+
error?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Cyber multi-line text area — vertical resize, neon focus ring, error state. */
|
|
13
|
+
export function Textarea({ error = false, className = '', ...rest }: TextareaProps) {
|
|
14
|
+
return (
|
|
15
|
+
<textarea
|
|
16
|
+
className={cx('cd-textarea', error && 'cd-textarea--error', className)}
|
|
17
|
+
{...rest}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
export interface TickerProps extends HTMLAttributes<HTMLDivElement> {
|
|
7
|
+
/** The feed items to scroll, in order. */
|
|
8
|
+
items: ReactNode[];
|
|
9
|
+
/** Scroll speed; `fast` ≈ 9s, `slow` ≈ 32s (default ≈ 18s). */
|
|
10
|
+
speed?: 'fast' | 'slow';
|
|
11
|
+
/** Shorthand for `speed="fast"`. */
|
|
12
|
+
fast?: boolean;
|
|
13
|
+
/** Scroll vertically instead of horizontally. */
|
|
14
|
+
vertical?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Auto-scrolling data ticker / feed — pauses on hover, respects reduced motion. */
|
|
18
|
+
export function Ticker({
|
|
19
|
+
items,
|
|
20
|
+
speed,
|
|
21
|
+
fast = false,
|
|
22
|
+
vertical = false,
|
|
23
|
+
className = '',
|
|
24
|
+
...rest
|
|
25
|
+
}: TickerProps) {
|
|
26
|
+
const resolvedSpeed = fast ? 'fast' : speed;
|
|
27
|
+
return (
|
|
28
|
+
<div
|
|
29
|
+
className={cx(
|
|
30
|
+
'cd-ticker',
|
|
31
|
+
resolvedSpeed && `cd-ticker--${resolvedSpeed}`,
|
|
32
|
+
vertical && 'cd-ticker--vertical',
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
{...rest}
|
|
36
|
+
>
|
|
37
|
+
<div className="cd-ticker__inner">
|
|
38
|
+
{items.map((item, i) => (
|
|
39
|
+
<span key={i} className="cd-ticker__item">
|
|
40
|
+
{item}
|
|
41
|
+
</span>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
export interface ToastProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
7
|
+
/** Semantic color. `accent` follows the active theme accent. */
|
|
8
|
+
variant?: 'info' | 'success' | 'warning' | 'error' | 'accent';
|
|
9
|
+
/** Leading glyph (e.g. ℹ ✓ ⚠ ✕ ⬡). */
|
|
10
|
+
icon?: ReactNode;
|
|
11
|
+
/** Bold uppercase heading line. */
|
|
12
|
+
title?: ReactNode;
|
|
13
|
+
/** When provided, renders a dismiss ✕ that calls this on click. */
|
|
14
|
+
onDismiss?: () => void;
|
|
15
|
+
/** Message body. */
|
|
16
|
+
children?: ReactNode;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Single notification toast — semantic color, icon, title/body and optional dismiss. */
|
|
20
|
+
export function Toast({
|
|
21
|
+
variant = 'info',
|
|
22
|
+
icon,
|
|
23
|
+
title,
|
|
24
|
+
onDismiss,
|
|
25
|
+
className = '',
|
|
26
|
+
children,
|
|
27
|
+
...rest
|
|
28
|
+
}: ToastProps) {
|
|
29
|
+
return (
|
|
30
|
+
<div className={cx('cd-toast', `cd-toast--${variant}`, className)} {...rest}>
|
|
31
|
+
{icon != null && <span className="cd-toast__icon">{icon}</span>}
|
|
32
|
+
<div className="cd-toast__content">
|
|
33
|
+
{title != null && <div className="cd-toast__title">{title}</div>}
|
|
34
|
+
{children != null && <div className="cd-toast__body">{children}</div>}
|
|
35
|
+
</div>
|
|
36
|
+
{onDismiss && (
|
|
37
|
+
<button className="cd-toast__dismiss" onClick={onDismiss} aria-label="Dismiss">
|
|
38
|
+
✕
|
|
39
|
+
</button>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { HTMLAttributes, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
const cx = (...parts: Array<string | false | null | undefined>) =>
|
|
4
|
+
parts.filter(Boolean).join(' ');
|
|
5
|
+
|
|
6
|
+
export interface TooltipProps extends Omit<HTMLAttributes<HTMLSpanElement>, 'content'> {
|
|
7
|
+
/** Tooltip text shown on hover (rendered via the `data-tip` attribute). */
|
|
8
|
+
tip: string;
|
|
9
|
+
/** Show the tooltip below the trigger instead of above. */
|
|
10
|
+
below?: boolean;
|
|
11
|
+
/** The trigger content the tooltip wraps. */
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** CSS-only tooltip — wraps a trigger and reveals `tip` text on hover. */
|
|
16
|
+
export function Tooltip({
|
|
17
|
+
tip,
|
|
18
|
+
below = false,
|
|
19
|
+
className = '',
|
|
20
|
+
children,
|
|
21
|
+
...rest
|
|
22
|
+
}: TooltipProps) {
|
|
23
|
+
return (
|
|
24
|
+
<span
|
|
25
|
+
className={cx('cd-tooltip', below && 'cd-tooltip--below', className)}
|
|
26
|
+
data-tip={tip}
|
|
27
|
+
{...rest}
|
|
28
|
+
>
|
|
29
|
+
{children}
|
|
30
|
+
</span>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// cyberdesign React bindings — thin wrappers over the cd-* CSS contract.
|
|
2
|
+
// The look comes from dist/cyberdesign.css; these components only apply classes.
|
|
3
|
+
|
|
4
|
+
export * from './GlowCard';
|
|
5
|
+
|
|
6
|
+
// Primitives
|
|
7
|
+
export * from './Button';
|
|
8
|
+
export * from './AugButton';
|
|
9
|
+
export * from './Badge';
|
|
10
|
+
export * from './Kbd';
|
|
11
|
+
export * from './Spinner';
|
|
12
|
+
export * from './Skeleton';
|
|
13
|
+
export * from './Avatar';
|
|
14
|
+
|
|
15
|
+
// Form controls
|
|
16
|
+
export * from './Input';
|
|
17
|
+
export * from './Textarea';
|
|
18
|
+
export * from './Select';
|
|
19
|
+
export * from './Checkbox';
|
|
20
|
+
export * from './Radio';
|
|
21
|
+
export * from './Switch';
|
|
22
|
+
export * from './Slider';
|
|
23
|
+
export * from './Label';
|
|
24
|
+
export * from './Field';
|
|
25
|
+
|
|
26
|
+
// Feedback & display
|
|
27
|
+
export * from './Alert';
|
|
28
|
+
export * from './Toast';
|
|
29
|
+
export * from './Tooltip';
|
|
30
|
+
export * from './Progress';
|
|
31
|
+
export * from './Separator';
|
|
32
|
+
export * from './PanelHeader';
|
|
33
|
+
export * from './Stat';
|
|
34
|
+
|
|
35
|
+
// Navigation & layout
|
|
36
|
+
export * from './Tabs';
|
|
37
|
+
export * from './Accordion';
|
|
38
|
+
export * from './Breadcrumb';
|
|
39
|
+
export * from './Pagination';
|
|
40
|
+
export * from './Dropdown';
|
|
41
|
+
export * from './Modal';
|
|
42
|
+
export * from './Table';
|
|
43
|
+
export * from './AugPanel';
|
|
44
|
+
|
|
45
|
+
// Cyber / terminal
|
|
46
|
+
export * from './Terminal';
|
|
47
|
+
export * from './HudFrame';
|
|
48
|
+
export * from './HudBar';
|
|
49
|
+
export * from './Gauge';
|
|
50
|
+
export * from './Ticker';
|
|
51
|
+
export * from './Scanner';
|
|
52
|
+
export * from './Interference';
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const palette = {
|
|
2
|
+
// Backgrounds — unified range from both source apps
|
|
3
|
+
bgBase: '#05060a', // AI-shell app's darkest
|
|
4
|
+
bgPanel: '#0b0f18', // AI-shell app bg-light / dashboard app panel midpoint
|
|
5
|
+
bgPanelHover: '#111827', // AI-shell app bg-lighter / dashboard app hover midpoint
|
|
6
|
+
bgDark: '#0a0a0c', // dashboard app's darkest (kept for compat)
|
|
7
|
+
|
|
8
|
+
// Borders
|
|
9
|
+
borderDim: '#1e1e24', // dashboard app --color-border-dim
|
|
10
|
+
borderMid: '#26324c', // AI-shell app --border
|
|
11
|
+
|
|
12
|
+
// Text — warm (dashboard app) and cool (AI-shell app) variants both exposed
|
|
13
|
+
textPrimary: '#e8e4e0', // dashboard app warm off-white
|
|
14
|
+
textPrimaryAlt: '#f2f7ff', // AI-shell app cool white
|
|
15
|
+
textSecondary: '#a7b7d6', // AI-shell app --text-secondary
|
|
16
|
+
textMuted: '#8a8078', // bumped for WCAG AA 4.5:1+ (was #706860)
|
|
17
|
+
textMutedAlt: '#7a8599', // bumped for WCAG AA (was #687797, cooler variant)
|
|
18
|
+
textDim: '#6a625c', // bumped for ~3.3:1 large/decorative text (was #4a4440)
|
|
19
|
+
|
|
20
|
+
// Orange family (dashboard app primary accent)
|
|
21
|
+
orange: '#ff8800', // normalized from #f80
|
|
22
|
+
amber: '#ffaa00', // normalized from #fa0
|
|
23
|
+
gold: '#ffcc00', // normalized from #fc0
|
|
24
|
+
|
|
25
|
+
// Cyan family (AI-shell app primary accent)
|
|
26
|
+
cyan: '#47f6ff',
|
|
27
|
+
cyanBright: '#bdffff',
|
|
28
|
+
|
|
29
|
+
// Shared secondary accents
|
|
30
|
+
magenta: '#ff2bd6', // AI-shell app; close to dashboard app's #f0f
|
|
31
|
+
magentaAlt: '#ff00ff', // dashboard app pure magenta (kept as alias)
|
|
32
|
+
purple: '#9d7cff', // AI-shell app --purple / --violet
|
|
33
|
+
green: '#00ff00', // dashboard app pure green
|
|
34
|
+
greenBright: '#52ff9f', // AI-shell app --green-bright
|
|
35
|
+
red: '#ff4444', // dashboard app #f44
|
|
36
|
+
redAlt: '#ff456c', // AI-shell app --red
|
|
37
|
+
redBright: '#ff7777', // lighter red for accent-bright
|
|
38
|
+
blue: '#4488ff', // dashboard app #48f
|
|
39
|
+
blueAlt: '#6aa8ff', // AI-shell app --ai-blue
|
|
40
|
+
yellow: '#f7ff5a', // AI-shell app --viz-active
|
|
41
|
+
|
|
42
|
+
// Steel / metallic family
|
|
43
|
+
steel: '#9aafc4', // blue-grey steel
|
|
44
|
+
steelBright: '#c8d8e8', // chrome highlight
|
|
45
|
+
|
|
46
|
+
// Surface
|
|
47
|
+
surfaceGlass: 'rgba(11, 15, 24, 0.94)',
|
|
48
|
+
} as const;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { palette } from './base.js';
|
|
2
|
+
export { semanticTokens } from './semantic.js';
|
|
3
|
+
export { orangeTheme } from './themes/orange.js';
|
|
4
|
+
export { cyanTheme } from './themes/cyan.js';
|
|
5
|
+
export { greenTheme } from './themes/green.js';
|
|
6
|
+
export { redTheme } from './themes/red.js';
|
|
7
|
+
export { steelTheme } from './themes/steel.js';
|