Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [0.8.0] - 2026-04-14

### Added
- **Nullable option** - Added `nullable` option to allow controls to explicitly set null values
Expand Down
182 changes: 177 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ the fly.

Load the [module file](https://github.com/highcharts/controls/blob/main/js/controls.js) from a CDN or your own server
```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.8.0"></script>
```

Or

```js
import Controls from 'https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0';
import Controls from 'https://cdn.jsdelivr.net/npm/@highcharts/controls@0.8.0';
```

## Usage
Expand All @@ -28,7 +28,7 @@ import Controls from 'https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0';
<html>
<head>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.8.0"></script>
</head>
<body>
<div id="container"></div>
Expand Down Expand Up @@ -65,6 +65,7 @@ import Controls from 'https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0';
<highcharts-control
type="color"
path="legend.backgroundColor"
nullable
value="#FFEEAA">
</highcharts-control>
</highcharts-group>
Expand All @@ -80,7 +81,7 @@ import Controls from 'https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0';
<html>
<head>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.8.0"></script>
</head>
<body>
<div id="container"></div>
Expand Down Expand Up @@ -121,6 +122,7 @@ import Controls from 'https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0';
},
{
path: 'legend.backgroundColor',
nullable: true,
value: '#FFEEAA'
}
]
Expand Down Expand Up @@ -435,7 +437,7 @@ Container element for controls.

**Example:**
```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.7.0/css/controls.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@highcharts/controls@0.8.0/css/controls.css" />
<highcharts-controls target="#my-chart" inject-css="false">
<!-- controls here -->
</highcharts-controls>
Expand Down Expand Up @@ -601,6 +603,176 @@ Individual control element. Must be a child of `<highcharts-controls>`.
- **Flexible Targeting** - Target specific charts or use defaults
- **Modern UI** - Clean, styled controls with smooth animations

# Standalone Control Usage

The Highcharts Controls support using individual controls standalone, without requiring a Highcharts/Grid target. This makes controls reusable as general-purpose UI components.

## Features

✅ **Standalone Controls** - Use controls independently without a target chart
✅ **Event-Driven** - Standard DOM EventTarget-based event handling
✅ **Modular** - Import only the controls you need
✅ **Type-Safe** - Full TypeScript support
✅ **Backward Compatible** - Existing API continues to work

## Basic Usage

### Standalone Color Picker

```typescript
import { ColorControl } from '@highcharts/controls';

// Create a standalone color picker
const colorPicker = new ColorControl({
type: 'color',
path: 'theme.primaryColor', // logical path, not bound to chart
label: 'Primary Color',
value: '#3498db'
});

// Render it anywhere in your UI
colorPicker.render(document.getElementById('my-container'));

// Listen for changes
colorPicker.addEventListener('change', (e) => {
console.log('Color changed:', e.detail.value);
document.body.style.backgroundColor = e.detail.value;
});

// Programmatically update the value
colorPicker.value = '#e74c3c';
```

### Standalone Number Slider

```typescript
import { NumberControl } from '@highcharts/controls';

const volumeSlider = new NumberControl({
type: 'number',
path: 'audio.volume',
label: 'Volume',
value: 50,
min: 0,
max: 100,
step: 1
});

volumeSlider.render(document.getElementById('volume-control'));

volumeSlider.addEventListener('change', (e) => {
audioElement.volume = e.detail.value / 100;
});
```

### Standalone Boolean Toggle

```typescript
import { BooleanControl } from '@highcharts/controls';

const darkModeToggle = new BooleanControl({
type: 'boolean',
path: 'settings.darkMode',
label: 'Dark Mode',
value: false
});

darkModeToggle.render(document.getElementById('theme-toggle'));

darkModeToggle.addEventListener('change', (e) => {
document.body.classList.toggle('dark-mode', e.detail.value);
});
```

## Using with Highcharts (Traditional Way)

The traditional collection-based approach still works:

```typescript
import Controls from '@highcharts/controls';

const controls = new Controls('#controls-container', {
target: chart, // Still supported, but now optional!
controls: [
{ type: 'color', path: 'chart.backgroundColor' },
{ type: 'boolean', path: 'legend.enabled' }
]
});
```

## Mixed Usage

You can also pre-create controls with custom behavior, then pass them to the Controls collection:

```typescript
import Controls, { ColorControl } from '@highcharts/controls';

// Create a control with custom logic
const customColor = new ColorControl({
label: 'Chart Background',
value: '#fff'
});

customColor.addEventListener('change', (e) => {
// Custom validation or transformation
if (isValidColor(e.detail.value)) {
console.log('Valid color selected');
}
});

// Use it in the collection
const controls = new Controls(container, {
target: chart,
controls: [
customColor, // Pass the instance directly
{ type: 'boolean', path: 'legend.enabled' } // Or use config
]
});
```

## Available Control Classes

- `Control` - Base class for all controls
- `ColorControl` - Color picker with opacity slider
- `BooleanControl` - Toggle switch (supports tri-state nullable)
- `NumberControl` - Range slider with unit support
- `SelectControl` - Dropdown or button group (function-based, to be refactored)
- `TextControl` - Text input (function-based, to be refactored)

## Events

All controls emit a `'change'` event with the following detail:

```typescript
{
value: any, // New value
oldValue: any, // Previous value
path: string, // Control path
animation?: boolean // Animation flag (NumberControl only)
}
```

## Methods

All control instances have these methods:

- `render(container: HTMLElement)` - Render the control into a container
- `destroy()` - Clean up event listeners and remove from DOM
- `getElement()` - Get the control's root DOM element

And these properties:

- `value` - Get or set the control's value programmatically
- `params` - Access the control's configuration parameters

## Architecture Benefits

1. **Decoupled** - Controls no longer require Highcharts/Datagrid
2. **Reusable** - Use anywhere in your application
3. **Testable** - Each control can be unit tested in isolation
4. **Flexible** - Compose controls however you want
5. **Progressive Enhancement** - Add chart binding only when needed

## License

ISC
Expand Down
Loading