> ## Documentation Index
> Fetch the complete documentation index at: https://calcs.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Subsheet Widget (2-way linking)

> Experimental widget for automatic creation of dependent calculations with bi-directional data synchronization

<Warning>
  This is an **EXPERIMENTAL** feature. Use with caution in production templates.
</Warning>

## Overview

The Subsheet Widget provides a way to automatically create calculations that are dependent on the result of a master sheet. The typical scenario is a calculator like `portalFrameAnalysis` which performs analysis on certain member sizes, but the design must be linked to and performed in `timberMember` calculators for each element.

Instead of manually creating the `portalFrameAnalysis` and each of the `timberMember` calculators and linking them, you only need to create the `portalFrameAnalysis` and everything is automatically synchronized!

## API Structure

The subsheet widget can be dragged from the sidebar and has multiple conditions like the equation widget, but must have an array of objects for the `result`.

```json theme={null}
{
  "label": "YOUR LABEL VALUE",
  "symbol": "String (Latex)",
  "equation": [
    {
      "condition": "@default",
      "result": "[{
        id: 
        name:
        sheetTemplate:
        capability:
        preset:
        commonInputs:
        linkTo:
        linkFromIndex:                        
      },
      { ... }]"
    }
  ],
  "referenceId": "subsheet_widget",
  "visibleIf": "true / false"
}
```

## Configuration Parameters

<ResponseField name="id" type="string" required>
  Unique identifier for each subsheet. Typically `string(row[x])` when iterating over table, which pulls the value in the Xth column of the 1st argument.
</ResponseField>

<ResponseField name="name" type="string" required>
  Visible to user so should be meaningful. Best to have 1st argument have a column of labels like "Left Leg", "Right Leg". Example: `string(row[y])` when iterating over table.
</ResponseField>

<ResponseField name="sheetTemplate" type="string" required>
  Subsheet name (e.g., `timberMember`)
</ResponseField>

<ResponseField name="capability" type="string" required>
  Any string that will match the "capability" key added to the subsheet (e.g., `elementLinking`)
</ResponseField>

<ResponseField name="preset" type="string" required>
  The preset to use for the subsheet (e.g., `elementLink`)
</ResponseField>

<ResponseField name="commonInputs" type="array" required>
  Array of reference IDs of widgets in the subsheet that are shared. Within parent sheet these are accessed per special syntax below.
</ResponseField>

<ResponseField name="linkTo" type="string" required>
  Reference ID of subsheet table widget that receives all the results export (e.g., `link_table`)
</ResponseField>

<ResponseField name="linkFromIndex" type="integer" required>
  The row of data selected in the subsheet (0-based). Typically a column in 1st argument table that has an incrementing integer (e.g., `row[1]` when iterating over table)
</ResponseField>

## Important Constraints

<Warning>
  **Critical Requirements:**

  1. The keys (e.g., id, name) must not have any data that is derived from one of the `commonInput` widgets. This will lead to cyclic calculation errors.

  2. The subsheets cannot have defaultValue formulas in any of the `commonInputs`. For example, if connecting to `member`, don't set a defaultValue in the timberMember subsheet of `=projectDefault(xx)` in any preset or the defaultValue key.

  3. The `subsheetData` that is mapped over must not have any data that is derived from one of the `commonInput` widgets.
</Warning>

## Iteration for Multiple Subsheets

When the number of subsheets varies dynamically (e.g., based on the number of elements in a frame analysis), you can iterate over a table using the `mapRows` function.

### Dynamic Sheet Management

When the number of subsheets changes, sheets will be added or deleted accordingly. The subsheet input data will be preserved only if the newly created subsheet shares the same `id` as the previously deleted subsheet.

**Example:** A beam analysis with 3 spans that changes to 2 spans will delete the 3rd subsheet. If the `id`s were `[0,1,2]` then `[0,1]`, and later a third span is re-added with matching IDs (`[0,1,2]`), the previous subsheet will be recreated with all its old inputs.

