@easyops-cn/a2ui-react 0.0.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/.claude/commands/speckit.analyze.md +184 -0
- package/.claude/commands/speckit.checklist.md +294 -0
- package/.claude/commands/speckit.clarify.md +181 -0
- package/.claude/commands/speckit.constitution.md +82 -0
- package/.claude/commands/speckit.implement.md +135 -0
- package/.claude/commands/speckit.plan.md +89 -0
- package/.claude/commands/speckit.specify.md +256 -0
- package/.claude/commands/speckit.tasks.md +137 -0
- package/.claude/commands/speckit.taskstoissues.md +30 -0
- package/.github/workflows/deploy.yml +69 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +7 -0
- package/.specify/memory/constitution.md +73 -0
- package/.specify/scripts/bash/check-prerequisites.sh +166 -0
- package/.specify/scripts/bash/common.sh +156 -0
- package/.specify/scripts/bash/create-new-feature.sh +297 -0
- package/.specify/scripts/bash/setup-plan.sh +61 -0
- package/.specify/scripts/bash/update-agent-context.sh +799 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/plan-template.md +105 -0
- package/.specify/templates/spec-template.md +115 -0
- package/.specify/templates/tasks-template.md +250 -0
- package/CLAUDE.md +105 -0
- package/CONTRIBUTING.md +97 -0
- package/README.md +126 -0
- package/components.json +21 -0
- package/eslint.config.js +25 -0
- package/netlify.toml +50 -0
- package/package.json +94 -0
- package/playground/README.md +75 -0
- package/playground/index.html +22 -0
- package/playground/package.json +32 -0
- package/playground/public/favicon.svg +8 -0
- package/playground/src/App.css +256 -0
- package/playground/src/App.tsx +115 -0
- package/playground/src/assets/react.svg +1 -0
- package/playground/src/components/ErrorDisplay.tsx +13 -0
- package/playground/src/components/ExampleSelector.tsx +64 -0
- package/playground/src/components/Header.tsx +47 -0
- package/playground/src/components/JsonEditor.tsx +32 -0
- package/playground/src/components/Preview.tsx +78 -0
- package/playground/src/components/ThemeToggle.tsx +19 -0
- package/playground/src/data/examples.ts +1571 -0
- package/playground/src/hooks/useTheme.ts +55 -0
- package/playground/src/index.css +220 -0
- package/playground/src/main.tsx +10 -0
- package/playground/tsconfig.app.json +34 -0
- package/playground/tsconfig.json +13 -0
- package/playground/tsconfig.node.json +26 -0
- package/playground/vite.config.ts +31 -0
- package/specs/001-a2ui-renderer/checklists/requirements.md +41 -0
- package/specs/001-a2ui-renderer/data-model.md +140 -0
- package/specs/001-a2ui-renderer/plan.md +123 -0
- package/specs/001-a2ui-renderer/quickstart.md +141 -0
- package/specs/001-a2ui-renderer/research.md +140 -0
- package/specs/001-a2ui-renderer/spec.md +165 -0
- package/specs/001-a2ui-renderer/tasks.md +310 -0
- package/specs/002-playground/checklists/requirements.md +37 -0
- package/specs/002-playground/contracts/components.md +120 -0
- package/specs/002-playground/data-model.md +149 -0
- package/specs/002-playground/plan.md +73 -0
- package/specs/002-playground/quickstart.md +158 -0
- package/specs/002-playground/research.md +117 -0
- package/specs/002-playground/spec.md +109 -0
- package/specs/002-playground/tasks.md +224 -0
- package/src/0.8/A2UIRender.test.tsx +793 -0
- package/src/0.8/A2UIRender.tsx +142 -0
- package/src/0.8/components/ComponentRenderer.test.tsx +373 -0
- package/src/0.8/components/ComponentRenderer.tsx +163 -0
- package/src/0.8/components/UnknownComponent.tsx +49 -0
- package/src/0.8/components/display/AudioPlayerComponent.tsx +37 -0
- package/src/0.8/components/display/DividerComponent.tsx +23 -0
- package/src/0.8/components/display/IconComponent.tsx +137 -0
- package/src/0.8/components/display/ImageComponent.tsx +57 -0
- package/src/0.8/components/display/TextComponent.tsx +56 -0
- package/src/0.8/components/display/VideoComponent.tsx +31 -0
- package/src/0.8/components/display/display.test.tsx +660 -0
- package/src/0.8/components/display/index.ts +10 -0
- package/src/0.8/components/index.ts +14 -0
- package/src/0.8/components/interactive/ButtonComponent.tsx +44 -0
- package/src/0.8/components/interactive/CheckBoxComponent.tsx +45 -0
- package/src/0.8/components/interactive/DateTimeInputComponent.tsx +176 -0
- package/src/0.8/components/interactive/MultipleChoiceComponent.tsx +157 -0
- package/src/0.8/components/interactive/SliderComponent.tsx +53 -0
- package/src/0.8/components/interactive/TextFieldComponent.tsx +65 -0
- package/src/0.8/components/interactive/index.ts +10 -0
- package/src/0.8/components/interactive/interactive.test.tsx +618 -0
- package/src/0.8/components/layout/CardComponent.tsx +30 -0
- package/src/0.8/components/layout/ColumnComponent.tsx +93 -0
- package/src/0.8/components/layout/ListComponent.tsx +81 -0
- package/src/0.8/components/layout/ModalComponent.tsx +41 -0
- package/src/0.8/components/layout/RowComponent.tsx +94 -0
- package/src/0.8/components/layout/TabsComponent.tsx +59 -0
- package/src/0.8/components/layout/index.ts +10 -0
- package/src/0.8/components/layout/layout.test.tsx +558 -0
- package/src/0.8/contexts/A2UIProvider.test.tsx +226 -0
- package/src/0.8/contexts/A2UIProvider.tsx +54 -0
- package/src/0.8/contexts/ActionContext.test.tsx +242 -0
- package/src/0.8/contexts/ActionContext.tsx +105 -0
- package/src/0.8/contexts/ComponentsMapContext.tsx +125 -0
- package/src/0.8/contexts/DataModelContext.test.tsx +335 -0
- package/src/0.8/contexts/DataModelContext.tsx +184 -0
- package/src/0.8/contexts/SurfaceContext.test.tsx +339 -0
- package/src/0.8/contexts/SurfaceContext.tsx +197 -0
- package/src/0.8/hooks/useA2UIMessageHandler.test.tsx +399 -0
- package/src/0.8/hooks/useA2UIMessageHandler.ts +123 -0
- package/src/0.8/hooks/useComponent.test.tsx +148 -0
- package/src/0.8/hooks/useComponent.ts +39 -0
- package/src/0.8/hooks/useDataBinding.test.tsx +334 -0
- package/src/0.8/hooks/useDataBinding.ts +99 -0
- package/src/0.8/hooks/useDispatchAction.test.tsx +83 -0
- package/src/0.8/hooks/useDispatchAction.ts +35 -0
- package/src/0.8/hooks/useSurface.test.tsx +114 -0
- package/src/0.8/hooks/useSurface.ts +34 -0
- package/src/0.8/index.ts +38 -0
- package/src/0.8/schemas/client_to_server.json +50 -0
- package/src/0.8/schemas/server_to_client.json +148 -0
- package/src/0.8/schemas/standard_catalog_definition.json +661 -0
- package/src/0.8/types/index.ts +448 -0
- package/src/0.8/utils/dataBinding.test.ts +443 -0
- package/src/0.8/utils/dataBinding.ts +212 -0
- package/src/0.8/utils/pathUtils.test.ts +353 -0
- package/src/0.8/utils/pathUtils.ts +200 -0
- package/src/components/ui/button.tsx +62 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/dialog.tsx +141 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/native-select.tsx +53 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/select.tsx +188 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/slider.tsx +61 -0
- package/src/components/ui/tabs.tsx +64 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/index.ts +1 -0
- package/src/lib/utils.ts +6 -0
- package/tsconfig.json +28 -0
- package/vite.config.ts +29 -0
- package/vitest.config.ts +22 -0
- package/vitest.setup.ts +8 -0
- package/website/README.md +4 -0
- package/website/assets/favicon.svg +8 -0
- package/website/content/.gitkeep +0 -0
- package/website/content/index.md +122 -0
- package/website/global.d.ts +9 -0
- package/website/package.json +17 -0
- package/website/plain.config.js +28 -0
- package/website/serve.json +6 -0
- package/website/src/client/color-mode-switch.css +47 -0
- package/website/src/client/index.js +61 -0
- package/website/src/client/moon.svg +1 -0
- package/website/src/client/sun.svg +1 -0
- package/website/src/components/Footer.jsx +9 -0
- package/website/src/components/Header.jsx +44 -0
- package/website/src/components/Page.jsx +28 -0
- package/website/src/global.css +423 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ColumnComponent - Vertical flex container.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { memo } from 'react'
|
|
6
|
+
import type { ColumnComponentProps, Distribution, Alignment } from '@/0.8/types'
|
|
7
|
+
import { useDataModel } from '@/0.8/hooks/useDataBinding'
|
|
8
|
+
import { cn } from '@/lib/utils'
|
|
9
|
+
import { getValueByPath } from '@/0.8/utils/pathUtils'
|
|
10
|
+
import { ComponentRenderer } from '../ComponentRenderer'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maps distribution values to Tailwind justify-content classes.
|
|
14
|
+
*/
|
|
15
|
+
const distributionStyles: Record<Distribution, string> = {
|
|
16
|
+
start: 'justify-start',
|
|
17
|
+
center: 'justify-center',
|
|
18
|
+
end: 'justify-end',
|
|
19
|
+
spaceBetween: 'justify-between',
|
|
20
|
+
spaceAround: 'justify-around',
|
|
21
|
+
spaceEvenly: 'justify-evenly',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Maps alignment values to Tailwind align-items classes.
|
|
26
|
+
*/
|
|
27
|
+
const alignmentStyles: Record<Alignment, string> = {
|
|
28
|
+
start: 'items-start',
|
|
29
|
+
center: 'items-center',
|
|
30
|
+
end: 'items-end',
|
|
31
|
+
stretch: 'items-stretch',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Column component - vertical flex container.
|
|
36
|
+
*/
|
|
37
|
+
export const ColumnComponent = memo(function ColumnComponent({
|
|
38
|
+
surfaceId,
|
|
39
|
+
children,
|
|
40
|
+
distribution = 'start',
|
|
41
|
+
alignment = 'stretch',
|
|
42
|
+
}: ColumnComponentProps) {
|
|
43
|
+
const dataModel = useDataModel(surfaceId)
|
|
44
|
+
|
|
45
|
+
const className = cn(
|
|
46
|
+
'flex flex-col gap-4',
|
|
47
|
+
distributionStyles[distribution],
|
|
48
|
+
alignmentStyles[alignment]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Render explicit list of children
|
|
52
|
+
if (children?.explicitList) {
|
|
53
|
+
return (
|
|
54
|
+
<div className={className}>
|
|
55
|
+
{children.explicitList.map((childId) => (
|
|
56
|
+
<ComponentRenderer
|
|
57
|
+
key={childId}
|
|
58
|
+
surfaceId={surfaceId}
|
|
59
|
+
componentId={childId}
|
|
60
|
+
/>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Render template-based children
|
|
67
|
+
if (children?.template) {
|
|
68
|
+
const { componentId, dataBinding } = children.template
|
|
69
|
+
const listData = getValueByPath(dataModel, dataBinding)
|
|
70
|
+
|
|
71
|
+
if (!listData || typeof listData !== 'object') {
|
|
72
|
+
return <div className={className} />
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const items = Object.entries(listData as Record<string, unknown>)
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className={className}>
|
|
79
|
+
{items.map(([key]) => (
|
|
80
|
+
<ComponentRenderer
|
|
81
|
+
key={key}
|
|
82
|
+
surfaceId={surfaceId}
|
|
83
|
+
componentId={componentId}
|
|
84
|
+
/>
|
|
85
|
+
))}
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return <div className={className} />
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
ColumnComponent.displayName = 'A2UI.Column'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ListComponent - List container with vertical or horizontal direction.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { memo } from 'react'
|
|
6
|
+
import type { ListComponentProps, Alignment } from '@/0.8/types'
|
|
7
|
+
import { useDataModel } from '@/0.8/hooks/useDataBinding'
|
|
8
|
+
import { cn } from '@/lib/utils'
|
|
9
|
+
import { getValueByPath } from '@/0.8/utils/pathUtils'
|
|
10
|
+
import { ComponentRenderer } from '../ComponentRenderer'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maps alignment values to Tailwind align-items classes.
|
|
14
|
+
*/
|
|
15
|
+
const alignmentStyles: Record<Alignment, string> = {
|
|
16
|
+
start: 'items-start',
|
|
17
|
+
center: 'items-center',
|
|
18
|
+
end: 'items-end',
|
|
19
|
+
stretch: 'items-stretch',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* List component - container for list items.
|
|
24
|
+
*/
|
|
25
|
+
export const ListComponent = memo(function ListComponent({
|
|
26
|
+
surfaceId,
|
|
27
|
+
children,
|
|
28
|
+
direction = 'vertical',
|
|
29
|
+
alignment = 'stretch',
|
|
30
|
+
}: ListComponentProps) {
|
|
31
|
+
const dataModel = useDataModel(surfaceId)
|
|
32
|
+
|
|
33
|
+
const className = cn(
|
|
34
|
+
'flex gap-3',
|
|
35
|
+
direction === 'horizontal' ? 'flex-row' : 'flex-col',
|
|
36
|
+
alignmentStyles[alignment]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// Render explicit list of children
|
|
40
|
+
if (children?.explicitList) {
|
|
41
|
+
return (
|
|
42
|
+
<div className={className}>
|
|
43
|
+
{children.explicitList.map((childId) => (
|
|
44
|
+
<ComponentRenderer
|
|
45
|
+
key={childId}
|
|
46
|
+
surfaceId={surfaceId}
|
|
47
|
+
componentId={childId}
|
|
48
|
+
/>
|
|
49
|
+
))}
|
|
50
|
+
</div>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Render template-based children
|
|
55
|
+
if (children?.template) {
|
|
56
|
+
const { componentId, dataBinding } = children.template
|
|
57
|
+
const listData = getValueByPath(dataModel, dataBinding)
|
|
58
|
+
|
|
59
|
+
if (!listData || typeof listData !== 'object') {
|
|
60
|
+
return <div className={className} />
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const items = Object.entries(listData as Record<string, unknown>)
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<div className={className}>
|
|
67
|
+
{items.map(([key]) => (
|
|
68
|
+
<ComponentRenderer
|
|
69
|
+
key={key}
|
|
70
|
+
surfaceId={surfaceId}
|
|
71
|
+
componentId={componentId}
|
|
72
|
+
/>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return <div className={className} />
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
ListComponent.displayName = 'A2UI.List'
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModalComponent - Modal/Dialog container.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { memo, useState } from 'react'
|
|
6
|
+
import type { ModalComponentProps } from '@/0.8/types'
|
|
7
|
+
import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
|
|
8
|
+
import { ComponentRenderer } from '../ComponentRenderer'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Modal component - dialog container with trigger.
|
|
12
|
+
*/
|
|
13
|
+
export const ModalComponent = memo(function ModalComponent({
|
|
14
|
+
surfaceId,
|
|
15
|
+
entryPointChild,
|
|
16
|
+
contentChild,
|
|
17
|
+
}: ModalComponentProps) {
|
|
18
|
+
const [open, setOpen] = useState(false)
|
|
19
|
+
|
|
20
|
+
if (!entryPointChild || !contentChild) {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
26
|
+
<DialogTrigger asChild>
|
|
27
|
+
<div className="cursor-pointer">
|
|
28
|
+
<ComponentRenderer
|
|
29
|
+
surfaceId={surfaceId}
|
|
30
|
+
componentId={entryPointChild}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
</DialogTrigger>
|
|
34
|
+
<DialogContent>
|
|
35
|
+
<ComponentRenderer surfaceId={surfaceId} componentId={contentChild} />
|
|
36
|
+
</DialogContent>
|
|
37
|
+
</Dialog>
|
|
38
|
+
)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
ModalComponent.displayName = 'A2UI.Modal'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RowComponent - Horizontal flex container.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { memo } from 'react'
|
|
6
|
+
import type { RowComponentProps, Distribution, Alignment } from '@/0.8/types'
|
|
7
|
+
import { useDataModel } from '@/0.8/hooks/useDataBinding'
|
|
8
|
+
import { cn } from '@/lib/utils'
|
|
9
|
+
import { getValueByPath } from '@/0.8/utils/pathUtils'
|
|
10
|
+
import { ComponentRenderer } from '../ComponentRenderer'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Maps distribution values to Tailwind justify-content classes.
|
|
14
|
+
*/
|
|
15
|
+
const distributionStyles: Record<Distribution, string> = {
|
|
16
|
+
start: 'justify-start',
|
|
17
|
+
center: 'justify-center',
|
|
18
|
+
end: 'justify-end',
|
|
19
|
+
spaceBetween: 'justify-between',
|
|
20
|
+
spaceAround: 'justify-around',
|
|
21
|
+
spaceEvenly: 'justify-evenly',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Maps alignment values to Tailwind align-items classes.
|
|
26
|
+
*/
|
|
27
|
+
const alignmentStyles: Record<Alignment, string> = {
|
|
28
|
+
start: 'items-start',
|
|
29
|
+
center: 'items-center',
|
|
30
|
+
end: 'items-end',
|
|
31
|
+
stretch: 'items-stretch',
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Row component - horizontal flex container.
|
|
36
|
+
*/
|
|
37
|
+
export const RowComponent = memo(function RowComponent({
|
|
38
|
+
surfaceId,
|
|
39
|
+
children,
|
|
40
|
+
distribution = 'start',
|
|
41
|
+
alignment = 'stretch',
|
|
42
|
+
}: RowComponentProps) {
|
|
43
|
+
const dataModel = useDataModel(surfaceId)
|
|
44
|
+
|
|
45
|
+
const className = cn(
|
|
46
|
+
'flex flex-row gap-3',
|
|
47
|
+
distributionStyles[distribution],
|
|
48
|
+
alignmentStyles[alignment]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
// Render explicit list of children
|
|
52
|
+
if (children?.explicitList) {
|
|
53
|
+
return (
|
|
54
|
+
<div className={className}>
|
|
55
|
+
{children.explicitList.map((childId) => (
|
|
56
|
+
<ComponentRenderer
|
|
57
|
+
key={childId}
|
|
58
|
+
surfaceId={surfaceId}
|
|
59
|
+
componentId={childId}
|
|
60
|
+
/>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Render template-based children
|
|
67
|
+
if (children?.template) {
|
|
68
|
+
const { componentId, dataBinding } = children.template
|
|
69
|
+
const listData = getValueByPath(dataModel, dataBinding)
|
|
70
|
+
|
|
71
|
+
if (!listData || typeof listData !== 'object') {
|
|
72
|
+
return <div className={className} />
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// listData is a Map-like object where each value represents an item
|
|
76
|
+
const items = Object.entries(listData as Record<string, unknown>)
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<div className={className}>
|
|
80
|
+
{items.map(([key]) => (
|
|
81
|
+
<ComponentRenderer
|
|
82
|
+
key={key}
|
|
83
|
+
surfaceId={surfaceId}
|
|
84
|
+
componentId={componentId}
|
|
85
|
+
/>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return <div className={className} />
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
RowComponent.displayName = 'A2UI.Row'
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TabsComponent - Tabbed content container.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { memo } from 'react'
|
|
6
|
+
import type { TabsComponentProps, ValueSource } from '@/0.8/types'
|
|
7
|
+
import { useDataBinding } from '@/0.8/hooks/useDataBinding'
|
|
8
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
9
|
+
import { ComponentRenderer } from '../ComponentRenderer'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Tabs component - tabbed content container.
|
|
13
|
+
*/
|
|
14
|
+
export const TabsComponent = memo(function TabsComponent({
|
|
15
|
+
surfaceId,
|
|
16
|
+
tabItems,
|
|
17
|
+
}: TabsComponentProps) {
|
|
18
|
+
if (!tabItems || tabItems.length === 0) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Get the first tab as default
|
|
23
|
+
const defaultTab = tabItems[0].child
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Tabs defaultValue={defaultTab} className="w-full">
|
|
27
|
+
<TabsList>
|
|
28
|
+
{tabItems.map((item, index) => (
|
|
29
|
+
<TabsTrigger key={item.child} value={item.child}>
|
|
30
|
+
<TabTitle surfaceId={surfaceId} title={item.title} index={index} />
|
|
31
|
+
</TabsTrigger>
|
|
32
|
+
))}
|
|
33
|
+
</TabsList>
|
|
34
|
+
{tabItems.map((item) => (
|
|
35
|
+
<TabsContent key={item.child} value={item.child}>
|
|
36
|
+
<ComponentRenderer surfaceId={surfaceId} componentId={item.child} />
|
|
37
|
+
</TabsContent>
|
|
38
|
+
))}
|
|
39
|
+
</Tabs>
|
|
40
|
+
)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper component to resolve tab titles.
|
|
45
|
+
*/
|
|
46
|
+
function TabTitle({
|
|
47
|
+
surfaceId,
|
|
48
|
+
title,
|
|
49
|
+
index,
|
|
50
|
+
}: {
|
|
51
|
+
surfaceId: string
|
|
52
|
+
title: ValueSource | undefined
|
|
53
|
+
index: number
|
|
54
|
+
}) {
|
|
55
|
+
const titleText = useDataBinding<string>(surfaceId, title, `Tab ${index + 1}`)
|
|
56
|
+
return <>{titleText}</>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
TabsComponent.displayName = 'A2UI.Tabs'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A2UI React Renderer - Layout Components
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { RowComponent } from './RowComponent'
|
|
6
|
+
export { ColumnComponent } from './ColumnComponent'
|
|
7
|
+
export { ListComponent } from './ListComponent'
|
|
8
|
+
export { CardComponent } from './CardComponent'
|
|
9
|
+
export { TabsComponent } from './TabsComponent'
|
|
10
|
+
export { ModalComponent } from './ModalComponent'
|