@brika/ui-kit 0.3.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/README.md +3 -0
- package/package.json +32 -0
- package/src/__tests__/define-brick.test.ts +125 -0
- package/src/__tests__/mutations.test.ts +211 -0
- package/src/__tests__/nodes.test.ts +1595 -0
- package/src/colors.ts +99 -0
- package/src/define-brick.ts +92 -0
- package/src/descriptors.ts +28 -0
- package/src/index.ts +154 -0
- package/src/jsx-dev-runtime.ts +3 -0
- package/src/jsx-runtime.ts +60 -0
- package/src/mutations.ts +79 -0
- package/src/nodes/_shared.ts +129 -0
- package/src/nodes/avatar.ts +36 -0
- package/src/nodes/badge.ts +29 -0
- package/src/nodes/box.ts +69 -0
- package/src/nodes/button.ts +44 -0
- package/src/nodes/callout.ts +23 -0
- package/src/nodes/chart.ts +43 -0
- package/src/nodes/checkbox.ts +27 -0
- package/src/nodes/code-block.ts +27 -0
- package/src/nodes/column.ts +37 -0
- package/src/nodes/divider.ts +25 -0
- package/src/nodes/grid.ts +44 -0
- package/src/nodes/icon.ts +28 -0
- package/src/nodes/image.ts +29 -0
- package/src/nodes/index.ts +54 -0
- package/src/nodes/key-value.ts +31 -0
- package/src/nodes/link.ts +25 -0
- package/src/nodes/markdown.ts +16 -0
- package/src/nodes/progress.ts +28 -0
- package/src/nodes/row.ts +37 -0
- package/src/nodes/section.ts +42 -0
- package/src/nodes/select.ts +44 -0
- package/src/nodes/skeleton.ts +23 -0
- package/src/nodes/slider.ts +40 -0
- package/src/nodes/spacer.ts +17 -0
- package/src/nodes/stat-value.ts +26 -0
- package/src/nodes/status.ts +20 -0
- package/src/nodes/table.ts +35 -0
- package/src/nodes/tabs.ts +52 -0
- package/src/nodes/text-input.ts +53 -0
- package/src/nodes/text.ts +66 -0
- package/src/nodes/toggle.ts +32 -0
- package/src/nodes/video.ts +24 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ActionHandler, BaseNode } from './_shared';
|
|
2
|
+
import { resolveAction } from './_shared';
|
|
3
|
+
|
|
4
|
+
export interface SelectOption {
|
|
5
|
+
value: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SelectNode extends BaseNode {
|
|
10
|
+
type: 'select';
|
|
11
|
+
/** Currently selected value */
|
|
12
|
+
value: string;
|
|
13
|
+
/** Available options */
|
|
14
|
+
options: SelectOption[];
|
|
15
|
+
/** Optional label above the select */
|
|
16
|
+
label?: string;
|
|
17
|
+
/** Placeholder when no value selected */
|
|
18
|
+
placeholder?: string;
|
|
19
|
+
/** Action dispatched when selection changes */
|
|
20
|
+
onChange: string;
|
|
21
|
+
/** Disable the select */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** Lucide icon name */
|
|
24
|
+
icon?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function Select(props: {
|
|
28
|
+
value: string;
|
|
29
|
+
options: SelectOption[];
|
|
30
|
+
label?: string;
|
|
31
|
+
placeholder?: string;
|
|
32
|
+
onChange: ActionHandler;
|
|
33
|
+
disabled?: boolean;
|
|
34
|
+
icon?: string;
|
|
35
|
+
}): SelectNode {
|
|
36
|
+
const { onChange, ...rest } = props;
|
|
37
|
+
return { type: 'select', ...rest, onChange: resolveAction(onChange) };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
declare module './_shared' {
|
|
41
|
+
interface NodeTypeMap {
|
|
42
|
+
select: SelectNode;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { BaseNode } from './_shared';
|
|
2
|
+
|
|
3
|
+
export interface SkeletonNode extends BaseNode {
|
|
4
|
+
type: 'skeleton';
|
|
5
|
+
/** Shape of the skeleton placeholder */
|
|
6
|
+
variant: 'text' | 'circle' | 'rect';
|
|
7
|
+
/** Width (CSS value, e.g. "100%", "80px") */
|
|
8
|
+
width?: string;
|
|
9
|
+
/** Height (CSS value) */
|
|
10
|
+
height?: string;
|
|
11
|
+
/** Number of lines (for 'text' variant) */
|
|
12
|
+
lines?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function Skeleton(props: Omit<SkeletonNode, 'type'>): SkeletonNode {
|
|
16
|
+
return { type: 'skeleton', ...props };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
declare module './_shared' {
|
|
20
|
+
interface NodeTypeMap {
|
|
21
|
+
skeleton: SkeletonNode;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ColorValue } from '../colors';
|
|
2
|
+
import type { ActionHandler, BaseNode } from './_shared';
|
|
3
|
+
import { resolveAction } from './_shared';
|
|
4
|
+
|
|
5
|
+
export interface SliderNode extends BaseNode {
|
|
6
|
+
type: 'slider';
|
|
7
|
+
label?: string;
|
|
8
|
+
value: number;
|
|
9
|
+
min: number;
|
|
10
|
+
max: number;
|
|
11
|
+
step?: number;
|
|
12
|
+
unit?: string;
|
|
13
|
+
onChange: string;
|
|
14
|
+
icon?: string;
|
|
15
|
+
color?: ColorValue;
|
|
16
|
+
/** Disable the slider */
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function Slider(props: {
|
|
21
|
+
label?: string;
|
|
22
|
+
value: number;
|
|
23
|
+
min: number;
|
|
24
|
+
max: number;
|
|
25
|
+
step?: number;
|
|
26
|
+
unit?: string;
|
|
27
|
+
onChange: ActionHandler;
|
|
28
|
+
icon?: string;
|
|
29
|
+
color?: ColorValue;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
}): SliderNode {
|
|
32
|
+
const { onChange, ...rest } = props;
|
|
33
|
+
return { type: 'slider', ...rest, onChange: resolveAction(onChange) };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare module './_shared' {
|
|
37
|
+
interface NodeTypeMap {
|
|
38
|
+
slider: SliderNode;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { BaseNode } from './_shared';
|
|
2
|
+
|
|
3
|
+
export interface SpacerNode extends BaseNode {
|
|
4
|
+
type: 'spacer';
|
|
5
|
+
/** Fixed size. When omitted, spacer flexes to fill available space. */
|
|
6
|
+
size?: 'sm' | 'md' | 'lg';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Spacer(props?: { size?: 'sm' | 'md' | 'lg' }): SpacerNode {
|
|
10
|
+
return { type: 'spacer', ...props };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
declare module './_shared' {
|
|
14
|
+
interface NodeTypeMap {
|
|
15
|
+
spacer: SpacerNode;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ColorValue } from '../colors';
|
|
2
|
+
import type { BaseNode } from './_shared';
|
|
3
|
+
|
|
4
|
+
export interface StatValueNode extends BaseNode {
|
|
5
|
+
type: 'stat-value';
|
|
6
|
+
label: string;
|
|
7
|
+
value: number | string;
|
|
8
|
+
unit?: string;
|
|
9
|
+
icon?: string;
|
|
10
|
+
trend?: 'up' | 'down' | 'flat';
|
|
11
|
+
color?: ColorValue;
|
|
12
|
+
/** Display value of the trend change (e.g. "+5.2%") */
|
|
13
|
+
trendValue?: string;
|
|
14
|
+
/** Sub-label / description below the value */
|
|
15
|
+
description?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function Stat(props: Omit<StatValueNode, 'type'>): StatValueNode {
|
|
19
|
+
return { type: 'stat-value', ...props };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare module './_shared' {
|
|
23
|
+
interface NodeTypeMap {
|
|
24
|
+
'stat-value': StatValueNode;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ColorValue } from '../colors';
|
|
2
|
+
import type { BaseNode } from './_shared';
|
|
3
|
+
|
|
4
|
+
export interface StatusNode extends BaseNode {
|
|
5
|
+
type: 'status';
|
|
6
|
+
label: string;
|
|
7
|
+
status: 'online' | 'offline' | 'warning' | 'error' | 'idle';
|
|
8
|
+
icon?: string;
|
|
9
|
+
color?: ColorValue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Status(props: Omit<StatusNode, 'type'>): StatusNode {
|
|
13
|
+
return { type: 'status', ...props };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
declare module './_shared' {
|
|
17
|
+
interface NodeTypeMap {
|
|
18
|
+
status: StatusNode;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ActionHandler, type BaseNode, resolveAction } from './_shared';
|
|
2
|
+
|
|
3
|
+
export interface TableColumn {
|
|
4
|
+
key: string;
|
|
5
|
+
label: string;
|
|
6
|
+
align?: 'left' | 'center' | 'right';
|
|
7
|
+
width?: number | string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TableNode extends BaseNode {
|
|
11
|
+
type: 'table';
|
|
12
|
+
columns: TableColumn[];
|
|
13
|
+
rows: Record<string, string | number>[];
|
|
14
|
+
/** Show zebra-striped rows */
|
|
15
|
+
striped?: boolean;
|
|
16
|
+
/** Compact row height */
|
|
17
|
+
compact?: boolean;
|
|
18
|
+
/** Max visible rows before scrolling */
|
|
19
|
+
maxRows?: number;
|
|
20
|
+
/** Action dispatched when a row is clicked */
|
|
21
|
+
onRowPress?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function Table(
|
|
25
|
+
props: Omit<TableNode, 'type' | 'onRowPress'> & { onRowPress?: ActionHandler }
|
|
26
|
+
): TableNode {
|
|
27
|
+
const { onRowPress, ...rest } = props;
|
|
28
|
+
return { type: 'table', ...rest, onRowPress: onRowPress ? resolveAction(onRowPress) : undefined };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
declare module './_shared' {
|
|
32
|
+
interface NodeTypeMap {
|
|
33
|
+
table: TableNode;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ActionHandler,
|
|
3
|
+
type BaseNode,
|
|
4
|
+
type Child,
|
|
5
|
+
type ComponentNode,
|
|
6
|
+
normalizeChildren,
|
|
7
|
+
resolveAction,
|
|
8
|
+
} from './_shared';
|
|
9
|
+
|
|
10
|
+
export interface TabItem {
|
|
11
|
+
key: string;
|
|
12
|
+
label: string;
|
|
13
|
+
icon?: string;
|
|
14
|
+
children: ComponentNode[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TabsNode extends BaseNode {
|
|
18
|
+
type: 'tabs';
|
|
19
|
+
/** Currently active tab key */
|
|
20
|
+
value: string;
|
|
21
|
+
tabs: TabItem[];
|
|
22
|
+
/** Action dispatched when tab changes */
|
|
23
|
+
onChange: string;
|
|
24
|
+
/** Visual style */
|
|
25
|
+
variant?: 'default' | 'pills';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function Tabs(props: {
|
|
29
|
+
value: string;
|
|
30
|
+
tabs: { key: string; label: string; icon?: string; children?: Child | Child[] }[];
|
|
31
|
+
onChange: ActionHandler;
|
|
32
|
+
variant?: 'default' | 'pills';
|
|
33
|
+
}): TabsNode {
|
|
34
|
+
const { tabs, onChange, ...rest } = props;
|
|
35
|
+
return {
|
|
36
|
+
type: 'tabs',
|
|
37
|
+
...rest,
|
|
38
|
+
tabs: tabs.map((tab) => ({
|
|
39
|
+
key: tab.key,
|
|
40
|
+
label: tab.label,
|
|
41
|
+
icon: tab.icon,
|
|
42
|
+
children: normalizeChildren(tab.children),
|
|
43
|
+
})),
|
|
44
|
+
onChange: resolveAction(onChange),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
declare module './_shared' {
|
|
49
|
+
interface NodeTypeMap {
|
|
50
|
+
tabs: TabsNode;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ActionHandler, BaseNode } from './_shared';
|
|
2
|
+
import { resolveAction } from './_shared';
|
|
3
|
+
|
|
4
|
+
export interface TextInputNode extends BaseNode {
|
|
5
|
+
type: 'text-input';
|
|
6
|
+
/** Current value */
|
|
7
|
+
value: string;
|
|
8
|
+
/** Placeholder text */
|
|
9
|
+
placeholder?: string;
|
|
10
|
+
/** Label above the input */
|
|
11
|
+
label?: string;
|
|
12
|
+
/** Lucide icon name (left side) */
|
|
13
|
+
icon?: string;
|
|
14
|
+
/** Action dispatched when value changes (debounced) */
|
|
15
|
+
onChange: string;
|
|
16
|
+
/** Action dispatched on Enter key */
|
|
17
|
+
onSubmit?: string;
|
|
18
|
+
/** Disable the input */
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
/** Input type */
|
|
21
|
+
inputType?: 'text' | 'password' | 'email' | 'number';
|
|
22
|
+
/** Render as textarea instead of input */
|
|
23
|
+
multiline?: boolean;
|
|
24
|
+
/** Number of visible rows when multiline (default 3) */
|
|
25
|
+
rows?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function TextInput(props: {
|
|
29
|
+
value: string;
|
|
30
|
+
placeholder?: string;
|
|
31
|
+
label?: string;
|
|
32
|
+
icon?: string;
|
|
33
|
+
onChange: ActionHandler;
|
|
34
|
+
onSubmit?: ActionHandler;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
inputType?: 'text' | 'password' | 'email' | 'number';
|
|
37
|
+
multiline?: boolean;
|
|
38
|
+
rows?: number;
|
|
39
|
+
}): TextInputNode {
|
|
40
|
+
const { onChange, onSubmit, ...rest } = props;
|
|
41
|
+
return {
|
|
42
|
+
type: 'text-input',
|
|
43
|
+
...rest,
|
|
44
|
+
onChange: resolveAction(onChange),
|
|
45
|
+
onSubmit: onSubmit ? resolveAction(onSubmit) : undefined,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
declare module './_shared' {
|
|
50
|
+
interface NodeTypeMap {
|
|
51
|
+
'text-input': TextInputNode;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ColorValue } from '../colors';
|
|
2
|
+
import type { ActionHandler, BaseNode, IntlRef, TextContent } from './_shared';
|
|
3
|
+
import { isI18nRef, isIntlRef, resolveAction, resolveIntlRef } from './_shared';
|
|
4
|
+
|
|
5
|
+
/** Wire-format node — sent over IPC to the UI renderer. */
|
|
6
|
+
export interface TextNode extends BaseNode {
|
|
7
|
+
type: 'text';
|
|
8
|
+
content: string;
|
|
9
|
+
/** When set, the UI renderer resolves this via i18next instead of using content. */
|
|
10
|
+
i18n?: { ns: string; key: string; params?: Record<string, string | number> };
|
|
11
|
+
/** When set, the UI renderer formats this value via Intl APIs with the user's locale. */
|
|
12
|
+
intl?: IntlRef;
|
|
13
|
+
variant?: 'body' | 'caption' | 'heading';
|
|
14
|
+
color?: ColorValue;
|
|
15
|
+
align?: 'left' | 'center' | 'right';
|
|
16
|
+
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
17
|
+
maxLines?: number;
|
|
18
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
19
|
+
onPress?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Props accepted by the Text() builder. */
|
|
23
|
+
export interface TextProps {
|
|
24
|
+
/** Text content — string, I18nRef from t(), or IntlRef from formatters. */
|
|
25
|
+
content?: TextContent;
|
|
26
|
+
/** Alias for content — allows `<Text>hello</Text>` JSX syntax. */
|
|
27
|
+
children?: TextContent;
|
|
28
|
+
variant?: 'body' | 'caption' | 'heading';
|
|
29
|
+
color?: ColorValue;
|
|
30
|
+
align?: 'left' | 'center' | 'right';
|
|
31
|
+
weight?: 'normal' | 'medium' | 'semibold' | 'bold';
|
|
32
|
+
maxLines?: number;
|
|
33
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
34
|
+
onPress?: ActionHandler;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function Text(props: TextProps): TextNode {
|
|
38
|
+
const { onPress, children, content: contentProp, ...rest } = props;
|
|
39
|
+
const content = contentProp ?? children ?? '';
|
|
40
|
+
const press = onPress ? resolveAction(onPress) : undefined;
|
|
41
|
+
if (isI18nRef(content)) {
|
|
42
|
+
return {
|
|
43
|
+
type: 'text',
|
|
44
|
+
...rest,
|
|
45
|
+
content: content.key,
|
|
46
|
+
i18n: { ns: content.ns, key: content.key, params: content.params },
|
|
47
|
+
onPress: press,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (isIntlRef(content)) {
|
|
51
|
+
return {
|
|
52
|
+
type: 'text',
|
|
53
|
+
...rest,
|
|
54
|
+
content: resolveIntlRef(content),
|
|
55
|
+
intl: content,
|
|
56
|
+
onPress: press,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return { type: 'text', ...rest, content, onPress: press };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
declare module './_shared' {
|
|
63
|
+
interface NodeTypeMap {
|
|
64
|
+
text: TextNode;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ColorValue } from '../colors';
|
|
2
|
+
import type { ActionHandler, BaseNode } from './_shared';
|
|
3
|
+
import { resolveAction } from './_shared';
|
|
4
|
+
|
|
5
|
+
export interface ToggleNode extends BaseNode {
|
|
6
|
+
type: 'toggle';
|
|
7
|
+
label: string;
|
|
8
|
+
checked: boolean;
|
|
9
|
+
onToggle: string;
|
|
10
|
+
icon?: string;
|
|
11
|
+
color?: ColorValue;
|
|
12
|
+
/** Disable the toggle */
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Toggle(props: {
|
|
17
|
+
label: string;
|
|
18
|
+
checked: boolean;
|
|
19
|
+
onToggle: ActionHandler;
|
|
20
|
+
icon?: string;
|
|
21
|
+
color?: ColorValue;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
}): ToggleNode {
|
|
24
|
+
const { onToggle, ...rest } = props;
|
|
25
|
+
return { type: 'toggle', ...rest, onToggle: resolveAction(onToggle) };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module './_shared' {
|
|
29
|
+
interface NodeTypeMap {
|
|
30
|
+
toggle: ToggleNode;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BaseNode } from './_shared';
|
|
2
|
+
|
|
3
|
+
export interface VideoNode extends BaseNode {
|
|
4
|
+
type: 'video';
|
|
5
|
+
src: string;
|
|
6
|
+
format: 'hls' | 'mjpeg';
|
|
7
|
+
poster?: string;
|
|
8
|
+
aspectRatio?: string;
|
|
9
|
+
muted?: boolean;
|
|
10
|
+
/** Show native video controls */
|
|
11
|
+
controls?: boolean;
|
|
12
|
+
/** Loop playback */
|
|
13
|
+
loop?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function Video(props: Omit<VideoNode, 'type'>): VideoNode {
|
|
17
|
+
return { type: 'video', ...props };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
declare module './_shared' {
|
|
21
|
+
interface NodeTypeMap {
|
|
22
|
+
video: VideoNode;
|
|
23
|
+
}
|
|
24
|
+
}
|