@stll/ui 0.58.0 → 0.59.1
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.
|
@@ -9,18 +9,28 @@ export type DayCalendarTone = {
|
|
|
9
9
|
} | {
|
|
10
10
|
type: "neutral";
|
|
11
11
|
};
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Whether an entry is final, still provisional (dashed), or only proposed:
|
|
14
|
+
* a suggestion is not an entry yet, so it is drawn lighter, dashed, and is a
|
|
15
|
+
* button the caller acts on.
|
|
16
|
+
*/
|
|
17
|
+
export type DayCalendarEntryState = "draft" | "final" | "suggestion";
|
|
14
18
|
type DayCalendarBlockFields = DayCalendarRange & {
|
|
15
19
|
accessibleLabel: string;
|
|
16
|
-
|
|
20
|
+
caption?: ReactNode;
|
|
17
21
|
id: string;
|
|
18
22
|
title: ReactNode;
|
|
19
23
|
tone: DayCalendarTone;
|
|
24
|
+
/** Shown on hover, for example the apps a `mixed` block merged. */
|
|
25
|
+
tooltip?: ReactNode;
|
|
20
26
|
};
|
|
21
27
|
export type DayCalendarBlock = (DayCalendarBlockFields & {
|
|
22
28
|
kind: "entry";
|
|
23
|
-
state: DayCalendarEntryState
|
|
29
|
+
state: Exclude<DayCalendarEntryState, "suggestion">;
|
|
30
|
+
}) | (DayCalendarBlockFields & {
|
|
31
|
+
kind: "entry";
|
|
32
|
+
onActivate: () => void;
|
|
33
|
+
state: "suggestion";
|
|
24
34
|
}) | (DayCalendarBlockFields & {
|
|
25
35
|
kind: "mixed";
|
|
26
36
|
}) | (DayCalendarBlockFields & {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { cn } from "../lib/utils.js";
|
|
2
|
+
import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "../components/tooltip.js";
|
|
2
3
|
import { DAY_CALENDAR_SLOT_HEIGHT_PX, boundsHeight, extendRange, getDayBounds, getHourTicks, isRepeatedWallTime, layoutOverlaps, moveRange, offsetToTime, rangeFromDrag, slotAt, timeToOffset } from "./day-calendar.logic.js";
|
|
3
4
|
import { resolveMatterColor } from "../lib/matter-colors.js";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
6
|
import { useState } from "react";
|
|
6
7
|
import { panic } from "better-result";
|
|
7
8
|
import { Temporal } from "temporal-polyfill/full";
|
|
@@ -305,7 +306,7 @@ const ColumnBlocks = ({ blocks, grid }) => {
|
|
|
305
306
|
}, block.id);
|
|
306
307
|
});
|
|
307
308
|
};
|
|
308
|
-
/** Below this height a block puts its title and
|
|
309
|
+
/** Below this height a block puts its title and caption on one line. */
|
|
309
310
|
const TWO_LINE_BLOCK_MIN_PX = 40;
|
|
310
311
|
/** A brief block paints at least this tall, unless the grid ends sooner. */
|
|
311
312
|
const BLOCK_MIN_PX = 6;
|
|
@@ -316,7 +317,8 @@ const BLOCK_KIND_CLASSES = {
|
|
|
316
317
|
};
|
|
317
318
|
const ENTRY_STATE_CLASSES = {
|
|
318
319
|
draft: "border border-dashed border-current/40",
|
|
319
|
-
final: ""
|
|
320
|
+
final: "",
|
|
321
|
+
suggestion: "border border-dashed border-current/45 outline-none hover:ring-1 hover:ring-ring/50 focus-visible:ring-2 focus-visible:ring-ring"
|
|
320
322
|
};
|
|
321
323
|
const DayCalendarBlockView = ({ block, grid, lane, range }) => {
|
|
322
324
|
const top = timeToOffset(range.startMs, grid);
|
|
@@ -327,36 +329,66 @@ const DayCalendarBlockView = ({ block, grid, lane, range }) => {
|
|
|
327
329
|
insetBlockStart: top,
|
|
328
330
|
insetInlineStart: `${String(lane.lane * 100 / lane.laneCount)}%`
|
|
329
331
|
};
|
|
332
|
+
const className = cn("text-foreground flex h-full w-full min-w-0 overflow-hidden rounded-md px-2.5 text-start text-xs", blockSize < TWO_LINE_BLOCK_MIN_PX ? "flex-row items-center gap-2" : "flex-col gap-0.5 py-1", BLOCK_KIND_CLASSES[block.kind], block.kind === "entry" && ENTRY_STATE_CLASSES[block.state], block.tone.type === "matter" && "border-s-[3px]", block.tone.type === "neutral" && "bg-muted");
|
|
333
|
+
const content = blockSize < 16 ? null : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
334
|
+
className: "min-w-0 shrink-0 truncate font-medium",
|
|
335
|
+
children: block.title
|
|
336
|
+
}), block.caption === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
337
|
+
className: "text-muted-foreground min-w-0 truncate tabular-nums",
|
|
338
|
+
children: block.caption
|
|
339
|
+
})] });
|
|
340
|
+
const shared = {
|
|
341
|
+
"aria-label": block.accessibleLabel,
|
|
342
|
+
className,
|
|
343
|
+
"data-block-id": block.id,
|
|
344
|
+
"data-kind": block.kind,
|
|
345
|
+
"data-slot": "day-calendar-block",
|
|
346
|
+
style: blockSurfaceStyle(block)
|
|
347
|
+
};
|
|
348
|
+
const surface = block.kind === "entry" && block.state === "suggestion" ? /* @__PURE__ */ jsx("button", {
|
|
349
|
+
...shared,
|
|
350
|
+
"data-state": block.state,
|
|
351
|
+
onClick: block.onActivate,
|
|
352
|
+
onKeyDown: (event) => event.stopPropagation(),
|
|
353
|
+
onPointerDown: (event) => event.stopPropagation(),
|
|
354
|
+
type: "button",
|
|
355
|
+
children: content
|
|
356
|
+
}) : /* @__PURE__ */ jsx("article", {
|
|
357
|
+
...shared,
|
|
358
|
+
"data-state": block.kind === "entry" ? block.state : void 0,
|
|
359
|
+
children: content
|
|
360
|
+
});
|
|
330
361
|
return /* @__PURE__ */ jsx("div", {
|
|
331
|
-
className: "
|
|
362
|
+
className: cn("absolute px-1.5 py-px", block.tooltip === void 0 && !(block.kind === "entry" && block.state === "suggestion") && "pointer-events-none"),
|
|
332
363
|
style: placement,
|
|
333
|
-
children: /* @__PURE__ */ jsxs(
|
|
334
|
-
"
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
"data-slot": "day-calendar-block",
|
|
338
|
-
style: blockSurfaceStyle(block),
|
|
339
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
340
|
-
className: "min-w-0 shrink-0 truncate font-medium",
|
|
341
|
-
children: block.title
|
|
342
|
-
}), block.detail === void 0 ? null : /* @__PURE__ */ jsx("span", {
|
|
343
|
-
className: "text-muted-foreground min-w-0 truncate tabular-nums",
|
|
344
|
-
children: block.detail
|
|
345
|
-
})]
|
|
346
|
-
})
|
|
364
|
+
children: block.tooltip === void 0 ? surface : /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: surface }), /* @__PURE__ */ jsx(TooltipPopup, {
|
|
365
|
+
className: "max-w-72",
|
|
366
|
+
children: block.tooltip
|
|
367
|
+
})] })
|
|
347
368
|
});
|
|
348
369
|
};
|
|
349
370
|
const MIXED_STRIPES = "repeating-linear-gradient(135deg, var(--muted) 0 6px, transparent 6px 12px)";
|
|
371
|
+
/** A suggestion's tint is half an entry's, so proposed time reads lighter. */
|
|
372
|
+
const TONE_TINT_PERCENT = {
|
|
373
|
+
entry: 16,
|
|
374
|
+
suggestion: 8
|
|
375
|
+
};
|
|
350
376
|
function blockSurfaceStyle(block) {
|
|
351
377
|
const tone = block.tone;
|
|
352
378
|
const stripes = block.kind === "mixed" ? MIXED_STRIPES : void 0;
|
|
379
|
+
const suggestion = block.kind === "entry" && block.state === "suggestion";
|
|
353
380
|
switch (tone.type) {
|
|
354
|
-
case "neutral": return {
|
|
381
|
+
case "neutral": return {
|
|
382
|
+
backgroundColor: suggestion ? "transparent" : void 0,
|
|
383
|
+
backgroundImage: stripes
|
|
384
|
+
};
|
|
355
385
|
case "matter": {
|
|
356
386
|
const color = resolveMatterColor(tone.matterId, tone.color);
|
|
387
|
+
const tint = suggestion ? TONE_TINT_PERCENT.suggestion : TONE_TINT_PERCENT.entry;
|
|
357
388
|
return {
|
|
358
|
-
backgroundColor: `color-mix(in srgb, ${color}
|
|
389
|
+
backgroundColor: `color-mix(in srgb, ${color} ${String(tint)}%, var(--card))`,
|
|
359
390
|
backgroundImage: stripes,
|
|
391
|
+
borderColor: suggestion ? color : void 0,
|
|
360
392
|
borderInlineStartColor: color
|
|
361
393
|
};
|
|
362
394
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
//#region src/calendar/day-calendar.logic.d.ts
|
|
2
2
|
export declare const DAY_CALENDAR_GRANULARITIES: readonly [15, 30, 60];
|
|
3
3
|
export type DayCalendarGranularity = (typeof DAY_CALENDAR_GRANULARITIES)[number];
|
|
4
|
+
/** Below this height a block cannot hold one line of its label, so it draws
|
|
5
|
+
* its surface only; its accessible label and tooltip still carry it. */
|
|
6
|
+
export declare const DAY_CALENDAR_ONE_LINE_BLOCK_MIN_PX = 16;
|
|
4
7
|
/**
|
|
5
8
|
* Height of one grid slot. Coarser grids get taller slots, but not
|
|
6
9
|
* proportionally: an hour stays tall enough for a two-line block.
|
|
@@ -6,6 +6,9 @@ const DAY_CALENDAR_GRANULARITIES = [
|
|
|
6
6
|
30,
|
|
7
7
|
60
|
|
8
8
|
];
|
|
9
|
+
/** Below this height a block cannot hold one line of its label, so it draws
|
|
10
|
+
* its surface only; its accessible label and tooltip still carry it. */
|
|
11
|
+
const DAY_CALENDAR_ONE_LINE_BLOCK_MIN_PX = 16;
|
|
9
12
|
/**
|
|
10
13
|
* Height of one grid slot. Coarser grids get taller slots, but not
|
|
11
14
|
* proportionally: an hour stays tall enough for a two-line block.
|
|
@@ -232,4 +235,4 @@ function compareIds(left, right) {
|
|
|
232
235
|
return left > right ? 1 : 0;
|
|
233
236
|
}
|
|
234
237
|
//#endregion
|
|
235
|
-
export { DAY_CALENDAR_GRANULARITIES, DAY_CALENDAR_SLOT_HEIGHT_PX, boundsHeight, clampToBounds, extendRange, getDayBounds, getHourTicks, granularityMs, isRepeatedWallTime, layoutOverlaps, mergeShortSegments, moveRange, offsetToTime, rangeFromDrag, slotAt, snapToGrid, timeToOffset };
|
|
238
|
+
export { DAY_CALENDAR_GRANULARITIES, DAY_CALENDAR_ONE_LINE_BLOCK_MIN_PX, DAY_CALENDAR_SLOT_HEIGHT_PX, boundsHeight, clampToBounds, extendRange, getDayBounds, getHourTicks, granularityMs, isRepeatedWallTime, layoutOverlaps, mergeShortSegments, moveRange, offsetToTime, rangeFromDrag, slotAt, snapToGrid, timeToOffset };
|
|
@@ -13,7 +13,7 @@ const ScrollArea = ({ axis = "both", className, children, scrollFade = false, sc
|
|
|
13
13
|
children: [
|
|
14
14
|
/* @__PURE__ */ jsx(ScrollArea$1.Viewport, {
|
|
15
15
|
ref: viewportRef,
|
|
16
|
-
className: cn("focus-visible:ring-ring focus-visible:ring-offset-background min-h-0 flex-1 overscroll-contain rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-offset-1 data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-x:pb-2.5 data-has-overflow-y:pe-2.5", AXIS_VIEWPORT_CLASS[axis]),
|
|
16
|
+
className: cn("focus-visible:ring-ring focus-visible:ring-offset-background relative min-h-0 flex-1 overscroll-contain rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-offset-1 data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-x:pb-2.5 data-has-overflow-y:pe-2.5", AXIS_VIEWPORT_CLASS[axis]),
|
|
17
17
|
"data-slot": "scroll-area-viewport",
|
|
18
18
|
children
|
|
19
19
|
}),
|
package/package.json
CHANGED