aur-openlayers 0.0.2 → 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,100 @@ 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
+ }
238
+ export type MutateOptions = {
239
+ /** Причина изменения (по умолчанию 'mutate'). */
240
+ reason?: ModelChangeReason;
241
+ /** Без оповещения подписчиков (по умолчанию false). */
242
+ silent?: boolean;
243
+ };
143
244
  /**
144
245
  * Паддинги для операций fit/center.
145
246
  *
@@ -184,22 +285,18 @@ export type Unsubscribe = () => void;
184
285
  * @example
185
286
  * ctx.layers.points.mutate(id, (prev) => ({ ...prev, active: true }));
186
287
  */
187
- export type VectorLayerApi<M, G extends Geometry> = {
188
- /** Заменить весь набор моделей в слое. */
189
- setModels: (models: readonly M[]) => void;
288
+ export type VectorLayerApi<M, G extends Geometry> = VectorLayerCollectionApi<M> & {
190
289
  /** Запросить пересчёт слоя/стилей. */
191
290
  invalidate: () => void;
192
- /** Синхронизировать модель в фичу при внешнем изменении модели. */
193
- syncFeatureFromModel: (model: M) => void;
194
291
  /** Найти модель по фиче слоя. */
195
292
  getModelByFeature: (feature: Feature<G>) => M | undefined;
196
293
  /**
197
294
  * Иммутабельное обновление модели по id.
198
295
  * `update` обязан вернуть новый объект (или тот же для no-op).
199
296
  */
200
- mutate: (id: string | number, update: (prev: M) => M, reason?: ModelChangeReason) => void;
297
+ mutate: (id: Id, update: (prev: M) => M, opts?: MutateOptions) => void;
201
298
  /** Массовая мутация (опционально). */
202
- mutateMany?: (ids: Array<string | number>, update: (prev: M) => M, reason?: ModelChangeReason) => void;
299
+ mutateMany?: (ids: Array<Id>, update: (prev: M) => M, opts?: MutateOptions) => void;
203
300
  /**
204
301
  * Переключение кластеризации (если слой её поддерживает).
205
302
  */
@@ -217,11 +314,11 @@ export type VectorLayerApi<M, G extends Geometry> = {
217
314
  /**
218
315
  * Центрирует карту на одной фиче по id.
219
316
  */
220
- centerOnModel: (id: string | number, opts?: ViewFitOptions) => void;
317
+ centerOnModel: (id: Id, opts?: ViewFitOptions) => void;
221
318
  /**
222
319
  * Центрирует карту на наборе фичей по списку id.
223
320
  */
224
- centerOnModels: (ids: ReadonlyArray<string | number>, opts?: ViewFitOptions) => void;
321
+ centerOnModels: (ids: ReadonlyArray<Id>, opts?: ViewFitOptions) => void;
225
322
  /**
226
323
  * Управление видимостью слоя.
227
324
  */
@@ -249,23 +346,19 @@ export type VectorLayerApi<M, G extends Geometry> = {
249
346
  /**
250
347
  * Получить модель по id.
251
348
  */
252
- getModelById: (id: string | number) => M | undefined;
349
+ getModelById: (id: Id) => M | undefined;
253
350
  /**
254
351
  * Проверить наличие модели по id.
255
352
  */
256
- hasModel: (id: string | number) => boolean;
257
- /**
258
- * Получить текущий снимок моделей.
259
- */
260
- getAllModels: () => readonly M[];
353
+ hasModel: (id: Id) => boolean;
261
354
  /**
262
355
  * Получить текущий снимок id моделей.
263
356
  */
264
- getAllModelIds: () => Array<string | number>;
357
+ getAllModelIds: () => Array<Id>;
265
358
  /**
266
359
  * Применить состояния стиля к фичам по id.
267
360
  */
268
- setFeatureStates: (ids: string | number | ReadonlyArray<string | number>, states?: FeatureState) => void;
361
+ setFeatureStates: (ids: Id | ReadonlyArray<Id>, states?: FeatureState) => void;
269
362
  };
270
363
  /**
271
364
  * Источник появления popup-элемента.
@@ -290,7 +383,7 @@ export type PopupItem<M> = {
290
383
  /** Смещение popup относительно координаты. */
291
384
  offset?: number[];
292
385
  /** Ключ дедупликации элементов. */
293
- dedupKey?: string | number;
386
+ dedupKey?: Id;
294
387
  /** Приоритет сортировки (чем больше, тем выше). */
295
388
  priority?: number;
296
389
  /** Источник элемента. */
@@ -310,7 +403,7 @@ export interface PopupHostApi {
310
403
  /** Очистить список. */
311
404
  clear: () => void;
312
405
  /** Удалить элемент по ключу. */
313
- remove: (key: string | number) => void;
406
+ remove: (key: Id) => void;
314
407
  /** Получить текущие элементы. */
315
408
  getItems: () => PopupItem<any>[];
316
409
  /** Примонтировать popup-хост к DOM-узлу. */
@@ -356,7 +449,7 @@ export type MapController = {
356
449
  */
357
450
  export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
358
451
  /** Получение идентификатора модели. */
359
- id: (model: M) => string | number;
452
+ id: (model: M) => Id;
360
453
  /** Синхронизация модели и геометрии. */
361
454
  geometry: {
362
455
  /** Преобразование модели в геометрию. */
@@ -389,12 +482,14 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
389
482
  */
390
483
  interactions?: {
391
484
  /**
392
- * Обработка клика по фиче.
485
+ * Обработка клика по карте в контексте слоя.
486
+ * Коллбек вызывается даже при отсутствии фич под курсором
487
+ * (в этом случае `items` будет пустым массивом).
393
488
  */
394
489
  click?: InteractionBase & {
395
490
  /** Переопределение hitTolerance для клика. */
396
491
  hitTolerance?: number;
397
- /** Коллбек клика по фичам текущего слоя. */
492
+ /** Коллбек клика по текущему слою. */
398
493
  onClick: (args: {
399
494
  items: Array<HitItem<M, G>>;
400
495
  ctx: MapContext;
@@ -402,12 +497,14 @@ export interface FeatureDescriptor<M, G extends Geometry, OPTS extends object> {
402
497
  }) => InteractionHandlerResult;
403
498
  };
404
499
  /**
405
- * Обработка двойного клика по фиче.
500
+ * Обработка двойного клика по карте в контексте слоя.
501
+ * Коллбек вызывается даже при отсутствии фич под курсором
502
+ * (в этом случае `items` будет пустым массивом).
406
503
  */
407
504
  doubleClick?: InteractionBase & {
408
505
  /** Переопределение hitTolerance для doubleClick. */
409
506
  hitTolerance?: number;
410
- /** Коллбек двойного клика по фичам текущего слоя. */
507
+ /** Коллбек двойного клика по текущему слою. */
411
508
  onDoubleClick: (args: {
412
509
  items: Array<HitItem<M, G>>;
413
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, 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
- syncFeatureFromModel(model: M): void;
28
+ protected syncFeatureFromModel(model: M): void;
26
29
  getModelByFeature(feature: Feature<G>): M | undefined;
27
- mutate(id: string | number, update: (prev: M) => M, reason?: ModelChange<M>['reason']): void;
28
- mutateMany(ids: Array<string | number>, update: (prev: M) => M, reason?: ModelChange<M>['reason']): 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.2",
3
+ "version": "1.0.0",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^19.2.0",
6
6
  "@angular/core": "^19.2.0",