clipboard.py 3.1 KB

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