@dowel-ui/react 0.3.0 → 0.5.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 +13 -8
- 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-extraction-review/ai-extraction-review.d.ts +52 -0
- package/dist/components/ai-extraction-review/ai-extraction-review.d.ts.map +1 -0
- package/dist/components/ai-extraction-review/ai-extraction-review.js +249 -0
- package/dist/components/ai-extraction-review/ai-extraction-review.js.map +1 -0
- package/dist/components/ai-extraction-review/extraction-model.d.ts +94 -0
- package/dist/components/ai-extraction-review/extraction-model.d.ts.map +1 -0
- package/dist/components/ai-extraction-review/extraction-model.js +91 -0
- package/dist/components/ai-extraction-review/extraction-model.js.map +1 -0
- package/dist/components/ai-extraction-review/index.d.ts +3 -0
- package/dist/components/ai-extraction-review/index.js +3 -0
- package/dist/components/ai-extraction-review/meta.js +17 -0
- package/dist/components/ai-extraction-review/meta.js.map +1 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.d.ts +72 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.d.ts.map +1 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.js +126 -0
- package/dist/components/ai-suggested-value/ai-suggested-value.js.map +1 -0
- package/dist/components/ai-suggested-value/index.d.ts +2 -0
- package/dist/components/ai-suggested-value/index.js +2 -0
- package/dist/components/ai-suggested-value/meta.js +17 -0
- package/dist/components/ai-suggested-value/meta.js.map +1 -0
- 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/confirm-typed/confirm-typed.d.ts +47 -0
- package/dist/components/confirm-typed/confirm-typed.d.ts.map +1 -0
- package/dist/components/confirm-typed/confirm-typed.js +106 -0
- package/dist/components/confirm-typed/confirm-typed.js.map +1 -0
- package/dist/components/confirm-typed/index.d.ts +2 -0
- package/dist/components/confirm-typed/index.js +2 -0
- package/dist/components/confirm-typed/meta.js +17 -0
- package/dist/components/confirm-typed/meta.js.map +1 -0
- package/dist/components/cron-editor/cron-editor.d.ts +36 -0
- package/dist/components/cron-editor/cron-editor.d.ts.map +1 -0
- package/dist/components/cron-editor/cron-editor.js +493 -0
- package/dist/components/cron-editor/cron-editor.js.map +1 -0
- package/dist/components/cron-editor/cron-expression.d.ts +82 -0
- package/dist/components/cron-editor/cron-expression.d.ts.map +1 -0
- package/dist/components/cron-editor/cron-expression.js +352 -0
- package/dist/components/cron-editor/cron-expression.js.map +1 -0
- package/dist/components/cron-editor/index.d.ts +3 -0
- package/dist/components/cron-editor/index.js +3 -0
- package/dist/components/cron-editor/meta.js +17 -0
- package/dist/components/cron-editor/meta.js.map +1 -0
- 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/dns-record/dns-record.d.ts +61 -0
- package/dist/components/dns-record/dns-record.d.ts.map +1 -0
- package/dist/components/dns-record/dns-record.js +227 -0
- package/dist/components/dns-record/dns-record.js.map +1 -0
- package/dist/components/dns-record/index.d.ts +2 -0
- package/dist/components/dns-record/index.js +2 -0
- package/dist/components/dns-record/meta.js +17 -0
- package/dist/components/dns-record/meta.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/permission-matrix/index.d.ts +3 -0
- package/dist/components/permission-matrix/index.js +3 -0
- package/dist/components/permission-matrix/meta.js +17 -0
- package/dist/components/permission-matrix/meta.js.map +1 -0
- package/dist/components/permission-matrix/permission-matrix.d.ts +46 -0
- package/dist/components/permission-matrix/permission-matrix.d.ts.map +1 -0
- package/dist/components/permission-matrix/permission-matrix.js +199 -0
- package/dist/components/permission-matrix/permission-matrix.js.map +1 -0
- package/dist/components/permission-matrix/permission-model.d.ts +53 -0
- package/dist/components/permission-matrix/permission-model.d.ts.map +1 -0
- package/dist/components/permission-matrix/permission-model.js +64 -0
- package/dist/components/permission-matrix/permission-model.js.map +1 -0
- package/dist/components/progress/progress.d.ts +1 -1
- package/dist/components/secret-field/index.d.ts +2 -0
- package/dist/components/secret-field/index.js +2 -0
- package/dist/components/secret-field/meta.js +17 -0
- package/dist/components/secret-field/meta.js.map +1 -0
- package/dist/components/secret-field/secret-field.d.ts +58 -0
- package/dist/components/secret-field/secret-field.d.ts.map +1 -0
- package/dist/components/secret-field/secret-field.js +168 -0
- package/dist/components/secret-field/secret-field.js.map +1 -0
- package/dist/components/session-expiry/index.d.ts +2 -0
- package/dist/components/session-expiry/index.js +2 -0
- package/dist/components/session-expiry/meta.js +17 -0
- package/dist/components/session-expiry/meta.js.map +1 -0
- package/dist/components/session-expiry/session-expiry.d.ts +64 -0
- package/dist/components/session-expiry/session-expiry.d.ts.map +1 -0
- package/dist/components/session-expiry/session-expiry.js +185 -0
- package/dist/components/session-expiry/session-expiry.js.map +1 -0
- package/dist/components/shortcut-recorder/index.d.ts +3 -0
- package/dist/components/shortcut-recorder/index.js +3 -0
- package/dist/components/shortcut-recorder/meta.js +17 -0
- package/dist/components/shortcut-recorder/meta.js.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-model.d.ts +67 -0
- package/dist/components/shortcut-recorder/shortcut-model.d.ts.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-model.js +168 -0
- package/dist/components/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.d.ts +47 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.d.ts.map +1 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.js +212 -0
- package/dist/components/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/components/spinner/spinner.d.ts +1 -1
- package/dist/components/sync-status/index.d.ts +2 -0
- package/dist/components/sync-status/index.js +2 -0
- package/dist/components/sync-status/meta.js +17 -0
- package/dist/components/sync-status/meta.js.map +1 -0
- package/dist/components/sync-status/sync-status.d.ts +65 -0
- package/dist/components/sync-status/sync-status.d.ts.map +1 -0
- package/dist/components/sync-status/sync-status.js +103 -0
- package/dist/components/sync-status/sync-status.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 +36 -1
- package/dist/index.js +24 -3
- package/dist/registry/components.d.ts.map +1 -1
- package/dist/registry/components.js +78 -50
- package/dist/registry/components.js.map +1 -1
- package/package.json +6 -4
- 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-extraction-review/ai-extraction-review.tsx +492 -0
- package/src/components/ai-extraction-review/extraction-model.ts +159 -0
- package/src/components/ai-extraction-review/index.ts +13 -0
- package/src/components/ai-extraction-review/meta.ts +24 -0
- package/src/components/ai-suggested-value/ai-suggested-value.tsx +295 -0
- package/src/components/ai-suggested-value/index.ts +6 -0
- package/src/components/ai-suggested-value/meta.ts +24 -0
- package/src/components/confirm-typed/confirm-typed.tsx +191 -0
- package/src/components/confirm-typed/index.ts +1 -0
- package/src/components/confirm-typed/meta.ts +22 -0
- package/src/components/cron-editor/cron-editor.tsx +676 -0
- package/src/components/cron-editor/cron-expression.ts +552 -0
- package/src/components/cron-editor/index.ts +23 -0
- package/src/components/cron-editor/meta.ts +23 -0
- 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/dns-record/dns-record.tsx +315 -0
- package/src/components/dns-record/index.ts +7 -0
- package/src/components/dns-record/meta.ts +21 -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/permission-matrix/index.ts +14 -0
- package/src/components/permission-matrix/meta.ts +23 -0
- package/src/components/permission-matrix/permission-matrix.tsx +368 -0
- package/src/components/permission-matrix/permission-model.ts +120 -0
- package/src/components/secret-field/index.ts +6 -0
- package/src/components/secret-field/meta.ts +21 -0
- package/src/components/secret-field/secret-field.tsx +284 -0
- package/src/components/session-expiry/index.ts +9 -0
- package/src/components/session-expiry/meta.ts +23 -0
- package/src/components/session-expiry/session-expiry.tsx +321 -0
- package/src/components/shortcut-recorder/index.ts +19 -0
- package/src/components/shortcut-recorder/meta.ts +22 -0
- package/src/components/shortcut-recorder/shortcut-model.ts +204 -0
- package/src/components/shortcut-recorder/shortcut-recorder.tsx +336 -0
- package/src/components/sync-status/index.ts +9 -0
- package/src/components/sync-status/meta.ts +21 -0
- package/src/components/sync-status/sync-status.tsx +217 -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 +14 -0
- package/src/registry/components.ts +28 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, useSyncExternalStore, type ComponentPropsWithRef } from "react";
|
|
4
|
+
|
|
5
|
+
import { Button } from "@/components/button";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* "Offline — 3 changes will sync when you're back."
|
|
10
|
+
*
|
|
11
|
+
* Linear, Notion, Figma and Google Docs each built this, and nothing in a
|
|
12
|
+
* component library touches network state at all. It is small, and it is
|
|
13
|
+
* the difference between an app that loses work and one that says it is
|
|
14
|
+
* holding it: the moment a request fails, the reader needs to know whether
|
|
15
|
+
* to keep typing, and whether what they typed a minute ago is anywhere.
|
|
16
|
+
*
|
|
17
|
+
* Two things this gets right that the obvious version gets wrong.
|
|
18
|
+
*
|
|
19
|
+
* `navigator.onLine` is believed in one direction only. False is reliable —
|
|
20
|
+
* the interface is down. True means only that there is an interface, not
|
|
21
|
+
* that the server is reachable, so an application's own failed request is
|
|
22
|
+
* the real signal and takes precedence. The hook is exported for the rest
|
|
23
|
+
* of the app to share the same reading.
|
|
24
|
+
*
|
|
25
|
+
* Announcements are for transitions, not states. Every save flips
|
|
26
|
+
* "Saving…" to "Saved", and a live region on that text narrates the whole
|
|
27
|
+
* session. So the visible text is not live; a separate region says something
|
|
28
|
+
* only when the situation changes — went offline, came back, could not sync —
|
|
29
|
+
* which is when a reader who is typing needs to be told.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export type SyncState = "synced" | "pending" | "syncing" | "offline" | "error";
|
|
33
|
+
|
|
34
|
+
export interface SyncInput {
|
|
35
|
+
/** From `useOnlineStatus`, or your own reading. */
|
|
36
|
+
online: boolean;
|
|
37
|
+
/** Changes waiting to be sent. */
|
|
38
|
+
pending?: number;
|
|
39
|
+
/** A send is in flight. */
|
|
40
|
+
syncing?: boolean;
|
|
41
|
+
/** The last send failed, in the reader's language. Takes precedence over `online`. */
|
|
42
|
+
error?: string | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Which state the inputs amount to. Error beats offline beats syncing beats
|
|
47
|
+
* pending beats synced: each one is the thing the reader most needs to know
|
|
48
|
+
* given the others.
|
|
49
|
+
*/
|
|
50
|
+
export function syncState({
|
|
51
|
+
online,
|
|
52
|
+
pending = 0,
|
|
53
|
+
syncing = false,
|
|
54
|
+
error,
|
|
55
|
+
}: SyncInput): SyncState {
|
|
56
|
+
if (error) return "error";
|
|
57
|
+
if (!online) return "offline";
|
|
58
|
+
if (syncing) return "syncing";
|
|
59
|
+
if (pending > 0) return "pending";
|
|
60
|
+
return "synced";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const changes = (n: number) => `${String(n)} ${n === 1 ? "change" : "changes"}`;
|
|
64
|
+
|
|
65
|
+
/** The state as a sentence. Used for the visible text and the announcement. */
|
|
66
|
+
export function describeSyncState(
|
|
67
|
+
state: SyncState,
|
|
68
|
+
pending = 0,
|
|
69
|
+
error?: string | null,
|
|
70
|
+
): string {
|
|
71
|
+
switch (state) {
|
|
72
|
+
case "synced":
|
|
73
|
+
return "All changes saved";
|
|
74
|
+
case "pending":
|
|
75
|
+
return `${changes(pending)} waiting to save`;
|
|
76
|
+
case "syncing":
|
|
77
|
+
return pending > 0 ? `Saving ${changes(pending)}…` : "Saving…";
|
|
78
|
+
case "offline":
|
|
79
|
+
return pending > 0
|
|
80
|
+
? `Offline. ${changes(pending)} will save when you're back online.`
|
|
81
|
+
: "Offline. Changes will save when you're back online.";
|
|
82
|
+
case "error":
|
|
83
|
+
return pending > 0
|
|
84
|
+
? `Could not save ${changes(pending)}. ${error ?? ""}`.trim()
|
|
85
|
+
: `Could not save. ${error ?? ""}`.trim();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* The browser's reading of the network, as an external store. The server
|
|
90
|
+
snapshot is "online": a page rendered on the server has no interface to
|
|
91
|
+
report, and rendering "offline" into HTML would be a lie until hydration. */
|
|
92
|
+
function subscribeOnline(callback: () => void) {
|
|
93
|
+
window.addEventListener("online", callback);
|
|
94
|
+
window.addEventListener("offline", callback);
|
|
95
|
+
return () => {
|
|
96
|
+
window.removeEventListener("online", callback);
|
|
97
|
+
window.removeEventListener("offline", callback);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const readOnline = () => navigator.onLine;
|
|
101
|
+
const readOnlineOnServer = () => true;
|
|
102
|
+
|
|
103
|
+
/** Whether the browser believes it has a network. False is reliable; true is not. */
|
|
104
|
+
export function useOnlineStatus(): boolean {
|
|
105
|
+
return useSyncExternalStore(subscribeOnline, readOnline, readOnlineOnServer);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface SyncStatusProps
|
|
109
|
+
extends Omit<ComponentPropsWithRef<"div">, "children">, Omit<SyncInput, "online"> {
|
|
110
|
+
/** Omit to read the browser's own status. */
|
|
111
|
+
online?: boolean;
|
|
112
|
+
/** When the last save succeeded. Shown as a time, not a relative phrase, so no clock is needed. */
|
|
113
|
+
lastSyncedAt?: Date;
|
|
114
|
+
locale?: string;
|
|
115
|
+
/** Offered when a save failed. */
|
|
116
|
+
onRetry?: () => void;
|
|
117
|
+
/**
|
|
118
|
+
* Announce transitions. On by default, because going offline is the one
|
|
119
|
+
* thing a reader who is typing has to be told; syncing itself never
|
|
120
|
+
* announces, whichever way this is set.
|
|
121
|
+
*/
|
|
122
|
+
announce?: boolean;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function SyncStatus({
|
|
126
|
+
className,
|
|
127
|
+
online: onlineProp,
|
|
128
|
+
pending = 0,
|
|
129
|
+
syncing = false,
|
|
130
|
+
error,
|
|
131
|
+
lastSyncedAt,
|
|
132
|
+
locale,
|
|
133
|
+
onRetry,
|
|
134
|
+
announce = true,
|
|
135
|
+
...props
|
|
136
|
+
}: SyncStatusProps) {
|
|
137
|
+
const browserOnline = useOnlineStatus();
|
|
138
|
+
const online = onlineProp ?? browserOnline;
|
|
139
|
+
const state = syncState({ online, pending, syncing, error });
|
|
140
|
+
const text = describeSyncState(state, pending, error);
|
|
141
|
+
|
|
142
|
+
// Said once, when the situation changes. Adjusted during render so the
|
|
143
|
+
// announcement lands in the same commit as the state it describes. Syncing
|
|
144
|
+
// and pending are the ordinary churn of saving and are never announced.
|
|
145
|
+
const [seen, setSeen] = useState(state);
|
|
146
|
+
const [announcement, setAnnouncement] = useState("");
|
|
147
|
+
// A failure is over when a save lands, not when the retry starts, so the
|
|
148
|
+
// recovery is remembered across the syncing in between.
|
|
149
|
+
const [recovering, setRecovering] = useState(false);
|
|
150
|
+
if (seen !== state) {
|
|
151
|
+
const from = seen;
|
|
152
|
+
setSeen(state);
|
|
153
|
+
if (state === "offline" || state === "error") {
|
|
154
|
+
setAnnouncement(text);
|
|
155
|
+
setRecovering(false);
|
|
156
|
+
} else if (from === "offline") {
|
|
157
|
+
setAnnouncement(
|
|
158
|
+
pending > 0 ? `Back online. Saving ${changes(pending)}.` : "Back online.",
|
|
159
|
+
);
|
|
160
|
+
} else if ((from === "error" || recovering) && state === "synced") {
|
|
161
|
+
setAnnouncement("Saved.");
|
|
162
|
+
setRecovering(false);
|
|
163
|
+
} else if (from === "error") {
|
|
164
|
+
setRecovering(true);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const savedAt =
|
|
169
|
+
lastSyncedAt !== undefined
|
|
170
|
+
? new Intl.DateTimeFormat(locale, { timeStyle: "short" }).format(lastSyncedAt)
|
|
171
|
+
: null;
|
|
172
|
+
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
data-slot="sync-status"
|
|
176
|
+
data-state={state}
|
|
177
|
+
className={cn("flex flex-wrap items-center gap-x-2 gap-y-1 text-xs", className)}
|
|
178
|
+
{...props}
|
|
179
|
+
>
|
|
180
|
+
{/* The dot is decoration. The word is the status. */}
|
|
181
|
+
<span
|
|
182
|
+
aria-hidden="true"
|
|
183
|
+
className={cn(
|
|
184
|
+
"size-2 shrink-0 rounded-full",
|
|
185
|
+
state === "synced" && "bg-success",
|
|
186
|
+
state === "pending" && "bg-muted-foreground",
|
|
187
|
+
state === "syncing" && "animate-pulse-soft bg-info",
|
|
188
|
+
state === "offline" && "bg-warning",
|
|
189
|
+
state === "error" && "bg-destructive",
|
|
190
|
+
)}
|
|
191
|
+
/>
|
|
192
|
+
<span
|
|
193
|
+
data-slot="sync-status-text"
|
|
194
|
+
className={cn(
|
|
195
|
+
state === "offline" && "text-warning",
|
|
196
|
+
state === "error" && "text-destructive",
|
|
197
|
+
(state === "synced" || state === "pending" || state === "syncing") &&
|
|
198
|
+
"text-muted-foreground",
|
|
199
|
+
)}
|
|
200
|
+
>
|
|
201
|
+
{text}
|
|
202
|
+
{savedAt && state === "synced" ? ` at ${savedAt}` : null}
|
|
203
|
+
</span>
|
|
204
|
+
|
|
205
|
+
{state === "error" && onRetry ? (
|
|
206
|
+
<Button variant="outline" size="sm" className="h-6 px-2 text-xs" onClick={onRetry}>
|
|
207
|
+
Retry
|
|
208
|
+
</Button>
|
|
209
|
+
) : null}
|
|
210
|
+
|
|
211
|
+
{/* Present from the start, so the first transition is heard. */}
|
|
212
|
+
<span role="status" aria-live={announce ? "polite" : "off"} className="sr-only">
|
|
213
|
+
{announce ? announcement : ""}
|
|
214
|
+
</span>
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export {
|
|
2
|
+
TimeRange,
|
|
3
|
+
TimeRangeCalendar,
|
|
4
|
+
TimeRangeContent,
|
|
5
|
+
TimeRangeExpression,
|
|
6
|
+
TimeRangePresets,
|
|
7
|
+
TimeRangeTrigger,
|
|
8
|
+
type TimeRangeContentProps,
|
|
9
|
+
type TimeRangeProps,
|
|
10
|
+
type TimeRangeTriggerProps,
|
|
11
|
+
} from "./time-range-picker";
|
|
12
|
+
export {
|
|
13
|
+
DEFAULT_PRESETS,
|
|
14
|
+
TimeExpressionError,
|
|
15
|
+
absoluteExpression,
|
|
16
|
+
describeTimeRange,
|
|
17
|
+
formatResolvedRange,
|
|
18
|
+
isValidTimeRange,
|
|
19
|
+
resolveExpression,
|
|
20
|
+
resolveTimeRange,
|
|
21
|
+
type ResolveOptions,
|
|
22
|
+
type ResolvedRange,
|
|
23
|
+
type TimeRangePreset,
|
|
24
|
+
type TimeUnit,
|
|
25
|
+
} from "./time-expression";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineMeta } from "@/registry/schema";
|
|
2
|
+
|
|
3
|
+
export const meta = defineMeta({
|
|
4
|
+
name: "time-range-picker",
|
|
5
|
+
title: "Time Range Picker",
|
|
6
|
+
description:
|
|
7
|
+
"A time range whose value is an expression, so it stays relative across a reload.",
|
|
8
|
+
category: "form",
|
|
9
|
+
status: "stable",
|
|
10
|
+
dependencies: ["react-day-picker"],
|
|
11
|
+
registryDependencies: ["button", "calendar", "input", "popover"],
|
|
12
|
+
files: ["time-expression.ts", "time-range-picker.tsx"],
|
|
13
|
+
a11y:
|
|
14
|
+
'The trigger is named by the range it holds — "Last 6 hours" — and the window that ' +
|
|
15
|
+
"resolves to is read after it, so the relative choice is not replaced by two timestamps. " +
|
|
16
|
+
'The popover carries role="dialog" and is named. Presets are buttons rather than radios ' +
|
|
17
|
+
"because choosing one both selects and dismisses, with aria-pressed carrying which matches " +
|
|
18
|
+
"the current expression. The expression field reports why an unparseable entry is invalid " +
|
|
19
|
+
"through one aria-describedby element that the preview and the error share, so a reader " +
|
|
20
|
+
"hears the error replace the preview rather than both at once; an invalid expression is " +
|
|
21
|
+
"never applied, because a chart silently re-scoping itself is worse than one that refuses.",
|
|
22
|
+
});
|