Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 152: Added string template cell #161

Merged
merged 11 commits into from
Feb 12, 2021
31 changes: 17 additions & 14 deletions packages/xlsx-renderer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,20 @@ It is possible to use the command line interface [read more about xlsx-renderer-
| - | [BaseCell](./src/cell/BaseCell.ts) | n/o | n/o | All Cell\`s definition classes extend it. | **abstract** |
| Content | [NormalCell](./src/cell/NormalCell.ts) | 1 | not started by `##`, `#!` or `#=` | This one copy all styles, width, properties and value form template. | **default** |
| Content | [VariableCell](./src/cell/VariableCell.ts) | 3 | `## pathToVariable` | Write variable from `ViewModel`. <br/> Paths to object's property or array item are allowed.<br/> When asking about undefined variable it returns empty string. | **Paths examples:** <br/> `simplePath` <br/> `someObject.property` <br/> `array.0.field` <br/> `items.1.path.to.object.prop`|
| Content | [HyperlinkCell](./src/cell/HyperlinkCell.ts) | 6 | `#! HYPERLINK pathToLabel pathToTarget` | Create a hyperlink. | *Paths resolve exactly same as VariableCell* |
| Content | [FormulaCell](./src/cell/FormulaCell.ts) | 5 | Cell.type eq. formulae | It handles correctly formulas inside and outside of loops - when rows were shifted compared to the template. | *It is used automatically when formulae from the template being rendered* <br/> [Example](./tests/integration/data/Renderer010-Formula)|
| Content | [TemplateFormulaCell](./src/cell/TemplateFormulaCell.ts) | 4 | Starts with `#= ` | This one allows you to put a template string into a cell as a formula. To write in a variable use `${pathToVariable}`. | **Example:**<br/> `#= ${summaryFormula}(A2:A${item.__endOutput.r})` gives something like `=MAX(A2:A2910)` <br/> [Example](./tests/integration/data/Renderer017-TemplateFormula)|
| Content | [TemplateStringCell](./src/cell/TemplateStringCell.ts) | 4 | Starts with <code>#` </code> | Template string allows you to create advanced text, for example concat two variables or put them into a sentence. To write in a variable use `${pathToVariable}`. | **Example:**<br/> <code>#` Hello ${name}! How are you?</code> gives for instance `Hello World! How are you?` <br/> [Example](./tests/integration/data/Renderer018-TemplateString)|
| Content | [HyperlinkCell](./src/cell/HyperlinkCell.ts) | 7 | `#! HYPERLINK pathToLabel pathToTarget` | Create a hyperlink. | *Paths resolve exactly same as VariableCell* |
| Content | [FormulaCell](./src/cell/FormulaCell.ts) | 6 | Cell.type eq. formulae | It handles correctly formulas inside and outside of loops - when rows were shifted compared to the template. | *It is used automatically when formulae from the template being rendered* <br/> [Example](./tests/integration/data/Renderer010-Formula)|
| Content | [TemplateFormulaCell](./src/cell/TemplateFormulaCell.ts) | 5 | Starts with `#= ` | This one allows you to put a template string (custom formula) into a cell as a formula. To write in a variable use `${pathToVariable}`. | **Example:**<br/> `#= ${summaryFormula}(A2:A${item.__endOutput.r})` gives something like `=MAX(A2:A2910)` <br/> [Example](./tests/integration/data/Renderer017-TemplateFormula)|
| Navigation | [EndRowCell](./src/cell/EndRowCell.ts) | 2 | `#! END_ROW` | Go to the beginning of next row | |
| Worksheet<br/>Navigation<br/>Loop | [FinishCell](./src/cell/FinishCell.ts) | 8 | `#! FINISH conditionPath` | Finish rendering for current worksheet and: <br/> 1) go to next worksheet if `conditionPath===true`<br/> 2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets <br/> 3) finished whole rendering when this worksheet is the last one. | **Examples:**<br/> `#! FINISHED` or `#! FINISHED itemFromLoop.__iterated` |
| Worksheet | [WsNameCell](./src/cell/WsNameCell.ts) | 14 | `#! WS_NAME pathToVariable` | Set worksheet's name. | **Examples:** <br/> `#! WS_NAME worksheetName` <br/> `#! WS_NAME item.title` <br/> `#! WS_NAME translatedNames.0` |
| Loop | [DumpColsCell](./src/cell/DumpColsCell.ts) | 11 | `#! DUMP_COLS pathToArray` | Useful for writing through multiple columns. It put each value of array to next column. | [Example](./tests/integration/data/Renderer011-DumpCols) |
| Loop | [ForEachCell](./src/cell/ForEachCell.ts) | 7 | #! FOR_EACH item items | Begin the loop named `item`, set the first element of `items` into `item` and go to the beginning of next line.| Connected to: `ContinueCell`, `EndLoopCell`, `DeleteCell`, `FinishedCell`, `SumCell`, `AverageCell`. |
| Loop | [ContinueCell](./src/cell/ContinueCell.ts) | 10 | `#! CONTINUE item` | Iterate to next element of loop named `item` (check `ForEachCell` for more information) and navigate to the beginning of new line. | |
| Loop | [EndLoopCell](./src/cell/EndLoopCell.ts) | 9 | `#! END_LOOP item` | Mark cell when the loop `item` finished. | |
| Aggregation| [SumCell](./src/cell/SumCell.ts) | 12 | `#! SUM item` | Write sum formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer007-ForEach-Sum) |
| Aggregation | [AverageCell](./src/cell/AverageCell.ts) | 13 | `#! AVERAGE item` | Write average formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer009-ForEach-Average) |
| View Model | [DeleteCell](./src/cell/DeleteCell.ts) | 15 | `#! DELETE pathToVariable` | Delete variable, useful for nested loops.| [Example](./tests/integration/data/Renderer009-ForEach-Average) |
| Worksheet<br/>Navigation<br/>Loop | [FinishCell](./src/cell/FinishCell.ts) | 9 | `#! FINISH conditionPath` | Finish rendering for current worksheet and: <br/> 1) go to next worksheet if `conditionPath===true`<br/> 2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets <br/> 3) finished whole rendering when this worksheet is the last one. | **Examples:**<br/> `#! FINISHED` or `#! FINISHED itemFromLoop.__iterated` |
| Worksheet | [WsNameCell](./src/cell/WsNameCell.ts) | 15 | `#! WS_NAME pathToVariable` | Set worksheet's name. | **Examples:** <br/> `#! WS_NAME worksheetName` <br/> `#! WS_NAME item.title` <br/> `#! WS_NAME translatedNames.0` |
| Loop | [DumpColsCell](./src/cell/DumpColsCell.ts) | 12 | `#! DUMP_COLS pathToArray` | Useful for writing through multiple columns. It put each value of array to next column. | [Example](./tests/integration/data/Renderer011-DumpCols) |
| Loop | [ForEachCell](./src/cell/ForEachCell.ts) | 8 | #! FOR_EACH item items | Begin the loop named `item`, set the first element of `items` into `item` and go to the beginning of next line.| Connected to: `ContinueCell`, `EndLoopCell`, `DeleteCell`, `FinishedCell`, `SumCell`, `AverageCell`. |
| Loop | [ContinueCell](./src/cell/ContinueCell.ts) | 11 | `#! CONTINUE item` | Iterate to next element of loop named `item` (check `ForEachCell` for more information) and navigate to the beginning of new line. | |
| Loop | [EndLoopCell](./src/cell/EndLoopCell.ts) | 10 | `#! END_LOOP item` | Mark cell when the loop `item` finished. | |
| Aggregation| [SumCell](./src/cell/SumCell.ts) | 13 | `#! SUM item` | Write sum formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer007-ForEach-Sum) |
| Aggregation | [AverageCell](./src/cell/AverageCell.ts) | 14 | `#! AVERAGE item` | Write average formulae for current column and the `item`'s rows. | [Example](./tests/integration/data/Renderer009-ForEach-Average) |
| View Model | [DeleteCell](./src/cell/DeleteCell.ts) | 16 | `#! DELETE pathToVariable` | Delete variable, useful for nested loops.| [Example](./tests/integration/data/Renderer009-ForEach-Average) |

