@nomideusz/svelte-calendar 0.7.4 → 0.8.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.
Files changed (36) hide show
  1. package/README.md +19 -4
  2. package/dist/calendar/Calendar.svelte +364 -497
  3. package/dist/calendar/Calendar.svelte.d.ts +6 -2
  4. package/dist/core/clock.svelte.js +12 -5
  5. package/dist/core/locale.d.ts +4 -0
  6. package/dist/core/locale.js +4 -0
  7. package/dist/engine/view-state.svelte.d.ts +2 -2
  8. package/dist/engine/view-state.svelte.js +20 -0
  9. package/dist/headless/create-calendar.svelte.js +18 -2
  10. package/dist/headless/types.d.ts +6 -6
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +3 -0
  13. package/dist/primitives/DayHeader.svelte +9 -25
  14. package/dist/primitives/EmptySlot.svelte +15 -31
  15. package/dist/primitives/EventBlock.svelte +33 -61
  16. package/dist/primitives/NowIndicator.svelte +16 -42
  17. package/dist/primitives/TimeGutter.svelte +9 -23
  18. package/dist/views/agenda/Agenda.svelte +3 -10
  19. package/dist/views/agenda/AgendaDay.svelte +195 -167
  20. package/dist/views/agenda/AgendaWeek.svelte +141 -195
  21. package/dist/views/mobile/Mobile.svelte +3 -10
  22. package/dist/views/mobile/MobileDay.svelte +422 -266
  23. package/dist/views/mobile/MobileWeek.svelte +131 -186
  24. package/dist/views/month/MonthGrid.svelte +334 -0
  25. package/dist/views/month/MonthGrid.svelte.d.ts +15 -0
  26. package/dist/views/planner/Planner.svelte +3 -10
  27. package/dist/views/planner/PlannerDay.svelte +645 -549
  28. package/dist/views/planner/PlannerWeek.svelte +299 -412
  29. package/dist/views/shared/EventContent.svelte +16 -0
  30. package/dist/views/shared/EventContent.svelte.d.ts +9 -0
  31. package/dist/views/shared/context.svelte.d.ts +1 -0
  32. package/dist/views/shared/context.svelte.js +1 -0
  33. package/dist/widget/CalendarWidget.svelte +79 -116
  34. package/package.json +3 -2
  35. package/widget/svelte-calendar.css +329 -13
  36. package/widget/widget.js +1945 -1031
