@tasknotes/calendar 0.1.4 → 0.1.5

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.5
4
+
5
+ ### Added
6
+
7
+ - Host-managed `CalendarItem.preview` placements for group drags. They use mirror styling, are non-interactive and hidden from assistive navigation, and remain visible beyond month/all-day event limits and timed stack limits. Multiple overflow previews stack separately.
8
+ - Supports the TaskNotes batch-scheduling workflow requested by @mnicolas94 in [Discussion 2435](https://github.com/callumalpass/tasknotes/discussions/2435), with the existing limitations clarified by @Thanatos9404.
9
+
10
+ ### Fixed
11
+
12
+ - The dragged anchor remains visible when moving into a timed cluster limited by `eventMaxStack`.
13
+
3
14
  ## 0.1.4
4
15
 
5
16
  ### Added
package/README.md CHANGED
@@ -41,6 +41,7 @@ The host persists changes; the calendar supplies optimistic updates and a
41
41
  - **Items** take `start`/`end` as `YYYY-MM-DD` (all-day) or `YYYY-MM-DDTHH:mm` (floating, read in the calendar's zone), ISO strings with an offset, or epoch ms. `end` is exclusive. Optional fields: `allDay`, `color`, `kind`, `lane` (`"plan"`/`"actual"`), `display: "background"`, `completed`, `dependsOn`, `rev` (cheap change signature), `data`.
42
42
  - **Updates** are incremental: `setItems` diffs against the previous set, and `upsertItems`/`removeItems` re-render only the days touched. Render hooks (`itemDidMount`, …) fire only for elements that actually change, but every callback's `item` is the object most recently passed in for that id, so `item.data` is never stale.
43
43
  - **Render hooks** (`itemContent`, `itemClassNames`, `itemDidMount`, `itemWillUnmount`, `listItemContent`) get `variant` (`"timed"`, `"bar"`, `"dot"`, `"indicator"`, `"list"`, `"timeline"`) and `timeText`, the time label in the calendar's locale and hour format (`"9am – 10:30am"` for timed, list and timeline items, the start for bars and dots, empty for all-day items).
44
+ - **Preview items**: set `preview: true` on a temporary item passed through `upsertItems` to show a host-managed group-drag placement. Previews have mirror styling, are hidden from assistive navigation, cannot be activated or edited, and remain visible beyond month/all-day event limits and timed stack limits. Multiple overflow previews stack without covering each other. `itemDidMount` reports `isMirror: true`. Restore the original item (or remove the preview ID) on drop, cancellation, source refresh and teardown; this flag does not persist or automatically commit a group.
44
45
  - **Drags** move the item itself with the pointer in every view (time grid, all-day row, month, timeline), for mouse and touch; the dragged item is never folded into "+N more".
45
46
  - **Changes** (`onItemDrop`, `onItemResize`, `onSelect`, `onExternalDrop`) report TaskNotes storage strings (`startStr`/`endStr`), `revert()`, and `source: "pointer" | "keyboard"`. Returning a rejected promise reverts.
46
47
  - **Clicks**: `onDateClick` reports `minute` (the slot's wall-clock minute, snapped to `slotDuration`; null for day cells and the all-day row), `allDay` and `startStr` (`"YYYY-MM-DD"` or `"YYYY-MM-DDTHH:mm"`), so a tap on a slot can prefill a time.
package/dist/calendar.css CHANGED
@@ -224,9 +224,10 @@
224
224
  opacity: 0.92;
225
225
  }
226
226
  .tnc-item.is-mirror {
227
+ z-index: 40 !important;
227
228
  opacity: 0.8;
228
229
  border-style: dashed !important;
229
- pointer-events: none;
230
+ pointer-events: none !important;
230
231
  }
231
232
 
232
233
  /* timed (time grid) */
@@ -1210,17 +1210,22 @@ var DayGridView = class {
1210
1210
  const n = w.days.length;
1211
1211
  const hidden = new Array(n).fill([]).map(() => []);
1212
1212
  w.pool.begin();
1213
+ const previewLevels = new Array(n).fill(0);
1213
1214
  let maxLevel = -1;
1214
1215
  for (const p of row.placements) {
1215
1216
  let level = p.level;
1216
1217
  let room = Infinity;
1217
1218
  for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visible(c));
1218
1219
  if (level >= room) {
1219
- if (p.item.id !== ctx.draggingId) {
1220
+ if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
1220
1221
  for (let c = p.startCol; c < p.endCol; c++) hidden[c].push(p.item);
1221
1222
  continue;
1222
1223
  }
1223
- level = Math.max(0, room - 1);
1224
+ if (p.item.item.preview) {
1225
+ level = 0;
1226
+ for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visible(c) + 1, previewLevels[c]);
1227
+ for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
1228
+ } else level = Math.max(0, room - 1);
1224
1229
  }
1225
1230
  maxLevel = Math.max(maxLevel, level);
1226
1231
  const single = !p.item.allDay && p.item.endDay - p.item.startDay <= 1;
@@ -2005,17 +2010,22 @@ var TimeGridView = class {
2005
2010
  const pool = this.allDayPool;
2006
2011
  pool.begin();
2007
2012
  const hidden = new Array(days.length).fill(0);
2013
+ const previewLevels = new Array(days.length).fill(0);
2008
2014
  let maxLevel = -1;
2009
2015
  for (const p of row.placements) {
2010
2016
  let level = p.level;
2011
2017
  let room = Infinity;
2012
2018
  for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visibleLevels(c));
2013
2019
  if (level >= room) {
2014
- if (p.item.id !== ctx.draggingId) {
2020
+ if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
2015
2021
  for (let c = p.startCol; c < p.endCol; c++) hidden[c]++;
2016
2022
  continue;
2017
2023
  }
2018
- level = Math.max(0, room - 1);
2024
+ if (p.item.item.preview) {
2025
+ level = 0;
2026
+ for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visibleLevels(c) + 1, previewLevels[c]);
2027
+ for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
2028
+ } else level = Math.max(0, room - 1);
2019
2029
  }
2020
2030
  maxLevel = Math.max(maxLevel, level);
2021
2031
  const el = pool.place(p.item.id, {
@@ -2103,7 +2113,8 @@ var TimeGridView = class {
2103
2113
  const hiddenByCluster = /* @__PURE__ */ new Map();
2104
2114
  pool.begin();
2105
2115
  for (const p of placements) {
2106
- if (withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
2116
+ const preview = p.item.item.preview || p.item.id === ctx.draggingId;
2117
+ if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
2107
2118
  const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, items: [] };
2108
2119
  rec.count++;
2109
2120
  rec.start = Math.min(rec.start, p.startMin);
@@ -2112,7 +2123,7 @@ var TimeGridView = class {
2112
2123
  hiddenByCluster.set(p.cluster, rec);
2113
2124
  continue;
2114
2125
  }
2115
- const cols = withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
2126
+ const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
2116
2127
  const span = Math.min(p.span, cols - p.col);
2117
2128
  const left = p.col / cols;
2118
2129
  let width = span / cols;
@@ -3283,7 +3294,7 @@ var Calendar = class {
3283
3294
  return out;
3284
3295
  }
3285
3296
  isEditable(n, what) {
3286
- if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
3297
+ if (n.id === EXTERNAL_ID || n.item.preview || this.opts.view === "year") return false;
3287
3298
  if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
3288
3299
  const it = n.item;
3289
3300
  const base = it.editable ?? this.opts.editable;
@@ -3309,6 +3320,7 @@ var Calendar = class {
3309
3320
  createItemEl(meta) {
3310
3321
  const { n, variant } = meta;
3311
3322
  const it = n.item;
3323
+ const preview = meta.mirror || it.preview === true;
3312
3324
  const el = h(this.doc, "div", "tnc-item");
3313
3325
  el.classList.add(`tnc-item--${variant}`);
3314
3326
  if (it.kind) el.classList.add(`tnc-k-${it.kind.replace(/[^\w-]/g, "-")}`);
@@ -3318,12 +3330,12 @@ var Calendar = class {
3318
3330
  if (!meta.isStart) el.classList.add("is-cont-start");
3319
3331
  if (!meta.isEnd) el.classList.add("is-cont-end");
3320
3332
  if (n.allDay) el.classList.add("is-all-day");
3321
- if (meta.mirror || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
3333
+ if (preview || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
3322
3334
  if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
3323
3335
  const indicator = variant === "indicator";
3324
- if (!meta.mirror && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
3336
+ if (!preview && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
3325
3337
  if (this.draggingId === n.id) el.classList.add("is-dragging");
3326
- if (this.selectedIds.has(n.id) && !meta.mirror) el.classList.add("is-selected");
3338
+ if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
3327
3339
  el.classList.add(...classList(it.className));
3328
3340
  const arg = this.itemArg(el, meta);
3329
3341
  el.classList.add(...classList(this.opts.itemClassNames?.(arg)));
@@ -3334,7 +3346,7 @@ var Calendar = class {
3334
3346
  if (it.backgroundColor) el.style.setProperty("--tnc-bg", it.backgroundColor);
3335
3347
  if (it.borderColor) el.style.setProperty("--tnc-border", it.borderColor);
3336
3348
  if (it.textColor) el.style.setProperty("--tnc-fg", it.textColor);
3337
- if (indicator) el.setAttribute("aria-hidden", "true");
3349
+ if (indicator || preview) el.setAttribute("aria-hidden", "true");
3338
3350
  else if (it.display !== "background") {
3339
3351
  el.tabIndex = 0;
3340
3352
  el.setAttribute("role", "button");
@@ -3351,7 +3363,7 @@ var Calendar = class {
3351
3363
  } else {
3352
3364
  el.append(this.defaultContent(meta));
3353
3365
  }
3354
- if (variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
3366
+ if (!preview && variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
3355
3367
  this.opts.itemDidMount?.(arg);
3356
3368
  return el;
3357
3369
  }
@@ -3360,7 +3372,7 @@ var Calendar = class {
3360
3372
  if (time) this.fillTime(time, meta.n, meta.variant, meta.day);
3361
3373
  el.classList.toggle("is-dragging", this.draggingId === meta.n.id);
3362
3374
  el.classList.toggle("is-conflict", this.conflicts.has(meta.n.id));
3363
- el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
3375
+ el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
3364
3376
  }
3365
3377
  selectedSet() {
3366
3378
  const s = this.opts.selected;
@@ -3370,7 +3382,7 @@ var Calendar = class {
3370
3382
  this.selectedIds = this.selectedSet();
3371
3383
  for (const el of this.viewHost.querySelectorAll(".tnc-item")) {
3372
3384
  const meta = this.meta.get(el);
3373
- if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
3385
+ if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
3374
3386
  }
3375
3387
  }
3376
3388
  destroyItemEl(el) {
@@ -3765,7 +3777,7 @@ var Calendar = class {
3765
3777
  day: meta.day,
3766
3778
  isStart: meta.isStart,
3767
3779
  isEnd: meta.isEnd,
3768
- isMirror: meta.mirror,
3780
+ isMirror: meta.mirror || meta.n.item.preview === true,
3769
3781
  variant: meta.variant,
3770
3782
  timeText: meta.n.allDay ? "" : end ? `${start} \u2013 ${end}` : start
3771
3783
  };
@@ -3910,7 +3922,7 @@ var Calendar = class {
3910
3922
  }
3911
3923
  itemElFrom(target) {
3912
3924
  const el = target?.closest?.(".tnc-item");
3913
- return el && this.root.contains(el) && this.meta.has(el) ? el : null;
3925
+ return el && this.root.contains(el) && this.meta.has(el) && !this.meta.get(el)?.n.item.preview ? el : null;
3914
3926
  }
3915
3927
  onDocPointerDown = (ev) => {
3916
3928
  const t = ev.target;
@@ -1118,17 +1118,22 @@ var DayGridView = class {
1118
1118
  const n = w.days.length;
1119
1119
  const hidden = new Array(n).fill([]).map(() => []);
1120
1120
  w.pool.begin();
1121
+ const previewLevels = new Array(n).fill(0);
1121
1122
  let maxLevel = -1;
1122
1123
  for (const p of row.placements) {
1123
1124
  let level = p.level;
1124
1125
  let room = Infinity;
1125
1126
  for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visible(c));
1126
1127
  if (level >= room) {
1127
- if (p.item.id !== ctx.draggingId) {
1128
+ if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
1128
1129
  for (let c = p.startCol; c < p.endCol; c++) hidden[c].push(p.item);
1129
1130
  continue;
1130
1131
  }
1131
- level = Math.max(0, room - 1);
1132
+ if (p.item.item.preview) {
1133
+ level = 0;
1134
+ for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visible(c) + 1, previewLevels[c]);
1135
+ for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
1136
+ } else level = Math.max(0, room - 1);
1132
1137
  }
1133
1138
  maxLevel = Math.max(maxLevel, level);
1134
1139
  const single = !p.item.allDay && p.item.endDay - p.item.startDay <= 1;
@@ -1913,17 +1918,22 @@ var TimeGridView = class {
1913
1918
  const pool = this.allDayPool;
1914
1919
  pool.begin();
1915
1920
  const hidden = new Array(days.length).fill(0);
1921
+ const previewLevels = new Array(days.length).fill(0);
1916
1922
  let maxLevel = -1;
1917
1923
  for (const p of row.placements) {
1918
1924
  let level = p.level;
1919
1925
  let room = Infinity;
1920
1926
  for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visibleLevels(c));
1921
1927
  if (level >= room) {
1922
- if (p.item.id !== ctx.draggingId) {
1928
+ if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
1923
1929
  for (let c = p.startCol; c < p.endCol; c++) hidden[c]++;
1924
1930
  continue;
1925
1931
  }
1926
- level = Math.max(0, room - 1);
1932
+ if (p.item.item.preview) {
1933
+ level = 0;
1934
+ for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visibleLevels(c) + 1, previewLevels[c]);
1935
+ for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
1936
+ } else level = Math.max(0, room - 1);
1927
1937
  }
1928
1938
  maxLevel = Math.max(maxLevel, level);
1929
1939
  const el = pool.place(p.item.id, {
@@ -2011,7 +2021,8 @@ var TimeGridView = class {
2011
2021
  const hiddenByCluster = /* @__PURE__ */ new Map();
2012
2022
  pool.begin();
2013
2023
  for (const p of placements) {
2014
- if (withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
2024
+ const preview = p.item.item.preview || p.item.id === ctx.draggingId;
2025
+ if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
2015
2026
  const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, items: [] };
2016
2027
  rec.count++;
2017
2028
  rec.start = Math.min(rec.start, p.startMin);
@@ -2020,7 +2031,7 @@ var TimeGridView = class {
2020
2031
  hiddenByCluster.set(p.cluster, rec);
2021
2032
  continue;
2022
2033
  }
2023
- const cols = withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
2034
+ const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
2024
2035
  const span = Math.min(p.span, cols - p.col);
2025
2036
  const left = p.col / cols;
2026
2037
  let width = span / cols;
@@ -3191,7 +3202,7 @@ var Calendar = class {
3191
3202
  return out;
3192
3203
  }
3193
3204
  isEditable(n, what) {
3194
- if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
3205
+ if (n.id === EXTERNAL_ID || n.item.preview || this.opts.view === "year") return false;
3195
3206
  if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
3196
3207
  const it = n.item;
3197
3208
  const base = it.editable ?? this.opts.editable;
@@ -3217,6 +3228,7 @@ var Calendar = class {
3217
3228
  createItemEl(meta) {
3218
3229
  const { n, variant } = meta;
3219
3230
  const it = n.item;
3231
+ const preview = meta.mirror || it.preview === true;
3220
3232
  const el = h(this.doc, "div", "tnc-item");
3221
3233
  el.classList.add(`tnc-item--${variant}`);
3222
3234
  if (it.kind) el.classList.add(`tnc-k-${it.kind.replace(/[^\w-]/g, "-")}`);
@@ -3226,12 +3238,12 @@ var Calendar = class {
3226
3238
  if (!meta.isStart) el.classList.add("is-cont-start");
3227
3239
  if (!meta.isEnd) el.classList.add("is-cont-end");
3228
3240
  if (n.allDay) el.classList.add("is-all-day");
3229
- if (meta.mirror || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
3241
+ if (preview || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
3230
3242
  if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
3231
3243
  const indicator = variant === "indicator";
3232
- if (!meta.mirror && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
3244
+ if (!preview && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
3233
3245
  if (this.draggingId === n.id) el.classList.add("is-dragging");
3234
- if (this.selectedIds.has(n.id) && !meta.mirror) el.classList.add("is-selected");
3246
+ if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
3235
3247
  el.classList.add(...classList(it.className));
3236
3248
  const arg = this.itemArg(el, meta);
3237
3249
  el.classList.add(...classList(this.opts.itemClassNames?.(arg)));
@@ -3242,7 +3254,7 @@ var Calendar = class {
3242
3254
  if (it.backgroundColor) el.style.setProperty("--tnc-bg", it.backgroundColor);
3243
3255
  if (it.borderColor) el.style.setProperty("--tnc-border", it.borderColor);
3244
3256
  if (it.textColor) el.style.setProperty("--tnc-fg", it.textColor);
3245
- if (indicator) el.setAttribute("aria-hidden", "true");
3257
+ if (indicator || preview) el.setAttribute("aria-hidden", "true");
3246
3258
  else if (it.display !== "background") {
3247
3259
  el.tabIndex = 0;
3248
3260
  el.setAttribute("role", "button");
@@ -3259,7 +3271,7 @@ var Calendar = class {
3259
3271
  } else {
3260
3272
  el.append(this.defaultContent(meta));
3261
3273
  }
3262
- if (variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
3274
+ if (!preview && variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
3263
3275
  this.opts.itemDidMount?.(arg);
3264
3276
  return el;
3265
3277
  }
@@ -3268,7 +3280,7 @@ var Calendar = class {
3268
3280
  if (time) this.fillTime(time, meta.n, meta.variant, meta.day);
3269
3281
  el.classList.toggle("is-dragging", this.draggingId === meta.n.id);
3270
3282
  el.classList.toggle("is-conflict", this.conflicts.has(meta.n.id));
3271
- el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
3283
+ el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
3272
3284
  }
3273
3285
  selectedSet() {
3274
3286
  const s = this.opts.selected;
@@ -3278,7 +3290,7 @@ var Calendar = class {
3278
3290
  this.selectedIds = this.selectedSet();
3279
3291
  for (const el of this.viewHost.querySelectorAll(".tnc-item")) {
3280
3292
  const meta = this.meta.get(el);
3281
- if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
3293
+ if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
3282
3294
  }
3283
3295
  }
3284
3296
  destroyItemEl(el) {
@@ -3673,7 +3685,7 @@ var Calendar = class {
3673
3685
  day: meta.day,
3674
3686
  isStart: meta.isStart,
3675
3687
  isEnd: meta.isEnd,
3676
- isMirror: meta.mirror,
3688
+ isMirror: meta.mirror || meta.n.item.preview === true,
3677
3689
  variant: meta.variant,
3678
3690
  timeText: meta.n.allDay ? "" : end ? `${start} \u2013 ${end}` : start
3679
3691
  };
@@ -3818,7 +3830,7 @@ var Calendar = class {
3818
3830
  }
3819
3831
  itemElFrom(target) {
3820
3832
  const el = target?.closest?.(".tnc-item");
3821
- return el && this.root.contains(el) && this.meta.has(el) ? el : null;
3833
+ return el && this.root.contains(el) && this.meta.has(el) && !this.meta.get(el)?.n.item.preview ? el : null;
3822
3834
  }
3823
3835
  onDocPointerDown = (ev) => {
3824
3836
  const t = ev.target;
@@ -512,17 +512,22 @@ var DayGridView = class {
512
512
  const n = w.days.length;
513
513
  const hidden = new Array(n).fill([]).map(() => []);
514
514
  w.pool.begin();
515
+ const previewLevels = new Array(n).fill(0);
515
516
  let maxLevel = -1;
516
517
  for (const p of row.placements) {
517
518
  let level = p.level;
518
519
  let room = Infinity;
519
520
  for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visible(c));
520
521
  if (level >= room) {
521
- if (p.item.id !== ctx.draggingId) {
522
+ if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
522
523
  for (let c = p.startCol; c < p.endCol; c++) hidden[c].push(p.item);
523
524
  continue;
524
525
  }
525
- level = Math.max(0, room - 1);
526
+ if (p.item.item.preview) {
527
+ level = 0;
528
+ for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visible(c) + 1, previewLevels[c]);
529
+ for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
530
+ } else level = Math.max(0, room - 1);
526
531
  }
527
532
  maxLevel = Math.max(maxLevel, level);
528
533
  const single = !p.item.allDay && p.item.endDay - p.item.startDay <= 1;
@@ -1307,17 +1312,22 @@ var TimeGridView = class {
1307
1312
  const pool = this.allDayPool;
1308
1313
  pool.begin();
1309
1314
  const hidden = new Array(days.length).fill(0);
1315
+ const previewLevels = new Array(days.length).fill(0);
1310
1316
  let maxLevel = -1;
1311
1317
  for (const p of row.placements) {
1312
1318
  let level = p.level;
1313
1319
  let room = Infinity;
1314
1320
  for (let c = p.startCol; c < p.endCol; c++) room = Math.min(room, visibleLevels(c));
1315
1321
  if (level >= room) {
1316
- if (p.item.id !== ctx.draggingId) {
1322
+ if (!p.item.item.preview && p.item.id !== ctx.draggingId) {
1317
1323
  for (let c = p.startCol; c < p.endCol; c++) hidden[c]++;
1318
1324
  continue;
1319
1325
  }
1320
- level = Math.max(0, room - 1);
1326
+ if (p.item.item.preview) {
1327
+ level = 0;
1328
+ for (let c = p.startCol; c < p.endCol; c++) level = Math.max(level, visibleLevels(c) + 1, previewLevels[c]);
1329
+ for (let c = p.startCol; c < p.endCol; c++) previewLevels[c] = level + 1;
1330
+ } else level = Math.max(0, room - 1);
1321
1331
  }
1322
1332
  maxLevel = Math.max(maxLevel, level);
1323
1333
  const el = pool.place(p.item.id, {
@@ -1405,7 +1415,8 @@ var TimeGridView = class {
1405
1415
  const hiddenByCluster = /* @__PURE__ */ new Map();
1406
1416
  pool.begin();
1407
1417
  for (const p of placements) {
1408
- if (withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
1418
+ const preview = p.item.item.preview || p.item.id === ctx.draggingId;
1419
+ if (!preview && withMore && maxStack > 0 && p.cols > maxStack && p.col >= maxStack - 1) {
1409
1420
  const rec = hiddenByCluster.get(p.cluster) ?? { count: 0, start: p.startMin, end: p.endMin, items: [] };
1410
1421
  rec.count++;
1411
1422
  rec.start = Math.min(rec.start, p.startMin);
@@ -1414,7 +1425,7 @@ var TimeGridView = class {
1414
1425
  hiddenByCluster.set(p.cluster, rec);
1415
1426
  continue;
1416
1427
  }
1417
- const cols = withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
1428
+ const cols = !preview && withMore && maxStack > 0 ? Math.min(p.cols, maxStack) : p.cols;
1418
1429
  const span = Math.min(p.span, cols - p.col);
1419
1430
  const left = p.col / cols;
1420
1431
  let width = span / cols;
@@ -2585,7 +2596,7 @@ var Calendar = class {
2585
2596
  return out;
2586
2597
  }
2587
2598
  isEditable(n, what) {
2588
- if (n.id === EXTERNAL_ID || this.opts.view === "year") return false;
2599
+ if (n.id === EXTERNAL_ID || n.item.preview || this.opts.view === "year") return false;
2589
2600
  if (this.opts.view === "list" && (!this.opts.listAgenda || what === "duration")) return false;
2590
2601
  const it = n.item;
2591
2602
  const base = it.editable ?? this.opts.editable;
@@ -2611,6 +2622,7 @@ var Calendar = class {
2611
2622
  createItemEl(meta) {
2612
2623
  const { n, variant } = meta;
2613
2624
  const it = n.item;
2625
+ const preview = meta.mirror || it.preview === true;
2614
2626
  const el = h(this.doc, "div", "tnc-item");
2615
2627
  el.classList.add(`tnc-item--${variant}`);
2616
2628
  if (it.kind) el.classList.add(`tnc-k-${it.kind.replace(/[^\w-]/g, "-")}`);
@@ -2620,12 +2632,12 @@ var Calendar = class {
2620
2632
  if (!meta.isStart) el.classList.add("is-cont-start");
2621
2633
  if (!meta.isEnd) el.classList.add("is-cont-end");
2622
2634
  if (n.allDay) el.classList.add("is-all-day");
2623
- if (meta.mirror || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
2635
+ if (preview || n.id === EXTERNAL_ID) el.classList.add("is-mirror");
2624
2636
  if (this.conflicts.has(n.id)) el.classList.add("is-conflict");
2625
2637
  const indicator = variant === "indicator";
2626
- if (!meta.mirror && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
2638
+ if (!preview && !indicator && (this.isEditable(n, "start") || this.isEditable(n, "duration"))) el.classList.add("is-draggable");
2627
2639
  if (this.draggingId === n.id) el.classList.add("is-dragging");
2628
- if (this.selectedIds.has(n.id) && !meta.mirror) el.classList.add("is-selected");
2640
+ if (this.selectedIds.has(n.id) && !preview) el.classList.add("is-selected");
2629
2641
  el.classList.add(...classList(it.className));
2630
2642
  const arg = this.itemArg(el, meta);
2631
2643
  el.classList.add(...classList(this.opts.itemClassNames?.(arg)));
@@ -2636,7 +2648,7 @@ var Calendar = class {
2636
2648
  if (it.backgroundColor) el.style.setProperty("--tnc-bg", it.backgroundColor);
2637
2649
  if (it.borderColor) el.style.setProperty("--tnc-border", it.borderColor);
2638
2650
  if (it.textColor) el.style.setProperty("--tnc-fg", it.textColor);
2639
- if (indicator) el.setAttribute("aria-hidden", "true");
2651
+ if (indicator || preview) el.setAttribute("aria-hidden", "true");
2640
2652
  else if (it.display !== "background") {
2641
2653
  el.tabIndex = 0;
2642
2654
  el.setAttribute("role", "button");
@@ -2653,7 +2665,7 @@ var Calendar = class {
2653
2665
  } else {
2654
2666
  el.append(this.defaultContent(meta));
2655
2667
  }
2656
- if (variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
2668
+ if (!preview && variant !== "list" && !indicator && it.display !== "background") this.addHandles(el, meta);
2657
2669
  this.opts.itemDidMount?.(arg);
2658
2670
  return el;
2659
2671
  }
@@ -2662,7 +2674,7 @@ var Calendar = class {
2662
2674
  if (time) this.fillTime(time, meta.n, meta.variant, meta.day);
2663
2675
  el.classList.toggle("is-dragging", this.draggingId === meta.n.id);
2664
2676
  el.classList.toggle("is-conflict", this.conflicts.has(meta.n.id));
2665
- el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
2677
+ el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
2666
2678
  }
2667
2679
  selectedSet() {
2668
2680
  const s = this.opts.selected;
@@ -2672,7 +2684,7 @@ var Calendar = class {
2672
2684
  this.selectedIds = this.selectedSet();
2673
2685
  for (const el of this.viewHost.querySelectorAll(".tnc-item")) {
2674
2686
  const meta = this.meta.get(el);
2675
- if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror);
2687
+ if (meta) el.classList.toggle("is-selected", this.selectedIds.has(meta.n.id) && !meta.mirror && !meta.n.item.preview);
2676
2688
  }
2677
2689
  }
2678
2690
  destroyItemEl(el) {
@@ -3067,7 +3079,7 @@ var Calendar = class {
3067
3079
  day: meta.day,
3068
3080
  isStart: meta.isStart,
3069
3081
  isEnd: meta.isEnd,
3070
- isMirror: meta.mirror,
3082
+ isMirror: meta.mirror || meta.n.item.preview === true,
3071
3083
  variant: meta.variant,
3072
3084
  timeText: meta.n.allDay ? "" : end ? `${start} \u2013 ${end}` : start
3073
3085
  };
@@ -3212,7 +3224,7 @@ var Calendar = class {
3212
3224
  }
3213
3225
  itemElFrom(target) {
3214
3226
  const el = target?.closest?.(".tnc-item");
3215
- return el && this.root.contains(el) && this.meta.has(el) ? el : null;
3227
+ return el && this.root.contains(el) && this.meta.has(el) && !this.meta.get(el)?.n.item.preview ? el : null;
3216
3228
  }
3217
3229
  onDocPointerDown = (ev) => {
3218
3230
  const t = ev.target;
package/dist/esm/index.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  TIMELINE_UNIT_DAYS,
8
8
  defaultOptions,
9
9
  localeFirstDay
10
- } from "./chunk-26DEUGQH.js";
10
+ } from "./chunk-25TY2UVR.js";
11
11
  import {
12
12
  DAY_MS,
13
13
  ItemStore,
package/dist/esm/react.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Calendar
3
- } from "./chunk-26DEUGQH.js";
3
+ } from "./chunk-25TY2UVR.js";
4
4
  import "./chunk-DVMZ5OXN.js";
5
5
 
6
6
  // src/react/index.tsx
@@ -26,6 +26,8 @@ export interface CalendarItem<D = unknown> {
26
26
  borderColor?: string;
27
27
  textColor?: string;
28
28
  className?: string | string[];
29
+ /** Host-managed temporary placement: inert, mirror-styled and never hidden by event limits. */
30
+ preview?: boolean;
29
31
  /** Overrides the calendar-wide `editable` option. */
30
32
  editable?: boolean;
31
33
  startEditable?: boolean;
@@ -26,6 +26,8 @@ export interface CalendarItem<D = unknown> {
26
26
  borderColor?: string;
27
27
  textColor?: string;
28
28
  className?: string | string[];
29
+ /** Host-managed temporary placement: inert, mirror-styled and never hidden by event limits. */
30
+ preview?: boolean;
29
31
  /** Overrides the calendar-wide `editable` option. */
30
32
  editable?: boolean;
31
33
  startEditable?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tasknotes/calendar",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Task-native calendar engine for TaskNotes: incremental layout, time grid, month, list, year and timeline views.",
5
5
  "license": "MIT",
6
6
  "type": "module",