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, 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 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[lambda data: [data]]
  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