@@ -1,141 +1,95 @@
1
- <script lang="ts">
2
- /**
3
- * AgendaDay single-day agenda view.
4
- *
5
- * Today ("The Queue"):
6
- * 3-column layout: Done | Now | Up next (hero).
7
- * Answers: "What's coming up next?"
8
- *
9
- * Past day ("The Log"):
10
- * Quiet chronological record of completed events.
11
- *
12
- * Future day ("The Plan"):
13
- * Clean numbered schedule list.
14
- */
15
- import { createClock } from '../../core/clock.svelte.js';
16
- import type { TimelineEvent } from '../../core/types.js';
17
- import { sod, DAY_MS, isAllDay, isMultiDay } from '../../core/time.js';
18
- import { getLabels } from '../../core/locale.js';
19
- import { useCalendarContext } from '../shared/context.svelte.js';
20
- import { fmtTime, duration, timeUntilMs, progress, groupIntoSlots } from '../shared/format.js';
21
- import type { TimeSlot } from '../shared/format.js';
22
-
23
- const L = $derived(getLabels());
24
- const ctx = useCalendarContext();
25
-
26
- interface Props {
27
- locale?: string;
28
- height?: number;
29
- events?: TimelineEvent[];
30
- style?: string;
31
- focusDate?: Date;
32
- oneventclick?: (event: TimelineEvent) => void;
33
- selectedEventId?: string | null;
34
- [key: string]: unknown;
35
- }
36
-
37
- let {
38
- locale,
39
- height,
40
- events = [],
41
- style = '',
42
- focusDate,
43
- oneventclick,
44
- selectedEventId = null,
45
- }: Props = $props();
46
-
47
- const clock = createClock();
48
- const viewState = $derived(ctx.viewState);
49
- const equalDays = $derived(ctx.equalDays);
50
- const isMobile = $derived(ctx.isMobile);
51
- const autoHeight = $derived(ctx.autoHeight);
52
- const compact = $derived(ctx.compact);
53
- const oneventhover = $derived(ctx.oneventhover);
54
- const disabledSet = $derived(ctx.disabledSet);
55
-
56
- // ── Swipe navigation (mobile) ──────────────────────
57
- let swipeStartX = 0;
58
- let swipeStartY = 0;
59
- const SWIPE_THRESHOLD = 50;
60
-
61
- function onPointerDown(e: PointerEvent) {
62
- if (!isMobile) return;
63
- swipeStartX = e.clientX;
64
- swipeStartY = e.clientY;
65
- }
66
-
67
- function onPointerUp(e: PointerEvent) {
68
- if (!isMobile) return;
69
- const dx = e.clientX - swipeStartX;
70
- const dy = e.clientY - swipeStartY;
71
- if (Math.abs(dx) > SWIPE_THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.4) {
72
- if (dx > 0) viewState?.prev();
73
- else viewState?.next();
74
- }
75
- }
76
-
77
- // ── Format helpers (delegated to shared/format.ts) ──
78
- const fmt = (d: Date) => fmtTime(d, locale);
79
- const eta = (ms: number) => timeUntilMs(ms, clock.tick);
80
- const prog = (ev: TimelineEvent) => progress(ev, clock.tick);
81
-
82
- // ── Event handlers ──────────────────────────────────
83
- function handleClick(ev: TimelineEvent): void {
84
- oneventclick?.(ev);
85
- }
86
-
87
- function handleKeydown(e: KeyboardEvent, ev: TimelineEvent): void {
88
- if (e.key === 'Enter' || e.key === ' ') {
89
- e.preventDefault();
90
- oneventclick?.(ev);
91
- }
92
- }
93
-
94
- // ── Day derivations ─────────────────────────────────
95
- const dayMs = $derived(focusDate ? sod(focusDate.getTime()) : clock.today);
96
- const dayEnd = $derived(dayMs + DAY_MS);
97
- const isToday = $derived(dayMs === clock.today);
98
- const isPastDay = $derived(equalDays ? false : dayMs < clock.today);
99
-
100
- /** All events for this day, sorted chronologically */
101
- const dayEvents = $derived.by((): TimelineEvent[] => {
102
- return events
103
- .filter((ev) => ev.start.getTime() < dayEnd && ev.end.getTime() > dayMs)
104
- .sort((a, b) => a.start.getTime() - b.start.getTime());
105
- });
106
-
107
- /** All-day / multi-day events shown in a separate strip */
108
- const allDayBanner = $derived(dayEvents.filter((ev) => isAllDay(ev) || isMultiDay(ev)));
109
-
110
- /** Timed events (non-all-day) for normal slot rendering */
111
- const timedDayEvents = $derived(dayEvents.filter((ev) => !isAllDay(ev) && !isMultiDay(ev)));
112
-
113
- const dayCat = $derived.by(() => {
114
- const now = clock.tick;
115
- const past: TimelineEvent[] = [];
116
- const current: TimelineEvent[] = [];
117
- const upcoming: TimelineEvent[] = [];
118
- for (const ev of timedDayEvents) {
119
- const s = ev.start.getTime();
120
- const e = ev.end.getTime();
121
- if (e <= now) past.push(ev);
122
- else if (s <= now && e > now) current.push(ev);
123
- else upcoming.push(ev);
124
- }
125
- return { past, current, upcomingSlots: groupIntoSlots(upcoming), totalUp: upcoming.length };
126
- });
127
-
128
- /** Flat list of next upcoming events (max 5) for the "Up next" column */
129
- const upcomingNext = $derived.by((): TimelineEvent[] => {
130
- const all: TimelineEvent[] = [];
131
- for (const slot of dayCat.upcomingSlots) {
132
- for (const ev of slot.events) {
133
- all.push(ev);
134
- if (all.length >= 5) return all;
135
- }
136
- }
137
- return all;
138
- });
1
+ <script lang="ts">import { createClock } from "../../core/clock.svelte.js";
2
+ import { sod, DAY_MS, isAllDay, isMultiDay } from "../../core/time.js";
3
+ import { getLabels } from "../../core/locale.js";
4
+ import { useCalendarContext } from "../shared/context.svelte.js";
5
+ import EventContent from "../shared/EventContent.svelte";
6
+ import { fmtTime, duration, timeUntilMs, progress, groupIntoSlots } from "../shared/format.js";
7
+ const L = $derived(getLabels());
8
+ const ctx = useCalendarContext();
9
+ const emptySnippet = $derived(ctx.emptySnippet);
10
+ let {
11
+ locale,
12
+ height,
13
+ events = [],
14
+ style = "",
15
+ focusDate,
16
+ oneventclick,
17
+ selectedEventId = null
18
+ } = $props();
19
+ const clock = createClock();
20
+ const viewState = $derived(ctx.viewState);
21
+ const equalDays = $derived(ctx.equalDays);
22
+ const isMobile = $derived(ctx.isMobile);
23
+ const autoHeight = $derived(ctx.autoHeight);
24
+ const compact = $derived(ctx.compact);
25
+ const oneventhover = $derived(ctx.oneventhover);
26
+ const disabledSet = $derived(ctx.disabledSet);
27
+ let swipeStartX = 0;
28
+ let swipeStartY = 0;
29
+ const SWIPE_THRESHOLD = 50;
30
+ function onPointerDown(e) {
31
+ if (!isMobile) return;
32
+ swipeStartX = e.clientX;
33
+ swipeStartY = e.clientY;
34
+ }
35
+ function onPointerUp(e) {
36
+ if (!isMobile) return;
37
+ const dx = e.clientX - swipeStartX;
38
+ const dy = e.clientY - swipeStartY;
39
+ if (Math.abs(dx) > SWIPE_THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.4) {
40
+ if (dx > 0) viewState?.prev();
41
+ else viewState?.next();
42
+ }
43
+ }
44
+ const fmt = (d) => fmtTime(d, locale);
45
+ const eta = (ms) => timeUntilMs(ms, clock.tick);
46
+ const prog = (ev) => progress(ev, clock.tick);
47
+ function handleClick(ev) {
48
+ oneventclick?.(ev);
49
+ }
50
+ function handleKeydown(e, ev) {
51
+ if (e.key === "Enter" || e.key === " ") {
52
+ e.preventDefault();
53
+ oneventclick?.(ev);
54
+ }
55
+ }
56
+ const dayMs = $derived(focusDate ? sod(focusDate.getTime()) : clock.today);
57
+ const dayEnd = $derived(dayMs + DAY_MS);
58
+ const isToday = $derived(dayMs === clock.today);
59
+ const isPastDay = $derived(equalDays ? false : dayMs < clock.today);
60
+ const dayEvents = $derived.by(() => {
61
+ return events.filter((ev) => ev.start.getTime() < dayEnd && ev.end.getTime() > dayMs).sort((a, b) => a.start.getTime() - b.start.getTime());
62
+ });
63
+ const allDayBanner = $derived(dayEvents.filter((ev) => isAllDay(ev) || isMultiDay(ev)));
64
+ const timedDayEvents = $derived(dayEvents.filter((ev) => !isAllDay(ev) && !isMultiDay(ev)));
65
+ const dayCat = $derived.by(() => {
66
+ const now = clock.tick;
67
+ const past = [];
68
+ const current = [];
69
+ const upcoming = [];
70
+ for (const ev of timedDayEvents) {
71
+ const s = ev.start.getTime();
72
+ const e = ev.end.getTime();
73
+ if (e <= now) past.push(ev);
74
+ else if (s <= now && e > now) current.push(ev);
75
+ else upcoming.push(ev);
76
+ }
77
+ return { past, current, upcomingSlots: groupIntoSlots(upcoming), totalUp: upcoming.length };
78
+ });
79
+ const upcomingNext = $derived.by(() => {
80
+ const all = [];
81
+ for (const slot of dayCat.upcomingSlots) {
82
+ for (const ev of slot.events) all.push(ev);
83
+ }
84
+ return all;
85
+ });
86
+ const UPCOMING_CARDS = 4;
87
+ const DONE_VISIBLE = 3;
88
+ let showAllDone = $state(false);
89
+ const visibleDone = $derived(
90
+ showAllDone ? dayCat.past : dayCat.past.slice(-DONE_VISIBLE)
91
+ );
92
+ const hiddenDoneCount = $derived(showAllDone ? 0 : Math.max(0, dayCat.past.length - DONE_VISIBLE));
139
93
  </script>
