UNPKG

@elastic/eui

Version:

Elastic UI Component Library

103 lines (95 loc) 4.43 kB
/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License * 2.0 and the Server Side Public License, v 1; you may not use this file except * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ import React, { useEffect } from 'react'; /** * Clipboard text cleaning logic */ // Special visually hidden unicode characters that we use to manually clean content // and force our own newlines/horizontal tabs import { jsx as ___EmotionJSX } from "@emotion/react"; export var CHARS = { NEWLINE: '↵', TAB: '↦', // Use multiple characters to reduce the chances of consumers also using these characters TABULAR_CONTENT_BOUND: '𐘂𐘂', NO_COPY_BOUND: '✄𐘗' }; // This regex finds all content between two bounds export var noCopyBoundsRegex = new RegExp("".concat(CHARS.NO_COPY_BOUND, "[^").concat(CHARS.NO_COPY_BOUND, "]*").concat(CHARS.NO_COPY_BOUND), 'gs'); var hasCharsToReplace = function hasCharsToReplace(text) { for (var _i = 0, _Object$values = Object.values(CHARS); _i < _Object$values.length; _i++) { var char = _Object$values[_i]; if (text.indexOf(char) >= 0) return true; } return false; }; // Strip all existing newlines and replace our special hidden characters // with the desired spacing needed to paste cleanly into a spreadsheet export var onTabularCopy = function onTabularCopy(event) { var _window$getSelection; if (!event.clipboardData) return; var selectedText = (_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.toString(); if (!selectedText || !hasCharsToReplace(selectedText)) return; var amendedText = selectedText.split(CHARS.TABULAR_CONTENT_BOUND).map(function (text) { return hasCharsToReplace(text) ? text.replace(/\r?\n/g, '') // remove all other newlines generated by content or block display .replaceAll(CHARS.NEWLINE, '\n') // insert newline for each table/grid row .replace(/\t/g, '') // remove tabs generated by content or automatically by <td> elements .replaceAll(CHARS.TAB, "\t") // insert horizontal tab for each table/grid cell .replace(noCopyBoundsRegex, '') // remove text that should not be copied (e.g. screen reader instructions) : text; }).join(''); event.clipboardData.setData('text/plain', amendedText); event.preventDefault(); }; /** * JSX utils for rendering the hidden marker characters */ var VisuallyHide = function VisuallyHide(_ref) { var children = _ref.children, _ref$type = _ref.type, type = _ref$type === void 0 ? 'true' : _ref$type; return ( // Hides the characters to both sighted user and screen readers // Sadly, we can't use `hidden` as that hides the chars from the clipboard as well ___EmotionJSX("span", { className: "euiScreenReaderOnly", "aria-hidden": true, "data-tabular-copy-marker": type }, children) ); }; export var tabularCopyMarkers = { hiddenTab: ___EmotionJSX(VisuallyHide, { type: "tab" }, CHARS.TAB), hiddenNewline: ___EmotionJSX(VisuallyHide, { type: "newline" }, CHARS.NEWLINE), hiddenWrapperBoundary: ___EmotionJSX(VisuallyHide, { type: "boundary" }, CHARS.TABULAR_CONTENT_BOUND), hiddenNoCopyBoundary: ___EmotionJSX(VisuallyHide, { type: "no-copy" }, CHARS.NO_COPY_BOUND) }; /** * Wrapper setup around table/grid tabular content we want to override/clean up on copy */ export var OverrideCopiedTabularContent = function OverrideCopiedTabularContent(_ref2) { var children = _ref2.children; useEffect(function () { // Chrome and webkit browsers work perfectly when passing `onTabularCopy` to a React // `onCopy` prop, but sadly Firefox does not if copying more than just the table/grid // (e.g. Ctrl+A). So we have to set up a global window event listener window.document.addEventListener('copy', onTabularCopy); // Note: Since onCopy is static, we don't have to worry about duplicate // event listeners - it's automatically handled by the browser. See: // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Multiple_identical_event_listeners }, []); return ___EmotionJSX(React.Fragment, null, tabularCopyMarkers.hiddenWrapperBoundary, children, tabularCopyMarkers.hiddenWrapperBoundary); };