clipboard.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. """Global on_paste handling for Reflex app."""
  2. from __future__ import annotations
  3. from typing import Dict, List, Tuple, Union
  4. from reflex.components.base.fragment import Fragment
  5. from reflex.components.tags.tag import Tag
  6. from reflex.event import EventChain, EventHandler, passthrough_event_spec
  7. from reflex.utils.format import format_prop, wrap
  8. from reflex.utils.imports import ImportVar
  9. from reflex.vars import get_unique_variable_name
  10. from reflex.vars.base import Var
  11. class Clipboard(Fragment):
  12. """Clipboard component."""
  13. # The element ids to attach the event listener to. Defaults to all child components or the document.
  14. targets: Var[List[str]]
  15. # Called when the user pastes data into the document. Data is a list of tuples of (mime_type, data). Binary types will be base64 encoded as a data uri.
  16. on_paste: EventHandler[passthrough_event_spec(List[Tuple[str, str]])]
  17. # Save the original event actions for the on_paste event.
  18. on_paste_event_actions: Var[Dict[str, Union[bool, int]]]
  19. @classmethod
  20. def create(cls, *children, **props):
  21. """Create a Clipboard component.
  22. Args:
  23. *children: The children of the component.
  24. **props: The properties of the component.
  25. Returns:
  26. The Clipboard Component.
  27. """
  28. if "targets" not in props:
  29. # Add all children as targets if not specified.
  30. targets = props.setdefault("targets", [])
  31. for c in children:
  32. if c.id is None:
  33. c.id = f"clipboard_{get_unique_variable_name()}"
  34. targets.append(c.id)
  35. if "on_paste" in props:
  36. # Capture the event actions for the on_paste handler if not specified.
  37. props.setdefault("on_paste_event_actions", props["on_paste"].event_actions)
  38. return super().create(*children, **props)
  39. def _exclude_props(self) -> list[str]:
  40. return [*super()._exclude_props(), "on_paste", "on_paste_event_actions"]
  41. def _render(self) -> Tag:
  42. tag = super()._render()
  43. tag.remove_props("targets")
  44. # Ensure a different Fragment component is created whenever targets differ
  45. tag.add_props(key=self.targets)
  46. return tag
  47. def add_imports(self) -> dict[str, ImportVar]:
  48. """Add the imports for the Clipboard component.
  49. Returns:
  50. The import dict for the component.
  51. """
  52. return {
  53. "$/utils/helpers/paste.js": ImportVar(
  54. tag="usePasteHandler", is_default=True
  55. ),
  56. }
  57. def add_hooks(self) -> list[str]:
  58. """Add hook to register paste event listener.
  59. Returns:
  60. The hooks to add to the component.
  61. """
  62. on_paste = self.event_triggers["on_paste"]
  63. if on_paste is None:
  64. return []
  65. if isinstance(on_paste, EventChain):
  66. on_paste = wrap(str(format_prop(on_paste)).strip("{}"), "(")
  67. return [
  68. "usePasteHandler(%s, %s, %s)"
  69. % (
  70. str(self.targets),
  71. str(self.on_paste_event_actions),
  72. on_paste,
  73. )
  74. ]
  75. clipboard = Clipboard.create