import { TomTomConfig } from '@tomtom-org/maps-sdk/core';
import { getKnob, setKnob, TomTomMap, TrafficFlowModule } from '@tomtom-org/maps-sdk/map';
import { effectKnobCatalogue, MapEffects, type MapEffectsSettings } from '@tomtom-org/maps-sdk-plugin-map-effects';
import './style.css';
import { API_KEY } from './config';
import { initTogglePanel } from './togglePanel';
TomTomConfig.instance.put({ apiKey: API_KEY, language: 'en-GB' });
const useCases: Record<string, MapEffectsSettings> = {
'data-viz': { 'grade.saturation': 0.35, 'grade.brightness': 0.85, 'tint.color': '#1f2937', 'tint.opacity': 0.2 },
'night-driving': { 'bloom.intensity': 0.6, 'bloom.radius': 14, 'bloom.threshold': 0.55 },
focus: { 'vignette.intensity': -0.45, 'edgeBlur.intensity': 10, 'edgeBlur.reach': 0.45 },
};
const map = new TomTomMap({
style: 'streetDark',
mapLibre: {
container: 'sdk-map',
center: [4.8952, 52.3702],
zoom: 13,
pitch: 60,
maxPitch: 85,
canvasContextAttributes: { preserveDrawingBuffer: true },
},
});
TrafficFlowModule.get(map, { visible: true });
const effects = new MapEffects(map);
const renderPanel = () => {
const container = document.querySelector('#ui-knobs') as HTMLElement;
container.innerHTML = '';
const sections = new Map<string, HTMLElement>();
for (const knob of effectKnobCatalogue) {
const [group, name] = knob.id.split('.');
const current = getKnob(effects, effectKnobCatalogue, knob.id);
let section = sections.get(group);
if (!section) {
section = document.createElement('div');
section.className = 'ui-section';
section.innerHTML = `<h4 class="ui-subheading" title="${knob.useCase}">${group}</h4>`;
sections.set(group, section);
container.appendChild(section);
}
const field = document.createElement('label');
field.className = 'ui-form-field';
field.title = knob.description;
if (knob.kind === 'color') {
field.innerHTML = `<span class="ui-form-label">${name}</span><span class="ui-color-swatch"><input type="color" value="${current}"></span>`;
const input = field.querySelector('input') as HTMLInputElement;
input.addEventListener('input', () => setKnob(effects, effectKnobCatalogue, knob.id, input.value));
} else if (knob.kind === 'colors') {
const selected = Array.isArray(current) ? (current[0] ?? '') : '';
const options = ['', ...knob.options].map(
(option) =>
`<option value="${option}"${option === selected ? ' selected' : ''}>${option || 'everything'}</option>`,
);
field.innerHTML = `<span class="ui-form-label">${name}</span><select class="ui-dropdown">${options.join('')}</select>`;
const select = field.querySelector('select') as HTMLSelectElement;
select.addEventListener('change', () =>
setKnob(effects, effectKnobCatalogue, knob.id, select.value ? [select.value] : []),
);
} else {
const { min, max, step } = knob.range;
field.innerHTML = `<span class="ui-form-label">${name}</span>
<div class="ui-slider-container"><input type="range" class="ui-slider" min="${min}" max="${max}" step="${step}" value="${current}"><span class="ui-slider-value">${current}</span></div>`;
const input = field.querySelector('input') as HTMLInputElement;
const value = field.querySelector('.ui-slider-value') as HTMLElement;
input.addEventListener('input', () => {
value.textContent = input.value;
setKnob(effects, effectKnobCatalogue, knob.id, Number(input.value));
});
}
section.appendChild(field);
}
};
renderPanel();
const useCaseSelector = document.querySelector('#ui-useCase') as HTMLSelectElement;
useCaseSelector.addEventListener('change', () => {
effects.applyConfig(useCases[useCaseSelector.value] ?? {});
renderPanel();
});
document.querySelector('#ui-reset')?.addEventListener('click', () => {
effects.resetConfig();
useCaseSelector.value = '';
renderPanel();
});
document.querySelector('#ui-capture')?.addEventListener('click', async () => {
const canvas = await effects.capture({ pixelRatio: 2 });
window.open()?.document.body.appendChild(canvas);
});
initTogglePanel();