Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add content_button equivalent to uploader #1074

Merged
merged 4 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 37 additions & 8 deletions demo/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,43 @@ def load(e: me.LoadEvent):
def app():
state = me.state(State)
with me.box(style=me.Style(padding=me.Padding.all(15))):
me.uploader(
label="Upload Image",
accepted_file_types=["image/jpeg", "image/png"],
on_upload=handle_upload,
type="flat",
color="primary",
style=me.Style(font_weight="bold"),
)
with me.box(style=me.Style(display="flex", gap=20)):
with me.content_uploader(
accepted_file_types=["image/jpeg", "image/png"],
on_upload=handle_upload,
type="flat",
color="primary",
style=me.Style(font_weight="bold"),
):
with me.box(style=me.Style(display="flex", gap=5)):
me.icon("upload")
me.text("Upload Image", style=me.Style(line_height="25px"))

with me.content_uploader(
accepted_file_types=["image/jpeg", "image/png"],
on_upload=handle_upload,
type="flat",
color="warn",
style=me.Style(font_weight="bold"),
):
me.icon("upload")

me.uploader(
label="Upload Image",
accepted_file_types=["image/jpeg", "image/png"],
on_upload=handle_upload,
type="flat",
color="accent",
style=me.Style(font_weight="bold"),
)

with me.content_uploader(
accepted_file_types=["image/jpeg", "image/png"],
on_upload=handle_upload,
type="icon",
style=me.Style(font_weight="bold"),
):
me.icon("upload")

if state.file.size:
with me.box(style=me.Style(margin=me.Margin.all(10))):
Expand Down
1 change: 1 addition & 0 deletions docs/components/uploader.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ matches the look of Angular Material Components.
## API

