@dowel-ui/react 0.2.0 → 0.4.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/README.md +14 -7
- package/dist/components/ai-agent-plan/ai-agent-plan.d.ts +50 -0
- package/dist/components/ai-agent-plan/ai-agent-plan.d.ts.map +1 -0
- package/dist/components/ai-agent-plan/ai-agent-plan.js +156 -0
- package/dist/components/ai-agent-plan/ai-agent-plan.js.map +1 -0
- package/dist/components/ai-agent-plan/index.d.ts +2 -0
- package/dist/components/ai-agent-plan/index.js +2 -0
- package/dist/components/ai-agent-plan/meta.js +17 -0
- package/dist/components/ai-agent-plan/meta.js.map +1 -0
- package/dist/components/ai-agent-status/ai-agent-status.d.ts +1 -1
- package/dist/components/ai-approval-request/ai-approval-request.d.ts +67 -0
- package/dist/components/ai-approval-request/ai-approval-request.d.ts.map +1 -0
- package/dist/components/ai-approval-request/ai-approval-request.js +218 -0
- package/dist/components/ai-approval-request/ai-approval-request.js.map +1 -0
- package/dist/components/ai-approval-request/index.d.ts +2 -0
- package/dist/components/ai-approval-request/index.js +2 -0
- package/dist/components/ai-approval-request/meta.js +17 -0
- package/dist/components/ai-approval-request/meta.js.map +1 -0
- package/dist/components/ai-disclosure/ai-disclosure.d.ts +92 -0
- package/dist/components/ai-disclosure/ai-disclosure.d.ts.map +1 -0
- package/dist/components/ai-disclosure/ai-disclosure.js +122 -0
- package/dist/components/ai-disclosure/ai-disclosure.js.map +1 -0
- package/dist/components/ai-disclosure/index.d.ts +2 -0
- package/dist/components/ai-disclosure/index.js +2 -0
- package/dist/components/ai-disclosure/meta.js +17 -0
- package/dist/components/ai-disclosure/meta.js.map +1 -0
- package/dist/components/ai-response/ai-response.d.ts.map +1 -1
- package/dist/components/ai-response/ai-response.js +1 -0
- package/dist/components/ai-response/ai-response.js.map +1 -1
- package/dist/components/ai-structured-output/ai-structured-output.d.ts.map +1 -1
- package/dist/components/ai-structured-output/ai-structured-output.js +13 -2
- package/dist/components/ai-structured-output/ai-structured-output.js.map +1 -1
- package/dist/components/ai-tool/ai-tool.d.ts +1 -1
- package/dist/components/alert/alert.d.ts +1 -1
- package/dist/components/avatar/avatar.d.ts +1 -1
- package/dist/components/badge/badge.d.ts +1 -1
- package/dist/components/button/button.d.ts +1 -1
- package/dist/components/date-picker/date-picker.js +1 -1
- package/dist/components/diff-viewer/diff-model.d.ts +69 -0
- package/dist/components/diff-viewer/diff-model.d.ts.map +1 -0
- package/dist/components/diff-viewer/diff-model.js +180 -0
- package/dist/components/diff-viewer/diff-model.js.map +1 -0
- package/dist/components/diff-viewer/diff-viewer.d.ts +42 -0
- package/dist/components/diff-viewer/diff-viewer.d.ts.map +1 -0
- package/dist/components/diff-viewer/diff-viewer.js +212 -0
- package/dist/components/diff-viewer/diff-viewer.js.map +1 -0
- package/dist/components/diff-viewer/index.d.ts +3 -0
- package/dist/components/diff-viewer/index.js +3 -0
- package/dist/components/diff-viewer/meta.js +17 -0
- package/dist/components/diff-viewer/meta.js.map +1 -0
- package/dist/components/file-upload/file-upload.d.ts +46 -0
- package/dist/components/file-upload/file-upload.d.ts.map +1 -0
- package/dist/components/file-upload/file-upload.js +204 -0
- package/dist/components/file-upload/file-upload.js.map +1 -0
- package/dist/components/file-upload/index.d.ts +3 -0
- package/dist/components/file-upload/index.js +3 -0
- package/dist/components/file-upload/meta.js +17 -0
- package/dist/components/file-upload/meta.js.map +1 -0
- package/dist/components/file-upload/upload-queue.d.ts +87 -0
- package/dist/components/file-upload/upload-queue.d.ts.map +1 -0
- package/dist/components/file-upload/upload-queue.js +249 -0
- package/dist/components/file-upload/upload-queue.js.map +1 -0
- package/dist/components/log-viewer/index.d.ts +3 -0
- package/dist/components/log-viewer/index.js +3 -0
- package/dist/components/log-viewer/log-stream.d.ts +68 -0
- package/dist/components/log-viewer/log-stream.d.ts.map +1 -0
- package/dist/components/log-viewer/log-stream.js +120 -0
- package/dist/components/log-viewer/log-stream.js.map +1 -0
- package/dist/components/log-viewer/log-viewer.d.ts +51 -0
- package/dist/components/log-viewer/log-viewer.d.ts.map +1 -0
- package/dist/components/log-viewer/log-viewer.js +284 -0
- package/dist/components/log-viewer/log-viewer.js.map +1 -0
- package/dist/components/log-viewer/meta.js +17 -0
- package/dist/components/log-viewer/meta.js.map +1 -0
- package/dist/components/progress/progress.d.ts +1 -1
- package/dist/components/progress/progress.js +1 -0
- package/dist/components/progress/progress.js.map +1 -1
- package/dist/components/spinner/spinner.d.ts +1 -1
- package/dist/components/spinner/spinner.js +1 -0
- package/dist/components/spinner/spinner.js.map +1 -1
- package/dist/components/tags-input/index.d.ts +2 -0
- package/dist/components/tags-input/index.js +2 -0
- package/dist/components/tags-input/meta.js +17 -0
- package/dist/components/tags-input/meta.js.map +1 -0
- package/dist/components/tags-input/tags-input.d.ts +47 -0
- package/dist/components/tags-input/tags-input.d.ts.map +1 -0
- package/dist/components/tags-input/tags-input.js +182 -0
- package/dist/components/tags-input/tags-input.js.map +1 -0
- package/dist/components/time-range-picker/index.d.ts +3 -0
- package/dist/components/time-range-picker/index.js +3 -0
- package/dist/components/time-range-picker/meta.js +22 -0
- package/dist/components/time-range-picker/meta.js.map +1 -0
- package/dist/components/time-range-picker/time-expression.d.ts +88 -0
- package/dist/components/time-range-picker/time-expression.d.ts.map +1 -0
- package/dist/components/time-range-picker/time-expression.js +335 -0
- package/dist/components/time-range-picker/time-expression.js.map +1 -0
- package/dist/components/time-range-picker/time-range-picker.d.ts +65 -0
- package/dist/components/time-range-picker/time-range-picker.d.ts.map +1 -0
- package/dist/components/time-range-picker/time-range-picker.js +377 -0
- package/dist/components/time-range-picker/time-range-picker.js.map +1 -0
- package/dist/components/toast/toast.d.ts +1 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +14 -2
- package/dist/registry/components.d.ts.map +1 -1
- package/dist/registry/components.js +66 -50
- package/dist/registry/components.js.map +1 -1
- package/package.json +6 -4
- package/src/components/ai-agent-plan/ai-agent-plan.tsx +0 -0
- package/src/components/ai-agent-plan/index.ts +10 -0
- package/src/components/ai-agent-plan/meta.ts +19 -0
- package/src/components/ai-approval-request/ai-approval-request.tsx +368 -0
- package/src/components/ai-approval-request/index.ts +7 -0
- package/src/components/ai-approval-request/meta.ts +21 -0
- package/src/components/ai-disclosure/ai-disclosure.tsx +244 -0
- package/src/components/ai-disclosure/index.ts +9 -0
- package/src/components/ai-disclosure/meta.ts +23 -0
- package/src/components/ai-response/ai-response.tsx +1 -0
- package/src/components/ai-structured-output/ai-structured-output.tsx +25 -1
- package/src/components/diff-viewer/diff-model.ts +256 -0
- package/src/components/diff-viewer/diff-viewer.tsx +339 -0
- package/src/components/diff-viewer/index.ts +17 -0
- package/src/components/diff-viewer/meta.ts +20 -0
- package/src/components/file-upload/file-upload.tsx +322 -0
- package/src/components/file-upload/index.ts +20 -0
- package/src/components/file-upload/meta.ts +21 -0
- package/src/components/file-upload/upload-queue.ts +337 -0
- package/src/components/log-viewer/index.ts +21 -0
- package/src/components/log-viewer/log-stream.ts +176 -0
- package/src/components/log-viewer/log-viewer.tsx +424 -0
- package/src/components/log-viewer/meta.ts +21 -0
- package/src/components/progress/progress.tsx +3 -0
- package/src/components/spinner/spinner.tsx +5 -0
- package/src/components/tags-input/index.ts +1 -0
- package/src/components/tags-input/meta.ts +23 -0
- package/src/components/tags-input/tags-input.tsx +300 -0
- package/src/components/time-range-picker/index.ts +25 -0
- package/src/components/time-range-picker/meta.ts +22 -0
- package/src/components/time-range-picker/time-expression.ts +491 -0
- package/src/components/time-range-picker/time-range-picker.tsx +522 -0
- package/src/index.ts +8 -0
- package/src/registry/components.ts +16 -0
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
//#region src/components/time-range-picker/time-expression.ts
|
|
2
|
+
/** Why an expression could not be resolved. Shown to the user verbatim. */
|
|
3
|
+
var TimeExpressionError = class extends Error {
|
|
4
|
+
input;
|
|
5
|
+
constructor(message, input) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.input = input;
|
|
8
|
+
this.name = "TimeExpressionError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
const UNIT_NAMES = {
|
|
12
|
+
s: "second",
|
|
13
|
+
m: "minute",
|
|
14
|
+
h: "hour",
|
|
15
|
+
d: "day",
|
|
16
|
+
w: "week",
|
|
17
|
+
M: "month",
|
|
18
|
+
y: "year"
|
|
19
|
+
};
|
|
20
|
+
const UNITS = new Set(Object.keys(UNIT_NAMES));
|
|
21
|
+
const PART_FORMATTERS = /* @__PURE__ */ new Map();
|
|
22
|
+
function formatterFor(timeZone) {
|
|
23
|
+
let formatter = PART_FORMATTERS.get(timeZone);
|
|
24
|
+
if (!formatter) {
|
|
25
|
+
formatter = new Intl.DateTimeFormat("en-US", {
|
|
26
|
+
timeZone,
|
|
27
|
+
hour12: false,
|
|
28
|
+
year: "numeric",
|
|
29
|
+
month: "2-digit",
|
|
30
|
+
day: "2-digit",
|
|
31
|
+
hour: "2-digit",
|
|
32
|
+
minute: "2-digit",
|
|
33
|
+
second: "2-digit"
|
|
34
|
+
});
|
|
35
|
+
PART_FORMATTERS.set(timeZone, formatter);
|
|
36
|
+
}
|
|
37
|
+
return formatter;
|
|
38
|
+
}
|
|
39
|
+
/** Wall-clock fields of an instant, as read in `timeZone`. */
|
|
40
|
+
function toParts(instant, timeZone) {
|
|
41
|
+
if (!timeZone) return {
|
|
42
|
+
year: instant.getFullYear(),
|
|
43
|
+
month: instant.getMonth() + 1,
|
|
44
|
+
day: instant.getDate(),
|
|
45
|
+
hour: instant.getHours(),
|
|
46
|
+
minute: instant.getMinutes(),
|
|
47
|
+
second: instant.getSeconds(),
|
|
48
|
+
ms: instant.getMilliseconds()
|
|
49
|
+
};
|
|
50
|
+
const found = {};
|
|
51
|
+
for (const part of formatterFor(timeZone).formatToParts(instant)) if (part.type !== "literal") found[part.type] = Number(part.value);
|
|
52
|
+
return {
|
|
53
|
+
year: found.year ?? 0,
|
|
54
|
+
month: found.month ?? 1,
|
|
55
|
+
day: found.day ?? 1,
|
|
56
|
+
hour: (found.hour ?? 0) % 24,
|
|
57
|
+
minute: found.minute ?? 0,
|
|
58
|
+
second: found.second ?? 0,
|
|
59
|
+
ms: instant.getMilliseconds()
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function asUtc(parts) {
|
|
63
|
+
return Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, parts.ms);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The instant at which `timeZone` reads these wall-clock fields.
|
|
67
|
+
*
|
|
68
|
+
* Two passes: guess the fields as UTC, measure the offset there, correct, and
|
|
69
|
+
* measure again in case the correction crossed a transition. A wall-clock time
|
|
70
|
+
* at a DST boundary can be ambiguous or absent; this picks one rather than
|
|
71
|
+
* throwing — an hour's difference once a year is a fair trade for not shipping
|
|
72
|
+
* a timezone database.
|
|
73
|
+
*/
|
|
74
|
+
function fromParts(parts, timeZone) {
|
|
75
|
+
if (!timeZone) return new Date(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second, parts.ms);
|
|
76
|
+
const wall = asUtc(parts);
|
|
77
|
+
let instant = new Date(wall);
|
|
78
|
+
for (let pass = 0; pass < 2; pass += 1) {
|
|
79
|
+
const offset = asUtc(toParts(instant, timeZone)) - instant.getTime();
|
|
80
|
+
const next = new Date(wall - offset);
|
|
81
|
+
if (next.getTime() === instant.getTime()) break;
|
|
82
|
+
instant = next;
|
|
83
|
+
}
|
|
84
|
+
return instant;
|
|
85
|
+
}
|
|
86
|
+
/** `month` is 1-based, matching `Parts`. */
|
|
87
|
+
function daysInMonth(year, month) {
|
|
88
|
+
return new Date(Date.UTC(year, month, 0)).getUTCDate();
|
|
89
|
+
}
|
|
90
|
+
const FIXED_MS = {
|
|
91
|
+
s: 1e3,
|
|
92
|
+
m: 6e4,
|
|
93
|
+
h: 36e5
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Applies `±N unit` to an instant.
|
|
97
|
+
*
|
|
98
|
+
* Seconds, minutes and hours are fixed durations. Days, weeks, months and years
|
|
99
|
+
* are calendar arithmetic: `now-1d` means the same wall-clock time yesterday,
|
|
100
|
+
* which on the day a zone changes offset is 23 or 25 hours, not 24. And
|
|
101
|
+
* `now-1M` from the 31st lands on the last day of a shorter month rather than
|
|
102
|
+
* overflowing into the next one.
|
|
103
|
+
*/
|
|
104
|
+
function applyOffset(instant, amount, unit, timeZone) {
|
|
105
|
+
const fixed = FIXED_MS[unit];
|
|
106
|
+
if (fixed !== void 0) return new Date(instant.getTime() + amount * fixed);
|
|
107
|
+
const parts = { ...toParts(instant, timeZone) };
|
|
108
|
+
if (unit === "d") parts.day += amount;
|
|
109
|
+
else if (unit === "w") parts.day += amount * 7;
|
|
110
|
+
else {
|
|
111
|
+
if (unit === "y") parts.year += amount;
|
|
112
|
+
else {
|
|
113
|
+
const target = parts.month - 1 + amount;
|
|
114
|
+
parts.year += Math.floor(target / 12);
|
|
115
|
+
parts.month = (target % 12 + 12) % 12 + 1;
|
|
116
|
+
}
|
|
117
|
+
parts.day = Math.min(parts.day, daysInMonth(parts.year, parts.month));
|
|
118
|
+
}
|
|
119
|
+
return fromParts(parts, timeZone);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Rounds an instant to a unit boundary, `edge` deciding which one.
|
|
123
|
+
*
|
|
124
|
+
* The detail that catches every reimplementation: `now/d..now/d` means *all of
|
|
125
|
+
* today*, so the start floors to midnight and the end climbs to that day's last
|
|
126
|
+
* millisecond. Flooring both yields a zero-length range and an empty chart.
|
|
127
|
+
*/
|
|
128
|
+
function applySnap(instant, unit, edge, timeZone) {
|
|
129
|
+
const parts = { ...toParts(instant, timeZone) };
|
|
130
|
+
parts.ms = 0;
|
|
131
|
+
if (unit !== "s") parts.second = 0;
|
|
132
|
+
if (unit !== "s" && unit !== "m") parts.minute = 0;
|
|
133
|
+
if (unit === "d" || unit === "w" || unit === "M" || unit === "y") parts.hour = 0;
|
|
134
|
+
if (unit === "M" || unit === "y") parts.day = 1;
|
|
135
|
+
if (unit === "y") parts.month = 1;
|
|
136
|
+
if (unit === "w") {
|
|
137
|
+
const weekday = new Date(Date.UTC(parts.year, parts.month - 1, parts.day)).getUTCDay();
|
|
138
|
+
parts.day -= (weekday + 6) % 7;
|
|
139
|
+
}
|
|
140
|
+
const start = fromParts(parts, timeZone);
|
|
141
|
+
if (edge === "start") return start;
|
|
142
|
+
const nextStart = applyOffset(start, 1, unit, timeZone);
|
|
143
|
+
return /* @__PURE__ */ new Date(nextStart.getTime() - 1);
|
|
144
|
+
}
|
|
145
|
+
const ISO = /^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:?\d{2})?)?$/;
|
|
146
|
+
const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
147
|
+
const CALENDAR_DATE = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
148
|
+
const OFFSET = /^([+-])(\d+)([smhdwMy])/;
|
|
149
|
+
/** Resolves a single side of a range. Exported for the input's live feedback. */
|
|
150
|
+
function resolveExpression(expression, edge, options) {
|
|
151
|
+
const input = expression.trim();
|
|
152
|
+
if (input === "") throw new TimeExpressionError("Enter a time.", input);
|
|
153
|
+
if (ISO.test(input)) {
|
|
154
|
+
const calendar = CALENDAR_DATE.exec(input);
|
|
155
|
+
if (calendar && (Number(calendar[2]) < 1 || Number(calendar[2]) > 12 || Number(calendar[3]) < 1 || Number(calendar[3]) > daysInMonth(Number(calendar[1]), Number(calendar[2])))) throw new TimeExpressionError(`${input} is not a real date.`, input);
|
|
156
|
+
const dateOnly = DATE_ONLY.exec(input);
|
|
157
|
+
if (dateOnly) {
|
|
158
|
+
const start = fromParts({
|
|
159
|
+
year: Number(dateOnly[1]),
|
|
160
|
+
month: Number(dateOnly[2]),
|
|
161
|
+
day: Number(dateOnly[3]),
|
|
162
|
+
hour: 0,
|
|
163
|
+
minute: 0,
|
|
164
|
+
second: 0,
|
|
165
|
+
ms: 0
|
|
166
|
+
}, options.timeZone);
|
|
167
|
+
if (Number.isNaN(start.getTime())) throw new TimeExpressionError(`${input} is not a real date.`, input);
|
|
168
|
+
return edge === "start" ? start : applySnap(start, "d", "end", options.timeZone);
|
|
169
|
+
}
|
|
170
|
+
const parsed = new Date(input);
|
|
171
|
+
if (Number.isNaN(parsed.getTime())) throw new TimeExpressionError(`${input} is not a real date.`, input);
|
|
172
|
+
return parsed;
|
|
173
|
+
}
|
|
174
|
+
if (!input.startsWith("now")) throw new TimeExpressionError(`Expected "now" or a date like 2026-01-31, not "${input}".`, input);
|
|
175
|
+
let rest = input.slice(3);
|
|
176
|
+
let instant = options.now;
|
|
177
|
+
while (rest.length > 0 && rest[0] !== "/") {
|
|
178
|
+
const match = OFFSET.exec(rest);
|
|
179
|
+
if (!match) throw new TimeExpressionError(`Expected an offset like -6h, not "${rest}".`, rest);
|
|
180
|
+
const [whole, sign, digits, unit] = match;
|
|
181
|
+
const amount = Number(digits) * (sign === "-" ? -1 : 1);
|
|
182
|
+
instant = applyOffset(instant, amount, unit, options.timeZone);
|
|
183
|
+
rest = rest.slice(whole.length);
|
|
184
|
+
}
|
|
185
|
+
if (rest.startsWith("/")) {
|
|
186
|
+
const unit = rest.slice(1);
|
|
187
|
+
if (!UNITS.has(unit)) throw new TimeExpressionError(`"${unit || "/"}" is not a unit. Use s, m, h, d, w, M or y.`, rest);
|
|
188
|
+
instant = applySnap(instant, unit, edge, options.timeZone);
|
|
189
|
+
}
|
|
190
|
+
return instant;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Turns a range expression into the two instants a query needs.
|
|
194
|
+
*
|
|
195
|
+
* Throws `TimeExpressionError` rather than returning a fallback range: a chart
|
|
196
|
+
* silently showing the wrong window is worse than one that says it cannot.
|
|
197
|
+
*/
|
|
198
|
+
function resolveTimeRange(expression, options) {
|
|
199
|
+
const separator = expression.indexOf("..");
|
|
200
|
+
if (separator === -1) throw new TimeExpressionError(`Expected two times separated by "..", as in now-6h..now.`, expression);
|
|
201
|
+
const from = resolveExpression(expression.slice(0, separator), "start", options);
|
|
202
|
+
const to = resolveExpression(expression.slice(separator + 2), "end", options);
|
|
203
|
+
if (from.getTime() > to.getTime()) throw new TimeExpressionError("The range ends before it starts.", expression);
|
|
204
|
+
return {
|
|
205
|
+
from,
|
|
206
|
+
to
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
/** Whether an expression resolves, without the caller writing a try/catch. */
|
|
210
|
+
function isValidTimeRange(expression, options) {
|
|
211
|
+
try {
|
|
212
|
+
resolveTimeRange(expression, options);
|
|
213
|
+
return true;
|
|
214
|
+
} catch {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** Relative windows first, for debugging; calendar periods after, for reporting. */
|
|
219
|
+
const DEFAULT_PRESETS = [
|
|
220
|
+
{
|
|
221
|
+
expression: "now-5m..now",
|
|
222
|
+
label: "Last 5 minutes",
|
|
223
|
+
group: "Relative"
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
expression: "now-15m..now",
|
|
227
|
+
label: "Last 15 minutes",
|
|
228
|
+
group: "Relative"
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
expression: "now-1h..now",
|
|
232
|
+
label: "Last hour",
|
|
233
|
+
group: "Relative"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
expression: "now-6h..now",
|
|
237
|
+
label: "Last 6 hours",
|
|
238
|
+
group: "Relative"
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
expression: "now-24h..now",
|
|
242
|
+
label: "Last 24 hours",
|
|
243
|
+
group: "Relative"
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
expression: "now-7d..now",
|
|
247
|
+
label: "Last 7 days",
|
|
248
|
+
group: "Relative"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
expression: "now-30d..now",
|
|
252
|
+
label: "Last 30 days",
|
|
253
|
+
group: "Relative"
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
expression: "now/d..now/d",
|
|
257
|
+
label: "Today",
|
|
258
|
+
group: "Calendar"
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
expression: "now-1d/d..now-1d/d",
|
|
262
|
+
label: "Yesterday",
|
|
263
|
+
group: "Calendar"
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
expression: "now/w..now/w",
|
|
267
|
+
label: "This week",
|
|
268
|
+
group: "Calendar"
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
expression: "now-1w/w..now-1w/w",
|
|
272
|
+
label: "Last week",
|
|
273
|
+
group: "Calendar"
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
expression: "now/M..now/M",
|
|
277
|
+
label: "This month",
|
|
278
|
+
group: "Calendar"
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
expression: "now-1M/M..now-1M/M",
|
|
282
|
+
label: "Last month",
|
|
283
|
+
group: "Calendar"
|
|
284
|
+
}
|
|
285
|
+
];
|
|
286
|
+
const RELATIVE = /^now-(\d+)([smhdwMy])(?:\/[smhdwMy])?\.\.now$/;
|
|
287
|
+
/**
|
|
288
|
+
* A sentence for the trigger.
|
|
289
|
+
*
|
|
290
|
+
* An unnamed relative range is described in its own terms — "Last 90 minutes" —
|
|
291
|
+
* because showing it as two timestamps throws away what the user chose. Only an
|
|
292
|
+
* absolute range, which has no relative meaning left, is shown as dates.
|
|
293
|
+
*/
|
|
294
|
+
function describeTimeRange(expression, options) {
|
|
295
|
+
const preset = (options.presets ?? DEFAULT_PRESETS).find((candidate) => candidate.expression === expression);
|
|
296
|
+
if (preset) return preset.label;
|
|
297
|
+
const relative = RELATIVE.exec(expression.trim());
|
|
298
|
+
if (relative) {
|
|
299
|
+
const amount = Number(relative[1]);
|
|
300
|
+
const unit = UNIT_NAMES[relative[2]];
|
|
301
|
+
return `Last ${String(amount)} ${unit}${amount === 1 ? "" : "s"}`;
|
|
302
|
+
}
|
|
303
|
+
try {
|
|
304
|
+
const { from, to } = resolveTimeRange(expression, options);
|
|
305
|
+
const format = new Intl.DateTimeFormat(options.locale, {
|
|
306
|
+
dateStyle: "medium",
|
|
307
|
+
timeStyle: "short",
|
|
308
|
+
timeZone: options.timeZone
|
|
309
|
+
});
|
|
310
|
+
return `${format.format(from)} – ${format.format(to)}`;
|
|
311
|
+
} catch {
|
|
312
|
+
return expression;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
/** The resolved window, spelled out. Pairs with the relative label, not replaces it. */
|
|
316
|
+
function formatResolvedRange(range, options = {}) {
|
|
317
|
+
const format = new Intl.DateTimeFormat(options.locale, {
|
|
318
|
+
dateStyle: "medium",
|
|
319
|
+
timeStyle: "medium",
|
|
320
|
+
timeZone: options.timeZone
|
|
321
|
+
});
|
|
322
|
+
return `${format.format(range.from)} – ${format.format(range.to)}`;
|
|
323
|
+
}
|
|
324
|
+
/** Builds the expression for an absolute range chosen on a calendar. */
|
|
325
|
+
function absoluteExpression(from, to) {
|
|
326
|
+
return `${toLocalIso(from)}..${toLocalIso(to)}`;
|
|
327
|
+
}
|
|
328
|
+
function toLocalIso(date) {
|
|
329
|
+
const pad = (value, width = 2) => String(value).padStart(width, "0");
|
|
330
|
+
return `${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
|
331
|
+
}
|
|
332
|
+
//#endregion
|
|
333
|
+
export { DEFAULT_PRESETS, TimeExpressionError, absoluteExpression, describeTimeRange, formatResolvedRange, isValidTimeRange, resolveExpression, resolveTimeRange };
|
|
334
|
+
|
|
335
|
+
//# sourceMappingURL=time-expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-expression.js","names":[],"sources":["../../../src/components/time-range-picker/time-expression.ts"],"sourcesContent":["/**\n * Time ranges as expressions, not as two frozen timestamps.\n *\n * `now-6h` is still the last six hours tomorrow; a resolved pair is six hours\n * of last Tuesday forever. Storing the expression is what lets a dashboard URL\n * survive being bookmarked and reloaded.\n *\n * The grammar is a deliberate subset of the one Grafana, Datadog and Kibana\n * converged on, so the strings are already familiar:\n *\n * range := expr \"..\" expr\n * expr := \"now\" offset* snap? | ISO-8601\n * offset := (\"-\" | \"+\") digits unit\n * snap := \"/\" unit\n * unit := s | m | h | d | w | M | y\n *\n * now-6h..now the last six hours\n * now-6h/h..now the last six hours, starting on the hour\n * now/d..now/d today, all of it\n * now-1d/d..now-1d/d yesterday\n * 2026-01-01..now since a fixed instant\n *\n * Everything here is pure and takes `now` as an argument: reading the clock\n * during render would disagree between the server and the browser.\n */\n\nexport type TimeUnit = \"s\" | \"m\" | \"h\" | \"d\" | \"w\" | \"M\" | \"y\";\n\nexport interface ResolveOptions {\n /** The instant `now` refers to. Required, so nothing here reads the clock. */\n now: Date;\n /**\n * IANA zone the snapping happens in — \"start of day\" is a different instant\n * in Auckland than in Denver. Defaults to the runtime zone.\n */\n timeZone?: string;\n}\n\nexport interface ResolvedRange {\n from: Date;\n to: Date;\n}\n\n/** Why an expression could not be resolved. Shown to the user verbatim. */\nexport class TimeExpressionError extends Error {\n constructor(\n message: string,\n /** The part of the input at fault, for pointing at it. */\n readonly input: string,\n ) {\n super(message);\n this.name = \"TimeExpressionError\";\n }\n}\n\nconst UNIT_NAMES: Record<TimeUnit, string> = {\n s: \"second\",\n m: \"minute\",\n h: \"hour\",\n d: \"day\",\n w: \"week\",\n M: \"month\",\n y: \"year\",\n};\n\nconst UNITS = new Set(Object.keys(UNIT_NAMES) as TimeUnit[]);\n\n/* ------------------------------------------------------------------ */\n/* Zone arithmetic */\n/* ------------------------------------------------------------------ */\n\ninterface Parts {\n year: number;\n month: number;\n day: number;\n hour: number;\n minute: number;\n second: number;\n ms: number;\n}\n\nconst PART_FORMATTERS = new Map<string, Intl.DateTimeFormat>();\n\nfunction formatterFor(timeZone: string): Intl.DateTimeFormat {\n let formatter = PART_FORMATTERS.get(timeZone);\n if (!formatter) {\n formatter = new Intl.DateTimeFormat(\"en-US\", {\n timeZone,\n hour12: false,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n PART_FORMATTERS.set(timeZone, formatter);\n }\n return formatter;\n}\n\n/** Wall-clock fields of an instant, as read in `timeZone`. */\nfunction toParts(instant: Date, timeZone?: string): Parts {\n if (!timeZone) {\n return {\n year: instant.getFullYear(),\n month: instant.getMonth() + 1,\n day: instant.getDate(),\n hour: instant.getHours(),\n minute: instant.getMinutes(),\n second: instant.getSeconds(),\n ms: instant.getMilliseconds(),\n };\n }\n\n const found: Record<string, number> = {};\n for (const part of formatterFor(timeZone).formatToParts(instant)) {\n if (part.type !== \"literal\") found[part.type] = Number(part.value);\n }\n return {\n year: found.year ?? 0,\n month: found.month ?? 1,\n day: found.day ?? 1,\n // Midnight formats as hour 24 rather than 0 in some engines.\n hour: (found.hour ?? 0) % 24,\n minute: found.minute ?? 0,\n second: found.second ?? 0,\n ms: instant.getMilliseconds(),\n };\n}\n\nfunction asUtc(parts: Parts): number {\n return Date.UTC(\n parts.year,\n parts.month - 1,\n parts.day,\n parts.hour,\n parts.minute,\n parts.second,\n parts.ms,\n );\n}\n\n/**\n * The instant at which `timeZone` reads these wall-clock fields.\n *\n * Two passes: guess the fields as UTC, measure the offset there, correct, and\n * measure again in case the correction crossed a transition. A wall-clock time\n * at a DST boundary can be ambiguous or absent; this picks one rather than\n * throwing — an hour's difference once a year is a fair trade for not shipping\n * a timezone database.\n */\nfunction fromParts(parts: Parts, timeZone?: string): Date {\n if (!timeZone) {\n return new Date(\n parts.year,\n parts.month - 1,\n parts.day,\n parts.hour,\n parts.minute,\n parts.second,\n parts.ms,\n );\n }\n\n const wall = asUtc(parts);\n let instant = new Date(wall);\n for (let pass = 0; pass < 2; pass += 1) {\n const offset = asUtc(toParts(instant, timeZone)) - instant.getTime();\n const next = new Date(wall - offset);\n if (next.getTime() === instant.getTime()) break;\n instant = next;\n }\n return instant;\n}\n\n/* ------------------------------------------------------------------ */\n/* Offsets and snapping */\n/* ------------------------------------------------------------------ */\n\n/** `month` is 1-based, matching `Parts`. */\nfunction daysInMonth(year: number, month: number): number {\n return new Date(Date.UTC(year, month, 0)).getUTCDate();\n}\n\nconst FIXED_MS: Partial<Record<TimeUnit, number>> = {\n s: 1000,\n m: 60_000,\n h: 3_600_000,\n};\n\n/**\n * Applies `±N unit` to an instant.\n *\n * Seconds, minutes and hours are fixed durations. Days, weeks, months and years\n * are calendar arithmetic: `now-1d` means the same wall-clock time yesterday,\n * which on the day a zone changes offset is 23 or 25 hours, not 24. And\n * `now-1M` from the 31st lands on the last day of a shorter month rather than\n * overflowing into the next one.\n */\nfunction applyOffset(instant: Date, amount: number, unit: TimeUnit, timeZone?: string): Date {\n const fixed = FIXED_MS[unit];\n if (fixed !== undefined) return new Date(instant.getTime() + amount * fixed);\n\n const parts = { ...toParts(instant, timeZone) };\n\n if (unit === \"d\") {\n parts.day += amount;\n } else if (unit === \"w\") {\n parts.day += amount * 7;\n } else {\n if (unit === \"y\") {\n parts.year += amount;\n } else {\n const target = parts.month - 1 + amount;\n parts.year += Math.floor(target / 12);\n parts.month = (((target % 12) + 12) % 12) + 1;\n }\n // Clamp instead of overflowing: 31 January minus one month is the end of\n // February, not the third of March, and a year back from 29 February is\n // the 28th rather than 1 March.\n parts.day = Math.min(parts.day, daysInMonth(parts.year, parts.month));\n }\n\n return fromParts(parts, timeZone);\n}\n\n/**\n * Rounds an instant to a unit boundary, `edge` deciding which one.\n *\n * The detail that catches every reimplementation: `now/d..now/d` means *all of\n * today*, so the start floors to midnight and the end climbs to that day's last\n * millisecond. Flooring both yields a zero-length range and an empty chart.\n */\nfunction applySnap(\n instant: Date,\n unit: TimeUnit,\n edge: \"start\" | \"end\",\n timeZone?: string,\n): Date {\n const parts = { ...toParts(instant, timeZone) };\n\n parts.ms = 0;\n if (unit !== \"s\") parts.second = 0;\n if (unit !== \"s\" && unit !== \"m\") parts.minute = 0;\n if (unit === \"d\" || unit === \"w\" || unit === \"M\" || unit === \"y\") parts.hour = 0;\n if (unit === \"M\" || unit === \"y\") parts.day = 1;\n if (unit === \"y\") parts.month = 1;\n\n if (unit === \"w\") {\n // ISO weeks: Monday starts the week. Locale-varying week starts are a\n // separate decision, and one an app should make once rather than per range.\n //\n // The weekday is read off the calendar date itself, not off the instant —\n // asking a `Date` built from local fields for `getDay()` would answer for\n // the runtime's zone rather than the one being snapped in.\n const weekday = new Date(Date.UTC(parts.year, parts.month - 1, parts.day)).getUTCDay();\n parts.day -= (weekday + 6) % 7;\n }\n\n const start = fromParts(parts, timeZone);\n if (edge === \"start\") return start;\n\n // The end of the unit is one millisecond before the next one begins, which\n // keeps `to` inclusive without an off-by-one at every boundary.\n const nextStart = applyOffset(start, 1, unit, timeZone);\n return new Date(nextStart.getTime() - 1);\n}\n\n/* ------------------------------------------------------------------ */\n/* Parsing */\n/* ------------------------------------------------------------------ */\n\nconst ISO =\n /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d{1,3})?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\nconst DATE_ONLY = /^(\\d{4})-(\\d{2})-(\\d{2})$/;\nconst CALENDAR_DATE = /^(\\d{4})-(\\d{2})-(\\d{2})/;\nconst OFFSET = /^([+-])(\\d+)([smhdwMy])/;\n\n/** Resolves a single side of a range. Exported for the input's live feedback. */\nexport function resolveExpression(\n expression: string,\n edge: \"start\" | \"end\",\n options: ResolveOptions,\n): Date {\n const input = expression.trim();\n if (input === \"\") throw new TimeExpressionError(\"Enter a time.\", input);\n\n if (ISO.test(input)) {\n // `new Date` rolls an impossible day forward rather than refusing it —\n // 30 February parses happily as 2 March — so the calendar date is checked\n // against the month before the string is trusted.\n const calendar = CALENDAR_DATE.exec(input);\n if (\n calendar &&\n (Number(calendar[2]) < 1 ||\n Number(calendar[2]) > 12 ||\n Number(calendar[3]) < 1 ||\n Number(calendar[3]) > daysInMonth(Number(calendar[1]), Number(calendar[2])))\n ) {\n throw new TimeExpressionError(`${input} is not a real date.`, input);\n }\n\n const dateOnly = DATE_ONLY.exec(input);\n if (dateOnly) {\n // A bare date means a day in the reader's zone, not midnight UTC, and on\n // the closing side it means the whole of that day. `2026-01-01..2026-01-31`\n // that stops at the 31st's first millisecond silently drops a day of data.\n const parts: Parts = {\n year: Number(dateOnly[1]),\n month: Number(dateOnly[2]),\n day: Number(dateOnly[3]),\n hour: 0,\n minute: 0,\n second: 0,\n ms: 0,\n };\n const start = fromParts(parts, options.timeZone);\n if (Number.isNaN(start.getTime())) {\n throw new TimeExpressionError(`${input} is not a real date.`, input);\n }\n return edge === \"start\" ? start : applySnap(start, \"d\", \"end\", options.timeZone);\n }\n\n const parsed = new Date(input);\n if (Number.isNaN(parsed.getTime())) {\n throw new TimeExpressionError(`${input} is not a real date.`, input);\n }\n return parsed;\n }\n\n if (!input.startsWith(\"now\")) {\n throw new TimeExpressionError(\n `Expected \"now\" or a date like 2026-01-31, not \"${input}\".`,\n input,\n );\n }\n\n let rest = input.slice(3);\n let instant = options.now;\n\n while (rest.length > 0 && rest[0] !== \"/\") {\n const match = OFFSET.exec(rest);\n if (!match) {\n throw new TimeExpressionError(`Expected an offset like -6h, not \"${rest}\".`, rest);\n }\n const [whole, sign, digits, unit] = match;\n const amount = Number(digits) * (sign === \"-\" ? -1 : 1);\n instant = applyOffset(instant, amount, unit as TimeUnit, options.timeZone);\n rest = rest.slice(whole.length);\n }\n\n if (rest.startsWith(\"/\")) {\n const unit = rest.slice(1);\n if (!UNITS.has(unit as TimeUnit)) {\n throw new TimeExpressionError(\n `\"${unit || \"/\"}\" is not a unit. Use s, m, h, d, w, M or y.`,\n rest,\n );\n }\n instant = applySnap(instant, unit as TimeUnit, edge, options.timeZone);\n }\n\n return instant;\n}\n\n/**\n * Turns a range expression into the two instants a query needs.\n *\n * Throws `TimeExpressionError` rather than returning a fallback range: a chart\n * silently showing the wrong window is worse than one that says it cannot.\n */\nexport function resolveTimeRange(expression: string, options: ResolveOptions): ResolvedRange {\n const separator = expression.indexOf(\"..\");\n if (separator === -1) {\n throw new TimeExpressionError(\n `Expected two times separated by \"..\", as in now-6h..now.`,\n expression,\n );\n }\n\n const from = resolveExpression(expression.slice(0, separator), \"start\", options);\n const to = resolveExpression(expression.slice(separator + 2), \"end\", options);\n\n if (from.getTime() > to.getTime()) {\n throw new TimeExpressionError(\"The range ends before it starts.\", expression);\n }\n\n return { from, to };\n}\n\n/** Whether an expression resolves, without the caller writing a try/catch. */\nexport function isValidTimeRange(expression: string, options: ResolveOptions): boolean {\n try {\n resolveTimeRange(expression, options);\n return true;\n } catch {\n return false;\n }\n}\n\n/* ------------------------------------------------------------------ */\n/* Presets and labelling */\n/* ------------------------------------------------------------------ */\n\nexport interface TimeRangePreset {\n expression: string;\n label: string;\n /** Groups presets under a heading in the picker. */\n group: string;\n}\n\n/** Relative windows first, for debugging; calendar periods after, for reporting. */\nexport const DEFAULT_PRESETS: TimeRangePreset[] = [\n { expression: \"now-5m..now\", label: \"Last 5 minutes\", group: \"Relative\" },\n { expression: \"now-15m..now\", label: \"Last 15 minutes\", group: \"Relative\" },\n { expression: \"now-1h..now\", label: \"Last hour\", group: \"Relative\" },\n { expression: \"now-6h..now\", label: \"Last 6 hours\", group: \"Relative\" },\n { expression: \"now-24h..now\", label: \"Last 24 hours\", group: \"Relative\" },\n { expression: \"now-7d..now\", label: \"Last 7 days\", group: \"Relative\" },\n { expression: \"now-30d..now\", label: \"Last 30 days\", group: \"Relative\" },\n { expression: \"now/d..now/d\", label: \"Today\", group: \"Calendar\" },\n { expression: \"now-1d/d..now-1d/d\", label: \"Yesterday\", group: \"Calendar\" },\n { expression: \"now/w..now/w\", label: \"This week\", group: \"Calendar\" },\n { expression: \"now-1w/w..now-1w/w\", label: \"Last week\", group: \"Calendar\" },\n { expression: \"now/M..now/M\", label: \"This month\", group: \"Calendar\" },\n { expression: \"now-1M/M..now-1M/M\", label: \"Last month\", group: \"Calendar\" },\n];\n\nconst RELATIVE = /^now-(\\d+)([smhdwMy])(?:\\/[smhdwMy])?\\.\\.now$/;\n\n/**\n * A sentence for the trigger.\n *\n * An unnamed relative range is described in its own terms — \"Last 90 minutes\" —\n * because showing it as two timestamps throws away what the user chose. Only an\n * absolute range, which has no relative meaning left, is shown as dates.\n */\nexport function describeTimeRange(\n expression: string,\n options: ResolveOptions & { locale?: string; presets?: TimeRangePreset[] },\n): string {\n const presets = options.presets ?? DEFAULT_PRESETS;\n const preset = presets.find((candidate) => candidate.expression === expression);\n if (preset) return preset.label;\n\n const relative = RELATIVE.exec(expression.trim());\n if (relative) {\n const amount = Number(relative[1]);\n const unit = UNIT_NAMES[relative[2] as TimeUnit];\n return `Last ${String(amount)} ${unit}${amount === 1 ? \"\" : \"s\"}`;\n }\n\n try {\n const { from, to } = resolveTimeRange(expression, options);\n const format = new Intl.DateTimeFormat(options.locale, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n timeZone: options.timeZone,\n });\n return `${format.format(from)} – ${format.format(to)}`;\n } catch {\n return expression;\n }\n}\n\n/** The resolved window, spelled out. Pairs with the relative label, not replaces it. */\nexport function formatResolvedRange(\n range: ResolvedRange,\n options: { locale?: string; timeZone?: string } = {},\n): string {\n const format = new Intl.DateTimeFormat(options.locale, {\n dateStyle: \"medium\",\n timeStyle: \"medium\",\n timeZone: options.timeZone,\n });\n return `${format.format(range.from)} – ${format.format(range.to)}`;\n}\n\n/** Builds the expression for an absolute range chosen on a calendar. */\nexport function absoluteExpression(from: Date, to: Date): string {\n return `${toLocalIso(from)}..${toLocalIso(to)}`;\n}\n\nfunction toLocalIso(date: Date): string {\n const pad = (value: number, width = 2) => String(value).padStart(width, \"0\");\n return (\n `${String(date.getFullYear())}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` +\n `T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { PopoverContent } from "../popover/popover.js";
|
|
2
|
+
import "../popover/index.js";
|
|
3
|
+
import { TimeRangePreset } from "./time-expression.js";
|
|
4
|
+
import { ComponentPropsWithRef, ReactNode } from "react";
|
|
5
|
+
//#region src/components/time-range-picker/time-range-picker.d.ts
|
|
6
|
+
interface TimeRangeProps {
|
|
7
|
+
/** The expression, e.g. `now-6h..now`. Controlled. */
|
|
8
|
+
value?: string;
|
|
9
|
+
defaultValue?: string;
|
|
10
|
+
onValueChange?: (expression: string) => void;
|
|
11
|
+
/**
|
|
12
|
+
* The instant relative expressions are measured from. Supplied by the
|
|
13
|
+
* consumer so nothing here reads the clock during render. Pass a value that
|
|
14
|
+
* changes when you refresh; a stable one holds the window still between them.
|
|
15
|
+
*/
|
|
16
|
+
now?: Date;
|
|
17
|
+
/** IANA zone the snapping happens in. Defaults to the runtime zone. */
|
|
18
|
+
timeZone?: string;
|
|
19
|
+
locale?: string;
|
|
20
|
+
presets?: TimeRangePreset[];
|
|
21
|
+
open?: boolean;
|
|
22
|
+
onOpenChange?: (open: boolean) => void;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
declare function TimeRange({ value: valueProp, defaultValue, onValueChange, now, timeZone, locale, presets, open: openProp, onOpenChange, children }: TimeRangeProps): import("react").JSX.Element;
|
|
26
|
+
interface TimeRangeTriggerProps extends ComponentPropsWithRef<"button"> {
|
|
27
|
+
/** Shows the resolved window under the label. Off by default; it is long. */
|
|
28
|
+
showResolved?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The button, named by the range it holds: "Last 6 hours", not two timestamps.
|
|
32
|
+
* The resolved window stays reachable as the title, and as `showResolved`.
|
|
33
|
+
*/
|
|
34
|
+
declare function TimeRangeTrigger({ className, showResolved, ...props }: TimeRangeTriggerProps): import("react").JSX.Element;
|
|
35
|
+
interface TimeRangeContentProps extends ComponentPropsWithRef<typeof PopoverContent> {
|
|
36
|
+
/** Names the dialog. */
|
|
37
|
+
label?: string;
|
|
38
|
+
}
|
|
39
|
+
declare function TimeRangeContent({ className, label, children, ...props }: TimeRangeContentProps): import("react").JSX.Element;
|
|
40
|
+
/**
|
|
41
|
+
* The named ranges, which is how most of these are actually used. Buttons
|
|
42
|
+
* rather than a radiogroup: choosing one both selects and dismisses, which is a
|
|
43
|
+
* command, and `aria-pressed` still carries which one matches.
|
|
44
|
+
*/
|
|
45
|
+
declare function TimeRangePresets({ className, ...props }: Omit<ComponentPropsWithRef<"div">, "children">): import("react").JSX.Element;
|
|
46
|
+
/**
|
|
47
|
+
* Raw expression entry, with the resolved window shown as you type — the escape
|
|
48
|
+
* hatch that keeps the presets from being a ceiling.
|
|
49
|
+
*
|
|
50
|
+
* An expression that does not parse says why and is not applied: a chart
|
|
51
|
+
* quietly re-scoping itself to a window nobody asked for is worse than one that
|
|
52
|
+
* refuses.
|
|
53
|
+
*/
|
|
54
|
+
declare function TimeRangeExpression({ className, ...props }: Omit<ComponentPropsWithRef<"div">, "children">): import("react").JSX.Element;
|
|
55
|
+
/**
|
|
56
|
+
* The absolute path, for a window with no relative meaning. Picking dates
|
|
57
|
+
* writes a concrete expression: "1 to 7 March" does not become relative just
|
|
58
|
+
* because it was chosen here.
|
|
59
|
+
*/
|
|
60
|
+
declare function TimeRangeCalendar({ className, numberOfMonths, ...props }: Omit<ComponentPropsWithRef<"div">, "children"> & {
|
|
61
|
+
numberOfMonths?: number;
|
|
62
|
+
}): import("react").JSX.Element;
|
|
63
|
+
//#endregion
|
|
64
|
+
export { TimeRange, TimeRangeCalendar, TimeRangeContent, TimeRangeContentProps, TimeRangeExpression, TimeRangePresets, TimeRangeProps, TimeRangeTrigger, TimeRangeTriggerProps };
|
|
65
|
+
//# sourceMappingURL=time-range-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time-range-picker.d.ts","names":[],"sources":["../../../src/components/time-range-picker/time-range-picker.tsx"],"mappings":";;;;;UA8FiB;;EAEf;EACA;EACA,iBAAiB;;;;;;EAMjB,MAAM;;EAEN;EACA;EACA,UAAU;EACV;EACA,gBAAgB;EAChB,UAAU;;iBAGI,YACd,OAAO,WACP,cACA,eACA,KACA,UACA,QACA,SACA,MAAM,UACN,cACA,YACC,iCAAc,IAAA;UAoGA,8BAA8B;;EAE7C;;;;;;iBAOc,mBACd,WACA,iBACG,SACF,wCAAqB,IAAA;UAkCP,8BAA8B,6BAA6B;;EAE1E;;iBAGc,mBACd,WACA,OACA,aACG,SACF,wCAAqB,IAAA;;;;;;iBAwBR,mBACd,cACG,SACF,KAAK,4DAAyC,IAAA;;;;;;;;;iBA6DjC,sBACd,cACG,SACF,KAAK,4DAAyC,IAAA;;;;;;iBAiGjC,oBACd,WACA,mBACG,SACF,KAAK;EAA8C;oBAAyB,IAAA"}
|