diff --git a/.gitignore b/.gitignore index 170d2b71..217f87c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .vscode/ coverage/ -lib/ +dist/ node_modules/ example.spec.ts diff --git a/.travis.yml b/.travis.yml index 58dff587..7b104b4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,18 +1,18 @@ language: node_js node_js: - - "10" - - "12" + - '10' + - '12' before_install: - npm install codecov -g after_success: - - npm run coverage | codecov + - npm run test:coverage | codecov deploy: provider: npm - email: "$NPM_EMAIL" - api_key: "$NPM_TOKEN" + email: '$NPM_EMAIL' + api_key: '$NPM_TOKEN' on: tags: true skip_cleanup: true diff --git a/CHANGELOG.md b/CHANGELOG.md index c3bf3a8f..d53314c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added +- **list:** allow styling of bullet lists - **list:** implement sublists ### Changed - **list:** text content of a list item cannot be styled, closes [#67](https://github.com/connium/simple-odf/issues/67) - **chore(lint):** replace tslint with eslint -- **chore(travis):** add Node.js v12 to travis configuration +- **chore(travis):** add Node.js 12 to travis configuration - **chore:** drop support for Node.js 8 - **chore(lint):** replace standard with prettier diff --git a/docs/API.md b/docs/API.md index 01ba488f..cc7ab199 100644 --- a/docs/API.md +++ b/docs/API.md @@ -23,11 +23,20 @@
TextDocument

This class represents a text document in OpenDocument format.

+
BulletListLevelStyle
+

This class represents a list style where list items are preceded by bullets.

+
FontFace

This class represents a font face declaration.

It is used to describe the characteristics of a font which is used in the document. The unique name of a font can be used inside styles to select a font face declaration.

+
ListStyle
+

This class represents a list style.

+

List styles are used to specify the formatting of a list and its items. +A list style contains a set of style elements for each list level (@see ListLevelStyle). +If a list style is applied to a list but does not contain a list level specification for a specific level, the list level style of the next lower level is used.

+
Style

This class represents a style.

