UNPKG

@grafana/ui

Version:
244 lines (236 loc) • 9.15 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var jsxRuntime = require('react/jsx-runtime'); var css = require('@emotion/css'); var react = require('@floating-ui/react'); var Prism = require('prismjs'); var React = require('react'); var reactUse = require('react-use'); var Plain = require('slate-plain-serializer'); var slateReact = require('slate-react'); var data = require('@grafana/data'); var index = require('../../slate-plugins/slate-prism/index.cjs'); var ThemeContext = require('../../themes/ThemeContext.cjs'); var mixins = require('../../themes/mixins.cjs'); var floating = require('../../utils/floating.cjs'); var slate = require('../../utils/slate.cjs'); var Input = require('../Input/Input.cjs'); var Portal = require('../Portal/Portal.cjs'); var ScrollContainer = require('../ScrollContainer/ScrollContainer.cjs'); var DataLinkSuggestions = require('./DataLinkSuggestions.cjs'); var SelectionReference = require('./SelectionReference.cjs'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var Prism__default = /*#__PURE__*/_interopDefaultCompat(Prism); var React__namespace = /*#__PURE__*/_interopNamespaceCompat(React); var Plain__default = /*#__PURE__*/_interopDefaultCompat(Plain); "use strict"; const modulo = (a, n) => a - n * Math.floor(a / n); const datalinksSyntax = { builtInVariable: { pattern: /(\${\S+?})/ } }; const plugins = [ index.SlatePrism( { onlyIn: (node) => "type" in node && node.type === "code_block", getSyntax: () => "links" }, { ...Prism__default.default.languages, links: datalinksSyntax } ) ]; const getStyles = (theme) => ({ input: Input.getInputStyles({ theme, invalid: false }).input, editor: css.css({ ".token.builtInVariable": { color: theme.colors.success.text }, ".token.variable": { color: theme.colors.primary.text } }), suggestionsWrapper: css.css({ boxShadow: theme.shadows.z2 }), // Wrapper with child selector needed. // When classnames are applied to the same element as the wrapper, it causes the suggestions to stop working wrapperOverrides: css.css({ width: "100%", "> .slate-query-field__wrapper": { padding: 0, backgroundColor: "transparent", border: "none", "&:focus-within": mixins.getFocusStyles(theme) } }) }); const DataLinkInput = React.memo( ({ value, onChange, suggestions, placeholder = "http://your-grafana.com/d/000000010/annotations", ["aria-labelledby"]: ariaLabelledby }) => { const editorRef = React.useRef(null); const styles = ThemeContext.useStyles2(getStyles); const [showingSuggestions, setShowingSuggestions] = React.useState(false); const [suggestionsIndex, setSuggestionsIndex] = React.useState(0); const [linkUrl, setLinkUrl] = React.useState(slate.makeValue(value)); const prevLinkUrl = reactUse.usePrevious(linkUrl); const [scrollTop, setScrollTop] = React.useState(0); const scrollRef = React.useRef(null); React.useEffect(() => { var _a; (_a = scrollRef.current) == null ? void 0 : _a.scrollTo(0, scrollTop); }, [scrollTop]); const middleware = [ react.offset(({ rects }) => ({ alignmentAxis: rects.reference.width })), ...floating.getPositioningMiddleware() ]; const { refs, floatingStyles } = react.useFloating({ open: showingSuggestions, placement: "bottom-start", onOpenChange: setShowingSuggestions, middleware, whileElementsMounted: react.autoUpdate, strategy: "fixed" }); const stateRef = React.useRef({ showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange }); stateRef.current = { showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange }; const activeRef = React.useRef(null); React.useEffect(() => { setScrollTop(getElementPosition(activeRef.current, suggestionsIndex)); }, [suggestionsIndex]); const onKeyDown = React__namespace.useCallback((event, next) => { if (!stateRef.current.showingSuggestions) { if (event.key === "=" || event.key === "$" || event.keyCode === 32 && event.ctrlKey) { const selectionRef = new SelectionReference.SelectionReference(); refs.setReference(selectionRef); return setShowingSuggestions(true); } return next(); } switch (event.key) { case "Backspace": if (stateRef.current.linkUrl.focusText.getText().length === 1) { next(); } case "Escape": setShowingSuggestions(false); return setSuggestionsIndex(0); case "Enter": event.preventDefault(); return onVariableSelect(stateRef.current.suggestions[stateRef.current.suggestionsIndex]); case "ArrowDown": case "ArrowUp": event.preventDefault(); const direction = event.key === "ArrowDown" ? 1 : -1; return setSuggestionsIndex((index) => modulo(index + direction, stateRef.current.suggestions.length)); default: return next(); } }, []); React.useEffect(() => { if (prevLinkUrl && prevLinkUrl.selection.isFocused && !linkUrl.selection.isFocused) { stateRef.current.onChange(Plain__default.default.serialize(linkUrl)); } }, [linkUrl, prevLinkUrl]); const onUrlChange = React__namespace.useCallback(({ value: value2 }) => { setLinkUrl(value2); }, []); const onVariableSelect = (item, editor = editorRef.current) => { const precedingChar = getCharactersAroundCaret(); const precedingDollar = precedingChar === "$"; if (item.origin !== data.VariableOrigin.Template || item.value === data.DataLinkBuiltInVars.includeVars) { editor.insertText(`${precedingDollar ? "" : "$"}{${item.value}}`); } else { editor.insertText(`${precedingDollar ? "" : "$"}{${item.value}:queryparam}`); } setLinkUrl(editor.value); setShowingSuggestions(false); setSuggestionsIndex(0); stateRef.current.onChange(Plain__default.default.serialize(editor.value)); }; const getCharactersAroundCaret = () => { const input = document.getElementById("data-link-input"); let precedingChar = "", sel, range; if (window.getSelection) { sel = window.getSelection(); if (sel && sel.rangeCount > 0) { range = sel.getRangeAt(0).cloneRange(); range.collapse(true); range.setStart(input, 0); precedingChar = range.toString().slice(-1); } } return precedingChar; }; return /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.wrapperOverrides, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "slate-query-field__wrapper", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { id: "data-link-input", className: "slate-query-field", children: [ showingSuggestions && /* @__PURE__ */ jsxRuntime.jsx(Portal.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: refs.setFloating, style: floatingStyles, children: /* @__PURE__ */ jsxRuntime.jsx( ScrollContainer.ScrollContainer, { maxHeight: "300px", ref: scrollRef, onScroll: (event) => setScrollTop(event.currentTarget.scrollTop), children: /* @__PURE__ */ jsxRuntime.jsx( DataLinkSuggestions.DataLinkSuggestions, { activeRef, suggestions: stateRef.current.suggestions, onSuggestionSelect: onVariableSelect, onClose: () => setShowingSuggestions(false), activeIndex: suggestionsIndex } ) } ) }) }), /* @__PURE__ */ jsxRuntime.jsx( slateReact.Editor, { schema: slate.SCHEMA, ref: editorRef, placeholder, value: stateRef.current.linkUrl, onChange: onUrlChange, onKeyDown: (event, _editor, next) => onKeyDown(event, next), plugins, className: css.cx( styles.editor, styles.input, css.css({ padding: "3px 8px" }) ), "aria-labelledby": ariaLabelledby } ) ] }) }) }); } ); DataLinkInput.displayName = "DataLinkInput"; function getElementPosition(suggestionElement, activeIndex) { var _a; return ((_a = suggestionElement == null ? void 0 : suggestionElement.clientHeight) != null ? _a : 0) * activeIndex; } exports.DataLinkInput = DataLinkInput; //# sourceMappingURL=DataLinkInput.cjs.map