From bf361f0a3567ce732e6940511c60d812a84616ae Mon Sep 17 00:00:00 2001 From: Will Chen Date: Fri, 15 Dec 2023 21:13:37 -0800 Subject: [PATCH] Handle native input event --- generator/fixtures/component_name.py | 2 +- generator/generate_ng_ts.py | 11 +++++++++++ generator/output_data/input.binarypb | 4 ++-- generator/output_data/input.json | 2 +- generator/spec_generator.ts | 1 + mesop/__init__.py | 3 +++ mesop/components/input/e2e/input_app.py | 25 ++++++++++++++++++++++++- mesop/components/input/input.ng.html | 1 + mesop/components/input/input.proto | 3 ++- mesop/components/input/input.py | 6 +++++- mesop/components/input/input.ts | 8 ++++++++ mesop/event_handler/event_handler.py | 8 ++++++++ mesop/events/__init__.py | 3 +++ mesop/events/events.py | 5 +++++ 14 files changed, 75 insertions(+), 7 deletions(-) diff --git a/generator/fixtures/component_name.py b/generator/fixtures/component_name.py index 97f3f6c2a..6f7ec299c 100644 --- a/generator/fixtures/component_name.py +++ b/generator/fixtures/component_name.py @@ -10,7 +10,7 @@ insert_composite_component, register_event_mapper, ) -from mesop.events import MesopEvent, ClickEvent +from mesop.events import MesopEvent, ClickEvent, InputEvent # INSERT_EVENTS diff --git a/generator/generate_ng_ts.py b/generator/generate_ng_ts.py index b32bc5d6b..9acc0785a 100644 --- a/generator/generate_ng_ts.py +++ b/generator/generate_ng_ts.py @@ -84,6 +84,17 @@ def generate_ts_event_method(prop: pb.OutputProp) -> str: def generate_ts_native_event_method(native_event: str) -> str: native_event_name = upper_camel_case(native_event) + if native_event == "input": + return f""" + on{native_event_name}(event: Event): void {{ + const userEvent = new UserEvent(); + userEvent.setHandlerId(this.config().getOn{native_event_name}HandlerId()) + userEvent.setString((event.target as HTMLInputElement).value); + userEvent.setKey(this.key); + this.channel.dispatch(userEvent); + }} + """ + return f""" on{native_event_name}(event: Event): void {{ const userEvent = new UserEvent(); diff --git a/generator/output_data/input.binarypb b/generator/output_data/input.binarypb index 79b458744..75ef4abcf 100644 --- a/generator/output_data/input.binarypb +++ b/generator/output_data/input.binarypb @@ -1,6 +1,6 @@ - -input"MatInput*input:matInputRerrorStateMatcher2) + +input"MatInput*input:matInputJinputRerrorStateMatcher2) @angular/material/inputMatInputModule22 @angular/material/form-fieldMatFormFieldModuleX disabledboolean diff --git a/generator/output_data/input.json b/generator/output_data/input.json index 78861dc55..614f10e43 100644 --- a/generator/output_data/input.json +++ b/generator/output_data/input.json @@ -6,7 +6,7 @@ "elementName": "input", "tsFilename": "", "directiveNamesList": ["matInput"], - "nativeEventsList": [], + "nativeEventsList": ["input"], "skipPropertyNamesList": ["errorStateMatcher"], "ngModulesList": [ { diff --git a/generator/spec_generator.ts b/generator/spec_generator.ts index 22ca8cced..a3c1c42e1 100644 --- a/generator/spec_generator.ts +++ b/generator/spec_generator.ts @@ -35,6 +35,7 @@ const inputSpecInput = (() => { i.setName('input'); i.setElementName('input'); i.addDirectiveNames('matInput'); + i.addNativeEvents('input'); i.setIsFormField(true); i.addSkipPropertyNames('errorStateMatcher'); return i; diff --git a/mesop/__init__.py b/mesop/__init__.py index 1a4060e45..37cf913e6 100644 --- a/mesop/__init__.py +++ b/mesop/__init__.py @@ -24,6 +24,9 @@ from mesop.events import ( ClickEvent as ClickEvent, ) +from mesop.events import ( + InputEvent as InputEvent, +) from mesop.features import page as page from mesop.key import Key as Key diff --git a/mesop/components/input/e2e/input_app.py b/mesop/components/input/e2e/input_app.py index c91ea8c0a..0115da153 100644 --- a/mesop/components/input/e2e/input_app.py +++ b/mesop/components/input/e2e/input_app.py @@ -1,7 +1,30 @@ import mesop as me +@me.stateclass +class State: + input: str = "" + count: int = 0 + checked: bool = False + + +@me.on(me.InputEvent) +def on_input(e: me.InputEvent): + state = me.state(State) + state.input = e.value + + +@me.on(me.CheckboxChangeEvent) +def on_change(e: me.CheckboxChangeEvent): + state = me.state(State) + state.checked = e.checked + + @me.page(path="/components/input/e2e/input_app") def app(): me.text(text="Hello, world!") - me.input(label="Basic input") + with me.checkbox(on_change=on_change): + me.text(text="check") + s = me.state(State) + me.input(label="Basic input", on_input=on_input, key=str(s.checked)) + me.text(text=s.input) diff --git a/mesop/components/input/input.ng.html b/mesop/components/input/input.ng.html index 1a599c7d2..2fa466a47 100644 --- a/mesop/components/input/input.ng.html +++ b/mesop/components/input/input.ng.html @@ -18,6 +18,7 @@ [aria-describedby]="config().getUserAriaDescribedBy()" [value]="config().getValue()" [readonly]="config().getReadonly()" + (input)="onInput($event)" /> } diff --git a/mesop/components/input/input.proto b/mesop/components/input/input.proto index 92e4ef7c8..6ed578dd4 100644 --- a/mesop/components/input/input.proto +++ b/mesop/components/input/input.proto @@ -20,5 +20,6 @@ message InputType { string subscript_sizing = 14; string hint_label = 15; string label = 16; - int32 variant_index = 17; + string on_input_handler_id = 17; + int32 variant_index = 18; } diff --git a/mesop/components/input/input.py b/mesop/components/input/input.py index f36e1813b..00696cf7e 100644 --- a/mesop/components/input/input.py +++ b/mesop/components/input/input.py @@ -1,11 +1,13 @@ -from typing import Literal +from typing import Any, Callable, Literal from pydantic import validate_arguments import mesop.components.input.input_pb2 as input_pb from mesop.component_helpers import ( + handler_type, insert_component, ) +from mesop.events import InputEvent @validate_arguments @@ -28,6 +30,7 @@ def input( subscript_sizing: Literal["fixed", "dynamic"] = "fixed", hint_label: str = "", label: str = "", + on_input: Callable[[InputEvent], Any] | None = None, variant: Literal["matInput"] = "matInput", ): """ @@ -53,6 +56,7 @@ def input( subscript_sizing=subscript_sizing, hint_label=hint_label, label=label, + on_input_handler_id=handler_type(on_input) if on_input else "", variant_index=_get_variant_index(variant), ), ) diff --git a/mesop/components/input/input.ts b/mesop/components/input/input.ts index c7df861d1..37d4ad1ca 100644 --- a/mesop/components/input/input.ts +++ b/mesop/components/input/input.ts @@ -46,4 +46,12 @@ export class InputComponent { getSubscriptSizing(): 'fixed' | 'dynamic' { return this.config().getSubscriptSizing() as 'fixed' | 'dynamic'; } + + onInput(event: Event): void { + const userEvent = new UserEvent(); + userEvent.setHandlerId(this.config().getOnInputHandlerId()); + userEvent.setString((event.target as HTMLInputElement).value); + userEvent.setKey(this.key); + this.channel.dispatch(userEvent); + } } diff --git a/mesop/event_handler/event_handler.py b/mesop/event_handler/event_handler.py index c53f43019..e5cda59da 100644 --- a/mesop/event_handler/event_handler.py +++ b/mesop/event_handler/event_handler.py @@ -49,3 +49,11 @@ def wrapper(action: E): key=key, ), ) + +runtime().register_event_mapper( + events.InputEvent, + lambda userEvent, key: events.InputEvent( + value=userEvent.string, + key=key, + ), +) diff --git a/mesop/events/__init__.py b/mesop/events/__init__.py index f4585815b..6fcb54ead 100644 --- a/mesop/events/__init__.py +++ b/mesop/events/__init__.py @@ -4,6 +4,9 @@ from .events import ( ClickEvent as ClickEvent, ) +from .events import ( + InputEvent as InputEvent, +) from .events import ( MesopEvent as MesopEvent, ) diff --git a/mesop/events/events.py b/mesop/events/events.py index 6a0cbf6d5..0b740dd68 100644 --- a/mesop/events/events.py +++ b/mesop/events/events.py @@ -13,6 +13,11 @@ class ClickEvent(MesopEvent): pass +@dataclass +class InputEvent(MesopEvent): + value: str + + @dataclass class ChangeEvent(MesopEvent): value: str