/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import * as React from "react"; import { useCallback, useMemo } from "react"; import * as ReactTable from "react-table"; import { Action, BeeTableCellProps, BeeTableContextMenuAllowedOperationsConditions, BeeTableHeaderVisibility, BeeTableOperation, BeeTableOperationConfig, BeeTableProps, BoxedExpression, BoxedFunction, BoxedFunctionKind, DmnBuiltInDataType, ExpressionChangedArgs, Normalized, } from "../../api"; import { useBoxedExpressionEditorI18n } from "../../i18n"; import { useNestedExpressionContainerWithNestedExpressions } from "../../resizing/Hooks"; import { NestedExpressionContainerContext } from "../../resizing/NestedExpressionContainerContext"; import { ResizerStopBehavior } from "../../resizing/ResizingWidthsContext"; import { FEEL_FUNCTION_EXPRESSION_EXTRA_WIDTH, FEEL_FUNCTION_EXPRESSION_MIN_WIDTH, } from "../../resizing/WidthConstants"; import { BeeTable, BeeTableColumnUpdate } from "../../table/BeeTable"; import { NestedExpressionDispatchContextProvider, OnSetExpression, useBoxedExpressionEditor, useBoxedExpressionEditorDispatch, } from "../../BoxedExpressionEditorContext"; import { DEFAULT_EXPRESSION_VARIABLE_NAME } from "../../expressionVariable/ExpressionVariableMenu"; import { useFunctionExpressionControllerCell, useFunctionExpressionParametersColumnHeader } from "./FunctionExpression"; import { ExpressionContainer } from "../ExpressionDefinitionRoot/ExpressionContainer"; import { DMN_LATEST__tFunctionDefinition } from "@kie-tools/dmn-marshaller"; import { findAllIdsDeep } from "../../ids/ids"; export type FEEL_ROWTYPE = { functionExpression: Normalized }; export type BoxedFunctionFeel = DMN_LATEST__tFunctionDefinition & { "@_kind": "FEEL"; __$$element: "functionDefinition"; }; export function FeelFunctionExpression({ isNested, parentElementId, functionExpression, }: { functionExpression: Normalized; isNested: boolean; parentElementId: string; }) { const { i18n } = useBoxedExpressionEditorI18n(); const { expressionHolderId, widthsById, isReadOnly } = useBoxedExpressionEditor(); const { setExpression, setWidthsById } = useBoxedExpressionEditorDispatch(); const parametersColumnHeader = useFunctionExpressionParametersColumnHeader( functionExpression.formalParameter, isReadOnly ?? false ); const parametersId = useMemo( () => (functionExpression["@_id"] ? `${functionExpression["@_id"]}-parameters` : "parameters"), [functionExpression] ); const beeTableColumns = useMemo[]>(() => { return [ { accessor: expressionHolderId as any, // FIXME: https://github.com/apache/incubator-kie-issues/issues/169 label: functionExpression["@_label"] ?? DEFAULT_EXPRESSION_VARIABLE_NAME, dataType: functionExpression["@_typeRef"] ?? DmnBuiltInDataType.Undefined, isRowIndexColumn: false, width: undefined, columns: [ { headerCellElement: parametersColumnHeader, accessor: parametersId as any, label: i18n.parameters, isRowIndexColumn: false, dataType: undefined as any, width: undefined, }, ], }, ]; }, [expressionHolderId, functionExpression, parametersColumnHeader, parametersId, i18n.parameters]); const headerVisibility = useMemo(() => { return isNested ? BeeTableHeaderVisibility.LastLevel : BeeTableHeaderVisibility.AllLevels; }, [isNested]); const onColumnUpdates = useCallback( ([{ name, typeRef }]: BeeTableColumnUpdate[]) => { const expressionChangedArgs: ExpressionChangedArgs = { action: Action.VariableChanged, variableUuid: expressionHolderId, typeChange: typeRef !== functionExpression["@_typeRef"] ? { from: functionExpression["@_typeRef"] ?? "", to: typeRef, } : undefined, nameChange: name !== functionExpression["@_label"] ? { from: functionExpression["@_label"] ?? "", to: name, } : undefined, }; setExpression({ setExpressionAction: (prev: Normalized) => { // Do not inline this variable for type safety. See https://github.com/microsoft/TypeScript/issues/241 const ret: Normalized = { ...prev, "@_label": name, "@_typeRef": typeRef, }; return ret; }, expressionChangedArgs, }); }, [expressionHolderId, functionExpression, setExpression] ); const beeTableOperationConfig = useMemo(() => { return [ { group: i18n.terms.selection.toUpperCase(), items: [{ name: i18n.terms.copy, type: BeeTableOperation.SelectionCopy }], }, { group: i18n.function.toUpperCase(), items: [{ name: i18n.rowOperations.reset, type: BeeTableOperation.RowReset }], }, ]; }, [i18n]); const beeTableRows = useMemo(() => { return [{ functionExpression }]; }, [functionExpression]); const controllerCell = useFunctionExpressionControllerCell(BoxedFunctionKind.Feel); const cellComponentByColumnAccessor: BeeTableProps["cellComponentByColumnAccessor"] = useMemo(() => { return { [`${parametersId}`]: (props) => , }; }, [parentElementId, parametersId]); const getRowKey = useCallback((r: ReactTable.Row) => { return r.id; }, []); const onRowReset = useCallback(() => { let oldExpression: Normalized | undefined; setExpression({ setExpressionAction: (prev: Normalized) => { oldExpression = prev.expression; return undefined!; // SPEC DISCREPANCY }, expressionChangedArgs: { action: Action.RowReset, rowIndex: 0 }, }); setWidthsById(({ newMap }) => { for (const id of findAllIdsDeep(oldExpression)) { newMap.delete(id); } }); }, [setExpression, setWidthsById]); /// ////////////////////////////////////////////////////// /// ///////////// RESIZING WIDTHS //////////////////////// /// ////////////////////////////////////////////////////// const { nestedExpressionContainerValue, onColumnResizingWidthChange } = useNestedExpressionContainerWithNestedExpressions( useMemo(() => { const nestedExpressions = [functionExpression.expression ?? undefined!]; return { nestedExpressions: nestedExpressions, fixedColumnActualWidth: 0, fixedColumnResizingWidth: { value: 0, isPivoting: false }, fixedColumnMinWidth: 0, nestedExpressionMinWidth: FEEL_FUNCTION_EXPRESSION_MIN_WIDTH, extraWidth: FEEL_FUNCTION_EXPRESSION_EXTRA_WIDTH, expression: functionExpression, flexibleColumnIndex: 1, widthsById: widthsById, }; }, [functionExpression, widthsById]) ); const allowedOperations = useCallback((conditions: BeeTableContextMenuAllowedOperationsConditions) => { if (!conditions.selection.selectionStart || !conditions.selection.selectionEnd) { return []; } return [ BeeTableOperation.SelectionCopy, ...(conditions.selection.selectionStart.rowIndex >= 0 ? [BeeTableOperation.RowReset] : []), ]; }, []); /// ////////////////////////////////////////////////////// return (
isReadOnly={isReadOnly} onColumnResizingWidthChange={onColumnResizingWidthChange} resizerStopBehavior={ResizerStopBehavior.SET_WIDTH_WHEN_SMALLER} operationConfig={beeTableOperationConfig} allowedOperations={allowedOperations} onColumnUpdates={onColumnUpdates} getRowKey={getRowKey} onRowReset={onRowReset} columns={beeTableColumns} rows={beeTableRows} headerLevelCountForAppendingRowIndexColumn={1} headerVisibility={headerVisibility} controllerCell={controllerCell} cellComponentByColumnAccessor={cellComponentByColumnAccessor} shouldRenderRowIndexColumn={true} shouldShowRowsInlineControls={false} shouldShowColumnsInlineControls={false} />
); } export function FeelFunctionImplementationCell({ data, rowIndex, columnIndex, parentElementId, }: BeeTableCellProps & { parentElementId: string }) { const functionExpression = data[rowIndex].functionExpression; const { setExpression } = useBoxedExpressionEditorDispatch(); const onSetExpression = useCallback( ({ getNewExpression, expressionChangedArgs, }: { getNewExpression: (prev: Normalized) => Normalized; expressionChangedArgs: ExpressionChangedArgs; }) => { setExpression({ setExpressionAction: (prev: Normalized) => { // Do not inline this variable for type safety. See https://github.com/microsoft/TypeScript/issues/241 const ret: Normalized = { ...prev, expression: getNewExpression(prev.expression ?? undefined!), }; return ret; }, expressionChangedArgs, }); }, [setExpression] ); return ( ); }