diff --git a/packages/xlsx-renderer/README.md b/packages/xlsx-renderer/README.md index e53fee1..95058a6 100644 --- a/packages/xlsx-renderer/README.md +++ b/packages/xlsx-renderer/README.md @@ -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`.
Paths to object's property or array item are allowed.
When asking about undefined variable it returns empty string. | **Paths examples:**
`simplePath`
`someObject.property`
`array.0.field`
`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*
[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:**
`#= ${summaryFormula}(A2:A${item.__endOutput.r})` gives something like `=MAX(A2:A2910)`
[Example](./tests/integration/data/Renderer017-TemplateFormula)| +| Content | [TemplateStringCell](./src/cell/TemplateStringCell.ts) | 4 | Starts with #` | 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:**
#` Hello ${name}! How are you? gives for instance `Hello World! How are you?`
[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*
[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:**
`#= ${summaryFormula}(A2:A${item.__endOutput.r})` gives something like `=MAX(A2:A2910)`
[Example](./tests/integration/data/Renderer017-TemplateFormula)| | Navigation | [EndRowCell](./src/cell/EndRowCell.ts) | 2 | `#! END_ROW` | Go to the beginning of next row | | -| Worksheet
Navigation
Loop | [FinishCell](./src/cell/FinishCell.ts) | 8 | `#! FINISH conditionPath` | Finish rendering for current worksheet and:
1) go to next worksheet if `conditionPath===true`
2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets
3) finished whole rendering when this worksheet is the last one. | **Examples:**
`#! FINISHED` or `#! FINISHED itemFromLoop.__iterated` | -| Worksheet | [WsNameCell](./src/cell/WsNameCell.ts) | 14 | `#! WS_NAME pathToVariable` | Set worksheet's name. | **Examples:**
`#! WS_NAME worksheetName`
`#! WS_NAME item.title`
`#! 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
Navigation
Loop | [FinishCell](./src/cell/FinishCell.ts) | 9 | `#! FINISH conditionPath` | Finish rendering for current worksheet and:
1) go to next worksheet if `conditionPath===true`
2) repeat this template worksheet again (`conditionPath === false`) - looping through worksheets
3) finished whole rendering when this worksheet is the last one. | **Examples:**
`#! FINISHED` or `#! FINISHED itemFromLoop.__iterated` | +| Worksheet | [WsNameCell](./src/cell/WsNameCell.ts) | 15 | `#! WS_NAME pathToVariable` | Set worksheet's name. | **Examples:**
`#! WS_NAME worksheetName`
`#! WS_NAME item.title`
`#! 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 @@ -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 @@ -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). --- diff --git a/packages/xlsx-renderer/src/CellTemplatePool.ts b/packages/xlsx-renderer/src/CellTemplatePool.ts index 55beefa..6bd88c4 100644 --- a/packages/xlsx-renderer/src/CellTemplatePool.ts +++ b/packages/xlsx-renderer/src/CellTemplatePool.ts @@ -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'; @@ -22,6 +23,7 @@ export class CellTemplatePool { NormalCell, EndRowCell, VariableCell, + TemplateStringCell, TemplateFormulaCell, FormulaCell, HyperlinkCell, diff --git a/packages/xlsx-renderer/src/cell/NormalCell.ts b/packages/xlsx-renderer/src/cell/NormalCell.ts index 34f54b3..5ac601d 100644 --- a/packages/xlsx-renderer/src/cell/NormalCell.ts +++ b/packages/xlsx-renderer/src/cell/NormalCell.ts @@ -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 ); } diff --git a/packages/xlsx-renderer/src/cell/TemplateStringCell.ts b/packages/xlsx-renderer/src/cell/TemplateStringCell.ts new file mode 100644 index 0000000..41435be --- /dev/null +++ b/packages/xlsx-renderer/src/cell/TemplateStringCell.ts @@ -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
#` 
+ */ +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; + } +} diff --git a/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/expected.xlsx b/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/expected.xlsx new file mode 100644 index 0000000..553e090 Binary files /dev/null and b/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/expected.xlsx differ diff --git a/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/template.xlsx b/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/template.xlsx new file mode 100644 index 0000000..888c071 Binary files /dev/null and b/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/template.xlsx differ diff --git a/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/viewModel.json b/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/viewModel.json new file mode 100644 index 0000000..942d505 --- /dev/null +++ b/packages/xlsx-renderer/tests/integration/data/Renderer018-TemplateString/viewModel.json @@ -0,0 +1,7 @@ +{ + "author": { + "firstName": "Paweł", + "lastName": "Siemienik", + "hobby": "OSS development" + } +}