diff --git a/mesop/components/box/box.proto b/mesop/components/box/box.proto index d57c2f6a8..9598b5146 100644 --- a/mesop/components/box/box.proto +++ b/mesop/components/box/box.proto @@ -4,4 +4,5 @@ package mesop.components.box; message BoxType { optional string on_click_handler_id = 2; + repeated string classes = 3; } diff --git a/mesop/components/box/box.py b/mesop/components/box/box.py index 003529275..2834a30d6 100644 --- a/mesop/components/box/box.py +++ b/mesop/components/box/box.py @@ -14,6 +14,7 @@ def box( *, style: Style | None = None, + classes: str = "", on_click: Callable[[ClickEvent], Any] | None = None, key: str | None = None, ) -> Any: @@ -21,6 +22,7 @@ def box( Args: style: Style to apply to component. Follows [HTML Element inline style API](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style). + classes: CSS classes on_click: The callback function that is called when the box is clicked. It receives a ClickEvent as its only argument. key: The component [key](../components/index.md#component-key). @@ -35,6 +37,7 @@ def box( on_click_handler_id=register_event_handler(on_click, event=ClickEvent) if on_click else "", + classes=classes if isinstance(classes, list) else classes.split(" "), ), style=style, ) diff --git a/mesop/examples/__init__.py b/mesop/examples/__init__.py index bd0bb87c7..00f0affd5 100644 --- a/mesop/examples/__init__.py +++ b/mesop/examples/__init__.py @@ -6,6 +6,7 @@ from mesop.examples import ( boilerplate_free_event_handlers as boilerplate_free_event_handlers, ) +from mesop.examples import bootstrap as bootstrap from mesop.examples import box as box from mesop.examples import buttons as buttons from mesop.examples import checkbox_and_radio as checkbox_and_radio @@ -45,6 +46,7 @@ ) from mesop.examples import starter_kit as starter_kit from mesop.examples import sxs as sxs +from mesop.examples import tailwind as tailwind from mesop.examples import testing as testing from mesop.examples import viewport_size as viewport_size from mesop.examples import web_component as web_component diff --git a/mesop/examples/bootstrap.py b/mesop/examples/bootstrap.py new file mode 100644 index 000000000..d2dafb0f8 --- /dev/null +++ b/mesop/examples/bootstrap.py @@ -0,0 +1,89 @@ +import mesop as me + + +@me.page( + security_policy=me.SecurityPolicy( + allowed_iframe_parents=["https://google.github.io"] + ), + stylesheets=[ + "/assets/bootstrap.css", + ], + path="/bootstrap", +) +def page(): + with me.box(classes="container"): + with me.box( + classes="d-flex flex-wrap justify-content-center py-3 mb-4 border-bottom", + ): + with me.box( + classes="d-flex align-items-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none fs-4", + ): + me.text("Mesop") + + with me.box(classes="nav nav-pills"): + with me.box(classes="nav-item"): + with me.box(classes="nav-link active"): + me.text("Pricing") + with me.box(classes="nav-item"): + with me.box(classes="nav-link"): + me.text("Features") + with me.box(classes="nav-item"): + with me.box(classes="nav-link"): + me.text("About") + + with me.box(classes="container px-4 py-5"): + with me.box(classes="pb-2 border-bottom"): + me.text("Columns", type="headline-5") + + with me.box(classes="row g-4 py-5 row-cols-1 row-cols-lg-3"): + with me.box(classes="feature col"): + with me.box(classes="fs-2 text-body-emphasis"): + me.text("Featured title") + me.text( + "Paragraph of text beneath the heading to explain the heading. We'll add onto it with another sentence and probably just keep going until we run out of words." + ) + me.link(text="Call to action", url="/#") + + with me.box(classes="feature col"): + with me.box(classes="fs-2 text-body-emphasis"): + me.text("Featured title") + me.text( + "Paragraph of text beneath the heading to explain the heading. We'll add onto it with another sentence and probably just keep going until we run out of words." + ) + me.link(text="Call to action", url="/#") + + with me.box(classes="feature col"): + with me.box(classes="fs-2 text-body-emphasis"): + me.text("Featured title") + me.text( + "Paragraph of text beneath the heading to explain the heading. We'll add onto it with another sentence and probably just keep going until we run out of words." + ) + me.link(text="Call to action", url="/#") + + with me.box( + classes="d-flex flex-wrap justify-content-between align-items-center py-3 my-4 border-top" + ): + with me.box(classes="col-md-4 mb-0 text-body-secondary"): + me.text("Copyright 2024 Mesop") + + with me.box( + classes="col-md-4 d-flex align-items-center justify-content-center mb-3 mb-md-0 me-md-auto link-body-emphasis text-decoration-none" + ): + me.icon("home") + + with me.box(classes="nav col-md-4 justify-content-end"): + with me.box(classes="nav-item"): + with me.box(classes="nav-link px-2 text-body-secondary"): + me.text("Home") + with me.box(classes="nav-item"): + with me.box(classes="nav-link px-2 text-body-secondary"): + me.text("Features") + with me.box(classes="nav-item"): + with me.box(classes="nav-link px-2 text-body-secondary"): + me.text("Pricing") + with me.box(classes="nav-item"): + with me.box(classes="nav-link px-2 text-body-secondary"): + me.text("FAQs") + with me.box(classes="nav-item"): + with me.box(classes="nav-link px-2 text-body-secondary"): + me.text("About") diff --git a/mesop/examples/tailwind.py b/mesop/examples/tailwind.py new file mode 100644 index 000000000..9576ab324 --- /dev/null +++ b/mesop/examples/tailwind.py @@ -0,0 +1,100 @@ +import mesop as me + + +@me.page( + security_policy=me.SecurityPolicy( + allowed_iframe_parents=["https://google.github.io"] + ), + stylesheets=[ + "/assets/tailwind.css", + ], + path="/tailwind", +) +def app(): + with me.box(classes="grid grid-cols-10 gap-2"): + with me.box(classes="bg-sky-50 aspect-square"): + pass + with me.box(classes="bg-sky-100 aspect-square"): + pass + with me.box(classes="bg-sky-200 aspect-square"): + pass + with me.box(classes="bg-sky-300 aspect-square"): + pass + with me.box(classes="bg-sky-400 aspect-square"): + pass + with me.box(classes="bg-sky-500 aspect-square"): + pass + with me.box(classes="bg-sky-600 aspect-square"): + pass + with me.box(classes="bg-sky-700 aspect-square"): + pass + with me.box(classes="bg-sky-800 aspect-square"): + pass + with me.box(classes="bg-sky-900 aspect-square"): + pass + + with me.box(classes="grid grid-cols-10 gap-2"): + with me.box(classes="bg-blue-50 aspect-square"): + pass + with me.box(classes="bg-blue-100 aspect-square"): + pass + with me.box(classes="bg-blue-200 aspect-square"): + pass + with me.box(classes="bg-blue-300 aspect-square"): + pass + with me.box(classes="bg-blue-400 aspect-square"): + pass + with me.box(classes="bg-blue-500 aspect-square"): + pass + with me.box(classes="bg-blue-600 aspect-square"): + pass + with me.box(classes="bg-blue-700 aspect-square"): + pass + with me.box(classes="bg-blue-800 aspect-square"): + pass + with me.box(classes="bg-blue-900 aspect-square"): + pass + + with me.box(classes="grid grid-cols-10 gap-2"): + with me.box(classes="bg-indigo-50 aspect-square"): + pass + with me.box(classes="bg-indigo-100 aspect-square"): + pass + with me.box(classes="bg-indigo-200 aspect-square"): + pass + with me.box(classes="bg-indigo-300 aspect-square"): + pass + with me.box(classes="bg-indigo-400 aspect-square"): + pass + with me.box(classes="bg-indigo-500 aspect-square"): + pass + with me.box(classes="bg-indigo-600 aspect-square"): + pass + with me.box(classes="bg-indigo-700 aspect-square"): + pass + with me.box(classes="bg-indigo-800 aspect-square"): + pass + with me.box(classes="bg-indigo-900 aspect-square"): + pass + + with me.box(classes="grid grid-cols-10 gap-2"): + with me.box(classes="bg-violet-50 aspect-square"): + pass + with me.box(classes="bg-violet-100 aspect-square"): + pass + with me.box(classes="bg-violet-200 aspect-square"): + pass + with me.box(classes="bg-violet-300 aspect-square"): + pass + with me.box(classes="bg-violet-400 aspect-square"): + pass + with me.box(classes="bg-violet-500 aspect-square"): + pass + with me.box(classes="bg-violet-600 aspect-square"): + pass + with me.box(classes="bg-violet-700 aspect-square"): + pass + with me.box(classes="bg-violet-800 aspect-square"): + pass + with me.box(classes="bg-violet-900 aspect-square"): + pass diff --git a/mesop/web/src/app/styles.scss b/mesop/web/src/app/styles.scss index 7d2cf8e01..153d881db 100644 --- a/mesop/web/src/app/styles.scss +++ b/mesop/web/src/app/styles.scss @@ -260,6 +260,10 @@ body { } } +component-renderer { + display: block; +} + mesop-markdown { h1, h2, diff --git a/mesop/web/src/component_renderer/component_renderer.ts b/mesop/web/src/component_renderer/component_renderer.ts index b46813660..336e1d7d2 100644 --- a/mesop/web/src/component_renderer/component_renderer.ts +++ b/mesop/web/src/component_renderer/component_renderer.ts @@ -288,6 +288,10 @@ export class ComponentRenderer { computeStyles() { this.elementRef.nativeElement.style = this.getStyle(); + const classes = this.getClasses(); + if (classes) { + this.elementRef.nativeElement.classList = classes; + } } createComponentRef() { @@ -371,6 +375,12 @@ Make sure the web component name is spelled the same between Python and JavaScri ////////////// // Box-specific implementation: ////////////// + getClasses(): string { + if (this._boxType) { + return this._boxType.getClassesList().join(' '); + } + return ''; + } getStyle(): string { if (!this._boxType) { @@ -395,10 +405,7 @@ Make sure the web component name is spelled the same between Python and JavaScri return ''; } - // `display: block` because box should have "div"-like semantics. - // Custom elements like Angular component tags are treated as inline by default. - let style = 'display: block;'; - + let style = ''; if (this.component.getStyle()) { style += formatStyle(this.component.getStyle()!); } diff --git a/mesop/web/src/dev_tools/editor_panel/editor_panel.scss b/mesop/web/src/dev_tools/editor_panel/editor_panel.scss index 017846d1d..95d724dff 100644 --- a/mesop/web/src/dev_tools/editor_panel/editor_panel.scss +++ b/mesop/web/src/dev_tools/editor_panel/editor_panel.scss @@ -7,7 +7,7 @@ --default-bottom-panel-height: 400px; } -.container { +.sidenav-container { height: 100%; } diff --git a/mesop/web/src/editor/editor.ng.html b/mesop/web/src/editor/editor.ng.html index b5cb37aa0..c027bfb7c 100644 --- a/mesop/web/src/editor/editor.ng.html +++ b/mesop/web/src/editor/editor.ng.html @@ -1,4 +1,4 @@ - + @defer (when showEditorToolbar()) { diff --git a/mesop/web/src/editor/editor.scss b/mesop/web/src/editor/editor.scss index c571d9097..005747509 100644 --- a/mesop/web/src/editor/editor.scss +++ b/mesop/web/src/editor/editor.scss @@ -1,4 +1,4 @@ -.container { +.sidenav-container { height: 100%; }