@adecore/ui 0.33.2 → 0.34.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 (63) hide show
  1. package/dist/Banner.d.ts +2 -1
  2. package/dist/Banner.js +92 -51
  3. package/dist/CopyButton.d.ts +24 -0
  4. package/dist/CopyButton.js +144 -0
  5. package/dist/ErrorBoundary.js +16 -18
  6. package/dist/ProjectSwitcher.js +66 -73
  7. package/dist/RelativeTime.d.ts +9 -0
  8. package/dist/RelativeTime.js +38 -0
  9. package/dist/Segmented.d.ts +4 -1
  10. package/dist/Segmented.js +111 -86
  11. package/dist/Toasts.js +1 -1
  12. package/dist/combobox/index.parts.d.ts +1 -0
  13. package/dist/combobox/index.parts.js +1 -0
  14. package/dist/combobox/parts.d.ts +32 -0
  15. package/dist/combobox/parts.js +556 -0
  16. package/dist/command-palette/index.parts.d.ts +2 -0
  17. package/dist/command-palette/index.parts.js +3 -0
  18. package/dist/command-palette/parts.d.ts +32 -0
  19. package/dist/command-palette/parts.js +422 -0
  20. package/dist/format/datetime.d.ts +1 -0
  21. package/dist/format/datetime.js +8 -0
  22. package/dist/format/duration.d.ts +1 -0
  23. package/dist/format/duration.js +24 -1
  24. package/dist/format/index.d.ts +2 -2
  25. package/dist/format/index.js +2 -2
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.js +5 -0
  28. package/dist/locales/en.d.ts +8 -0
  29. package/dist/locales/en.js +8 -0
  30. package/dist/locales/nl.d.ts +8 -0
  31. package/dist/locales/nl.js +8 -0
  32. package/dist/match.d.ts +1 -0
  33. package/dist/match.js +47 -0
  34. package/dist/segmented-keys.d.ts +3 -0
  35. package/dist/segmented-keys.js +33 -0
  36. package/dist/theme.css +15 -0
  37. package/dist/toast-store.d.ts +1 -0
  38. package/dist/toast-store.js +16 -6
  39. package/dist/workspace/TabStrip.d.ts +2 -1
  40. package/dist/workspace/TabStrip.js +432 -372
  41. package/package.json +1 -1
  42. package/src/Banner.tsx +33 -9
  43. package/src/CopyButton.tsx +86 -0
  44. package/src/ErrorBoundary.tsx +3 -4
  45. package/src/ProjectSwitcher.tsx +4 -2
  46. package/src/RelativeTime.tsx +44 -0
  47. package/src/Segmented.tsx +60 -33
  48. package/src/Toasts.tsx +1 -1
  49. package/src/combobox/index.parts.ts +12 -0
  50. package/src/combobox/parts.tsx +147 -0
  51. package/src/command-palette/index.parts.ts +16 -0
  52. package/src/command-palette/parts.tsx +145 -0
  53. package/src/format/datetime.ts +5 -0
  54. package/src/format/duration.ts +25 -1
  55. package/src/format/index.ts +2 -1
  56. package/src/index.ts +7 -0
  57. package/src/locales/en.ts +8 -0
  58. package/src/locales/nl.ts +8 -0
  59. package/src/match.ts +50 -0
  60. package/src/segmented-keys.ts +33 -0
  61. package/src/theme.css +15 -0
  62. package/src/toast-store.ts +16 -6
  63. package/src/workspace/TabStrip.tsx +31 -2
package/dist/Banner.d.ts CHANGED
@@ -6,7 +6,8 @@ export interface BannerProps {
6
6
  tone: BannerTone;
7
7
  message: ReactNode;
8
8
  children?: ReactNode;
9
+ placement?: 'overlay' | 'inline';
9
10
  className?: string;
10
11
  ref?: Ref<HTMLDivElement>;
11
12
  }
12
- export declare function Banner({ icon, tone, message, children, className, ref }: BannerProps): import("react").JSX.Element;
13
+ export declare function Banner({ icon, tone, message, children, placement, className, ref }: BannerProps): import("react").JSX.Element;
package/dist/Banner.js CHANGED
@@ -2,98 +2,139 @@ import { c as _c } from "react/compiler-runtime";
2
2
  import clsx from 'clsx';
