Skip to content

Commit

Permalink
Have <mat-form-field> and <mat-label> as part of input component
Browse files Browse the repository at this point in the history
  • Loading branch information
wwwillchen committed Dec 16, 2023
1 parent a000c92 commit c979f82
Show file tree
Hide file tree
Showing 32 changed files with 267 additions and 280 deletions.
27 changes: 24 additions & 3 deletions generator/component_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
MAYBEs:
- doesn't handle event with multiple properties
"""
LABEL_PROP = pb.Prop(
name="label",
type=pb.XType(simple_type=pb.SimpleType.STRING),
target=pb.Target.TARGET_LABEL,
)

FLAGS = flags.FLAGS

Expand All @@ -31,17 +36,33 @@
def main(argv):
# Process all binarypb files in output_data dir
output_data_path = get_path_from_workspace_root("generator", "output_data")
form_field_spec = pb.ComponentSpec()
form_field_path = os.path.join(output_data_path, "form_field.binarypb")
with open(form_field_path, "rb") as f:
form_field_spec.ParseFromString(f.read())
for prop in form_field_spec.input_props:
prop.target = pb.Target.TARGET_FORM_FIELD
for file in os.listdir(output_data_path):
full_path = os.path.join(output_data_path, file)
if os.path.isfile(full_path) and file.endswith(".binarypb"):
process_component(full_path)
if (
os.path.isfile(full_path)
and file.endswith(".binarypb")
and not file.endswith("form_field.binarypb")
):
process_component(full_path, form_field_spec)


def process_component(pb_path: str):
def process_component(pb_path: str, form_field_spec: pb.ComponentSpec):
spec = pb.ComponentSpec()
with open(pb_path, "rb") as f:
spec.ParseFromString(f.read())

if spec.input.is_form_field:
spec.input_props.MergeFrom(form_field_spec.input_props)
label_prop = pb.Prop()
# Copy from constant just in case there's any unexpected proto mutations.
label_prop.CopyFrom(LABEL_PROP)
spec.input_props.append(label_prop)
name = spec.input.name
write(generate_ng_template(spec), name, ext="ng.html")
write(generate_ng_ts(spec), name, ext="ts")
Expand Down
17 changes: 16 additions & 1 deletion generator/component_spec.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ message ComponentSpecInput {
repeated string skip_property_names = 10;

// TODO: may need to make this multiple modules
NgModuleSpec ng_module = 6;
repeated NgModuleSpec ng_modules = 6;

// If this is a form component that needs to be wrapped by <mat-form-field> (https://material.angular.io/components/form-field/overview)
// then set this to true. This will merge all the properties from form field.
bool is_form_field = 11;
}

message NgModuleSpec {
Expand All @@ -44,13 +48,24 @@ message ComponentSpec {
repeated OutputProp output_props = 3;
}

// If it's merged from form field, we need to keep track when generating the template.
enum Target {
// If it belongs to the regular component
TARGET_UNDEFINED = 0;
// If it belongs to <mat-form-field>
TARGET_FORM_FIELD = 1;
// If it belongs to <mat-label>
TARGET_LABEL = 2;
}

message Prop {
// Name of the property (e.g. JS property name on Angular component)
string name = 1;
// https://angular.io/api/core/Input#alias
string alias = 4;
XType type = 2;
string debug_type = 3;
Target target = 5;
}

message OutputProp {
Expand Down
36 changes: 26 additions & 10 deletions generator/generate_ng_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@

def generate_ng_template(spec: pb.ComponentSpec) -> str:
element_props = (
[format_input_prop(prop) for prop in spec.input_props]
[
format_input_prop(prop)
for prop in spec.input_props
if prop.target == pb.Target.TARGET_UNDEFINED
]
+ [format_output_prop(prop) for prop in spec.output_props]
+ [
format_native_event(native_event)
Expand All @@ -23,28 +27,40 @@ def generate_ng_template(spec: pb.ComponentSpec) -> str:
content = ""
if spec.input.has_content:
content = "<ng-content></ng-content>"
template = f"""<{spec.input.element_name} {" ".join(element_props)}>

