mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Implement gui for most of the details view
This commit is contained in:
parent
127d4b5c00
commit
5f3995b983
9 changed files with 548 additions and 182 deletions
|
|
@ -3,25 +3,35 @@ import { FC, JSXElementConstructor, ReactElement, ReactNode } from "react";
|
|||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
["gauntlet:text"]: {
|
||||
["gauntlet:metadata_link"]: {
|
||||
children?: StringComponent;
|
||||
label: string;
|
||||
href: string;
|
||||
};
|
||||
["gauntlet:metadata_tag"]: {
|
||||
children?: StringComponent;
|
||||
onClick?: () => void;
|
||||
};
|
||||
["gauntlet:metadata_tags"]: {
|
||||
children?: ElementComponent<typeof MetadataTag>;
|
||||
label: string;
|
||||
};
|
||||
["gauntlet:metadata_separator"]: {};
|
||||
["gauntlet:metadata_value"]: {
|
||||
children?: StringComponent;
|
||||
label: string;
|
||||
};
|
||||
["gauntlet:metadata_icon"]: {
|
||||
icon: string;
|
||||
label: string;
|
||||
};
|
||||
["gauntlet:metadata"]: {
|
||||
children?: ElementComponent<typeof MetadataTags | typeof MetadataLink | typeof MetadataValue | typeof MetadataIcon | typeof MetadataSeparator>;
|
||||
};
|
||||
["gauntlet:link"]: {
|
||||
children?: StringComponent;
|
||||
href: string;
|
||||
};
|
||||
["gauntlet:tag"]: {
|
||||
children?: StringComponent;
|
||||
color?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
["gauntlet:metadata_item"]: {
|
||||
children?: Component<typeof Text | typeof Link | typeof Tag>;
|
||||
};
|
||||
["gauntlet:separator"]: {};
|
||||
["gauntlet:metadata"]: {
|
||||
children?: Component<typeof MetadataItem | typeof Separator>;
|
||||
};
|
||||
["gauntlet:image"]: {};
|
||||
["gauntlet:h1"]: {
|
||||
children?: StringComponent;
|
||||
|
|
@ -48,11 +58,14 @@ declare global {
|
|||
["gauntlet:code"]: {
|
||||
children?: StringComponent;
|
||||
};
|
||||
["gauntlet:paragraph"]: {
|
||||
children?: StringOrElementComponent<typeof Link | typeof Code>;
|
||||
};
|
||||
["gauntlet:content"]: {
|
||||
children?: Component<typeof Text | typeof Link | typeof Image | typeof H1 | typeof H2 | typeof H3 | typeof H4 | typeof H5 | typeof H6 | typeof HorizontalBreak | typeof CodeBlock | typeof Code>;
|
||||
children?: ElementComponent<typeof Paragraph | typeof Link | typeof Image | typeof H1 | typeof H2 | typeof H3 | typeof H4 | typeof H5 | typeof H6 | typeof HorizontalBreak | typeof CodeBlock | typeof Code>;
|
||||
};
|
||||
["gauntlet:detail"]: {
|
||||
children?: Component<typeof Metadata | typeof Content>;
|
||||
children?: ElementComponent<typeof Metadata | typeof Content>;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -61,14 +74,68 @@ export type ElementParams<Comp extends FC<any>> = Comp extends FC<infer Params>
|
|||
export type Element<Comp extends FC<any>> = ReactElement<ElementParams<Comp>, JSXElementConstructor<ElementParams<Comp>>>;
|
||||
export type StringNode = string | number;
|
||||
export type EmptyNode = boolean | null | undefined;
|
||||
export type Component<Comp extends FC<any>> = Element<Comp> | EmptyNode | Iterable<Component<Comp>>;
|
||||
export type ElementComponent<Comp extends FC<any>> = Element<Comp> | EmptyNode | Iterable<ElementComponent<Comp>>;
|
||||
export type StringComponent = StringNode | EmptyNode | Iterable<StringComponent>;
|
||||
export interface TextProps {
|
||||
export type StringOrElementComponent<Comp extends FC<any>> = StringNode | EmptyNode | Element<Comp> | Iterable<StringOrElementComponent<Comp>>;
|
||||
export interface MetadataLinkProps {
|
||||
children?: StringComponent;
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
export const Text: FC<TextProps> = (props: TextProps): ReactNode => {
|
||||
return <gauntlet:text children={props.children}/>;
|
||||
export const MetadataLink: FC<MetadataLinkProps> = (props: MetadataLinkProps): ReactNode => {
|
||||
return <gauntlet:metadata_link children={props.children} label={props.label} href={props.href}/>;
|
||||
};
|
||||
export interface MetadataTagProps {
|
||||
children?: StringComponent;
|
||||
onClick?: () => void;
|
||||
}
|
||||
export const MetadataTag: FC<MetadataTagProps> = (props: MetadataTagProps): ReactNode => {
|
||||
return <gauntlet:metadata_tag children={props.children} onClick={props.onClick}/>;
|
||||
};
|
||||
export interface MetadataTagsProps {
|
||||
children?: ElementComponent<typeof MetadataTag>;
|
||||
label: string;
|
||||
}
|
||||
export const MetadataTags: FC<MetadataTagsProps> & {
|
||||
Tag: typeof MetadataTag;
|
||||
} = (props: MetadataTagsProps): ReactNode => {
|
||||
return <gauntlet:metadata_tags children={props.children} label={props.label}/>;
|
||||
};
|
||||
MetadataTags.Tag = MetadataTag;
|
||||
export const MetadataSeparator: FC = (): ReactNode => {
|
||||
return <gauntlet:metadata_separator />;
|
||||
};
|
||||
export interface MetadataValueProps {
|
||||
children?: StringComponent;
|
||||
label: string;
|
||||
}
|
||||
export const MetadataValue: FC<MetadataValueProps> = (props: MetadataValueProps): ReactNode => {
|
||||
return <gauntlet:metadata_value children={props.children} label={props.label}/>;
|
||||
};
|
||||
export interface MetadataIconProps {
|
||||
icon: string;
|
||||
label: string;
|
||||
}
|
||||
export const MetadataIcon: FC<MetadataIconProps> = (props: MetadataIconProps): ReactNode => {
|
||||
return <gauntlet:metadata_icon icon={props.icon} label={props.label}/>;
|
||||
};
|
||||
export interface MetadataProps {
|
||||
children?: ElementComponent<typeof MetadataTags | typeof MetadataLink | typeof MetadataValue | typeof MetadataIcon | typeof MetadataSeparator>;
|
||||
}
|
||||
export const Metadata: FC<MetadataProps> & {
|
||||
Tags: typeof MetadataTags;
|
||||
Link: typeof MetadataLink;
|
||||
Value: typeof MetadataValue;
|
||||
Icon: typeof MetadataIcon;
|
||||
Separator: typeof MetadataSeparator;
|
||||
} = (props: MetadataProps): ReactNode => {
|
||||
return <gauntlet:metadata children={props.children}/>;
|
||||
};
|
||||
Metadata.Tags = MetadataTags;
|
||||
Metadata.Link = MetadataLink;
|
||||
Metadata.Value = MetadataValue;
|
||||
Metadata.Icon = MetadataIcon;
|
||||
Metadata.Separator = MetadataSeparator;
|
||||
export interface LinkProps {
|
||||
children?: StringComponent;
|
||||
href: string;
|
||||
|
|
@ -76,41 +143,6 @@ export interface LinkProps {
|
|||
export const Link: FC<LinkProps> = (props: LinkProps): ReactNode => {
|
||||
return <gauntlet:link children={props.children} href={props.href}/>;
|
||||
};
|
||||
export interface TagProps {
|
||||
children?: StringComponent;
|
||||
color?: string;
|
||||
onClick?: () => void;
|
||||
}
|
||||
export const Tag: FC<TagProps> = (props: TagProps): ReactNode => {
|
||||
return <gauntlet:tag children={props.children} color={props.color} onClick={props.onClick}/>;
|
||||
};
|
||||
export interface MetadataItemProps {
|
||||
children?: Component<typeof Text | typeof Link | typeof Tag>;
|
||||
}
|
||||
export const MetadataItem: FC<MetadataItemProps> & {
|
||||
Text: typeof Text;
|
||||
Link: typeof Link;
|
||||
Tag: typeof Tag;
|
||||
} = (props: MetadataItemProps): ReactNode => {
|
||||
return <gauntlet:metadata_item children={props.children}/>;
|
||||
};
|
||||
MetadataItem.Text = Text;
|
||||
MetadataItem.Link = Link;
|
||||
MetadataItem.Tag = Tag;
|
||||
export const Separator: FC = (): ReactNode => {
|
||||
return <gauntlet:separator />;
|
||||
};
|
||||
export interface MetadataProps {
|
||||
children?: Component<typeof MetadataItem | typeof Separator>;
|
||||
}
|
||||
export const Metadata: FC<MetadataProps> & {
|
||||
Item: typeof MetadataItem;
|
||||
Separator: typeof Separator;
|
||||
} = (props: MetadataProps): ReactNode => {
|
||||
return <gauntlet:metadata children={props.children}/>;
|
||||
};
|
||||
Metadata.Item = MetadataItem;
|
||||
Metadata.Separator = Separator;
|
||||
export const Image: FC = (): ReactNode => {
|
||||
return <gauntlet:image />;
|
||||
};
|
||||
|
|
@ -165,11 +197,22 @@ export interface CodeProps {
|
|||
export const Code: FC<CodeProps> = (props: CodeProps): ReactNode => {
|
||||
return <gauntlet:code children={props.children}/>;
|
||||
};
|
||||
export interface ParagraphProps {
|
||||
children?: StringOrElementComponent<typeof Link | typeof Code>;
|
||||
}
|
||||
export const Paragraph: FC<ParagraphProps> & {
|
||||
Link: typeof Link;
|
||||
Code: typeof Code;
|
||||
} = (props: ParagraphProps): ReactNode => {
|
||||
return <gauntlet:paragraph children={props.children}/>;
|
||||
};
|
||||
Paragraph.Link = Link;
|
||||
Paragraph.Code = Code;
|
||||
export interface ContentProps {
|
||||
children?: Component<typeof Text | typeof Link | typeof Image | typeof H1 | typeof H2 | typeof H3 | typeof H4 | typeof H5 | typeof H6 | typeof HorizontalBreak | typeof CodeBlock | typeof Code>;
|
||||
children?: ElementComponent<typeof Paragraph | typeof Link | typeof Image | typeof H1 | typeof H2 | typeof H3 | typeof H4 | typeof H5 | typeof H6 | typeof HorizontalBreak | typeof CodeBlock | typeof Code>;
|
||||
}
|
||||
export const Content: FC<ContentProps> & {
|
||||
Text: typeof Text;
|
||||
Paragraph: typeof Paragraph;
|
||||
Link: typeof Link;
|
||||
Image: typeof Image;
|
||||
H1: typeof H1;
|
||||
|
|
@ -184,7 +227,7 @@ export const Content: FC<ContentProps> & {
|
|||
} = (props: ContentProps): ReactNode => {
|
||||
return <gauntlet:content children={props.children}/>;
|
||||
};
|
||||
Content.Text = Text;
|
||||
Content.Paragraph = Paragraph;
|
||||
Content.Link = Link;
|
||||
Content.Image = Image;
|
||||
Content.H1 = H1;
|
||||
|
|
@ -197,7 +240,7 @@ Content.HorizontalBreak = HorizontalBreak;
|
|||
Content.CodeBlock = CodeBlock;
|
||||
Content.Code = Code;
|
||||
export interface DetailProps {
|
||||
children?: Component<typeof Metadata | typeof Content>;
|
||||
children?: ElementComponent<typeof Metadata | typeof Content>;
|
||||
}
|
||||
export const Detail: FC<DetailProps> & {
|
||||
Metadata: typeof Metadata;
|
||||
|
|
|
|||
|
|
@ -28,12 +28,17 @@ type Property = {
|
|||
type: PropertyType
|
||||
}
|
||||
type PropertyType = TypeString | TypeNumber | TypeBoolean | TypeArray | TypeFunction
|
||||
type Children = ChildrenMembers | ChildrenString | ChildrenNone
|
||||
type Children = ChildrenMembers | ChildrenString | ChildrenNone | ChildrenStringOrMembers
|
||||
|
||||
type ChildrenMembers = {
|
||||
type: "members",
|
||||
members: ChildrenMember[]
|
||||
}
|
||||
type ChildrenStringOrMembers = {
|
||||
type: "string_or_members",
|
||||
component_internal_name: string,
|
||||
members: ChildrenMember[]
|
||||
}
|
||||
type ChildrenString = {
|
||||
type: "string"
|
||||
component_internal_name: string,
|
||||
|
|
@ -209,7 +214,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
|
|||
),
|
||||
ts.factory.createTypeAliasDeclaration(
|
||||
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.factory.createIdentifier("Component"),
|
||||
ts.factory.createIdentifier("ElementComponent"),
|
||||
[ts.factory.createTypeParameterDeclaration(
|
||||
undefined,
|
||||
ts.factory.createIdentifier("Comp"),
|
||||
|
|
@ -234,7 +239,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
|
|||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Iterable"),
|
||||
[ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Component"),
|
||||
ts.factory.createIdentifier("ElementComponent"),
|
||||
[ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Comp"),
|
||||
undefined
|
||||
|
|
@ -264,6 +269,46 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
|
|||
)]
|
||||
)
|
||||
])
|
||||
),
|
||||
ts.factory.createTypeAliasDeclaration(
|
||||
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.factory.createIdentifier("StringOrElementComponent"),
|
||||
[ts.factory.createTypeParameterDeclaration(
|
||||
undefined,
|
||||
ts.factory.createIdentifier("Comp"),
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("FC"),
|
||||
[ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)]
|
||||
),
|
||||
undefined
|
||||
)],
|
||||
ts.factory.createUnionTypeNode([
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("StringNode"),
|
||||
undefined
|
||||
),
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("EmptyNode"),
|
||||
undefined
|
||||
),
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Element"),
|
||||
[ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Comp"),
|
||||
undefined
|
||||
)]
|
||||
),
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Iterable"),
|
||||
[ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("StringOrElementComponent"),
|
||||
[ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Comp"),
|
||||
undefined
|
||||
)]
|
||||
)]
|
||||
)
|
||||
])
|
||||
)
|
||||
];
|
||||
|
||||
|
|
@ -343,7 +388,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
|
|||
)
|
||||
|
||||
let componentType: ts.TypeReferenceNode | ts.IntersectionTypeNode;
|
||||
if (component.children.type == "members") {
|
||||
if (component.children.type == "members" || component.children.type == "string_or_members") {
|
||||
componentType = ts.factory.createIntersectionTypeNode([
|
||||
componentFCType,
|
||||
ts.factory.createTypeLiteralNode(
|
||||
|
|
@ -367,6 +412,7 @@ function makeComponents(modelInput: Component[]): ts.SourceFile {
|
|||
|
||||
let memberAssignments: ts.Statement[];
|
||||
switch (component.children.type) {
|
||||
case "string_or_members":
|
||||
case "members": {
|
||||
memberAssignments = component.children.members.map(member => {
|
||||
return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
|
||||
|
|
@ -495,7 +541,22 @@ function makeChildrenType(type: Children): ts.TypeNode {
|
|||
switch (type.type) {
|
||||
case "members": {
|
||||
return ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("Component"),
|
||||
ts.factory.createIdentifier("ElementComponent"),
|
||||
[
|
||||
ts.factory.createUnionTypeNode(
|
||||
type.members.map(value => (
|
||||
ts.factory.createTypeQueryNode(
|
||||
ts.factory.createIdentifier(value.componentName),
|
||||
undefined
|
||||
)
|
||||
))
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
case "string_or_members": {
|
||||
return ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("StringOrElementComponent"),
|
||||
[
|
||||
ts.factory.createUnionTypeNode(
|
||||
type.members.map(value => (
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@ export default function View(): ReactElement {
|
|||
<Detail.Content.H4>H4 Title</Detail.Content.H4>
|
||||
<Detail.Content.H5>H5 Title</Detail.Content.H5>
|
||||
<Detail.Content.H6>H6 Title</Detail.Content.H6>
|
||||
<Detail.Content.Code></Detail.Content.Code>
|
||||
<Detail.Content.Image></Detail.Content.Image>
|
||||
<Detail.Content.Link href={""}>s</Detail.Content.Link>
|
||||
<Detail.Content.CodeBlock></Detail.Content.CodeBlock>
|
||||
<Detail.Content.Code>Code code Code</Detail.Content.Code>
|
||||
<Detail.Content.Image/>
|
||||
<Detail.Content.Link href={"https://google.com/"}>Google Link</Detail.Content.Link>
|
||||
<Detail.Content.CodeBlock>Code block Test</Detail.Content.CodeBlock>
|
||||
<Detail.Content.HorizontalBreak/>
|
||||
<Detail.Content.Text>
|
||||
You clicked
|
||||
<Detail.Content.Paragraph>
|
||||
You clicked {count} times
|
||||
{true}
|
||||
{false}
|
||||
{count}
|
||||
|
|
@ -29,26 +29,58 @@ export default function View(): ReactElement {
|
|||
{undefined}
|
||||
{null}
|
||||
{upperCase("times")}
|
||||
</Detail.Content.Text>
|
||||
</Detail.Content.Paragraph>
|
||||
<Detail.Content.H4>Another H4 Title</Detail.Content.H4>
|
||||
<Detail.Content.Paragraph>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
|
||||
et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||
aliquip <Detail.Content.Code> ex ea commodo consequat. </Detail.Content.Code> Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim id est laborum.
|
||||
</Detail.Content.Paragraph>
|
||||
<Detail.Content.Paragraph>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore
|
||||
et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
||||
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||
culpa qui officia deserunt mollit anim id est laborum.
|
||||
</Detail.Content.Paragraph>
|
||||
</Detail.Content>
|
||||
<Detail.Metadata>
|
||||
<Detail.Metadata.Item>
|
||||
<Detail.Metadata.Item.Text>Test item text</Detail.Metadata.Item.Text>
|
||||
<Detail.Metadata.Item.Tag
|
||||
<Detail.Metadata.Tags label="Tags 1">
|
||||
<Detail.Metadata.Tags.Tag
|
||||
onClick={() => {
|
||||
console.log("test " + upperCase("events") + count)
|
||||
setCount(count + 1);
|
||||
}}
|
||||
>
|
||||
Tag
|
||||
</Detail.Metadata.Item.Tag>
|
||||
</Detail.Metadata.Item>
|
||||
</Detail.Metadata.Tags.Tag>
|
||||
<Detail.Metadata.Tags.Tag>
|
||||
Another Tag
|
||||
</Detail.Metadata.Tags.Tag>
|
||||
</Detail.Metadata.Tags>
|
||||
<Detail.Metadata.Separator/>
|
||||
<Detail.Metadata.Item>
|
||||
<Detail.Metadata.Item.Text>Test metadata 1</Detail.Metadata.Item.Text>
|
||||
<Detail.Metadata.Item.Link href={""}>Test Link</Detail.Metadata.Item.Link>
|
||||
<Detail.Metadata.Item.Text>Test metadata 2</Detail.Metadata.Item.Text>
|
||||
</Detail.Metadata.Item>
|
||||
<Detail.Metadata.Link label="Test 2" href={""}>
|
||||
Link text
|
||||
</Detail.Metadata.Link>
|
||||
<Detail.Metadata.Value label="Label 3">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
|
||||
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
|
||||
anim id est laborum.
|
||||
</Detail.Metadata.Value>
|
||||
<Detail.Metadata.Icon label="Label 4" icon="icon"/>
|
||||
<Detail.Metadata.Value label="Label 5">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
|
||||
nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
|
||||
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit
|
||||
anim id est laborum.
|
||||
</Detail.Metadata.Value>
|
||||
</Detail.Metadata>
|
||||
</Detail>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
if has_children {
|
||||
let string = match children {
|
||||
Children::StringOrMembers { .. } => "Vec<ComponentWidgetWrapper>".to_owned(),
|
||||
Children::Members { .. } => "Vec<ComponentWidgetWrapper>".to_owned(),
|
||||
Children::String { .. } => "Vec<ComponentWidgetWrapper>".to_owned(),
|
||||
Children::None => panic!("cannot create type for Children::None")
|
||||
|
|
@ -144,6 +145,12 @@ fn main() -> anyhow::Result<()> {
|
|||
output.push_str(" match get_component_widget_type(&child) {\n");
|
||||
|
||||
match children {
|
||||
Children::StringOrMembers { members, component_internal_name } => {
|
||||
output.push_str(&format!(" (\"gauntlet:{}\", _) => (),\n", component_internal_name));
|
||||
for member in members {
|
||||
output.push_str(&format!(" (\"gauntlet:{}\", _) => (),\n", member.component_internal_name));
|
||||
}
|
||||
}
|
||||
Children::Members { members } => {
|
||||
for member in members {
|
||||
output.push_str(&format!(" (\"gauntlet:{}\", _) => (),\n", member.component_internal_name));
|
||||
|
|
@ -245,6 +252,12 @@ fn main() -> anyhow::Result<()> {
|
|||
output.push_str(" match get_component_widget_type(new_child) {\n");
|
||||
|
||||
match children {
|
||||
Children::StringOrMembers { members, component_internal_name } => {
|
||||
output.push_str(&format!(" (\"gauntlet:{}\", _) => (),\n", component_internal_name));
|
||||
for member in members {
|
||||
output.push_str(&format!(" (\"gauntlet:{}\", _) => (),\n", member.component_internal_name));
|
||||
}
|
||||
}
|
||||
Children::Members { members } => {
|
||||
for member in members {
|
||||
output.push_str(&format!(" (\"gauntlet:{}\", _) => (),\n", member.component_internal_name));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::sync::{Arc, RwLock as StdRwLock};
|
||||
|
||||
use iced::{Application, Command, Event, executor, futures, keyboard, Length, Padding, Subscription, subscription};
|
||||
use iced::{Application, Command, Event, executor, futures, keyboard, Length, Padding, Size, Subscription, subscription};
|
||||
use iced::futures::channel::mpsc::Sender;
|
||||
use iced::futures::SinkExt;
|
||||
use iced::keyboard::KeyCode;
|
||||
|
|
@ -64,6 +64,8 @@ pub enum AppMsg {
|
|||
|
||||
const WINDOW_WIDTH: u32 = 650;
|
||||
const WINDOW_HEIGHT: u32 = 400;
|
||||
const SUB_VIEW_WINDOW_WIDTH: u32 = 850;
|
||||
const SUB_VIEW_WINDOW_HEIGHT: u32 = 500;
|
||||
|
||||
pub fn run() {
|
||||
AppModel::run(Settings {
|
||||
|
|
@ -143,7 +145,7 @@ impl Application for AppModel {
|
|||
|
||||
let dbus_client = self.dbus_client.clone();
|
||||
|
||||
Command::perform(async move {
|
||||
let open_view = Command::perform(async move {
|
||||
let event_view_created = DbusEventViewCreated {
|
||||
reconciler_mode: "persistent".to_owned(),
|
||||
view_name: entrypoint_id.to_string(), // TODO what was view_name supposed to be?
|
||||
|
|
@ -154,7 +156,12 @@ impl Application for AppModel {
|
|||
DbusClient::view_created_signal(signal_context, &plugin_id.to_string(), event_view_created)
|
||||
.await
|
||||
.unwrap();
|
||||
}, |_| AppMsg::Noop)
|
||||
}, |_| AppMsg::Noop);
|
||||
|
||||
Command::batch([
|
||||
iced::window::resize(Size::new(SUB_VIEW_WINDOW_WIDTH, SUB_VIEW_WINDOW_HEIGHT)),
|
||||
open_view
|
||||
])
|
||||
}
|
||||
AppMsg::PromptChanged(new_prompt) => {
|
||||
match self.state.last_mut().expect("state is supposed to always have at least one item") {
|
||||
|
|
@ -197,6 +204,9 @@ impl Application for AppModel {
|
|||
KeyCode::Escape => {
|
||||
if self.state.len() <= 1 {
|
||||
iced::window::close()
|
||||
} else if self.state.len() == 2 {
|
||||
self.state.pop();
|
||||
iced::window::resize(Size::new(WINDOW_WIDTH, WINDOW_HEIGHT))
|
||||
} else {
|
||||
self.state.pop();
|
||||
Command::none()
|
||||
|
|
@ -260,7 +270,7 @@ impl Application for AppModel {
|
|||
.into();
|
||||
|
||||
let element: Element<_> = container(column)
|
||||
.style(ContainerStyle::ApplicationBackground)
|
||||
.style(ContainerStyle::Background)
|
||||
.height(Length::Fixed(WINDOW_HEIGHT as f32))
|
||||
.width(Length::Fixed(WINDOW_WIDTH as f32))
|
||||
.into();
|
||||
|
|
@ -277,11 +287,14 @@ impl Application for AppModel {
|
|||
widget_event,
|
||||
});
|
||||
|
||||
container(container_element)
|
||||
.style(ContainerStyle::ApplicationBackground)
|
||||
.height(Length::Fixed(WINDOW_HEIGHT as f32))
|
||||
.width(Length::Fixed(WINDOW_WIDTH as f32))
|
||||
.into()
|
||||
let element: Element<_> = container(container_element)
|
||||
.style(ContainerStyle::Background)
|
||||
.height(Length::Fixed(SUB_VIEW_WINDOW_HEIGHT as f32))
|
||||
.width(Length::Fixed(SUB_VIEW_WINDOW_WIDTH as f32))
|
||||
.into();
|
||||
|
||||
// element.explain(iced::color!(0xFF0000))
|
||||
element
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,7 +167,8 @@ impl scrollable::StyleSheet for GauntletTheme {
|
|||
pub enum ContainerStyle {
|
||||
#[default]
|
||||
Transparent,
|
||||
ApplicationBackground,
|
||||
Background,
|
||||
Code,
|
||||
}
|
||||
|
||||
impl container::StyleSheet for GauntletTheme {
|
||||
|
|
@ -176,7 +177,7 @@ impl container::StyleSheet for GauntletTheme {
|
|||
fn appearance(&self, style: &Self::Style) -> container::Appearance {
|
||||
match style {
|
||||
ContainerStyle::Transparent => Default::default(),
|
||||
ContainerStyle::ApplicationBackground => {
|
||||
ContainerStyle::Background => {
|
||||
let palette = self.extended_palette();
|
||||
|
||||
container::Appearance {
|
||||
|
|
@ -187,6 +188,17 @@ impl container::StyleSheet for GauntletTheme {
|
|||
border_color: palette.background.weak.color,
|
||||
}
|
||||
}
|
||||
ContainerStyle::Code => {
|
||||
let palette = self.extended_palette();
|
||||
|
||||
container::Appearance {
|
||||
text_color: None,
|
||||
background: Some(palette.background.weak.color.into()),
|
||||
border_radius: 4.0.into(),
|
||||
border_width: 1.0,
|
||||
border_color: palette.background.weak.color,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -267,7 +279,7 @@ impl button::StyleSheet for GauntletTheme {
|
|||
ButtonStyle::Positive => from_pair(palette.success.base),
|
||||
ButtonStyle::Destructive => from_pair(palette.danger.base),
|
||||
ButtonStyle::Link => button::Appearance {
|
||||
text_color: palette.background.base.text,
|
||||
text_color: palette.background.weak.text,
|
||||
..appearance
|
||||
},
|
||||
ButtonStyle::EntrypointItem => button::Appearance {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use iced::Font;
|
||||
use iced::{Font, Length, Padding};
|
||||
use iced::font::Weight;
|
||||
use iced::widget::{button, column, row, text};
|
||||
use iced::widget::{button, column, container, horizontal_rule, row, scrollable, text, tooltip, vertical_rule};
|
||||
use iced::widget::tooltip::Position;
|
||||
use zbus::SignalContext;
|
||||
|
||||
use common::dbus::DbusEventViewEvent;
|
||||
|
|
@ -11,7 +12,7 @@ use common::model::PluginId;
|
|||
|
||||
use crate::dbus::DbusClient;
|
||||
use crate::model::{NativeUiPropertyValue, NativeUiWidget, NativeUiWidgetId};
|
||||
use crate::ui::theme::{ButtonStyle, Element};
|
||||
use crate::ui::theme::{ButtonStyle, ContainerStyle, Element};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ComponentWidgetWrapper {
|
||||
|
|
@ -87,37 +88,100 @@ impl ComponentWidgetWrapper {
|
|||
|
||||
text.into()
|
||||
}
|
||||
ComponentWidget::Text { children } => {
|
||||
row(render_children(children, context))
|
||||
ComponentWidget::MetadataTag { children, onClick: _ } => {
|
||||
let content: Element<_> = row(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
let tag: Element<_> = button(content)
|
||||
.on_press(ComponentWidgetEvent::TagClick { widget: self.as_native_widget() })
|
||||
.into();
|
||||
|
||||
container(tag)
|
||||
.padding(Padding::new(5.0))
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::MetadataTags { label, children } => {
|
||||
let value = row(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
render_metadata_item(label, value)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::MetadataLink { label, children, href } => {
|
||||
let content: Element<_> = row(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
let link: Element<_> = button(content)
|
||||
.style(ButtonStyle::Link)
|
||||
.on_press(ComponentWidgetEvent::LinkClick { href: href.to_owned() })
|
||||
.into();
|
||||
|
||||
let content: Element<_> = if href.is_empty() {
|
||||
link
|
||||
} else {
|
||||
tooltip(link, href, Position::Top)
|
||||
.style(ContainerStyle::Background)
|
||||
.into()
|
||||
};
|
||||
|
||||
render_metadata_item(label, content)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::MetadataValue { label, children} => {
|
||||
let value = row(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
render_metadata_item(label, value)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::MetadataIcon { label, icon} => {
|
||||
let value = text(icon).into();
|
||||
|
||||
render_metadata_item(label, value)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::MetadataSeparator => {
|
||||
let separator: Element<_> = horizontal_rule(1)
|
||||
.into();
|
||||
|
||||
container(separator)
|
||||
.width(Length::Fill)
|
||||
.padding(Padding::from([10.0, 0.0]))
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Metadata { children } => {
|
||||
let metadata: Element<_> = column(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
scrollable(metadata)
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Paragraph { children } => {
|
||||
let paragraph: Element<_> = row(render_children(children, context))
|
||||
.into();
|
||||
|
||||
container(paragraph)
|
||||
.width(Length::Fill)
|
||||
.padding(Padding::new(5.0))
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Link { children, href } => {
|
||||
let content: Element<_> = row(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
button(content)
|
||||
let content: Element<_> = button(content)
|
||||
.style(ButtonStyle::Link)
|
||||
.on_press(ComponentWidgetEvent::LinkClick { href: href.to_owned() })
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Tag { children, onClick: _, color: _ } => {
|
||||
let content: Element<_> = row(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
button(content)
|
||||
.on_press(ComponentWidgetEvent::TagClick { widget: self.as_native_widget() })
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::MetadataItem { children } => {
|
||||
row(render_children(children, ComponentRenderContext::None))
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Separator => {
|
||||
text("Separator").into()
|
||||
}
|
||||
ComponentWidget::Metadata { children } => {
|
||||
column(render_children(children, ComponentRenderContext::None))
|
||||
.into()
|
||||
if href.is_empty() {
|
||||
content
|
||||
} else {
|
||||
tooltip(content, href, Position::Top)
|
||||
.style(ContainerStyle::Background)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
ComponentWidget::Image => {
|
||||
text("Image").into()
|
||||
|
|
@ -147,25 +211,67 @@ impl ComponentWidgetWrapper {
|
|||
.into()
|
||||
}
|
||||
ComponentWidget::HorizontalBreak => {
|
||||
text("HorizontalBreak").into()
|
||||
let separator: Element<_> = horizontal_rule(1).into();
|
||||
|
||||
container(separator)
|
||||
.width(Length::Fill)
|
||||
.padding(Padding::from([10.0, 0.0]))
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::CodeBlock { children } => {
|
||||
text("CodeBlock").into()
|
||||
let content: Element<_> = row(render_children(children, ComponentRenderContext::None))
|
||||
.padding(Padding::from([3.0, 5.0]))
|
||||
.into();
|
||||
|
||||
container(content)
|
||||
.width(Length::Fill)
|
||||
.style(ContainerStyle::Code)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Code { children } => {
|
||||
text("Code").into()
|
||||
let content: Element<_> = row(render_children(children, ComponentRenderContext::None))
|
||||
.padding(Padding::from([3.0, 5.0]))
|
||||
.into();
|
||||
|
||||
container(content)
|
||||
.style(ContainerStyle::Code)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Content { children } => {
|
||||
column(render_children(children, ComponentRenderContext::None))
|
||||
let content: Element<_> = column(render_children(children, ComponentRenderContext::None))
|
||||
.into();
|
||||
|
||||
scrollable(content)
|
||||
// .direction(Direction::Both { horizontal: Properties::default(), vertical: Properties::default() })
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Detail { children } => {
|
||||
let metadata_element = render_child_by_type(children, |widget| matches!(widget, ComponentWidget::Metadata { .. }), ComponentRenderContext::None)
|
||||
.unwrap();
|
||||
|
||||
let metadata_element = container(metadata_element)
|
||||
.width(Length::FillPortion(2))
|
||||
.padding(Padding::new(5.0))
|
||||
.into();
|
||||
|
||||
let content_element = render_child_by_type(children, |widget| matches!(widget, ComponentWidget::Content { .. }), ComponentRenderContext::None)
|
||||
.unwrap();
|
||||
|
||||
row(vec![content_element, metadata_element])
|
||||
let content_element = container(content_element)
|
||||
.width(Length::FillPortion(3))
|
||||
.padding(Padding::new(5.0))
|
||||
.into();
|
||||
|
||||
let separator = vertical_rule(1)
|
||||
.into();
|
||||
|
||||
let content: Element<_> = row(vec![content_element, separator, metadata_element])
|
||||
.into();
|
||||
|
||||
container(content)
|
||||
.width(Length::Fill)
|
||||
.padding(Padding::new(10.0))
|
||||
.into()
|
||||
}
|
||||
ComponentWidget::Root { children } => {
|
||||
|
|
@ -196,6 +302,24 @@ impl ComponentWidgetWrapper {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_metadata_item<'a>(label: &str, value: Element<'a, ComponentWidgetEvent>) -> Element<'a, ComponentWidgetEvent> {
|
||||
let bold_font = Font {
|
||||
weight: Weight::Bold,
|
||||
..Font::DEFAULT
|
||||
};
|
||||
|
||||
let label: Element<_> = text(label)
|
||||
.font(bold_font)
|
||||
.into();
|
||||
|
||||
let value = container(value)
|
||||
.padding(Padding::new(5.0))
|
||||
.into();
|
||||
|
||||
column(vec![label, value])
|
||||
.into()
|
||||
}
|
||||
|
||||
fn render_children<'a>(
|
||||
content: &[ComponentWidgetWrapper],
|
||||
context: ComponentRenderContext
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ pub enum PropertyType {
|
|||
#[derive(Debug, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Children {
|
||||
#[serde(rename = "string_or_members")]
|
||||
StringOrMembers {
|
||||
members: Vec<ChildrenMember>,
|
||||
component_internal_name: String,
|
||||
},
|
||||
#[serde(rename = "members")]
|
||||
Members {
|
||||
members: Vec<ChildrenMember>,
|
||||
|
|
@ -109,6 +114,13 @@ pub struct RootChild {
|
|||
pub component_name: ComponentName,
|
||||
}
|
||||
|
||||
fn children_string_or_members(members: Vec<ChildrenMember>) -> Children {
|
||||
Children::StringOrMembers {
|
||||
component_internal_name: "text_part".to_owned(),
|
||||
members,
|
||||
}
|
||||
}
|
||||
|
||||
fn children_members(members: Vec<ChildrenMember>) -> Children {
|
||||
Children::Members {
|
||||
members,
|
||||
|
|
@ -180,11 +192,74 @@ fn property(name: impl Into<String>, optional: bool, property_type: PropertyType
|
|||
}
|
||||
|
||||
pub fn create_component_model() -> Vec<Component> {
|
||||
let text_component = component(
|
||||
"text",
|
||||
"Text",
|
||||
let metadata_link_component = component(
|
||||
"metadata_link",
|
||||
"MetadataLink",
|
||||
vec![
|
||||
property("label", false, PropertyType::String),
|
||||
property("href", false, PropertyType::String),
|
||||
],
|
||||
children_string()
|
||||
);
|
||||
|
||||
let metadata_tag_component = component(
|
||||
"metadata_tag",
|
||||
"MetadataTag",
|
||||
vec![
|
||||
// property("color", true, PropertyType::String),
|
||||
property("onClick", true, PropertyType::Function)
|
||||
],
|
||||
children_string()
|
||||
);
|
||||
|
||||
let metadata_tags_component = component(
|
||||
"metadata_tags",
|
||||
"MetadataTags",
|
||||
vec![
|
||||
property("label", false, PropertyType::String)
|
||||
],
|
||||
children_members(vec![
|
||||
member("Tag", &metadata_tag_component),
|
||||
])
|
||||
);
|
||||
|
||||
let metadata_separator_component = component(
|
||||
"metadata_separator",
|
||||
"MetadataSeparator",
|
||||
vec![],
|
||||
children_string(),
|
||||
children_none()
|
||||
);
|
||||
|
||||
let metadata_icon_component = component(
|
||||
"metadata_icon",
|
||||
"MetadataIcon",
|
||||
vec![
|
||||
property("icon", false, PropertyType::String),
|
||||
property("label", false, PropertyType::String),
|
||||
],
|
||||
children_none()
|
||||
);
|
||||
|
||||
let metadata_value_component = component(
|
||||
"metadata_value",
|
||||
"MetadataValue",
|
||||
vec![
|
||||
property("label", false, PropertyType::String),
|
||||
],
|
||||
children_string()
|
||||
);
|
||||
|
||||
let metadata_component = component(
|
||||
"metadata",
|
||||
"Metadata",
|
||||
vec![],
|
||||
children_members(vec![
|
||||
member("Tags", &metadata_tags_component),
|
||||
member("Link", &metadata_link_component),
|
||||
member("Value", &metadata_value_component),
|
||||
member("Icon", &metadata_icon_component),
|
||||
member("Separator", &metadata_separator_component),
|
||||
])
|
||||
);
|
||||
|
||||
let link_component = component(
|
||||
|
|
@ -196,44 +271,6 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
children_string()
|
||||
);
|
||||
|
||||
let tag_component = component(
|
||||
"tag",
|
||||
"Tag",
|
||||
vec![
|
||||
property("color", true, PropertyType::String),
|
||||
property("onClick", true, PropertyType::Function)
|
||||
],
|
||||
children_string()
|
||||
);
|
||||
|
||||
let metadata_item_component = component(
|
||||
"metadata_item",
|
||||
"MetadataItem",
|
||||
vec![],
|
||||
children_members(vec![
|
||||
member("Text", &text_component),
|
||||
member("Link", &link_component),
|
||||
member("Tag", &tag_component),
|
||||
])
|
||||
);
|
||||
|
||||
let separator_component = component(
|
||||
"separator",
|
||||
"Separator",
|
||||
vec![],
|
||||
children_none()
|
||||
);
|
||||
|
||||
let metadata_component = component(
|
||||
"metadata",
|
||||
"Metadata",
|
||||
vec![],
|
||||
children_members(vec![
|
||||
member("Item", &metadata_item_component),
|
||||
member("Separator", &separator_component),
|
||||
])
|
||||
);
|
||||
|
||||
let image_component = component(
|
||||
"image",
|
||||
"Image",
|
||||
|
|
@ -306,12 +343,22 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
children_string()
|
||||
);
|
||||
|
||||
let paragraph_component = component(
|
||||
"paragraph",
|
||||
"Paragraph",
|
||||
vec![],
|
||||
children_string_or_members(vec![
|
||||
member("Link", &link_component),
|
||||
member("Code", &code_component),
|
||||
]),
|
||||
);
|
||||
|
||||
let content_component = component(
|
||||
"content",
|
||||
"Content",
|
||||
vec![],
|
||||
children_members(vec![
|
||||
member("Text", &text_component),
|
||||
member("Paragraph", ¶graph_component),
|
||||
member("Link", &link_component),
|
||||
member("Image", &image_component),
|
||||
member("H1", &h1_component),
|
||||
|
|
@ -342,18 +389,22 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
|
||||
// Detail
|
||||
// Detail.Content
|
||||
// Detail.Content.Text
|
||||
// Detail.Content.Link
|
||||
// Detail.Content.Code
|
||||
// Detail.Content.Paragraph
|
||||
// Detail.Content.Paragraph.Link
|
||||
// Detail.Content.Paragraph.Code
|
||||
// Detail.Content.Image
|
||||
// Detail.Content.H1-6
|
||||
// Detail.Content.HorizontalBreak
|
||||
// Detail.Content.CodeBlock
|
||||
// Detail.Content.Code
|
||||
// Detail.Metadata.Item -- label, icon
|
||||
// Detail.Metadata.Item.Text
|
||||
// Detail.Metadata.Item.Link
|
||||
// Detail.Metadata.Item.Tag
|
||||
// Detail.Metadata
|
||||
// Detail.Metadata.Tags
|
||||
// Detail.Metadata.Tags.Tag
|
||||
// Detail.Metadata.Separator
|
||||
// Detail.Metadata.Link
|
||||
// Detail.Metadata.Value
|
||||
// Detail.Metadata.Icon
|
||||
|
||||
// ActionPanel
|
||||
// ActionPanel.Section
|
||||
|
|
@ -396,14 +447,14 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
vec![
|
||||
text_part,
|
||||
|
||||
text_component,
|
||||
link_component,
|
||||
|
||||
tag_component,
|
||||
metadata_item_component,
|
||||
separator_component,
|
||||
metadata_link_component,
|
||||
metadata_tag_component,
|
||||
metadata_tags_component,
|
||||
metadata_separator_component,
|
||||
metadata_value_component,
|
||||
metadata_icon_component,
|
||||
metadata_component,
|
||||
|
||||
link_component,
|
||||
image_component,
|
||||
h1_component,
|
||||
h2_component,
|
||||
|
|
@ -414,6 +465,7 @@ pub fn create_component_model() -> Vec<Component> {
|
|||
horizontal_break_component,
|
||||
code_block_component,
|
||||
code_component,
|
||||
paragraph_component,
|
||||
content_component,
|
||||
|
||||
detail_component,
|
||||
|
|
|
|||
|
|
@ -736,13 +736,29 @@ fn validate_child(state: &Rc<RefCell<OpState>>, parent_internal_name: &str, chil
|
|||
match parent_component {
|
||||
Component::Standard { name: parent_name, children: parent_children, .. } => {
|
||||
match parent_children {
|
||||
Children::Members { members } => {
|
||||
let allowed_members: HashMap<_, _> = members.iter()
|
||||
.map(|member| (&member.component_internal_name, member))
|
||||
.collect();
|
||||
|
||||
Children::StringOrMembers { members, .. } => {
|
||||
match child_component {
|
||||
Component::Standard { internal_name, name, .. } => {
|
||||
let allowed_members: HashMap<_, _> = members.iter()
|
||||
.map(|member| (&member.component_internal_name, member))
|
||||
.collect();
|
||||
|
||||
match allowed_members.get(internal_name) {
|
||||
None => Err(anyhow::anyhow!("{} component not be a child of {}", name, parent_name))?,
|
||||
Some(_) => (),
|
||||
}
|
||||
}
|
||||
Component::Root { .. } => Err(anyhow::anyhow!("root component not be a child"))?,
|
||||
Component::TextPart { .. } => ()
|
||||
}
|
||||
}
|
||||
Children::Members { members } => {
|
||||
match child_component {
|
||||
Component::Standard { internal_name, name, .. } => {
|
||||
let allowed_members: HashMap<_, _> = members.iter()
|
||||
.map(|member| (&member.component_internal_name, member))
|
||||
.collect();
|
||||
|
||||
match allowed_members.get(internal_name) {
|
||||
None => Err(anyhow::anyhow!("{} component not be a child of {}", name, parent_name))?,
|
||||
Some(_) => (),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue