# Package

This text will explore the Package file used to define the parts included in, and used by a Writer plugin.

## Plugin Structure

A plugin is a collection of many different resources, and the Package file is that collection's bootstrap. Label definitions, node- and converter-registration, adding validatiors, and registering tools and commands, are all placed in the Package file, using the Writer's `NPWriterConfigurator`-class.

The Package file also contains the plugin's name and id.

## Example Plugin Structure

```
/ se.mydomain.myplugin
  / components
    MyPluginComponent.js
    ChildComponent.js
  MyPluginNode.js
  MyPluginConverter.js 
  MyPluginPackage.js
  index.js
```

In this example, `index.js` is the plugin's entrypoint. It's here where the Package file is imported and the plugin is registered in the Writer.

`MyPluginPackage.js` contains information about the plugin, and registers `MyPluginNode.js` as a substance node, `MyPluginConverter.js` as a NewsML-converter, and `MyPluginComponent.js` as the component used to present `MyPluginNode.js`.

Depending on the type of plugin, the Package will contain different definitions and registrations.

### MyPluginPackage.js

```javascript
import './scss/link.scss' // Provide an entrypoint for the plugin's style
import MyPluginNode from './MyPluginNode'
import MyPluginConverter from './MyPluginConverter'
import MyPluginComponent from './components/MyPluginComponent'

export default {
    name: 'my-plugin',
    id: 'se.mydomain.myplugin',
    version: '{{version}}',
    configure: function (configurator, pluginConfig) {

        configurator.addNode(MyPluginNode)
        configurator.addComponent(MyPluginNode.type, MyPluginComponent)
        configurator.addConverter('newsml', MyPluginConverter)

        configurator.addLabel('Hello World', {
            sv: 'Tjena Världen'
        })
    }
}
```

This example Package file contains definitions for a `Node`, a `Component` linked to the Node's `type`-property, a `Converter` to import and export the `Node`-data using NewsML. It also adds a label which can be used within the plugin's components.

