Skip to content

Commit c3ce92a

Browse files
committed
Merge branch 'main' of github.com:zauberzeug/nicegui
2 parents 8f75c79 + b373df1 commit c3ce92a

File tree

10 files changed

+119
-19
lines changed

10 files changed

+119
-19
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Custom Vue Component
2+
3+
This example demonstrates how to create and use custom Vue components in NiceGUI.
4+
One component is implemented using JavaScript, the other using Vue's Single-File Component (SFC) syntax.
5+
6+
## Counter Component (counter.js)
7+
8+
The `Counter` component is a simple counter that increments a value.
9+
On change, it emits an event with the new value.
10+
A reset method allows to reset the counter to 0.
11+
12+
The JavaScript code in `counter.js` defines the front-end logic of the component using JavaScript.
13+
14+
## OnOff Component (on_off.vue)
15+
16+
The `OnOff` component is a simple toggle that switches between on and off.
17+
On change, it emits an event with the new value.
18+
A reset method is also provided to reset the toggle to off.
19+
20+
The Single-File Component in `on_off.vue` defines the front-end logic of the component using Vue 2.
21+
In contrast to the JavaScript code in `counter.js`, it splits the template, script, and style into separate sections.

examples/custom_vue_component/counter.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// NOTE: Make sure to reload the browser with cache disabled after making changes to this file.
22
export default {
33
template: `
4-
<button @click="handle_click">
5-
<strong>{{title}}: {{value}}</strong>
6-
</button>`,
4+
<button @click="handle_click" :style="{ background: value > 0 ? '#bf8' : '#eee', padding: '8px 16px', borderRadius: '4px' }">
5+
<strong>{{title}}: {{value}}</strong>
6+
</button>
7+
`,
8+
props: {
9+
title: String,
10+
},
711
data() {
812
return {
913
value: 0,
@@ -18,7 +22,4 @@ export default {
1822
this.value = 0;
1923
},
2024
},
21-
props: {
22-
title: String,
23-
},
2425
};

examples/custom_vue_component/main.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
#!/usr/bin/env python3
22
from counter import Counter
3+
from on_off import OnOff
34

45
from nicegui import ui
56

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

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

1415

15-
ui.button('Reset', on_click=counter.reset).props('small outline')
16-
17-
ui.run()
16+
ui.run(uvicorn_reload_includes='*.py,*.js,*.vue')
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from typing import Callable, Optional
2+
3+
from nicegui.element import Element
4+
5+
6+
class OnOff(Element, component='on_off.vue'):
7+
8+
def __init__(self, title: str, *, on_change: Optional[Callable] = None) -> None:
9+
super().__init__()
10+
self._props['title'] = title
11+
self.on('change', on_change)
12+
13+
def reset(self) -> None:
14+
self.run_method('reset')
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<template>
2+
<div>
3+
<button @click="handle_click" :class="{ active: value }">
4+
<strong>{{ title }}: {{ value ? "ON" : "OFF" }}</strong>
5+
</button>
6+
</div>
7+
</template>
8+
9+
<script>
10+
export default {
11+
props: {
12+
title: String,
13+
},
14+
data() {
15+
return {
16+
value: false,
17+
};
18+
},
19+
methods: {
20+
handle_click() {
21+
this.value = !this.value;
22+
this.$emit("change", this.value);
23+
},
24+
reset() {
25+
this.value = false;
26+
},
27+
},
28+
};
29+
</script>
30+
31+
<style scoped>
32+
button {
33+
background-color: #eee;
34+
padding: 8px 16px;
35+
border-radius: 4px;
36+
}
37+
38+
button.active {
39+
background-color: #fb8;
40+
}
41+
</style>

nicegui/elements/keyboard.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@ export default {
22
mounted() {
33
for (const event of this.events) {
44
document.addEventListener(event, (evt) => {
5+
// https://github.com/zauberzeug/nicegui/issues/4290
6+
if (!(evt instanceof KeyboardEvent)) return;
7+
58
// https://stackoverflow.com/a/36469636/3419103
69
const focus = document.activeElement;
710
if (focus && this.ignore.includes(focus.tagName.toLowerCase())) return;
11+
812
if (evt.repeat && !this.repeating) return;
13+
914
this.$emit("key", {
1015
action: event,
1116
altKey: evt.altKey,

nicegui/elements/leaflet.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ export default {
1010
draw_control: Object,
1111
resource_path: String,
1212
hide_drawn_items: Boolean,
13+
additional_resources: Array,
1314
},
1415
async mounted() {
1516
await this.$nextTick(); // NOTE: wait for window.path_prefix to be set
1617
await Promise.all([
1718
loadResource(window.path_prefix + `${this.resource_path}/leaflet/leaflet.css`),
1819
loadResource(window.path_prefix + `${this.resource_path}/leaflet/leaflet.js`),
20+
...this.additional_resources.map((resource) => loadResource(resource)),
1921
]);
2022
if (this.draw_control) {
2123
await Promise.all([

nicegui/elements/leaflet.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import asyncio
22
from pathlib import Path
3-
from typing import Any, Dict, List, Tuple, Union, cast
3+
from typing import Any, Dict, List, Optional, Tuple, Union, cast
44

55
from typing_extensions import Self
66

@@ -27,6 +27,7 @@ def __init__(self,
2727
options: Dict = {}, # noqa: B006
2828
draw_control: Union[bool, Dict] = False,
2929
hide_drawn_items: bool = False,
30+
additional_resources: Optional[List[str]] = None,
3031
) -> None:
3132
"""Leaflet map
3233
@@ -37,6 +38,7 @@ def __init__(self,
3738
:param draw_control: whether to show the draw toolbar (default: False)
3839
:param options: additional options passed to the Leaflet map (default: {})
3940
:param hide_drawn_items: whether to hide drawn items on the map (default: False, *added in version 2.0.0*)
41+
:param additional_resources: additional resources like CSS or JS files to load (default: None)
4042
"""
4143
super().__init__()
4244
self.add_resource(Path(__file__).parent / 'lib' / 'leaflet')
@@ -51,6 +53,7 @@ def __init__(self,
5153
self._props['options'] = {**options}
5254
self._props['draw_control'] = draw_control
5355
self._props['hide_drawn_items'] = hide_drawn_items
56+
self._props['additional_resources'] = additional_resources or []
5457

5558
self.on('init', self._handle_init)
5659
self.on('map-moveend', self._handle_moveend)

poetry.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/documentation/content/leaflet_documentation.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,18 @@ async def page():
196196
m.run_map_method('fitBounds', [[bounds['_southWest'], bounds['_northEast']]])
197197
ui.timer(0, page, once=True) # HIDE
198198

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

0 commit comments

Comments
 (0)