UNPKG

@eccenca/gui-elements

Version:

GUI elements based on other libraries, usable in React application, written in Typescript.

436 lines 28.2 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.nodeContentUtils = void 0; exports.NodeContent = NodeContent; const react_1 = __importDefault(require("react")); const react_flow_renderer_1 = require("react-flow-renderer"); const react_2 = require("@xyflow/react"); const color_1 = __importDefault(require("color")); const re_resizable_1 = require("re-resizable"); const Intent_1 = require("../../../common/Intent"); const constants_1 = require("../../../configuration/constants"); const index_1 = require("../../../index"); const versionsupport_1 = require("../versionsupport"); const HandleDefault_1 = require("./../handles/HandleDefault"); const defaultHandles = (flowVersion) => { switch (flowVersion) { case "v9": case "v12": return [{ type: "target" }, { type: "source" }]; default: return []; } }; const getDefaultMinimalTooltipData = (node) => { var _a, _b, _c, _d; return { label: (_a = node.data) === null || _a === void 0 ? void 0 : _a.label, content: (_b = node.data) === null || _b === void 0 ? void 0 : _b.content, iconName: (_c = node.data) === null || _c === void 0 ? void 0 : _c.iconName, depiction: (_d = node.data) === null || _d === void 0 ? void 0 : _d.depiction, }; }; const addHandles = (handles, position, posDirection, isConnectable, nodeStyle, flowVersion = versionsupport_1.ReactFlowVersions.V9) => { return handles[position].map((handle, idx) => { var _a, _b; // FIXME: remove? orig v12 change: return handles[position].map((handle: any, idx: any) => { const { style = {} } = handle, otherHandleProps = __rest(handle, ["style"]); const styleAdditions = { color: (_a = nodeStyle.borderColor) !== null && _a !== void 0 ? _a : undefined, }; styleAdditions[posDirection] = (100 / (handles[position].length + 1)) * (idx + 1) + "%"; const handleProperties = Object.assign(Object.assign({}, otherHandleProps), { position: (_b = handle.position) !== null && _b !== void 0 ? _b : position, style: Object.assign(Object.assign({}, style), styleAdditions), posdirection: posDirection, isConnectable: typeof handle.isConnectable !== "undefined" ? handle.isConnectable : isConnectable, }); return (react_1.default.createElement(MemoHandler, Object.assign({ flowVersion: flowVersion }, handleProperties, { key: "handle" + idx }))); // FIXME: remove? orig v12 change: return <MemoHandler flowVersion={flowVersion} {...handleProperties} key={"handle" + idx} />; }); }; function compareStyleDirection(styleA, styleB, direction) { return styleA[direction] === styleB[direction]; } const MemoHandler = react_1.default.memo((props) => react_1.default.createElement(HandleDefault_1.HandleDefault, Object.assign({}, props)), (prev, next) => { return ( // we only test a few properties to control re-rendering // need to be extended if also other properties need to be changed late compareStyleDirection(prev.style, next.style, prev.posdirection) && prev.isConnectable === next.isConnectable && prev.intent === next.intent && prev.category === next.category); }); const DEFAULT_RESIZE_DIRECTIONS = { bottom: true, right: true }; /** * The `NodeContent` element manages the main view of how a node is displaying which content. * This element cannot be used directly, all properties must be routed through the `data` property of an `elements` property item inside the `ReactFlow` container. */ function NodeContent(_a) { var _b, _c, _d, _e; var { flowVersion, iconName, depiction, leftElement, typeLabel, label, labelSubline, enlargeHeader, fullWidth, showExecutionButtons = true, executionButtons, menuButtons, content, contentExtension, footerContent, size = "small", minimalShape = "circular", intent, border, highlightColor, //handles = defaultHandles(), adaptHeightForHandleMinCount, adaptSizeIncrement = 15, // FIXME: getMinimalTooltipData is just being ignored, only used in `NodeDefault` getMinimalTooltipData = getDefaultMinimalTooltipData, style = {}, showUnconnectableHandles = false, animated = false, introductionTime = 0, // resizing onNodeResize, nodeDimensions, resizeDirections = DEFAULT_RESIZE_DIRECTIONS, resizeMaxDimensions, // forwarded props targetPosition = react_flow_renderer_1.Position.Left, sourcePosition = react_flow_renderer_1.Position.Right, isConnectable = true, selected, letPassWheelEvents = false, // FIXME: businessData is just being ignored businessData } = _a, // other props for DOM element otherDomProps = __rest(_a, ["flowVersion", "iconName", "depiction", "leftElement", "typeLabel", "label", "labelSubline", "enlargeHeader", "fullWidth", "showExecutionButtons", "executionButtons", "menuButtons", "content", "contentExtension", "footerContent", "size", "minimalShape", "intent", "border", "highlightColor", "adaptHeightForHandleMinCount", "adaptSizeIncrement", "getMinimalTooltipData", "style", "showUnconnectableHandles", "animated", "introductionTime", "onNodeResize", "nodeDimensions", "resizeDirections", "resizeMaxDimensions", "targetPosition", "sourcePosition", "isConnectable", "selected", "letPassWheelEvents", "businessData"]); const evaluateFlowVersion = (0, versionsupport_1.useReactFlowVersion)(); const flowVersionCheck = flowVersion || evaluateFlowVersion; const [introductionDone, setIntroductionDone] = react_1.default.useState(false); // ignore some properties for now (remove them later) // if (otherDomProps.businessData) { delete otherDomProps.businessData } // if (otherDomProps.getMinimalTooltipData) { delete otherDomProps.getMinimalTooltipData } const { handles = defaultHandles(flowVersionCheck) } = otherDomProps, otherProps = __rest(otherDomProps, ["handles"]); const hasValidResizeDirection = resizeDirections.bottom || resizeDirections.right; const isResizable = typeof onNodeResize === "function" && hasValidResizeDirection && minimalShape === "none"; const [width, setWidth] = react_1.default.useState((_b = nodeDimensions === null || nodeDimensions === void 0 ? void 0 : nodeDimensions.width) !== null && _b !== void 0 ? _b : undefined); const [height, setHeight] = react_1.default.useState((_c = nodeDimensions === null || nodeDimensions === void 0 ? void 0 : nodeDimensions.height) !== null && _c !== void 0 ? _c : undefined); // Keeps the initial size of the element const originalSize = react_1.default.useRef({}); let zoom = 1; if (isResizable) try { switch (flowVersionCheck) { case "v9": [, , zoom] = (0, react_flow_renderer_1.useStoreState)((state) => state.transform); break; case "v12": // we are calling a hook here conditionally. Not recommended, by the flowversion check is // is basically compile time determined. So we just do it. [, , zoom] = (0, react_2.useStore)((state) => state.transform); break; } } catch (error) { // do not handle error but at least push it to the console // eslint-disable-next-line no-console console.error(error); } const [adjustedContentProps, setAdjustedContentProps] = react_1.default.useState({}); const nodeContentRef = react_1.default.useRef(); const handleStack = { [react_flow_renderer_1.Position.Top]: [], [react_flow_renderer_1.Position.Right]: [], [react_flow_renderer_1.Position.Bottom]: [], [react_flow_renderer_1.Position.Left]: [], }; const saveOriginalSize = () => { const currentClassNames = nodeContentRef.current.classList; if (currentClassNames.contains("was-resized") && !width && !height) { currentClassNames.remove("was-resized"); } originalSize.current.width = nodeContentRef.current.offsetWidth; originalSize.current.height = nodeContentRef.current.offsetHeight; }; react_1.default.useEffect(() => { if (nodeContentRef.current && (!(originalSize.current.width || originalSize.current.height) || !(width || height))) { saveOriginalSize(); } }, [!!nodeContentRef.current, !(originalSize.current.width || originalSize.current.height), !(width || height)]); // Update width and height when node dimensions parameters has changed react_1.default.useEffect(() => { const updateWidth = (nodeDimensions === null || nodeDimensions === void 0 ? void 0 : nodeDimensions.width) ? validateWidth(nodeDimensions === null || nodeDimensions === void 0 ? void 0 : nodeDimensions.width) : undefined; const updateHeight = (nodeDimensions === null || nodeDimensions === void 0 ? void 0 : nodeDimensions.height) ? validateHeight(nodeDimensions === null || nodeDimensions === void 0 ? void 0 : nodeDimensions.height) : undefined; setWidth(updateWidth); setHeight(updateHeight); }, [nodeDimensions]); const isResizingActive = react_1.default.useCallback(() => { const currentClassNames = nodeContentRef.current.classList; return (resizeDirections.right === currentClassNames.contains("is-resizable-horizontal") || resizeDirections.bottom === currentClassNames.contains("is-resizable-vertical")); }, []); // force default size when resizing is activated but no dimensions are set react_1.default.useEffect(() => { var _a, _b; const resizingActive = isResizingActive(); if (isResizable && !resizingActive) { if (!width || !height) { const newWidth = validateWidth(width !== null && width !== void 0 ? width : (_a = originalSize.current) === null || _a === void 0 ? void 0 : _a.width); const newHeight = validateHeight(height !== null && height !== void 0 ? height : (_b = originalSize.current) === null || _b === void 0 ? void 0 : _b.height); setWidth(newWidth); setHeight(newHeight); } } }, [ nodeContentRef.current, onNodeResize, minimalShape, resizeDirections === null || resizeDirections === void 0 ? void 0 : resizeDirections.bottom, resizeDirections === null || resizeDirections === void 0 ? void 0 : resizeDirections.right, width, height, ]); // need to be done everytime a property is changed and the element is re-rendered, otherwise the resizing class is lost // conditional enhancements for activated resizing react_1.default.useEffect(() => { const currentClassNames = nodeContentRef.current.classList; const resizingActive = isResizingActive(); if (isResizable && !resizingActive) { if (currentClassNames.contains("is-resizable-horizontal")) { currentClassNames.remove("is-resizable-horizontal"); } if (currentClassNames.contains("is-resizable-vertical")) { currentClassNames.remove("is-resizable-vertical"); } if (resizeDirections.right) { currentClassNames.add("is-resizable-horizontal"); } if (resizeDirections.bottom) { currentClassNames.add("is-resizable-vertical"); } } }); // need to be done everytime a property is changed and the element is re-rendered, otherwise the resizing class is lost // remove introduction class react_1.default.useEffect(() => { var _a; if (nodeContentRef && introductionTime) { const timeDelay = typeof introductionTime === "object" ? (_a = introductionTime.delay) !== null && _a !== void 0 ? _a : 0 : 0; const timeRun = typeof introductionTime === "object" ? introductionTime.run : introductionTime; setTimeout(() => { nodeContentRef.current.className = nodeContentRef.current.className.replace(`${constants_1.CLASSPREFIX}-graphviz__node--introduction`, `${constants_1.CLASSPREFIX}-graphviz__node--introduction-runs`); }, timeDelay); setTimeout(() => { nodeContentRef.current.className = nodeContentRef.current.className.replace(`${constants_1.CLASSPREFIX}-graphviz__node--introduction-runs`, `${constants_1.CLASSPREFIX}-graphviz__node--introduction-done`); setIntroductionDone(true); }, timeDelay + timeRun); } }, [nodeContentRef, introductionTime]); if (handles.length > 0) { handles .sort((a, b) => { if (a.category === "dependency") { return 1; } if (b.category === "dependency") { return -1; } return 0; }) .forEach((handle) => { let position = handle.position; // force position regarding special configuration if (handle.category === "configuration") { position = react_flow_renderer_1.Position.Top; } else { const handleType = handle; if (handleType.type === "target") { position = targetPosition; } if (handleType.type === "source") { position = sourcePosition; } } handle.position = position; handleStack[position].push(handle); }); } const styleExpandDimensions = Object.create(null); if (typeof adaptHeightForHandleMinCount !== "undefined" && (minimalShape === "none" || !!selected) && adaptSizeIncrement && (handleStack[react_flow_renderer_1.Position.Left].length >= adaptHeightForHandleMinCount || handleStack[react_flow_renderer_1.Position.Right].length >= adaptHeightForHandleMinCount)) { const minHeightLeft = handleStack[react_flow_renderer_1.Position.Left].length * adaptSizeIncrement; const minHeightRight = handleStack[react_flow_renderer_1.Position.Right].length * adaptSizeIncrement; styleExpandDimensions["minHeight"] = Math.max(minHeightLeft, minHeightRight); } const { highlightClassNameSuffix, highlightCustomPropertySettings } = evaluateHighlightColors("--node-highlight", highlightColor); const resizableStyles = isResizable && (width !== null && width !== void 0 ? width : 0) + (height !== null && height !== void 0 ? height : 0) > 0 ? { width, height, maxWidth: resizeDirections.right ? (_d = resizeMaxDimensions === null || resizeMaxDimensions === void 0 ? void 0 : resizeMaxDimensions.width) !== null && _d !== void 0 ? _d : undefined : undefined, maxHeight: resizeDirections.bottom ? (_e = resizeMaxDimensions === null || resizeMaxDimensions === void 0 ? void 0 : resizeMaxDimensions.height) !== null && _e !== void 0 ? _e : undefined : undefined, } : {}; const introductionStyles = introductionTime && !introductionDone ? { "--node-introduction-time": `${typeof introductionTime === "object" ? introductionTime.run : introductionTime}ms`, } : {}; const nodeContent = (react_1.default.createElement(react_1.default.Fragment, null, react_1.default.createElement("section", Object.assign({ ref: nodeContentRef }, otherProps, { style: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, style), highlightCustomPropertySettings), styleExpandDimensions), resizableStyles), introductionStyles), className: `${constants_1.CLASSPREFIX}-graphviz__node` + ` ${constants_1.CLASSPREFIX}-graphviz__node--${flowVersionCheck}` + ` ${constants_1.CLASSPREFIX}-graphviz__node--${size}` + ` ${constants_1.CLASSPREFIX}-graphviz__node--minimal-${minimalShape}` + (fullWidth ? ` ${constants_1.CLASSPREFIX}-graphviz__node--fullwidth` : "") + (border ? ` ${constants_1.CLASSPREFIX}-graphviz__node--border-${border}` : "") + (intent ? ` ${(0, Intent_1.intentClassName)(intent)}` : "") + (highlightClassNameSuffix.length > 0 ? highlightClassNameSuffix .map((highlight) => ` ${constants_1.CLASSPREFIX}-graphviz__node--highlight-${highlight}`) .join("") : "") + (animated ? ` ${constants_1.CLASSPREFIX}-graphviz__node--animated` : "") + (introductionTime && !introductionDone ? ` ${constants_1.CLASSPREFIX}-graphviz__node--introduction` : "") + (showUnconnectableHandles === false ? ` ${constants_1.CLASSPREFIX}-graphviz__node--hidehandles` : "") + (letPassWheelEvents === false ? ` nowheel` : ""), "data-introduction-animation": typeof introductionTime === "object" && !introductionDone ? introductionTime.animation : undefined }), react_1.default.createElement("header", { className: `${constants_1.CLASSPREFIX}-graphviz__node__header` + (enlargeHeader && minimalShape === "none" ? ` ${constants_1.CLASSPREFIX}-graphviz__node__header--large` : "") }, (!!iconName || !!depiction || !!leftElement) && (react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-graphviz__node__header-depiction` }, leftElement, !!depiction && !leftElement && typeof depiction === "string" && (react_1.default.createElement(index_1.Depiction, { image: react_1.default.createElement("img", { src: depiction, alt: "" }), caption: minimalShape === "none" || selected ? typeLabel : undefined, captionPosition: "tooltip", padding: "tiny", ratio: "1:1", resizing: "contain", forceInlineSvg: true })), !!depiction && !leftElement && typeof depiction !== "string" && react_1.default.cloneElement(depiction, { caption: minimalShape === "none" || selected ? typeLabel : undefined, captionPosition: "tooltip", padding: "tiny", ratio: "1:1", resizing: "contain", forceInlineSvg: true, }), !!iconName && !leftElement && !depiction && (react_1.default.createElement(index_1.Depiction, { image: react_1.default.createElement(index_1.Icon, { name: iconName }), caption: minimalShape === "none" || selected ? typeLabel : undefined, captionPosition: "tooltip", padding: "tiny", ratio: "1:1", resizing: "contain", forceInlineSvg: true })))), react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-graphviz__node__header-label`, title: typeof label === "string" ? label : undefined }, typeof label === "string" ? (react_1.default.createElement(index_1.OverflowText, { className: `${constants_1.CLASSPREFIX}-graphviz__node__header-label__mainline` }, label)) : (react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-graphviz__node__header-label__mainline` }, label)), !!labelSubline && (react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-graphviz__node__header-label__subline` }, labelSubline))), (menuButtons || (showExecutionButtons && executionButtons)) && (react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-graphviz__node__header-menu nodrag` }, showExecutionButtons && typeof executionButtons === "function" ? executionButtons(adjustedContentProps, setAdjustedContentProps) : null, menuButtons !== null && menuButtons !== void 0 ? menuButtons : null))), content && (react_1.default.createElement("div", { className: `${constants_1.CLASSPREFIX}-graphviz__node__content` }, typeof content === "function" ? content(adjustedContentProps) : content)), contentExtension, footerContent && react_1.default.createElement("footer", { className: `${constants_1.CLASSPREFIX}-graphviz__node__footer` }, footerContent)), !!handles && (react_1.default.createElement(react_1.default.Fragment, null, addHandles(handleStack, react_flow_renderer_1.Position.Top, "left", isConnectable, style, flowVersionCheck), addHandles(handleStack, react_flow_renderer_1.Position.Right, "top", isConnectable, style, flowVersionCheck), addHandles(handleStack, react_flow_renderer_1.Position.Bottom, "left", isConnectable, style, flowVersionCheck), addHandles(handleStack, react_flow_renderer_1.Position.Left, "top", isConnectable, style, flowVersionCheck))))); const validateWidth = (resizedWidth) => { var _a; // only allow value if resize direction is allowed if (!resizeDirections.right) { return undefined; } // we need to check because there is probably a min value defined via CSS const min = parseFloat(getComputedStyle(nodeContentRef.current).getPropertyValue("min-width")); // we need to check for a given max value const max = (_a = resizeMaxDimensions === null || resizeMaxDimensions === void 0 ? void 0 : resizeMaxDimensions.width) !== null && _a !== void 0 ? _a : Infinity; const validatedWidth = Math.max(Math.min(resizedWidth, max), min); return validatedWidth; }; const validateHeight = (resizedHeight) => { var _a; if (!resizeDirections.bottom) { return undefined; } // we need to check because there is probably a min value defined via CSS const min = parseFloat(getComputedStyle(nodeContentRef.current).getPropertyValue("min-height")); const max = (_a = resizeMaxDimensions === null || resizeMaxDimensions === void 0 ? void 0 : resizeMaxDimensions.height) !== null && _a !== void 0 ? _a : Infinity; const validatedHeight = Math.max(Math.min(resizedHeight, max), min); return validatedHeight; }; const onResize = react_1.default.useCallback((_0, _1, _2, d) => { var _a, _b; if (nodeContentRef.current) { const nextWidth = resizeDirections.right ? ((_a = width !== null && width !== void 0 ? width : originalSize.current.width) !== null && _a !== void 0 ? _a : 0) + d.width : undefined; const nextHeight = resizeDirections.bottom ? ((_b = height !== null && height !== void 0 ? height : originalSize.current.height) !== null && _b !== void 0 ? _b : 0) + d.height : undefined; if (nextWidth || nextHeight) { const currentClassNames = nodeContentRef.current.classList; currentClassNames.add("was-resized"); } if (nextWidth) { nodeContentRef.current.style.width = `${nextWidth}px`; } if (nextHeight) { nodeContentRef.current.style.height = `${nextHeight}px`; } } }, [resizeDirections, originalSize, width, height]); const onResizeStop = react_1.default.useCallback((_0, _1, _2, d) => { var _a, _b; const nextWidth = validateWidth(((_a = width !== null && width !== void 0 ? width : originalSize.current.width) !== null && _a !== void 0 ? _a : 0) + d.width); const nextHeight = validateHeight(((_b = height !== null && height !== void 0 ? height : originalSize.current.height) !== null && _b !== void 0 ? _b : 0) + d.height); setWidth(nextWidth); setHeight(nextHeight); if (onNodeResize) { onNodeResize({ height: nextHeight, width: nextWidth, }); } }, [onNodeResize, width, height, originalSize]); const resizableSize = react_1.default.useMemo(() => ({ height: height !== null && height !== void 0 ? height : "auto", width: width !== null && width !== void 0 ? width : "auto" }), [height, width]); const enableResize = react_1.default.useMemo(() => resizeDirections.bottom && resizeDirections.right ? { bottomRight: true } : resizeDirections, [resizeDirections]); const resizableNode = () => { var _a, _b; return (react_1.default.createElement(re_resizable_1.Resizable, { className: `${constants_1.CLASSPREFIX}-graphviz__node__resizer` + (resizeDirections.right ? ` ${constants_1.CLASSPREFIX}-graphviz__node__resizer--right` : "") + (resizeDirections.bottom ? ` ${constants_1.CLASSPREFIX}-graphviz__node__resizer--bottom` : ""), handleWrapperClass: `${constants_1.CLASSPREFIX}-graphviz__node__resizer--cursorhandles` + " nodrag", size: resizableSize, maxHeight: (_a = resizeMaxDimensions === null || resizeMaxDimensions === void 0 ? void 0 : resizeMaxDimensions.height) !== null && _a !== void 0 ? _a : undefined, maxWidth: (_b = resizeMaxDimensions === null || resizeMaxDimensions === void 0 ? void 0 : resizeMaxDimensions.width) !== null && _b !== void 0 ? _b : undefined, enable: enableResize, scale: zoom, onResize: onResize, onResizeStop: onResizeStop }, nodeContent)); }; return isResizable ? resizableNode() : nodeContent; } const evaluateHighlightColors = (baseCustomProperty, highlightColor) => { let styleHighlightColors = { [`${baseCustomProperty}-default-color`]: undefined, [`${baseCustomProperty}-alternate-color`]: undefined, }; const classesHightlightColors = []; if (highlightColor) { const highlightingColors = typeof highlightColor === "string" ? [highlightColor] : highlightColor; highlightingColors.map((color, idx) => { switch (color) { case "default": classesHightlightColors.push("default"); break; case "alternate": classesHightlightColors.push("alternate"); break; default: { classesHightlightColors.push("custom"); let customColor = (0, color_1.default)("#ffffff"); try { customColor = (0, color_1.default)(color); } catch (_a) { // eslint-disable-next-line no-console console.warn("Received invalid color for highlight: " + color); } if (idx === 0) { styleHighlightColors = Object.assign(Object.assign({}, styleHighlightColors), { [`${baseCustomProperty}-default-color`]: customColor.rgb().toString() }); } else { styleHighlightColors = Object.assign(Object.assign({}, styleHighlightColors), { [`${baseCustomProperty}-alternate-color`]: customColor.rgb().toString() }); } break; } } return color; }); } return { highlightClassNameSuffix: classesHightlightColors, highlightCustomPropertySettings: styleHighlightColors, }; }; exports.nodeContentUtils = { evaluateHighlightColors, }; //# sourceMappingURL=NodeContent.js.map