From 5c66f7e969550d36484608eab88cc2241605da2e Mon Sep 17 00:00:00 2001 From: David Vegh Date: Tue, 19 Dec 2023 10:29:15 +0100 Subject: [PATCH 01/11] Added: `chart.plugin` method --- src/ipyvizzu/chart.py | 32 ++++++++-- src/ipyvizzu/method.py | 93 +++++++++++++++++++++--------- src/ipyvizzu/template.py | 12 ++++ src/ipyvizzu/templates/ipyvizzu.js | 10 ++++ 4 files changed, 117 insertions(+), 30 deletions(-) diff --git a/src/ipyvizzu/chart.py b/src/ipyvizzu/chart.py index 1033b0247..a96754fcb 100644 --- a/src/ipyvizzu/chart.py +++ b/src/ipyvizzu/chart.py @@ -9,8 +9,8 @@ from ipyvizzu.animation import AbstractAnimation, Snapshot, AnimationMerger from ipyvizzu.animationcontrol import AnimationControl -from ipyvizzu.method import Animate, Feature, Store, EventOn, EventOff, Log -from ipyvizzu.template import ChartProperty, DisplayTarget, DisplayTemplate +from ipyvizzu.method import Animate, Feature, Plugin, Store, EventOn, EventOff, Log +from ipyvizzu.template import ChartProperty, DisplayTarget, DisplayTemplate, VIZZU from ipyvizzu.event import EventHandler from ipyvizzu.__version__ import __version__ @@ -20,8 +20,8 @@ class Chart: # pylint: disable=too-many-instance-attributes - VIZZU: str = "https://cdn.jsdelivr.net/npm/vizzu@0.9/dist/vizzu.min.js" - """A variable for storing the default url of vizzu package.""" + VIZZU: str = VIZZU + """A variable for storing the default url of the `vizzu` package.""" def __init__( self, @@ -225,6 +225,30 @@ def feature(self, name: str, enabled: bool) -> None: ) ) + def plugin( + self, + plugin: str, + options: Optional[dict] = None, + name: str = "default", + enabled: bool = True, + ) -> None: + """ + A method for register/unregister plugins of the chart. + + Args: + plugin: The package name or the url of the plugin. + options: The plugin constructor options. + name: The name of the plugin (default `default`). + enabled: The state of the plugin (default `True`). + """ + + self._display( + DisplayTemplate.PLUGIN.format( + chart_id=self._chart_id, + **Plugin(plugin, options, name, enabled).dump(), + ) + ) + def store(self) -> Snapshot: """ A method for saving and storing the actual state of the chart. diff --git a/src/ipyvizzu/method.py b/src/ipyvizzu/method.py index 8c778ed33..b21fc9215 100644 --- a/src/ipyvizzu/method.py +++ b/src/ipyvizzu/method.py @@ -2,14 +2,19 @@ import json from typing import Optional +from urllib.parse import urlparse from ipyvizzu.animation import AbstractAnimation, PlainAnimation from ipyvizzu.event import EventHandler -from ipyvizzu.template import ChartProperty +from ipyvizzu.json import RawJavaScriptEncoder +from ipyvizzu.template import ChartProperty, VIZZU_VERSION class Method: - """A class for storing and dumping any kind of data.""" + """ + A class for dumping chart independent parameters to + [DisplayTemplate.STORE][ipyvizzu.template.DisplayTemplate] template. + """ # pylint: disable=too-few-public-methods @@ -28,8 +33,7 @@ def dump(self) -> dict: class Animate(Method): """ - A class for dumping chart independent parameters to - [DisplayTemplate.ANIMATE][ipyvizzu.template.DisplayTemplate] template. + It stores and dumps `chart_target` and `chart_anim_opts` parameters. """ # pylint: disable=too-few-public-methods @@ -42,8 +46,6 @@ def __init__( """ Animate constructor. - It stores and dumps `chart_target` and `chart_anim_opts` parameters. - Args: chart_target: AbstractAnimation inherited object such as @@ -64,8 +66,7 @@ def __init__( class Feature(Method): """ - A class for dumping chart independent parameters to - [DisplayTemplate.FEATURE][ipyvizzu.template.DisplayTemplate] template. + It stores and dumps `name` and `enabled` parameters. """ # pylint: disable=too-few-public-methods @@ -74,8 +75,6 @@ def __init__(self, name: str, enabled: bool): """ Feature constructor. - It stores and dumps `name` and `enabled` parameters. - Args: name: The name of a chart feature. enabled: The new state of a chart feature. @@ -84,10 +83,63 @@ def __init__(self, name: str, enabled: bool): self._data = {"name": name, "enabled": json.dumps(enabled)} +class Plugin(Method): + """ + It stores and dumps `plugin`, `options` and `name` parameters. + """ + + def __init__(self, plugin: str, options: Optional[dict], name: str, enabled: bool): + """ + Plugin constructor. + + Args: + plugin: The package name or the url of the plugin. + options: The plugin constructor options. + name: The name of the plugin (default `default`). + enabled: The state of the plugin (default `True`). + """ + + self._data = { + "plugin": Plugin.resolve_url(plugin), + "options": json.dumps(options, cls=RawJavaScriptEncoder), + "name": name, + "enabled": json.dumps(enabled), + } + + @staticmethod + def resolve_url(plugin: str) -> str: + """ + A static method for resolving the url of the plugin. + + Args: + plugin: The package name or the url of the plugin. + + Returns: + The url of the plugin. + """ + + if Plugin._is_url(plugin): + return plugin + return Plugin._get_url(plugin) + + @staticmethod + def _is_url(plugin: str) -> bool: + try: + urlparse(plugin) + return True + except ValueError: + return False + + @staticmethod + def _get_url(plugin: str) -> str: + jsdelivr = "https://cdn.jsdelivr.net/npm/@vizzu" + tag = f"vizzu-{VIZZU_VERSION}" + return f"{jsdelivr}/{plugin}@{tag}/dist/mjs/index.min.js" + + class Store(Method): """ - A class for dumping chart independent parameters to - [DisplayTemplate.STORE][ipyvizzu.template.DisplayTemplate] template. + It stores and dumps `snapshot_id` parameter. """ # pylint: disable=too-few-public-methods @@ -96,8 +148,6 @@ def __init__(self, snapshot_id: str): """ Store constructor. - It stores and dumps `snapshot_id` parameter. - Args: snapshot_id: The id of snapshot object. """ @@ -107,8 +157,7 @@ def __init__(self, snapshot_id: str): class EventOn(Method): """ - A class for dumping chart independent parameters to - [DisplayTemplate.SET_EVENT][ipyvizzu.template.DisplayTemplate] template. + It stores and dumps the `id`, the `event` and the `handler` of the event handler object. """ # pylint: disable=too-few-public-methods @@ -117,8 +166,6 @@ def __init__(self, event_handler: EventHandler): """ EventOn constructor. - It stores and dumps the `id`, the `event` and the `handler` of the event handler object. - Args: event_handler: An event handler object. """ @@ -132,8 +179,7 @@ def __init__(self, event_handler: EventHandler): class EventOff(Method): """ - A class for dumping chart independent parameters to - [DisplayTemplate.CLEAR_EVENT][ipyvizzu.template.DisplayTemplate] template. + It stores and dumps the `id` and the `event` of the event handler object. """ # pylint: disable=too-few-public-methods @@ -142,8 +188,6 @@ def __init__(self, event_handler: EventHandler): """ EventOff constructor. - It stores and dumps the `id` and the `event` of the event handler object. - Args: event_handler: An event handler object. """ @@ -153,8 +197,7 @@ def __init__(self, event_handler: EventHandler): class Log(Method): """ - A class for dumping chart independent parameters to - [DisplayTemplate.LOG][ipyvizzu.template.DisplayTemplate] template. + It stores and dumps the value of the chart property object. """ # pylint: disable=too-few-public-methods @@ -163,8 +206,6 @@ def __init__(self, chart_property: ChartProperty): """ Log constructor. - It stores and dumps the value of the chart property object. - Args: chart_property: A chart property such as diff --git a/src/ipyvizzu/template.py b/src/ipyvizzu/template.py index 6279a2588..1b2d74ed8 100644 --- a/src/ipyvizzu/template.py +++ b/src/ipyvizzu/template.py @@ -2,6 +2,12 @@ from enum import Enum +VIZZU_VERSION: str = "0.9" +"""A variable for storing the default version of the `vizzu` package.""" + +VIZZU: str = f"https://cdn.jsdelivr.net/npm/vizzu@{VIZZU_VERSION}/dist/vizzu.min.js" +"""A variable for storing the default url of the `vizzu` package.""" + class ChartProperty(Enum): """An enum class for storing chart properties.""" @@ -60,6 +66,12 @@ class DisplayTemplate: ) """Call feature JavaScript method.""" + PLUGIN: str = ( + "window.ipyvizzu.plugin(element, " + + "'{chart_id}', '{plugin}', {options}, '{name}', {enabled});" + ) + """Call plugin JavaScript method.""" + STORE: str = "window.ipyvizzu.store(element, '{chart_id}', '{id}');" """Call store JavaScript method.""" diff --git a/src/ipyvizzu/templates/ipyvizzu.js b/src/ipyvizzu/templates/ipyvizzu.js index 1e86ce5a2..367c1b837 100644 --- a/src/ipyvizzu/templates/ipyvizzu.js +++ b/src/ipyvizzu/templates/ipyvizzu.js @@ -55,6 +55,16 @@ if (window.IpyVizzu?.version !== '__version__') { this._moveHere(chartId, element) } + plugin(element, chartId, plugin, options, name, enabled) { + this.charts[chartId] = this.charts[chartId].then((chart) => { + return import(plugin).then((pluginModule) => { + const Plugin = pluginModule[name] + chart.feature(new Plugin(options), enabled) + return chart + }) + }) + } + animate( element, chartId, From 8dfc07070e813c0c86516737807facec059dd164 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Tue, 19 Dec 2023 10:36:15 +0100 Subject: [PATCH 02/11] added: error handling for plugin import --- src/ipyvizzu/templates/ipyvizzu.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ipyvizzu/templates/ipyvizzu.js b/src/ipyvizzu/templates/ipyvizzu.js index 367c1b837..68e0aa1bc 100644 --- a/src/ipyvizzu/templates/ipyvizzu.js +++ b/src/ipyvizzu/templates/ipyvizzu.js @@ -57,11 +57,16 @@ if (window.IpyVizzu?.version !== '__version__') { plugin(element, chartId, plugin, options, name, enabled) { this.charts[chartId] = this.charts[chartId].then((chart) => { - return import(plugin).then((pluginModule) => { - const Plugin = pluginModule[name] - chart.feature(new Plugin(options), enabled) - return chart - }) + return import(plugin) + .then((pluginModule) => { + const Plugin = pluginModule[name] + chart.feature(new Plugin(options), enabled) + return chart + }) + .catch((error) => { + console.error('Error importing plugin:', plugin, error) + return chart + }) }) } From ac4ad12eece4fefe22215d067b81bfb7c41ed71b Mon Sep 17 00:00:00 2001 From: David Vegh Date: Tue, 19 Dec 2023 10:50:55 +0100 Subject: [PATCH 03/11] fixed: plugin options dumping --- src/ipyvizzu/method.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ipyvizzu/method.py b/src/ipyvizzu/method.py index b21fc9215..c5a25ea07 100644 --- a/src/ipyvizzu/method.py +++ b/src/ipyvizzu/method.py @@ -101,7 +101,9 @@ def __init__(self, plugin: str, options: Optional[dict], name: str, enabled: boo self._data = { "plugin": Plugin.resolve_url(plugin), - "options": json.dumps(options, cls=RawJavaScriptEncoder), + "options": {} + if options is None + else json.dumps(options, cls=RawJavaScriptEncoder), "name": name, "enabled": json.dumps(enabled), } From 81e63ae2d3fb26704f23f48db2b58a3d5f3591e4 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Tue, 19 Dec 2023 10:57:47 +0100 Subject: [PATCH 04/11] fixed: plugin disable --- src/ipyvizzu/templates/ipyvizzu.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ipyvizzu/templates/ipyvizzu.js b/src/ipyvizzu/templates/ipyvizzu.js index 68e0aa1bc..45f3ab8ac 100644 --- a/src/ipyvizzu/templates/ipyvizzu.js +++ b/src/ipyvizzu/templates/ipyvizzu.js @@ -60,7 +60,11 @@ if (window.IpyVizzu?.version !== '__version__') { return import(plugin) .then((pluginModule) => { const Plugin = pluginModule[name] - chart.feature(new Plugin(options), enabled) + if (enabled) { + chart.feature(new Plugin(options), enabled) + } else { + chart.feature(new Plugin(options).meta.name, false) + } return chart }) .catch((error) => { From 43dfbe84330fe589557898e680b02da8b6e3d932 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Tue, 19 Dec 2023 10:58:38 +0100 Subject: [PATCH 05/11] fixed: plugin register --- src/ipyvizzu/templates/ipyvizzu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipyvizzu/templates/ipyvizzu.js b/src/ipyvizzu/templates/ipyvizzu.js index 45f3ab8ac..bfc8d1184 100644 --- a/src/ipyvizzu/templates/ipyvizzu.js +++ b/src/ipyvizzu/templates/ipyvizzu.js @@ -61,7 +61,7 @@ if (window.IpyVizzu?.version !== '__version__') { .then((pluginModule) => { const Plugin = pluginModule[name] if (enabled) { - chart.feature(new Plugin(options), enabled) + chart.feature(new Plugin(options), true) } else { chart.feature(new Plugin(options).meta.name, false) } From 3fac4875371574793a735370dedaf8d74ecc5e88 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Tue, 19 Dec 2023 11:06:40 +0100 Subject: [PATCH 06/11] refactor plugin method --- src/ipyvizzu/templates/ipyvizzu.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ipyvizzu/templates/ipyvizzu.js b/src/ipyvizzu/templates/ipyvizzu.js index bfc8d1184..0396a6d27 100644 --- a/src/ipyvizzu/templates/ipyvizzu.js +++ b/src/ipyvizzu/templates/ipyvizzu.js @@ -59,11 +59,11 @@ if (window.IpyVizzu?.version !== '__version__') { this.charts[chartId] = this.charts[chartId].then((chart) => { return import(plugin) .then((pluginModule) => { - const Plugin = pluginModule[name] + const plugin = new pluginModule[name](options) if (enabled) { - chart.feature(new Plugin(options), true) + chart.feature(plugin, true) } else { - chart.feature(new Plugin(options).meta.name, false) + chart.feature(plugin.meta.name, false) } return chart }) From 3693d7c97cebafd7e7985609998c4c0409370468 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Wed, 20 Dec 2023 10:23:25 +0100 Subject: [PATCH 07/11] fixed url detection --- src/ipyvizzu/method.py | 10 +++----- tests/test_chart.py | 56 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/ipyvizzu/method.py b/src/ipyvizzu/method.py index c5a25ea07..79f3c3099 100644 --- a/src/ipyvizzu/method.py +++ b/src/ipyvizzu/method.py @@ -1,8 +1,8 @@ """A module for working with template methods.""" import json +import re from typing import Optional -from urllib.parse import urlparse from ipyvizzu.animation import AbstractAnimation, PlainAnimation from ipyvizzu.event import EventHandler @@ -88,6 +88,8 @@ class Plugin(Method): It stores and dumps `plugin`, `options` and `name` parameters. """ + URL = re.compile(r"https?://\S+") + def __init__(self, plugin: str, options: Optional[dict], name: str, enabled: bool): """ Plugin constructor. @@ -126,11 +128,7 @@ def resolve_url(plugin: str) -> str: @staticmethod def _is_url(plugin: str) -> bool: - try: - urlparse(plugin) - return True - except ValueError: - return False + return bool(re.match(Plugin.URL, plugin)) @staticmethod def _get_url(plugin: str) -> str: diff --git a/tests/test_chart.py b/tests/test_chart.py index f604fbe56..94b44d95c 100644 --- a/tests/test_chart.py +++ b/tests/test_chart.py @@ -158,7 +158,7 @@ class IPy: ) -class TestChartMethods(TestChart): +class TestChartAnimateMethod(TestChart): def test_animate_chart_target_has_to_be_passed(self) -> None: with self.assertRaises(ValueError): self.chart.animate() @@ -312,6 +312,54 @@ def test_animate_with_not_default_scroll_into_view(self) -> None: + "undefined);", ) + +class TestChartPluginMethod(TestChart): + URL = "https://cdn.jsdelivr.net/npm/@vizzu/marker-dropshadow@vizzu-0.9/dist/mjs/index.min.js" + + def test_plugin_with_package_name(self) -> None: + with unittest.mock.patch(self.mock) as output: + self.chart.plugin("marker-dropshadow") + self.assertEqual( + self.normalizer.normalize_output(output), + f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, 'default', true);", + ) + + def test_plugin_with_package_url(self) -> None: + with unittest.mock.patch(self.mock) as output: + self.chart.plugin(self.URL) + self.assertEqual( + self.normalizer.normalize_output(output), + f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, 'default', true);", + ) + + def test_plugin_with_options(self) -> None: + with unittest.mock.patch(self.mock) as output: + self.chart.plugin("marker-dropshadow", options={"debug": True}) + self.assertEqual( + self.normalizer.normalize_output(output), + "window.ipyvizzu.plugin(element, id, " + + f"'{self.URL}', {{\"debug\": true}}, 'default', true);", + ) + + def test_plugin_with_name(self) -> None: + with unittest.mock.patch(self.mock) as output: + name = "MarkerDropshadow" + self.chart.plugin("marker-dropshadow", name=name) + self.assertEqual( + self.normalizer.normalize_output(output), + f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, '{name}', true);", + ) + + def test_plugin_with_enabled(self) -> None: + with unittest.mock.patch(self.mock) as output: + self.chart.plugin("marker-dropshadow", enabled=False) + self.assertEqual( + self.normalizer.normalize_output(output), + f"window.ipyvizzu.plugin(element, id, '{self.URL}', {{}}, 'default', false);", + ) + + +class TestChartFeatureMethod(TestChart): def test_feature(self) -> None: with unittest.mock.patch(self.mock) as output: self.chart.feature("tooltip", True) @@ -320,6 +368,8 @@ def test_feature(self) -> None: "window.ipyvizzu.feature(element, id, 'tooltip', true);", ) + +class TestChartStoreMethod(TestChart): def test_store(self) -> None: with unittest.mock.patch(self.mock) as output: self.chart.store() @@ -329,7 +379,7 @@ def test_store(self) -> None: ) -class TestChartEvents(TestChart): +class TestChartEventMethods(TestChart): def test_on(self) -> None: with unittest.mock.patch(self.mock) as output: handler_method = """event.renderingContext.fillStyle = @@ -355,7 +405,7 @@ def test_off(self) -> None: ) -class TestChartLogs(TestChart): +class TestChartLogMethod(TestChart): def test_log_config(self) -> None: with unittest.mock.patch(self.mock) as output: self.chart.log(ChartProperty.CONFIG) From 71e02b45fd37580351809d1499d0202d139cc9c6 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Wed, 20 Dec 2023 11:33:02 +0100 Subject: [PATCH 08/11] simplify url recognition --- src/ipyvizzu/method.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ipyvizzu/method.py b/src/ipyvizzu/method.py index 79f3c3099..674fdcea7 100644 --- a/src/ipyvizzu/method.py +++ b/src/ipyvizzu/method.py @@ -1,7 +1,6 @@ """A module for working with template methods.""" import json -import re from typing import Optional from ipyvizzu.animation import AbstractAnimation, PlainAnimation @@ -88,8 +87,6 @@ class Plugin(Method): It stores and dumps `plugin`, `options` and `name` parameters. """ - URL = re.compile(r"https?://\S+") - def __init__(self, plugin: str, options: Optional[dict], name: str, enabled: bool): """ Plugin constructor. @@ -128,7 +125,7 @@ def resolve_url(plugin: str) -> str: @staticmethod def _is_url(plugin: str) -> bool: - return bool(re.match(Plugin.URL, plugin)) + return "/" in plugin @staticmethod def _get_url(plugin: str) -> str: From acfed03c6b0d42ee660e272c043fca263743baf7 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 21 Dec 2023 09:08:41 +0100 Subject: [PATCH 09/11] added alias to VIZZU url import --- src/ipyvizzu/chart.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ipyvizzu/chart.py b/src/ipyvizzu/chart.py index a96754fcb..860576802 100644 --- a/src/ipyvizzu/chart.py +++ b/src/ipyvizzu/chart.py @@ -10,7 +10,12 @@ from ipyvizzu.animation import AbstractAnimation, Snapshot, AnimationMerger from ipyvizzu.animationcontrol import AnimationControl from ipyvizzu.method import Animate, Feature, Plugin, Store, EventOn, EventOff, Log -from ipyvizzu.template import ChartProperty, DisplayTarget, DisplayTemplate, VIZZU +from ipyvizzu.template import ( + ChartProperty, + DisplayTarget, + DisplayTemplate, + VIZZU as VIZZU_URL, +) from ipyvizzu.event import EventHandler from ipyvizzu.__version__ import __version__ @@ -20,7 +25,7 @@ class Chart: # pylint: disable=too-many-instance-attributes - VIZZU: str = VIZZU + VIZZU: str = VIZZU_URL """A variable for storing the default url of the `vizzu` package.""" def __init__( From 822690ea925312bc86ed948b6d0b336ce631fc9a Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 21 Dec 2023 09:29:08 +0100 Subject: [PATCH 10/11] fixed docs build - cause vizzu 0.9.3 --- tools/docs/examples/gen_examples.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/docs/examples/gen_examples.py b/tools/docs/examples/gen_examples.py index b9e816eae..be6c58da5 100644 --- a/tools/docs/examples/gen_examples.py +++ b/tools/docs/examples/gen_examples.py @@ -15,10 +15,8 @@ MKDOCS_PATH = TOOLS_PATH / "docs" GEN_PATH = MKDOCS_PATH / "examples" VIZZU_LIB_PATH = REPO_PATH / "vizzu-lib" -WEB_CONTENT_PATH = ( - VIZZU_LIB_PATH / "test" / "integration" / "test_cases" / "web_content" -) -TEST_DATA_PATH = VIZZU_LIB_PATH / "test" / "integration" / "test_data" +WEB_CONTENT_PATH = VIZZU_LIB_PATH / "test" / "e2e" / "test_cases" / "web_content" +TEST_DATA_PATH = VIZZU_LIB_PATH / "test" / "e2e" / "test_data" STATIC_EXAMPLES_PATH = WEB_CONTENT_PATH / "static" OPERATION_EXAMPLES_PATH = WEB_CONTENT_PATH / "analytical_operations" PRESET_EXAMPLES_PATH = WEB_CONTENT_PATH / "presets" From 5bf28e072c71f4bb92acb74e279439978108b6d9 Mon Sep 17 00:00:00 2001 From: David Vegh Date: Thu, 21 Dec 2023 10:03:33 +0100 Subject: [PATCH 11/11] import plugin once, store imported module --- src/ipyvizzu/templates/ipyvizzu.js | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/ipyvizzu/templates/ipyvizzu.js b/src/ipyvizzu/templates/ipyvizzu.js index 0396a6d27..27cdbf382 100644 --- a/src/ipyvizzu/templates/ipyvizzu.js +++ b/src/ipyvizzu/templates/ipyvizzu.js @@ -37,6 +37,7 @@ if (window.IpyVizzu?.version !== '__version__') { this.events = {} this.loaded = {} this.libs = {} + this.plugins = {} } static clearInhibitScroll(element) { @@ -57,20 +58,25 @@ if (window.IpyVizzu?.version !== '__version__') { plugin(element, chartId, plugin, options, name, enabled) { this.charts[chartId] = this.charts[chartId].then((chart) => { - return import(plugin) - .then((pluginModule) => { - const plugin = new pluginModule[name](options) + if (!this.plugins[plugin]) { + this.plugins[plugin] = import(plugin).catch((error) => { + console.error('Error importing plugin:', plugin, error) + return null + }) + } + + return this.plugins[plugin].then((pluginModule) => { + if (pluginModule) { + const pluginInstance = new pluginModule[name](options) if (enabled) { - chart.feature(plugin, true) + chart.feature(pluginInstance, true) } else { - chart.feature(plugin.meta.name, false) + chart.feature(pluginInstance.meta.name, false) } - return chart - }) - .catch((error) => { - console.error('Error importing plugin:', plugin, error) - return chart - }) + } + + return chart + }) }) }