::: mesop.components.uploader.uploader.uploader
::: mesop.components.uploader.uploader.content_uploader
::: mesop.components.uploader.uploader.UploadEvent
::: mesop.components.uploader.uploader.UploadedFile
3 changes: 3 additions & 0 deletions mesop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,9 @@
from mesop.components.uploader.uploader import (
UploadEvent as UploadEvent,
)
from mesop.components.uploader.uploader import (
content_uploader as content_uploader,
)
from mesop.components.uploader.uploader import uploader as uploader
from mesop.components.video.video import video as video
from mesop.dataclass_utils import dataclass_with_defaults
Expand Down
5 changes: 4 additions & 1 deletion mesop/components/uploader/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ package(
mesop_component(
name = "uploader",
assets = [":uploader.css"],
py_deps = [":uploaded_file"],
py_deps = [
":uploaded_file",
"//mesop/components/text:py",
],
)

sass_binary(
Expand Down
1 change: 1 addition & 0 deletions mesop/components/uploader/e2e/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import content_uploader_app as content_uploader_app
from . import uploader_app as uploader_app
47 changes: 47 additions & 0 deletions mesop/components/uploader/e2e/content_uploader_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import base64

import mesop as me


@me.stateclass
class State:
file: me.UploadedFile
upload_count: int = 0


@me.page(path="/components/uploader/e2e/content_uploader_app")
def app():
state = me.state(State)
with me.box(style=me.Style(padding=me.Padding.all(15))):
with me.content_uploader(
accepted_file_types=["image/jpeg", "image/png"],
on_upload=handle_upload,
type="flat",
color="primary",
style=me.Style(font_weight="bold"),
):
with me.box(style=me.Style(display="flex", gap=5)):
me.icon("upload")
me.text("Upload Image", style=me.Style(line_height="25px"))

if state.file.size:
with me.box(style=me.Style(margin=me.Margin.all(10))):
me.text(f"File name: {state.file.name}")
me.text(f"File size: {state.file.size}")
me.text(f"File type: {state.file.mime_type}")
me.text(f"Upload count: {state.upload_count}")

with me.box(style=me.Style(margin=me.Margin.all(10))):
me.image(src=_convert_contents_data_url(state.file))


def handle_upload(event: me.UploadEvent):
state = me.state(State)
state.file = event.file
state.upload_count += 1


def _convert_contents_data_url(file: me.UploadedFile) -> str:
return (
f"data:{file.mime_type};base64,{base64.b64encode(file.getvalue()).decode()}"
)
32 changes: 32 additions & 0 deletions mesop/components/uploader/e2e/content_uploader_test.ts

Large diffs are not rendered by default.

20 changes: 16 additions & 4 deletions mesop/components/uploader/uploader.ng.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
[style]="getStyle()"
(click)="fileUpload.click()"
>
{{ config().getLabel() }}
<ng-content></ng-content>
</button>

} @if(config().getTypeIndex() === 1) {
Expand All @@ -26,7 +26,7 @@
[style]="getStyle()"
(click)="fileUpload.click()"
>
{{ config().getLabel() }}
<ng-content></ng-content>
</button>

} @if(config().getTypeIndex() === 2) {
Expand All @@ -38,7 +38,7 @@
[style]="getStyle()"
(click)="fileUpload.click()"
>
{{ config().getLabel() }}
<ng-content></ng-content>
</button>

} @if(config().getTypeIndex() === 3) {
Expand All @@ -50,7 +50,19 @@
[style]="getStyle()"
(click)="fileUpload.click()"
>
{{ config().getLabel() }}
<ng-content></ng-content>
</button>

} @if(config().getTypeIndex() === 4) {
<button
mat-icon-button
[color]="config().getColor()"
[disableRipple]="config().getDisableRipple()"
[disabled]="config().getDisabled()"
[style]="getStyle()"
(click)="fileUpload.click()"
>
<ng-content></ng-content>
</button>

}
Expand Down
17 changes: 8 additions & 9 deletions mesop/components/uploader/uploader.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@ message UploadEvent {
}


// Next ID: 9
// Next ID: 8
message UploaderType {
optional string label = 1;
repeated string accepted_file_type = 2;
optional string on_upload_event_handler_id = 3;
optional string color = 4;
optional bool disable_ripple = 5;
optional bool disabled = 6;
repeated string accepted_file_type = 1;
optional string on_upload_event_handler_id = 2;
optional string color = 3;
optional bool disable_ripple = 4;
optional bool disabled = 5;

// Type has two properties:
// |type_index| is used for rendering
// |type| is used for editor value
optional int32 type_index = 7;
optional string type = 8;
optional int32 type_index = 6;
optional string type = 7;
}
59 changes: 51 additions & 8 deletions mesop/components/uploader/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import mesop.components.uploader.uploader_pb2 as uploader_pb
from mesop.component_helpers import (
Style,
insert_component,
component,
insert_composite_component,
register_event_handler,
register_event_mapper,
register_native_component,
)
from mesop.components.text.text import text
from mesop.components.uploader.uploaded_file import UploadedFile
from mesop.events import MesopEvent
from mesop.exceptions import MesopDeveloperException
Expand Down Expand Up @@ -44,7 +46,7 @@ def map_upload_event(event, key):
register_event_mapper(UploadEvent, map_upload_event)


@register_native_component
@component
def uploader(
*,
label: str,
Expand All @@ -57,11 +59,51 @@ def uploader(
disabled: bool = False,
style: Style | None = None,
):
"""Creates an uploader with a simple text Button component.

Args:
label: Uploader button text.
accepted_file_types: List of accepted file types. See the [accept parameter](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept).
key: The component [key](../components/index.md#component-key).
on_upload: File upload event handler.
type: Type of button style to use.
color: Theme color palette of the button.
disable_ripple: Whether the ripple effect is disabled or not.
disabled: Whether the button is disabled.
style: Style for the component.
"""
This function creates an uploader.
with content_uploader(
on_upload=on_upload,
accepted_file_types=accepted_file_types,
type=type,
color=color,
disable_ripple=disable_ripple,
disabled=disabled,
style=style,
key=key,
):
text(label)


@register_native_component
def content_uploader(
*,
accepted_file_types: Sequence[str] | None = None,
key: str | None = None,
on_upload: Callable[[UploadEvent], Any] | None = None,
type: Literal["raised", "flat", "stroked", "icon"] | None = None,
color: Literal["primary", "accent", "warn"] | None = None,
disable_ripple: bool = False,
disabled: bool = False,
style: Style | None = None,
):
"""
Creates an uploader component, which is a composite component. Typically, you would
use a text or icon component as a child.

Intended for advanced use cases.

Args:
label: Upload button label.
accepted_file_types: List of accepted file types. See the [accept parameter](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept).
key: The component [key](../components/index.md#component-key).
on_upload: File upload event handler.
Expand All @@ -71,11 +113,10 @@ def uploader(
disabled: Whether the button is disabled.
style: Style for the component.
"""
insert_component(
return insert_composite_component(
key=key,
type_name="uploader",
type_name="content_uploader",
proto=uploader_pb.UploaderType(
label=label,
accepted_file_type=accepted_file_types or [],
on_upload_event_handler_id=register_event_handler(
on_upload, event=UploadEvent
Expand All @@ -93,7 +134,7 @@ def uploader(


def _get_type_index(
type: Literal["raised", "flat", "stroked"] | None,
type: Literal["raised", "flat", "stroked", "icon"] | None,
) -> int:
if type is None:
return 0
Expand All @@ -103,4 +144,6 @@ def _get_type_index(
return 2
if type == "stroked":
return 3
if type == "icon":
return 4
raise Exception("Unexpected type: " + type)
2 changes: 1 addition & 1 deletion mesop/web/src/component_renderer/type_to_component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const typeToComponent = {
'autocomplete': AutocompleteComponent,
'link': LinkComponent,
'html': HtmlComponent,
'uploader': UploaderComponent,
'content_uploader': UploaderComponent,
'embed': EmbedComponent,
'table': TableComponent,
'sidenav': SidenavComponent,
Expand Down
Loading