### MapRows Function

| Parameter      | Type     | Description                                                      |
| -------------- | -------- | ---------------------------------------------------------------- |
| `subsheetData` | string   | Table to iterate over                                            |
| `fn(tableRow)` | function | Function that returns subsheet configuration object for each row |

```javascript theme={null}
mapRows(subsheetData, f(row) = { 
  id: string(row[2]), // Must be unique
  name: string(row[2]),
  sheetTemplate: 'timberMember',
  capability: 'elementLinking',
  preset: 'elementLink',
  commonInputs: ['member','n_{com}'],
  linkTo: 'link_table',
  linkFromIndex: row[1] // Must be integer
})
```

## Implementation Steps

### 1. Create Template

Create a template based on Portal Frame or Truss template.

### 2. Add Export Table

Add a table for exported inputs (as you would for normal linking).

### 3. Add Subsheet Widget

Add a Subsheet Widget and configure it according to the API above.

### 4. Update Capabilities

Add/Update the capabilities array in the subsheet header:

```json theme={null}
"data": {
  "attributes": {
    "capabilities": ["elementLinking"]
  }
}
```

### 5. Reference Values

Reference the subsheet widget values throughout the master sheet using the special syntax below.

## Referencing CommonInput Values

When working inside the super sheet, use this special syntax to reference any commonInputs:

### Input Widgets

```javascript theme={null}
subsheetWidgetRefId.subsheetId.commonInput
// Example: subsheetWidget.LeftLeg.n_com
```

### Lookup Widgets (Data Table or Shared Table)

```javascript theme={null}
subsheetWidgetRefId.subsheetId.commonInput.commonInputColumn
// Example: subsheetWidget.LeftLeg.member.Ixx
```

### Special Characters

When you have special characters (`-`, `/`, `space`), use square brackets instead of dots:

```javascript theme={null}
// With dots: subsheetWidget.LeftLeg.n_com
// With brackets: subsheetWidget.LeftLeg["n-com"]
```

## Example: Data Table Lookup

```json theme={null}
{
  "dataColumns": [
    {
      "label": "Orientation Axis",
      "referenceId": "axis" // This is the commonInputColumn
    },
    {
      "label": "Orientation Direction", 
      "referenceId": "direction"
    }
  ],
  "dataTable": [
    ["Loaded in 'L' Axis", "+y"],
    ["Loaded in '7' Axis", "-y"]
  ],
  "referenceId": "orient_load"
}
```

## Usage Workflow

1. **Master Sheet Setup**: Go to master sheet and set up your common inputs
2. **Subsheet Access**: Go to the subsheet and results should be available
3. **Bi-directional Updates**: Change common inputs in the subsheet and the master sheet will re-calculate automatically

<Info>
  As the UI develops, this usage section will be expanded with more detailed workflow examples.
</Info>

## Example: Portal Frame Analysis

Instead of manually generating the array of objects, map over each row in the `elemExport` table:

```javascript theme={null}
mapRows(exportTable, f(row) = { // 1-indexed 
  id: string(row[2]), // e.g. "LeftLeg" - must be unique
  name: string(row[2]),
  sheetTemplate: 'timberMember',
  capability: 'elementLinking', 
  preset: 'elementLink',
  commonInputs: ['member','n_{com}'],
  linkTo: 'link_table',
  linkFromIndex: row[1] // must be integer
})
```

## Best Practices

1. **Consistent IDs**: Use consistent IDs for dynamically sized arrays of subsheets to preserve user input data
2. **Avoid Cycles**: Ensure no cyclic dependencies between commonInputs and derived data
3. **Testing**: Thoroughly test dynamic sheet creation and deletion scenarios
4. **Documentation**: Document the relationship between master and subsheets clearly

<Warning>
  This feature is experimental. Monitor for any unexpected behavior and report issues to the development team.
</Warning>