3
3
  import { Icon } from "./Icon.js";
4
4
  import { Surface } from "./Surface.js";
5
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  const TONE = {
7
7
  attention: 'text-status-needs-you',
8
8
  error: 'text-status-error',
9
9
  neutral: 'text-text-muted'
10
10
  };
11
+ const OVERLAY = 'absolute inset-x-0 top-3 z-20';
11
12
  /*
12
13
  * The strip over a view: one line and the buttons that answer it. It is where anything that waits
13
14
  * for a person goes (a conflict, a failure, a request), because a corner toast is read after the fact
14
15
  * and a decision has to be where the eyes already are. One at a time: the slot is one row and the
15
- * caller picks what stands in it.
16
+ * caller picks what stands in it. Inline, it is a notice that belongs to the page it stands in.
16
17
  */
17
18
  export function Banner(t0) {
18
- const $ = _c(17);
19
+ const $ = _c(25);
19
20
  const {
20
21
  icon,
21
22
  tone,
22
23
  message,
23
24
  children,
24
- className: t1,
25
+ placement: t1,
26
+ className,
25
27
  ref
26
28
  } = t0;
27
- const className = t1 === undefined ? "absolute inset-x-0 top-3 z-20" : t1;
28
- let t2;
29
- if ($[0] !== className) {
30
- t2 = clsx("pointer-events-auto flex justify-center px-4", className);
31
- $[0] = className;
32
- $[1] = t2;
29
+ const placement = t1 === undefined ? "overlay" : t1;
30
+ const t2 = TONE[tone];
31
+ let t3;
32
+ if ($[0] !== t2) {
33
+ t3 = clsx("shrink-0", t2);
34
+ $[0] = t2;
35
+ $[1] = t3;
33
36
  } else {
34
- t2 = $[1];
37
+ t3 = $[1];
35
38
  }
36
- const t3 = TONE[tone];
37
39
  let t4;
38
- if ($[2] !== t3) {
39
- t4 = clsx("shrink-0", t3);
40
- $[2] = t3;
41
- $[3] = t4;
40
+ if ($[2] !== icon || $[3] !== t3) {
41
+ t4 = /*#__PURE__*/_jsx(Icon, {
42
+ icon: icon,
43
+ size: 16,
44
+ className: t3
45
+ });
46
+ $[2] = icon;
47
+ $[3] = t3;
48
+ $[4] = t4;
42
49
  } else {
43
- t4 = $[3];
50
+ t4 = $[4];
44
51
  }
45
52
  let t5;
46
- if ($[4] !== icon || $[5] !== t4) {
47
- t5 = /*#__PURE__*/_jsx(Icon, {
48
- icon: icon,
49
- size: 16,
50
- className: t4
53
+ if ($[5] !== message) {
54
+ t5 = /*#__PURE__*/_jsx("span", {
55
+ className: "grow",
56
+ children: message
51
57
  });
52
- $[4] = icon;
53
- $[5] = t4;
58
+ $[5] = message;
54
59
  $[6] = t5;
55
60
  } else {
56
61
  t5 = $[6];
57
62
  }
58
63
  let t6;
59
- if ($[7] !== message) {
60
- t6 = /*#__PURE__*/_jsx("span", {
61
- className: "grow",
62
- children: message
64
+ if ($[7] !== children || $[8] !== t4 || $[9] !== t5) {
65
+ t6 = /*#__PURE__*/_jsxs(_Fragment, {
66
+ children: [t4, t5, children]
63
67
  });
64
- $[7] = message;
65
- $[8] = t6;
68
+ $[7] = children;
69
+ $[8] = t4;
70
+ $[9] = t5;
71
+ $[10] = t6;
66
72
  } else {
67
- t6 = $[8];
73
+ t6 = $[10];
68
74
  }
69
- let t7;
70
- if ($[9] !== children || $[10] !== t5 || $[11] !== t6) {
71
- t7 = /*#__PURE__*/_jsxs(Surface, {
75
+ const line = t6;
76
+ if (placement === "inline") {
77
+ let t7;
78
+ if ($[11] !== className) {
79
+ t7 = clsx("flex items-center gap-3 rounded-lg border border-border bg-surface px-3 py-2 text-sm text-text", className);
80
+ $[11] = className;
81
+ $[12] = t7;
82
+ } else {
83
+ t7 = $[12];
84
+ }
85
+ let t8;
86
+ if ($[13] !== line || $[14] !== ref || $[15] !== t7) {
87
+ t8 = /*#__PURE__*/_jsx("div", {
88
+ ref: ref,
89
+ className: t7,
90
+ role: "status",
91
+ "aria-live": "polite",
92
+ children: line
93
+ });
94
+ $[13] = line;
95
+ $[14] = ref;
96
+ $[15] = t7;
97
+ $[16] = t8;
98
+ } else {
99
+ t8 = $[16];
100
+ }
101
+ return t8;
102
+ }
103
+ const t7 = className ?? OVERLAY;
104
+ let t8;
105
+ if ($[17] !== t7) {
106
+ t8 = clsx("pointer-events-auto flex justify-center px-4", t7);
107
+ $[17] = t7;
108
+ $[18] = t8;
109
+ } else {
110
+ t8 = $[18];
111
+ }
112
+ let t9;
113
+ if ($[19] !== line) {
114
+ t9 = /*#__PURE__*/_jsx(Surface, {
72
115
  className: "flex max-w-[640px] items-center gap-3 rounded-lg px-3 py-2 text-sm text-text",
73
- children: [t5, t6, children]
116
+ children: line
74
117
  });
75
- $[9] = children;
76
- $[10] = t5;
77
- $[11] = t6;
78
- $[12] = t7;
118
+ $[19] = line;
119
+ $[20] = t9;
79
120
  } else {
80
- t7 = $[12];
121
+ t9 = $[20];
81
122
  }
82
- let t8;
83
- if ($[13] !== ref || $[14] !== t2 || $[15] !== t7) {
84
- t8 = /*#__PURE__*/_jsx("div", {
123
+ let t10;
124
+ if ($[21] !== ref || $[22] !== t8 || $[23] !== t9) {
125
+ t10 = /*#__PURE__*/_jsx("div", {
85
126
  ref: ref,
86
- className: t2,
127
+ className: t8,
87
128
  role: "status",
88
129
  "aria-live": "polite",
89
- children: t7
130
+ children: t9
90
131
  });
91
- $[13] = ref;
92
- $[14] = t2;
93
- $[15] = t7;
94
- $[16] = t8;
132
+ $[21] = ref;
133
+ $[22] = t8;
134
+ $[23] = t9;
135
+ $[24] = t10;
95
136
  } else {
96
- t8 = $[16];
137
+ t10 = $[24];
97
138
  }
98
- return t8;
139
+ return t10;
99
140
  }
@@ -0,0 +1,24 @@
1
+ import { type Ref } from 'react';
2
+ import { type ButtonSize, type ButtonVariant } from './Button.tsx';
3
+ import { type IconButtonSize } from './IconButton.tsx';
4
+ import type { TooltipSide } from './Tooltip.tsx';
5
+ interface CopyButtonBase {
6
+ text: string;
7
+ label?: string;
8
+ disabled?: boolean;
9
+ className?: string;
10
+ ref?: Ref<HTMLButtonElement>;
11
+ }
12
+ interface CopyIconButtonProps extends CopyButtonBase {
13
+ showLabel?: false;
14
+ size?: IconButtonSize;
15
+ tooltipSide?: TooltipSide;
16
+ }
17
+ interface CopyLabelButtonProps extends CopyButtonBase {
18
+ showLabel: true;
19
+ size?: ButtonSize;
20
+ variant?: ButtonVariant;
21
+ }
22
+ export type CopyButtonProps = CopyIconButtonProps | CopyLabelButtonProps;
23
+ export declare function CopyButton(props: CopyButtonProps): import("react").JSX.Element;
24
+ export {};
@@ -0,0 +1,144 @@
1
+ import { c as _c } from "react/compiler-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Check, Copy } from 'lucide-react';
5
+ import { Button } from "./Button.js";
6
+ import { copyText } from './clipboard.js';
7
+ import { Icon } from "./Icon.js";
8
+ import { IconButton } from "./IconButton.js";
9
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
10
+ const COPIED_MS = 1500;
11
+ /* A button that puts text on the clipboard and shows a check and "Copied" for a moment once it got there. */
12
+ export function CopyButton(props) {
13
+ const $ = _c(29);
14
+ const {
15
+ text,
16
+ label,
17
+ disabled,
18
+ className,
19
+ ref
20
+ } = props;
21
+ const {
22
+ t
23
+ } = useTranslation("ui");
24
+ const [copiedAt, setCopiedAt] = useState(null);
25
+ let t0;
26
+ let t1;
27
+ if ($[0] !== copiedAt) {
28
+ t0 = () => {
29
+ if (copiedAt === null) {
30
+ return;
31
+ }
32
+ const timer = window.setTimeout(() => setCopiedAt(null), COPIED_MS);
33
+ return () => window.clearTimeout(timer);
34
+ };
35
+ t1 = [copiedAt];
36
+ $[0] = copiedAt;
37
+ $[1] = t0;
38
+ $[2] = t1;
39
+ } else {
40
+ t0 = $[1];
41
+ t1 = $[2];
42
+ }
43
+ useEffect(t0, t1);
44
+ const copied = copiedAt !== null;
45
+ let t2;
46
+ if ($[3] !== label || $[4] !== t) {
47
+ t2 = label ?? t("action.copy");
48
+ $[3] = label;
49
+ $[4] = t;
50
+ $[5] = t2;
51
+ } else {
52
+ t2 = $[5];
53
+ }
54
+ const name = t2;
55
+ let t3;
56
+ if ($[6] !== text) {
57
+ t3 = async () => {
58
+ if (await copyText(text)) {
59
+ setCopiedAt(Date.now());
60
+ }
61
+ };
62
+ $[6] = text;
63
+ $[7] = t3;
64
+ } else {
65
+ t3 = $[7];
66
+ }
67
+ const copy = t3;
68
+ let t4;
69
+ if ($[8] !== className || $[9] !== copied || $[10] !== copy || $[11] !== disabled || $[12] !== name || $[13] !== props.showLabel || $[14] !== props.size || $[15] !== props.tooltipSide || $[16] !== props.variant || $[17] !== ref || $[18] !== t) {
70
+ t4 = props.showLabel ? /*#__PURE__*/_jsxs(Button, {
71
+ ref: ref,
72
+ variant: props.variant,
73
+ size: props.size,
74
+ disabled: disabled,
75
+ className: className,
76
+ onClick: () => void copy(),
77
+ children: [/*#__PURE__*/_jsx(Icon, {
78
+ icon: copied ? Check : Copy,
79
+ size: props.size === "xs" ? 12 : 14
80
+ }), copied ? t("action.copied") : name]
81
+ }) : /*#__PURE__*/_jsx(IconButton, {
82
+ ref: ref,
83
+ icon: copied ? Check : Copy,
84
+ label: name,
85
+ tooltip: copied ? t("action.copied") : name,
86
+ tooltipSide: props.tooltipSide,
87
+ size: props.size,
88
+ disabled: disabled,
89
+ className: className,
90
+ onClick: () => void copy()
91
+ });
92
+ $[8] = className;
93
+ $[9] = copied;
94
+ $[10] = copy;
95
+ $[11] = disabled;
96
+ $[12] = name;
97
+ $[13] = props.showLabel;
98
+ $[14] = props.size;
99
+ $[15] = props.tooltipSide;
100
+ $[16] = props.variant;
101
+ $[17] = ref;
102
+ $[18] = t;
103
+ $[19] = t4;
104
+ } else {
105
+ t4 = $[19];
106
+ }
107
+ const button = t4;
108
+ let t5;
109
+ if ($[20] !== copied || $[21] !== t) {
110
+ t5 = copied ? t("action.copied") : "";
111
+ $[20] = copied;
112
+ $[21] = t;
113
+ $[22] = t5;
114
+ } else {
115
+ t5 = $[22];
116
+ }
117
+ let t6;
118
+ if ($[23] !== copiedAt || $[24] !== t5) {
119
+ t6 = /*#__PURE__*/_jsx("span", {
120
+ role: "status",
121
+ className: "sr-only",
122
+ children: /*#__PURE__*/_jsx("span", {
123
+ children: t5
124
+ }, copiedAt)
125
+ });
126
+ $[23] = copiedAt;
127
+ $[24] = t5;
128
+ $[25] = t6;
129
+ } else {
130
+ t6 = $[25];
131
+ }
132
+ let t7;
133
+ if ($[26] !== button || $[27] !== t6) {
134
+ t7 = /*#__PURE__*/_jsxs(_Fragment, {
135
+ children: [button, t6]
136
+ });
137
+ $[26] = button;
138
+ $[27] = t6;
139
+ $[28] = t7;
140
+ } else {
141
+ t7 = $[28];
142
+ }
143
+ return t7;
144
+ }
@@ -1,14 +1,13 @@
1
1
  import { c as _c } from "react/compiler-runtime";
2
2
  import { Component } from 'react';
3
3
  import clsx from 'clsx';
4
- import { Copy, RotateCw, TriangleAlert } from 'lucide-react';
4
+ import { RotateCw, TriangleAlert } from 'lucide-react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import { Button } from "./Button.js";
7
7
  import { ButtonGroup } from "./ButtonGroup.js";
8
- import { copyText } from './clipboard.js';
8
+ import { CopyButton } from "./CopyButton.js";
9
9
  import { errorMessageOf, errorReport, shouldReset } from './error-boundary.js';
10
10
  import { Icon } from "./Icon.js";
11
- import { IconButton } from "./IconButton.js";
12
11
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
12
  const CLEAR = {
14
13
  error: null,
@@ -140,19 +139,19 @@ function ErrorFallback(t0) {
140
139
  t13 = $[22];
141
140
  }
142
141
  let t14;
143
- if ($[23] !== t) {
144
- t14 = t("action.copyError");
145
- $[23] = t;
146
- $[24] = t14;
142
+ if ($[23] !== componentStack || $[24] !== error || $[25] !== label) {
143
+ t14 = errorReport(label, error, componentStack);
144
+ $[23] = componentStack;
145
+ $[24] = error;
146
+ $[25] = label;
147
+ $[26] = t14;
147
148
  } else {
148
- t14 = $[24];
149
+ t14 = $[26];
149
150
  }
150
151
  let t15;
151
- if ($[25] !== componentStack || $[26] !== error || $[27] !== label) {
152
- t15 = () => copyText(errorReport(label, error, componentStack));
153
- $[25] = componentStack;
154
- $[26] = error;
155
- $[27] = label;
152
+ if ($[27] !== t) {
153
+ t15 = t("action.copyError");
154
+ $[27] = t;
156
155
  $[28] = t15;
157
156
  } else {
158
157
  t15 = $[28];
@@ -160,11 +159,10 @@ function ErrorFallback(t0) {
160
159
  let t16;
161
160
  if ($[29] !== t14 || $[30] !== t15) {
162
161
  t16 = /*#__PURE__*/_jsx(ButtonGroup, {
163
- children: /*#__PURE__*/_jsx(IconButton, {
164
- icon: Copy,
165
- size: "sm",
166
- label: t14,
167
- onClick: t15
162
+ children: /*#__PURE__*/_jsx(CopyButton, {
163
+ text: t14,
164
+ label: t15,
165
+ size: "sm"
168
166
  })
169
167
  });
170
168
  $[29] = t14;
@@ -7,7 +7,7 @@ import * as Menu from './menu/index.parts.js';
7
7
  import { ProjectSwitcherRow } from "./project-switcher-row.js";
8
8
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
9
9
  export function ProjectSwitcher(t0) {
10
- const $ = _c(32);
10
+ const $ = _c(28);
11
11
  const {
12
12
  current,
13
13
  projects,
@@ -28,78 +28,71 @@ export function ProjectSwitcher(t0) {
28
28
  t
29
29
  } = useTranslation("ui");
30
30
  let t1;
31
- if ($[0] !== current?.name || $[1] !== label || $[2] !== t) {
32
- t1 = label ?? current?.name ?? t("projectSwitcher.label");
33
- $[0] = current?.name;
34
- $[1] = label;
35
- $[2] = t;
36
- $[3] = t1;
31
+ if ($[0] !== label || $[1] !== t) {
32
+ t1 = label ?? t("projectSwitcher.label");
33
+ $[0] = label;
34
+ $[1] = t;
35
+ $[2] = t1;
37
36
  } else {
38
- t1 = $[3];
37
+ t1 = $[2];
39
38
  }
40
- let t2;
41
- if ($[4] !== className) {
42
- t2 = clsx("project-switcher-trigger", className);
43
- $[4] = className;
44
- $[5] = t2;
45
- } else {
46
- t2 = $[5];
47
- }
48
- const t3 = current?.icon;
49
- let t4;
50
- if ($[6] !== current?.name || $[7] !== t) {
51
- t4 = current?.name ?? t("projectSwitcher.label");
52
- $[6] = current?.name;
53
- $[7] = t;
54
- $[8] = t4;
39
+ const empty = t1;
40
+ const t2 = current === null ? empty : label === undefined ? current.name : `${label}: ${current.name}`;
41
+ let t3;
42
+ if ($[3] !== className) {
43
+ t3 = clsx("project-switcher-trigger", className);
44
+ $[3] = className;
45
+ $[4] = t3;
55
46
  } else {
56
- t4 = $[8];
47
+ t3 = $[4];
57
48
  }
58
- let t5;
59
- if ($[9] !== t4) {
60
- t5 = /*#__PURE__*/_jsx("span", {
49
+ const t4 = current?.icon;
50
+ const t5 = current?.name ?? empty;
51
+ let t6;
52
+ if ($[5] !== t5) {
53
+ t6 = /*#__PURE__*/_jsx("span", {
61
54
  className: "truncate text-sm font-medium text-text",
62
- children: t4
55
+ children: t5
63
56
  });
64
- $[9] = t4;
65
- $[10] = t5;
57
+ $[5] = t5;
58
+ $[6] = t6;
66
59
  } else {
67
- t5 = $[10];
60
+ t6 = $[6];
68
61
  }
69
- let t6;
70
- if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
71
- t6 = /*#__PURE__*/_jsx(Icon, {
62
+ let t7;
63
+ if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
64
+ t7 = /*#__PURE__*/_jsx(Icon, {
72
65
  icon: ChevronDown,
73
66
  size: 14,
74
67
  className: "shrink-0 text-text-muted"
75
68
  });
76
- $[11] = t6;
69
+ $[7] = t7;
77
70
  } else {
78
- t6 = $[11];
71
+ t7 = $[7];
79
72
  }
80
- let t7;
81
- if ($[12] !== disabled || $[13] !== leading || $[14] !== ref || $[15] !== t1 || $[16] !== t2 || $[17] !== t3 || $[18] !== t5) {
82
- t7 = /*#__PURE__*/_jsxs(Menu.Trigger, {
73
+ let t8;
74
+ if ($[8] !== disabled || $[9] !== leading || $[10] !== ref || $[11] !== t2 || $[12] !== t3 || $[13] !== t4 || $[14] !== t6) {
75
+ t8 = /*#__PURE__*/_jsxs(Menu.Trigger, {
83
76
  ref: ref,
84
77
  disabled: disabled,
85
- "aria-label": t1,
86
- className: t2,
87
- children: [leading, t3, t5, t6]
78
+ "aria-label": t2,
79
+ className: t3,
80
+ children: [leading, t4, t6, t7]
88
81
  });
89
- $[12] = disabled;
90
- $[13] = leading;
91
- $[14] = ref;
92
- $[15] = t1;
93
- $[16] = t2;
94
- $[17] = t3;
95
- $[18] = t5;
96
- $[19] = t7;
82
+ $[8] = disabled;
83
+ $[9] = leading;
84
+ $[10] = ref;
85
+ $[11] = t2;
86
+ $[12] = t3;
87
+ $[13] = t4;
88
+ $[14] = t6;
89
+ $[15] = t8;
97
90
  } else {
98
- t7 = $[19];
91
+ t8 = $[15];
99
92
  }
100
- let t8;
101
- if ($[20] !== before || $[21] !== children || $[22] !== onSelect || $[23] !== projects || $[24] !== recentProjects) {
102
- t8 = /*#__PURE__*/_jsx(Menu.Popup, {
93
+ let t9;
94
+ if ($[16] !== before || $[17] !== children || $[18] !== onSelect || $[19] !== projects || $[20] !== recentProjects) {
95
+ t9 = /*#__PURE__*/_jsx(Menu.Popup, {
103
96
  className: "min-w-60",
104
97
  children: /*#__PURE__*/_jsx(ProjectSwitcherGroups, {
105
98
  projects: projects,
@@ -109,33 +102,33 @@ export function ProjectSwitcher(t0) {
109
102
  children: children
110
103
  })
111
104
  });
112
- $[20] = before;
113
- $[21] = children;
114
- $[22] = onSelect;
115
- $[23] = projects;
116
- $[24] = recentProjects;
117
- $[25] = t8;
105
+ $[16] = before;
106
+ $[17] = children;
107
+ $[18] = onSelect;
108
+ $[19] = projects;
109
+ $[20] = recentProjects;
110
+ $[21] = t9;
118
111
  } else {
119
- t8 = $[25];
112
+ t9 = $[21];
120
113
  }
121
- let t9;
122
- if ($[26] !== defaultOpen || $[27] !== onOpenChange || $[28] !== open || $[29] !== t7 || $[30] !== t8) {
123
- t9 = /*#__PURE__*/_jsxs(Menu.Root, {
114
+ let t10;
115
+ if ($[22] !== defaultOpen || $[23] !== onOpenChange || $[24] !== open || $[25] !== t8 || $[26] !== t9) {
116
+ t10 = /*#__PURE__*/_jsxs(Menu.Root, {
124
117
  open: open,
125
118
  defaultOpen: defaultOpen,
126
119
  onOpenChange: onOpenChange,
127
- children: [t7, t8]
120
+ children: [t8, t9]
128
121
  });
129
- $[26] = defaultOpen;
130
- $[27] = onOpenChange;
131
- $[28] = open;
132
- $[29] = t7;
133
- $[30] = t8;
134
- $[31] = t9;
122
+ $[22] = defaultOpen;
123
+ $[23] = onOpenChange;
124
+ $[24] = open;
125
+ $[25] = t8;
126
+ $[26] = t9;
127
+ $[27] = t10;
135
128
  } else {
136
- t9 = $[31];
129
+ t10 = $[27];
137
130
  }
138
- return t9;
131
+ return t10;
139
132
  }
140
133
  /* The rows of the popup, apart from it so they render without an open menu. */
141
134
  export function ProjectSwitcherGroups(t0) {
@@ -0,0 +1,9 @@
1
+ import type { Ref } from 'react';
2
+ import { type TooltipSide } from './Tooltip.tsx';
3
+ export interface RelativeTimeProps {
4
+ at: Date | number | null | undefined;
5
+ tooltipSide?: TooltipSide;
6
+ className?: string;
7
+ ref?: Ref<HTMLElement>;
8
+ }
9
+ export declare function RelativeTime({ at, tooltipSide, className, ref }: RelativeTimeProps): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ import clsx from 'clsx';
2
+ import { formatAge, formatDayClockWithYear, useFormatLocale } from './format/index.js';
3
+ import { Tooltip } from "./Tooltip.js";
4
+ import { useNow } from './useNow.js';
5
+ /* The finest step of an age is a minute, so half of one keeps it at most thirty seconds behind. */
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ const TICK_MS = 30_000;
8
+ /* A moment as its age, `3m ago`, counting on while it is on screen, with the exact time on hover and on keyboard focus. */
9
+ export function RelativeTime({
10
+ at,
11
+ tooltipSide,
12
+ className,
13
+ ref
14
+ }) {
15
+ // The formatters read the locale from a module, not from an input, so a compiled render would keep its first words.
16
+ 'use no memo';
17
+
18
+ useFormatLocale();
19
+ const now = useNow(TICK_MS);
20
+ if (at === null || at === undefined || Number.isNaN(new Date(at).getTime())) {
21
+ return /*#__PURE__*/_jsx("span", {
22
+ ref: ref,
23
+ className: className,
24
+ children: "-"
25
+ });
26
+ }
27
+ return /*#__PURE__*/_jsx(Tooltip, {
28
+ label: formatDayClockWithYear(at),
29
+ side: tooltipSide,
30
+ children: /*#__PURE__*/_jsx("time", {
31
+ ref: ref,
32
+ dateTime: new Date(at).toISOString(),
33
+ tabIndex: 0,
34
+ className: clsx('focus-ring rounded-sm whitespace-nowrap', className),
35
+ children: formatAge(at, now)
36
+ })
37
+ });
38
+ }