Skip to content

Commit

Permalink
clean up
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcSkovMadsen committed Nov 24, 2024
1 parent 645859a commit a4a2dde
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 22 deletions.
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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)"
Expand Down
2 changes: 1 addition & 1 deletion src/panel_copy_paste/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
4 changes: 1 addition & 3 deletions src/panel_copy_paste/_copy_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
})
Expand Down
50 changes: 45 additions & 5 deletions src/panel_copy_paste/_paste_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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
12 changes: 11 additions & 1 deletion tests/test_paste_input_button.py → tests/test_paste_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down

0 comments on commit a4a2dde

Please sign in to comment.