@pnp/spfx-controls-react
Version:
Reusable React controls for SharePoint Framework solutions
144 lines • 8.6 kB
JavaScript
;
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