# Conditional Formatting

This topic documents worksheet-level conditional formatting rules for `univer run`.

Use conditional formatting when cell appearance should be controlled by values, text, formulas, scales, bars, or icon sets. Unlike static formatting APIs such as `setBackgroundColor()`, conditional formatting creates rules on the worksheet. Those rules are evaluated by the spreadsheet engine.

## Core Workflow

The core workflow is:

1. Get the workbook with `univerAPI.getActiveWorkbook()`.
2. Look up the target worksheet explicitly.
3. Create an `FRange`, such as `sheet.getRange("A1:A10")`.
4. Create a builder with `sheet.newConditionalFormattingRule()`.
5. Configure the condition and formatting.
6. Bind scopes with `setRanges([fRange.getRange()])`.
7. Call `build()` and pass the rule to `sheet.addConditionalFormattingRule(rule)`.
8. Read back rules with `sheet.getConditionalFormattingRules()` when the script needs verification output.

Example:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Scores");
  if (!sheet) return { success: false, error: 'Sheet "Scores" not found' };

  const range = sheet.getRange("B2:B20");
  const rule = sheet
    .newConditionalFormattingRule()
    .whenNumberGreaterThan(90)
    .setBackground("#C6EFCE")
    .setFontColor("#006100")
    .setRanges([range.getRange()])
    .build();

  sheet.addConditionalFormattingRule(rule);

  return {
    success: true,
    ruleCount: sheet.getConditionalFormattingRules().length
  };
};
```

## Highlight Rules

Highlight rules test each cell and apply formatting when the rule matches.

Number predicates:

- `whenNumberGreaterThan(value)`
- `whenNumberGreaterThanOrEqualTo(value)`
- `whenNumberLessThan(value)`
- `whenNumberLessThanOrEqualTo(value)`
- `whenNumberEqualTo(value)`
- `whenNumberNotEqualTo(value)`
- `whenNumberBetween(start, end)`
- `whenNumberNotBetween(start, end)`

Text predicates:

- `whenTextContains(text)`
- `whenTextDoesNotContain(text)`
- `whenTextStartsWith(text)`
- `whenTextEndsWith(text)`
- `whenTextEqualTo(text)`

Other predicates:

- `whenCellEmpty()`
- `whenCellNotEmpty()`
- `whenDate(operator)`
- `whenFormulaSatisfied(formulaString)` - `formulaString` starts with `=`
- `setAverage(operator)`
- `setRank({ isBottom, isPercent, value })`
- `setDuplicateValues()`
- `setUniqueValues()`

Formatting setters:

- `setBackground(color?)`
- `setFontColor(color?)`
- `setBold(isBold)`
- `setItalic(isItalic)`
- `setUnderline(isUnderline)`
- `setStrikethrough(isStrikethrough)`

Formula example:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Orders");
  if (!sheet) return { success: false, error: 'Sheet "Orders" not found' };

  const range = sheet.getRange("D2:D100");
  const rule = sheet
    .newConditionalFormattingRule()
    .whenFormulaSatisfied("=D2>1000")
    .setBold(true)
    .setBackground("#FFF2CC")
    .setRanges([range.getRange()])
    .build();

  sheet.addConditionalFormattingRule(rule);

  return { success: true, ruleCount: sheet.getConditionalFormattingRules().length };
};
```

## Color Scales

Use `setColorScale(config)` for gradient-style rules. The config is an ordered array of `{ index, color, value }` entries.

Use `univerAPI.Enum.ConditionFormatValueTypeEnum` for each value config `type`. Common value types include `num`, `min`, `max`, `percent`, `percentile`, and `formula`.

Example:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Scores");
  if (!sheet) return { success: false, error: 'Sheet "Scores" not found' };

  const range = sheet.getRange("B2:B20");
  const rule = sheet
    .newConditionalFormattingRule()
    .setColorScale([
      {
        index: 0,
        color: "#F8696B",
        value: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 0 }
      },
      {
        index: 1,
        color: "#FFEB84",
        value: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 50 }
      },
      {
        index: 2,
        color: "#63BE7B",
        value: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 100 }
      }
    ])
    .setRanges([range.getRange()])
    .build();

  sheet.addConditionalFormattingRule(rule);

  return { success: true, ruleCount: sheet.getConditionalFormattingRules().length };
};
```

## Data Bars

Use `setDataBar(config)` to display proportional bars in cells.

Config fields:

- `min` - value config such as `{ type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 0 }`
- `max` - value config such as `{ type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 100 }`
- `positiveColor` - bar color for positive values
- `nativeColor` - bar color for negative values
- `isGradient` - optional boolean
- `isShowValue` - optional boolean

Example:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Scores");
  if (!sheet) return { success: false, error: 'Sheet "Scores" not found' };

  const range = sheet.getRange("C2:C20");
  const rule = sheet
    .newConditionalFormattingRule()
    .setDataBar({
      min: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 0 },
      max: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 100 },
      positiveColor: "#63BE7B",
      nativeColor: "#F8696B",
      isGradient: true,
      isShowValue: true
    })
    .setRanges([range.getRange()])
    .build();

  sheet.addConditionalFormattingRule(rule);

  return { success: true, ruleCount: sheet.getConditionalFormattingRules().length };
};
```

