aur-openlayers 0.0.3 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,13 @@ import type { TranslateEvent } from 'ol/interaction/Translate';
14
14
  * const state: FeatureStyleState = 'SELECTED';
15
15
  */
16
16
  export type FeatureStyleState = string;
17
+ /**
18
+ * Идентификатор доменной модели.
19
+ *
20
+ * @example
21
+ * const id: Id = 'point-1';
22
+ */
23
+ export type Id = string | number;
17
24
  /**
18
25
  * Значение или функция, которая его вычисляет.
19
26
  *
@@ -140,6 +147,94 @@ export type ModelChange<M> = {
140
147
  /** Причина изменения. */
141
148
  reason: ModelChangeReason;
142
149
  };
150
+ /**
151
+ * Причина внешнего изменения коллекции моделей слоя.
152
+ *
153
+ * @example
154
+ * const reason: ModelsSetReason = 'add';
155
+ */
156
+ export type ModelsSetReason = 'set' | 'add' | 'remove' | 'clear';
157
+ /**
158
+ * Ошибка, выбрасываемая при нарушении уникальности id моделей слоя.
159
+ *
160
+ * Возникает при попытке:
161
+ * - добавить модель с уже существующим id;
162
+ * - передать в setModels массив с дублирующимися id.
163
+ */
164
+ export declare class DuplicateModelIdError extends Error {
165
+ /** Дублирующийся id. */
166
+ readonly id: Id;
167
+ /** Идентификатор слоя. */
168
+ readonly layerId: string;
169
+ readonly name = "DuplicateModelIdError";
170
+ constructor(
171
+ /** Дублирующийся id. */
172
+ id: Id,
173
+ /** Идентификатор слоя. */
174
+ layerId: string, message?: string);
175
+ }
176
+ /**
177
+ * Событие внешнего изменения коллекции моделей слоя.
178
+ */
179
+ export type ModelsCollectionEvent<M> = {
180
+ /** Снимок коллекции до изменения. */
181
+ prev: readonly M[];
182
+ /** Снимок коллекции после изменения. */
183
+ next: readonly M[];
184
+ /** Причина изменения. */
185
+ reason: ModelsSetReason;
186
+ /**
187
+ * Опциональный diff.
188
+ *
189
+ * Гарантируется для reason = 'add' | 'remove' | 'clear'.
190
+ * Может отсутствовать для reason = 'set'.
191
+ */
192
+ added?: readonly M[];
193
+ removed?: readonly M[];
194
+ };
195
+ /**
196
+ * API управления коллекцией моделей слоя.
197
+ */
198
+ export interface VectorLayerCollectionApi<M> {
199
+ /** Текущий иммутабельный снимок моделей слоя. */
200
+ getAllModels: () => readonly M[];
201
+ /**
202
+ * Полная замена набора моделей.
203
+ *
204
+ * Требования:
205
+ * - Все id в массиве должны быть уникальны.
206
+ * - При обнаружении дубликата выбрасывается DuplicateModelIdError.
207
+ */
208
+ setModels: (models: readonly M[]) => void;
209
+ /**
210
+ * Подписка на внешние изменения коллекции моделей
211
+ * (set / add / remove / clear).
212
+ */
213
+ onModelsCollectionChanged: (cb: (e: ModelsCollectionEvent<M>) => void) => Unsubscribe;
214
+ /**
215
+ * Добавляет модель в конец коллекции.
216
+ *
217
+ * @throws DuplicateModelIdError если модель с таким id уже существует.
218
+ */
219
+ addModel: (model: M) => void;
220
+ /**
221
+ * Добавляет несколько моделей в конец коллекции
222
+ * (в порядке входного массива).
223
+ *
224
+ * @throws DuplicateModelIdError
225
+ */
226
+ addModels: (models: readonly M[]) => void;
227
+ /**
228
+ * Удаляет модели по id.
229
+ *
230
+ * @returns Количество реально удалённых моделей.
231
+ */
232
+ removeModelsById: (ids: readonly Id[]) => number;
233
+ /**
234
+ * Полностью очищает слой.
235
+ */
236
+ clear: () => void;
237
+ }
143
238
  export type MutateOptions = {
144
239
  /** Причина изменения (по умолчанию 'mutate'). */
145
240
  reason?: ModelChangeReason;
@@ -190,9 +285,7 @@ export type Unsubscribe = () => void;
190
285
  * @example
191
286
  * ctx.layers.points.mutate(id, (prev) => ({ ...prev, active: true }));
192
287
  */
193
- export type VectorLayerApi<M, G extends Geometry> = {
194
- /** Заменить весь набор моделей в слое. */
195
- setModels: (models: readonly M[]) => void;
288
+ export type VectorLayerApi<M, G extends Geometry> = VectorLayerCollectionApi<M> & {
196
289
  /** Запросить пересчёт слоя/стилей. */
197
290
  invalidate: () => void;
198
291
  /** Найти модель по фиче слоя. */
@@ -201,9 +294,9 @@ export type VectorLayerApi<M, G extends Geometry> = {
201
294
  * Иммутабельное обновление модели по id.
202
295
  * `update` обязан вернуть новый объект (или тот же для no-op).
203
296
  */
204
- mutate: (id: string | number, update: (prev: M) => M, opts?: MutateOptions) => void;
297
+ mutate: (id: Id, update: (prev: M) => M, opts?: MutateOptions) => void;
205
298
  /** Массовая мутация (опционально). */
206
- mutateMany?: (ids: Array<string | number>, update: (prev: M) => M, opts?: MutateOptions) => void;
299
+ mutateMany?: (ids: Array<Id>, update: (prev: M) => M, opts?: MutateOptions) => void;
207
300
  /**
208
301
  * Переключение кластеризации (если слой её поддерживает).
209
302
  */
@@ -221,11 +314,11 @@ export type VectorLayerApi<M, G extends Geometry> = {
221
314
  /**
222
315
  * Центрирует карту на одной фиче по id.
223
316
  */
224
- centerOnModel: (id: string | number, opts?: ViewFitOptions) => void;
317
+ centerOnModel: (id: Id, opts?: ViewFitOptions) => void;
225
318
  /**
226
319
  * Центрирует карту на наборе фичей по списку id.
227
320
  */
228
- centerOnModels: (ids: ReadonlyArray<string | number>, opts?: ViewFitOptions) => void;
321
+ centerOnModels: (ids: ReadonlyArray<Id>, opts?: ViewFitOptions) => void;
229
322
  /**
230
323
  * Управление видимостью слоя.
231
324
  */
@@ -253,23 +346,23 @@ export type VectorLayerApi<M, G extends Geometry> = {
253
346
  /**
254
347
  * Получить модель по id.
255
348
  */
256
- getModelById: (id: string | number) => M | undefined;
349
+ getModelById: (id: Id) => M | undefined;
257
350
  /**
258
351
  * Проверить наличие модели по id.
259
352
  */
260
- hasModel: (id: string | number) => boolean;
261
- /**
262
- * Получить текущий снимок моделей.
263
- */
264
- getAllModels: () => readonly M[];
353
+ hasModel: (id: Id) => boolean;
265
354
  /**
266
355
  * Получить текущий снимок id моделей.
267
356
  */
268
- getAllModelIds: () => Array<string | number>;
357
+ getAllModelIds: () => Array<Id>;
269
358
  /**
270
359
  * Применить состояния стиля к фичам по id.
271
360
  */
272
- setFeatureStates: (ids: string | number | ReadonlyArray<string | number>, states?: FeatureState) => void;
361
+ setFeatureStates: (ids: Id | ReadonlyArray<Id>, states?: FeatureState) => void;
362
+ /**
363
+ * Сбросить состояния стиля у фич к состоянию по умолчанию.
364
+ */
365
+ clearFeatureStates: (ids: Id | ReadonlyArray<Id>) => void;
273
366
  };
274
367
  /**
275
368
  * Источник появления popup-элемента.
@@ -294,7 +387,7 @@ export type PopupItem<M> = {
294
387
  /** Смещение popup относительно координаты. */
295
388
  offset?: number[];
296
389
  /** Ключ дедупликации элементов. */
297
- dedupKey?: string | number;
390
+ dedupKey?: Id;
298
391
  /** Приоритет сортировки (чем больше, тем выше). */
299
392
  priority?: number;
300
393
  /** Источник элемента. */
@@ -314,7 +407,7 @@ export interface PopupHostApi {
314
407
  /** Очистить список. */
315
408
  clear: () => void;
316
409
  /** Удалить элемент по ключу. */
317
- remove: (key: string | number) => void;
410
+ remove: (key: Id) => void;
318
411
  /** Получить текущие элементы. */
319
412
  getItems: () => PopupItem<any>[];
320
413
  /** Примонтировать popup-хост к DOM-узлу. */
@@ -360,7 +453,7 @@ export type MapController = {
360
453
  */
361
454
  export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
362
455
  /** Получение идентификатора модели. */
363
- id: (model: M) => string | number;
456
+ id: (model: M) => Id;
364
457
  /** Синхронизация модели и геометрии. */
365
458
  geometry: {
366
459
  /** Преобразование модели в геометрию. */
@@ -393,12 +486,14 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
393
486
  */
394
487
  interactions?: {
395
488
  /**
396
- * Обработка клика по фиче.
489
+ * Обработка клика по карте в контексте слоя.
490
+ * Коллбек вызывается даже при отсутствии фич под курсором
491
+ * (в этом случае `items` будет пустым массивом).
397
492
  */
398
493
  click?: InteractionBase & {
399
494
  /** Переопределение hitTolerance для клика. */
400
495
  hitTolerance?: number;
401
- /** Коллбек клика по фичам текущего слоя. */
496
+ /** Коллбек клика по текущему слою. */
402
497
  onClick: (args: {
403
498
  items: Array<HitItem<M, G>>;
404
499
  ctx: MapContext;
@@ -406,12 +501,14 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
406
501
  }) => InteractionHandlerResult;
407
502
  };
408
503
  /**
409
- * Обработка двойного клика по фиче.
504
+ * Обработка двойного клика по карте в контексте слоя.
505
+ * Коллбек вызывается даже при отсутствии фич под курсором
506
+ * (в этом случае `items` будет пустым массивом).
410
507
  */
411
508
  doubleClick?: InteractionBase & {
412
509
  /** Переопределение hitTolerance для doubleClick. */
413
510
  hitTolerance?: number;
414
- /** Коллбек двойного клика по фичам текущего слоя. */
511
+ /** Коллбек двойного клика по текущему слою. */
415
512
  onDoubleClick: (args: {
416
513
  items: Array<HitItem<M, G>>;
417
514
  ctx: MapContext;
@@ -443,6 +540,17 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
443
540
  select?: InteractionBase & {
444
541
  /** Переопределение hitTolerance для select. */
445
542
  hitTolerance?: number;
543
+ /**
544
+ * Фильтрация/выбор целевых элементов select из кандидатов hit-test.
545
+ *
546
+ * Возврат `null/undefined` или пустого массива означает игнорирование select
547
+ * для текущего клика (без `onSelect`, `onClear` и без изменения `state`).
548
+ */
549
+ pickTargets?: (args: {
550
+ candidates: Array<HitItem<M, G>>;
551
+ ctx: MapContext;
552
+ event: MapBrowserEvent<UIEvent>;
553
+ }) => Array<HitItem<M, G>> | null | undefined;
446
554
  /** Выбраны фичи текущего слоя. */
447
555
  onSelect?: (args: {
448
556
  items: Array<HitItem<M, G>>;
@@ -95,6 +95,7 @@ export declare class InteractionManager<Layers extends readonly VectorLayerDescr
95
95
  private getOrderedLayers;
96
96
  private getHitTolerance;
97
97
  private isEnabled;
98
+ private isMaybeEnabled;
98
99
  private processHover;
99
100
  private processSelect;
100
101
  private processClick;
@@ -2,7 +2,7 @@ import Feature from 'ol/Feature';
2
2
  import type Geometry from 'ol/geom/Geometry';
3
3
  import type VectorLayer from 'ol/layer/Vector';
4
4
  import type VectorSource from 'ol/source/Vector';
5
- import type { FeatureDescriptor, MapContext, ModelChange, MutateOptions, VectorLayerApi, VectorLayerDescriptor, ViewFitOptions } from '../public/types';
5
+ import { type FeatureDescriptor, type Id, type MapContext, type ModelChange, type ModelsCollectionEvent, type MutateOptions, type VectorLayerApi, type VectorLayerDescriptor, type ViewFitOptions } from '../public/types';
6
6
  import { FeatureRegistry } from './feature-registry';
7
7
  export type VectorLayerBaseOptions<M, G extends Geometry, OPTS extends object> = {
8
8
  descriptor: VectorLayerDescriptor<M, G, OPTS>;
@@ -18,15 +18,23 @@ export declare abstract class VectorLayerBase<M, G extends Geometry, OPTS extend
18
18
  protected readonly registry: FeatureRegistry<M, G>;
19
19
  protected readonly scheduleInvalidate: () => void;
20
20
  protected readonly ctx: MapContext;
21
+ protected readonly layerId: string;
21
22
  private readonly changeHandlers;
23
+ private readonly collectionHandlers;
24
+ private models;
22
25
  constructor(options: VectorLayerBaseOptions<M, G, OPTS>);
23
26
  setModels(models: readonly M[]): void;
24
27
  invalidate(): void;
25
28
  protected syncFeatureFromModel(model: M): void;
26
29
  getModelByFeature(feature: Feature<G>): M | undefined;
27
- mutate(id: string | number, update: (prev: M) => M, opts?: MutateOptions): void;
28
- mutateMany(ids: Array<string | number>, update: (prev: M) => M, opts?: MutateOptions): void;
30
+ mutate(id: Id, update: (prev: M) => M, opts?: MutateOptions): void;
31
+ mutateMany(ids: Array<Id>, update: (prev: M) => M, opts?: MutateOptions): void;
29
32
  onModelsChanged(cb: (changes: ModelChange<M>[]) => void): () => void;
33
+ onModelsCollectionChanged(cb: (event: ModelsCollectionEvent<M>) => void): () => void;
34
+ addModel(model: M): void;
35
+ addModels(models: readonly M[]): void;
36
+ removeModelsById(ids: readonly Id[]): number;
37
+ clear(): void;
30
38
  /** Fit view to all features on the layer. No-op if extent is empty. */
31
39
  centerOnAllModels(opts?: ViewFitOptions): void;
32
40
  /** Fit view to a single feature by id. No-op if feature/geometry is missing. */
@@ -42,12 +50,18 @@ export declare abstract class VectorLayerBase<M, G extends Geometry, OPTS extend
42
50
  getOpacity(): number;
43
51
  getZIndex(): number | undefined;
44
52
  setZIndex(z: number): void;
45
- getModelById(id: string | number): M | undefined;
46
- hasModel(id: string | number): boolean;
53
+ getModelById(id: Id): M | undefined;
54
+ hasModel(id: Id): boolean;
47
55
  getAllModels(): readonly M[];
48
- getAllModelIds(): Array<string | number>;
49
- setFeatureStates(ids: string | number | ReadonlyArray<string | number>, states?: string | string[]): void;
56
+ getAllModelIds(): Array<Id>;
57
+ setFeatureStates(ids: Id | ReadonlyArray<Id>, states?: string | string[]): void;
58
+ clearFeatureStates(ids: Id | ReadonlyArray<Id>): void;
50
59
  protected setModelsInternal(models: readonly M[]): void;
51
60
  protected getCenterOnAllModelsSource(): VectorSource<G> | null;
52
61
  private emitModelChanges;
62
+ private emitCollectionChange;
63
+ private assertUniqueIds;
64
+ private throwDuplicateId;
65
+ private applyModelsUpdate;
66
+ private replaceModelSnapshot;
53
67
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aur-openlayers",
3
- "version": "0.0.3",
3
+ "version": "1.0.1",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.2.0",
6
6
  "@angular/core": "^19.2.0",