Skip to content

Commit

Permalink
Merge branch 'main' of github.com:zauberzeug/nicegui
Browse files Browse the repository at this point in the history
  • Loading branch information
rodja committed Feb 1, 2025
2 parents 8f75c79 + b373df1 commit c3ce92a
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 19 deletions.
21 changes: 21 additions & 0 deletions examples/custom_vue_component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Custom Vue Component

This example demonstrates how to create and use custom Vue components in NiceGUI.
One component is implemented using JavaScript, the other using Vue's Single-File Component (SFC) syntax.

## Counter Component (counter.js)

The `Counter` component is a simple counter that increments a value.
On change, it emits an event with the new value.
A reset method allows to reset the counter to 0.

The JavaScript code in `counter.js` defines the front-end logic of the component using JavaScript.

## OnOff Component (on_off.vue)

The `OnOff` component is a simple toggle that switches between on and off.
On change, it emits an event with the new value.
A reset method is also provided to reset the toggle to off.

The Single-File Component in `on_off.vue` defines the front-end logic of the component using Vue 2.
In contrast to the JavaScript code in `counter.js`, it splits the template, script, and style into separate sections.
13 changes: 7 additions & 6 deletions examples/custom_vue_component/counter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
// NOTE: Make sure to reload the browser with cache disabled after making changes to this file.
export default {
template: `
<button @click="handle_click">
<strong>{{title}}: {{value}}</strong>
</button>`,
<button @click="handle_click" :style="{ background: value > 0 ? '#bf8' : '#eee', padding: '8px 16px', borderRadius: '4px' }">
<strong>{{title}}: {{value}}</strong>
</button>
`,
props: {
title: String,
},
data() {
return {
value: 0,
Expand All @@ -18,7 +22,4 @@ export default {
this.value = 0;
},
},
props: {
title: String,
},
};
17 changes: 8 additions & 9 deletions examples/custom_vue_component/main.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
#!/usr/bin/env python3
from counter import Counter
from on_off import OnOff

from nicegui import ui

ui.markdown('''
#### Try the new click counter!
with ui.row(align_items='center'):
counter = Counter('Count', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
ui.button('Reset', on_click=counter.reset).props('outline')

Click to increment its value.
''')
with ui.card():
counter = Counter('Clicks', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
with ui.row(align_items='center'):
on_off = OnOff('State', on_change=lambda e: ui.notify(f'The value changed to {e.args}.'))
ui.button('Reset', on_click=on_off.reset).props('outline')


ui.button('Reset', on_click=counter.reset).props('small outline')

ui.run()
ui.run(uvicorn_reload_includes='*.py,*.js,*.vue')
14 changes: 14 additions & 0 deletions examples/custom_vue_component/on_off.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from typing import Callable, Optional

from nicegui.element import Element


class OnOff(Element, component='on_off.vue'):

def __init__(self, title: str, *, on_change: Optional[Callable] = None) -> None:
super().__init__()
self._props['title'] = title
self.on('change', on_change)

def reset(self) -> None:
self.run_method('reset')
41 changes: 41 additions & 0 deletions examples/custom_vue_component/on_off.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<div>
<button @click="handle_click" :class="{ active: value }">
<strong>{{ title }}: {{ value ? "ON" : "OFF" }}</strong>
</button>
</div>
</template>

<script>
export default {
props: {
title: String,
},
data() {
return {
value: false,
};
},
methods: {
handle_click() {
this.value = !this.value;
this.$emit("change", this.value);
},
reset() {
this.value = false;
},
},
};
</script>

<style scoped>
button {
background-color: #eee;
padding: 8px 16px;
border-radius: 4px;
}
button.active {
background-color: #fb8;
}
</style>
5 changes: 5 additions & 0 deletions nicegui/elements/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ export default {
mounted() {
for (const event of this.events) {
document.addEventListener(event, (evt) => {
// https://github.com/zauberzeug/nicegui/issues/4290
if (!(evt instanceof KeyboardEvent)) return;

// https://stackoverflow.com/a/36469636/3419103
const focus = document.activeElement;
if (focus && this.ignore.includes(focus.tagName.toLowerCase())) return;

if (evt.repeat && !this.repeating) return;

this.$emit("key", {
action: event,
altKey: evt.altKey,
Expand Down
2 changes: 2 additions & 0 deletions nicegui/elements/leaflet.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ export default {
draw_control: Object,
resource_path: String,
hide_drawn_items: Boolean,
additional_resources: Array,
},
async mounted() {
await this.$nextTick(); // NOTE: wait for window.path_prefix to be set
await Promise.all([
loadResource(window.path_prefix + `${this.resource_path}/leaflet/leaflet.css`),
loadResource(window.path_prefix + `${this.resource_path}/leaflet/leaflet.js`),
...this.additional_resources.map((resource) => loadResource(resource)),
]);
if (this.draw_control) {
await Promise.all([
Expand Down
5 changes: 4 additions & 1 deletion nicegui/elements/leaflet.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import asyncio
from pathlib import Path
from typing import Any, Dict, List, Tuple, Union, cast
from typing import Any, Dict, List, Optional, Tuple, Union, cast

from typing_extensions import Self

Expand All @@ -27,6 +27,7 @@ def __init__(self,
options: Dict = {}, # noqa: B006
draw_control: Union[bool, Dict] = False,
hide_drawn_items: bool = False,
additional_resources: Optional[List[str]] = None,
) -> None:
"""Leaflet map
Expand All @@ -37,6 +38,7 @@ def __init__(self,
:param draw_control: whether to show the draw toolbar (default: False)
:param options: additional options passed to the Leaflet map (default: {})
:param hide_drawn_items: whether to hide drawn items on the map (default: False, *added in version 2.0.0*)
:param additional_resources: additional resources like CSS or JS files to load (default: None)
"""
super().__init__()
self.add_resource(Path(__file__).parent / 'lib' / 'leaflet')
Expand All @@ -51,6 +53,7 @@ def __init__(self,
self._props['options'] = {**options}
self._props['draw_control'] = draw_control
self._props['hide_drawn_items'] = hide_drawn_items
self._props['additional_resources'] = additional_resources or []

self.on('init', self._handle_init)
self.on('map-moveend', self._handle_moveend)
Expand Down
6 changes: 3 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions website/documentation/content/leaflet_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,18 @@ async def page():
m.run_map_method('fitBounds', [[bounds['_southWest'], bounds['_northEast']]])
ui.timer(0, page, once=True) # HIDE


@doc.demo('Leaflet Plugins', '''
You can add plugins to the map by passing the URLs of JS and CSS files to the `additional_resources` parameter.
This demo shows how to add the [Leaflet.RotatedMarker](https://github.com/bbecquet/Leaflet.RotatedMarker) plugin.
It allows you to rotate markers by a given `rotationAngle`.
''')
def leaflet_plugins() -> None:
m = ui.leaflet((51.51, -0.09), additional_resources=[
'https://unpkg.com/[email protected]/leaflet.rotatedMarker.js',
])
m.marker(latlng=(51.51, -0.091), options={'rotationAngle': -30})
m.marker(latlng=(51.51, -0.090), options={'rotationAngle': 30})


doc.reference(ui.leaflet)

0 comments on commit c3ce92a

Please sign in to comment.