DocumentationObjects

Objects

The Objects panel lists the primary price source, each indicator instance, drawings, and profiles that the host explicitly registers. Search by name, type, or pane. Selection follows the drawing canvas, and each row offers the operations its object supports. The primary price source is protected from removal.

live
Rendering live chart…
Simulated stock candles with irregular moves, pullbacks and changing volume. Open Objects to manage drawings and indicators, or focus the rectangle beyond the newest bar. Try the 350 px width, or hide RSI, save the layout, show it, and restore. The profile offers only show/hide and removal; its host-owned state is separate from the saved chart layout.
View example code
el.style.display = 'flex';
el.style.flexDirection = 'column';
const controls = document.createElement('div');
controls.style.cssText = 'display:flex;gap:6px;flex-wrap:wrap;padding:8px;flex-shrink:0';
const stage = document.createElement('div');
stage.style.cssText = 'flex:1;min-height:0;max-width:100%;width:100%';
el.append(controls, stage);
const bars = lib.generateBars(1700000000, 160, 60);
const widget = lib.createWidget(stage, {
  symbol: 'OBJECTS SIM', interval: '1m', intervals: ['1m'],
  rail: false, statusline: false, topbar: false,
  navigation: { defaultVisibleBars: 100 },
});
widget.series.setData(bars);
function addDrawing() {
  widget.draw.add({ tool: 'trend-line', paneIndex: 0,
    points: [{ time: bars[100].time, price: bars[100].low },
      { time: bars[140].time, price: bars[140].low }],
    style: { color: '#f0a020', lineWidth: 3 },
  });
}
addDrawing();
widget.draw.add({ tool: 'rectangle', paneIndex: 0,
  points: [{ time: bars[159].time + 20 * 60, price: bars[159].high + 2 },
    { time: bars[159].time + 35 * 60, price: bars[159].high + 5 }],
  style: { color: '#4da3ff', lineWidth: 2 },
});
widget.chart.addIndicator('rsi');
const profile = new lib.VolumeProfile(
  lib.computeVolumeProfileSessions(bars, { tickSize: 0.5, session: 'composite' }),
  { width: 60, showValueArea: false, showPocLabel: false },
);
widget.chart.addPrimitive(profile);
let exists = true;
let visible = true;
const unregister = widget.objects.register({
  id: 'session-profile',
  get: () => exists ? { kind: 'profile', name: 'Session profile', paneIndex: 0, visible } : null,
  setVisible(on) {
    if (visible === on) return;
    visible = on;
    if (on) widget.chart.addPrimitive(profile); else widget.chart.removePrimitive(profile);
  },
  remove() { widget.chart.removePrimitive(profile); exists = false; },
});
function button(label, action) {
  const node = document.createElement('button');
  node.textContent = label;
  node.type = 'button';
  node.style.cssText = 'padding:5px 9px;border:1px solid var(--oac-card-border);border-radius:4px;background:var(--oac-card);color:inherit;font:12px system-ui';
  node.addEventListener('click', action);
  controls.appendChild(node);
  return node;
}
button('Open Objects', () => widget.openObjects());
button('Add drawing', addDrawing);
button('Add RSI', () => widget.chart.addIndicator('rsi'));
button('Undo drawing action', () => widget.draw.undo());
let compact = false;
const widthButton = button('Width: fit', () => {
  compact = !compact;
  stage.style.width = compact ? '350px' : '100%';
  widthButton.textContent = compact ? 'Width: 350 px' : 'Width: fit';
  widthButton.setAttribute('aria-pressed', String(compact));
});
widthButton.setAttribute('aria-pressed', 'false');
let saved;
const restoreButton = button('Restore layout', () => {
  if (saved) widget.restoreState(saved);
});
restoreButton.disabled = true;
button('Save layout', () => {
  saved = JSON.parse(JSON.stringify(widget.getState()));
  restoreButton.disabled = false;
});
return { destroy() { unregister(); widget.destroy(); } };

Open the widget panel

The widget’s text-labelled Objects control opens the panel. A custom toolbar can call widget.openObjects(). Both use widget.objects, the shared inventory.

import { createWidget } from 'openalgo-charts/widget';
import 'openalgo-charts/indicators';
 
const widget = createWidget(container, { symbol: 'EXAMPLE', interval: '1m' });
widget.series.setData(bars);
widget.chart.addIndicator('rsi');
widget.openObjects();

Rows show pane numbers starting at 1, visibility, drawing lock state, selection, and external-indicator data status. Search and row-action focus survive inventory updates. Unsupported actions are absent. A supported action that fails reports through the existing widget toast. The panel fits the actual chart container, including narrow and short hosts; its list scrolls while the search field and footer remain available.

