import { Optional } from '@ephox/katamari'; import { Keying } from '../../api/behaviour/Keying'; import { Receiving } from '../../api/behaviour/Receiving'; import { Representing } from '../../api/behaviour/Representing'; import type { AlloyComponent } from '../../api/component/ComponentApi'; import * as SketchBehaviours from '../../api/component/SketchBehaviours'; import type { AlloySpec } from '../../api/component/SpecTypes'; import * as AlloyEvents from '../../api/events/AlloyEvents'; import * as NativeEvents from '../../api/events/NativeEvents'; import * as Channels from '../../api/messages/Channels'; import type { CompositeSketchFactory } from '../../api/ui/UiSketcher'; import type { EventFormat, NativeSimulatedEvent } from '../../events/SimulatedEvent'; import * as AlloyParts from '../../parts/AlloyParts'; import type { SliderDetail, SliderSpec, SliderUpdateEvent, SliderValue } from '../types/SliderTypes'; import * as ModelCommon from './ModelCommon'; const sketch: CompositeSketchFactory = (detail: SliderDetail, components: AlloySpec[], _spec: SliderSpec, _externals) => { const getThumb = (component: AlloyComponent): AlloyComponent => AlloyParts.getPartOrDie(component, detail, 'thumb'); const getSpectrum = (component: AlloyComponent): AlloyComponent => AlloyParts.getPartOrDie(component, detail, 'spectrum'); const getLeftEdge = (component: AlloyComponent): Optional => AlloyParts.getPart(component, detail, 'left-edge'); const getRightEdge = (component: AlloyComponent): Optional => AlloyParts.getPart(component, detail, 'right-edge'); const getTopEdge = (component: AlloyComponent): Optional => AlloyParts.getPart(component, detail, 'top-edge'); const getBottomEdge = (component: AlloyComponent): Optional => AlloyParts.getPart(component, detail, 'bottom-edge'); const modelDetail = detail.model; const model = modelDetail.manager; const refresh = (slider: AlloyComponent, thumb: AlloyComponent): void => { model.setPositionFromValue(slider, thumb, detail, { getLeftEdge, getRightEdge, getTopEdge, getBottomEdge, getSpectrum }); }; const setValue = (slider: AlloyComponent, newValue: SliderValue): void => { modelDetail.value.set(newValue); const thumb = getThumb(slider); refresh(slider, thumb); }; const changeValue = (slider: AlloyComponent, newValue: SliderValue): Optional => { setValue(slider, newValue); const thumb = getThumb(slider); detail.onChange(slider, thumb, newValue); return Optional.some(true); }; const resetToMin = (slider: AlloyComponent) => { model.setToMin(slider, detail); }; const resetToMax = (slider: AlloyComponent) => { model.setToMax(slider, detail); }; const choose = (slider: AlloyComponent) => { const fireOnChoose = () => { AlloyParts.getPart(slider, detail, 'thumb').each((thumb) => { const value = modelDetail.value.get(); detail.onChoose(slider, thumb, value); }); }; const wasDown = detail.mouseIsDown.get(); detail.mouseIsDown.set(false); // We don't want this to fire if the mouse wasn't pressed down over anything other than the slider. if (wasDown) { fireOnChoose(); } }; const onDragStart = (slider: AlloyComponent, simulatedEvent: NativeSimulatedEvent) => { simulatedEvent.stop(); detail.mouseIsDown.set(true); detail.onDragStart(slider, getThumb(slider)); }; const onDragEnd = (slider: AlloyComponent, simulatedEvent: NativeSimulatedEvent) => { simulatedEvent.stop(); detail.onDragEnd(slider, getThumb(slider)); choose(slider); }; const focusWidget = (component: AlloyComponent) => { AlloyParts.getPart(component, detail, 'spectrum').map(Keying.focusIn); }; return { uid: detail.uid, dom: detail.dom, components, behaviours: SketchBehaviours.augment( detail.sliderBehaviours, [ Keying.config({ mode: 'special', focusIn: focusWidget }), Representing.config({ store: { mode: 'manual', getValue: (_) => { return modelDetail.value.get(); }, setValue } }), Receiving.config({ channels: { [Channels.mouseReleased()]: { onReceive: choose } } }) ] ), events: AlloyEvents.derive([ AlloyEvents.run(ModelCommon.sliderChangeEvent(), (slider, simulatedEvent) => { changeValue(slider, simulatedEvent.event.value); }) as AlloyEvents.AlloyEventKeyAndHandler, AlloyEvents.runOnAttached((slider, _simulatedEvent) => { // Set the initial value const getInitial = modelDetail.getInitialValue(); modelDetail.value.set(getInitial); const thumb = getThumb(slider); refresh(slider, thumb); const spectrum = getSpectrum(slider); // Call onInit instead of onChange for the first value. detail.onInit(slider, thumb, spectrum, modelDetail.value.get()); }), AlloyEvents.run(NativeEvents.touchstart(), onDragStart), AlloyEvents.run(NativeEvents.touchend(), onDragEnd), AlloyEvents.run(NativeEvents.mousedown(), (component, event: NativeSimulatedEvent) => { focusWidget(component); onDragStart(component, event); }), AlloyEvents.run(NativeEvents.mouseup(), onDragEnd), ]), apis: { resetToMin, resetToMax, setValue, refresh }, domModification: { styles: { position: 'relative' } } }; }; export { sketch };