All files / src/pages/flowsheet-page/flowsheet/LeftSideBar/Formulas useAggregateFormulaSuggestions.ts

75.67% Statements 28/37
60% Branches 12/20
30% Functions 3/10
88.23% Lines 15/17

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98                                                                                76x                                 53x           12x         12x 72x 318x   282x         53x 312x 126x         267x     119x       22x   53x   97x    
import { useMemo } from "react";
import objectDefinitions from "@/data/objects.json";
import {
  type AggregateSuggestion,
  getAggregateFunctionOptions,
  getAggregateKeyInsertionText,
} from "./aggregateFormula";
 
type AggregatePropertySchema = {
  /** Human-facing property label from the object schema. */
  displayName?: string;
  /** Indexed properties are excluded because aggregate formulas are scalar. */
  indexSets?: string[];
  /** Only numeric properties can participate in aggregate expressions. */
  type?: string;
};
 
type AggregateSourceObject = {
  /** Object schema key used to discover which property keys are available. */
  objectType?: string | null;
};
 
type AggregateFormulaSuggestions = {
  /** All scalar numeric property keys available on the current flowsheet. */
  aggregateKeyOptions: AggregateSuggestion[];
  /** Plain-text snippets offered by the aggregate function mention source. */
  aggregateFunctionSuggestions: AggregateSuggestion[];
  /** Plain-text snippets offered by the aggregate property-key mention source. */
  aggregateKeySuggestions: AggregateSuggestion[];
};
 
type AggregateFormulaSuggestionParams = {
  /** Unit operations currently present in the flowsheet. */
  flowsheetUnitOps?: AggregateSourceObject[];
  /** User-visible formula text, with mention markup converted to display text. */
  plainFormula: string;
  /** Caret position in `plainFormula`. */
  cursorPosition: number;
};
 
const aggregateObjectDefinitions = objectDefinitions as unknown as Record<
  string,
  { properties?: Record<string, AggregatePropertySchema> }
>;
 
/**
 * Build autocomplete data for aggregate formula authoring.
 *
 * `flowsheetUnitOps` determines the property keys that can be suggested.
 * `plainFormula` and `cursorPosition` let property-key suggestions decide
 * whether the selected key should also insert the closing aggregate paren.
 *
 * The returned suggestions are consumed by `react-mentions` with a temporary
 * aggregate markup that `useFormulaEditorState` immediately unwraps, so
 * aggregate selections persist as plain formula text instead of property
 * mention markup.
 */
export function useAggregateFormulaSuggestions({
  flowsheetUnitOps,
  plainFormula,
  cursorPosition,
}: AggregateFormulaSuggestionParams): AggregateFormulaSuggestions {
  const aggregateKeyOptions = useMemo<AggregateSuggestion[]>(() => {
    const keys = new Map<string, string>();
 
    // Aggregate formulas are authored by stable property key, not property ID.
    // The backend remains the source of truth for which values actually
    // participate in a solve; this list is just an authoring aid.
    flowsheetUnitOps?.forEach((obj) => {
      const objectSchema = aggregateObjectDefinitions[obj.objectType ?? ""];
      Object.entries(objectSchema?.properties ?? {}).forEach(([key, value]) => {
        if (value.type === "numeric" && !value.indexSets?.length) {
          keys.set(key, `${key} (${value.displayName})`);
        }
      });
    });
 
    return Array.from(keys.entries())
      .sort(([left], [right]) => left.localeCompare(right))
      .map(([id, display]) => ({ id, display }));
  }, [flowsheetUnitOps]);
 
  const aggregateKeySuggestions = useMemo(
    () =>
      aggregateKeyOptions.map(({ id, display }) => ({
        id: `@${getAggregateKeyInsertionText(id, plainFormula, cursorPosition)}`,
        display,
      })),
    [aggregateKeyOptions, cursorPosition, plainFormula],
  );
 
  return {
    aggregateKeyOptions,
    aggregateFunctionSuggestions: getAggregateFunctionOptions(""),
    aggregateKeySuggestions,
  };
}