aur-openlayers 0.0.3 → 1.0.0

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,19 @@ 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;
273
362
  };
274
363
  /**
275
364
  * Источник появления popup-элемента.
@@ -294,7 +383,7 @@ export type PopupItem<M> = {
294
383
  /** Смещение popup относительно координаты. */
295
384
  offset?: number[];
296
385
  /** Ключ дедупликации элементов. */
297
- dedupKey?: string | number;
386
+ dedupKey?: Id;
298
387
  /** Приоритет сортировки (чем больше, тем выше). */
299
388
  priority?: number;
300
389
  /** Источник элемента. */
@@ -314,7 +403,7 @@ export interface PopupHostApi {
314
403
  /** Очистить список. */
315
404
  clear: () => void;
316
405
  /** Удалить элемент по ключу. */
317
- remove: (key: string | number) => void;
406
+ remove: (key: Id) => void;
318
407
  /** Получить текущие элементы. */
319
408
  getItems: () => PopupItem<any>[];
320
409
  /** Примонтировать popup-хост к DOM-узлу. */
@@ -360,7 +449,7 @@ export type MapController = {
360
449
  */
361
450
  export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
362
451
  /** Получение идентификатора модели. */
363
- id: (model: M) => string | number;
452
+ id: (model: M) => Id;
364
453
  /** Синхронизация модели и геометрии. */
365
454
  geometry: {
366
455
  /** Преобразование модели в геометрию. */
@@ -393,12 +482,14 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
393
482
  */
394
483
  interactions?: {
395
484
  /**
396
- * Обработка клика по фиче.
485
+ * Обработка клика по карте в контексте слоя.
486
+ * Коллбек вызывается даже при отсутствии фич под курсором
487
+ * (в этом случае `items` будет пустым массивом).
397
488
  */
398
489
  click?: InteractionBase & {
399
490
  /** Переопределение hitTolerance для клика. */
400
491
  hitTolerance?: number;
401
- /** Коллбек клика по фичам текущего слоя. */
492
+ /** Коллбек клика по текущему слою. */
402
493
  onClick: (args: {
403
494
  items: Array<HitItem<M, G>>;
404
495
  ctx: MapContext;
@@ -406,12 +497,14 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
406
497
  }) => InteractionHandlerResult;
407
498
  };
408
499
  /**
409
- * Обработка двойного клика по фиче.
500
+ * Обработка двойного клика по карте в контексте слоя.
501
+ * Коллбек вызывается даже при отсутствии фич под курсором
502
+ * (в этом случае `items` будет пустым массивом).
410
503
  */
411
504
  doubleClick?: InteractionBase & {
412
505
  /** Переопределение hitTolerance для doubleClick. */
413
506
  hitTolerance?: number;
414
- /** Коллбек двойного клика по фичам текущего слоя. */
507
+ /** Коллбек двойного клика по текущему слою. */
415
508
  onDoubleClick: (args: {
416
509
  items: Array<HitItem<M, G>>;
417
510
  ctx: MapContext;
@@ -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,17 @@ 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;
50
58
  protected setModelsInternal(models: readonly M[]): void;
51
59
  protected getCenterOnAllModelsSource(): VectorSource<G> | null;
52
60
  private emitModelChanges;
61
+ private emitCollectionChange;
62
+ private assertUniqueIds;
63
+ private throwDuplicateId;
64
+ private applyModelsUpdate;
65
+ private replaceModelSnapshot;
53
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aur-openlayers",
3
- "version": "0.0.3",
3
+ "version": "1.0.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.2.0",
6
6
  "@angular/core": "^19.2.0",