UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

315 lines 12.4 kB
"use strict"; /** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.computeDataGridRowStyles = computeDataGridRowStyles; exports.evaluateDataGridStyleRules = evaluateDataGridStyleRules; const react_color_1 = require("@uiw/react-color"); const style_rules_1 = require("../../_generated/style-rules"); function getInterpolatedColor(colorRange, rowData, converters) { const { onColumn, minValue, minColor, maxValue, maxColor, midValue, midColor, } = colorRange; if (!(0, react_color_1.validHex)(minColor) || !(0, react_color_1.validHex)(maxColor)) { return null; } const minRgb = (0, react_color_1.color)(minColor).rgb; const maxRgb = (0, react_color_1.color)(maxColor).rgb; const actualMinValue = Math.min(minValue, maxValue); const actualMaxValue = Math.max(minValue, maxValue); let midRgb = undefined; if (midValue !== undefined && midColor !== undefined) { const isMidInRange = actualMinValue <= midValue && midValue <= actualMaxValue; if (!isMidInRange || !(0, react_color_1.validHex)(midColor)) { return null; } midRgb = (0, react_color_1.color)(midColor).rgb; } const rawValue = rowData[onColumn]; let numericValue = null; if (typeof rawValue === 'number') { numericValue = Number.isFinite(rawValue) ? rawValue : null; } else if (typeof rawValue === 'bigint') { const n = Number(rawValue); numericValue = Number.isFinite(n) ? n : null; } else if (rawValue instanceof Date) { const ts = rawValue.getTime(); numericValue = Number.isFinite(ts) ? ts : null; } else { const converter = converters === null || converters === void 0 ? void 0 : converters[onColumn]; if (converter !== undefined) { const converted = converter(rawValue); if (typeof converted === 'number') { numericValue = Number.isFinite(converted) ? converted : null; } } } if (numericValue === null) { return null; } const clampedValue = Math.max(actualMinValue, Math.min(actualMaxValue, numericValue)); let rgb; if (midRgb !== undefined && midValue !== undefined) { const t = (0, style_rules_1.getWeight)(minValue, midValue, clampedValue); const isBetweenMinAndMid = t <= 1; if (isBetweenMinAndMid) { rgb = (0, style_rules_1.lerpColor)(minRgb, midRgb, t); } else { const t2 = (0, style_rules_1.getWeight)(midValue, maxValue, clampedValue); rgb = (0, style_rules_1.lerpColor)(midRgb, maxRgb, t2); } } else { const t = (0, style_rules_1.getWeight)(minValue, maxValue, clampedValue); rgb = (0, style_rules_1.lerpColor)(minRgb, maxRgb, t); } return (0, react_color_1.rgbToHex)(rgb); } function resolveValue(value, rowData, converters) { if (typeof value === 'object' && value !== null && 'column' in value) { const cellVal = rowData[value.column]; if (cellVal === undefined) { return null; } if (typeof cellVal === 'string' || typeof cellVal === 'number' || typeof cellVal === 'boolean') { return cellVal; } if (typeof cellVal === 'bigint') { return Number(cellVal); } if (cellVal instanceof Date) { const ts = cellVal.getTime(); return isNaN(ts) ? null : ts; } const converter = converters === null || converters === void 0 ? void 0 : converters[value.column]; if (converter !== undefined) { return converter(cellVal); } return null; } if (typeof value === 'object' && value !== null && 'datetime' in value) { return (0, style_rules_1.parseDateString)(value.datetime); } if (typeof value === 'object' && value !== null && 'localdatetime' in value) { return (0, style_rules_1.parseLocalDateString)(value.localdatetime); } if (typeof value === 'object' && value !== null && 'time' in value) { return (0, style_rules_1.parseTimeString)(value.time); } return value; } function safeCompare(left, right, rowData, compareFn, converters) { const leftVal = resolveValue(left, rowData, converters); const rightVal = resolveValue(right, rowData, converters); if (leftVal === null || rightVal === null) { return null; } return compareFn(leftVal, rightVal); } function safeStringCompare(left, right, rowData, compareFn, converters) { const leftVal = resolveValue(left, rowData, converters); const rightVal = resolveValue(right, rowData, converters); if (leftVal === null || rightVal === null || typeof leftVal !== 'string' || typeof rightVal !== 'string') { return null; } return compareFn(leftVal, rightVal); } function evaluateWhere(rowData, where, converters) { if (!where) { return true; } if ('equal' in where) { const [left, right] = where.equal; const leftVal = resolveValue(left, rowData, converters); const rightVal = resolveValue(right, rowData, converters); if (leftVal === null || rightVal === null) { return null; } return leftVal === rightVal; } if ('not' in where) { const isMatch = evaluateWhere(rowData, where.not, converters); return isMatch === null ? null : !isMatch; } if ('lessThan' in where) { return safeCompare(where.lessThan[0], where.lessThan[1], rowData, (a, b) => a < b, converters); } if ('lessThanOrEqual' in where) { return safeCompare(where.lessThanOrEqual[0], where.lessThanOrEqual[1], rowData, (a, b) => a <= b, converters); } if ('greaterThan' in where) { return safeCompare(where.greaterThan[0], where.greaterThan[1], rowData, (a, b) => a > b, converters); } if ('greaterThanOrEqual' in where) { return safeCompare(where.greaterThanOrEqual[0], where.greaterThanOrEqual[1], rowData, (a, b) => a >= b, converters); } if ('contains' in where) { return safeStringCompare(where.contains[0], where.contains[1], rowData, (a, b) => a.includes(b), converters); } if ('startsWith' in where) { return safeStringCompare(where.startsWith[0], where.startsWith[1], rowData, (a, b) => a.startsWith(b), converters); } if ('endsWith' in where) { return safeStringCompare(where.endsWith[0], where.endsWith[1], rowData, (a, b) => a.endsWith(b), converters); } if ('isNull' in where) { const value = resolveValue(where.isNull, rowData, converters); return value === null; } if ('and' in where) { let hasNull = false; for (const clause of where.and) { const isMatch = evaluateWhere(rowData, clause, converters); if (isMatch === false) { return false; } if (isMatch === null) { hasNull = true; } } return hasNull ? null : true; } if ('or' in where) { let hasNull = false; for (const clause of where.or) { const isMatch = evaluateWhere(rowData, clause, converters); if (isMatch === true) { return true; } if (isMatch === null) { hasNull = true; } } return hasNull ? null : false; } return false; } // --- Style resolution --- function resolveAppliedStyle(apply, rowData, converters) { const result = {}; let hasStyle = false; if (apply.color && !apply.color.isDisabled) { result.backgroundColor = apply.color.value; hasStyle = true; } if (apply.textColor && !apply.textColor.isDisabled) { result.color = apply.textColor.value; hasStyle = true; } if (apply.colorRange && !apply.colorRange.isDisabled) { const interpolated = getInterpolatedColor(apply.colorRange, rowData, converters); if (interpolated !== null) { result.backgroundColor = interpolated; hasStyle = true; } } if (apply.textColorRange && !apply.textColorRange.isDisabled) { const interpolated = getInterpolatedColor(apply.textColorRange, rowData, converters); if (interpolated !== null) { result.color = interpolated; hasStyle = true; } } return hasStyle ? result : undefined; } /** * Computes styles for all cells in a row based on the given style rules. * When a rule's `column` is `null`, its style is applied to every cell. * * Used internally by hooks, but exported for context-free usage. */ function computeDataGridRowStyles(rules, rowData, converters) { const enabledRules = rules.filter((r) => !r.isDisabled); const totalRules = enabledRules.length; const sortedRules = enabledRules .map((rule, index) => { var _a; if (rule.priority !== undefined && rule.priority < 0) { throw new Error(`DataGridStyleRule priority must be >= 0, got ${rule.priority}. Negative values are reserved for internal use.`); } return Object.assign(Object.assign({}, rule), { _order: index, _priority: (_a = rule.priority) !== null && _a !== void 0 ? _a : index - totalRules }); }) .sort((a, b) => { if (a._priority !== b._priority) { return a._priority - b._priority; } return a._order - b._order; }); const cellPriorityMap = {}; for (const rule of sortedRules) { const isMatching = evaluateWhere(rowData, rule.where, converters) === true; if (!isMatching) { continue; } const style = resolveAppliedStyle(rule.apply, rowData, converters); if (!style) { continue; } const effectivePriority = rule._priority; const affectedColumns = rule.column === null ? Object.keys(rowData) : [rule.column]; for (const col of affectedColumns) { if (!cellPriorityMap[col]) { cellPriorityMap[col] = {}; } for (const [prop, value] of Object.entries(style)) { const existing = cellPriorityMap[col][prop]; if (!existing || effectivePriority >= existing.priority) { cellPriorityMap[col][prop] = { priority: effectivePriority, value: value, }; } } } } const cellStyles = {}; for (const [col, propMap] of Object.entries(cellPriorityMap)) { const style = {}; let hasProp = false; for (const [prop, entry] of Object.entries(propMap)) { style[prop] = entry.value; hasProp = true; } cellStyles[col] = hasProp ? style : undefined; } return { cellStyles }; } /** * Evaluate style rules for a single cell. * Pure function -- no React context needed. * * @param rules - Array of style rules to evaluate * @param rowData - The row's data as a record (column id -> value) * @param columnId - Returns the computed style for this specific cell. * @param converters - Optional per-column value converters for complex cell types. */ function evaluateDataGridStyleRules(rules, rowData, columnId, converters) { const { cellStyles } = computeDataGridRowStyles(rules, rowData, converters); return cellStyles[columnId]; } //# sourceMappingURL=evaluate.js.map