Clipboard
A control to allow you to copy content to the clipboard.
Usage
Example
Clipboard(success: "Copied!", error: "Copy failed!", class: "relative", options: {placement: "top"}) do ClipboardSource(class: "hidden") { span { "Born rich!!!" } } ClipboardTrigger do Link(href: "#", class: "gap-1") do Text(size: :small, class: "text-primary") { "Copy the secret of success!!!" } end end end
Copied!
Copy failed!
Installation
Using RubyUI CLI
Run the install command
rails g ruby_ui:component Clipboard
Copied!
Copy failed!
Manual installation
1
Add RubyUI::Clipboard
to app/components/ruby_ui/clipboard.rb
# frozen_string_literal: true module RubyUI class Clipboard < Base def initialize(options: {}, success: "Copied!", error: "Copy Failed!", **attrs) @options = options @success = success @error = error super(**attrs) end def view_template(&block) div(**attrs) do div(&block) success_popover error_popover end end private def success_popover ClipboardPopover(type: :success) { @success } end def error_popover ClipboardPopover(type: :error) { @error } end def default_attrs { data: { controller: "ruby-ui--clipboard", action: "click@window->ruby-ui--clipboard#onClickOutside", ruby_ui__clipboard_success_value: @success, ruby_ui__clipboard_error_value: @error, ruby_ui__clipboard_options_value: @options.to_json } } end end end
Copied!
Copy failed!
2
Add RubyUI::ClipboardPopover
to app/components/ruby_ui/clipboard/clipboard_popover.rb
# frozen_string_literal: true module RubyUI class ClipboardPopover < Base def initialize(type:, **attrs) @type = type super(**attrs) end def view_template(&block) div( class: "hidden", style: "width: max-content; position: absolute; top: 0; left: 0;", data: {ruby_ui__clipboard_target: clipboard_target} ) do div(**attrs, &block) end end private def clipboard_target case @type when :success "successPopover" when :error "errorPopover" end end def default_attrs { data: { state: :open }, class: "z-50 rounded-md text-sm border bg-background px-2 py-0.5 text-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2" } end end end
Copied!
Copy failed!
3
Add RubyUI::ClipboardSource
to app/components/ruby_ui/clipboard/clipboard_source.rb
# frozen_string_literal: true module RubyUI class ClipboardSource < Base def view_template(&) div(**attrs, &) end private def default_attrs { data: { ruby_ui__clipboard_target: "source" } } end end end
Copied!
Copy failed!
4
Add RubyUI::ClipboardTrigger
to app/components/ruby_ui/clipboard/clipboard_trigger.rb
# frozen_string_literal: true module RubyUI class ClipboardTrigger < Base def view_template(&) div(**attrs, &) end private def default_attrs { data: { ruby_ui__clipboard_target: "trigger", action: "click->ruby-ui--clipboard#copy" } } end end end
Copied!
Copy failed!
5
Add clipboard_controller.js
to app/javascript/controllers/ruby_ui/clipboard_controller.js
import { Controller } from "@hotwired/stimulus" import { computePosition, flip, shift } from "@floating-ui/dom"; // Connects to data-controller="accordion" export default class extends Controller { static targets = ['trigger', 'source', 'successPopover', 'errorPopover'] static values = { options: { type: Object, default: {}, }, } copy() { let sourceElement = this.sourceTarget.children[0]; if (!sourceElement) { this.showErrorPopover(); return; } let textToCopy = sourceElement.tagName === 'INPUT' ? sourceElement.value : sourceElement.innerText; navigator.clipboard.writeText(textToCopy).then(() => { this.#showSuccessPopover(); }).catch(() => { this.#showErrorPopover(); }) } onClickOutside() { if (!this.successPopoverTarget.classList.contains("hidden")) this.successPopoverTarget.classList.add("hidden"); if (!this.errorPopoverTarget.classList.contains("hidden")) this.errorPopoverTarget.classList.add("hidden"); } #computeTooltip(popoverElement) { computePosition(this.triggerTarget, popoverElement, { placement: this.optionsValue.placement || "top", middleware: [flip(), shift()], }).then(({ x, y }) => { Object.assign(popoverElement.style, { left: `${x}px`, top: `${y}px`, }); }); } #showSuccessPopover() { this.#computeTooltip(this.successPopoverTarget); this.successPopoverTarget.classList.remove("hidden"); } #showErrorPopover() { this.#computeTooltip(this.errorPopoverTarget); this.errorPopoverTarget.classList.remove("hidden"); } }
Copied!
Copy failed!
6
Update the Stimulus controllers manifest file
Importmap!
You don't need to run this command if you are using Importmap
rake stimulus:manifest:update
Copied!
Copy failed!
7
Install @floating-ui/dom
Javascript dependency
// with yarn yarn add @floating-ui/dom // with npm npm install @floating-ui/dom // with importmaps bin/importmap pin @floating-ui/dom
Copied!
Copy failed!
Components
Component | Built using | Source |
---|---|---|
Clipboard | Phlex | |
ClipboardPopover | Phlex | |
ClipboardSource | Phlex | |
ClipboardTrigger | Phlex | |
ClipboardController | Stimulus JS |