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

Unify declaration of third-party dependencies #2991

Merged
merged 15 commits into from
Aug 12, 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
2 changes: 2 additions & 0 deletions examples/node_module_integration/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
package-lock.json
30 changes: 30 additions & 0 deletions examples/node_module_integration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Use Bundled Node Modules as Third-party Dependencies

This example demonstrates how to use multiple third-party node modules as dependencies in a NiceGUI app.
The app uses the [is-odd](https://www.npmjs.com/package/is-odd) node modules to check if a number is even or odd.
We chose this package to demonstrate a very simple node module which has a dependency itself,
namely the [is-number](https://www.npmjs.com/package/is-number) package.
Using NPM, we can easily install both packages and bundle them into a single file which can be used in the app.
The package.json file defines the is-odd dependency and some dev dependencies for bundling the node module,
the webpack.config.js file specifies the entry point for the node module,
and number_checker.js as well as number_checker.py define a new UI element to be used in the NiceGUI app main.py.

1. First, install all third-party node modules (assuming you have NPM installed):

```bash
npm install
```

This will create a node_modules directory containing the is-odd and is-number modules as well as some dev dependencies.

2. Now bundle the node module:

```bash
npm run build
```

3. Finally, you can run the app as usual:

```bash
python3 main.py
```
19 changes: 19 additions & 0 deletions examples/node_module_integration/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env python3
from number_checker import NumberChecker

from nicegui import ui


@ui.page('/')
def page():
number_checker = NumberChecker()
number = ui.number(value=42.0)

async def check():
even = await number_checker.is_even(number.value)
ui.notify(f'{number.value} is {"even" if even else "odd"}')

ui.button('Check', on_click=check)


ui.run()
13 changes: 13 additions & 0 deletions examples/node_module_integration/number_checker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default {
async mounted() {
await import("is-odd");
},
methods: {
isOdd(number) {
return isOdd(number);
},
isEven(number) {
return !isOdd(number);
},
},
};
19 changes: 19 additions & 0 deletions examples/node_module_integration/number_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from nicegui import ui


class NumberChecker(ui.element, component='number_checker.js', dependencies=['dist/is-odd.js']):

def __init__(self) -> None:
"""NumberChecker

A number checker based on the `is-odd <https://www.npmjs.com/package/is-odd>`_ NPM package.
"""
super().__init__()

async def is_odd(self, number: int) -> bool:
"""Check if a number is odd."""
return await self.run_method('isOdd', number)

async def is_even(self, number: int) -> bool:
"""Check if a number is even."""
return await self.run_method('isEven', number)
15 changes: 15 additions & 0 deletions examples/node_module_integration/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"scripts": {
"build": "webpack --config webpack.config.js"
},
"dependencies": {
"is-odd": "^3.0.1"
},
"devDependencies": {
"@babel/core": "^7.24.5",
"@babel/preset-env": "^7.24.5",
"babel-loader": "^9.1.3",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4"
}
}
26 changes: 26 additions & 0 deletions examples/node_module_integration/webpack.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const path = require("path");

module.exports = {
entry: "is-odd/index.js",
mode: "development",
output: {
path: path.resolve(__dirname, "dist"),
filename: "is-odd.js",
library: "isOdd",
libraryTarget: "umd",
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
},
},
},
],
},
};
2 changes: 2 additions & 0 deletions examples/signature_pad/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
package-lock.json
20 changes: 20 additions & 0 deletions examples/signature_pad/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Use Node Modules as Third-party Dependencies

This example demonstrates how to use third-party node modules as dependencies in a NiceGUI app.
The app uses the [signature_pad](https://www.npmjs.com/package/signature_pad) node module to create a signature pad.
In package.json, the signature_pad module is listed as a dependency,
while signature_pad.js and signature_pad.py define the new UI element which can be used in main.py.

1. First, install the third-party node modules (assuming you have NPM installed):

```bash
npm install
```

This will create a node_modules directory containing the signature_pad module.

2. Now you can run the app as usual:

```bash
python3 main.py
```
9 changes: 9 additions & 0 deletions examples/signature_pad/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env python3
from signature_pad import SignaturePad

from nicegui import ui

pad = SignaturePad().classes('border')
ui.button('Clear', on_click=pad.clear)

ui.run()
5 changes: 5 additions & 0 deletions examples/signature_pad/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"dependencies": {
"signature_pad": "^4.1.7"
}
}
16 changes: 16 additions & 0 deletions examples/signature_pad/signature_pad.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import SignaturePad from "signature_pad";