140
94
 
141
95
  <!-- svelte-ignore a11y_no_static_element_interactions -->
@@ -179,7 +133,9 @@
179
133
  <!-- ─── Compact: minimal text rows ─── -->
180
134
  <div class="ag-compact-list">
181
135
  {#if timedDayEvents.length === 0 && allDayBanner.length === 0}
182
- <div class="ag-q-empty">{L.nothingScheduledYet}</div>
136
+ <div class="ag-q-empty">
137
+ {#if emptySnippet}{@render emptySnippet()}{:else}{L.nothingScheduledYet}{/if}
138
+ </div>
183
139
  {:else}
184
140
  {#each timedDayEvents as ev (ev.id)}
185
141
  <div
@@ -195,6 +151,7 @@
195
151
  onpointerenter={() => oneventhover?.(ev)}
196
152
  onkeydown={(e) => handleKeydown(e, ev)}
197
153
  >
154
+ <EventContent event={ev}>
198
155
  <span class="ag-compact-row-dot"></span>
199
156
  <span class="ag-compact-row-time">{fmt(ev.start)}</span>
200
157
  <span class="ag-compact-row-title">{ev.title}</span>
@@ -207,6 +164,7 @@
207
164
  {/each}
208
165
  {/if}
209
166
  <span class="ag-compact-row-dur">{duration(ev)}</span>
167
+ </EventContent>
210
168
  </div>
211
169
  {/each}
212
170
  {/if}
@@ -215,28 +173,8 @@
215
173
  {:else if isToday}
216
174
  <!-- ─── Today: "The Queue" — upcoming is the hero ─── -->
217
175
  <div class="ag-q">
218
- <!-- NOW column: past events stacked above NOW strip -->
176
+ <!-- NOW column: the live strip first, completed collapsed below -->
219
177
  <div class="ag-q-status">
220
- {#if dayCat.past.length > 0}
221
- <div class="ag-q-done-section">
222
- <div class="ag-q-label">{L.done}</div>
223
- {#each dayCat.past as ev (ev.id)}
224
- <div
225
- class="ag-q-done-item"
226
- class:ag-q-done-item--selected={selectedEventId === ev.id}
227
- role="button"
228
- tabindex="0"
229
- aria-label="{ev.title}, {L.completed}, {fmt(ev.start)}"
230
- onclick={() => handleClick(ev)}
231
- onkeydown={(e) => handleKeydown(e, ev)}
232
- >
233
- <span class="ag-q-done-check">✓</span>
234
- <span class="ag-q-done-title">{ev.title}</span>
235
- </div>
236
- {/each}
237
- </div>
238
- {/if}
239
-
240
178
  <div class="ag-q-label">{L.now} <span class="ag-q-clock">{clock.hm}</span></div>
241
179
  {#if dayCat.current.length > 0}
242
180
  {#each dayCat.current as ev (ev.id)}
@@ -251,6 +189,7 @@
251
189
  >
252
190
  <div class="ag-q-now-dot"></div>
253
191
  <div class="ag-q-now-title">{ev.title}</div>
192
+ {#if ev.subtitle}<div class="ag-q-now-sub">{ev.subtitle}</div>{/if}
254
193
  <div class="ag-q-now-time">{L.until} {fmt(ev.end)}</div>
255
194
  <div class="ag-q-now-track">
256
195
  <div class="ag-q-now-fill" style:width="{prog(ev) * 100}%"></div>
@@ -262,6 +201,31 @@
262
201
  <div class="ag-q-free-label">{L.free}</div>
263
202
  </div>
264
203
  {/if}
204
+
205
+ {#if dayCat.past.length > 0}
206
+ <div class="ag-q-done-section">
207
+ <div class="ag-q-label">{L.done}</div>
208
+ {#each visibleDone as ev (ev.id)}
209
+ <div
210
+ class="ag-q-done-item"
211
+ class:ag-q-done-item--selected={selectedEventId === ev.id}
212
+ role="button"
213
+ tabindex="0"
214
+ aria-label="{ev.title}, {L.completed}, {fmt(ev.start)}"
215
+ onclick={() => handleClick(ev)}
216
+ onkeydown={(e) => handleKeydown(e, ev)}
217
+ >
218
+ <span class="ag-q-done-check">✓</span>
219
+ <span class="ag-q-done-title">{ev.title}</span>
220
+ </div>
221
+ {/each}
222
+ {#if hiddenDoneCount > 0}
223
+ <button class="ag-q-done-toggle" onclick={() => (showAllDone = !showAllDone)}>
224
+ {showAllDone ? L.showLess : L.nCompleted(hiddenDoneCount)}
225
+ </button>
226
+ {/if}
227
+ </div>
228
+ {/if}
265
229
  </div>
266
230
 
267
231
  <!-- NEXT: the hero center column -->
@@ -273,6 +237,28 @@
273
237
  </div>
274
238
  {:else}
275
239
  {#each upcomingNext as ev, i (ev.id)}
240
+ {#if i >= UPCOMING_CARDS}
241
+ <div
242
+ class="ag-compact-row ag-compact-row--queue"
243
+ class:ag-compact-row--selected={selectedEventId === ev.id}
244
+ style:--ev-color={ev.color || 'var(--dt-accent)'}
245
+ role="button"
246
+ tabindex="0"
247
+ aria-label="{ev.title}, {fmt(ev.start)}, {duration(ev)}"
248
+ onclick={() => handleClick(ev)}
249
+ onpointerenter={() => oneventhover?.(ev)}
250
+ onkeydown={(e) => handleKeydown(e, ev)}
251
+ >
252
+ <EventContent event={ev}>
253
+ <span class="ag-compact-row-dot"></span>
254
+ <span class="ag-compact-row-time">{fmt(ev.start)}</span>
255
+ <span class="ag-compact-row-title">{ev.title}</span>
256
+ {#if ev.subtitle}
257
+ <span class="ag-compact-row-sub">{ev.subtitle}</span>
258
+ {/if}
259
+ </EventContent>
260
+ </div>
261
+ {:else}
276
262
  <div
277
263
  class="ag-card ag-card--q"
278
264
  class:ag-card--hero={i === 0}
@@ -286,6 +272,7 @@
286
272
  onkeydown={(e) => handleKeydown(e, ev)}
287
273
  >
288
274
  <div class="ag-card-body">
275
+ <EventContent event={ev}>
289
276
  <div class="ag-card-top">
290
277
  <span class="ag-card-title">{ev.title}</span>
291
278
  <span class="ag-card-eta">{eta(ev.start.getTime())}</span>
@@ -304,8 +291,10 @@
304
291
  {/each}
305
292
  </div>
306
293
  {/if}
294
+ </EventContent>
307
295
  </div>
308
296
  </div>
297
+ {/if}
309
298
  {/each}
310
299
  {/if}
311
300
  </div>
@@ -341,7 +330,9 @@
341
330
  <!-- ─── Future day: "The Plan" — everything is ahead ─── -->
342
331
  <div class="ag-plan">
343
332
  {#if timedDayEvents.length === 0 && allDayBanner.length === 0}
344
- <div class="ag-q-empty">{L.nothingScheduledYet}</div>
333
+ <div class="ag-q-empty">
334
+ {#if emptySnippet}{@render emptySnippet()}{:else}{L.nothingScheduledYet}{/if}
335
+ </div>
345
336
  {:else}
346
337
  {#each timedDayEvents as ev, i (ev.id)}
347
338
  <div
@@ -359,6 +350,7 @@
359
350
  onclick={() => handleClick(ev)} onpointerenter={() => oneventhover?.(ev)} onkeydown={(e) => handleKeydown(e, ev)}
360
351
  >
361
352
  <div class="ag-card-body">
353
+ <EventContent event={ev}>
362
354
  <div class="ag-card-top">
363
355
  <span class="ag-card-order">{i + 1}</span>
364
356
  <span class="ag-card-title">{ev.title}</span>
@@ -380,6 +372,7 @@
380
372
  {/each}
381
373
  </div>
382
374
  {/if}
375
+ </EventContent>
383
376
  </div>
384
377
  </div>
385
378
  {/each}
@@ -596,9 +589,11 @@
596
589
 
597
590
  /* ── Queue card variant ── */
598
591
  .ag-card--q {
599
- margin-bottom: 6px;
600
592
  transition: border-color 150ms, transform 100ms;
601
593
  }
594
+ .ag-compact-row--queue {
595
+ margin: 0;
596
+ }
602
597
 
603
598
  .ag-card--q .ag-card-body {
604
599
  gap: 3px;
@@ -750,10 +745,31 @@
750
745
  .ag-q-status::-webkit-scrollbar {
751
746
  display: none;
752
747
  }
748
+ .ag-q-done-toggle {
749
+ align-self: flex-start;
750
+ margin-top: 2px;
751
+ padding: 3px 8px;
752
+ border: 1px solid var(--dt-border);
753
+ border-radius: 999px;
754
+ background: none;
755
+ font-family: var(--dt-mono);
756
+ font-size: 11px;
757
+ color: var(--dt-text-3);
758
+ cursor: pointer;
759
+ }
760
+ .ag-q-done-toggle:hover {
761
+ color: var(--dt-text);
762
+ border-color: var(--dt-text-3);
763
+ }
764
+ .ag-q-now-sub {
765
+ font-size: 12px;
766
+ color: var(--dt-text-2);
767
+ margin-top: 1px;
768
+ }
753
769
  .ag-q-done-section {
754
- margin-bottom: 10px;
755
- padding-bottom: 8px;
756
- border-bottom: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
770
+ margin-top: 12px;
771
+ padding-top: 10px;
772
+ border-top: 1px solid var(--dt-border, rgba(0, 0, 0, 0.08));
757
773
  }
758
774
  .ag-q-clock {
759
775
  font-size: 10px;
@@ -764,6 +780,7 @@
764
780
  }
765
781
  .ag-q-now {
766
782
  padding: 8px 10px;
783
+ margin-bottom: 8px;
767
784
  border-radius: 8px;
768
785
  background: color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, var(--dt-surface, var(--dt-bg, #ffffff)));
769
786
  border: 1px solid color-mix(in srgb, var(--ev-color, var(--dt-accent)) 15%, transparent);
@@ -840,6 +857,7 @@
840
857
  scrollbar-width: none;
841
858
  display: flex;
842
859
  flex-direction: column;
860
+ gap: 8px;
843
861
  }
844
862
  .ag-q-queue::-webkit-scrollbar {
845
863
  display: none;
@@ -966,10 +984,11 @@
966
984
  .ag-compact-list::-webkit-scrollbar { display: none; }
967
985
  .ag-compact-row {
968
986
  display: flex;
969
- align-items: center;
987
+ align-items: baseline;
970
988
  gap: 8px;
971
989
  padding: 4px 0;
972
990
  cursor: pointer;
991
+ min-width: 0;
973
992
  }
974
993
  .ag-compact-row--selected {
975
994
  background: color-mix(in srgb, var(--ev-color) 10%, transparent);
@@ -988,6 +1007,7 @@
988
1007
  border-radius: 50%;
989
1008
  background: var(--ev-color, var(--dt-accent));
990
1009
  flex-shrink: 0;
1010
+ align-self: center;
991
1011
  }
992
1012
  .ag-compact-row-time {
993
1013
  font-size: 11px;
@@ -995,6 +1015,7 @@
995
1015
  color: var(--dt-text-2, rgba(0, 0, 0, 0.54));
996
1016
  min-width: 64px;
997
1017
  flex-shrink: 0;
1018
+ line-height: 1.4;
998
1019
  }
999
1020
  .ag-compact-row-title {
1000
1021
  font-size: 12px;
@@ -1005,17 +1026,24 @@
1005
1026
  overflow: hidden;
1006
1027
  text-overflow: ellipsis;
1007
1028
  transition: color 150ms;
1029
+ line-height: 1.4;
1008
1030
  }
1009
1031
  .ag-compact-row-dur {
1010
1032
  font-size: 10px;
1011
1033
  font-family: var(--dt-mono, monospace);
1012
1034
  color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
1013
1035
  flex-shrink: 0;
1036
+ line-height: 1.4;
1014
1037
  }
1015
1038
  .ag-compact-row-sub {
1016
1039
  font-size: 10px;
1017
1040
  color: var(--dt-text-3, rgba(0, 0, 0, 0.38));
1018
- flex-shrink: 0;
1041
+ flex-shrink: 1;
1042
+ max-width: 45%;
1043
+ white-space: nowrap;
1044
+ overflow: hidden;
1045
+ text-overflow: ellipsis;
1046
+ line-height: 1.4;
1019
1047
  }
1020
1048
  .ag-compact-row-tag {
1021
1049
  font: 500 8px / 1 var(--dt-sans, system-ui, sans-serif);