1import { Controller } from "@hotwired/stimulus" 2import ClipboardJS from "clipboard" 3import { autoUpdate, autoPlacement, computePosition, offset, arrow } from "@floating-ui/dom" 4 5export default class extends Controller { 6 static values = { 7 hideTooltipAfter: 1500, 8 tooltipPlacement: String, 9 tooltipOffset: 6, 10 errorMessage: { type: String, default: "Failed!" }, 11 successMessage: { type: String, default: "Copied!" } 12 } 13 14 connect() { 15 this.clipboard = new ClipboardJS(this.element) 16 this.clipboard.on("success", (e) => this.handleEvent(this.successMessageValue)) 17 this.clipboard.on("error", (e) => this.handleEvent(this.errorMessageValue)) 18 } 19 20 disconnect() { 21 this.clearTooltip() 22 } 23 24 handleEvent(message) { 25 this.clearTooltip() 26 this.createTooltipElements(message) 27 this.updatePosition() 28 29 this.timeout = setTimeout(() => { 30 this.clearTooltip() 31 }, this.hideTooltipAfterValue) 32 } 33 34 clearTooltip() { 35 this.tooltip?.remove() 36 clearTimeout(this.timeout) 37 } 38 39 createTooltipElements(message) { 40 this.tooltip = document.createElement("div") 41 this.tooltip.dataset.tooltipTarget = "content" 42 this.tooltip.classList.add("tooltip", "open") 43 this.tooltip.textContent = message 44 45 this.arrow = document.createElement("div") 46 this.arrow.classList.add("arrow") 47 48 this.tooltip.appendChild(this.arrow) 49 document.body.appendChild(this.tooltip) 50 } 51 52 updatePosition() { 53 computePosition(this.element, this.tooltip, { 54 middleware: [ 55 autoPlacement({ 56 allowedPlacements: (this.tooltipPlacementValue ? [this.tooltipPlacementValue] : undefined), 57 }), 58 offset(this.tooltipOffsetValue), 59 arrow({element: this.arrow}), 60 ] 61 }).then(({x, y, placement, middlewareData}) => { 62 Object.assign(this.tooltip.style, { 63 left: `${x}px`, 64 top: `${y}px` 65 }); 66 67 const side = placement.split("-")[0]; 68 69 const staticSide = { 70 top: "bottom", 71 right: "left", 72 bottom: "top", 73 left: "right" 74 }[side]; 75 76 if (middlewareData.arrow) { 77 const { x, y } = middlewareData.arrow; 78 Object.assign(this.arrow.style, { 79 left: x != null ? `${x}px` : "", 80 top: y != null ? `${y}px` : "", 81 // Ensure the static side gets unset when 82 // flipping to other placements' axes. 83 right: "", 84 bottom: "", 85 [staticSide]: `${-8 / 2}px`, 86 }); 87 } 88 }) 89 } 90}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.