Skip to content

Commit

Permalink
Create generate_from_spec.py which creates almost all the boilerplate…
Browse files Browse the repository at this point in the history
… for a new component based on ComponentSpec
  • Loading branch information
wwwillchen committed Dec 14, 2023
1 parent 5e6530e commit ffbbec2
Show file tree
Hide file tree
Showing 8 changed files with 309 additions and 60 deletions.
6 changes: 4 additions & 2 deletions component_specs/BUILD
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
load("//build_defs:defaults.bzl", "py_library")
load("//build_defs:defaults.bzl", "py_binary", "py_library")

package(
default_visibility = ["//build_defs:mesop_internal"],
Expand All @@ -14,8 +14,10 @@ py_library(

py_binary(
name = "checkbox_spec",
srcs = ["checkbox_spec.py"],
srcs = glob(["*.py"]),
main = "checkbox_spec.py",
deps = [
# ":component_specs",
"//mesop/protos:ui_py_pb2",
],
)
Empty file added component_specs/__init__.py
Empty file.
59 changes: 18 additions & 41 deletions component_specs/checkbox_spec.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import mesop.protos.ui_pb2 as pb
from component_specs.generate_from_spec import (
generate_ng_template,
generate_ng_ts,
generate_proto_schema,
generate_py_component,
)

# // <mat-checkbox [checked]="isChecked" (change)="handleCheckboxChange($event)">
# // {{config().getLabel()}}
Expand All @@ -7,7 +13,10 @@
type_name="checkbox",
element_name="mat-checkbox",
props=[
# pb.ElementProp(key="checked", property_binding=""),
pb.ElementProp(
key="checked",
property_binding=pb.PropertyBinding(name="value", type=pb.JsType.STRING),
), # TODO: need a special marker for value (default value)
pb.ElementProp(
key="change",
event_binding=pb.EventBinding(
Expand All @@ -19,45 +28,13 @@
content=pb.ContentSpec(name="label", type=pb.JsType.STRING),
)

"""
Spec recipe:
1. Generate Angular template
2. Generate Angular TS
3. Generate Python method
"""


def generate_ng_template(spec: pb.ComponentSpec) -> str:
element_props = [format_element_prop(prop) for prop in spec.props]
opening_braces = "{{"
closing_braces = "}}"

return f"""<{spec.element_name} {" ".join(element_props)}>
{opening_braces}config().getLabel({spec.content.name}){closing_braces}
</{spec.element_name}>
"""


def generate_ng_ts(spec: pb.ComponentSpec) -> str:
"""
Read from component_name.ts
Do simple string replacements
Build event handlers
"""
pass


def format_element_prop(prop: pb.ElementProp) -> str:
if prop.HasField("event_binding"):
return f"""({prop.key})="on{prop.event_binding.event_name}($event)\""""
elif prop.HasField("property_binding"):
raise Exception("not yet implemented property_binding")
elif prop.HasField("model_binding"):
raise Exception("not yet implemented model_binding")
else:
raise Exception("not yet handled", prop)


if __name__ == "__main__":
out = generate_ng_template(CHECKBOX_SPEC)
print(out)
print(".ng.html:")
print(generate_ng_template(CHECKBOX_SPEC))
print(".ts:")
print(generate_ng_ts(CHECKBOX_SPEC))
print(".proto:")
print(generate_proto_schema(CHECKBOX_SPEC))
print(":.py:")
print(generate_py_component(CHECKBOX_SPEC))
31 changes: 31 additions & 0 deletions component_specs/fixtures/component_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from dataclasses import dataclass
from typing import Any, Callable

from pydantic import validate_arguments

import mesop.components.component_name.component_name_pb2 as component_name_pb
from mesop.component_helpers import (
handler_type,
insert_component,
register_event_mapper,
)
from mesop.events import MesopEvent

# INSERT_EVENTS


@validate_arguments
def component_name(
*,
# INSERT_COMPONENT_PARAMS
):
"""
TODO_doc_string
"""
insert_component(
key=key,
type_name="component_name",
proto=component_name_pb.ComponentNameType(
# INSERT_PROTO_CALLSITE
),
)
21 changes: 6 additions & 15 deletions component_specs/fixtures/component_name.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,34 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
import {MatCheckboxChange} from '@angular/material/checkbox';
import {Component, Input} from '@angular/core';
import {
Click,
Key,
Type,
UserEvent,
} from 'mesop/mesop/protos/ui_jspb_proto_pb/mesop/protos/ui_pb';
import {ComponentNameType} from 'mesop/mesop/components/component_name/component_name_jspb_proto_pb/mesop/components/component_name/component_name_pb';
import {Channel} from '../../web/src/services/channel';

@Component({
selector: 'mesop-{component-name}',
templateUrl: '{component_name}.ng.html',
// selector: 'mesop-{component-name}',
templateUrl: 'component_name.ng.html',
standalone: true,
})
export class ComponentNameComponent {
@Input({required: true}) type!: Type;
@Input() key!: Key;
private _config!: ComponentNameType;
isChecked = false;
value: any;

constructor(private readonly channel: Channel) {}

ngOnChanges() {
this._config = ComponentNameType.deserializeBinary(
this.type.getValue() as unknown as Uint8Array,
);
this.value = this._config.getDefaultValue();
}

config(): ComponentNameType {
return this._config;
}

handleCheckboxChange(event: MatCheckboxChange) {
this.isChecked = event.checked;
const userEvent = new UserEvent();
userEvent.setBool(event.checked);
userEvent.setHandlerId(this.config().getOnUpdateHandlerId()!);
userEvent.setKey(this.key);
this.channel.dispatch(userEvent);
}
// INSERT_EVENT_METHODS:
}
Loading

0 comments on commit ffbbec2

Please sign in to comment.