## Icon Sets

Use `setIconSet({ iconConfigs, isShowValue })` for icon-set rules. `iconConfigs` entries include `iconType`, `iconId`, `operator`, and `value`.

Use `univerAPI.Enum.ConditionFormatIconSetTypeEnum` for `iconType`, such as `ConditionFormatIconSetTypeEnum.threeArrows`. Use `univerAPI.Enum.ConditionFormatValueTypeEnum` for each threshold value config `type`.

Use `builder.getIconMap()` to inspect supported icon groups before choosing an icon set. This avoids hard-coding a full icon set list in scripts.

Example:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Scores");
  if (!sheet) return { success: false, error: 'Sheet "Scores" not found' };

  const range = sheet.getRange("D2:D20");
  const builder = sheet.newConditionalFormattingRule();
  const rule = builder
    .setIconSet({
      iconConfigs: [
        {
          iconType: univerAPI.Enum.ConditionFormatIconSetTypeEnum.threeArrows,
          iconId: "0",
          operator: univerAPI.Enum.ConditionFormatNumberOperatorEnum.greaterThan,
          value: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 80 }
        },
        {
          iconType: univerAPI.Enum.ConditionFormatIconSetTypeEnum.threeArrows,
          iconId: "1",
          operator: univerAPI.Enum.ConditionFormatNumberOperatorEnum.greaterThan,
          value: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 50 }
        },
        {
          iconType: univerAPI.Enum.ConditionFormatIconSetTypeEnum.threeArrows,
          iconId: "2",
          operator: univerAPI.Enum.ConditionFormatNumberOperatorEnum.lessThanOrEqual,
          value: { type: univerAPI.Enum.ConditionFormatValueTypeEnum.num, value: 50 }
        }
      ],
      isShowValue: true
    })
    .setRanges([range.getRange()])
    .build();

  sheet.addConditionalFormattingRule(rule);

  return {
    success: true,
    iconGroups: Object.keys(builder.getIconMap()).length,
    ruleCount: sheet.getConditionalFormattingRules().length
  };
};
```

## Rule List APIs

Worksheet-level rule APIs:

- `getConditionalFormattingRules() -> IConditionFormattingRule[]`
- `addConditionalFormattingRule(rule) -> FWorksheet`
- `deleteConditionalFormattingRule(cfId) -> FWorksheet`
- `setConditionalFormattingRule(cfId, rule) -> FWorksheet`
- `moveConditionalFormattingRule(cfId, toCfId, type?) -> FWorksheet`
- `clearConditionalFormatRules() -> FWorksheet`

Readback and clear example:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Scores");
  if (!sheet) return { success: false, error: 'Sheet "Scores" not found' };

  const before = sheet.getConditionalFormattingRules().length;
  sheet.clearConditionalFormatRules();
  const after = sheet.getConditionalFormattingRules().length;

  return { success: true, before, after };
};
```

Update a rule's range:

```javascript
() => {
  const workbook = univerAPI.getActiveWorkbook();
  const sheet = workbook.getSheetByName("Scores");
  if (!sheet) return { success: false, error: 'Sheet "Scores" not found' };

  const rules = sheet.getConditionalFormattingRules();
  const firstRule = rules[0];
  if (!firstRule) return { success: false, error: "No conditional formatting rule found" };

  const newRange = sheet.getRange("B2:B50");
  sheet.setConditionalFormattingRule(firstRule.cfId, {
    ...firstRule,
    ranges: [newRange.getRange()]
  });

  return { success: true, ruleCount: sheet.getConditionalFormattingRules().length };
};
```

## Gotchas

- Conditional formatting creates worksheet-level rules, not static cell styles.
- `setRanges()` expects underlying range objects, so pass `fRange.getRange()`.
- Rule objects contain engine-owned IDs such as `cfId`; use public worksheet methods instead of editing internal model state directly.
- Verify command-line behavior through API readback such as `getConditionalFormattingRules()`.
- Formula-based conditions should follow formula calculation guidance from `formulas` when readback timing matters.
- Use static formatting APIs from `formatting` when the target appearance should not depend on cell values.

## Related Topics

- See `formatting` for static cell formatting.
- See `formulas` for calculation timing.
- See `ranges` for A1 ranges and shape rules.