export default {
template: "<canvas></canvas>",
props: {
options: Array,
},
mounted() {
this.pad = new SignaturePad(this.$el, this.options);
},
methods: {
clear() {
this.pad.clear();
},
},
};
20 changes: 20 additions & 0 deletions examples/signature_pad/signature_pad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from typing import Dict, Optional

from nicegui import ui


class SignaturePad(ui.element,
component='signature_pad.js',
dependencies=['node_modules/signature_pad/dist/signature_pad.min.js']):

def __init__(self, options: Optional[Dict] = None) -> None:
"""SignaturePad

An element that integrates the `Signature Pad library <https://szimek.github.io/signature_pad/>`_.
"""
super().__init__()
self._props['options'] = options or {}

def clear(self):
"""Clear the signature."""
self.run_method('clear')
19 changes: 15 additions & 4 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,10 @@ def __init__(self, tag: Optional[str] = None, *, _client: Optional[Client] = Non

def __init_subclass__(cls, *,
component: Union[str, Path, None] = None,
libraries: List[Union[str, Path]] = [], # noqa: B006
exposed_libraries: List[Union[str, Path]] = [], # noqa: B006
extra_libraries: List[Union[str, Path]] = [], # noqa: B006
dependencies: List[Union[str, Path]] = [], # noqa: B006
libraries: List[Union[str, Path]] = [], # noqa: B006 # DEPRECATED
exposed_libraries: List[Union[str, Path]] = [], # noqa: B006 # DEPRECATED
extra_libraries: List[Union[str, Path]] = [], # noqa: B006 # DEPRECATED
) -> None:
super().__init_subclass__()
base = Path(inspect.getfile(cls)).parent
Expand All @@ -120,6 +121,16 @@ def glob_absolute_paths(file: Union[str, Path]) -> List[Path]:
path = base / path
return sorted(path.parent.glob(path.name), key=lambda p: p.stem)

if libraries:
helpers.warn_once('The `libraries` parameter is deprecated. Use `dependencies` instead.',
stack_info=True)
if exposed_libraries:
helpers.warn_once('The `exposed_libraries` parameter is deprecated. Use `dependencies` instead.',
stack_info=True)
if extra_libraries:
helpers.warn_once('The `extra_libraries` parameter is deprecated. Use `dependencies` instead.',
stack_info=True)

cls.component = copy(cls.component)
cls.libraries = copy(cls.libraries)
cls.extra_libraries = copy(cls.extra_libraries)
Expand All @@ -133,7 +144,7 @@ def glob_absolute_paths(file: Union[str, Path]) -> List[Path]:
for library in extra_libraries:
for path in glob_absolute_paths(library):
cls.extra_libraries.append(register_library(path))
for library in exposed_libraries:
for library in exposed_libraries + dependencies:
for path in glob_absolute_paths(library):
cls.exposed_libraries.append(register_library(path, expose=True))

Expand Down
1 change: 1 addition & 0 deletions nicegui/elements/aggrid.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "ag-grid-community";
import { convertDynamicProperties } from "../../static/utils/dynamic_properties.js";

export default {
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/aggrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
pass


class AgGrid(Element, component='aggrid.js', libraries=['lib/aggrid/ag-grid-community.min.js']):
class AgGrid(Element, component='aggrid.js', dependencies=['lib/aggrid/ag-grid-community.min.js']):

def __init__(self,
options: Dict, *,
Expand Down
5 changes: 5 additions & 0 deletions nicegui/elements/echart.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import "echarts";
import { convertDynamicProperties } from "../../static/utils/dynamic_properties.js";

export default {
template: "<div></div>",
async mounted() {
await this.$nextTick(); // wait for Tailwind classes to be applied
if (this.enable_3d) {
await import("echarts-gl");
}

this.chart = echarts.init(this.$el);
this.chart.on("click", (e) => this.$emit("pointClick", e));
Expand Down Expand Up @@ -81,5 +85,6 @@ export default {
},
props: {
options: Object,
enable_3d: Boolean,
},
};
7 changes: 2 additions & 5 deletions nicegui/elements/echart.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
pass


class EChart(Element, component='echart.js', libraries=['lib/echarts/echarts.min.js'], extra_libraries=['lib/echarts-gl/echarts-gl.min.js']):
class EChart(Element, component='echart.js', dependencies=['lib/echarts/echarts.min.js', 'lib/echarts-gl/echarts-gl.min.js']):

def __init__(self, options: Dict, on_point_click: Optional[Callable] = None, *, enable_3d: bool = False) -> None:
"""Apache EChart
Expand All @@ -32,11 +32,8 @@ def __init__(self, options: Dict, on_point_click: Optional[Callable] = None, *,
"""
super().__init__()
self._props['options'] = options
self._props['enable_3d'] = enable_3d or any('3D' in key for key in options)
self._classes.append('nicegui-echart')
for key in options:
if '3D' in key or enable_3d:
self.libraries.extend(library for library in self.extra_libraries if library.name == 'echarts-gl')
break

if on_point_click:
self.on_point_click(on_point_click)
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/joystick.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ..events import GenericEventArguments, JoystickEventArguments, handle_event


class Joystick(Element, component='joystick.vue', libraries=['lib/nipplejs/nipplejs.js']):
class Joystick(Element, component='joystick.vue', dependencies=['lib/nipplejs/nipplejs.js']):

def __init__(self, *,
on_start: Optional[Callable[..., Any]] = None,
Expand Down
3 changes: 2 additions & 1 deletion nicegui/elements/joystick.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

<script>
export default {
mounted() {
async mounted() {
await import("nipplejs");
const joystick = nipplejs.create({
zone: this.$el.children[0],
position: { left: "50%", top: "50%" },
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/json_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ..events import GenericEventArguments, JsonEditorChangeEventArguments, JsonEditorSelectEventArguments, handle_event


class JsonEditor(Element, component='json_editor.js', exposed_libraries=['lib/vanilla-jsoneditor/index.js']):
class JsonEditor(Element, component='json_editor.js', dependencies=['lib/vanilla-jsoneditor/index.js']):

def __init__(self,
properties: Dict, *,
Expand Down
6 changes: 4 additions & 2 deletions nicegui/elements/mermaid.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

class Mermaid(ContentElement,
component='mermaid.js',
exposed_libraries=['lib/mermaid/mermaid.esm.min.mjs'],
extra_libraries=['lib/mermaid/*.js']):
dependencies=[
'lib/mermaid/*.js',
'lib/mermaid/mermaid.esm.min.mjs',
]):
CONTENT_PROP = 'content'

def __init__(self, content: str, config: Optional[Dict] = None) -> None:
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/plotly.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
pass


class Plotly(Element, component='plotly.vue', libraries=['lib/plotly/plotly.min.js']):
class Plotly(Element, component='plotly.vue', dependencies=['lib/plotly/plotly.min.js']):

def __init__(self, figure: Union[Dict, go.Figure]) -> None:
"""Plotly Element
Expand Down
2 changes: 1 addition & 1 deletion nicegui/elements/plotly.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<script>
export default {
async mounted() {
await this.$nextTick();
await import("plotly");
this.update();
},
updated() {
Expand Down
3 changes: 2 additions & 1 deletion nicegui/elements/scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import * as THREE from "three";
import { CSS2DRenderer, CSS2DObject } from "CSS2DRenderer";
import { CSS3DRenderer, CSS3DObject } from "CSS3DRenderer";
import { DragControls } from "DragControls";
import { GLTFLoader } from "GLTFLoader";
import { OrbitControls } from "OrbitControls";
import { STLLoader } from "STLLoader";
import { GLTFLoader } from "GLTFLoader";
import "tween";

function texture_geometry(coords) {
const geometry = new THREE.BufferGeometry();
Expand Down
8 changes: 4 additions & 4 deletions nicegui/elements/scene.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,16 @@ class SceneObject:

class Scene(Element,
component='scene.js',
libraries=['lib/tween/tween.umd.js'],
exposed_libraries=[
dependencies=[
'lib/three/three.module.js',
'lib/three/modules/BufferGeometryUtils.js',
'lib/three/modules/CSS2DRenderer.js',
'lib/three/modules/CSS3DRenderer.js',
'lib/three/modules/DragControls.js',
'lib/three/modules/GLTFLoader.js',
'lib/three/modules/OrbitControls.js',
'lib/three/modules/STLLoader.js',
'lib/three/modules/GLTFLoader.js',
'lib/three/modules/BufferGeometryUtils.js',
'lib/tween/tween.umd.js',
]):
# pylint: disable=import-outside-toplevel
from .scene_objects import Box as box
Expand Down
Loading
Loading