It is used to specify the formatting of a document or a portion of a document. @@ -852,6 +861,7 @@ It is used to manage the named styles that are used in the document. * [CommonStyles](#CommonStyles) * [`new CommonStyles()`](#new_CommonStyles_new) + * [`.createListStyle(name)`](#CommonStyles+createListStyle) ⇒ [ListStyle](#ListStyle) * [`.createParagraphStyle(name)`](#CommonStyles+createParagraphStyle) ⇒ ParagraphStyle * [`.getName(displayName)`](#CommonStyles+getName) ⇒ string \| undefined * [`.getAll()`](#CommonStyles+getAll) @@ -871,6 +881,28 @@ const commonStyles = new CommonStyles(); * * * + + +### `commonStyles.createListStyle(name)` ⇒ [ListStyle](#ListStyle) +The `createListStyle()` method creates a new `ListStyle` instance with the given name. +If a style with this name already exists, the existing style will be returned. + +#### Parameters +- name string +The unique name for the style + +**Return value** +[ListStyle](#ListStyle) - A new `ListStyle` instance with the specified name or an existing style, if one with the specified name exists + +**Example** +```js +const commonStyles = new CommonStyles(); +commonStyles.createListStyle('Contents'); +``` +**Since**: 0.11.0 + +* * * + ### `commonStyles.createParagraphStyle(name)` ⇒ ParagraphStyle @@ -1079,6 +1111,11 @@ Adds an empty list at the end of the document. **Return value** [List](#List) - The newly added list +**Example** +```js +new TextBody() + .addList(); +``` **Since**: 0.7.0 * * * @@ -1233,6 +1270,230 @@ Returns the string representation of this document in flat open document xml for * * * + + +## BulletListLevelStyle +This class represents a list style where list items are preceded by bullets. + +**Since**: 0.11.0 + +* [BulletListLevelStyle](#BulletListLevelStyle) + * [`new BulletListLevelStyle(level)`](#new_BulletListLevelStyle_new) + * [`.getBulletChar()`](#BulletListLevelStyle+getBulletChar) ⇒ string + * [`.setBulletChar(bulletChar)`](#BulletListLevelStyle+setBulletChar) ⇒ [BulletListLevelStyle](#BulletListLevelStyle) + * [`.getLevel()`](#BulletListLevelStyle+getLevel) ⇒ number + * [`.getNumberPrefix()`](#BulletListLevelStyle+getNumberPrefix) ⇒ string \| undefined + * [`.setNumberPrefix(prefix)`](#BulletListLevelStyle+setNumberPrefix) ⇒ [BulletListLevelStyle](#BulletListLevelStyle) + * [`.getNumberSuffix()`](#BulletListLevelStyle+getNumberSuffix) ⇒ string \| undefined + * [`.setNumberSuffix(suffix)`](#BulletListLevelStyle+setNumberSuffix) ⇒ [BulletListLevelStyle](#BulletListLevelStyle) + * [`.getRelativeBulletSize()`](#BulletListLevelStyle+getRelativeBulletSize) ⇒ string \| undefined + * [`.setRelativeBulletSize(relativeSize)`](#BulletListLevelStyle+setRelativeBulletSize) ⇒ [BulletListLevelStyle](#BulletListLevelStyle) + + +* * * + + + +### `new BulletListLevelStyle(level)` +Creates a `BulletListLevelStyle` instance that represents a list style where list items are preceded by bullets. + +#### Parameters +- level number +The level of the list style, starting with `1` + +**Example** +```js +const style = new BulletListLevelStyle(3); +``` + +* * * + + + +### `bulletListLevelStyle.getBulletChar()` ⇒ string +The `getBulletChar()` method returns the character to use as the bullet. + +**Return value** +string - The character to use as the bullet + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.getBulletChar(); // '\u2022' +style.setBulletChar('~'); +style.getBulletChar(); // '~' +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.setBulletChar(bulletChar)` ⇒ [BulletListLevelStyle](#BulletListLevelStyle) +The `setBulletChar()` method sets the character to use as the bullet. + +If an illegal value is provided, the value will be ignored. + +#### Parameters +- bulletChar string +The character to use as the bullet + +**Return value** +[BulletListLevelStyle](#BulletListLevelStyle) - The `BulletListLevelStyle` object + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.setBulletChar('~'); // '~' +style.setBulletChar(''); // '~' +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.getLevel()` ⇒ number +The `getLevel()` method returns the level of the list style. + +**Return value** +number - The level of the list style, starting with `1` + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.getLevel(); // 3 +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.getNumberPrefix()` ⇒ string \| undefined +The `getNumberPrefix()` method returns the character to display before a bullet. + +**Return value** +string \| undefined - The character to display before a bullet or `undefined` if no prefix is set + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.getNumberPrefix(); // undefined +style.setNumberPrefix('~'); +style.getNumberPrefix(); // '~' +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.setNumberPrefix(prefix)` ⇒ [BulletListLevelStyle](#BulletListLevelStyle) +The `setNumberPrefix()` method sets the character to display before a bullet. + +#### Parameters +- prefix string | undefined +The character to display before a bullet or `undefined` to unset the prefix + +**Return value** +[BulletListLevelStyle](#BulletListLevelStyle) - The `BulletListLevelStyle` object + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.setNumberPrefix('~'); // '~' +style.setNumberPrefix(undefined); // undefined +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.getNumberSuffix()` ⇒ string \| undefined +The `getNumberSuffix()` method returns the character to display after a bullet. + +**Return value** +string \| undefined - The character to display after a bullet or `undefined` if no suffix is set + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.getNumberSuffix(); // undefined +style.setNumberSuffix('~'); +style.getNumberSuffix(); // '~' +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.setNumberSuffix(suffix)` ⇒ [BulletListLevelStyle](#BulletListLevelStyle) +The `setNumberSuffix()` method sets the character to display after a bullet. + +#### Parameters +- suffix string | undefined +The character to display after a bullet or `undefined` to unset the suffix + +**Return value** +[BulletListLevelStyle](#BulletListLevelStyle) - The `BulletListLevelStyle` object + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.setNumberSuffix('~'); // '~' +style.setNumberSuffix(undefined); // undefined +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.getRelativeBulletSize()` ⇒ string \| undefined +The `getRelativeBulletSize()` method returns the percentage value for the bullet size relative to the font size of the paragraphs in the bullet list. + +**Return value** +string \| undefined - The percentage value for the bullet size or `undefined` if no relative bullet size is set + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.getRelativeBulletSize(); // undefined +style.setRelativeBulletSize('23%'); +style.getRelativeBulletSize(); // '23%' +``` +**Since**: 0.11.0 + +* * * + + + +### `bulletListLevelStyle.setRelativeBulletSize(relativeSize)` ⇒ [BulletListLevelStyle](#BulletListLevelStyle) +The `setNumberSuffix()` method sets the percentage value for the bullet size relative to the font size of the paragraphs in the bullet list. + +If an illegal value is provided, the value will be ignored. + +#### Parameters +- relativeSize string | undefined +The percentage value for the bullet size or `undefined` to unset the bullet size + +**Return value** +[BulletListLevelStyle](#BulletListLevelStyle) - The `BulletListLevelStyle` object + +**Example** +```js +const style = new BulletListLevelStyle(3); +style.setRelativeBulletSize('23%'); // '23%' +style.setRelativeBulletSize('42px'); // '23%' +style.setRelativeBulletSize(undefined); // undefined +``` +**Since**: 0.11.0 + +* * * + ## FontFace @@ -1467,6 +1728,175 @@ font.getName(); // 'FreeSans' * * * + + +## ListStyle +This class represents a list style. + +List styles are used to specify the formatting of a list and its items. +A list style contains a set of style elements for each list level (@see ListLevelStyle). +If a list style is applied to a list but does not contain a list level specification for a specific level, the list level style of the next lower level is used. + +**Since**: 0.11.0 + +* [ListStyle](#ListStyle) + * [`new ListStyle(displayName)`](#new_ListStyle_new) + * [`.getConsecutiveNumbering()`](#ListStyle+getConsecutiveNumbering) ⇒ boolean + * [`.setConsecutiveNumbering(consecutiveNumbering)`](#ListStyle+setConsecutiveNumbering) ⇒ [ListStyle](#ListStyle) + * [`.createBulletListLevelStyle(level)`](#ListStyle+createBulletListLevelStyle) ⇒ [BulletListLevelStyle](#BulletListLevelStyle) + * [`.getListLevelStyle(level)`](#ListStyle+getListLevelStyle) ⇒ [BulletListLevelStyle](#BulletListLevelStyle) \| undefined + * [`.getListLevelStyles()`](#ListStyle+getListLevelStyles) ⇒ [Array.<BulletListLevelStyle>](#BulletListLevelStyle) + * [`.removeListLevelStyle(level)`](#ListStyle+removeListLevelStyle) ⇒ [ListStyle](#ListStyle) + + +* * * + + + +### `new ListStyle(displayName)` +Creates a `ListStyle` instance that represents the formatting of a list. + +#### Parameters +- displayName string +The unique display name for the style + +**Example** +```js +const style = new ListStyle('Contents'); +``` + +* * * + + + +### `listStyle.getConsecutiveNumbering()` ⇒ boolean +The `getConsecutiveNumbering()` method returns whether the style uses consecutive numbering for all list levels or whether each list level restarts the numbering. + +**Return value** +boolean - `true` if consecutive numbering is used for all list levels or `false` if each list level restarts numbering + +**Example** +```js +const style = new ListStyle('Contents'); +style.getConsecutiveNumbering(); // false +style.setConsecutiveNumbering(true); +style.getConsecutiveNumbering(); // true +``` +**Since**: 0.11.0 + +* * * + + + +### `listStyle.setConsecutiveNumbering(consecutiveNumbering)` ⇒ [ListStyle](#ListStyle) +The `setConsecutiveNumbering()` method sets returns whether the style uses consecutive numbering for all list levels or whether each list level restarts the numbering. + +#### Parameters +- consecutiveNumbering boolean +`true` if consecutive numbering is used for all list levels or `false` if each list level restarts numbering + +**Return value** +[ListStyle](#ListStyle) - The `ListStyle` object + +**Example** +```js +const style = new ListStyle('Contents'); +style.setConsecutiveNumbering(true); // true +``` +**Since**: 0.11.0 + +* * * + + + +### `listStyle.createBulletListLevelStyle(level)` ⇒ [BulletListLevelStyle](#BulletListLevelStyle) +The `createBulletListLevelStyle()` method creates a new `BulletListLevelStyle` instance for the given list level. +If a list level style for this level already exists, the existing style will be overwritten. + +#### Parameters +- level number +The level of the list style, starting with `1` + +**Return value** +[BulletListLevelStyle](#BulletListLevelStyle) - A new `BulletListLevelStyle` instance with the specified level + +**Throws**: + +- Error if the given list level is invalid + +**Example** +```js +const style = new ListStyle('Contents'); +style.createBulletListLevelStyle(3); +``` +**Since**: 0.11.0 + +* * * + + + +### `listStyle.getListLevelStyle(level)` ⇒ [BulletListLevelStyle](#BulletListLevelStyle) \| undefined +The `getListLevelStyle()` method returns the list level style for the given list level. +If a list level style for this level already exists, the existing style will be overwritten. + +#### Parameters +- level number +The level of the list style, starting with `1` + +**Return value** +[BulletListLevelStyle](#BulletListLevelStyle) \| undefined - The list level style for the specified level or `undefined` if no list level style is defined for the specified level + +**Example** +```js +const style = new ListStyle('Contents'); +style.getListLevelStyle(3); +``` +**Since**: 0.11.0 + +* * * + + + +### `listStyle.getListLevelStyles()` ⇒ [Array.<BulletListLevelStyle>](#BulletListLevelStyle) +The `getListLevelStyles()` method returns a new `Array` object that contains all list level styles of a list style. + +**Return value** +[Array.<BulletListLevelStyle>](#BulletListLevelStyle) - A new `Array` object that contains the list level styles of a list style + +**Example** +```js +const style = new ListStyle('Contents'); +style.createBulletListLevelStyle(1); +style.createBulletListLevelStyle(2); +styles.getListLevelStyles(); +``` +**Since**: 0.11.0 + +* * * + + + +### `listStyle.removeListLevelStyle(level)` ⇒ [ListStyle](#ListStyle) +The `removeListLevelStyle()` method removes the list level style for the given list level. + +#### Parameters +- level number +The level of the list style, starting with `1` + +**Return value** +[ListStyle](#ListStyle) - The `ListStyle` object + +**Example** +```js +const style = new ListStyle('Contents'); +style.createBulletListLevelStyle(3); +style.removeListLevelStyle(3); +styles.getListLevelStyles(); // [] +``` +**Since**: 0.11.0 + +* * * + ## Style @@ -2221,10 +2651,14 @@ This class represents a list and may contain any number list items. * [List](#List) * [`new List()`](#new_List_new) * [`.addItem([item])`](#List+addItem) ⇒ [ListItem](#ListItem) - * [`.insertItem(position, item)`](#List+insertItem) ⇒ [ListItem](#ListItem) * [`.getItem(position)`](#List+getItem) ⇒ [ListItem](#ListItem) \| undefined * [`.getItems()`](#List+getItems) ⇒ [Array.<ListItem>](#ListItem) + * [`.insertItem(position, item)`](#List+insertItem) ⇒ [ListItem](#ListItem) * [`.removeItemAt(position)`](#List+removeItemAt) ⇒ [ListItem](#ListItem) \| undefined + * [`.getStyle()`](#List+getStyle) ⇒ [ListStyle](#ListStyle) \| undefined + * [`.setStyle(style)`](#List+setStyle) ⇒ [List](#List) + * [`.getStyleName()`](#List+getStyleName) ⇒ string \| undefined + * [`.setStyleName(styleName)`](#List+setStyleName) ⇒ [List](#List) * [`.clear()`](#List+clear) ⇒ [List](#List) * [`.size()`](#List+size) ⇒ number @@ -2246,11 +2680,11 @@ new List(); ### `list.addItem([item])` ⇒ [ListItem](#ListItem) -The `addItem()` method adds a new list item with the specified text or adds the specified item to the list. +The `addItem()` method adds a new list item or adds the specified item to the list. #### Parameters -- [item] string | [ListItem](#ListItem) -The text content of the new item or the item to add +- [item] [ListItem](#ListItem) +The item to add **Return value** [ListItem](#ListItem) - The added `ListItem` object @@ -2258,8 +2692,54 @@ The text content of the new item or the item to add **Example** ```js const list = new List(); -list.addItem('First item'); -list.addItem(new ListItem('Second item')); +list.addItem(); +list.addItem(new ListItem()); +``` +**Since**: 0.2.0 + +* * * + + + +### `list.getItem(position)` ⇒ [ListItem](#ListItem) \| undefined +The `getItem()` method returns the item at the specified position in the list. +If an invalid position is given, undefined is returned. + +#### Parameters +- position number +The index of the requested list item (starting from 0). + +**Return value** +[ListItem](#ListItem) \| undefined - The `ListItem` object at the specified position +or `undefined` if there is no list item at the specified position + +**Example** +```js +const list = new List(); +list.addItem(); +list.addItem(); +list.getItem(1); // second item +list.getItem(2); // undefined +``` +**Since**: 0.2.0 + +* * * + + + +### `list.getItems()` ⇒ [Array.<ListItem>](#ListItem) +The `getItems()` method returns all list items. + +**Return value** +[Array.<ListItem>](#ListItem) - A copy of the list of `ListItem` objects + +**Example** +```js +const list = new List(); +list.getItems(); // [] +list.addItem(); +list.addItem(); +list.getItems(); // [first item, second item] ``` **Since**: 0.2.0 @@ -2268,18 +2748,17 @@ list.addItem(new ListItem('Second item')); ### `list.insertItem(position, item)` ⇒ [ListItem](#ListItem) -The `insertItem` method inserts a new list item with the specified text -or inserts the specified item at the specified position. +The `insertItem` method inserts the specified item at the specified position. The item is inserted before the item at the specified position. -If the position is greater than the current number items, the new item is appended at the end of the list. +If the position is greater than the current number of items, the new item is appended at the end of the list. If the position is negative, the new item is inserted as first element. #### Parameters - position number The index at which to insert the list item (starting from 0). -- item string | [ListItem](#ListItem) -The text content of the new item or the item to insert +- item [ListItem](#ListItem) +The item to insert **Return value** [ListItem](#ListItem) - The inserted `ListItem` object @@ -2287,83 +2766,122 @@ The text content of the new item or the item to insert **Example** ```js const list = new List(); -list.addItem('First item'); // 'First item' -list.addItem('Second item'); // 'First item', 'Second item' -list.insertItem(1, 'After first item'); // 'First item', 'After first item', 'Second item' +list.addItem(); +list.insertItem(0, new ListItem()); // insert before existing item ``` **Since**: 0.2.0 * * * - + -### `list.getItem(position)` ⇒ [ListItem](#ListItem) \| undefined -The `getItem()` method returns the item at the specified position in the list. -If an invalid position is given, undefined is returned. +### `list.removeItemAt(position)` ⇒ [ListItem](#ListItem) \| undefined +The `removeItemAt()` method removes the list item from the specified position. #### Parameters - position number -The index of the requested list item (starting from 0). +The index of the list item to remove (starting from 0). **Return value** -[ListItem](#ListItem) \| undefined - The `ListItem` object at the specified position -or `undefined` if there is no list item at the specified position +[ListItem](#ListItem) \| undefined - The removed `ListItem` object +or undefined if there is no list item at the specified position **Example** ```js const list = new List(); -list.addItem('First item'); -list.addItem('Second item'); -list.getItem(1); // 'Second item' -list.getItem(2); // undefined +list.addItem(); +list.addItem(); +list.removeItemAt(0); // first item +list.getItems(); // [second item] +list.removeItemAt(2); // undefined ``` **Since**: 0.2.0 * * * - + -### `list.getItems()` ⇒ [Array.<ListItem>](#ListItem) -The `getItems()` method returns all list items. +### `list.getStyle()` ⇒ [ListStyle](#ListStyle) \| undefined +Returns the style of the list. **Return value** -[Array.<ListItem>](#ListItem) - A copy of the list of `ListItem` objects +[ListStyle](#ListStyle) \| undefined - The style of the list or `undefined` if no style was set **Example** ```js const list = new List(); -list.getItems(); // [] -list.addItem('First item'); -list.addItem('Second item'); -list.getItems(); // ['First item', 'Second item'] +list.getStyle(); // undefined +list.setStyle(new ListStyle()); +list.getStyle(); // previously set style ``` -**Since**: 0.2.0 +**Since**: 0.11.0 * * * - + -### `list.removeItemAt(position)` ⇒ [ListItem](#ListItem) \| undefined -The `removeItemAt()` method removes the list item from the specified position. +### `list.setStyle(style)` ⇒ [List](#List) +Sets the new style of the list. +To reset the style, `undefined` must be given. + +If style and style name are both set, the custom style will be set and the common style will be ignored. #### Parameters -- position number -The index of the list item to remove (starting from 0). +- style [ListStyle](#ListStyle) | undefined +The new style or `undefined` to reset the style **Return value** -[ListItem](#ListItem) \| undefined - The removed `ListItem` object -or undefined if there is no list item at the specified position +[List](#List) - The `List` object + +**Example** +```js +new List() + .setStyle(new ListStyle()); +``` +**Since**: 0.11.0 + +* * * + + + +### `list.getStyleName()` ⇒ string \| undefined +Returns the name of the common style of the list. + +**Return value** +string \| undefined - The name of the common style or `undefined` if no common style was set **Example** ```js const list = new List(); -list.addItem('First item'); -list.addItem('Second item'); -list.removeItemAt(0); // 'First item' -list.getItems(); // ['Second item'] -list.removeItemAt(2); // undefined +list.getStyleName(); // undefined +list.setStyleName('Summary'); +list.getStyleName(); // 'Summary' ``` -**Since**: 0.2.0 +**Since**: 0.11.0 + +* * * + + + +### `list.setStyleName(styleName)` ⇒ [List](#List) +Sets the name of the common style that should be applied to the list. +To reset the common style, `undefined` must be given. + +If style and style name are both set, the custom style will be set and the common style will be ignored. + +#### Parameters +- styleName string | undefined +The name of the common style or `undefined` to reset the common style + +**Return value** +[List](#List) - The `List` object + +**Example** +```js +new List() + .setStyleName('Summary'); +``` +**Since**: 0.11.0 * * * @@ -2378,9 +2896,10 @@ The `clear()` method removes all items from the list. **Example** ```js const list = new List(); -list.addItem('First item'); // 'First item' -list.addItem('Second item'); // 'First item', 'Second item' -list.clear(); // - +list.addItem(); +list.addItem(); +list.clear(); +list.getItems(); // [] ``` **Since**: 0.2.0 @@ -2397,10 +2916,10 @@ The `size()` method returns the number of items in the list. **Example** ```js const list = new List(); -list.size(); // 0 -list.addItem('First item'); -list.addItem('Second item'); -list.size(); // 2 +list.size(); // 0 +list.addItem(); +list.addItem(); +list.size(); // 2 ``` **Since**: 0.2.0 @@ -2413,21 +2932,77 @@ This class represents an item in a list. **Since**: 0.2.0 +* [ListItem](#ListItem) + * [`new ListItem()`](#new_ListItem_new) + * [`.addHeading([text], [level])`](#ListItem+addHeading) ⇒ [Heading](#Heading) + * [`.addList()`](#ListItem+addList) ⇒ [List](#List) + * [`.addParagraph([text])`](#ListItem+addParagraph) ⇒ [Paragraph](#Paragraph) + + * * * -### `new ListItem([text])` +### `new ListItem()` Creates a `ListItem` instance that represents an item in a list. +**Example** +```js +new ListItem(); +``` + +* * * + + + +### `listItem.addHeading([text], [level])` ⇒ [Heading](#Heading) +Adds a heading at the end of the list item. +If a text is given, this will be set as text content of the heading. + #### Parameters -- [text] string = "''" -The text content of the list item; defaults to an empty string if omitted +- [text] string +The text content of the heading +- [level] number = 1 +The heading level; defaults to 1 if omitted + +**Return value** +[Heading](#Heading) - The newly added heading + +**Since**: 0.11.0 + +* * * + + + +### `listItem.addList()` ⇒ [List](#List) +Adds an empty list at the end of the list item. + +**Return value** +[List](#List) - The newly added list **Example** ```js -new ListItem('First item'); +new ListItem() + .addList(); ``` +**Since**: 0.11.0 + +* * * + + + +### `listItem.addParagraph([text])` ⇒ [Paragraph](#Paragraph) +Adds a paragraph at the end of the list item. +If a text is given, this will be set as text content of the paragraph. + +#### Parameters +- [text] string +The text content of the paragraph + +**Return value** +[Paragraph](#Paragraph) - The newly added paragraph + +**Since**: 0.11.0 * * * diff --git a/docs/Features.md b/docs/Features.md index 63dd36f1..2483646c 100644 --- a/docs/Features.md +++ b/docs/Features.md @@ -10,91 +10,95 @@ ## Style ### Character + - Font - - :heavy_check_mark: Family - - :heavy_check_mark: Style - - :heavy_check_mark: Size - - :x: Language + - ✔️ Family + - ✔️ Style + - ✔️ Size + - ❌ Language - Font effects - - :heavy_check_mark: Font color - - :heavy_check_mark: Effects - - :x: Relief - - :x: Overlining - - :x: Strikethrough - - :x: Underlining + - ✔️ Font color + - ✔️ Effects + - ❌ Relief + - ❌ Overlining + - ❌ Strikethrough + - ❌ Underlining - Position - - :x: Position - - :x: Rotation & scaling - - :x: Spacing + - ❌ Position + - ❌ Rotation & scaling + - ❌ Spacing - Hyperlink - - :x: Hyperlink - - :x: Events - - :x: Character Styles + - ❌ Hyperlink + - ❌ Events + - ❌ Character Styles - Highlighting - - :x: Color + - ❌ Color - Borders - - :x: Line arrangement - - :x: Line - - :x: Spacing to contents - - :x: Shadow style + - ❌ Line arrangement + - ❌ Line + - ❌ Spacing to contents + - ❌ Shadow style ### Paragraph + - Indents & spacing - - :heavy_check_mark: Indent - - :heavy_check_mark: Spacing - - :heavy_check_mark: Line spacing + - ✔️ Indent + - ✔️ Spacing + - ✔️ Line spacing - Alignment - - :heavy_check_mark: horizontal - - :heavy_check_mark: vertical + - ✔️ horizontal + - ✔️ vertical - Text flow - - :x: Hyphenation - - :heavy_check_mark: Breaks - - :heavy_check_mark: Break control + - ❌ Hyphenation + - ✔️ Breaks + - ✔️ Break control - Outline & numbering - - :x: Outline - - :x: Numbering - - :x: Line numbering + - ❌ Outline + - ❌ Numbering + - ❌ Line numbering - Tabs - - :heavy_check_mark: Type - - :construction: Fill character + - ✔️ Type + - ❌ Fill character - Drop caps - Borders - - :construction: Line arrangement - - :construction: Line - - :heavy_check_mark: Padding - - :x: Shadow style + - ❌ Line arrangement + - ❌ Line + - ✔️ Padding + - ❌ Shadow style - Background - - :heavy_check_mark: Color - - :x: Gradient - - :x: Hatching - - :x: Bitmap + - ✔️ Color + - ❌ Gradient + - ❌ Hatching + - ❌ Bitmap ## Text content -- :heavy_check_mark: Heading -- :heavy_check_mark: Paragraph - - :heavy_check_mark: tab - - :heavy_check_mark: line break - - :x: hyphen - - :x: span - - :construction: hyperlink - - :x: number -- :construction: List -- :x: Section -- :x: Change Tracking -- :x: Bookmark & Reference -- :x: Note -- :x: Text field -- :x: Table -- :x: Graphic content - - :x: Drawing shape - - :x: Frame - - :x: 3D Shape -- :x: Chart -- :x: Form +- ✔️ Heading +- ✔️ Paragraph + - ✔️ tab + - ✔️ line break + - ❌ hyphen + - ❌ span + - ❌ hyperlink + - ❌ number +- ✔️ List + - ❌ List header + - ✔️ List item +- ❌ Section +- ❌ Change Tracking +- ❌ Bookmark & Reference +- ❌ Note +- ❌ Text field +- ❌ Table +- ❌ Graphic content + - ❌ Drawing shape + - ❌ Frame + - ❌ 3D Shape +- ❌ Chart +- ❌ Form diff --git a/jest.config.js b/jest.config.js index 4a5b465e..26112d7b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,5 @@ module.exports = { + collectCoverage: true, preset: 'ts-jest', testEnvironment: 'node', }; diff --git a/package.json b/package.json index bac9c632..c9edbd9f 100644 --- a/package.json +++ b/package.json @@ -26,14 +26,14 @@ "Cain Watson" ], "files": [ - "lib", + "dist", "package.json", "CHANGELOG.md", "LICENSE", "README.md" ], - "main": "./lib/index.js", - "types": "./lib/index.d.ts", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", "scripts": { "build": "tsc -p .", "preversion": "npm test", @@ -41,13 +41,12 @@ "prepublishOnly": "npm run build", "pretest": "npm run build", "posttest": "npm run lint", - "test": "jest", - "test:watch": "jest --watch", + "test": "jest --clearCache && jest", + "test:watch": "jest --clearCache && jest --watch", "test:coverage": "jest --coverage", - "coverage": "jest --coverage", "lint": "eslint './{examples,src,test}/**/*.{ts,tsx}'", "format": "prettier --write './{examples,src,test}/**/*.{ts,js,json,md}'", - "docs": "rm -r ./lib && tsc && jsdoc2md --name-format --param-list-format list --separators --partial ./tools/jsdoc2md/body.hbs ./tools/jsdoc2md/params-list.hbs ./tools/jsdoc2md/returns.hbs ./tools/jsdoc2md/scope.hbs --files ./lib/api/**/*.js > ./docs/API.md" + "docs": "rm -r ./dist && tsc && jsdoc2md --name-format --param-list-format list --separators --partial ./tools/jsdoc2md/body.hbs ./tools/jsdoc2md/params-list.hbs ./tools/jsdoc2md/returns.hbs ./tools/jsdoc2md/scope.hbs --files ./dist/api/**/*.js > ./docs/API.md" }, "dependencies": { "xmldom": "^0.3.0" diff --git a/src/api/meta/Meta.spec.ts b/src/api/meta/Meta.spec.ts index faa65fcf..1f69c844 100644 --- a/src/api/meta/Meta.spec.ts +++ b/src/api/meta/Meta.spec.ts @@ -58,7 +58,6 @@ describe(Meta.name, () => { }); it('return previous set date', () => { - console.log({ testDate }); meta.setDate(testDate); expect(meta.getDate()).toBe(testDate); diff --git a/src/api/office/AutomaticStyles.spec.ts b/src/api/office/AutomaticStyles.spec.ts index f2c9d954..aaed959a 100644 --- a/src/api/office/AutomaticStyles.spec.ts +++ b/src/api/office/AutomaticStyles.spec.ts @@ -1,48 +1,74 @@ -import { ParagraphStyle, TabStopType } from '../style'; +import { ListStyle, ParagraphStyle, TabStopType } from '../style'; import { AutomaticStyles } from './AutomaticStyles'; describe(AutomaticStyles.name, () => { let automaticStyles: AutomaticStyles; - let testStyle1: ParagraphStyle; - let testStyle2: ParagraphStyle; + let listStyle: ListStyle; + let paragraphStyle1: ParagraphStyle; + let paragraphStyle2: ParagraphStyle; beforeEach(() => { - testStyle1 = new ParagraphStyle(); - testStyle2 = new ParagraphStyle().setFontSize(23); - testStyle2.addTabStop(42, TabStopType.Right); + listStyle = new ListStyle(); + paragraphStyle1 = new ParagraphStyle(); + paragraphStyle2 = new ParagraphStyle().setFontSize(23); + paragraphStyle2.addTabStop(42, TabStopType.Right); automaticStyles = new AutomaticStyles(); }); - describe('styles', () => { - it('add similar styles only once', () => { - automaticStyles.add(testStyle1); - automaticStyles.add(new ParagraphStyle()); - const styles = automaticStyles.getAll(); + it('should add a list style and name it L1', () => { + automaticStyles.add(listStyle); - expect(styles.length).toBe(1); - expect(styles[0]).toEqual(testStyle1); + const styles = automaticStyles.getAll(); + expect(styles.length).toBe(1); + expect(automaticStyles.getName(listStyle)).toBe('L1'); + }); + + it('should add different list styles', () => { + const listStyle2 = new ListStyle(); + listStyle2.setConsecutiveNumbering(true).createBulletListLevelStyle(1); + + automaticStyles.add(listStyle); + automaticStyles.add(listStyle2); + + const styles = automaticStyles.getAll(); + expect(styles.length).toBe(2); + expect(automaticStyles.getName(listStyle)).toBe('L1'); + expect(automaticStyles.getName(listStyle2)).toBe('L2'); + }); - expect(automaticStyles.getName(testStyle1)).toBe('P1'); - }); + it('should add a paragraph style and name it P1', () => { + automaticStyles.add(paragraphStyle1); - it('add different styles', () => { - automaticStyles.add(testStyle1); - automaticStyles.add(testStyle2); - const styles = automaticStyles.getAll(); + const styles = automaticStyles.getAll(); + expect(styles.length).toBe(1); + expect(automaticStyles.getName(paragraphStyle1)).toBe('P1'); + }); + + it('should add different paragraph styles', () => { + automaticStyles.add(paragraphStyle1); + automaticStyles.add(paragraphStyle2); - expect(styles.length).toBe(2); + const styles = automaticStyles.getAll(); + expect(styles.length).toBe(2); + expect(automaticStyles.getName(paragraphStyle1)).toBe('P1'); + expect(automaticStyles.getName(paragraphStyle2)).toBe('P2'); + }); - expect(automaticStyles.getName(testStyle1)).toBe('P1'); - expect(automaticStyles.getName(testStyle2)).toBe('P2'); - }); + it('should add similar styles only once', () => { + automaticStyles.add(paragraphStyle1); + automaticStyles.add(new ParagraphStyle()); + + const styles = automaticStyles.getAll(); + expect(styles.length).toBe(1); + expect(styles[0]).toEqual(paragraphStyle1); + }); - it('throw if the name of an unknown style is requested', () => { - automaticStyles.add(testStyle1); + it('should throw if the name of an unknown style is requested', () => { + automaticStyles.add(paragraphStyle1); - expect(() => { - automaticStyles.getName(testStyle2); - }).toThrow(); - }); + expect(() => { + automaticStyles.getName(paragraphStyle2); + }).toThrow(); }); }); diff --git a/src/api/office/AutomaticStyles.ts b/src/api/office/AutomaticStyles.ts index 02c0393f..455ae0c9 100644 --- a/src/api/office/AutomaticStyles.ts +++ b/src/api/office/AutomaticStyles.ts @@ -1,5 +1,12 @@ -import { createHash } from 'crypto'; -import { ParagraphStyle, Style, StyleFamily } from '../style'; +import { createHash, Hash } from 'crypto'; +import { + BulletListLevelStyle, + ListStyle, + ParagraphStyle, + Style, +} from '../style'; +import { IParagraphProperties } from '../style/IParagraphProperties'; +import { ITextProperties } from '../style/ITextProperties'; import { IStyles } from './IStyles'; type IStyleInformation = { @@ -23,6 +30,7 @@ type IStyleInformation = { */ export class AutomaticStyles implements IStyles { private styles: Map; + private listStyleCounter: number; private paragraphStyleCounter: number; /** @@ -35,6 +43,7 @@ export class AutomaticStyles implements IStyles { */ public constructor() { this.styles = new Map(); + this.listStyleCounter = 0; this.paragraphStyleCounter = 0; } @@ -63,7 +72,7 @@ export class AutomaticStyles implements IStyles { this.styles.set(hash, { style: style, - name: `P${++this.paragraphStyleCounter}`, + name: this.createUniqueName(style), }); } @@ -85,13 +94,13 @@ export class AutomaticStyles implements IStyles { */ public getName(style: Style): string | never { const hash = this.getHash(style); - const styleInformation = this.styles.get(hash); + const existingStyle = this.styles.get(hash); - if (styleInformation === undefined) { + if (existingStyle === undefined) { throw new Error(`Unknown style [${style}}]`); } - return styleInformation.name; + return existingStyle.name; } /** @inheritdoc */ @@ -101,6 +110,23 @@ export class AutomaticStyles implements IStyles { .map((styleInformation) => styleInformation.style); } + /** + * Creates the unique name for the given style. + * The name follows the pattern (e.g. P2 for the second paragraph style). + * + * @param {Style} style The style for which a unique name is being requested + * @returns {string} The unique name representing the given style + */ + private createUniqueName(style: Style): string { + if (style instanceof ListStyle) { + return `L${++this.listStyleCounter}`; + } else if (style instanceof ParagraphStyle) { + return `P${++this.paragraphStyleCounter}`; + } + + throw new Error(`Unknown style type [${style}}]`); + } + /** * The `getHash()` method returns a representative hash for a given style. * @@ -113,50 +139,115 @@ export class AutomaticStyles implements IStyles { hash.update(style.getClass() || ''); hash.update(style.getFamily()); - if (style.getFamily() === StyleFamily.Paragraph) { - const paragraphStyle = style as ParagraphStyle; - - // paragraph properties - hash.update('background-color' + paragraphStyle.getBackgroundColor()); - hash.update('border-bottom' + paragraphStyle.getBorderBottom()); - hash.update('border-left' + paragraphStyle.getBorderLeft()); - hash.update('border-right' + paragraphStyle.getBorderRight()); - hash.update('border-top' + paragraphStyle.getBorderTop()); - hash.update(paragraphStyle.getHorizontalAlignment()); - hash.update(paragraphStyle.getHorizontalAlignmentLastLine()); - hash.update(paragraphStyle.getKeepTogether() ? 'kt' : ''); - hash.update(paragraphStyle.getKeepWithNext() ? 'kwn' : ''); - hash.update('lh' + paragraphStyle.getLineHeight()); - hash.update('lhal' + paragraphStyle.getLineHeightAtLeast()); - hash.update('ls' + paragraphStyle.getLineSpacing()); - hash.update('margin-bottom' + paragraphStyle.getMarginBottom()); - hash.update('margin-left' + paragraphStyle.getMarginLeft()); - hash.update('margin-right' + paragraphStyle.getMarginRight()); - hash.update('margin-top' + paragraphStyle.getMarginTop()); - hash.update('orphans' + paragraphStyle.getOrphans()); - hash.update('padding-bottom' + paragraphStyle.getPaddingBottom()); - hash.update('padding-left' + paragraphStyle.getPaddingLeft()); - hash.update('padding-right' + paragraphStyle.getPaddingRight()); - hash.update('padding-top' + paragraphStyle.getPaddingTop()); - hash.update(paragraphStyle.getPageBreak().toString()); - hash.update('text-indent' + paragraphStyle.getTextIndent()); - hash.update(paragraphStyle.getVerticalAlignment()); - hash.update('widows' + paragraphStyle.getWidows()); - paragraphStyle.getTabStops().forEach((tabStop) => { - hash.update( - `tab${tabStop.getChar()}${tabStop.getLeaderColor()}${tabStop.getLeaderStyle()}${tabStop.getPosition()}${tabStop.getType()}` - ); // eslint-disable-line max-len + if (style instanceof ListStyle) { + hash.update('consecutive-numbering' + style.getConsecutiveNumbering()); + style.getListLevelStyles().forEach((listLevelStyle) => { + this.updateHashWithListLevelStyle(hash, listLevelStyle); }); - - // text properties - hash.update('color' + paragraphStyle.getColor()); - hash.update(paragraphStyle.getFontName() || ''); - hash.update(paragraphStyle.getFontSize().toString()); - hash.update(paragraphStyle.getFontVariant()); - hash.update(paragraphStyle.getTextTransformation()); - hash.update(paragraphStyle.getTypeface().toString()); + } else if (style instanceof ParagraphStyle) { + this.updateHashWithParagraphProperties(hash, style); + this.updateHashWithTextProperties(hash, style); } return hash.digest('hex'); } + + /** + * Updates the hash with the list level style. + * + * @param {Hash} hash The hash to update + * @param {BulletListLevelStyle} listLevelStyle The list level style to evaluate + */ + private updateHashWithListLevelStyle( + hash: Hash, + listLevelStyle: BulletListLevelStyle + ): void { + const level = listLevelStyle.getLevel(); + const properties = [ + 'bullet-char', + listLevelStyle.getBulletChar(), + 'bullet-relative-size', + listLevelStyle.getRelativeBulletSize(), + 'num-prefix', + listLevelStyle.getNumberPrefix(), + 'num-suffix', + listLevelStyle.getNumberSuffix(), + ]; + const listLevelProperties = [ + 'list-level-position-and-space-mode', + listLevelStyle.getListLevelPositionAndSpaceMode(), + 'label-followed-by', + listLevelStyle.getLabelFollowedBy(), + 'list-tab-stop-position', + listLevelStyle.getListTabStopPosition(), + 'text-indent', + listLevelStyle.getTextIndent(), + 'margin-left', + listLevelStyle.getMarginLeft(), + ]; + + hash.update( + `${level}${properties.join('')}${listLevelProperties.join('')}` + ); + } + + /** + * Updates the hash with the paragraph properties. + * + * @param {Hash} hash The hash to update + * @param {IParagraphProperties} paragraphProperties The paragraph properties to evaluate + */ + private updateHashWithParagraphProperties( + hash: Hash, + paragraphProperties: IParagraphProperties + ): void { + hash.update('background-color' + paragraphProperties.getBackgroundColor()); + hash.update('border-bottom' + paragraphProperties.getBorderBottom()); + hash.update('border-left' + paragraphProperties.getBorderLeft()); + hash.update('border-right' + paragraphProperties.getBorderRight()); + hash.update('border-top' + paragraphProperties.getBorderTop()); + hash.update(paragraphProperties.getHorizontalAlignment()); + hash.update(paragraphProperties.getHorizontalAlignmentLastLine()); + hash.update(paragraphProperties.getKeepTogether() ? 'kt' : ''); + hash.update(paragraphProperties.getKeepWithNext() ? 'kwn' : ''); + hash.update('lh' + paragraphProperties.getLineHeight()); + hash.update('lhal' + paragraphProperties.getLineHeightAtLeast()); + hash.update('ls' + paragraphProperties.getLineSpacing()); + hash.update('margin-bottom' + paragraphProperties.getMarginBottom()); + hash.update('margin-left' + paragraphProperties.getMarginLeft()); + hash.update('margin-right' + paragraphProperties.getMarginRight()); + hash.update('margin-top' + paragraphProperties.getMarginTop()); + hash.update('orphans' + paragraphProperties.getOrphans()); + hash.update('padding-bottom' + paragraphProperties.getPaddingBottom()); + hash.update('padding-left' + paragraphProperties.getPaddingLeft()); + hash.update('padding-right' + paragraphProperties.getPaddingRight()); + hash.update('padding-top' + paragraphProperties.getPaddingTop()); + hash.update(paragraphProperties.getPageBreak().toString()); + hash.update('text-indent' + paragraphProperties.getTextIndent()); + hash.update(paragraphProperties.getVerticalAlignment()); + hash.update('widows' + paragraphProperties.getWidows()); + paragraphProperties.getTabStops().forEach((tabStop) => { + hash.update( + `tab${tabStop.getChar()}${tabStop.getLeaderColor()}${tabStop.getLeaderStyle()}${tabStop.getPosition()}${tabStop.getType()}` + ); + }); + } + + /** + * Updates the hash with the text properties. + * + * @param {Hash} hash The hash to update + * @param {ITextProperties} textProperties The text properties to evaluate + */ + private updateHashWithTextProperties( + hash: Hash, + textProperties: ITextProperties + ): void { + hash.update('color' + textProperties.getColor()); + hash.update(textProperties.getFontName() || ''); + hash.update(textProperties.getFontSize().toString()); + hash.update(textProperties.getFontVariant()); + hash.update(textProperties.getTextTransformation()); + hash.update(textProperties.getTypeface().toString()); + } } diff --git a/src/api/office/CommonStyles.spec.ts b/src/api/office/CommonStyles.spec.ts index 1ad7b4ee..1717c4df 100644 --- a/src/api/office/CommonStyles.spec.ts +++ b/src/api/office/CommonStyles.spec.ts @@ -1,4 +1,4 @@ -import { ParagraphStyle } from '../style'; +import { ListStyle, ParagraphStyle } from '../style'; import { CommonStyles } from './CommonStyles'; describe(CommonStyles.name, () => { @@ -10,40 +10,52 @@ describe(CommonStyles.name, () => { commonStyles = new CommonStyles(); }); - describe('styles', () => { - it('create a style only once', () => { - const style1 = commonStyles.createParagraphStyle(testStyleName); - const style2 = commonStyles.createParagraphStyle(testStyleName); - const styles = commonStyles.getAll(); + it('should create and return a new list style', () => { + const style = commonStyles.createListStyle(testStyleName); - expect(style1).toBe(style2); - expect(styles.length).toBe(1); - expect(styles[0]).toBe(style1); - }); + expect(style).toBeInstanceOf(ListStyle); + expect(style.getDisplayName()).toBe(testStyleName); + }); + + it('should create a list style only once', () => { + const style1 = commonStyles.createListStyle(testStyleName); + const style2 = commonStyles.createListStyle(testStyleName); + const styles = commonStyles.getAll(); + + expect(style1).toBe(style2); + expect(styles.length).toBe(1); + expect(styles[0]).toBe(style1); + }); - it('get name of previously created style', () => { - const style = commonStyles.createParagraphStyle(testStyleName); - const styleName = commonStyles.getName(testStyleName); + it('should create and return a new paragraph style', () => { + const style = commonStyles.createParagraphStyle(testStyleName); - expect(styleName).toBe(style.getName()); - }); + expect(style).toBeInstanceOf(ParagraphStyle); + expect(style.getDisplayName()).toBe(testStyleName); + }); + + it('should create a paragraph style only once', () => { + const style1 = commonStyles.createParagraphStyle(testStyleName); + const style2 = commonStyles.createParagraphStyle(testStyleName); + const styles = commonStyles.getAll(); - it('return undefined if unknown style is requested', () => { - commonStyles.createParagraphStyle(testStyleName); + expect(style1).toBe(style2); + expect(styles.length).toBe(1); + expect(styles[0]).toBe(style1); + }); - const styleName = commonStyles.getName('unknownStyleName'); + it('should return the name of previously created style', () => { + const style = commonStyles.createParagraphStyle(testStyleName); + const styleName = commonStyles.getName(testStyleName); - expect(styleName).toBeUndefined(); - }); + expect(styleName).toBe(style.getName()); }); - describe('paragraph style', () => { - it('create and return a new paragraph style', () => { - const style = commonStyles.createParagraphStyle(testStyleName); + it('should return undefined if the name of an unknown style is requested', () => { + commonStyles.createParagraphStyle(testStyleName); + + const styleName = commonStyles.getName('unknownStyleName'); - expect(style).toBeInstanceOf(ParagraphStyle); - expect(style.getDisplayName()).toBe(testStyleName); - expect(style.getFamily()).toBe('paragraph'); - }); + expect(styleName).toBeUndefined(); }); }); diff --git a/src/api/office/CommonStyles.ts b/src/api/office/CommonStyles.ts index b9295180..8e79676a 100644 --- a/src/api/office/CommonStyles.ts +++ b/src/api/office/CommonStyles.ts @@ -1,4 +1,4 @@ -import { ParagraphStyle, Style } from '../style'; +import { ListStyle, ParagraphStyle, Style } from '../style'; import { IStyles } from './IStyles'; /** @@ -27,6 +27,31 @@ export class CommonStyles implements IStyles { this.styles = new Map(); } + /** + * The `createListStyle()` method creates a new `ListStyle` instance with the given name. + * If a style with this name already exists, the existing style will be returned. + * + * @example + * const commonStyles = new CommonStyles(); + * commonStyles.createListStyle('Contents'); + * + * @param {string} name The unique name for the style + * @returns {ListStyle} A new `ListStyle` instance with the specified name or an existing style, if one with the specified name exists + * @since 0.11.0 + */ + public createListStyle(name: string): ListStyle { + const existingStyle = this.styles.get(name); + + if (existingStyle !== undefined) { + return existingStyle as ListStyle; + } + + const newStyle = new ListStyle(name); + this.styles.set(name, newStyle); + + return newStyle; + } + /** * The `createParagraphStyle()` method creates a new `ParagraphStyle` instance with the given name. * If a style with this name already exists, the existing style will be returned. @@ -41,16 +66,16 @@ export class CommonStyles implements IStyles { * @since 0.9.0 */ public createParagraphStyle(name: string): ParagraphStyle { - let style = this.styles.get(name); + const existingStyle = this.styles.get(name); - if (style !== undefined) { - return style as ParagraphStyle; + if (existingStyle !== undefined) { + return existingStyle as ParagraphStyle; } - style = new ParagraphStyle(name); - this.styles.set(name, style); + const newStyle = new ParagraphStyle(name); + this.styles.set(name, newStyle); - return style as ParagraphStyle; + return newStyle; } /** diff --git a/src/api/style/BulletListLevelStyle.spec.ts b/src/api/style/BulletListLevelStyle.spec.ts new file mode 100644 index 00000000..84c30907 --- /dev/null +++ b/src/api/style/BulletListLevelStyle.spec.ts @@ -0,0 +1,99 @@ +import { BulletListLevelStyle } from './BulletListLevelStyle'; + +describe(BulletListLevelStyle.name, () => { + const level = 3; + const prefixOrSuffix = '§'; + const relativeBulletSize = '23%'; + + let bulletListLevelStyle: BulletListLevelStyle; + + beforeEach(() => { + bulletListLevelStyle = new BulletListLevelStyle(level); + }); + + it('should return initial bullet character', () => { + expect(bulletListLevelStyle.getBulletChar()).toBe('\u2022'); + }); + + it('should return previously set bullet character', () => { + bulletListLevelStyle.setBulletChar('\u2714'); + + expect(bulletListLevelStyle.getBulletChar()).toBe('\u2714'); + }); + + it('should ignore empty bullet character', () => { + bulletListLevelStyle.setBulletChar(''); + + expect(bulletListLevelStyle.getBulletChar()).toBe('\u2022'); + }); + + it('should return initial level', () => { + expect(bulletListLevelStyle.getLevel()).toBe(level); + }); + + it('should have no number prefix by default', () => { + expect(bulletListLevelStyle.getNumberPrefix()).toBeUndefined(); + }); + + it('should return previously set number prefix', () => { + bulletListLevelStyle.setNumberPrefix(prefixOrSuffix); + + expect(bulletListLevelStyle.getNumberPrefix()).toBe(prefixOrSuffix); + }); + + it('should reset previously set number prefix', () => { + bulletListLevelStyle.setNumberPrefix(prefixOrSuffix); + + bulletListLevelStyle.setNumberPrefix(undefined); + + expect(bulletListLevelStyle.getNumberPrefix()).toBeUndefined(); + }); + + it('should have no number suffix by default', () => { + expect(bulletListLevelStyle.getNumberSuffix()).toBeUndefined(); + }); + + it('should return previously set number suffix', () => { + bulletListLevelStyle.setNumberSuffix(prefixOrSuffix); + + expect(bulletListLevelStyle.getNumberSuffix()).toBe(prefixOrSuffix); + }); + + it('should reset previously set number suffix', () => { + bulletListLevelStyle.setNumberSuffix(prefixOrSuffix); + + bulletListLevelStyle.setNumberSuffix(undefined); + + expect(bulletListLevelStyle.getNumberSuffix()).toBeUndefined(); + }); + + it('should have no relative bullet size by default', () => { + expect(bulletListLevelStyle.getRelativeBulletSize()).toBeUndefined(); + }); + + it('should return previously set relative bullet size', () => { + bulletListLevelStyle.setRelativeBulletSize(relativeBulletSize); + + expect(bulletListLevelStyle.getRelativeBulletSize()).toBe( + relativeBulletSize + ); + }); + + it('should reset previously set relative bullet size', () => { + bulletListLevelStyle.setRelativeBulletSize(relativeBulletSize); + + bulletListLevelStyle.setRelativeBulletSize(undefined); + + expect(bulletListLevelStyle.getRelativeBulletSize()).toBeUndefined(); + }); + + it('should ignore invalid values for relative bullet size', () => { + bulletListLevelStyle.setRelativeBulletSize(relativeBulletSize); + + bulletListLevelStyle.setRelativeBulletSize('42px'); + + expect(bulletListLevelStyle.getRelativeBulletSize()).toBe( + relativeBulletSize + ); + }); +}); diff --git a/src/api/style/BulletListLevelStyle.ts b/src/api/style/BulletListLevelStyle.ts new file mode 100644 index 00000000..e08c465b --- /dev/null +++ b/src/api/style/BulletListLevelStyle.ts @@ -0,0 +1,296 @@ +import { isPercent } from '../util'; +import { IListLevelProperties } from './IListLevelProperties'; +import { ListLevelProperties } from './ListLevelProperties'; + +const DEFAULT_BULLET_CHAR = '\u2022'; + +/** + * This class represents a list style where list items are preceded by bullets. + * + * @example + * document.getStyleManager() + * .createListStyle('Contents') + * .createBulletListLevelStyle(3); + * + * @since 0.11.0 + */ +export class BulletListLevelStyle implements IListLevelProperties { + private bulletChar: string; + private bulletRelativeSize: string | undefined; + private level: number; // for all list level styles, regardless of the type + private numPrefix: string | undefined; + private numSuffix: string | undefined; + + private listLevelProperties: IListLevelProperties; + + /** + * Creates a `BulletListLevelStyle` instance that represents a list style where list items are preceded by bullets. + * + * @example + * const style = new BulletListLevelStyle(3); + * + * @param {number} level The level of the list style, starting with `1` + * + * @since 0.11.0 + */ + public constructor(level: number) { + this.bulletChar = DEFAULT_BULLET_CHAR; + this.level = level; + + this.listLevelProperties = new ListLevelProperties(); + } + + /** + * The `getBulletChar()` method returns the character to use as the bullet. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.getBulletChar(); // '\u2022' + * style.setBulletChar('~'); + * style.getBulletChar(); // '~' + * + * @returns {string} The character to use as the bullet + * @since 0.11.0 + */ + public getBulletChar(): string { + return this.bulletChar; + } + + /** + * The `setBulletChar()` method sets the character to use as the bullet. + * + * If an illegal value is provided, the value will be ignored. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.setBulletChar('~'); // '~' + * style.setBulletChar(''); // '~' + * + * @param {string} bulletChar The character to use as the bullet + * @returns {BulletListLevelStyle} The `BulletListLevelStyle` object + * @since 0.11.0 + */ + public setBulletChar(bulletChar: string): BulletListLevelStyle { + this.bulletChar = bulletChar.trim().charAt(0) || this.bulletChar; + + return this; + } + + /** + * The `getLevel()` method returns the level of the list style. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.getLevel(); // 3 + * + * @returns {number} The level of the list style, starting with `1` + * @since 0.11.0 + */ + public getLevel(): number { + return this.level; + } + + /** + * The `getNumberPrefix()` method returns the character to display before a bullet. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.getNumberPrefix(); // undefined + * style.setNumberPrefix('~'); + * style.getNumberPrefix(); // '~' + * + * @returns {string | undefined} The character to display before a bullet or `undefined` if no prefix is set + * @since 0.11.0 + */ + public getNumberPrefix(): string | undefined { + return this.numPrefix; + } + + /** + * The `setNumberPrefix()` method sets the character to display before a bullet. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.setNumberPrefix('~'); // '~' + * style.setNumberPrefix(undefined); // undefined + * + * @param {string | undefined} prefix The character to display before a bullet or `undefined` to unset the prefix + * @returns {BulletListLevelStyle} The `BulletListLevelStyle` object + * @since 0.11.0 + */ + public setNumberPrefix(prefix: string | undefined): this { + this.numPrefix = prefix; + + return this; + } + + /** + * The `getNumberSuffix()` method returns the character to display after a bullet. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.getNumberSuffix(); // undefined + * style.setNumberSuffix('~'); + * style.getNumberSuffix(); // '~' + * + * @returns {string | undefined} The character to display after a bullet or `undefined` if no suffix is set + * @since 0.11.0 + */ + public getNumberSuffix(): string | undefined { + return this.numSuffix; + } + + /** + * The `setNumberSuffix()` method sets the character to display after a bullet. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.setNumberSuffix('~'); // '~' + * style.setNumberSuffix(undefined); // undefined + * + * @param {string | undefined} suffix The character to display after a bullet or `undefined` to unset the suffix + * @returns {BulletListLevelStyle} The `BulletListLevelStyle` object + * @since 0.11.0 + */ + public setNumberSuffix(suffix: string | undefined): this { + this.numSuffix = suffix; + + return this; + } + + /** + * The `getRelativeBulletSize()` method returns the percentage value for the bullet size relative to the font size of the paragraphs in the bullet list. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.getRelativeBulletSize(); // undefined + * style.setRelativeBulletSize('23%'); + * style.getRelativeBulletSize(); // '23%' + * + * @returns {string | undefined} The percentage value for the bullet size or `undefined` if no relative bullet size is set + * @since 0.11.0 + */ + public getRelativeBulletSize(): string | undefined { + return this.bulletRelativeSize; + } + + /** + * The `setNumberSuffix()` method sets the percentage value for the bullet size relative to the font size of the paragraphs in the bullet list. + * + * If an illegal value is provided, the value will be ignored. + * + * @example + * const style = new BulletListLevelStyle(3); + * style.setRelativeBulletSize('23%'); // '23%' + * style.setRelativeBulletSize('42px'); // '23%' + * style.setRelativeBulletSize(undefined); // undefined + * + * @param {string | undefined} relativeSize The percentage value for the bullet size or `undefined` to unset the bullet size + * @returns {BulletListLevelStyle} The `BulletListLevelStyle` object + * @since 0.11.0 + */ + public setRelativeBulletSize( + relativeSize: string | undefined + ): BulletListLevelStyle { + if (relativeSize === undefined || isPercent(relativeSize)) { + this.bulletRelativeSize = relativeSize; + } + + return this; + } + + // ListLevelProperties + /** @inheritdoc */ + /* istanbul ignore next */ + public getListLevelPositionAndSpaceMode(): string { + return this.listLevelProperties.getListLevelPositionAndSpaceMode(); + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public getLabelFollowedBy(): string { + return this.listLevelProperties.getLabelFollowedBy(); + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public setLabelFollowedBy(value: 'listtab' | 'space' | 'nothing'): this { + this.listLevelProperties.setLabelFollowedBy(value); + + return this; + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public getListTabStopPosition(): number | undefined { + return this.listLevelProperties.getListTabStopPosition(); + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public setListTabStopPosition(position: number | undefined): this { + this.listLevelProperties.setListTabStopPosition(position); + + return this; + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public getTextIndent(): number | undefined { + return this.listLevelProperties.getTextIndent(); + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public setTextIndent(indent: number | undefined): this { + this.listLevelProperties.setTextIndent(indent); + + return this; + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public getMarginLeft(): number | undefined { + return this.listLevelProperties.getMarginLeft(); + } + + /** @inheritdoc */ + /* istanbul ignore next */ + public setMarginLeft(margin: number | undefined): this { + this.listLevelProperties.setMarginLeft(margin); + + return this; + } +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ diff --git a/src/api/style/IListLevelProperties.ts b/src/api/style/IListLevelProperties.ts new file mode 100644 index 00000000..57775518 --- /dev/null +++ b/src/api/style/IListLevelProperties.ts @@ -0,0 +1,255 @@ +/** + * This class specifies formatting properties for a list level. + * This includes the position and spacing of a list label and its list item. + * + * @since 0.11.0 + */ +export interface IListLevelProperties { + /** + * The `getListLevelPositionAndSpaceMode()` method returns how the position and spacing of the list labels (numbers or bullets) is defined. + * + * The value is fixed to `label-alignment`. + * + * @example + * listLevelStyle.getListLevelPositionAndSpaceMode(); // 'label-alignment' + * + * @returns {string} always `'label-alignment'` + * @since 0.11.0 + */ + getListLevelPositionAndSpaceMode(): string; + + /** + * The `getLabelFollowedBy()` method returns the character that is inserted behind a list label. + * + * The defined values are: + * * `listtab`: a tab character is inserted after a list label before the text starts. + * * `nothing`: text starts directly after a list label. + * * `space`: a SPACE character is inserted after a list label before the text starts. + * + * @example + * listLevelStyle.getLabelFollowedBy(); // 'listtab' + * listLevelStyle.setLabelFollowedBy('space'); + * listLevelStyle.getLabelFollowedBy(); // 'space' + * + * @returns {string} the character that is inserted behind a list label + * @since 0.11.0 + */ + getLabelFollowedBy(): string; + + /** + * The `setLabelFollowedBy()` method sets the character that is inserted behind a list label. + * + * The defined values are: + * * `listtab`: a tab character is inserted after a list label before the text starts. + * * `nothing`: text starts directly after a list label. + * * `space`: a SPACE character is inserted after a list label before the text starts. + * + * @example + * listLevelStyle.setLabelFollowedBy('space'); + * + * @param {string} character The character that is inserted behind a list label + * @returns {IListLevelProperties} The `IListLevelProperties` object + * @since 0.11.0 + */ + setLabelFollowedBy(character: 'listtab' | 'space' | 'nothing'): this; + + /** + * The `getListTabStopPosition()` method returns the position of the tab stop which is inserted behind a list label. + * + * This additional tab stop is inserted into the list of tab stops that are defined for a list item. + * By default the tab stop's position is behind list label. + * The text of the first line of list item starts at this tab stop. + * + * The position of the tab stop is ignored unless the `labelFollowedBy` property is set set to `listtab`. + * + * @example + * style.getListTabStopPosition(); // undefined + * style.setListTabStopPosition(23); + * style.getListTabStopPosition(); // 23 + * + * @returns {number | undefined} The position of the tab stop in millimeters or `undefined` if no tab stop will be inserted + * @since 0.11.0 + */ + getListTabStopPosition(): number | undefined; + + /** + * The `setListTabStopPosition()` method sets the position of the tab stop which is inserted behind a list label. + * + * This additional tab stop is inserted into the list of tab stops that are defined for a list item. + * By default the tab stop's position is behind list label. + * The text of the first line of list item starts at this tab stop. + * + * The position of the tab stop is ignored unless the `labelFollowedBy` property is set set to `listtab`. + * + * @example + * style.setListTabStopPosition(23); + * style.setListTabStopPosition(undefined); + * + * @param {number | undefined} position The position of the tab stop in millimeters or `undefined` to remove the tab stop + * @returns {IListLevelProperties} The `IListLevelProperties` object + * @since 0.11.0 + */ + setListTabStopPosition(position: number | undefined): this; + + /** + * The `getTextIndent()` method returns the indent for the text lines of a list item. + * + * @example + * style.getTextIndent(); // undefined + * style.setTextIndent(23); + * style.getTextIndent(); // 23 + * + * @returns {number | undefined} The indent for the text lines of a list item or `undefined` if the text is not indented + * @since 0.11.0 + */ + getTextIndent(): number | undefined; + + /** + * The `setTextIndent()` method sets the indent for the text lines of a list item. + * + * @example + * style.setTextIndent(23); + * style.setTextIndent(undefined); + * + * @param {number | undefined} indent The indent for the text lines of a list item or `undefined` to remove indentation + * @returns {IListLevelProperties} The `IListLevelProperties` object + * @since 0.11.0 + */ + setTextIndent(indent: number | undefined): this; + + /** + * The `getMarginLeft()` method returns the left margins for the text lines of a list item. + * + * @example + * style.getMarginLeft(); // undefined + * style.setMarginLeft(23); + * style.getMarginLeft(); // 23 + * + * @returns {number | undefined} The left margins for the text lines of a list item or `undefined` if the text has no left margin + * @since 0.11.0 + */ + getMarginLeft(): number | undefined; + + /** + * The `setMarginLeft()` method sets the left margins for the text lines of a list item. + * + * @example + * style.setMarginLeft(23); + * style.setMarginLeft(undefined); + * + * @param {number | undefined} margin The left margins for the text lines of a list item or `undefined` to remove the left margin + * @returns {IListLevelProperties} The `IListLevelProperties` object + * @since 0.11.0 + */ + setMarginLeft(margin: number | undefined): this; +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + label-width-and-position + label-alignment + + + + + + + + + + + + start + end + left + right + center + justify + + + + + + + + + + + top + middle + bottom + from-top + below + + + + + + + + + + + + + + + + page + page-content + frame + frame-content + paragraph + paragraph-content + char + line + baseline + text + + + + +*/ diff --git a/src/api/style/ListLevelProperties.ts b/src/api/style/ListLevelProperties.ts new file mode 100644 index 00000000..c7c43b09 --- /dev/null +++ b/src/api/style/ListLevelProperties.ts @@ -0,0 +1,72 @@ +import { IListLevelProperties } from './IListLevelProperties'; + +/** + * @todo document + * @since 0.11.0 + * @private + */ +export class ListLevelProperties implements IListLevelProperties { + private labelFollowedBy: 'listtab' | 'space' | 'nothing'; + private listTabStopPosition: number | undefined; + private marginLeft: number | undefined; + private textIndent: number | undefined; + + /** + * @todo document + * @since 0.11.0 + */ + public constructor() { + this.labelFollowedBy = 'listtab'; + } + + /** @inheritdoc */ + public getListLevelPositionAndSpaceMode(): string { + return 'label-alignment'; + } + + /** @inheritdoc */ + public getLabelFollowedBy(): string { + return this.labelFollowedBy; + } + + /** @inheritdoc */ + public setLabelFollowedBy(value: 'listtab' | 'space' | 'nothing'): this { + this.labelFollowedBy = value; + + return this; + } + + /** @inheritdoc */ + public getListTabStopPosition(): number | undefined { + return this.listTabStopPosition; + } + + /** @inheritdoc */ + public setListTabStopPosition(position: number | undefined): this { + this.listTabStopPosition = position; + + return this; + } + + /** @inheritdoc */ + public getTextIndent(): number | undefined { + return this.textIndent; + } + + /** @inheritdoc */ + public setTextIndent(indent: number | undefined): this { + this.textIndent = indent; + return this; + } + + /** @inheritdoc */ + public getMarginLeft(): number | undefined { + return this.marginLeft; + } + + /** @inheritdoc */ + public setMarginLeft(margin: number | undefined): this { + this.marginLeft = margin; + return this; + } +} diff --git a/src/api/style/ListStyle.spec.ts b/src/api/style/ListStyle.spec.ts new file mode 100644 index 00000000..f4b75e90 --- /dev/null +++ b/src/api/style/ListStyle.spec.ts @@ -0,0 +1,88 @@ +import { BulletListLevelStyle } from './BulletListLevelStyle'; +import { ListStyle } from './ListStyle'; + +describe(ListStyle.name, () => { + const level = 3; + + let listStyle: ListStyle; + + beforeEach(() => { + listStyle = new ListStyle(); + }); + + it('should not set consecutive numbering by default', () => { + expect(listStyle.getConsecutiveNumbering()).toBe(false); + }); + + it('should return previously set consecutive numbering', () => { + listStyle.setConsecutiveNumbering(true); + + expect(listStyle.getConsecutiveNumbering()).toBe(true); + }); + + it('should have no list level styles by default', () => { + const listLevelStyles = listStyle.getListLevelStyles(); + + expect(listLevelStyles.length).toBe(0); + }); + + it('should create new BulletListLevelStyle', () => { + const bulletListLevelStyle = listStyle.createBulletListLevelStyle(level); + + expect(bulletListLevelStyle).toBeInstanceOf(BulletListLevelStyle); + expect(bulletListLevelStyle.getLevel()).toBe(level); + }); + + it('should overwrite existing list level style with same level', () => { + const listLevelStylesBefore = listStyle.createBulletListLevelStyle(level); + + expect(listStyle.getListLevelStyle(level)).toBe(listLevelStylesBefore); + + const listLevelStylesAfter = listStyle.createBulletListLevelStyle(level); + + expect(listStyle.getListLevelStyle(level)).toBe(listLevelStylesAfter); + }); + + it('should throw an error if the level is not an integer', () => { + expect(() => listStyle.createBulletListLevelStyle(1.23)).toThrowError(); + }); + + it('should throw an error if the level is smaller than 1', () => { + expect(() => listStyle.createBulletListLevelStyle(0)).toThrowError(); + }); + + it('should throw an error if the level is greater then 10', () => { + expect(() => listStyle.createBulletListLevelStyle(11)).toThrowError(); + }); + + it('should return list level style for requested level', () => { + listStyle.createBulletListLevelStyle(level); + + const bulletListLevelStyle = listStyle.getListLevelStyle(level); + + expect(bulletListLevelStyle).toBeInstanceOf(BulletListLevelStyle); + if (bulletListLevelStyle) { + expect(bulletListLevelStyle.getLevel()).toBe(level); + } + }); + + it('should return undefined if no list level style is set for the requested level', () => { + listStyle.createBulletListLevelStyle(level); + + const bulletListLevelStyle = listStyle.getListLevelStyle(level + 1); + + expect(bulletListLevelStyle).toBeUndefined(); + }); + + it('should remove the list level style for the requested level', () => { + listStyle.createBulletListLevelStyle(level); + + const listLevelStylesBefore = listStyle.getListLevelStyles(); + expect(listLevelStylesBefore.length).toBe(1); + + listStyle.removeListLevelStyle(level); + + const listLevelStylesAfter = listStyle.getListLevelStyles(); + expect(listLevelStylesAfter.length).toBe(0); + }); +}); diff --git a/src/api/style/ListStyle.ts b/src/api/style/ListStyle.ts new file mode 100644 index 00000000..14a9a8c3 --- /dev/null +++ b/src/api/style/ListStyle.ts @@ -0,0 +1,197 @@ +import { BulletListLevelStyle } from './BulletListLevelStyle'; +import { Style } from './Style'; +import { StyleFamily } from './StyleFamily'; + +/** + * This class represents a list style. + * + * List styles are used to specify the formatting of a list and its items. + * A list style contains a set of style elements for each list level (@see ListLevelStyle). + * If a list style is applied to a list but does not contain a list level specification for a specific level, the list level style of the next lower level is used. + * + * @example + * document.getStyleManager().createListStyle('Contents'); + * document.getBody() + * .addList() + * .setStyleName('Contents); + * + * @since 0.11.0 + */ +export class ListStyle extends Style { + private isConsecutiveNumbering: boolean; + private listLevelStyles: BulletListLevelStyle[]; + + /** + * Creates a `ListStyle` instance that represents the formatting of a list. + * + * @example + * const style = new ListStyle('Contents'); + * + * @param {string} displayName The unique display name for the style + * + * @since 0.11.0 + */ + public constructor(displayName: string = Style.UNNAMED) { + super(displayName, StyleFamily.None); + + this.isConsecutiveNumbering = false; + this.listLevelStyles = []; + } + + /** + * The `getConsecutiveNumbering()` method returns whether the style uses consecutive numbering for all list levels or whether each list level restarts the numbering. + * + * @example + * const style = new ListStyle('Contents'); + * style.getConsecutiveNumbering(); // false + * style.setConsecutiveNumbering(true); + * style.getConsecutiveNumbering(); // true + * + * @returns {boolean} `true` if consecutive numbering is used for all list levels or `false` if each list level restarts numbering + * @since 0.11.0 + */ + public getConsecutiveNumbering(): boolean { + return this.isConsecutiveNumbering; + } + + /** + * The `setConsecutiveNumbering()` method sets returns whether the style uses consecutive numbering for all list levels or whether each list level restarts the numbering. + * + * @example + * const style = new ListStyle('Contents'); + * style.setConsecutiveNumbering(true); // true + * + * @param {boolean} consecutiveNumbering `true` if consecutive numbering is used for all list levels or `false` if each list level restarts numbering + * @returns {ListStyle} The `ListStyle` object + * @since 0.11.0 + */ + public setConsecutiveNumbering(consecutiveNumbering: boolean): ListStyle { + this.isConsecutiveNumbering = consecutiveNumbering; + + return this; + } + + /** + * The `createBulletListLevelStyle()` method creates a new `BulletListLevelStyle` instance for the given list level. + * If a list level style for this level already exists, the existing style will be overwritten. + * + * @example + * const style = new ListStyle('Contents'); + * style.createBulletListLevelStyle(3); + * + * @param {number} level The level of the list style, starting with `1` + * @returns {BulletListLevelStyle} A new `BulletListLevelStyle` instance with the specified level + * @throws {Error} if the given list level is invalid + * @since 0.11.0 + */ + public createBulletListLevelStyle(level: number): BulletListLevelStyle { + if (Number.isInteger(level) === false || level < 1 || level > 10) { + throw new Error('Level must be an integer between 1 and 10'); + } + + const bulletListLevelStyle = new BulletListLevelStyle(level); + this.removeListLevelStyle(level); + this.listLevelStyles.push(bulletListLevelStyle); + + return bulletListLevelStyle; + } + + /** + * The `getListLevelStyle()` method returns the list level style for the given list level. + * If a list level style for this level already exists, the existing style will be overwritten. + * + * @example + * const style = new ListStyle('Contents'); + * style.getListLevelStyle(3); + * + * @param {number} level The level of the list style, starting with `1` + * @returns {BulletListLevelStyle | undefined} The list level style for the specified level or `undefined` if no list level style is defined for the specified level + * @since 0.11.0 + */ + public getListLevelStyle(level: number): BulletListLevelStyle | undefined { + return this.listLevelStyles.find((listLevelStyle) => { + return listLevelStyle.getLevel() === level; + }); + } + + /** + * The `getListLevelStyles()` method returns a new `Array` object that contains all list level styles of a list style. + * + * @example + * const style = new ListStyle('Contents'); + * style.createBulletListLevelStyle(1); + * style.createBulletListLevelStyle(2); + * styles.getListLevelStyles(); + * + * @returns {BulletListLevelStyle[]} A new `Array` object that contains the list level styles of a list style + * @since 0.11.0 + */ + public getListLevelStyles(): BulletListLevelStyle[] { + return [...this.listLevelStyles]; + } + + /** + * The `removeListLevelStyle()` method removes the list level style for the given list level. + * + * @example + * const style = new ListStyle('Contents'); + * style.createBulletListLevelStyle(3); + * style.removeListLevelStyle(3); + * styles.getListLevelStyles(); // [] + * + * @param {number} level The level of the list style, starting with `1` + * @returns {ListStyle} The `ListStyle` object + * @since 0.11.0 + */ + public removeListLevelStyle(level: number): ListStyle { + this.listLevelStyles = this.listLevelStyles.filter((listLevelStyle) => { + return listLevelStyle.getLevel() !== level; + }); + + return this; + } +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ diff --git a/src/api/style/ParagraphStyle.ts b/src/api/style/ParagraphStyle.ts index b1b81f11..61e52299 100644 --- a/src/api/style/ParagraphStyle.ts +++ b/src/api/style/ParagraphStyle.ts @@ -22,8 +22,8 @@ export class ParagraphStyle extends Style private paragraphProperties: ParagraphProperties; private textProperties: TextProperties; - public constructor(name: string = Style.UNNAMED) { - super(name, StyleFamily.Paragraph); + public constructor(displayName: string = Style.UNNAMED) { + super(displayName, StyleFamily.Paragraph); this.paragraphProperties = new ParagraphProperties(); this.textProperties = new TextProperties(); diff --git a/src/api/style/StyleFamily.ts b/src/api/style/StyleFamily.ts index 2d523eb8..c388db80 100644 --- a/src/api/style/StyleFamily.ts +++ b/src/api/style/StyleFamily.ts @@ -2,6 +2,7 @@ export enum StyleFamily { Chart = 'chart', DrawingPage = 'drawing-page', Graphic = 'graphic', + None = '', Paragraph = 'paragraph', Presentation = 'presentation', Ruby = 'ruby', diff --git a/src/api/style/index.ts b/src/api/style/index.ts index cd64517e..b877063d 100644 --- a/src/api/style/index.ts +++ b/src/api/style/index.ts @@ -1,5 +1,6 @@ export { Border } from './Border'; export { BorderStyle } from './BorderStyle'; +export { BulletListLevelStyle } from './BulletListLevelStyle'; export { Color } from './Color'; export { FontFace } from './FontFace'; export { FontFamilyGeneric } from './FontFamilyGeneric'; @@ -7,6 +8,7 @@ export { FontPitch } from './FontPitch'; export { FontVariant } from './FontVariant'; export { HorizontalAlignment } from './HorizontalAlignment'; export { HorizontalAlignmentLastLine } from './HorizontalAlignmentLastLine'; +export { ListStyle } from './ListStyle'; export { PageBreak } from './PageBreak'; export { ParagraphProperties } from './ParagraphProperties'; export { ParagraphStyle } from './ParagraphStyle'; diff --git a/src/api/text/List.spec.ts b/src/api/text/List.spec.ts index 08e494ef..3aeb510a 100644 --- a/src/api/text/List.spec.ts +++ b/src/api/text/List.spec.ts @@ -1,191 +1,160 @@ +import { ListStyle } from '../style'; import { List } from './List'; import { ListItem } from './ListItem'; describe(List.name, () => { + const styleName = 'someStyleName'; + let list: List; - let testItem1: ListItem; - let testItem2: ListItem; - let testItem3: ListItem; + let listItem1: ListItem; + let listItem2: ListItem; + let listItem3: ListItem; + let listStyle: ListStyle; beforeEach(() => { list = new List(); - testItem1 = new ListItem(); - testItem2 = new ListItem(); - testItem3 = new ListItem(); - }); + listItem1 = new ListItem(); + listItem2 = new ListItem(); + listItem3 = new ListItem(); - describe('#addItem', () => { - beforeEach(() => { - list.addItem(); - }); + listStyle = new ListStyle(); - it('create new item at the end of the list and return the added item', () => { - const addedItem = list.addItem(); + list.addItem(listItem1); + list.addItem(listItem2); + list.addItem(listItem3); + }); - expect(addedItem).toBeInstanceOf(ListItem); - expect(list.getItems().length).toBe(2); - }); + it('should create new item at the end of the list and return the added item', () => { + const addedItem = list.addItem(); + + expect(addedItem).toBeInstanceOf(ListItem); + expect(list.getItems().length).toBe(4); + expect(list.getItem(3)).toBe(addedItem); + }); - it('add new item to the end of the list and return the added item', () => { - const testItem = new ListItem(); + it('should add new item to the end of the list and return the added item', () => { + const testItem = new ListItem(); - const addedItem = list.addItem(testItem); + const addedItem = list.addItem(testItem); - expect(addedItem).toBe(testItem); - expect(list.getItems().length).toBe(2); - expect(list.getItem(1)).toBe(testItem); - }); + expect(addedItem).toBe(testItem); + expect(list.getItems().length).toBe(4); + expect(list.getItem(3)).toBe(testItem); }); - describe('#insertItem', () => { - let itemToAdd: ListItem; + it('should insert item at the specified position and return the added item', () => { + const testItem = new ListItem(); - beforeEach(() => { - list.addItem(testItem1); - list.addItem(testItem2); - list.addItem(testItem3); + const insertedItem = list.insertItem(2, testItem); - itemToAdd = new ListItem(); - }); + expect(insertedItem).toEqual(testItem); + expect(list.getItems()).toEqual([ + listItem1, + listItem2, + testItem, + listItem3, + ]); + }); - it('insert item at the specified position and return the added item', () => { - const insertedItem = list.insertItem(2, itemToAdd); + it('should insert item at the front of the list if position is negative', () => { + const testItem = new ListItem(); - expect(insertedItem).toEqual(itemToAdd); - expect(list.getItems()).toEqual([ - testItem1, - testItem2, - itemToAdd, - testItem3, - ]); - }); + const insertedItem = list.insertItem(-2, testItem); - it('add new items to the specified position and return the added item', () => { - const insertedItem = list.insertItem(2, itemToAdd); + expect(insertedItem).toBe(testItem); + expect(list.getItems()).toEqual([ + testItem, + listItem1, + listItem2, + listItem3, + ]); + }); - expect(insertedItem).toBe(itemToAdd); - expect(list.getItems()).toEqual([ - testItem1, - testItem2, - itemToAdd, - testItem3, - ]); - }); + it('should insert item at the end of the list if position is larger than the size of the list', () => { + const testItem = new ListItem(); - it('insert item at the front of the list if position is negative', () => { - const insertedItem = list.insertItem(-2, itemToAdd); + const insertedItem = list.insertItem(10, testItem); - expect(insertedItem).toBe(itemToAdd); - expect(list.getItems()).toEqual([ - itemToAdd, - testItem1, - testItem2, - testItem3, - ]); - }); + expect(insertedItem).toBe(testItem); + expect(list.getItems()).toEqual([ + listItem1, + listItem2, + listItem3, + testItem, + ]); + }); - it('insert item at the end of the list if position is larger than the size of the list', () => { - const insertedItem = list.insertItem(10, itemToAdd); + it('should return the item at the specified position', () => { + const item = list.getItem(1); - expect(insertedItem).toBe(itemToAdd); - expect(list.getItems()).toEqual([ - testItem1, - testItem2, - testItem3, - itemToAdd, - ]); - }); + expect(item).toEqual(listItem2); }); - describe('#getItem', () => { - beforeEach(() => { - list.addItem(testItem1); - list.addItem(testItem2); - list.addItem(testItem3); - }); - - it('get the item at the specified position', () => { - const item = list.getItem(1); + it('should return undefined if the specified position is less then 0', () => { + const item = list.getItem(-2); - expect(item).toEqual(testItem2); - }); + expect(item).toBeUndefined(); + }); - it('return undefined if the specified position is less then 0', () => { - const item = list.getItem(-2); + it('should return undefined if the specified position is larger than the list size', () => { + const item = list.getItem(10); - expect(item).toBeUndefined(); - }); + expect(item).toBeUndefined(); + }); - it('return undefined if the specified position is larger than the list size', () => { - const item = list.getItem(10); + it('should return the items in order', () => { + const items = list.getItems(); - expect(item).toBeUndefined(); - }); + expect(items).toEqual([listItem1, listItem2, listItem3]); }); - describe('#getItems', () => { - it('return the items in order', () => { - list.addItem(testItem1); - list.addItem(testItem2); - list.addItem(testItem3); - - const items = list.getItems(); + it('should remove the item at the specified position and return it', () => { + const removedItem = list.removeItemAt(1); - expect(items).toEqual([testItem1, testItem2, testItem3]); - }); + expect(removedItem).toEqual(listItem2); + expect(list.getItems()).toEqual([listItem1, listItem3]); }); - describe('#removeItemAt', () => { - beforeEach(() => { - list.addItem(testItem1); - list.addItem(testItem2); - list.addItem(testItem3); - }); + it('should not remove any item and return undefined if the specified position is less then 0', () => { + const removedItem = list.removeItemAt(-2); - it('remove the item at the specified position and return it', () => { - const removedItem = list.removeItemAt(1); + expect(removedItem).toBeUndefined(); + }); - expect(removedItem).toEqual(testItem2); - expect(list.getItems()).toEqual([testItem1, testItem3]); - }); + it('should not remove any item and return undefined if the specified position is larger than the list size', () => { + const removedItem = list.removeItemAt(10); - it('return undefined if the specified position is less then 0', () => { - const removedItem = list.removeItemAt(-2); + expect(removedItem).toBeUndefined(); + }); - expect(removedItem).toBeUndefined(); - }); + it('should return undefined as style by default', () => { + expect(list.getStyle()).toBeUndefined(); + }); - it('return undefined if the specified position is larger than the list size', () => { - const removedItem = list.removeItemAt(10); + it('should return previous set style', () => { + list.setStyle(listStyle); - expect(removedItem).toBeUndefined(); - }); + expect(list.getStyle()).toBe(listStyle); }); - describe('#clear', () => { - beforeEach(() => { - list.addItem(testItem1); - list.addItem(testItem2); - list.addItem(testItem3); - }); + it('should return undefined as style name by default', () => { + expect(list.getStyleName()).toBeUndefined(); + }); - it('remove all items from the list', () => { - list.clear(); + it('should return previous set style name', () => { + list.setStyleName(styleName); - expect(list.size()).toBe(0); - }); + expect(list.getStyleName()).toBe(styleName); }); - describe('#size', () => { - beforeEach(() => { - list.addItem(testItem1); - list.addItem(testItem2); - list.addItem(testItem3); - }); + it('should remove all items from the list if it gets cleared', () => { + list.clear(); + + expect(list.size()).toBe(0); + }); - it('return the size of the list', () => { - expect(list.size()).toBe(3); - }); + it('should return the size of the list', () => { + expect(list.size()).toBe(3); }); }); diff --git a/src/api/text/List.ts b/src/api/text/List.ts index 0eb26663..35f7542d 100644 --- a/src/api/text/List.ts +++ b/src/api/text/List.ts @@ -1,3 +1,4 @@ +import { ListStyle } from '../style'; import { OdfElement } from '../OdfElement'; import { ListItem } from './ListItem'; @@ -14,6 +15,9 @@ import { ListItem } from './ListItem'; * @since 0.2.0 */ export class List extends OdfElement { + private style: ListStyle | undefined; + private styleName: string | undefined; + /** * Creates a `List` instance that represents a list. * @@ -45,28 +49,6 @@ export class List extends OdfElement { return listItem; } - /** - * The `insertItem` method inserts the specified item at the specified position. - * The item is inserted before the item at the specified position. - * - * If the position is greater than the current number of items, the new item is appended at the end of the list. - * If the position is negative, the new item is inserted as first element. - * - * @example - * const list = new List(); - * list.addItem(); - * list.insertItem(0, new ListItem()); // insert before existing item - * - * @param {number} position The index at which to insert the list item (starting from 0). - * @param {ListItem} item The item to insert - * @returns {ListItem} The inserted `ListItem` object - * @since 0.2.0 - */ - public insertItem(position: number, item: ListItem): ListItem { - this.insert(position, item); - return item; - } - /** * The `getItem()` method returns the item at the specified position in the list. * If an invalid position is given, undefined is returned. @@ -104,6 +86,28 @@ export class List extends OdfElement { return this.getAll() as ListItem[]; } + /** + * The `insertItem` method inserts the specified item at the specified position. + * The item is inserted before the item at the specified position. + * + * If the position is greater than the current number of items, the new item is appended at the end of the list. + * If the position is negative, the new item is inserted as first element. + * + * @example + * const list = new List(); + * list.addItem(); + * list.insertItem(0, new ListItem()); // insert before existing item + * + * @param {number} position The index at which to insert the list item (starting from 0). + * @param {ListItem} item The item to insert + * @returns {ListItem} The inserted `ListItem` object + * @since 0.2.0 + */ + public insertItem(position: number, item: ListItem): ListItem { + this.insert(position, item); + return item; + } + /** * The `removeItemAt()` method removes the list item from the specified position. * @@ -124,6 +128,78 @@ export class List extends OdfElement { return this.removeAt(position) as ListItem; } + /** + * Returns the style of the list. + * + * @example + * const list = new List(); + * list.getStyle(); // undefined + * list.setStyle(new ListStyle()); + * list.getStyle(); // previously set style + * + * @returns {ListStyle | undefined} The style of the list or `undefined` if no style was set + * @since 0.11.0 + */ + public getStyle(): ListStyle | undefined { + return this.style; + } + + /** + * Sets the new style of the list. + * To reset the style, `undefined` must be given. + * + * If style and style name are both set, the custom style will be set and the common style will be ignored. + * + * @example + * new List() + * .setStyle(new ListStyle()); + * + * @param {ListStyle | undefined} style The new style or `undefined` to reset the style + * @returns {List} The `List` object + * @since 0.11.0 + */ + public setStyle(style: ListStyle | undefined): List { + this.style = style; + + return this; + } + + /** + * Returns the name of the common style of the list. + * + * @example + * const list = new List(); + * list.getStyleName(); // undefined + * list.setStyleName('Summary'); + * list.getStyleName(); // 'Summary' + * + * @returns {string | undefined} The name of the common style or `undefined` if no common style was set + * @since 0.11.0 + */ + public getStyleName(): string | undefined { + return this.styleName; + } + + /** + * Sets the name of the common style that should be applied to the list. + * To reset the common style, `undefined` must be given. + * + * If style and style name are both set, the custom style will be set and the common style will be ignored. + * + * @example + * new List() + * .setStyleName('Summary'); + * + * @param {string | undefined} styleName The name of the common style or `undefined` to reset the common style + * @returns {List} The `List` object + * @since 0.11.0 + */ + public setStyleName(styleName: string | undefined): List { + this.styleName = styleName; + + return this; + } + /** * The `clear()` method removes all items from the list. * diff --git a/src/api/util/isNonNegativeNumber.ts b/src/api/util/isNonNegativeNumber.ts index ed2b6e29..2c641d5c 100644 --- a/src/api/util/isNonNegativeNumber.ts +++ b/src/api/util/isNonNegativeNumber.ts @@ -7,6 +7,7 @@ * @returns {boolean} `true` if the given value is a non-negative number, `false` otherwise * @private */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isNonNegativeNumber(value: any): boolean { return typeof value === 'number' && value > 0; } diff --git a/src/api/util/isPercent.ts b/src/api/util/isPercent.ts index b22545e6..573c3579 100644 --- a/src/api/util/isPercent.ts +++ b/src/api/util/isPercent.ts @@ -7,6 +7,7 @@ * @returns {boolean} `true` if the given value is a percentage, `false` otherwise * @private */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isPercent(value: any): boolean { return ( typeof value === 'string' && /^-?([0-9]+(\.[0-9]*)?|\.[0-9]+)%$/.test(value) diff --git a/src/api/util/isPositiveLength.ts b/src/api/util/isPositiveLength.ts index 95cc57a7..c59463eb 100644 --- a/src/api/util/isPositiveLength.ts +++ b/src/api/util/isPositiveLength.ts @@ -7,6 +7,7 @@ * @returns {boolean} `true` if the given value is a positive number, `false` otherwise * @private */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export function isPositiveLength(value: any): boolean { return typeof value === 'number' && value >= 0; } diff --git a/src/index.ts b/src/index.ts index 3f7b1d75..76a7da50 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,6 +21,7 @@ export { FontPitch } from './api/style/FontPitch'; export { FontVariant } from './api/style/FontVariant'; export { HorizontalAlignment } from './api/style/HorizontalAlignment'; export { HorizontalAlignmentLastLine } from './api/style/HorizontalAlignmentLastLine'; +export { ListStyle } from './api/style/ListStyle'; export { PageBreak } from './api/style/PageBreak'; export { ParagraphStyle } from './api/style/ParagraphStyle'; export { Style } from './api/style/Style'; diff --git a/src/xml/AutomaticStyleVisitor.spec.ts b/src/xml/AutomaticStyleVisitor.spec.ts index 09270b30..6dbcf9bd 100644 --- a/src/xml/AutomaticStyleVisitor.spec.ts +++ b/src/xml/AutomaticStyleVisitor.spec.ts @@ -1,85 +1,97 @@ -import { Heading, Paragraph } from '../api/text'; +import { Heading, List, Paragraph } from '../api/text'; import { AutomaticStyles } from '../api/office'; -import { ParagraphStyle } from '../api/style'; +import { ListStyle, ParagraphStyle } from '../api/style'; import { AutomaticStyleVisitor } from './AutomaticStyleVisitor'; describe(AutomaticStyleVisitor.name, () => { - describe('#visit', () => { - const testText = 'some text'; - const testStyleName = 'someStyleName'; + const testText = 'some text'; + const testStyleName = 'someStyleName'; + + let automaticStyleVisitor: AutomaticStyleVisitor; + let automaticStyles: AutomaticStyles; + let heading: Heading; + let list: List; + let paragraph: Paragraph; + + beforeEach(() => { + heading = new Heading(testText, 2); + list = new List(); + paragraph = new Paragraph(testText); + + automaticStyles = new AutomaticStyles(); + jest.spyOn(automaticStyles, 'add'); + jest.spyOn(automaticStyles, 'getAll'); + jest.spyOn(automaticStyles, 'getName'); + + automaticStyleVisitor = new AutomaticStyleVisitor(automaticStyles); + }); - let automaticStyleVisitor: AutomaticStyleVisitor; - let automaticStyles: AutomaticStyles; + it('should skip heading without any style', () => { + automaticStyleVisitor.visit(heading); - beforeEach(() => { - automaticStyles = new AutomaticStyles(); - jest.spyOn(automaticStyles, 'add'); - jest.spyOn(automaticStyles, 'getAll'); - jest.spyOn(automaticStyles, 'getName'); + expect(automaticStyles.add).not.toHaveBeenCalled(); + }); - automaticStyleVisitor = new AutomaticStyleVisitor(automaticStyles); - }); + it('should skip heading with common style', () => { + heading.setStyleName(testStyleName); - describe('#visitHeading', () => { - let heading: Heading; + automaticStyleVisitor.visit(heading); - beforeEach(() => { - heading = new Heading(testText, 2); - }); + expect(automaticStyles.add).not.toHaveBeenCalled(); + }); - it('skip heading without any style', () => { - automaticStyleVisitor.visit(heading); + it('should add automatic style for heading with custom style', () => { + const testStyle = new ParagraphStyle(); + heading.setStyle(testStyle); - expect(automaticStyles.add).not.toHaveBeenCalled(); - }); + automaticStyleVisitor.visit(heading); + + expect(automaticStyles.add).toHaveBeenCalledWith(testStyle); + }); - it('skip heading with common style', () => { - heading.setStyleName(testStyleName); + it('should skip list without any style', () => { + automaticStyleVisitor.visit(list); - automaticStyleVisitor.visit(heading); + expect(automaticStyles.add).not.toHaveBeenCalled(); + }); - expect(automaticStyles.add).not.toHaveBeenCalled(); - }); + it('should skip list with common style', () => { + list.setStyleName(testStyleName); - it('handle heading with automatic style', () => { - const testStyle = new ParagraphStyle(); - heading.setStyle(testStyle); + automaticStyleVisitor.visit(list); - automaticStyleVisitor.visit(heading); + expect(automaticStyles.add).not.toHaveBeenCalled(); + }); - expect(automaticStyles.add).toHaveBeenCalledWith(testStyle); - }); - }); + it('should add automatic style for list with custom style', () => { + const testStyle = new ListStyle(); + list.setStyle(testStyle); - describe('#visitParagraph', () => { - let paragraph: Paragraph; + automaticStyleVisitor.visit(list); - beforeEach(() => { - paragraph = new Paragraph(testText); - }); + expect(automaticStyles.add).toHaveBeenCalledWith(testStyle); + }); - it('skip paragraph without any style', () => { - automaticStyleVisitor.visit(paragraph); + it('should skip paragraph without any style', () => { + automaticStyleVisitor.visit(paragraph); - expect(automaticStyles.add).not.toHaveBeenCalled(); - }); + expect(automaticStyles.add).not.toHaveBeenCalled(); + }); - it('skip paragraph with common style', () => { - paragraph.setStyleName(testStyleName); + it('should skip paragraph with common style', () => { + paragraph.setStyleName(testStyleName); - automaticStyleVisitor.visit(paragraph); + automaticStyleVisitor.visit(paragraph); - expect(automaticStyles.add).not.toHaveBeenCalled(); - }); + expect(automaticStyles.add).not.toHaveBeenCalled(); + }); - it('handle paragraph with automatic style', () => { - const testStyle = new ParagraphStyle(); - paragraph.setStyle(testStyle); + it('should add automatic style for paragraph with custom style', () => { + const testStyle = new ParagraphStyle(); + paragraph.setStyle(testStyle); - automaticStyleVisitor.visit(paragraph); + automaticStyleVisitor.visit(paragraph); - expect(automaticStyles.add).toHaveBeenCalledWith(testStyle); - }); - }); + expect(automaticStyles.add).toHaveBeenCalledWith(testStyle); }); }); diff --git a/src/xml/AutomaticStyleVisitor.ts b/src/xml/AutomaticStyleVisitor.ts index 3454881c..b5ebdaab 100644 --- a/src/xml/AutomaticStyleVisitor.ts +++ b/src/xml/AutomaticStyleVisitor.ts @@ -1,6 +1,7 @@ import { OdfElement } from '../api/OdfElement'; import { AutomaticStyles } from '../api/office'; -import { Heading, Paragraph } from '../api/text'; +import { Style } from '../api/style'; +import { Heading, Paragraph, List } from '../api/text'; export class AutomaticStyleVisitor { public constructor(private automaticStyles: AutomaticStyles) {} @@ -8,6 +9,8 @@ export class AutomaticStyleVisitor { public visit(odfElement: OdfElement): void { if (odfElement instanceof Heading) { this.visitHeading(odfElement, this.automaticStyles); + } else if (odfElement instanceof List) { + this.visitList(odfElement, this.automaticStyles); } else if (odfElement instanceof Paragraph) { this.visitParagraph(odfElement, this.automaticStyles); } @@ -21,21 +24,24 @@ export class AutomaticStyleVisitor { heading: Heading, automaticStyles: AutomaticStyles ): void { - const style = heading.getStyle(); - - if (style === undefined) { - return; - } + this.addStyle(heading.getStyle(), automaticStyles); + } - automaticStyles.add(style); + private visitList(list: List, automaticStyles: AutomaticStyles): void { + this.addStyle(list.getStyle(), automaticStyles); } private visitParagraph( paragraph: Paragraph, automaticStyles: AutomaticStyles ): void { - const style = paragraph.getStyle(); + this.addStyle(paragraph.getStyle(), automaticStyles); + } + private addStyle( + style: Style | undefined, + automaticStyles: AutomaticStyles + ): void { if (style === undefined) { return; } diff --git a/src/xml/DomVisitor.spec.ts b/src/xml/DomVisitor.spec.ts index 5a1f6852..af7a6f4e 100644 --- a/src/xml/DomVisitor.spec.ts +++ b/src/xml/DomVisitor.spec.ts @@ -5,7 +5,7 @@ import { Heading, Hyperlink, List, Paragraph } from '../api/text'; import { DomVisitor } from './DomVisitor'; import { OdfElementName } from './OdfElementName'; import { AutomaticStyles, CommonStyles } from '../api/office'; -import { ParagraphStyle } from '../api/style'; +import { ParagraphStyle, ListStyle } from '../api/style'; class AutomaticStylesMock extends AutomaticStyles { public getName(): string { @@ -195,6 +195,34 @@ describe(DomVisitor.name, () => { /some text<\/text:p><\/text:list-item><\/text:list>/ ); }); + + it('add a list with a common style', () => { + list.setStyleName('testStyleName'); + list.addItem().addParagraph(testText); + + domVisitor.visit(list, testDocument, testRoot); + + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + expect(documentAsString).toMatch( + /some text<\/text:p><\/text:list-item><\/text:list>/ + ); + }); + + it('add a list with an automatic style', () => { + list.setStyle(new ListStyle()); + list.addItem().addParagraph(testText); + + domVisitor.visit(list, testDocument, testRoot); + + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + expect(documentAsString).toMatch( + /some text<\/text:p><\/text:list-item><\/text:list>/ + ); + }); }); describe('visitOdfText', () => { diff --git a/src/xml/DomVisitor.ts b/src/xml/DomVisitor.ts index 19374ce8..834bbb6d 100644 --- a/src/xml/DomVisitor.ts +++ b/src/xml/DomVisitor.ts @@ -130,6 +130,8 @@ export class DomVisitor { const listElement = document.createElement(TextElementName.TextList); parent.appendChild(listElement); + this.setStyleName(list, listElement); + return listElement; } @@ -177,7 +179,7 @@ export class DomVisitor { } private setStyleName( - odfElement: Heading | Paragraph, + odfElement: Heading | List | Paragraph, domElement: Element ): void { let styleName = this.getAutomaticStyleName(odfElement); @@ -192,7 +194,7 @@ export class DomVisitor { } private getAutomaticStyleName( - odfElement: Heading | Paragraph + odfElement: Heading | List | Paragraph ): string | undefined { const style = odfElement.getStyle(); @@ -202,7 +204,7 @@ export class DomVisitor { } private getCommonStyleName( - odfElement: Heading | Paragraph + odfElement: Heading | List | Paragraph ): string | undefined { const styleName = odfElement.getStyleName(); diff --git a/src/xml/OdfAttributeName.ts b/src/xml/OdfAttributeName.ts index 475cd928..550d9ee5 100644 --- a/src/xml/OdfAttributeName.ts +++ b/src/xml/OdfAttributeName.ts @@ -40,6 +40,8 @@ export enum OdfAttributeName { StyleLineHeightAtLeast = 'style:line-height-at-least', StyleLineSpacing = 'style:line-spacing', StyleName = 'style:name', + StyleNumPrefix = 'style:num-prefix', + StyleNumSuffix = 'style:num-suffix', StylePosition = 'style:position', StyleType = 'style:type', StyleVerticalAlign = 'style:vertical-align', @@ -48,6 +50,13 @@ export enum OdfAttributeName { SvgWidth = 'svg:width', TextAnchorType = 'text:anchor-type', + TextBulletChar = 'text:bullet-char', + TextBulletRelativeSize = 'text:bullet-relative-size', + TextConsecutiveNumbering = 'text:consecutive-numbering', + TextLabelFollowedBy = 'text:label-followed-by', + TextLevel = 'text:level', + TextListLevelPositionAndSpaceMode = 'text:list-level-position-and-space-mode', + TextListTabStopPosition = 'text:list-tab-stop-position', TextOutlineLevel = 'text:outline-level', TextStyleName = 'text:style-name', diff --git a/src/xml/OdfElementName.ts b/src/xml/OdfElementName.ts index c4aed310..bc5ae56b 100644 --- a/src/xml/OdfElementName.ts +++ b/src/xml/OdfElementName.ts @@ -9,9 +9,14 @@ export enum OdfElementName { OfficeText = 'office:text', StyleFontFace = 'style:font-face', + StyleListLevelLabelAlignment = 'style:list-level-label-alignment', + StyleListLevelProperties = 'style:list-level-properties', StyleParagraphProperties = 'style:paragraph-properties', StyleStyle = 'style:style', StyleTabStop = 'style:tab-stop', StyleTabStops = 'style:tab-stops', StyleTextProperties = 'style:text-properties', + + TextListStyle = 'text:list-style', + TextListLevelStyleBullet = 'text:list-level-style-bullet', } diff --git a/src/xml/office/ListLevelStyleVisitor.ts b/src/xml/office/ListLevelStyleVisitor.ts new file mode 100644 index 00000000..2a0480df --- /dev/null +++ b/src/xml/office/ListLevelStyleVisitor.ts @@ -0,0 +1,108 @@ +import { BulletListLevelStyle } from '../../api/style'; +import { OdfElementName } from '../OdfElementName'; +import { OdfAttributeName } from '../OdfAttributeName'; + +export class ListLevelStyleVisitor { + public visit( + listLevelStyle: BulletListLevelStyle, + document: Document, + parent: Element + ): Element { + const listLevelStyleElement = document.createElement( + OdfElementName.TextListLevelStyleBullet + ); + parent.appendChild(listLevelStyleElement); + + listLevelStyleElement.setAttribute( + OdfAttributeName.TextLevel, + `${listLevelStyle.getLevel()}` + ); + + listLevelStyleElement.setAttribute( + OdfAttributeName.TextBulletChar, + listLevelStyle.getBulletChar() + ); + + const numberPrefix = listLevelStyle.getNumberPrefix(); + if (numberPrefix !== undefined) { + listLevelStyleElement.setAttribute( + OdfAttributeName.StyleNumPrefix, + numberPrefix + ); + } + + const numberSuffix = listLevelStyle.getNumberSuffix(); + if (numberSuffix !== undefined) { + listLevelStyleElement.setAttribute( + OdfAttributeName.StyleNumSuffix, + numberSuffix + ); + } + + const relativeBulletSize = listLevelStyle.getRelativeBulletSize(); + if (relativeBulletSize !== undefined) { + listLevelStyleElement.setAttribute( + OdfAttributeName.TextBulletRelativeSize, + relativeBulletSize + ); + } + + this.setListLevelProperties( + listLevelStyle, + document, + listLevelStyleElement + ); + + return listLevelStyleElement; + } + + private setListLevelProperties( + listLevelStyle: BulletListLevelStyle, + document: Document, + parent: Element + ): void { + const listLevelPropertiesElement = document.createElement( + OdfElementName.StyleListLevelProperties + ); + parent.appendChild(listLevelPropertiesElement); + + listLevelPropertiesElement.setAttribute( + OdfAttributeName.TextListLevelPositionAndSpaceMode, + listLevelStyle.getListLevelPositionAndSpaceMode() + ); + + const listLevelLabelAlignmentElement = document.createElement( + OdfElementName.StyleListLevelLabelAlignment + ); + listLevelPropertiesElement.appendChild(listLevelLabelAlignmentElement); + + listLevelLabelAlignmentElement.setAttribute( + OdfAttributeName.TextLabelFollowedBy, + listLevelStyle.getLabelFollowedBy() + ); + + const tabStopPosition = listLevelStyle.getListTabStopPosition(); + if (tabStopPosition !== undefined) { + listLevelLabelAlignmentElement.setAttribute( + OdfAttributeName.TextListTabStopPosition, + `${tabStopPosition}mm` + ); + } + + const textIndent = listLevelStyle.getTextIndent(); + if (textIndent !== undefined) { + listLevelLabelAlignmentElement.setAttribute( + OdfAttributeName.FormatTextIndent, + `${textIndent}mm` + ); + } + + const marginLeft = listLevelStyle.getMarginLeft(); + if (marginLeft !== undefined) { + listLevelLabelAlignmentElement.setAttribute( + OdfAttributeName.FormatMarginLeft, + `${marginLeft}mm` + ); + } + } +} diff --git a/src/xml/office/ListStyleVisitor.ts b/src/xml/office/ListStyleVisitor.ts new file mode 100644 index 00000000..19a015a2 --- /dev/null +++ b/src/xml/office/ListStyleVisitor.ts @@ -0,0 +1,16 @@ +import { ListStyle } from '../../api/style'; +import { OdfAttributeName } from '../OdfAttributeName'; + +export class ListStyleVisitor { + public visit(listStyle: ListStyle, parent: Element): Element { + const consecutiveNumbering = listStyle.getConsecutiveNumbering(); + if (consecutiveNumbering === true) { + parent.setAttribute( + OdfAttributeName.TextConsecutiveNumbering, + consecutiveNumbering.toString() + ); + } + + return parent; + } +} diff --git a/src/xml/office/ParagraphPropertiesVisitor.ts b/src/xml/office/ParagraphPropertiesVisitor.ts new file mode 100644 index 00000000..32e09b03 --- /dev/null +++ b/src/xml/office/ParagraphPropertiesVisitor.ts @@ -0,0 +1,323 @@ +import { IParagraphProperties } from '../../api/style/IParagraphProperties'; +import { + BorderStyle, + HorizontalAlignment, + HorizontalAlignmentLastLine, + PageBreak, + TabStopLeaderStyle, + TabStopType, + VerticalAlignment, +} from '../../api/style'; +import { OdfAttributeName } from '../OdfAttributeName'; +import { OdfElementName } from '../OdfElementName'; + +export class ParagraphPropertiesVisitor { + public visit( + paragraphProperties: IParagraphProperties, + document: Document, + parent: Element + ): Element { + const paragraphPropertiesElement = document.createElement( + OdfElementName.StyleParagraphProperties + ); + parent.appendChild(paragraphPropertiesElement); + + const lineHeight = paragraphProperties.getLineHeight(); + switch (typeof lineHeight) { + case 'number': + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatLineHeight, + lineHeight + 'mm' + ); + break; + case 'string': + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatLineHeight, + lineHeight + ); + break; + default: + break; + } + + const lineHeightAtLeast = paragraphProperties.getLineHeightAtLeast(); + if (lineHeightAtLeast !== undefined) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.StyleLineHeightAtLeast, + lineHeightAtLeast + 'mm' + ); + } + + const lineSpacing = paragraphProperties.getLineSpacing(); + if (lineSpacing !== undefined) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.StyleLineSpacing, + lineSpacing + 'mm' + ); + } + + const horizontalAlignment = paragraphProperties.getHorizontalAlignment(); + if (horizontalAlignment !== HorizontalAlignment.Default) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatTextAlign, + horizontalAlignment + ); + } + + const horizontalAlignmentLastLine = paragraphProperties.getHorizontalAlignmentLastLine(); + if ( + horizontalAlignment === HorizontalAlignment.Justify && + horizontalAlignmentLastLine !== HorizontalAlignmentLastLine.Default + ) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatTextAlignLast, + horizontalAlignmentLastLine + ); + } + + if (paragraphProperties.getKeepTogether() === true) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatKeepTogether, + 'always' + ); + } + + const widows = paragraphProperties.getWidows(); + if (widows !== undefined) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatWidows, + widows.toString(10) + ); + } + + const orphans = paragraphProperties.getOrphans(); + if (orphans !== undefined) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatOrphans, + orphans.toString(10) + ); + } + + const marginLeft = paragraphProperties.getMarginLeft(); + if (marginLeft !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatMarginLeft, + marginLeft.toString(10) + 'mm' + ); + } + + const marginRight = paragraphProperties.getMarginRight(); + if (marginRight !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatMarginRight, + marginRight.toString(10) + 'mm' + ); + } + + const textIndent = paragraphProperties.getTextIndent(); + if (textIndent !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatTextIndent, + textIndent.toString(10) + 'mm' + ); + } + + const marginTop = paragraphProperties.getMarginTop(); + if (marginTop !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatMarginTop, + marginTop.toString(10) + 'mm' + ); + } + + const marginBottom = paragraphProperties.getMarginBottom(); + if (marginBottom !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatMarginBottom, + marginBottom.toString(10) + 'mm' + ); + } + + switch (paragraphProperties.getPageBreak()) { + case PageBreak.Before: + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBreakBefore, + 'page' + ); + break; + case PageBreak.After: + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBreakAfter, + 'page' + ); + break; + default: + break; + } + + const backgroundColor = paragraphProperties.getBackgroundColor(); + if (backgroundColor !== undefined) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBackgroundColor, + backgroundColor.toHex() + ); + } + + const borderTop = paragraphProperties.getBorderTop(); + if ( + borderTop !== undefined && + borderTop.width > 0 && + borderTop.style !== BorderStyle.None + ) { + const border = `${borderTop.width}mm ${ + borderTop.style + } ${borderTop.color.toHex()}`; + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBorderTop, + border + ); + } + + const borderBottom = paragraphProperties.getBorderBottom(); + if ( + borderBottom !== undefined && + borderBottom.width > 0 && + borderBottom.style !== BorderStyle.None + ) { + const border = `${borderBottom.width}mm ${ + borderBottom.style + } ${borderBottom.color.toHex()}`; + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBorderBottom, + border + ); + } + + const borderLeft = paragraphProperties.getBorderLeft(); + if ( + borderLeft !== undefined && + borderLeft.width > 0 && + borderLeft.style !== BorderStyle.None + ) { + const border = `${borderLeft.width}mm ${ + borderLeft.style + } ${borderLeft.color.toHex()}`; + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBorderLeft, + border + ); + } + + const borderRight = paragraphProperties.getBorderRight(); + if ( + borderRight !== undefined && + borderRight.width > 0 && + borderRight.style !== BorderStyle.None + ) { + const border = `${borderRight.width}mm ${ + borderRight.style + } ${borderRight.color.toHex()}`; + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatBorderRight, + border + ); + } + + const paddingLeft = paragraphProperties.getPaddingLeft(); + if (paddingLeft !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatPaddingLeft, + paddingLeft.toString(10) + 'mm' + ); + } + + const paddingRight = paragraphProperties.getPaddingRight(); + if (paddingRight !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatPaddingRight, + paddingRight.toString(10) + 'mm' + ); + } + + const paddingTop = paragraphProperties.getPaddingTop(); + if (paddingTop !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatPaddingTop, + paddingTop.toString(10) + 'mm' + ); + } + + const paddingBottom = paragraphProperties.getPaddingBottom(); + if (paddingBottom !== 0) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatPaddingBottom, + paddingBottom.toString(10) + 'mm' + ); + } + + if (paragraphProperties.getKeepWithNext() === true) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.FormatKeepWithNext, + 'always' + ); + } + + if ( + paragraphProperties.getVerticalAlignment() !== VerticalAlignment.Default + ) { + paragraphPropertiesElement.setAttribute( + OdfAttributeName.StyleVerticalAlign, + paragraphProperties.getVerticalAlignment() + ); + } + + const tabStops = paragraphProperties.getTabStops(); + if (tabStops.length === 0) { + return paragraphPropertiesElement; + } + + const tabStopsElement = document.createElement( + OdfElementName.StyleTabStops + ); + paragraphPropertiesElement.appendChild(tabStopsElement); + + tabStops.forEach((tabStop) => { + const tabStopElement = document.createElement( + OdfElementName.StyleTabStop + ); + tabStopsElement.appendChild(tabStopElement); + + tabStopElement.setAttribute( + OdfAttributeName.StylePosition, + tabStop.getPosition() + 'mm' + ); + + const type = tabStop.getType(); + const char = tabStop.getChar(); + if (type === TabStopType.Char && char !== undefined) { + tabStopElement.setAttribute(OdfAttributeName.StyleType, type); + tabStopElement.setAttribute(OdfAttributeName.StyleChar, char); + } else if (type !== TabStopType.Left) { + tabStopElement.setAttribute(OdfAttributeName.StyleType, type); + } + + const leaderStyle = tabStop.getLeaderStyle(); + if (leaderStyle !== TabStopLeaderStyle.None) { + tabStopElement.setAttribute( + OdfAttributeName.StyleLeaderStyle, + leaderStyle + ); + } + + const leaderColor = tabStop.getLeaderColor(); + if (leaderColor !== undefined) { + tabStopElement.setAttribute( + OdfAttributeName.StyleLeaderColor, + leaderColor.toHex() + ); + } + }); + + return paragraphPropertiesElement; + } +} diff --git a/src/xml/office/StylesWriter.spec.ts b/src/xml/office/StylesWriter.spec.ts index 14931319..3de4575b 100644 --- a/src/xml/office/StylesWriter.spec.ts +++ b/src/xml/office/StylesWriter.spec.ts @@ -6,6 +6,7 @@ import { FontVariant, HorizontalAlignment, HorizontalAlignmentLastLine, + ListStyle, PageBreak, ParagraphStyle, TabStop, @@ -14,80 +15,98 @@ import { TextTransformation, Typeface, VerticalAlignment, + BulletListLevelStyle, } from '../../api/style'; import { OdfElementName } from '../OdfElementName'; import { StylesWriter } from './StylesWriter'; describe(StylesWriter.name, () => { - describe('#write', () => { - let stylesWriter: StylesWriter; - let testDocument: Document; - let testRoot: Element; - let commonStyles: CommonStyles; + let stylesWriter: StylesWriter; + let testDocument: Document; + let testRoot: Element; + let commonStyles: CommonStyles; + + beforeEach(() => { + testDocument = new DOMImplementation().createDocument( + 'someNameSpace', + OdfElementName.OfficeDocument, + null + ); + testRoot = testDocument.firstChild as Element; + commonStyles = new CommonStyles(); + + stylesWriter = new StylesWriter(); + }); - beforeEach(() => { - testDocument = new DOMImplementation().createDocument( - 'someNameSpace', - OdfElementName.OfficeDocument, - null - ); - testRoot = testDocument.firstChild as Element; - commonStyles = new CommonStyles(); + it('should not add any style elements if no style is defined', () => { + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - stylesWriter = new StylesWriter(); - }); + expect(documentAsString).not.toMatch(/office:styles/); + }); - it('do nothing if no style is defined', () => { - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('should add a common style', () => { + commonStyles.createParagraphStyle('Summary'); - expect(documentAsString).not.toMatch(/office:styles/); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('add common style', () => { - commonStyles.createParagraphStyle('Summary'); + expect(documentAsString).toMatch( + /.*<\/style:style><\/office:styles>/ + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('should add an automatic style', () => { + const automaticStyles = new AutomaticStyles(); + automaticStyles.add(new ParagraphStyle()); - expect(documentAsString).toMatch( - /.*<\/style:style><\/office:styles>/ - ); - }); + stylesWriter.write(automaticStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('add automatic style', () => { - const automaticStyles = new AutomaticStyles(); - automaticStyles.add(new ParagraphStyle()); + expect(documentAsString).toMatch( + /.*<\/style:style><\/office:automatic-styles>/ + ); + }); - stylesWriter.write(automaticStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('should add style and set class', () => { + commonStyles.createParagraphStyle('Summary').setClass('someClass'); - expect(documentAsString).toMatch( - /.*<\/style:style><\/office:automatic-styles>/ - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + /.*<\/style:style><\/office:styles>/ + ); + }); + + describe('list', () => { + let testStyle: ListStyle; - it('add style and set class', () => { - commonStyles.createParagraphStyle('Summary').setClass('someClass'); + beforeEach(() => { + testStyle = commonStyles.createListStyle('Contents'); + }); + it('should add list style', () => { stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( testDocument ); expect(documentAsString).toMatch( - /.*<\/style:style><\/office:styles>/ + /<\/office:styles>/ ); }); - it('add paragraph style', () => { - commonStyles.createParagraphStyle('Summary'); + it('should set consecutive numbering', () => { + testStyle.setConsecutiveNumbering(true); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -95,36 +114,30 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - /<\/style:style><\/office:styles>/ + /<\/office:styles>/ ); }); - describe('paragraph properties', () => { - let testStyle: ParagraphStyle; + describe('bullet list', () => { + let bulletListLevelStyle: BulletListLevelStyle; beforeEach(() => { - testStyle = commonStyles.createParagraphStyle('Summary'); + bulletListLevelStyle = testStyle.createBulletListLevelStyle(3); }); - it('set background color', () => { - testStyle.setBackgroundColor(Color.fromRgb(1, 2, 3)); - + it('should add a bullet list level style with level', () => { stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( testDocument ); expect(documentAsString).toMatch( - // + /<\/style:list-level-properties><\/text:list-level-style-bullet><\/text:list-style><\/office:styles>/ ); }); - it('set border bottom', () => { - testStyle.setBorderBottom( - 23.42, - BorderStyle.Dashed, - Color.fromRgb(1, 2, 3) - ); + it('should set bullet character', () => { + bulletListLevelStyle.setBulletChar('§'); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -132,16 +145,12 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // ); }); - it('set border left', () => { - testStyle.setBorderLeft( - 23.42, - BorderStyle.Dotted, - Color.fromRgb(1, 2, 3) - ); + it('should set number prefix', () => { + bulletListLevelStyle.setNumberPrefix('#'); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -149,16 +158,12 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // ); }); - it('set border right', () => { - testStyle.setBorderRight( - 23.42, - BorderStyle.Double, - Color.fromRgb(1, 2, 3) - ); + it('should set number suffix', () => { + bulletListLevelStyle.setNumberSuffix(':'); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -166,16 +171,12 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // ); }); - it('set border top', () => { - testStyle.setBorderTop( - 23.42, - BorderStyle.Solid, - Color.fromRgb(1, 2, 3) - ); + it('should set relative bullet size', () => { + bulletListLevelStyle.setRelativeBulletSize('23%'); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -183,12 +184,13 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // ); }); - it('set horizontal alignment', () => { - testStyle.setHorizontalAlignment(HorizontalAlignment.Center); + // list-level-properties + it('should set label followed by', () => { + bulletListLevelStyle.setLabelFollowedBy('space'); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -196,37 +198,25 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + /<\/style:list-level-properties>/ ); }); - it('set horizontal alignment of last line if horizontal alignment is justify', () => { - testStyle.setHorizontalAlignmentLastLine( - HorizontalAlignmentLastLine.Center - ); + it('should set list tab stop position', () => { + bulletListLevelStyle.setListTabStopPosition(23); stylesWriter.write(commonStyles, testDocument, testRoot); - let documentAsString = new XMLSerializer().serializeToString( + const documentAsString = new XMLSerializer().serializeToString( testDocument ); - expect(documentAsString).not.toMatch(/fo:text-align-last/); - - testStyle.setHorizontalAlignment(HorizontalAlignment.Justify); - testStyle.setHorizontalAlignmentLastLine( - HorizontalAlignmentLastLine.Center - ); - - stylesWriter.write(commonStyles, testDocument, testRoot); - documentAsString = new XMLSerializer().serializeToString(testDocument); - expect(documentAsString).toMatch( - // + /<\/style:list-level-properties>/ ); }); - it('set keep together', () => { - testStyle.setKeepTogether(true); + it('should set text indent', () => { + bulletListLevelStyle.setTextIndent(23); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -234,12 +224,12 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + /<\/style:list-level-properties>/ ); }); - it('set keep with next', () => { - testStyle.setKeepWithNext(true); + it('should set left margin', () => { + bulletListLevelStyle.setMarginLeft(23); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -247,407 +237,410 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + /<\/style:list-level-properties>/ ); }); + }); + }); - it('set line height as fix value', () => { - testStyle.setLineHeight(23); - - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('add paragraph style', () => { + commonStyles.createParagraphStyle('Summary'); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set line height as percentage', () => { - testStyle.setLineHeight('42%'); + expect(documentAsString).toMatch( + /<\/style:style><\/office:styles>/ + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + describe('paragraph properties', () => { + let testStyle: ParagraphStyle; - expect(documentAsString).toMatch( - // - ); - }); + beforeEach(() => { + testStyle = commonStyles.createParagraphStyle('Summary'); + }); - it('set line height at least', () => { - testStyle.setLineHeightAtLeast(23); + it('set background color', () => { + testStyle.setBackgroundColor(Color.fromRgb(1, 2, 3)); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set line spacing', () => { - testStyle.setLineSpacing(23); + it('set border bottom', () => { + testStyle.setBorderBottom( + 23.42, + BorderStyle.Dashed, + Color.fromRgb(1, 2, 3) + ); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set margin bottom', () => { - testStyle.setMarginBottom(23.42); + it('set border left', () => { + testStyle.setBorderLeft( + 23.42, + BorderStyle.Dotted, + Color.fromRgb(1, 2, 3) + ); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set margin left', () => { - testStyle.setMarginLeft(23.42); + it('set border right', () => { + testStyle.setBorderRight( + 23.42, + BorderStyle.Double, + Color.fromRgb(1, 2, 3) + ); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set margin right', () => { - testStyle.setMarginRight(23.42); + it('set border top', () => { + testStyle.setBorderTop(23.42, BorderStyle.Solid, Color.fromRgb(1, 2, 3)); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set margin top', () => { - testStyle.setMarginTop(23.42); + it('set horizontal alignment', () => { + testStyle.setHorizontalAlignment(HorizontalAlignment.Center); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set orphans', () => { - testStyle.setOrphans(23); + it('set horizontal alignment of last line if horizontal alignment is justify', () => { + testStyle.setHorizontalAlignmentLastLine( + HorizontalAlignmentLastLine.Center + ); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + let documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).not.toMatch(/fo:text-align-last/); - it('set padding bottom', () => { - testStyle.setPaddingBottom(23.42); + testStyle.setHorizontalAlignment(HorizontalAlignment.Justify); + testStyle.setHorizontalAlignmentLastLine( + HorizontalAlignmentLastLine.Center + ); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + documentAsString = new XMLSerializer().serializeToString(testDocument); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set padding left', () => { - testStyle.setPaddingLeft(23.42); + it('set keep together', () => { + testStyle.setKeepTogether(true); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set padding right', () => { - testStyle.setPaddingRight(23.42); + it('set keep with next', () => { + testStyle.setKeepWithNext(true); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set padding top', () => { - testStyle.setPaddingTop(23.42); + it('set line height as fix value', () => { + testStyle.setLineHeight(23); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set page break before', () => { - testStyle.setPageBreak(PageBreak.Before); + it('set line height as percentage', () => { + testStyle.setLineHeight('42%'); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set page break after', () => { - testStyle.setPageBreak(PageBreak.After); + it('set line height at least', () => { + testStyle.setLineHeightAtLeast(23); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set text indent', () => { - testStyle.setTextIndent(23.42); + it('set line spacing', () => { + testStyle.setLineSpacing(23); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set vertical alignment', () => { - testStyle.setVerticalAlignment(VerticalAlignment.Middle); + it('set margin bottom', () => { + testStyle.setMarginBottom(23.42); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - it('set widows', () => { - testStyle.setWidows(23); + it('set margin left', () => { + testStyle.setMarginLeft(23.42); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - expect(documentAsString).toMatch( - // - ); - }); + expect(documentAsString).toMatch( + // + ); + }); - describe('tab stops', () => { - it('set tab stop', () => { - testStyle.addTabStop(new TabStop(2, TabStopType.Left)); - - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); - - expect(documentAsString).toMatch( - /<\/style:tab-stops><\/style:paragraph-properties>/ - ); - }); - - it('set tab stop types', () => { - testStyle.addTabStop(new TabStop(2, TabStopType.Center)); - testStyle.addTabStop(new TabStop(4, TabStopType.Char).setChar('~')); - testStyle.addTabStop(new TabStop(5, TabStopType.Char)); - testStyle.addTabStop(new TabStop(6, TabStopType.Left)); - testStyle.addTabStop(new TabStop(8, TabStopType.Right)); - - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); - - expect(documentAsString).toMatch( - // - ); - expect(documentAsString).toMatch( - // - ); - expect(documentAsString).toMatch( - // - ); - expect(documentAsString).toMatch( - // - ); - }); - - it('set leader style', () => { - testStyle.addTabStop( - new TabStop(2, TabStopType.Center).setLeaderStyle( - TabStopLeaderStyle.Dotted - ) - ); - - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); - - expect(documentAsString).toMatch( - // - ); - }); - - it('set leader color', () => { - testStyle.addTabStop( - new TabStop(2, TabStopType.Center).setLeaderColor( - Color.fromRgb(1, 2, 3) - ) - ); - - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); - - expect(documentAsString).toMatch( - // - ); - }); - }); + it('set margin right', () => { + testStyle.setMarginRight(23.42); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); }); - describe('text properties', () => { - let testStyle: ParagraphStyle; + it('set margin top', () => { + testStyle.setMarginTop(23.42); - beforeEach(() => { - testStyle = commonStyles.createParagraphStyle('Summary'); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set color', () => { - testStyle.setColor(Color.fromRgb(1, 2, 3)); + expect(documentAsString).toMatch( + // + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('set orphans', () => { + testStyle.setOrphans(23); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set font name', () => { - testStyle.setFontName('someFontName'); + expect(documentAsString).toMatch( + // + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('set padding bottom', () => { + testStyle.setPaddingBottom(23.42); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set font size', () => { - testStyle.setFontSize(23); + expect(documentAsString).toMatch( + // + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('set padding left', () => { + testStyle.setPaddingLeft(23.42); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set font variant', () => { - testStyle.setFontVariant(FontVariant.SmallCaps); + expect(documentAsString).toMatch( + // + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('set padding right', () => { + testStyle.setPaddingRight(23.42); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set text transformation', () => { - testStyle.setTextTransformation(TextTransformation.Capitalize); + expect(documentAsString).toMatch( + // + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('set padding top', () => { + testStyle.setPaddingTop(23.42); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); - it('set the font-style for italic', () => { - testStyle.setTypeface(Typeface.Italic); + expect(documentAsString).toMatch( + // + ); + }); - stylesWriter.write(commonStyles, testDocument, testRoot); - const documentAsString = new XMLSerializer().serializeToString( - testDocument - ); + it('set page break before', () => { + testStyle.setPageBreak(PageBreak.Before); - expect(documentAsString).toMatch( - // - ); - }); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set page break after', () => { + testStyle.setPageBreak(PageBreak.After); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set text indent', () => { + testStyle.setTextIndent(23.42); - it('set the font-style for oblique', () => { - testStyle.setTypeface(Typeface.Oblique); + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set vertical alignment', () => { + testStyle.setVerticalAlignment(VerticalAlignment.Middle); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set widows', () => { + testStyle.setWidows(23); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + describe('tab stops', () => { + it('set tab stop', () => { + testStyle.addTabStop(new TabStop(2, TabStopType.Left)); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -655,12 +648,16 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + /<\/style:tab-stops><\/style:paragraph-properties>/ ); }); - it('set the font-weight for bold', () => { - testStyle.setTypeface(Typeface.Bold); + it('set tab stop types', () => { + testStyle.addTabStop(new TabStop(2, TabStopType.Center)); + testStyle.addTabStop(new TabStop(4, TabStopType.Char).setChar('~')); + testStyle.addTabStop(new TabStop(5, TabStopType.Char)); + testStyle.addTabStop(new TabStop(6, TabStopType.Left)); + testStyle.addTabStop(new TabStop(8, TabStopType.Right)); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -668,12 +665,25 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // + ); + expect(documentAsString).toMatch( + // + ); + expect(documentAsString).toMatch( + // + ); + expect(documentAsString).toMatch( + // ); }); - it('set the font-style and font-weight for bold-italic', () => { - testStyle.setTypeface(Typeface.BoldItalic); + it('set leader style', () => { + testStyle.addTabStop( + new TabStop(2, TabStopType.Center).setLeaderStyle( + TabStopLeaderStyle.Dotted + ) + ); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -681,12 +691,16 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // ); }); - it('set the font-style and font-weight for bold-oblique', () => { - testStyle.setTypeface(Typeface.BoldOblique); + it('set leader color', () => { + testStyle.addTabStop( + new TabStop(2, TabStopType.Center).setLeaderColor( + Color.fromRgb(1, 2, 3) + ) + ); stylesWriter.write(commonStyles, testDocument, testRoot); const documentAsString = new XMLSerializer().serializeToString( @@ -694,9 +708,147 @@ describe(StylesWriter.name, () => { ); expect(documentAsString).toMatch( - // + // ); }); }); }); + + describe('text properties', () => { + let testStyle: ParagraphStyle; + + beforeEach(() => { + testStyle = commonStyles.createParagraphStyle('Summary'); + }); + + it('set color', () => { + testStyle.setColor(Color.fromRgb(1, 2, 3)); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set font name', () => { + testStyle.setFontName('someFontName'); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set font size', () => { + testStyle.setFontSize(23); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set font variant', () => { + testStyle.setFontVariant(FontVariant.SmallCaps); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set text transformation', () => { + testStyle.setTextTransformation(TextTransformation.Capitalize); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set the font-style for italic', () => { + testStyle.setTypeface(Typeface.Italic); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set the font-style for oblique', () => { + testStyle.setTypeface(Typeface.Oblique); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set the font-weight for bold', () => { + testStyle.setTypeface(Typeface.Bold); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set the font-style and font-weight for bold-italic', () => { + testStyle.setTypeface(Typeface.BoldItalic); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + + it('set the font-style and font-weight for bold-oblique', () => { + testStyle.setTypeface(Typeface.BoldOblique); + + stylesWriter.write(commonStyles, testDocument, testRoot); + const documentAsString = new XMLSerializer().serializeToString( + testDocument + ); + + expect(documentAsString).toMatch( + // + ); + }); + }); }); diff --git a/src/xml/office/StylesWriter.ts b/src/xml/office/StylesWriter.ts index 261a9605..3ebfb4f1 100644 --- a/src/xml/office/StylesWriter.ts +++ b/src/xml/office/StylesWriter.ts @@ -1,21 +1,10 @@ import { AutomaticStyles, CommonStyles, IStyles } from '../../api/office'; -import { - BorderStyle, - FontVariant, - HorizontalAlignment, - HorizontalAlignmentLastLine, - PageBreak, - ParagraphStyle, - Style, - StyleFamily, - TabStopLeaderStyle, - TabStopType, - TextTransformation, - Typeface, - VerticalAlignment, -} from '../../api/style'; -import { OdfAttributeName } from '../OdfAttributeName'; +import { ListStyle, ParagraphStyle, Style, StyleFamily } from '../../api/style'; import { OdfElementName } from '../OdfElementName'; +import { ListLevelStyleVisitor } from './ListLevelStyleVisitor'; +import { ListStyleVisitor } from './ListStyleVisitor'; +import { ParagraphPropertiesVisitor } from './ParagraphPropertiesVisitor'; +import { TextPropertiesVisitor } from './TextPropertiesVisitor'; /** * Transforms a {@link StyleManager} object into ODF conform XML @@ -58,438 +47,61 @@ export class StylesWriter { document: Document, parent: Element ): Element { - const styleElement = document.createElement(OdfElementName.StyleStyle); + const styleElement = this.createStyleElement(style, document); parent.appendChild(styleElement); - const styleName = style.getName(); - if (styleName === Style.UNNAMED) { - styleElement.setAttribute( - 'style:name', - (styles as AutomaticStyles).getName(style) - ); - } else { - styleElement.setAttribute('style:name', style.getName()); - styleElement.setAttribute('style:display-name', style.getDisplayName()); - } + this.setName(styleElement, style, styles); + this.setFamily(styleElement, style); + this.setClass(styleElement, style); - styleElement.setAttribute('style:family', style.getFamily()); - - const clazz = style.getClass(); - if (clazz !== undefined) { - styleElement.setAttribute('style:class', clazz); - } - - switch (style.getFamily()) { - case StyleFamily.Paragraph: - this.visitParagraphProperties( - style as ParagraphStyle, - document, - styleElement - ); - this.visitTextProperties( - style as ParagraphStyle, + if (style instanceof ListStyle) { + new ListStyleVisitor().visit(style, styleElement); + style.getListLevelStyles().forEach((listLevelStyle) => { + new ListLevelStyleVisitor().visit( + listLevelStyle, document, styleElement ); - break; - default: - break; + }); + } else if (style instanceof ParagraphStyle) { + new ParagraphPropertiesVisitor().visit(style, document, styleElement); + new TextPropertiesVisitor().visit(style, document, styleElement); } return styleElement; } - private visitParagraphProperties( - style: ParagraphStyle, - document: Document, - parent: Element - ): Element { - const paragraphPropertiesElement = document.createElement( - OdfElementName.StyleParagraphProperties - ); - parent.appendChild(paragraphPropertiesElement); - - const lineHeight = style.getLineHeight(); - switch (typeof lineHeight) { - case 'number': - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatLineHeight, - lineHeight + 'mm' - ); - break; - case 'string': - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatLineHeight, - lineHeight - ); - break; - default: - break; - } - - const lineHeightAtLeast = style.getLineHeightAtLeast(); - if (lineHeightAtLeast !== undefined) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.StyleLineHeightAtLeast, - lineHeightAtLeast + 'mm' - ); - } - - const lineSpacing = style.getLineSpacing(); - if (lineSpacing !== undefined) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.StyleLineSpacing, - lineSpacing + 'mm' - ); - } - - const horizontalAlignment = style.getHorizontalAlignment(); - if (horizontalAlignment !== HorizontalAlignment.Default) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatTextAlign, - horizontalAlignment - ); - } - - const horizontalAlignmentLastLine = style.getHorizontalAlignmentLastLine(); - if ( - horizontalAlignment === HorizontalAlignment.Justify && - horizontalAlignmentLastLine !== HorizontalAlignmentLastLine.Default - ) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatTextAlignLast, - horizontalAlignmentLastLine - ); - } - - if (style.getKeepTogether() === true) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatKeepTogether, - 'always' - ); - } - - const widows = style.getWidows(); - if (widows !== undefined) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatWidows, - widows.toString(10) - ); - } - - const orphans = style.getOrphans(); - if (orphans !== undefined) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatOrphans, - orphans.toString(10) - ); - } - - const marginLeft = style.getMarginLeft(); - if (marginLeft !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatMarginLeft, - marginLeft.toString(10) + 'mm' - ); - } - - const marginRight = style.getMarginRight(); - if (marginRight !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatMarginRight, - marginRight.toString(10) + 'mm' - ); - } - - const textIndent = style.getTextIndent(); - if (textIndent !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatTextIndent, - textIndent.toString(10) + 'mm' - ); - } - - const marginTop = style.getMarginTop(); - if (marginTop !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatMarginTop, - marginTop.toString(10) + 'mm' - ); - } - - const marginBottom = style.getMarginBottom(); - if (marginBottom !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatMarginBottom, - marginBottom.toString(10) + 'mm' - ); - } - - switch (style.getPageBreak()) { - case PageBreak.Before: - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBreakBefore, - 'page' - ); - break; - case PageBreak.After: - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBreakAfter, - 'page' - ); - break; - default: - break; - } - - const backgroundColor = style.getBackgroundColor(); - if (backgroundColor !== undefined) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBackgroundColor, - backgroundColor.toHex() - ); - } - - const borderTop = style.getBorderTop(); - if ( - borderTop !== undefined && - borderTop.width > 0 && - borderTop.style !== BorderStyle.None - ) { - const border = `${borderTop.width}mm ${ - borderTop.style - } ${borderTop.color.toHex()}`; - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBorderTop, - border - ); - } - - const borderBottom = style.getBorderBottom(); - if ( - borderBottom !== undefined && - borderBottom.width > 0 && - borderBottom.style !== BorderStyle.None - ) { - const border = `${borderBottom.width}mm ${ - borderBottom.style - } ${borderBottom.color.toHex()}`; - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBorderBottom, - border - ); - } - - const borderLeft = style.getBorderLeft(); - if ( - borderLeft !== undefined && - borderLeft.width > 0 && - borderLeft.style !== BorderStyle.None - ) { - const border = `${borderLeft.width}mm ${ - borderLeft.style - } ${borderLeft.color.toHex()}`; - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBorderLeft, - border - ); - } - - const borderRight = style.getBorderRight(); - if ( - borderRight !== undefined && - borderRight.width > 0 && - borderRight.style !== BorderStyle.None - ) { - const border = `${borderRight.width}mm ${ - borderRight.style - } ${borderRight.color.toHex()}`; - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatBorderRight, - border - ); - } - - const paddingLeft = style.getPaddingLeft(); - if (paddingLeft !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatPaddingLeft, - paddingLeft.toString(10) + 'mm' - ); - } - - const paddingRight = style.getPaddingRight(); - if (paddingRight !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatPaddingRight, - paddingRight.toString(10) + 'mm' - ); - } - - const paddingTop = style.getPaddingTop(); - if (paddingTop !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatPaddingTop, - paddingTop.toString(10) + 'mm' - ); - } - - const paddingBottom = style.getPaddingBottom(); - if (paddingBottom !== 0) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatPaddingBottom, - paddingBottom.toString(10) + 'mm' - ); - } - - if (style.getKeepWithNext() === true) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.FormatKeepWithNext, - 'always' - ); - } - - if (style.getVerticalAlignment() !== VerticalAlignment.Default) { - paragraphPropertiesElement.setAttribute( - OdfAttributeName.StyleVerticalAlign, - style.getVerticalAlignment() - ); - } - - const tabStops = style.getTabStops(); - if (tabStops.length === 0) { - return paragraphPropertiesElement; + private createStyleElement(style: Style, document: Document): Element { + if (style instanceof ListStyle) { + return document.createElement(OdfElementName.TextListStyle); } - - const tabStopsElement = document.createElement( - OdfElementName.StyleTabStops - ); - paragraphPropertiesElement.appendChild(tabStopsElement); - - tabStops.forEach((tabStop) => { - const tabStopElement = document.createElement( - OdfElementName.StyleTabStop - ); - tabStopsElement.appendChild(tabStopElement); - - tabStopElement.setAttribute( - OdfAttributeName.StylePosition, - tabStop.getPosition() + 'mm' - ); - - const type = tabStop.getType(); - const char = tabStop.getChar(); - if (type === TabStopType.Char && char !== undefined) { - tabStopElement.setAttribute(OdfAttributeName.StyleType, type); - tabStopElement.setAttribute(OdfAttributeName.StyleChar, char); - } else if (type !== TabStopType.Left) { - tabStopElement.setAttribute(OdfAttributeName.StyleType, type); - } - - const leaderStyle = tabStop.getLeaderStyle(); - if (leaderStyle !== TabStopLeaderStyle.None) { - tabStopElement.setAttribute( - OdfAttributeName.StyleLeaderStyle, - leaderStyle - ); - } - - const leaderColor = tabStop.getLeaderColor(); - if (leaderColor !== undefined) { - tabStopElement.setAttribute( - OdfAttributeName.StyleLeaderColor, - leaderColor.toHex() - ); - } - }); - - return paragraphPropertiesElement; + // else if (style instanceof ParagraphStyle) + return document.createElement(OdfElementName.StyleStyle); } - private visitTextProperties( - style: ParagraphStyle, - document: Document, - parent: Element - ): Element { - const textPropertiesElement = document.createElement( - OdfElementName.StyleTextProperties - ); - parent.appendChild(textPropertiesElement); - - const fontVariant = style.getFontVariant(); - if (fontVariant !== FontVariant.Normal) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatFontVariant, - fontVariant - ); - } - - const textTransformation = style.getTextTransformation(); - if (textTransformation !== TextTransformation.None) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatTextTransform, - textTransformation - ); - } - - const color = style.getColor(); - if (color !== undefined) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatColor, - color.toHex() - ); - } - - const fontName = style.getFontName(); - if (fontName !== undefined) { - textPropertiesElement.setAttribute( - OdfAttributeName.StyleFontName, - fontName - ); - } - - const fontSize = style.getFontSize(); - if (fontSize !== 12) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatFontSize, - fontSize + 'pt' - ); - } - - const typeface = style.getTypeface(); - if (typeface === Typeface.Italic || typeface === Typeface.BoldItalic) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatFontStyle, - 'italic' - ); - } - - if (typeface === Typeface.Oblique || typeface === Typeface.BoldOblique) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatFontStyle, - 'oblique' - ); + private setClass(styleElement: Element, style: Style): void { + const clazz = style.getClass(); + if (clazz !== undefined) { + styleElement.setAttribute('style:class', clazz); } + } - if ( - typeface === Typeface.Bold || - typeface === Typeface.BoldItalic || - typeface === Typeface.BoldOblique - ) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatFontWeight, - 'bold' - ); + private setFamily(styleElement: Element, style: Style): void { + if (style.getFamily() !== StyleFamily.None) { + styleElement.setAttribute('style:family', style.getFamily()); } + } - const backgroundColor = style.getBackgroundColor(); - if (backgroundColor !== undefined && !(style instanceof ParagraphStyle)) { - textPropertiesElement.setAttribute( - OdfAttributeName.FormatBackgroundColor, - backgroundColor.toHex() + private setName(styleElement: Element, style: Style, styles: IStyles): void { + const styleName = style.getName(); + if (styleName === Style.UNNAMED) { + styleElement.setAttribute( + 'style:name', + (styles as AutomaticStyles).getName(style) ); + } else { + styleElement.setAttribute('style:name', style.getName()); + styleElement.setAttribute('style:display-name', style.getDisplayName()); } - - return textPropertiesElement; } } diff --git a/src/xml/office/TextPropertiesVisitor.ts b/src/xml/office/TextPropertiesVisitor.ts new file mode 100644 index 00000000..059b51a4 --- /dev/null +++ b/src/xml/office/TextPropertiesVisitor.ts @@ -0,0 +1,101 @@ +import { ITextProperties } from '../../api/style/ITextProperties'; +import { + FontVariant, + TextTransformation, + Typeface, + ParagraphStyle, +} from '../../api/style'; +import { OdfAttributeName } from '../OdfAttributeName'; +import { OdfElementName } from '../OdfElementName'; + +export class TextPropertiesVisitor { + public visit( + textProperties: ITextProperties, + document: Document, + parent: Element + ): Element { + const textPropertiesElement = document.createElement( + OdfElementName.StyleTextProperties + ); + parent.appendChild(textPropertiesElement); + + const fontVariant = textProperties.getFontVariant(); + if (fontVariant !== FontVariant.Normal) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatFontVariant, + fontVariant + ); + } + + const textTransformation = textProperties.getTextTransformation(); + if (textTransformation !== TextTransformation.None) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatTextTransform, + textTransformation + ); + } + + const color = textProperties.getColor(); + if (color !== undefined) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatColor, + color.toHex() + ); + } + + const fontName = textProperties.getFontName(); + if (fontName !== undefined) { + textPropertiesElement.setAttribute( + OdfAttributeName.StyleFontName, + fontName + ); + } + + const fontSize = textProperties.getFontSize(); + if (fontSize !== 12) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatFontSize, + fontSize + 'pt' + ); + } + + const typeface = textProperties.getTypeface(); + if (typeface === Typeface.Italic || typeface === Typeface.BoldItalic) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatFontStyle, + 'italic' + ); + } + + if (typeface === Typeface.Oblique || typeface === Typeface.BoldOblique) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatFontStyle, + 'oblique' + ); + } + + if ( + typeface === Typeface.Bold || + typeface === Typeface.BoldItalic || + typeface === Typeface.BoldOblique + ) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatFontWeight, + 'bold' + ); + } + + const backgroundColor = textProperties.getBackgroundColor(); + if ( + backgroundColor !== undefined && + !(textProperties instanceof ParagraphStyle) + ) { + textPropertiesElement.setAttribute( + OdfAttributeName.FormatBackgroundColor, + backgroundColor.toHex() + ); + } + + return textPropertiesElement; + } +} diff --git a/test/integration/list.spec.ts b/test/integration/list.spec.ts new file mode 100644 index 00000000..2b0becba --- /dev/null +++ b/test/integration/list.spec.ts @@ -0,0 +1,146 @@ +import { unlink } from 'fs'; +import { promisify } from 'util'; +import { FontPitch, ListStyle, TextBody, TextDocument } from '../../src'; + +const FILEPATH = './integration-list.fodt'; + +xdescribe('list', () => { + const styleName = 'List Content'; + let document: TextDocument; + let body: TextBody; + + beforeEach(() => { + document = new TextDocument(); + body = document.getBody(); + document + .getFontFaceDeclarations() + .create('FreeSans', 'FreeSans', FontPitch.Variable); + document + .getCommonStyles() + .createParagraphStyle(styleName) + .setFontName('FreeSans'); + }); + + afterEach(async (done) => { + const unlinkAsync = promisify(unlink); + + // if (Date.now() < 1) { + await unlinkAsync(FILEPATH); + // } + + done(); + }); + + it('should create a document with a 10 level bullet list', async (done) => { + const listStyle = new ListStyle(); + listStyle + .createBulletListLevelStyle(1) + .setBulletChar('\u2660') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(10) + .setMarginLeft(10) + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(2) + .setBulletChar('\u2663') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(15) + .setMarginLeft(15) + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(3) + .setBulletChar('\u2665') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(20) + .setMarginLeft(20) + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(4) + .setBulletChar('\u2666') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(25) + .setMarginLeft(25) + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(5) + .setBulletChar('\u2605') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(30) + .setMarginLeft(30) + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(6) + .setBulletChar('\u2605') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(35) + .setMarginLeft(35) + .setRelativeBulletSize('50%') + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(7) + .setBulletChar('\u2714') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(40) + .setMarginLeft(40) + .setNumberPrefix('§') + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(8) + .setBulletChar('\u2717') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(45) + .setMarginLeft(45) + .setNumberSuffix('~') + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(9) + .setBulletChar('\u2022') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(50) + .setMarginLeft(50) + .setTextIndent(-5); + listStyle + .createBulletListLevelStyle(10) + .setBulletChar('\u2023') + .setLabelFollowedBy('listtab') + .setListTabStopPosition(55) + .setMarginLeft(55) + .setTextIndent(-5); + + const list = body.addList(); + list.setStyle(listStyle); + + const level1Item = list.addItem(); + level1Item.addParagraph('1st level').setStyleName(styleName); + const level2Item = level1Item.addList().addItem(); + level2Item.addParagraph('2nd level').setStyleName(styleName); + const level3Item = level2Item.addList().addItem(); + level3Item.addParagraph('3rd level').setStyleName(styleName); + const level4Item = level3Item.addList().addItem(); + level4Item.addParagraph('4th level').setStyleName(styleName); + const level5Item = level4Item.addList().addItem(); + level5Item.addParagraph('5th level').setStyleName(styleName); + const level6Item = level5Item.addList().addItem(); + level6Item + .addParagraph('6th level with small bullet') + .setStyleName(styleName); + const level7Item = level6Item.addList().addItem(); + level7Item + .addParagraph('7th level with number prefix') + .setStyleName(styleName); + const level8Item = level7Item.addList().addItem(); + level8Item + .addParagraph('8th level with number suffix') + .setStyleName(styleName); + const level9Item = level8Item.addList().addItem(); + level9Item.addParagraph('9th level').setStyleName(styleName); + const level10Item = level9Item.addList().addItem(); + level10Item.addParagraph('10th level').setStyleName(styleName); + + await document.saveFlat(FILEPATH); + + // TODO use snapshot testing + + done(); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 89d60a0e..faad12b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ - "outDir": "./lib", /* Redirect output structure to the directory. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ // "composite": true, /* Enable project compilation */ // "removeComments": true, /* Do not emit comments to output. */ @@ -51,18 +51,14 @@ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ - "plugins": [ - { - "name": "typescript-tslint-plugin" - } - ] + "plugins": [] }, "include": [ "src/**/*" ], "exclude": [ - "lib", + "dist", "node_modules", "**/*.spec.ts" ] -} \ No newline at end of file +}