Skip to content

Writing a Plugin ​

A plugin is a plain class. The core only requires two static properties and a constructor.

Minimal template ​

ts
import type ewColorPicker from '@ew-color-picker/core'
import { ApplyOrder, create, addClass, insertNode, isFunction } from '@ew-color-picker/utils'

export default class MyBadgePlugin {
  static pluginName = 'myBadge'
  static applyOrder = ApplyOrder.Post

  el: HTMLElement | null = null

  constructor(public ewColorPicker: ewColorPicker) {
    this.run()

    if (isFunction(this.ewColorPicker.on)) {
      this.ewColorPicker.on('change', this.handleChange)
    }
  }

  run() {
    const panelContainer = this.ewColorPicker.getMountPoint('panelContainer')
    if (!panelContainer) return

    this.el = create('span')
    addClass(this.el, 'my-badge')
    insertNode(panelContainer, this.el)
    this.renderColor(this.ewColorPicker.getColor())
  }

  handleChange = (color: string) => {
    this.renderColor(color)
  }

  renderColor(color?: string) {
    if (this.el) this.el.textContent = color || 'no color'
  }

  destroy() {
    if (isFunction(this.ewColorPicker.off)) {
      this.ewColorPicker.off('change', this.handleChange)
    }
    this.el?.parentNode?.removeChild(this.el)
    this.el = null
  }
}

Register it ​

ts
import ewColorPicker from '@ew-color-picker/core'
import MyBadgePlugin from './my-badge'

ewColorPicker.use(MyBadgePlugin)

new ewColorPicker({ el: '#app' })

use() is chainable and idempotent.

Contract ​

MemberRequiredNotes
static pluginNameYesUnique key in core.plugins; missing name logs a warning
static applyOrderNoApplyOrder.Pre (first) / unset (registration order) / ApplyOrder.Post (last)
constructor(core)YesReceives the instance; render + subscribe here
destroy()NoUnbind DOM events and listeners
install(core)NoSupport manual plugin.install(core) usage
updateOptions()NoCalled when core.updateOptions() broadcasts options-update

Rules ​

  1. Change colors only via core.setColor().
  2. Subscribe to change rather than reading state in loops.
  3. Render only into root or panelContainer.
  4. Scope DOM queries to your own wrapper.
  5. Clean up everything in destroy().

Execution order (applyOrder) ​

static applyOrder is optional and controls construction order. Values come from the ApplyOrder enum in @ew-color-picker/utils (compiled as a regular runtime enum):

ValueWeightWhen to use
ApplyOrder.Pre-1Create shared nodes, containers or global state before other plugins
unset0Default; runs in ewColorPicker.use() registration order
ApplyOrder.Post1Read DOM already rendered by other plugins

Plugins with the same weight run in registration order, so the default behavior stays deterministic. applyOrder only affects construction time, not event callback order.

ts
import { ApplyOrder } from '@ew-color-picker/utils'

export default class MyOverlayPlugin {
  static pluginName = 'myOverlay'
  static applyOrder = ApplyOrder.Pre // build the overlay container first

  // ...
}

Omit applyOrder when your plugin only subscribes to events and doesn't depend on other plugins' DOM.

Type augmentation ​

Declare your options on the core's CustomOptions interface:

ts
declare module '@ew-color-picker/core' {
  interface CustomOptions {
    myBadge?: {
      label?: string
    }
  }
}

Now new ewColorPicker({ myBadge: { label: 'Hi' } }) type-checks.

Lifecycle summary ​

text
Core.use(Plugin)  → registered statically
new ewColorPicker → creates instance, calls constructor
mount(el)         → mount points exist, plugins render
updateOptions()   → updateOptions() + options-update event
destroy()         → destroy() called, DOM removed

Next steps ​