diff --git a/README.md b/README.md index 9bb09dd..e3f24c1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Extends HoloViz Panel with copy-paste functionality. ## Features - `CopyButton`: Enables you to copy Python objects to the clipboard. +- `PasteButton`: Enables you to paste strings from the clipboard. +- `PasteToDataFrameButton`: Enables you to paste strings from the clipboard into dataframe values. ## Pin your version! @@ -28,26 +30,43 @@ pip install panel-copy-paste To use the `CopyButton`: ```python +import panel as pn from panel_copy_paste import CopyButton -CopyButton(value="Hello World").servable() +pn.extension("codeeditor") + +editor = pn.widgets.CodeEditor() +button = CopyButton(value="Hello World") +pn.Column(button, editor).servable() ``` -## Supported Types +To use the `PasteButton`: + +```python +import panel as pn +from panel_copy_paste import PasteButton + +pn.extension("codeeditor") -As `value` you can use any of the types below. +editor = pn.widgets.CodeEditor() +button = PasteButton(target=editor) +pn.Column(button, editor).servable() +``` + +To use the `PasteToDataFrameButton`: -- `None`: Will copy as the empty string. -- `str`: Any String value -- `DataFrame`: Pandas and Polars dataframes will copy as a tab separated csv string. +```python +import panel as pn +from panel_copy_paste import PasteToDataFrameButton -More types can be supported. Please [create a Feature Request](https://github.com/awesome-panel/panel-copy-paste/issues). +pn.extension("tabulator") -In addition the below values will be resolved to above types +table = pn.widgets.Tabulator() +button = PasteToDataFrameButton(target=table) +pn.Column(button, table).servable() +``` -- `Parameterized`: Must have `.value` or `.object` attribute. -- `Parameter`: -- `Callback`: +For more examples check out the [documentation](https://awesome-panel.github.io/panel-copy-paste/). ## Development diff --git a/mkdocs.yml b/mkdocs.yml index 88bf684..9624c0e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -9,7 +9,7 @@ theme: name: material logo: 'assets/logo.svg' features: - - content.copy.code + - content.code.copy palette: # Palette toggle for light mode - media: "(prefers-color-scheme: light)" diff --git a/src/panel_copy_paste/__init__.py b/src/panel_copy_paste/__init__.py index 6c02cc7..9939d1c 100644 --- a/src/panel_copy_paste/__init__.py +++ b/src/panel_copy_paste/__init__.py @@ -1,4 +1,4 @@ -"""Panel Copy-Paste provides features for copy to and pasting from the clipboard.""" +"""`panel-copy-paste` provides features for copying to and pasting from the clipboard.""" import importlib.metadata import warnings diff --git a/src/panel_copy_paste/_copy_button.py b/src/panel_copy_paste/_copy_button.py index 9021c86..c04f4b1 100644 --- a/src/panel_copy_paste/_copy_button.py +++ b/src/panel_copy_paste/_copy_button.py @@ -34,9 +34,7 @@ class CopyButton(pn.custom.JSComponent): el.appendChild(button) model.on("_data", (e)=>{ - navigator.clipboard.writeText(model._data).then(function() { - console.log('Writing to clipboard was successful!'); - }, function(err) { + navigator.clipboard.writeText(model._data).then(function() { }, function(err) { console.error('Could not write to clipboard: ', err); }); }) diff --git a/src/panel_copy_paste/_paste_button.py b/src/panel_copy_paste/_paste_button.py index c1ee022..1d827ff 100644 --- a/src/panel_copy_paste/_paste_button.py +++ b/src/panel_copy_paste/_paste_button.py @@ -36,7 +36,7 @@ class PasteButtonBase(pn.custom.JSComponent): allow_refs=False, ) - transform_func = do_nothing + _transform_func = do_nothing _DEFAULT_BUTTON = pn.widgets.ButtonIcon(description="Paste from clipboard.", icon="clipboard", active_icon="check", toggle_duration=1500) _rename = {"value": None, "target": None} @@ -63,7 +63,7 @@ class PasteButtonBase(pn.custom.JSComponent): def __init__(self, **params): if "button" not in params: params["button"] = self._get_new_button() - self.transform_func = params.pop("transform_func", self.transform_func) # type: ignore[method-assign] + self._transform_func = params.pop("_transform_func", self._transform_func) # type: ignore[method-assign] super().__init__(**params) @classmethod @@ -72,7 +72,7 @@ def _get_new_button(cls): @param.depends("data", watch=True) def _handle_data(self): - self.value = self.transform_func(self.data) + self.value = self._transform_func(self.data) if self.target: self._set_target_value(self.target, self.value) @@ -90,12 +90,52 @@ def _set_target_value(target, value): class PasteButton(PasteButtonBase): + """ + A Custom Panel widget to paste a string value from the clipboard. + + Examples + -------- + >>> import panel as pn + >>> from panel_copy_paste import PasteButton + >>> pn.extension("codeeditor") + >>> editor = pn.widgets.CodeEditor() + >>> button = PasteButton(target=editor) + >>> pn.Column(button, editor).servable() + + """ + value = param.String(default="", doc="""The value from the clip board as a string.""") + button = pn.custom.Child(constant=True, doc="""A custom Button or ButtonIcon to use.""") + target = param.Parameter( + doc="""If a widget its value is set to value when it changes. If a Pane its object will be + set to the value. If a callable the callable will be executed on the value.""", + allow_refs=False, + ) - transform_func = do_nothing + _transform_func = do_nothing class PasteToDataFrameButton(PasteButtonBase): + """ + A Custom Panel widget to paste a tab separated string from the clipboard into a Pandas DataFrame. + + Examples + -------- + >>> import panel as pn + >>> from panel_copy_paste import PasteToDataFrameButton + >>> pn.extension("tabulator") + >>> table = pn.widgets.Tabulator() + >>> button = PasteToDataFrameButton(target=table) + >>> pn.Column(button, table).servable() + + """ + value = param.DataFrame(doc="""The value from the clip board as a Pandas DataFrame.""") + button = pn.custom.Child(constant=True, doc="""A custom Button or ButtonIcon to use.""") + target = param.Parameter( + doc="""If a widget its value is set to value when it changes. If a Pane its object will be + set to the value. If a callable the callable will be executed on the value.""", + allow_refs=False, + ) - transform_func = read_csv + _transform_func = read_csv diff --git a/tests/test_paste_input_button.py b/tests/test_paste_button.py similarity index 62% rename from tests/test_paste_input_button.py rename to tests/test_paste_button.py index 6829389..c378cfa 100644 --- a/tests/test_paste_input_button.py +++ b/tests/test_paste_button.py @@ -3,7 +3,17 @@ import pandas as pd import panel as pn -from panel_copy_paste import PasteToDataFrameButton +from panel_copy_paste import PasteButton, PasteToDataFrameButton + + +def test_paste_string_input(): + """We can paste string data into a Code Editor.""" + editor = pn.widgets.CodeEditor() + button = PasteButton(target=editor) + + button.data = "Hello World" + assert button.value == "Hello World" + assert editor.value == "Hello World" def test_paste_csv_input():