Drawing hide, lock and removal use the drawing controller, so its undo history stays authoritative. Focus brings a drawing’s anchors into view, including anchors beyond the newest bar, and sets that pane’s price range to manual. Reset view returns to autoscale. Indicator removal releases its data lifecycle and prunes an empty indicator pane.

Use the inventory without widget chrome

ChartObjects is in the base bundle and creates no DOM. The optional drawing source is structural; pass a DrawingController from the draw tier when drawings are present. The inventory observes the existing chart and drawing state.

import { ChartObjects } from 'openalgo-charts';
import { DrawingController } from 'openalgo-charts/draw';
 
const draw = new DrawingController(chart);
const objects = new ChartObjects(chart, {
  drawings: draw,
  onSettings: object => openExistingEditor(object.kind, object.sourceId),
});
const off = objects.subscribe(rows => renderObjectList(rows));
 
// The subscription immediately supplies the current immutable inventory.
const drawing = objects.list().find(row => row.kind === 'drawing');
if (drawing?.capabilities.visibility) objects.setVisible(drawing.id, false);
 
// Release the inventory before its host is replaced.
off();
objects.destroy();
draw.destroy();

objects.destroy() releases observations and provider subscriptions. It leaves the chart and its objects intact. The caller still owns drawing-controller and chart teardown. widget.destroy() handles its own inventory automatically.

MethodContract
list() / get(id)Immutable rows, or one row; an unknown ID returns undefined.
subscribe(listener)Current rows immediately, then inventory changes. Returns an unsubscribe function.
select(id, additive?)Select an object; pass null to clear selection. Additive selection applies to drawings.
setVisible(id, on) / setLocked(id, on)Delegate the supported operation to its owner.
remove(id) / openSettings(id) / focus(id)Delegate a supported action. Return false for missing, unsupported, or failed operations.
register(provider)Add an explicit host-owned object. Returns idempotent registration cleanup.
refresh()Re-read host state when its provider has no change subscription.
destroy()Stop observing; safe to call again.

Each ChartObjectSnapshot has id, sourceId, kind, name, paneIndex, visible, selected, optional locked and dataStatus, plus boolean capabilities for select, visibility, lock, remove, settings, and focus. Use the snapshot’s id for inventory actions; sourceId identifies the existing indicator, drawing, or provider. The primary row is source:primary; indicator and drawing IDs have their kind prefix. Host registrations use custom:<provider.id> and cannot replace a built-in row.

Register a profile with supported actions

Primitives are not automatically manageable objects. A profile host declares the operations it actually owns. This example registers visibility and removal; it does not offer settings, selection, lock, or focus.

const objects = widget.objects;
const chart = widget.chart;
chart.addPrimitive(profile);
let exists = true;
let visible = true;
 
const unregister = objects.register({
  id: 'session-profile',
  get: () => exists
    ? { kind: 'profile', name: 'Session profile', paneIndex: 0, visible }
    : null,
  setVisible(on) {
    if (on === visible) return;
    visible = on;
    if (on) chart.addPrimitive(profile);
    else chart.removePrimitive(profile);
  },
  remove() {
    chart.removePrimitive(profile);
    exists = false;
  },
});

Provider callbacks are synchronous. Missing callbacks produce disabled capabilities, and throwing callbacks make the operation return false. If another part of the host changes the profile, call objects.refresh() or implement the provider’s subscribe(listener) callback, returning its unsubscribe function. get() returns null while the object does not exist. Duplicate provider IDs are rejected.

Registration cleanup removes the inventory entry and its observation subscription. It does not remove the primitive. At host teardown, remove or destroy the host-owned profile as appropriate and call unregister().

Reuse the panel in a custom host

import { mountObjectsPanel } from 'openalgo-charts/widget';
 
const panel = mountObjectsPanel(widget.context, openButton, {
  objects,
  onClose: () => updateToolbarState(false),
});
panel.close();

The explicit objects option overrides optional WidgetContext.objects. One of them must exist. The panel returns PanelHandle with el, close() and isOpen(); closing releases only its subscription. A host assembling its own widget context supplies the usual overlay stack and theme furniture. Include OBJECTS_PANEL_CSS alongside WIDGET_CSS and DIALOG_CSS when supplying styles yourself; createWidget includes it.

Persistence and lifecycle

Indicator visibility round-trips in chart and widget layouts. Older layouts without the optional visible field restore indicators as visible. Drawing visibility and lock state remain part of the drawing document. A restored indicator can receive a new instance ID, so consumers should use the latest inventory snapshot.

Provider definitions, callbacks, profile visibility, and profile data belong to the host and are not serialized by the chart layout. Re-register them when replacing a chart and persist their state separately when needed. The Objects workflow does not invoke broker order actions or claim arbitrary trading primitives as removable rows.