@svar-ui/calendar-store 2.6.0 → 2.7.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.
package/dist/index.d.mts CHANGED
@@ -6,6 +6,7 @@ interface EventChunk {
6
6
  event: CalendarEvent;
7
7
  start: Date;
8
8
  end: Date;
9
+ unitIndex?: number;
9
10
  }
10
11
  declare abstract class ViewModel {
11
12
  render?: string;
@@ -22,6 +23,7 @@ declare abstract class ViewModel {
22
23
  abstract getRangeLabel(): string;
23
24
  setRange(date: Date): [Date, Date];
24
25
  process(events: CalendarEvent[]): SectionResult[];
26
+ projectEvent(event: Partial<CalendarEvent>): ProjectedEvent[];
25
27
  toPositionStart(sectionName: string, x: number, y: number, ev?: Partial<CalendarEvent>, snap?: boolean): Partial<CalendarEvent>;
26
28
  toPositionEnd(sectionName: string, x: number, y: number, ev?: Partial<CalendarEvent>, snap?: boolean): Partial<CalendarEvent>;
27
29
  private resolvePosition;
@@ -29,7 +31,6 @@ declare abstract class ViewModel {
29
31
  protected buildCells(_xScale: Scale, _yScale: Scale): GridCell[];
30
32
  protected sortBeforeLayout(primitives: Primitive[], mode: string): void;
31
33
  private getPrimaryAxis;
32
- private splitEvent;
33
34
  protected findUnitIndex(scale: Scale, event: CalendarEvent): number;
34
35
  private findUnitForPosition;
35
36
  protected mapToPrimitive(chunk: EventChunk, primaryUnit: {
@@ -42,12 +43,13 @@ declare abstract class ViewModel {
42
43
  declare class EventsStore implements IEventStore {
43
44
  protected events: CalendarEvent[];
44
45
  constructor(initialEvents?: CalendarEvent[]);
45
- addEvent(event: Partial<CalendarEvent>): CalendarEvent;
46
+ addEvent(event: Partial<CalendarEvent>, overwrite?: boolean): CalendarEvent;
46
47
  updateEvent(id: EventID, updates: Partial<CalendarEvent>, _mode?: "single" | "following", _originalDate?: string): CalendarEvent | null;
47
48
  removeEvent(id: EventID): boolean;
48
49
  getEvent(id: EventID): CalendarEvent | undefined;
49
50
  getEvents(start?: Date, end?: Date): CalendarEvent[];
50
51
  clear(): void;
52
+ restore(events: CalendarEvent[]): void;
51
53
  getCount(): number;
52
54
  }
53
55
  //#endregion
@@ -68,9 +70,23 @@ type State = {
68
70
  events: EventsStore;
69
71
  viewData: any;
70
72
  filters: Map<string, (obj: any) => boolean>;
71
- editorData: CalendarEvent | null;
73
+ editorData: EditorData | null;
74
+ history: HistoryState;
72
75
  _view: ViewModel;
73
76
  };
77
+ type RecurringEditMode = "series" | "single" | "following";
78
+ interface EditorData {
79
+ id: EventID;
80
+ values: CalendarEvent;
81
+ rawId: EventID;
82
+ recurring: boolean;
83
+ recurringMode: RecurringEditMode;
84
+ recurringOriginalDate: string | null;
85
+ }
86
+ type HistoryState = {
87
+ undo: number;
88
+ redo: number;
89
+ };
74
90
  interface StoreActions {
75
91
  ["navigate-to"]: {
76
92
  date?: Date;
@@ -83,29 +99,49 @@ interface StoreActions {
83
99
  event: Partial<CalendarEvent>;
84
100
  edit?: boolean;
85
101
  id?: EventID;
102
+ rawId?: EventID;
86
103
  };
87
104
  ["update-event"]: {
88
105
  id: EventID;
106
+ rawId?: EventID;
89
107
  event: Partial<CalendarEvent>;
90
108
  mode?: "single" | "following";
91
- originalDate?: string;
92
109
  };
93
110
  ["delete-event"]: {
94
111
  id: EventID;
112
+ rawId?: EventID;
113
+ cascade?: boolean;
95
114
  };
96
115
  ["select-event"]: {
97
116
  id: EventID | null;
117
+ rawId?: EventID | null;
118
+ mode?: RecurringEditMode | null;
98
119
  };
99
120
  ["move-event"]: {
100
121
  id: EventID;
101
- x: number;
102
- y: number;
122
+ rawId?: EventID;
123
+ event: Partial<CalendarEvent>;
124
+ mode?: "single" | "following";
103
125
  };
104
126
  ["filter-events"]: {
105
127
  filter?: ((obj: any) => boolean) | null;
106
128
  tag?: string;
107
129
  };
130
+ ["request-data"]: RequestDataAction;
131
+ ["provide-data"]: ProvideDataAction;
108
132
  }
133
+ type RequestDataAction = {
134
+ startDate: Date;
135
+ endDate: Date;
136
+ date: Date;
137
+ view: string;
138
+ };
139
+ type ProvideDataAction = {
140
+ data: {
141
+ events: CalendarEvent[];
142
+ };
143
+ reset?: boolean;
144
+ };
109
145
  type EventID = string | number;
110
146
  interface CalendarEvent {
111
147
  id: EventID;
@@ -114,6 +150,10 @@ interface CalendarEvent {
114
150
  allDay?: boolean;
115
151
  [key: string]: any;
116
152
  }
153
+ type EventProjection = {
154
+ htmlEvent: any;
155
+ event: Partial<CalendarEvent>;
156
+ };
117
157
  interface ScaleUnit {
118
158
  id: string | number;
119
159
  label: string;
@@ -122,6 +162,13 @@ interface ScaleUnit {
122
162
  weekend?: boolean;
123
163
  ui?: Record<string, any>;
124
164
  }
165
+ type ScaleValue = Date | string | number | ScaleValue[];
166
+ interface ScaleSegment {
167
+ start: Date;
168
+ end: Date;
169
+ unitIndex: number;
170
+ sourceUnitId?: EventID;
171
+ }
125
172
  interface Scale {
126
173
  units: ScaleUnit[];
127
174
  eventToPosition(event: CalendarEvent): {
@@ -131,7 +178,10 @@ interface Scale {
131
178
  contains(date: Date): boolean;
132
179
  readonly count: number;
133
180
  getHeaders(): ScaleUnit[][];
134
- positionToValue(position: number): Date | string | number;
181
+ positionToValue(position: number): ScaleValue;
182
+ segmentEvent(event: CalendarEvent): ScaleSegment[];
183
+ getUnitStart(unitIndex: number): Date | null;
184
+ applyPosition(position: number, target: "start" | "end", event?: Partial<CalendarEvent>, snap?: boolean): Partial<CalendarEvent>;
135
185
  }
136
186
  interface DateScaleConfig {
137
187
  type: "date";
@@ -167,8 +217,9 @@ interface UnitScaleConfig {
167
217
  id: string | number;
168
218
  label: string;
169
219
  }[];
220
+ multiple?: boolean;
170
221
  accessor: string | {
171
- get: (event: CalendarEvent) => string | number;
222
+ get: (event: CalendarEvent) => string | number | (string | number)[];
172
223
  set: (event: Partial<CalendarEvent>, id: string | number) => Partial<CalendarEvent>;
173
224
  };
174
225
  visible?: boolean;
@@ -199,6 +250,11 @@ interface Primitive {
199
250
  slot?: number;
200
251
  maxConcurrency?: number;
201
252
  }
253
+ interface ProjectedEvent {
254
+ section: string;
255
+ mode: SectionMode;
256
+ primitives: Primitive[];
257
+ }
202
258
  interface GridCell {
203
259
  date: Date;
204
260
  day: number;
@@ -212,6 +268,19 @@ interface GridCell {
212
268
  }
213
269
  type SectionMode = "bars" | "boxes" | "grid" | "list" | "year";
214
270
  type BoxLayoutMode = "split" | "overlap";
271
+ type EventOverflowMode = "more" | "expand";
272
+ interface SectionUI {
273
+ [key: string]: any;
274
+ drag?: boolean;
275
+ dragCreate?: boolean;
276
+ clipDrag?: boolean;
277
+ boxLayout?: BoxLayoutMode;
278
+ eventOverflow?: EventOverflowMode;
279
+ columns?: number;
280
+ weekStartDay?: number;
281
+ months?: any[];
282
+ nowLine?: boolean;
283
+ }
215
284
  interface Section {
216
285
  name: string;
217
286
  mode: SectionMode;
@@ -221,7 +290,7 @@ interface Section {
221
290
  boxLayout?: BoxLayoutMode;
222
291
  filter?: (event: CalendarEvent) => boolean;
223
292
  size?: number | "content" | "content-optional";
224
- ui?: Record<string, any>;
293
+ ui?: SectionUI;
225
294
  }
226
295
  interface SectionResult {
227
296
  name: string;
@@ -233,7 +302,7 @@ interface SectionResult {
233
302
  xVisible?: boolean;
234
303
  yVisible?: boolean;
235
304
  cells?: GridCell[];
236
- ui?: Record<string, any>;
305
+ ui?: SectionUI;
237
306
  }
238
307
  interface CellContext {
239
308
  view: string;
@@ -262,7 +331,7 @@ interface IEventStore {
262
331
  getCount(): number;
263
332
  }
264
333
  //#endregion
265
- //#region src/models/helpers/scales.d.ts
334
+ //#region src/models/helpers/linear_scale.d.ts
266
335
  declare class LinearScale implements Scale {
267
336
  rangeStart: Date;
268
337
  rangeEnd: Date;
@@ -280,8 +349,13 @@ declare class LinearScale implements Scale {
280
349
  };
281
350
  contains(date: Date): boolean;
282
351
  positionToValue(position: number): Date;
352
+ segmentEvent(event: CalendarEvent): ScaleSegment[];
353
+ getUnitStart(unitIndex: number): Date | null;
354
+ applyPosition(position: number, target: "start" | "end", event?: Partial<CalendarEvent>, snap?: boolean): Partial<CalendarEvent>;
283
355
  getHeaders(): ScaleUnit[][];
284
356
  }
357
+ //#endregion
358
+ //#region src/models/helpers/discrete_scale.d.ts
285
359
  declare class DiscreteScale implements Scale {
286
360
  items: {
287
361
  id: string | number;
@@ -293,13 +367,15 @@ declare class DiscreteScale implements Scale {
293
367
  };
294
368
  boxSize: number;
295
369
  units: ScaleUnit[];
370
+ multiple: boolean;
371
+ private readValue;
296
372
  constructor(items: {
297
373
  id: string | number;
298
374
  label: string;
299
375
  }[], accessor: {
300
- get: (event: CalendarEvent) => string | number;
376
+ get: (event: CalendarEvent) => string | number | (string | number)[];
301
377
  set: (event: Partial<CalendarEvent>, id: string | number) => Partial<CalendarEvent>;
302
- }, ui?: Record<string, any>);
378
+ }, ui?: Record<string, any>, multiple?: boolean);
303
379
  get count(): number;
304
380
  eventToPosition(event: CalendarEvent): {
305
381
  start: number;
@@ -307,8 +383,34 @@ declare class DiscreteScale implements Scale {
307
383
  };
308
384
  contains(): boolean;
309
385
  positionToValue(position: number): string | number;
386
+ segmentEvent(event: CalendarEvent): ScaleSegment[];
387
+ getUnitStart(): Date | null;
388
+ applyPosition(position: number, _target: "start" | "end", event?: Partial<CalendarEvent>): Partial<CalendarEvent>;
389
+ getHeaders(): ScaleUnit[][];
390
+ }
391
+ //#endregion
392
+ //#region src/models/helpers/combined_scale.d.ts
393
+ declare class CombinedScale implements Scale {
394
+ outer: Scale;
395
+ inner: Scale;
396
+ units: ScaleUnit[];
397
+ private headers;
398
+ private leaves;
399
+ constructor(outer: Scale, inner: Scale);
400
+ get count(): number;
401
+ eventToPosition(event: CalendarEvent): {
402
+ start: number;
403
+ end: number;
404
+ };
405
+ contains(date: Date): boolean;
310
406
  getHeaders(): ScaleUnit[][];
407
+ positionToValue(position: number): ScaleValue[];
408
+ segmentEvent(event: CalendarEvent): ScaleSegment[];
409
+ getUnitStart(unitIndex: number): Date | null;
410
+ applyPosition(position: number, target: "start" | "end", event?: Partial<CalendarEvent>, snap?: boolean): Partial<CalendarEvent>;
311
411
  }
412
+ //#endregion
413
+ //#region src/models/helpers/scales.d.ts
312
414
  declare function createScale(config: ScaleConfig, startDate: Date, fmt?: FormatFactory): Scale;
313
415
  //#endregion
314
416
  //#region src/models/helpers/layout.d.ts
@@ -325,6 +427,21 @@ declare function layoutBoxes(primitives: Primitive[]): BoxLayoutResult;
325
427
  //#region src/models/helpers/filters.d.ts
326
428
  declare function isMultiDay(event: CalendarEvent): boolean;
327
429
  //#endregion
430
+ //#region src/helpers/ids.d.ts
431
+ interface DecodedEventId {
432
+ id: EventID;
433
+ index?: number;
434
+ unitId?: EventID;
435
+ eventDate?: string;
436
+ }
437
+ interface EventIdDetails {
438
+ index?: number | null;
439
+ unitId?: EventID | null;
440
+ eventDate?: string | null;
441
+ }
442
+ declare function decodeId(value: EventID): DecodedEventId;
443
+ declare function encodeId(value: EventID, details: EventIdDetails): EventID;
444
+ //#endregion
328
445
  //#region src/models/week_view.d.ts
329
446
  declare class WeekViewModel extends ViewModel {
330
447
  getSections(): Section[];
@@ -384,11 +501,14 @@ type ToolbarItem = {
384
501
  }[];
385
502
  [key: string]: any;
386
503
  };
387
- declare function getToolbarItems(): ToolbarItem[];
504
+ declare function getToolbarItems(config?: {
505
+ history?: boolean;
506
+ }): ToolbarItem[];
388
507
  //#endregion
389
508
  //#region src/calendar_store.d.ts
390
509
  declare class CalendarStore extends Store<State> {
391
510
  in: EventBus<StoreActions, keyof StoreActions>;
511
+ meta: Record<string, any>;
392
512
  private _router;
393
513
  private _views;
394
514
  private _weekStartDay;
@@ -400,6 +520,7 @@ declare class CalendarStore extends Store<State> {
400
520
  });
401
521
  configureViews(views?: ViewConfig[]): void;
402
522
  init(state: Partial<State>): void;
523
+ postInit(): void;
403
524
  private applyViewConfig;
404
525
  getView(name: string): any;
405
526
  getEvents(start?: Date, end?: Date): CalendarEvent[];
@@ -413,4 +534,4 @@ declare class CalendarStore extends Store<State> {
413
534
  //#region src/index.d.ts
414
535
  declare const version: string;
415
536
  //#endregion
416
- export { type Brandmark, type CalendarEvent, CalendarStore, type CellContext, type CellCss, type CombinedScaleConfig, type DateScaleConfig, DayViewModel, DiscreteScale, type EventContentMode, type EventContext, type EventCss, type EventID, EventsStore, type FormatFactory, type GridCell, type IEventStore, LinearScale, MonthViewModel, type Primitive, type Scale, type ScaleConfig, type ScaleUnit, type Section, type SectionMode, type SectionResult, type StackedScaleConfig, type State, type StoreActions, type TimeScaleConfig, type ToolbarItem, type UnitScaleConfig, type ViewConfig, ViewModel, WeekViewModel, createScale, getMenuOptions, getToolbarItems, isMultiDay, layoutBars, layoutBoxes, registerCalendarView, version };
537
+ export { type Brandmark, type CalendarEvent, CalendarStore, type CellContext, type CellCss, CombinedScale, type CombinedScaleConfig, type DateScaleConfig, DayViewModel, type DecodedEventId, DiscreteScale, type EditorData, type EventContentMode, type EventContext, type EventCss, type EventID, type EventIdDetails, type EventOverflowMode, type EventProjection, EventsStore, type FormatFactory, type GridCell, type IEventStore, LinearScale, MonthViewModel, type Primitive, type ProjectedEvent, type ProvideDataAction, type RecurringEditMode, type RequestDataAction, type Scale, type ScaleConfig, type ScaleSegment, type ScaleUnit, type ScaleValue, type Section, type SectionMode, type SectionResult, type SectionUI, type StackedScaleConfig, type State, type StoreActions, type TimeScaleConfig, type ToolbarItem, type UnitScaleConfig, type ViewConfig, ViewModel, WeekViewModel, createScale, decodeId, encodeId, getMenuOptions, getToolbarItems, isMultiDay, layoutBars, layoutBoxes, registerCalendarView, version };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DataRouter, EventBus, Store, tempID } from "@svar-ui/lib-state";
2
2
 
3
3
  //#region package.json
4
- var version$1 = "2.6.0";
4
+ var version$1 = "2.7.0";
5
5
 
6
6
  //#endregion
7
7
  //#region src/events_store.ts
@@ -10,12 +10,19 @@ var EventsStore = class {
10
10
  constructor(initialEvents) {
11
11
  if (initialEvents) for (const ev of initialEvents) this.addEvent(ev);
12
12
  }
13
- addEvent(event) {
14
- const id = event.id ?? tempID();
15
- const full = {
13
+ addEvent(event, overwrite) {
14
+ let full = event;
15
+ if (!event.id) full = {
16
16
  ...event,
17
- id
17
+ id: tempID()
18
18
  };
19
+ if (overwrite) {
20
+ const idx = this.events.findIndex((e) => e.id === full.id);
21
+ if (idx !== -1) {
22
+ this.events[idx] = full;
23
+ return full;
24
+ }
25
+ }
19
26
  this.events.push(full);
20
27
  return full;
21
28
  }
@@ -51,19 +58,16 @@ var EventsStore = class {
51
58
  clear() {
52
59
  this.events = [];
53
60
  }
61
+ restore(events) {
62
+ this.events = [...events];
63
+ }
54
64
  getCount() {
55
65
  return this.events.length;
56
66
  }
57
67
  };
58
68
 
59
69
  //#endregion
60
- //#region src/models/helpers/scales.ts
61
- const DAY_MS$1 = 1440 * 60 * 1e3;
62
- function midnight(date) {
63
- const d = new Date(date);
64
- d.setHours(0, 0, 0, 0);
65
- return d;
66
- }
70
+ //#region src/models/helpers/linear_scale.ts
67
71
  var LinearScale = class {
68
72
  rangeStart;
69
73
  rangeEnd;
@@ -81,8 +85,8 @@ var LinearScale = class {
81
85
  this.discrete = discrete ?? false;
82
86
  const size = 100 / unitCount;
83
87
  this.units = [];
84
- const stepDays = stepMs >= DAY_MS$1 ? Math.round(stepMs / DAY_MS$1) : 0;
85
- const markWeekend = stepMs === DAY_MS$1 && unitCount > 1;
88
+ const stepDays = stepMs >= 864e5 ? Math.round(stepMs / DAY_MS) : 0;
89
+ const markWeekend = stepMs === 864e5 && unitCount > 1;
86
90
  for (let i = 0; i < unitCount; i++) {
87
91
  let unitStart;
88
92
  if (stepDays > 0) {
@@ -107,7 +111,7 @@ var LinearScale = class {
107
111
  }
108
112
  }
109
113
  formatId(date) {
110
- if (this.stepMs >= DAY_MS$1) return date.toISOString().slice(0, 10);
114
+ if (this.stepMs >= 864e5) return date.toISOString().slice(0, 10);
111
115
  return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
112
116
  }
113
117
  get count() {
@@ -132,18 +136,71 @@ var LinearScale = class {
132
136
  const range = this.rangeEnd.getTime() - this.rangeStart.getTime();
133
137
  return new Date(this.rangeStart.getTime() + position / 100 * range);
134
138
  }
139
+ segmentEvent(event) {
140
+ const segments = [];
141
+ const units = this.units;
142
+ let i = 0;
143
+ while (i < units.length && units[i].ui.date <= event.start) i++;
144
+ if (i > 0) i--;
145
+ for (; i < units.length; i++) {
146
+ const start = units[i].ui.date;
147
+ if (start >= event.end) break;
148
+ const next = units[i + 1];
149
+ const end = next ? next.ui.date : this.rangeEnd;
150
+ const chunkStart = event.start > start ? event.start : start;
151
+ const chunkEnd = event.end < end ? event.end : end;
152
+ if (chunkEnd > chunkStart) segments.push({
153
+ start: new Date(chunkStart),
154
+ end: new Date(chunkEnd),
155
+ unitIndex: i
156
+ });
157
+ }
158
+ return segments;
159
+ }
160
+ getUnitStart(unitIndex) {
161
+ const unit = this.units[unitIndex];
162
+ return unit?.ui?.date instanceof Date ? new Date(unit.ui.date) : null;
163
+ }
164
+ applyPosition(position, target, event, snap) {
165
+ let value = this.positionToValue(position);
166
+ if (snap && this.snapStepMs !== false) {
167
+ const base = this.rangeStart.getTime();
168
+ value = new Date(base + Math.round((value.getTime() - base) / this.snapStepMs) * this.snapStepMs);
169
+ }
170
+ if (this.stepMs >= 864e5) {
171
+ const source = event?.[target];
172
+ if (source instanceof Date) value.setHours(source.getHours(), source.getMinutes(), source.getSeconds(), source.getMilliseconds());
173
+ }
174
+ return {
175
+ ...event,
176
+ [target]: value
177
+ };
178
+ }
135
179
  getHeaders() {
136
180
  return [this.units];
137
181
  }
138
182
  };
183
+
184
+ //#endregion
185
+ //#region src/models/helpers/discrete_scale.ts
139
186
  var DiscreteScale = class {
140
187
  items;
141
188
  accessor;
142
189
  boxSize;
143
190
  units;
144
- constructor(items, accessor, ui) {
191
+ multiple;
192
+ readValue;
193
+ constructor(items, accessor, ui, multiple) {
145
194
  this.items = items;
146
- this.accessor = accessor;
195
+ this.multiple = multiple ?? false;
196
+ this.readValue = accessor.get;
197
+ this.accessor = {
198
+ ...accessor,
199
+ get: (event) => {
200
+ const value = accessor.get(event);
201
+ return Array.isArray(value) ? value[0] : value;
202
+ }
203
+ };
147
204
  this.boxSize = 100 / items.length;
148
205
  this.units = items.map((item, i) => ({
149
206
  id: item.id,
@@ -175,10 +232,150 @@ var DiscreteScale = class {
175
232
  const idx = Math.max(0, Math.min(Math.floor(position / this.boxSize), this.items.length - 1));
176
233
  return this.items[idx].id;
177
234
  }
235
+ segmentEvent(event) {
236
+ if (!this.multiple && this.items.length === 1) return [{
237
+ start: event.start,
238
+ end: event.end,
239
+ unitIndex: 0
240
+ }];
241
+ const raw = this.readValue(event);
242
+ if (!raw) return [];
243
+ return (Array.isArray(raw) ? raw : [raw]).map((value) => this.items.findIndex((item) => item.id === value)).filter((index) => index !== -1).map((unitIndex) => ({
244
+ start: event.start,
245
+ end: event.end,
246
+ unitIndex,
247
+ ...this.multiple ? { sourceUnitId: this.items[unitIndex].id } : {}
248
+ }));
249
+ }
250
+ getUnitStart() {
251
+ return null;
252
+ }
253
+ applyPosition(position, _target, event) {
254
+ return this.accessor.set(event ?? {}, this.positionToValue(position));
255
+ }
178
256
  getHeaders() {
179
257
  return [this.units];
180
258
  }
181
259
  };
260
+
261
+ //#endregion
262
+ //#region src/models/helpers/combined_scale.ts
263
+ function compositeId(outer, inner) {
264
+ return outer.id + "#" + inner.id;
265
+ }
266
+ function combineUnit(outer, inner, position, size) {
267
+ return {
268
+ id: compositeId(outer, inner),
269
+ label: inner.label,
270
+ position,
271
+ size,
272
+ weekend: inner.weekend ?? outer.weekend,
273
+ ui: {
274
+ ...outer.ui,
275
+ ...inner.ui,
276
+ combined: {
277
+ outer,
278
+ inner
279
+ }
280
+ }
281
+ };
282
+ }
283
+ function findUnitForPosition(units, position) {
284
+ for (let i = units.length - 1; i >= 0; i--) if (position >= units[i].position - .001) return i;
285
+ return 0;
286
+ }
287
+ var CombinedScale = class {
288
+ outer;
289
+ inner;
290
+ units;
291
+ headers;
292
+ leaves;
293
+ constructor(outer, inner) {
294
+ this.outer = outer;
295
+ this.inner = inner;
296
+ this.leaves = [];
297
+ const innerHeaders = inner.getHeaders();
298
+ this.headers = [...outer.getHeaders()];
299
+ for (const level of innerHeaders) {
300
+ const repeated = [];
301
+ for (let oi = 0; oi < outer.units.length; oi++) {
302
+ const outerUnit = outer.units[oi];
303
+ for (const innerUnit of level) repeated.push(combineUnit(outerUnit, innerUnit, outerUnit.position + innerUnit.position / 100 * outerUnit.size, innerUnit.size / 100 * outerUnit.size));
304
+ }
305
+ this.headers.push(repeated);
306
+ }
307
+ this.units = this.headers[this.headers.length - 1] ?? [];
308
+ for (let oi = 0; oi < outer.count; oi++) for (let ii = 0; ii < inner.count; ii++) this.leaves.push({
309
+ outerIndex: oi,
310
+ innerIndex: ii
311
+ });
312
+ }
313
+ get count() {
314
+ return this.outer.count * this.inner.count;
315
+ }
316
+ eventToPosition(event) {
317
+ const first = this.segmentEvent(event)[0];
318
+ if (!first) return {
319
+ start: -1,
320
+ end: -1
321
+ };
322
+ const unit = this.units[first.unitIndex];
323
+ return {
324
+ start: unit.position,
325
+ end: unit.position + unit.size
326
+ };
327
+ }
328
+ contains(date) {
329
+ return this.outer.contains(date) && this.inner.contains(date);
330
+ }
331
+ getHeaders() {
332
+ return this.headers;
333
+ }
334
+ positionToValue(position) {
335
+ const index = findUnitForPosition(this.units, position);
336
+ const leaf = this.leaves[index];
337
+ return [this.outer.positionToValue(this.outer.units[leaf.outerIndex].position), this.inner.positionToValue(this.inner.units[leaf.innerIndex].position)];
338
+ }
339
+ segmentEvent(event) {
340
+ const result = [];
341
+ for (const outerSegment of this.outer.segmentEvent(event)) {
342
+ const scoped = {
343
+ ...event,
344
+ start: outerSegment.start,
345
+ end: outerSegment.end
346
+ };
347
+ for (const innerSegment of this.inner.segmentEvent(scoped)) result.push({
348
+ start: innerSegment.start,
349
+ end: innerSegment.end,
350
+ unitIndex: outerSegment.unitIndex * this.inner.count + innerSegment.unitIndex,
351
+ ...innerSegment.sourceUnitId !== void 0 ? { sourceUnitId: innerSegment.sourceUnitId } : outerSegment.sourceUnitId !== void 0 ? { sourceUnitId: outerSegment.sourceUnitId } : {}
352
+ });
353
+ }
354
+ return result;
355
+ }
356
+ getUnitStart(unitIndex) {
357
+ const leaf = this.leaves[unitIndex];
358
+ if (!leaf) return null;
359
+ return this.inner.getUnitStart(leaf.innerIndex) ?? this.outer.getUnitStart(leaf.outerIndex);
360
+ }
361
+ applyPosition(position, target, event, snap) {
362
+ const index = findUnitForPosition(this.units, position);
363
+ const leaf = this.leaves[index];
364
+ let result = event ? { ...event } : {};
365
+ Object.assign(result, this.outer.applyPosition(this.outer.units[leaf.outerIndex].position, target, result, snap));
366
+ Object.assign(result, this.inner.applyPosition(this.inner.units[leaf.innerIndex].position, target, result, snap));
367
+ return result;
368
+ }
369
+ };
370
+
371
+ //#endregion
372
+ //#region src/models/helpers/scales.ts
373
+ const DAY_MS = 1440 * 60 * 1e3;
374
+ function midnight(date) {
375
+ const d = new Date(date);
376
+ d.setHours(0, 0, 0, 0);
377
+ return d;
378
+ }
182
379
  function resolveAccessor(accessor) {
183
380
  if (typeof accessor === "string") return {
184
381
  get: (event) => event[accessor],
@@ -189,15 +386,21 @@ function resolveAccessor(accessor) {
189
386
  };
190
387
  return accessor;
191
388
  }
389
+ function countMultipleScales(config) {
390
+ if (config.type === "unit") return config.multiple ? 1 : 0;
391
+ if (config.type === "combined") return countMultipleScales(config.outer) + countMultipleScales(config.inner);
392
+ if (config.type === "stacked") return config.levels.reduce((count, level) => count + countMultipleScales(level), 0);
393
+ return 0;
394
+ }
192
395
  function createScale(config, startDate, fmt) {
193
396
  switch (config.type) {
194
397
  case "date": {
195
398
  const c = config;
196
399
  const step = c.step ?? 1;
197
400
  const rangeStart = midnight(startDate);
198
- const rangeEnd = new Date(rangeStart.getTime() + c.length * step * DAY_MS$1);
199
- const stepMs = step * DAY_MS$1;
200
- const snapStepMs = c.snapStep === false ? false : (c.snapStep ?? step) * DAY_MS$1;
401
+ const rangeEnd = new Date(rangeStart.getTime() + c.length * step * DAY_MS);
402
+ const stepMs = step * DAY_MS;
403
+ const snapStepMs = c.snapStep === false ? false : (c.snapStep ?? step) * DAY_MS;
201
404
  const format = c.format && fmt ? fmt(c.format) : (d) => d.toLocaleDateString("en-US", { weekday: "short" });
202
405
  return new LinearScale(rangeStart, rangeEnd, c.length, stepMs, format, c.ui, c.discrete, snapStepMs);
203
406
  }
@@ -220,9 +423,16 @@ function createScale(config, startDate, fmt) {
220
423
  }
221
424
  case "unit": {
222
425
  const c = config;
223
- return new DiscreteScale(c.items, resolveAccessor(c.accessor), c.ui);
426
+ return new DiscreteScale(c.items, resolveAccessor(c.accessor), c.ui, c.multiple);
427
+ }
428
+ case "combined": {
429
+ const c = config;
430
+ if (countMultipleScales(c) > 1) throw new Error("CombinedScale supports at most one unit scale with multiple: true");
431
+ const outer = createScale(c.outer, startDate, fmt);
432
+ const inner = createScale(c.inner, startDate, fmt);
433
+ if (outer.count === 0 || inner.count === 0) throw new Error("CombinedScale requires non-empty outer and inner scales");
434
+ return new CombinedScale(outer, inner);
224
435
  }
225
- case "combined":
226
436
  case "stacked": throw new Error(`${config.type} scale not implemented`);
227
437
  default: throw new Error(`Unknown scale type`);
228
438
  }
@@ -308,10 +518,49 @@ function layoutBoxes(primitives) {
308
518
  function isMultiDay(event) {
309
519
  if (event.allDay) return true;
310
520
  const s = event.start;
311
- const e = event.end;
521
+ let e = event.end;
522
+ if (e.getHours() === 0 && e.getMinutes() === 0 && e.getSeconds() === 0 && e.getMilliseconds() === 0) e = /* @__PURE__ */ new Date(e.getTime() - 1);
312
523
  return s.getFullYear() !== e.getFullYear() || s.getMonth() !== e.getMonth() || s.getDate() !== e.getDate();
313
524
  }
314
525
 
526
+ //#endregion
527
+ //#region src/helpers/ids.ts
528
+ const SEPARATOR = "#";
529
+ function encodeValue(value) {
530
+ return typeof value === "string" ? `:${value}` : String(value);
531
+ }
532
+ function decodeValue(value) {
533
+ return value.startsWith(":") ? value.slice(1) : Number(value);
534
+ }
535
+ function setDetail(current, next) {
536
+ return next === void 0 ? current : next === null ? void 0 : next;
537
+ }
538
+ function decodeId(value) {
539
+ if (typeof value !== "string" || !value.includes(SEPARATOR)) return { id: value };
540
+ const [rawId, ...details] = value.split(SEPARATOR);
541
+ const id = decodeValue(rawId);
542
+ const [rawIndex, rawUnitId, eventDate] = details;
543
+ return {
544
+ id,
545
+ ...rawIndex ? { index: Number(rawIndex) } : {},
546
+ ...rawUnitId ? { unitId: decodeValue(rawUnitId) } : {},
547
+ ...eventDate ? { eventDate } : {}
548
+ };
549
+ }
550
+ function encodeId(value, details) {
551
+ const decoded = decodeId(value);
552
+ const index = setDetail(decoded.index, details.index);
553
+ const unitId = setDetail(decoded.unitId, details.unitId);
554
+ const eventDate = setDetail(decoded.eventDate, details.eventDate);
555
+ if (index === void 0 && unitId === void 0 && eventDate === void 0) return decoded.id;
556
+ return [
557
+ encodeValue(decoded.id),
558
+ index ?? "",
559
+ unitId === void 0 ? "" : encodeValue(unitId),
560
+ eventDate ?? ""
561
+ ].join(SEPARATOR);
562
+ }
563
+
315
564
  //#endregion
316
565
  //#region src/models/model.ts
317
566
  var ViewModel = class {
@@ -344,6 +593,51 @@ var ViewModel = class {
344
593
  }
345
594
  return results;
346
595
  }
596
+ projectEvent(event) {
597
+ if (!(event.start instanceof Date) || !(event.end instanceof Date) || event.end <= event.start) return [];
598
+ const full = {
599
+ ...event,
600
+ id: event.id ?? "$event-placeholder"
601
+ };
602
+ const projections = [];
603
+ for (const cached of this.cachedSections) {
604
+ const { section, primaryScale, secondaryScales } = cached;
605
+ if (section.filter && !section.filter(full)) continue;
606
+ const primaryAxis = this.getPrimaryAxis(section);
607
+ const secondaryConfig = primaryAxis === "x" ? section.yScale : section.xScale;
608
+ const primitives = [];
609
+ const segments = primaryScale.segmentEvent(full);
610
+ for (let i = 0; i < segments.length; i++) {
611
+ const segment = segments[i];
612
+ const chunk = {
613
+ id: segments.length > 1 || segment.sourceUnitId !== void 0 ? encodeId(full.id, {
614
+ index: segments.length > 1 ? i : void 0,
615
+ unitId: segment.sourceUnitId
616
+ }) : full.id,
617
+ event: full,
618
+ start: segment.start,
619
+ end: segment.end,
620
+ unitIndex: segment.unitIndex
621
+ };
622
+ const unitIdx = chunk.unitIndex ?? this.findUnitIndex(primaryScale, full);
623
+ const unit = primaryScale.units[unitIdx];
624
+ if (!unit) continue;
625
+ let secondaryScale = secondaryScales.get(unitIdx);
626
+ if (!secondaryScale) {
627
+ secondaryScale = createScale(secondaryConfig, primaryScale.getUnitStart(unitIdx) ?? this.startDate, this.fmt);
628
+ secondaryScales.set(unitIdx, secondaryScale);
629
+ }
630
+ const primitive = this.mapToPrimitive(chunk, unit, secondaryScale, primaryAxis);
631
+ if (primitive) primitives.push(primitive);
632
+ }
633
+ if (primitives.length) projections.push({
634
+ section: section.name,
635
+ mode: section.mode,
636
+ primitives
637
+ });
638
+ }
639
+ return projections;
640
+ }
347
641
  toPositionStart(sectionName, x, y, ev, snap) {
348
642
  return this.resolvePosition(sectionName, x, y, "start", ev, snap);
349
643
  }
@@ -357,56 +651,15 @@ var ViewModel = class {
357
651
  const primaryAxis = this.getPrimaryAxis(section);
358
652
  const primaryPos = primaryAxis === "x" ? x : y;
359
653
  const secondaryPos = primaryAxis === "x" ? y : x;
360
- const result = ev ? { ...ev } : {};
361
- const primaryVal = cached.primaryScale.positionToValue(primaryPos);
362
654
  const unitIdx = this.findUnitForPosition(cached.primaryScale, primaryPos);
363
- const unit = cached.primaryScale.units[unitIdx];
364
655
  let secScale = cached.secondaryScales.get(unitIdx);
365
656
  if (!secScale) {
366
- const secondaryConfig = primaryAxis === "x" ? section.yScale : section.xScale;
367
- const groupStartVal = cached.primaryScale.positionToValue(unit.position);
368
- secScale = createScale(secondaryConfig, groupStartVal instanceof Date ? groupStartVal : this.startDate);
657
+ secScale = createScale(primaryAxis === "x" ? section.yScale : section.xScale, cached.primaryScale.getUnitStart(unitIdx) ?? this.startDate, this.fmt);
369
658
  cached.secondaryScales.set(unitIdx, secScale);
370
659
  }
371
- const secondaryVal = secScale.positionToValue(secondaryPos);
372
- if (primaryVal instanceof Date && secondaryVal instanceof Date) {
373
- const secStepMs = secScale.stepMs || 3600 * 1e3;
374
- if (secStepMs >= DAY_MS) {
375
- const combined = new Date(secondaryVal);
376
- const src = ev?.[target];
377
- if (src instanceof Date) combined.setHours(src.getHours(), src.getMinutes(), src.getSeconds(), src.getMilliseconds());
378
- result[target] = combined;
379
- } else {
380
- const combined = new Date(primaryVal);
381
- combined.setHours(secondaryVal.getHours(), secondaryVal.getMinutes(), secondaryVal.getSeconds(), secondaryVal.getMilliseconds());
382
- const snapMs = secScale.snapStepMs ?? secStepMs;
383
- if (snap && snapMs !== false) {
384
- const ms = combined.getTime();
385
- const base = secScale.rangeStart?.getTime() || 0;
386
- const snapped = base + Math.round((ms - base) / snapMs) * snapMs;
387
- result[target] = new Date(snapped);
388
- } else result[target] = combined;
389
- }
390
- } else if (primaryVal instanceof Date) {
391
- if (snap) {
392
- const rawStep = cached.primaryScale.snapStepMs ?? cached.primaryScale.stepMs ?? DAY_MS;
393
- const base = cached.primaryScale.rangeStart?.getTime();
394
- if (rawStep !== false && base != null) {
395
- const snapped = base + Math.round((primaryVal.getTime() - base) / rawStep) * rawStep;
396
- result[target] = new Date(snapped);
397
- } else result[target] = primaryVal;
398
- } else result[target] = primaryVal;
399
- const src = ev?.[target];
400
- if (src instanceof Date) result[target].setHours(src.getHours(), src.getMinutes(), src.getSeconds(), src.getMilliseconds());
401
- if (typeof secondaryVal !== "object") {
402
- const acc = secScale.accessor;
403
- if (acc?.set) Object.assign(result, acc.set(result, secondaryVal));
404
- }
405
- } else if (secondaryVal instanceof Date) {
406
- result[target] = secondaryVal;
407
- const acc = cached.primaryScale.accessor;
408
- if (acc?.set) Object.assign(result, acc.set(result, primaryVal));
409
- }
660
+ let result = ev ? { ...ev } : {};
661
+ Object.assign(result, cached.primaryScale.applyPosition(primaryPos, target, result, snap));
662
+ Object.assign(result, secScale.applyPosition(secondaryPos, target, result, snap));
410
663
  return result;
411
664
  }
412
665
  processSection(section, events, doLayout) {
@@ -417,14 +670,27 @@ var ViewModel = class {
417
670
  const filtered = section.filter ? events.filter(section.filter) : events;
418
671
  const allChunks = [];
419
672
  for (const event of filtered) {
420
- const chunks = this.splitEvent(event, primaryScale, primaryConfig);
421
- allChunks.push(...chunks);
673
+ const segments = primaryScale.segmentEvent(event);
674
+ for (let i = 0; i < segments.length; i++) {
675
+ const segment = segments[i];
676
+ const contextual = segments.length > 1 || segment.sourceUnitId !== void 0;
677
+ allChunks.push({
678
+ id: contextual ? encodeId(event.id, {
679
+ index: segments.length > 1 ? i : void 0,
680
+ unitId: segment.sourceUnitId
681
+ }) : event.id,
682
+ event,
683
+ start: segment.start,
684
+ end: segment.end,
685
+ unitIndex: segment.unitIndex
686
+ });
687
+ }
422
688
  }
423
689
  const secondaryScales = /* @__PURE__ */ new Map();
424
690
  const allPrimitives = [];
425
691
  const unitGroups = /* @__PURE__ */ new Map();
426
692
  for (const chunk of allChunks) {
427
- const unitIdx = this.findUnitIndex(primaryScale, {
693
+ const unitIdx = chunk.unitIndex ?? this.findUnitIndex(primaryScale, {
428
694
  ...chunk.event,
429
695
  start: chunk.start,
430
696
  end: chunk.end
@@ -439,8 +705,7 @@ var ViewModel = class {
439
705
  }
440
706
  for (const [unitIdx, chunks] of unitGroups) {
441
707
  const unit = primaryScale.units[unitIdx];
442
- const groupStartVal = primaryScale.positionToValue(unit.position);
443
- const secScale = createScale(secondaryConfig, groupStartVal instanceof Date ? groupStartVal : this.startDate, this.fmt);
708
+ const secScale = createScale(secondaryConfig, primaryScale.getUnitStart(unitIdx) ?? this.startDate, this.fmt);
444
709
  secondaryScales.set(unitIdx, secScale);
445
710
  const primitives = [];
446
711
  for (const chunk of chunks) {
@@ -459,8 +724,7 @@ var ViewModel = class {
459
724
  } else allPrimitives.push(...primitives);
460
725
  }
461
726
  if (secondaryScales.size === 0) {
462
- const fallbackStart = primaryScale.units.length > 0 ? primaryScale.positionToValue(primaryScale.units[0].position) : this.startDate;
463
- const groupStart = fallbackStart instanceof Date ? fallbackStart : this.startDate;
727
+ const groupStart = primaryScale.getUnitStart(0) ?? this.startDate;
464
728
  secondaryScales.set(0, createScale(secondaryConfig, groupStart, this.fmt));
465
729
  }
466
730
  const xScale = primaryAxis === "x" ? primaryScale : secondaryScales.values().next().value;
@@ -506,41 +770,6 @@ var ViewModel = class {
506
770
  if (section.primaryScale) return section.primaryScale;
507
771
  return section.mode === "boxes" ? "x" : "y";
508
772
  }
509
- splitEvent(event, primaryScale, _config) {
510
- const units = primaryScale.units;
511
- if (units.length <= 1) return [{
512
- id: event.id,
513
- event,
514
- start: event.start,
515
- end: event.end
516
- }];
517
- const chunks = [];
518
- const boundaries = [];
519
- for (let i = 1; i < units.length; i++) {
520
- const d = primaryScale.positionToValue(units[i].position);
521
- if (d instanceof Date) boundaries.push(d);
522
- }
523
- let currentStart = event.start;
524
- for (const boundary of boundaries) {
525
- if (boundary <= currentStart) continue;
526
- if (boundary >= event.end) break;
527
- chunks.push({
528
- id: event.id,
529
- event,
530
- start: currentStart,
531
- end: boundary
532
- });
533
- currentStart = boundary;
534
- }
535
- chunks.push({
536
- id: event.id,
537
- event,
538
- start: currentStart,
539
- end: event.end
540
- });
541
- if (chunks.length > 1) for (let i = 0; i < chunks.length; i++) chunks[i].id = (typeof event.id === "string" ? ":" : "") + `${event.id}#${i}`;
542
- return chunks;
543
- }
544
773
  findUnitIndex(scale, event) {
545
774
  const units = scale.units;
546
775
  if (units.length === 0) return -1;
@@ -582,7 +811,6 @@ var ViewModel = class {
582
811
  };
583
812
  }
584
813
  };
585
- const DAY_MS = 1440 * 60 * 1e3;
586
814
  function deepMerge(target, source) {
587
815
  const result = { ...target };
588
816
  for (const key of Object.keys(source)) {
@@ -872,7 +1100,7 @@ function getMenuOptions() {
872
1100
  icon: "wxi-delete"
873
1101
  }];
874
1102
  }
875
- function getToolbarItems() {
1103
+ function getToolbarItems(config) {
876
1104
  return [
877
1105
  {
878
1106
  id: "nav",
@@ -890,11 +1118,12 @@ function getToolbarItems() {
890
1118
  { comp: "spacer" },
891
1119
  {
892
1120
  id: "modes",
893
- comp: "richselect"
1121
+ comp: "richselect-navigation"
894
1122
  },
895
1123
  {
896
1124
  id: "add-event",
897
- comp: "addEventButton"
1125
+ comp: "addEventButton",
1126
+ pinned: true
898
1127
  }
899
1128
  ];
900
1129
  }
@@ -1005,13 +1234,88 @@ function normalizeAllDayUpdate(updates, existing) {
1005
1234
  };
1006
1235
  }
1007
1236
 
1237
+ //#endregion
1238
+ //#region src/actions/delete-event.ts
1239
+ function cascadeDeleteExceptions(store, masterId) {
1240
+ const orphans = store.getState().events.getEvents().filter((e) => e.masterEventId === masterId);
1241
+ for (const orphan of orphans) store.in.exec("delete-event", {
1242
+ id: orphan.id,
1243
+ rawId: orphan.id,
1244
+ cascade: true
1245
+ });
1246
+ }
1247
+ function deleteEvent(store, params) {
1248
+ const { events, editorData } = store.getState();
1249
+ const existing = events.getEvent(params.id);
1250
+ events.removeEvent(params.id);
1251
+ const updates = { events };
1252
+ if (editorData?.id === params.id) updates.editorData = null;
1253
+ store.setState(updates);
1254
+ if (existing?.rrule) cascadeDeleteExceptions(store, params.id);
1255
+ }
1256
+
1008
1257
  //#endregion
1009
1258
  //#region src/actions/update-event.ts
1010
1259
  function updateEvent(store, action) {
1011
1260
  const { events } = store.getState();
1012
- const event = normalizeAllDayUpdate(action.event, events.getEvent(action.id));
1261
+ const existing = events.getEvent(action.id);
1262
+ const event = normalizeAllDayUpdate(action.event, existing);
1263
+ const originalDate = decodeId(action.rawId ?? action.id).eventDate;
1264
+ const wasRecurring = !!existing?.rrule;
1013
1265
  action.event = event;
1014
- if (events.updateEvent(action.id, event, action.mode, action.originalDate)) store.setState({ events });
1266
+ const updated = events.updateEvent(action.id, event, action.mode, originalDate);
1267
+ if (!updated) return;
1268
+ const selected = store.getState().editorData;
1269
+ store.setState({ events });
1270
+ if (originalDate && selected?.id === action.id) store.in.exec("select-event", {
1271
+ id: updated.id,
1272
+ rawId: updated.id
1273
+ });
1274
+ if (!originalDate && wasRecurring && !updated.rrule) cascadeDeleteExceptions(store, action.id);
1275
+ }
1276
+
1277
+ //#endregion
1278
+ //#region src/actions/move-event.ts
1279
+ function replaceAssignment(event, updates, sourceUnitId) {
1280
+ const result = { ...updates };
1281
+ for (const key of Object.keys(updates)) {
1282
+ const current = event[key];
1283
+ const destination = updates[key];
1284
+ if (!Array.isArray(current) || Array.isArray(destination)) continue;
1285
+ const index = current.findIndex((id) => id === sourceUnitId);
1286
+ if (index === -1) continue;
1287
+ const next = [...current];
1288
+ next[index] = destination;
1289
+ result[key] = [...new Set(next)];
1290
+ }
1291
+ return result;
1292
+ }
1293
+ function moveEvent(store, action) {
1294
+ const original = store.getState().events.getEvent(action.id);
1295
+ const sourceUnitId = decodeId(action.rawId ?? action.id).unitId;
1296
+ if (original && sourceUnitId !== void 0) action.event = replaceAssignment(original, action.event, sourceUnitId);
1297
+ updateEvent(store, action);
1298
+ }
1299
+
1300
+ //#endregion
1301
+ //#region src/helpers/editor.ts
1302
+ function withoutStorageFields(event) {
1303
+ const result = { ...event };
1304
+ delete result.duration;
1305
+ delete result.exdates;
1306
+ return result;
1307
+ }
1308
+ function getEditorEvent(events, event, mode = "series", originalDate, rawId = event.id, recurring = false) {
1309
+ let date = originalDate ?? null;
1310
+ const createEditorData = (values) => ({
1311
+ id: values.id,
1312
+ values,
1313
+ rawId,
1314
+ recurring,
1315
+ recurringMode: mode,
1316
+ recurringOriginalDate: date
1317
+ });
1318
+ return createEditorData(withoutStorageFields(event));
1015
1319
  }
1016
1320
 
1017
1321
  //#endregion
@@ -1053,8 +1357,9 @@ function addEvent(store, action) {
1053
1357
  const full = events.addEvent(filled);
1054
1358
  action.event = { ...full };
1055
1359
  action.id = full.id;
1360
+ action.rawId = full.id;
1056
1361
  const updates = { events };
1057
- if (action.edit) updates.editorData = { ...full };
1362
+ if (action.edit) updates.editorData = getEditorEvent(events, full, "series", null, full.id, !!store.meta.recurring);
1058
1363
  store.setState(updates);
1059
1364
  }
1060
1365
 
@@ -1066,18 +1371,21 @@ function selectEvent(store, params) {
1066
1371
  return;
1067
1372
  }
1068
1373
  const { events } = store.getState();
1374
+ const rawId = params.rawId ?? params.id;
1069
1375
  const event = events.getEvent(params.id);
1070
- if (event) store.setState({ editorData: { ...event } });
1376
+ if (event) {
1377
+ const mode = params.mode ?? (event.masterEventId != null ? "single" : "series");
1378
+ store.setState({ editorData: getEditorEvent(events, event, mode, decodeId(rawId).eventDate, rawId, !!store.meta.recurring) });
1379
+ }
1071
1380
  }
1072
1381
 
1073
1382
  //#endregion
1074
- //#region src/actions/delete-event.ts
1075
- function deleteEvent(store, params) {
1076
- const { events, editorData } = store.getState();
1077
- events.removeEvent(params.id);
1078
- const updates = { events };
1079
- if (editorData?.id === params.id) updates.editorData = null;
1080
- store.setState(updates);
1383
+ //#region src/actions/provide-data.ts
1384
+ function provideData(store, action) {
1385
+ const { events } = store.getState();
1386
+ if (action.reset) events.clear();
1387
+ for (const event of action.data.events) events.addEvent(event, !action.reset);
1388
+ store.setState({ events });
1081
1389
  }
1082
1390
 
1083
1391
  //#endregion
@@ -1087,29 +1395,41 @@ function init(inBus, store) {
1087
1395
  inBus.on("navigate-time", (params) => navigateTime(store, inBus, params));
1088
1396
  inBus.on("filter-events", (params) => filterEvents(store, params));
1089
1397
  inBus.on("update-event", (params) => updateEvent(store, params));
1398
+ inBus.on("move-event", (params) => moveEvent(store, params));
1090
1399
  inBus.on("add-event", (params) => addEvent(store, params));
1091
1400
  inBus.on("select-event", (params) => selectEvent(store, params));
1092
1401
  inBus.on("delete-event", (params) => deleteEvent(store, params));
1402
+ inBus.on("provide-data", (params) => provideData(store, params));
1093
1403
  }
1094
1404
 
1095
1405
  //#endregion
1096
1406
  //#region src/calendar_reactive.ts
1407
+ function sameRange(left, right) {
1408
+ return left.start.getTime() === right.start.getTime() && left.end.getTime() === right.end.getTime();
1409
+ }
1097
1410
  function reactive(store) {
1098
1411
  return [
1099
1412
  {
1100
1413
  in: ["currentDate", "currentView"],
1101
1414
  out: ["rangeLabel", "visibleDateRange"],
1102
1415
  exec: (ctx) => {
1103
- const { currentView, currentDate } = store.getState();
1416
+ const { currentView, currentDate, visibleDateRange } = store.getState();
1104
1417
  const view = store.getView(currentView);
1105
1418
  const [start, end] = view.setRange(currentDate);
1106
- store.setState({
1107
- rangeLabel: view.getRangeLabel(),
1108
- visibleDateRange: {
1109
- start,
1110
- end
1111
- }
1112
- }, ctx);
1419
+ const nextRange = {
1420
+ start,
1421
+ end
1422
+ };
1423
+ const rangeChanged = !sameRange(visibleDateRange, nextRange);
1424
+ const update = { rangeLabel: view.getRangeLabel() };
1425
+ if (rangeChanged) update.visibleDateRange = nextRange;
1426
+ store.setState(update, ctx);
1427
+ if (rangeChanged) store.in.exec("request-data", {
1428
+ startDate: start,
1429
+ endDate: end,
1430
+ date: currentDate,
1431
+ view: currentView
1432
+ });
1113
1433
  }
1114
1434
  },
1115
1435
  {
@@ -1149,36 +1469,41 @@ registerCalendarView("day", DayViewModel);
1149
1469
  registerCalendarView("month", MonthViewModel);
1150
1470
  var CalendarStore = class extends Store {
1151
1471
  in;
1472
+ meta = {};
1152
1473
  _router;
1153
1474
  _views = {};
1154
1475
  _weekStartDay;
1155
1476
  _dateFormat;
1156
1477
  constructor(w, options) {
1157
- options?.recurring;
1478
+ const recurring = options?.recurring ?? false;
1158
1479
  let EventsClass = EventsStore;
1159
- const defaultState = {
1480
+ super({
1481
+ writable: w,
1482
+ async: false
1483
+ });
1484
+ this.meta.recurring = recurring;
1485
+ this._router = new DataRouter(super.setState.bind(this), reactive(this), { events: (v) => {
1486
+ const events = new EventsClass(v);
1487
+ if (this._history) this._history.reset();
1488
+ return events;
1489
+ } });
1490
+ this._weekStartDay = options?.weekStart ?? 1;
1491
+ this._dateFormat = options?.dateFormat ?? (() => (d) => d.toLocaleDateString());
1492
+ super.setState({
1160
1493
  currentDate: /* @__PURE__ */ new Date(),
1161
- currentView: "",
1494
+ currentView: "week",
1162
1495
  rangeLabel: "",
1163
1496
  visibleDateRange: {
1164
1497
  start: /* @__PURE__ */ new Date(),
1165
1498
  end: /* @__PURE__ */ new Date()
1166
1499
  },
1167
- events: new EventsClass(),
1500
+ events: new EventsClass([]),
1168
1501
  viewData: {},
1169
1502
  filters: /* @__PURE__ */ new Map(),
1170
1503
  editorData: null,
1171
1504
  _view: new WeekViewModel()
1172
- };
1173
- super({
1174
- writable: w,
1175
- async: false
1176
1505
  });
1177
- this._weekStartDay = options?.weekStart ?? 1;
1178
- this._dateFormat = options?.dateFormat ?? (() => (d) => d.toLocaleDateString());
1179
1506
  this.configureViews();
1180
- super.setState(defaultState);
1181
- this._router = new DataRouter(super.setState.bind(this), reactive(this), { events: (v) => new EventsClass(v) });
1182
1507
  init(this.in = new EventBus(), this);
1183
1508
  }
1184
1509
  configureViews(views) {
@@ -1206,6 +1531,7 @@ var CalendarStore = class extends Store {
1206
1531
  init(state) {
1207
1532
  this._router.init({ ...state });
1208
1533
  }
1534
+ postInit() {}
1209
1535
  applyViewConfig(instance) {
1210
1536
  instance.weekStartDay = this._weekStartDay;
1211
1537
  instance.fmt = this._dateFormat;
@@ -1217,17 +1543,7 @@ var CalendarStore = class extends Store {
1217
1543
  return this.getState().events.getEvents(start, end);
1218
1544
  }
1219
1545
  getEvent(id) {
1220
- if (typeof id === "string") {
1221
- const isString = id.startsWith(":");
1222
- const start = isString ? 1 : 0;
1223
- let idx = id.indexOf("#", start);
1224
- if (idx === -1 && isString) idx = id.length;
1225
- if (idx !== -1) {
1226
- id = id.substring(start, idx);
1227
- if (!isString) id = parseInt(id);
1228
- }
1229
- }
1230
- return this.getState().events.getEvent(id);
1546
+ return this.getState().events.getEvent(decodeId(id).id);
1231
1547
  }
1232
1548
  getBrandmark() {
1233
1549
  return null;
@@ -1242,4 +1558,4 @@ var CalendarStore = class extends Store {
1242
1558
  const version = version$1;
1243
1559
 
1244
1560
  //#endregion
1245
- export { CalendarStore, DayViewModel, DiscreteScale, EventsStore, LinearScale, MonthViewModel, ViewModel, WeekViewModel, createScale, getMenuOptions, getToolbarItems, isMultiDay, layoutBars, layoutBoxes, registerCalendarView, version };
1561
+ export { CalendarStore, CombinedScale, DayViewModel, DiscreteScale, EventsStore, LinearScale, MonthViewModel, ViewModel, WeekViewModel, createScale, decodeId, encodeId, getMenuOptions, getToolbarItems, isMultiDay, layoutBars, layoutBoxes, registerCalendarView, version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svar-ui/calendar-store",
3
- "version": "2.6.0",
3
+ "version": "2.7.0",
4
4
  "description": "State and models for SVAR Calendar",
5
5
  "homepage": "https://svar.dev",
6
6
  "license": "MIT",
@@ -21,18 +21,18 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "@svar-ui/lib-dom": "0.12.1",
25
- "@svar-ui/lib-state": "1.9.6"
24
+ "@svar-ui/lib-dom": "0.14.0",
25
+ "@svar-ui/lib-state": "1.9.7"
26
26
  },
27
27
  "devDependencies": {
28
- "@svar-ui/lib-state": "1.9.6",
28
+ "@svar-ui/lib-state": "1.9.7",
29
29
  "@typescript/native-preview": "7.0.0-dev.20260316.1",
30
30
  "typescript": "^5.9.3",
31
31
  "vite-plus": "0.1.16"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "vp pack",
35
- "watch": "vp pack --watch",
35
+ "dev": "vp pack --watch",
36
36
  "test": "vp test",
37
37
  "check": "vp check"
38
38
  }