def template(directive: str = "") -> str:
out = f"""<{spec.input.element_name} {directive} {" ".join(element_props)}>
{content}
{closing_tag}
"""
"""
form_field_props = [
format_input_prop(prop)
for prop in spec.input_props
if prop.target == pb.Target.TARGET_FORM_FIELD
]
if spec.input.is_form_field:
out = (
f"<mat-form-field {' '.join(form_field_props)}>"
# Hardcode label since it's a special case (assumed it's there when spec is form field)
+ "<mat-label>{{config().getLabel()}}</mat-label>"
+ out
+ "</mat-form-field>"
)
return out

if not len(spec.input.directive_names):
return template
return template()
# You cannot dynamically set the directive (e.g. for button class)
# see: https://github.com/angular/components/issues/26350
# As a workaround, we print the template N times where N = # of variants
open_brace = "{"
closed_brace = "}"
outs: list[str] = []
for index, directive in enumerate(spec.input.directive_names):
template = f"""<{spec.input.element_name} {directive} {" ".join(element_props)}>
{content}
{closing_tag}
"""

outs.append(
f"""
@if(config().getVariantIndex() === {index}) {open_brace}
{template}
{template(directive=directive)}
{closed_brace}
"""
)
Expand Down
17 changes: 7 additions & 10 deletions generator/generate_ng_ts.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,13 @@ def generate_ng_ts(spec: pb.ComponentSpec) -> str:
# default_value_prop = [
# prop for prop in spec.input_props if prop.name == "value"
# ][0]
symbols = ",".join(
[spec.input.ng_module.module_name]
+ list(spec.input.ng_module.other_symbols)
)
symbols = "{" + symbols + "}"
for ng_module in spec.input.ng_modules:
symbols = ",".join([ng_module.module_name] + list(ng_module.other_symbols))
symbols = "{" + symbols + "}"

ts_template = (
f"import {symbols} from '{spec.input.ng_module.import_path}'\n"
+ ts_template
)
ts_template = (
f"import {symbols} from '{ng_module.import_path}'\n" + ts_template
)
# Do simple string replacements
ts_template = (
ts_template.replace("component_name", spec.input.name)
Expand All @@ -41,7 +38,7 @@ def generate_ng_ts(spec: pb.ComponentSpec) -> str:


def generate_ng_imports(spec: pb.ComponentSpec) -> str:
return f"imports: [{spec.input.ng_module.module_name}]"
return f"imports: [{','.join([ng_module.module_name for ng_module in spec.input.ng_modules])}]"


def generate_ts_methods(spec: pb.ComponentSpec) -> str:
Expand Down
25 changes: 16 additions & 9 deletions generator/output_data/button.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
],
"nativeEventsList": ["click"],
"skipPropertyNamesList": ["disabledInteractive"],
"ngModule": {
"importPath": "@angular/material/button",
"moduleName": "MatButtonModule",
"otherSymbolsList": []
}
"ngModulesList": [
{
"importPath": "@angular/material/button",
"moduleName": "MatButtonModule",
"otherSymbolsList": []
}
],
"isFormField": false
},
"inputPropsList": [
{
Expand All @@ -26,31 +29,35 @@
"type": {
"simpleType": 1
},
"debugType": "string | null"
"debugType": "string | null",
"target": 0
},
{
"name": "disableRipple",
"alias": "",
"type": {
"simpleType": 2
},
"debugType": "boolean"
"debugType": "boolean",
"target": 0
},
{
"name": "disabled",
"alias": "",
"type": {
"simpleType": 2
},
"debugType": "boolean"
"debugType": "boolean",
"target": 0
},
{
"name": "ariaDisabled",
"alias": "",
"type": {
"simpleType": 2
},
"debugType": "boolean | undefined"
"debugType": "boolean | undefined",
"target": 0
}
],
"outputPropsList": []
Expand Down
Loading

0 comments on commit c979f82

Please sign in to comment.