diff --git a/app/javascript/components/MapEditor/hooks.ts b/app/javascript/components/MapEditor/hooks.ts index bc74f63c3..8c941ac58 100644 --- a/app/javascript/components/MapEditor/hooks.ts +++ b/app/javascript/components/MapEditor/hooks.ts @@ -37,9 +37,7 @@ export function useFeatureCollection( type: 'FeatureCollection', features: callback(features) })); - ajax({ url, type: 'GET' }) - .then(() => fire(document, 'ds:page:update')) - .catch(() => null); + ajax({ url, type: 'GET' }).catch(() => null); }, [url, setFeatureCollection] ); diff --git a/app/javascript/controllers/geo_area_controller.tsx b/app/javascript/controllers/geo_area_controller.tsx new file mode 100644 index 000000000..3c1022895 --- /dev/null +++ b/app/javascript/controllers/geo_area_controller.tsx @@ -0,0 +1,53 @@ +import { Controller } from '@hotwired/stimulus'; +import { debounce } from '@utils'; + +type Detail = Record; + +export class GeoAreaController extends Controller { + static values = { + id: String, + description: String + }; + static targets = ['description']; + + declare readonly idValue: string; + declare readonly descriptionTarget: HTMLInputElement; + + onFocus() { + this.globalDispatch('map:feature:focus', { id: this.idValue }); + } + + onClick(event: MouseEvent) { + event.preventDefault(); + this.globalDispatch('map:feature:focus', { id: this.idValue }); + } + + onInput() { + this.debounce(this.updateDescription, 200); + } + + private updateDescription(): void { + this.globalDispatch('map:feature:update', { + id: this.idValue, + properties: { description: this.descriptionTarget.value.trim() } + }); + } + + #debounced = new Map<() => void, () => void>(); + private debounce(fn: () => void, interval: number): void { + let debounced = this.#debounced.get(fn); + if (!debounced) { + debounced = debounce(fn.bind(this), interval); + this.#debounced.set(fn, debounced); + } + debounced(); + } + + private globalDispatch(type: string, detail: Detail): void { + this.dispatch(type, { + detail, + prefix: '', + target: document.documentElement + }); + } +} diff --git a/app/javascript/packs/application.js b/app/javascript/packs/application.js index 921cef1fc..be0e31f8f 100644 --- a/app/javascript/packs/application.js +++ b/app/javascript/packs/application.js @@ -19,6 +19,7 @@ import { registerComponents } from '../controllers/react_controller'; import { TurboEventController } from '../controllers/turbo_event_controller'; +import { GeoAreaController } from '../controllers/geo_area_controller'; import '../new_design/dropdown'; import '../new_design/form-validation'; @@ -95,6 +96,7 @@ Turbo.session.drive = false; const Stimulus = Application.start(); Stimulus.register('react', ReactController); Stimulus.register('turbo-event', TurboEventController); +Stimulus.register('geo-area', GeoAreaController); // Expose globals window.DS = window.DS || DS; diff --git a/app/views/shared/champs/carte/_geo_area.html.haml b/app/views/shared/champs/carte/_geo_area.html.haml index 1915fbd4f..7f945f5d8 100644 --- a/app/views/shared/champs/carte/_geo_area.html.haml +++ b/app/views/shared/champs/carte/_geo_area.html.haml @@ -1,10 +1,10 @@ -%li{ class: editing ? 'mb-1' : 'flex column mb-2' } +%li{ class: editing ? 'mb-1' : 'flex column mb-2', data: { controller: 'geo-area', geo_area_id_value: geo_area.id } } - if editing - = link_to '#', data: { geo_area: geo_area.id } do + = link_to '#', data: { action: 'geo-area#onClick' } do = geo_area_label(geo_area) - = text_field_tag :description, geo_area.description, data: { geo_area: geo_area.id }, placeholder: 'Description', class: 'no-margin' + = text_field_tag :description, geo_area.description, data: { action: 'focus->geo-area#onFocus input->geo-area#onInput', geo_area_target: 'description' }, placeholder: 'Description', class: 'no-margin' - else - = link_to '#', data: { geo_area: geo_area.id } do + = link_to '#', data: { action: 'geo-area#onClick' } do = geo_area_label(geo_area) - if geo_area.description.present? %span