helper.mjs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { isFirefox } from "../../../utils/browser.mjs";
  2. import { ensureArray } from "../../../utils/arrays.mjs";
  3. //#region ../../packages/components/mention/src/helper.ts
  4. const filterOption = (pattern, option) => {
  5. const lowerCase = pattern.toLowerCase();
  6. return (option.label || option.value || "").toLowerCase().includes(lowerCase);
  7. };
  8. const getMentionCtx = (inputEl, prefix, split) => {
  9. const { selectionEnd } = inputEl;
  10. if (selectionEnd === null) return;
  11. const inputValue = inputEl.value;
  12. const prefixArray = ensureArray(prefix);
  13. let splitIndex = -1;
  14. let mentionCtx;
  15. for (let i = selectionEnd - 1; i >= 0; --i) {
  16. const char = inputValue[i];
  17. if (splitIndex === -1 && (char === split || char === "\n" || char === "\r")) {
  18. splitIndex = i;
  19. continue;
  20. }
  21. if (prefixArray.includes(char)) {
  22. const end = splitIndex === -1 ? selectionEnd : splitIndex;
  23. mentionCtx = {
  24. pattern: inputValue.slice(i + 1, end),
  25. start: i + 1,
  26. end,
  27. prefix: char,
  28. prefixIndex: i,
  29. splitIndex,
  30. selectionEnd
  31. };
  32. break;
  33. }
  34. }
  35. return mentionCtx;
  36. };
  37. /**
  38. * fork from textarea-caret-position
  39. * https://github.com/component/textarea-caret-position
  40. * The MIT License (MIT)
  41. * Copyright (c) 2015 Jonathan Ong me@jongleberry.com
  42. * Permission is hereby granted, free of charge, to any person obtaining a copy
  43. * of this software and associated documentation files (the "Software"), to deal
  44. * in the Software without restriction, including without limitation the rights
  45. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  46. * copies of the Software, and to permit persons to whom the Software is
  47. * furnished to do so, subject to the following conditions:
  48. * The above copyright notice and this permission notice shall be included in all
  49. * copies or substantial portions of the Software.
  50. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  51. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  52. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  53. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  54. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  55. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  56. * SOFTWARE.
  57. */
  58. const getCursorPosition = (element, options = {
  59. debug: false,
  60. useSelectionEnd: false
  61. }) => {
  62. const selectionStart = element.selectionStart !== null ? element.selectionStart : 0;
  63. const selectionEnd = element.selectionEnd !== null ? element.selectionEnd : 0;
  64. const position = options.useSelectionEnd ? selectionEnd : selectionStart;
  65. const properties = [
  66. "direction",
  67. "boxSizing",
  68. "width",
  69. "height",
  70. "overflowX",
  71. "overflowY",
  72. "borderTopWidth",
  73. "borderRightWidth",
  74. "borderBottomWidth",
  75. "borderLeftWidth",
  76. "borderStyle",
  77. "paddingTop",
  78. "paddingRight",
  79. "paddingBottom",
  80. "paddingLeft",
  81. "fontStyle",
  82. "fontVariant",
  83. "fontWeight",
  84. "fontStretch",
  85. "fontSize",
  86. "fontSizeAdjust",
  87. "lineHeight",
  88. "fontFamily",
  89. "textAlign",
  90. "textTransform",
  91. "textIndent",
  92. "textDecoration",
  93. "letterSpacing",
  94. "wordSpacing",
  95. "tabSize",
  96. "MozTabSize"
  97. ];
  98. if (options.debug) {
  99. const el = document.querySelector("#input-textarea-caret-position-mirror-div");
  100. if (el?.parentNode) el.parentNode.removeChild(el);
  101. }
  102. const div = document.createElement("div");
  103. div.id = "input-textarea-caret-position-mirror-div";
  104. document.body.appendChild(div);
  105. const style = div.style;
  106. const computed = window.getComputedStyle(element);
  107. const isInput = element.nodeName === "INPUT";
  108. style.whiteSpace = isInput ? "nowrap" : "pre-wrap";
  109. if (!isInput) style.wordWrap = "break-word";
  110. style.position = "absolute";
  111. if (!options.debug) style.visibility = "hidden";
  112. properties.forEach((prop) => {
  113. if (isInput && prop === "lineHeight") if (computed.boxSizing === "border-box") {
  114. const height = Number.parseInt(computed.height);
  115. const outerHeight = Number.parseInt(computed.paddingTop) + Number.parseInt(computed.paddingBottom) + Number.parseInt(computed.borderTopWidth) + Number.parseInt(computed.borderBottomWidth);
  116. const targetHeight = outerHeight + Number.parseInt(computed.lineHeight);
  117. if (height > targetHeight) style.lineHeight = `${height - outerHeight}px`;
  118. else if (height === targetHeight) style.lineHeight = computed.lineHeight;
  119. else style.lineHeight = "0";
  120. } else style.lineHeight = computed.height;
  121. else style[prop] = computed[prop];
  122. });
  123. if (isFirefox()) {
  124. if (element.scrollHeight > Number.parseInt(computed.height)) style.overflowY = "scroll";
  125. } else style.overflow = "hidden";
  126. div.textContent = element.value.slice(0, Math.max(0, position));
  127. if (isInput && div.textContent) div.textContent = div.textContent.replace(/\s/g, "\xA0");
  128. const span = document.createElement("span");
  129. span.textContent = element.value.slice(Math.max(0, position)) || ".";
  130. span.style.position = "relative";
  131. span.style.left = `${-element.scrollLeft}px`;
  132. span.style.top = `${-element.scrollTop}px`;
  133. div.appendChild(span);
  134. const relativePosition = {
  135. top: span.offsetTop + Number.parseInt(computed.borderTopWidth),
  136. left: span.offsetLeft + Number.parseInt(computed.borderLeftWidth),
  137. height: Number.parseInt(computed.fontSize) * 1.5
  138. };
  139. if (options.debug) span.style.backgroundColor = "#aaa";
  140. else document.body.removeChild(div);
  141. if (relativePosition.left >= element.clientWidth) relativePosition.left = element.clientWidth;
  142. return relativePosition;
  143. };
  144. //#endregion
  145. export { filterOption, getCursorPosition, getMentionCtx };
  146. //# sourceMappingURL=helper.mjs.map