UNPKG

@atlaskit/editor-plugin-selection-marker

Version:

Selection marker plugin for @atlaskit/editor-core.

134 lines (129 loc) 6.75 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.createWidgetDecoration = void 0; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _state = require("@atlaskit/editor-prosemirror/state"); var _view = require("@atlaskit/editor-prosemirror/view"); /** * @jsxRuntime classic * @jsx jsx */ var selectionMarkerHighlightStyles = { content: "''", position: 'absolute', backgroundImage: "url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMyIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDMgMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMSAxSDBMMSAxLjg1NzE0VjE4LjE0MzNMMCAxOS4wMDA0SDNMMiAxOC4xNDMzVjEuODU3MTRMMyAxSDJIMVoiIGZpbGw9IiM1NzlERkYiLz4KPHJlY3QgeT0iMTkiIHdpZHRoPSIzIiBoZWlnaHQ9IjEiIGZpbGw9IiM1NzlERkYiLz4KPHJlY3Qgd2lkdGg9IjMiIGhlaWdodD0iMSIgZmlsbD0iIzU3OURGRiIvPgo8L3N2Zz4K')", top: "var(--ds-space-0, 0px)", bottom: "var(--ds-space-negative-025, -2px)", backgroundRepeat: 'no-repeat', backgroundPositionX: 'center', backgroundPositionY: 'center', backgroundSize: 'contain', aspectRatio: '3/20', left: '0px', marginLeft: "var(--ds-space-negative-025, -2px)", right: '0px', marginRight: "var(--ds-space-negative-025, -2px)", pointerEvents: 'none' }; var selectionMarkerBlockCursorStyles = { content: "''", position: 'absolute', background: "var(--ds-text, #292A2E)", width: '1px', display: 'inline-block', top: "var(--ds-space-0, 0px)", bottom: "var(--ds-space-negative-025, -2px)", left: '1px', marginLeft: "var(--ds-space-negative-025, -2px)", right: '0px', marginRight: "var(--ds-space-negative-025, -2px)", pointerEvents: 'none' }; // Same as above but defined as an inline element to avoid breaking long words var selectionMarkerInlineCursorStyles = { content: "''", position: 'relative', pointerEvents: 'none', borderLeft: "var(--ds-border-width, 1px)".concat(" solid ", "var(--ds-text, #292A2E)"), marginLeft: '-1px', left: '0.5px' }; /** * Converts a camelCased CSS property name to a hyphenated CSS property name. * * @param property - CamelCased CSS property name. * @returns Hyphenated CSS property name. */ function hyphenate(property) { // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp return property.replace(/[A-Z]/g, function (match) { return "-".concat(match.toLowerCase()); }).replace(/^ms/, '-ms'); } var Widget = function Widget(_ref) { var type = _ref.type, isHighlight = _ref.isHighlight, isInWord = _ref.isInWord; var span = document.createElement('span'); var selectionMarkerCursorStyles = isInWord ? selectionMarkerInlineCursorStyles : selectionMarkerBlockCursorStyles; var styles = isHighlight ? selectionMarkerHighlightStyles : selectionMarkerCursorStyles; for (var _i = 0, _Object$entries = Object.entries(styles); _i < _Object$entries.length; _i++) { var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2), rule = _Object$entries$_i[0], value = _Object$entries$_i[1]; span.style.setProperty(hyphenate(rule), value); } span.setAttribute('contentEditable', 'false'); span.dataset.testid = "selection-marker-".concat(type, "-cursor"); return span; }; var toDOM = function toDOM(type, isHighlight, isInWord) { var element = document.createElement('span'); element.contentEditable = 'false'; element.setAttribute('style', "position: relative;"); element.appendChild(Widget({ type: type, isHighlight: isHighlight, isInWord: isInWord })); return element; }; var containsText = function containsText(resolvedPos) { var nodeBefore = resolvedPos.nodeBefore, nodeAfter = resolvedPos.nodeAfter; return (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.isInline) || (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.isInline); }; var createWidgetDecoration = exports.createWidgetDecoration = function createWidgetDecoration(resolvedPos, type, selection, isHighlight) { var _nodeBefore$textConte, _nodeAfter$textConten; // We don't want the cursor to show if it's not text selection // ie. if it's on media selection if (!(selection instanceof _state.TextSelection) || containsText(resolvedPos) === false || !selection.empty) { return []; } // We're inside a word if the parent, before, and after nodes are all text nodes // and the before/after nodes are appended/prepended with non-whitespace characters // Also if we're making a selection and not just a cursor, this isn't relevant var nodeBefore = resolvedPos.nodeBefore, nodeAfter = resolvedPos.nodeAfter, parent = resolvedPos.parent; // Check if the parent is a text node and the before/after nodes are also text nodes var areTextNodes = parent.isTextblock && (nodeBefore === null || nodeBefore === void 0 ? void 0 : nodeBefore.isText) && (nodeAfter === null || nodeAfter === void 0 ? void 0 : nodeAfter.isText); var lastCharacterOfBeforeNode = nodeBefore === null || nodeBefore === void 0 || (_nodeBefore$textConte = nodeBefore.textContent) === null || _nodeBefore$textConte === void 0 ? void 0 : _nodeBefore$textConte.slice(-1); var firstCharacterOfAfterNode = nodeAfter === null || nodeAfter === void 0 || (_nodeAfter$textConten = nodeAfter.textContent) === null || _nodeAfter$textConten === void 0 ? void 0 : _nodeAfter$textConten.slice(0, 1); var areAdjacentCharactersNonWhitespace = // @ts-ignore - TS1501 Older versions of TypeScript don't play nice with the u flag. With the current AFM TypeScript version, this *should* be fine, but the pipeline type check fails, hence why a ts-ignore is needed (over a ts-expect-error) /(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/.test(lastCharacterOfBeforeNode || '') && /(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/.test(firstCharacterOfAfterNode || ''); var isInWord = Boolean(areTextNodes && areAdjacentCharactersNonWhitespace); return [_view.Decoration.widget(resolvedPos.pos, toDOM(type, isHighlight, isInWord), { side: -1, key: "".concat(type, "WidgetDecoration"), stopEvent: function stopEvent() { return true; }, ignoreSelection: true })]; };