## Examples

Expand Down Expand Up @@ -108,6 +109,8 @@ _These examples might be runned by using the command line tool, [read more](../x
| 15 | [ForEach-merged-two-tables](./tests/integration/data/Renderer015-ForEach-merged-two-tables) | Checks merged cells behaviour |
| 16 | [ForEach-merged-pyramid](./tests/integration/data/Renderer016-ForEach-merged-pyramid) | Checks merged cells behaviour |
| 17 | [TemplateFormulaCell](./tests/integration/data/Renderer017-TemplateFormulaCell) | Dynamic formula creation |
| 18 | [TemplateStringCell](./tests/integration/data/Renderer018-TemplateStringCell) | Dynamic content creation following by custom template string (`Hello ${name}`). |


## Support

Expand All @@ -123,9 +126,9 @@ We are ready to provide paid support, in order that please contact me: [hi@siemi

If Node v8 & v9 needed, please contact us [support@siemienik.pl](mailto://support@siemienik.pl).

## Browser Support
### ✅ Browser Support

XLSX Renderer may run on browser side, [read more how to do it](https://github.com/Siemienik/XToolSet/issues/93#issuecomment-732309383).
XLSX Renderer may run on browser side, [read more how to do it](https://github.com/Siemienik/XToolSet/issues/93).

---

Expand Down
2 changes: 2 additions & 0 deletions packages/xlsx-renderer/src/CellTemplatePool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Cell } from 'exceljs';
import { BaseCell, CellType } from './cell/BaseCell';
import { NormalCell } from './cell/NormalCell';
import { TemplateFormulaCell } from './cell/TemplateFormulaCell';
import { TemplateStringCell } from './cell/TemplateStringCell';
import { VariableCell } from './cell/VariableCell';
import { FinishCell } from './cell/FinishCell';
import { ForEachCell } from './cell/ForEachCell';
Expand All @@ -22,6 +23,7 @@ export class CellTemplatePool {
NormalCell,
EndRowCell,
VariableCell,
TemplateStringCell,
TemplateFormulaCell,
FormulaCell,
HyperlinkCell,
Expand Down
2 changes: 1 addition & 1 deletion packages/xlsx-renderer/src/cell/NormalCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class NormalCell extends BaseCell {
cell &&
cell.type === ValueType.String &&
typeof cell.value === 'string' &&
!['##', '#!', "#="].includes(cell.value.substring(0, 2))
!['##', '#!', "#=", '#`'].includes(cell.value.substring(0, 2)) // todo documentation: describe prefixes in a documentation
);
}

Expand Down
56 changes: 56 additions & 0 deletions packages/xlsx-renderer/src/cell/TemplateStringCell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { BaseCell } from './BaseCell';
import { Cell, ValueType } from 'exceljs';
import { Scope } from '../Scope';

const variableRegex = /\${[^{]+?}/g;

/**
* @description
* TemplateStringCellinterpolate string and put it into cell as a string value
* * starts width <pre>#` </pre>
*/
export class TemplateStringCell extends BaseCell {
public static match(cell: Cell): boolean {
return (
cell &&
cell.type === ValueType.String &&
(cell.isMerged ? cell.master.address === cell.address : true) &&
typeof cell.value === 'string' &&
cell.value.substring(0, 3) === '#` '
);
}

public apply(scope: Scope): TemplateStringCell {
super.apply(scope);

const template = scope
.getCurrentTemplateString()
.substring(3);

const result = template.replace(variableRegex, (match) => {
const path = match.slice(2, -1).split('.');

// todo refactoring extract, similar like in VariableCell
const value = path.reduce((p, c) => (typeof p === 'object' ? p[c] : p), scope.vm);
if (value === undefined && !scope.isFrozen()) {
// todo do it better (use logger or something like that)
// tslint:disable-next-line:no-console
console.warn(
`WARN: ${path} is undefined for template string output: ${
JSON.stringify(scope.outputCell)
} when template is:${
JSON.stringify(scope.templateCell)
}`
);
}

return value;
});

// todo refactoring: this is only one line which the logic is different as in TemplateFormulaCell, consider about refactoring
scope.setCurrentOutputValue(result);
scope.incrementCol();

return this;
}
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"author": {
"firstName": "Paweł",
"lastName": "Siemienik",
"hobby": "OSS development"
}
}