UNPKG

@pnp/spfx-controls-react

Version:

Reusable React controls for SharePoint Framework solutions

144 lines • 8.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var React = tslib_1.__importStar(require("react")); var Icon_1 = require("@fluentui/react/lib/Icon"); /** * A class that provides helper methods for custom formatting * See: https://learn.microsoft.com/en-us/sharepoint/dev/declarative-customization/formatting-syntax-reference */ var CustomFormattingHelper = /** @class */ (function () { /** * Custom Formatting Helper / Renderer * @param formulaEvaluator An instance of FormulaEvaluation used for evaluating expressions in custom formatting */ function CustomFormattingHelper(formulaEvaluator) { var _this = this; /** * The Formula Evaluator expects an ASTNode to be passed to it for evaluation. This method converts expressions * described by the interface ICustomFormattingExpressionNode to ASTNodes. * @param node An ICustomFormattingExpressionNode to be converted to an ASTNode */ this.convertCustomFormatExpressionNodes = function (node) { if (typeof node !== "object") { switch (typeof node) { case "string": return { type: "string", value: node }; case "number": return { type: "number", value: node }; case "boolean": return { type: "booelan", value: node ? 1 : 0 }; } } var operator = node.operator; var operands = node.operands.map(function (o) { return _this.convertCustomFormatExpressionNodes(o); }); return { type: "operator", value: operator, operands: operands }; }; /** * Given a single custom formatting expression, node or element, this method evaluates the expression and returns the result * @param content An object, expression or literal value to be evaluated * @param context A context object containing values / variables to be used in the evaluation * @returns */ this.evaluateCustomFormatContent = function (content, context) { // If content is a string or number, it is a literal value and should be returned as-is if ((typeof content === "string" && content.charAt(0) !== "=") || typeof content === "number") return content; // If content is a string beginning with '=' it is a formula/expression, and should be evaluated if (typeof content === "string" && content.charAt(0) === "=") { var result = _this._formulaEvaluator.evaluate(content.substring(1), context); return result; } // If content is an object, it is either further custom formatting described by an ICustomFormattingNode, // or an expression to be evaluated - as described by an ICustomFormattingExpressionNode if (typeof content === "object") { if (Object.prototype.hasOwnProperty.call(content, "elmType")) { // Custom Formatting Content return _this.renderCustomFormatContent(content, context); } else if (Object.prototype.hasOwnProperty.call(content, "operator")) { // Expression to be evaluated var astNode = _this.convertCustomFormatExpressionNodes(content); var result = _this._formulaEvaluator.evaluateASTNode(astNode, context); if (typeof result === "object" && Object.prototype.hasOwnProperty.call(result, "elmType")) { return _this.renderCustomFormatContent(result, context); } return result; } } }; // eslint-disable-next-line @typescript-eslint/no-explicit-any this.renderCustomFormatContent = function (node, context, rootEl) { if (rootEl === void 0) { rootEl = false; } // We don't want attempts to render custom format content to kill the component or web part, // so we wrap the entire method in a try/catch block, log errors and return null if an error occurs try { // If node is a string or number, it is a literal value and should be returned as-is if (typeof node === "string" || typeof node === "number") return node; // Custom formatting nodes / elements may have a txtContent property, which represents the inner // content of a HTML element. This can be a string literal, or another expression to be evaluated: var textContent = void 0; if (node.txtContent) { textContent = _this.evaluateCustomFormatContent(node.txtContent, context); } // Custom formatting nodes / elements may have a style property, which contains the style rules // to be applied to the resulting HTML element. Rule values can be string literals or another expression // to be evaluated: var styleProperties = {}; if (node.style) { var nodeStyle = node.style; for (var styleAttribute in nodeStyle) { if (nodeStyle[styleAttribute]) { styleProperties[styleAttribute] = _this.evaluateCustomFormatContent(nodeStyle[styleAttribute], context); } } } // Custom formatting nodes / elements may have an attributes property, which represents the HTML attributes // to be applied to the resulting HTML element. Attribute values can be string literals or another expression // to be evaluated: var attributes = {}; if (node.attributes) { for (var attribute in node.attributes) { if (node.attributes[attribute]) { var attributeName = attribute; // Because we're using React to render the HTML content, we need to rename the 'class' attribute if (attributeName === "class") attributeName = "className"; // Evaluation attributes[attributeName] = _this.evaluateCustomFormatContent(node.attributes[attribute], context); // Add the 'sp-field-customFormatter' class to the root element if (attributeName === "className" && rootEl) { attributes[attributeName] = "".concat(attributes[attributeName], " sp-field-customFormatter"); } } } } // Custom formatting nodes / elements may have children. These are likely to be further custom formatting var children = []; // If the node has an iconName property, we'll render an Icon component as the first child. // SharePoint uses CSS to apply the icon in a ::before rule, but we can't count on the global selector for iconName // being present on the page, so we'll add it as a child instead: if (attributes.iconName) { var icon = React.createElement(Icon_1.Icon, { iconName: attributes.iconName }); children.push(icon); } // Each child object is evaluated recursively and added to the children array if (node.children) { children = node.children.map(function (c) { return _this.evaluateCustomFormatContent(c, context); }); } // The resulting HTML element is returned to the callee using React.createElement var el = React.createElement.apply(React, tslib_1.__spreadArray([node.elmType, tslib_1.__assign({ style: styleProperties }, attributes), textContent], children, false)); return el; } catch (error) { console.error('Unable to render custom formatted content', error); return null; } }; this._formulaEvaluator = formulaEvaluator; } return CustomFormattingHelper; }()); exports.default = CustomFormattingHelper; //# sourceMappingURL=CustomFormatting.js.map