1 import * as DOM from "../services/dom";
2 import Clipboard from "clipboard/dist/clipboard.min";
3 import {Component} from "./component";
6 export class Pointer extends Component {
9 this.container = this.$el;
10 this.pageId = this.$opts.pageId;
14 this.isSelection = false;
15 this.pointerModeLink = true;
16 this.pointerSectionId = '';
18 this.setupListeners();
21 new Clipboard(this.container.querySelector('button'));
25 // Select all contents on input click
26 DOM.onChildEvent(this.container, 'input', 'click', (event, input) => {
28 event.stopPropagation();
31 // Prevent closing pointer when clicked or focused
32 DOM.onEvents(this.container, ['click', 'focus'], event => {
33 event.stopPropagation();
36 // Pointer mode toggle
37 DOM.onChildEvent(this.container, 'span.icon', 'click', (event, icon) => {
38 event.stopPropagation();
39 this.pointerModeLink = !this.pointerModeLink;
40 icon.querySelector('[data-icon="include"]').style.display = (!this.pointerModeLink) ? 'inline' : 'none';
41 icon.querySelector('[data-icon="link"]').style.display = (this.pointerModeLink) ? 'inline' : 'none';
42 this.updateForTarget();
45 // Hide pointer when clicking away
46 DOM.onEvents(document.body, ['click', 'focus'], event => {
47 if (!this.showing || this.isSelection) return;
51 // Show pointer when selecting a single block of tagged content
52 const pageContent = document.querySelector('.page-content');
53 DOM.onEvents(pageContent, ['mouseup', 'keyup'], event => {
54 event.stopPropagation();
55 const targetEl = event.target.closest('[id^="bkmrk"]');
57 this.showPointerAtTarget(targetEl, event.pageX);
63 this.container.style.display = null;
68 * Move and display the pointer at the given element, targeting the given screen x-position if possible.
69 * @param {Element} element
70 * @param {Number} xPosition
72 showPointerAtTarget(element, xPosition) {
73 const selection = window.getSelection();
74 if (selection.toString().length === 0) return;
76 // Show pointer and set link
77 this.pointerSectionId = element.id;
78 this.updateForTarget(element);
80 this.container.style.display = 'block';
81 const targetBounds = element.getBoundingClientRect();
82 const pointerBounds = this.container.getBoundingClientRect();
84 const xTarget = Math.min(Math.max(xPosition, targetBounds.left), targetBounds.right);
85 const xOffset = xTarget - (pointerBounds.width / 2);
86 const yOffset = (targetBounds.top - pointerBounds.height) - 16;
88 this.container.style.left = `${xOffset}px`;
89 this.container.style.top = `${yOffset}px`;
92 this.isSelection = true;
95 this.isSelection = false;
98 const scrollListener = () => {
100 window.removeEventListener('scroll', scrollListener, {passive: true});
102 window.addEventListener('scroll', scrollListener, {passive: true});
106 * Update the pointer inputs/content for the given target element.
107 * @param {?Element} element
109 updateForTarget(element) {
110 let inputText = this.pointerModeLink ? window.baseUrl(`/link/${this.pageId}#${this.pointerSectionId}`) : `{{@${this.pageId}#${this.pointerSectionId}}}`;
111 if (this.pointerModeLink && !inputText.startsWith('http')) {
112 inputText = window.location.protocol + "//" + window.location.host + inputText;
115 this.container.querySelector('input').value = inputText;
117 // Update anchor if present
118 const editAnchor = this.container.querySelector('#pointer-edit');
119 if (editAnchor && element) {
120 const editHref = editAnchor.dataset.editHref;
121 const elementId = element.id;
123 // get the first 50 characters.
124 const queryContent = element.textContent && element.textContent.substring(0, 50);
125 editAnchor.href = `${editHref}?content-id=${elementId}&content-text=${encodeURIComponent(queryContent)}`;