To learn more about Nodes, Converters, and Components, have look at [their respective documents](https://github.com/Infomaker/dw-documentation/tree/89ef316ecc2545f627b27987592a99f910ebd089/developer-guide/toc.md).

## The configurator object

As seen in the Package example above, the supplied `configurator`-object is used to add different resources used in the plugin. Below are relevant `configurator`-functions which are useful when developing a plugin.

### Common Add-functions

**addNode(NodeClass)**\
Registers a node in Substance configuration for use later.

**addComponent(type, ComponentClass)**\
Registers a class which extends Substance's `Component`-class for a Node type. A Component in this instance is a UI representation of a `Node` class. Only on Component can be registered to one specific type.

**addConverter(type, converterObject)**\
Registers a converter object for a specific output `type`. In the case of a Writer plugin, whose output is NewsML, the `type` is `"newsml"`. A converter is an object used for exporting and importing the data stored in a `Node` object.

**addLabel(label, { translations })**\
Adds a localized label string which can be fetched in the plugin's components using `this.getLabel(string)`, assuming that the component is extending Substance's `Component`-class. The first string property is used as fallback if none of the supplied translations are found.

Label Example:

```javascript
configurator.addLabel('Hello World', {
    sv: 'Hejsan Världen'
})

this.getLabel('Hello World') 
// If the Writer's configured "labelLanguage" property is set 
// to "sv", "Hejsan Världen" is returned, any other language
// will return "Hello World"
```

**addIcon(iconName, options)**\
Registers an icon from [FontAwesome](https://fontawesome.com/icons?d=gallery), or another registered icon provider for use in the plugin. Example:

```javascript
configurator.addIcon('my-icon', {'fontawesome': 'fa-external-link'})

$$(Button, {
    icon: 'my-icon'
}).on('click', () => { console.info('Foobar') })
```

**addKeyboardShortcut(combo, spec, globalCombo, description = '')**\
Add keyboard shortcut and connect to a Command.

Keyboard Shortcut Example:

```javascript
  // configure function in a Package file

  const combo = {
    standard: {
        'default': 'ctrl+d',
        'mac': 'cmd+d'
    }
  }

  configurator.addKeyboardShortcut(combo, {command: 'my-amazing-command'}, true, 'Runs my amazing command in the global context.')
```

**addValidator(Validator)**\
Adds a class which inherits from Validator to validate the Article's content and metadata before saving.

Validator Example:

```javascript
import {Validator, api} from 'writer'

class MyHeadlineValidator extends Validator {

    constructor(...args) {
        super(...args)
    }

    /**
     * Main validation method
     */
    validate() {
        this.validateHeadline()
    }

    /**
     * Ensure that a valid headline exists
     */
    validateHeadline() {
        const headlines = this.newsItem.querySelectorAll('idf > group element[type="headline"]')
        const headline = headlines[0].childNodes.length === 0 ? '' : headlines[0].firstChild.textContent.trim()

        if (headlines.length === 0 || headline === '') {
            this.addError(
                api.getLabel('Headline is missing or empty!')
            )
        }
    }
}

export {MyHeadlineValidator}
```

**addToSidebar(tabId, pluginConfig, ComponentClass)**\
Registers a Component to be rendered in the Sidebar. `tabId` is a string, which is the id of the tab, but also the label which is used for the tab. If the tab does not exist, it will be created, and the Component added to this new tab.

**addTopBarComponent(id, def, component)**\
Registers a Component to be rendered in the Top bar. The parameter `def` is an object which contains alignment, whether the Component is placed on the left, or right, side of the top bar.

Topbar Example:

```javascript
configurator.addTopBarComponent(
    'my-amazing-topbar-component',
    {
        align: 'left' // Or 'right'
    },
    MyAmazingTopBarComponent
)
```

**addPopover(id, def, component)**\
Similar to `addTopBarComponent`, this function adds an icon in the Top bar which, when clicked opens a popover overlay with its component rendered inside.

Popover Example:

```javascript
configurator.addPopover(
    'my-amazing-popover',
    {
        icon: 'fa-smiley',
        align: 'right',
        sticky: true    // Set to `true` to keep the popover open when clicking somewhere else, default `false`
    },
    MyAmazingComponent
)
```

### Tools and Commands

Adding a Tool is a simple way to add a button, or some text, which can execute a Command when clicked. The only difference between the `add***Tool()`-functions is where in the Writer the Tool is added. When adding a Tool, it needs the name of the Command (which was previously registered) it should be bound to, and a class which is used to render the Tool. For a visual representation of all the Writer sections, see the [Plugin Overview](/writer/6.0.0-1/developer-guide/index/pluginoverview.md).

Tool class Example:

```javascript
import {Tool} from 'substance'

class MyAmazingTool extends Tool {

    render($$) {
        return $$('div').attr('title', this.getLabel('Cheers you up'))
            .append(
                $$('button').addClass('se-tool').append(
                    $$('i').addClass('fa fa-smile')
                )
                    .on('click', this.executeCommand) // This will execute the command which name was used to register the Tool 
            )
      }
}

export {MyAmazingTool}
```

**addCommand(name, CommandClass, options)**\
Adds a Command which can be executed by a Tool, or manually using `EditorSession`. In its simplest form, a Command contains the logic for a Tool.

Command Example:

```javascript
import {WriterCommand} from 'writer'

class MyAmazingCommand extends WriterCommand {

  /**
  * Executes command with supplied params.
  * When called from Tool class, props from Tools is contained in params.
  * 
  * @param params
  */
  execute(params) {
      console.info('Have a great day!')
      console.info('Also, here\'s the supplied data', params)
  }
}

export {MyAmazingCommand}
```

**addContentMenuTool(commandName, ToolClass)**\
Adds a Tool to the Content Menu, it should return a rendered `ContextMenuItem` instance.

**addContentMenuTopTool(commandName, ToolClass)**\
Adds a Tool to the top of the Content Menu.

**addContextMenuTool(commandName, ToolClass)**\
Adds a Tool to the context menu, e.g when right clicking on the editing surface.

**addOverlayTool(commandName, ToolClass)**\
Adds a Tool which is displayed when selecting text, such as annotation plugins.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.navigaglobal.com/writer/6.0.0-1/developer-guide/writer-plugin-building-blocks/package.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
