@gearbox-protocol/ui-kit 4.0.0-next.14 → 4.0.0-next.15
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/dist/cjs/components/checkbox/checkbox-labeled.cjs +1 -1
- package/dist/cjs/components/client-adapters/styled-rounded-image/styled-rounded-image.cjs +1 -1
- package/dist/cjs/components/index.cjs +1 -1
- package/dist/cjs/components/sdk-status-pill/constants.cjs +1 -0
- package/dist/cjs/components/sdk-status-pill/index.cjs +1 -0
- package/dist/cjs/components/sdk-status-pill/sdk-status-pill.cjs +1 -0
- package/dist/cjs/components/sdk-status-pill/sdk-status.cjs +1 -0
- package/dist/cjs/components/with-copy/with-copy.cjs +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/locale/en.json.cjs +1 -1
- package/dist/esm/components/checkbox/checkbox-labeled.js +1 -1
- package/dist/esm/components/client-adapters/styled-rounded-image/styled-rounded-image.js +1 -1
- package/dist/esm/components/index.js +234 -235
- package/dist/esm/components/sdk-status-pill/constants.js +8 -0
- package/dist/esm/components/sdk-status-pill/index.js +8 -0
- package/dist/esm/components/sdk-status-pill/sdk-status-pill.js +59 -0
- package/dist/esm/components/sdk-status-pill/sdk-status.js +6 -0
- package/dist/esm/components/with-copy/with-copy.js +1 -1
- package/dist/esm/index.js +491 -492
- package/dist/esm/locale/en.json.js +3 -8
- package/dist/types/components/index.d.ts +1 -1
- package/dist/types/components/sdk-status-pill/constants.d.ts +5 -0
- package/dist/types/components/sdk-status-pill/index.d.ts +3 -0
- package/dist/types/components/sdk-status-pill/sdk-status-pill.d.ts +27 -0
- package/dist/types/components/sdk-status-pill/sdk-status.d.ts +11 -0
- package/dist/types/index.d.ts +1 -2
- package/dist/types/locale/en.json.d.ts +3 -8
- package/dist/types/types/index.d.ts +0 -1
- package/package.json +1 -1
- package/dist/cjs/components/sync-status-pill/constants.cjs +0 -1
- package/dist/cjs/components/sync-status-pill/get-sync-status.cjs +0 -1
- package/dist/cjs/components/sync-status-pill/index.cjs +0 -1
- package/dist/cjs/components/sync-status-pill/sync-status-pill.cjs +0 -1
- package/dist/cjs/types/footer.cjs +0 -1
- package/dist/esm/components/sync-status-pill/constants.js +0 -10
- package/dist/esm/components/sync-status-pill/get-sync-status.js +0 -78
- package/dist/esm/components/sync-status-pill/index.js +0 -9
- package/dist/esm/components/sync-status-pill/sync-status-pill.js +0 -137
- package/dist/esm/types/footer.js +0 -1
- package/dist/types/components/sync-status-pill/constants.d.ts +0 -7
- package/dist/types/components/sync-status-pill/get-sync-status.d.ts +0 -100
- package/dist/types/components/sync-status-pill/index.d.ts +0 -3
- package/dist/types/components/sync-status-pill/sync-status-pill.d.ts +0 -42
- package/dist/types/types/footer.d.ts +0 -7
|
@@ -17,14 +17,9 @@ const e = {
|
|
|
17
17
|
"components.footer.gearbox": "Copyright © Gearbox Foundation 2023-2026.",
|
|
18
18
|
"components.footer.built": "Built at ETHGlobal MarketMake hackathon.",
|
|
19
19
|
"components.footer.version": "Version {value}",
|
|
20
|
-
"components.
|
|
21
|
-
"components.
|
|
22
|
-
"components.
|
|
23
|
-
"components.syncStatus.none": "No data",
|
|
24
|
-
"components.syncStatus.unavailable": "Unavailable",
|
|
25
|
-
"components.syncStatus.ago": "{age} ago",
|
|
26
|
-
"components.syncStatus.sourceOutdated": "{source} data outdated",
|
|
27
|
-
"components.syncStatus.sourceMissing": "no {source} data",
|
|
20
|
+
"components.sdkStatus.connecting": "Connecting…",
|
|
21
|
+
"components.sdkStatus.connected": "Connected",
|
|
22
|
+
"components.sdkStatus.disconnected": "Not connected",
|
|
28
23
|
"components.creditAccountHistory.table.protocol": "Protocol",
|
|
29
24
|
"components.creditAccountHistory.table.operation": "Operation",
|
|
30
25
|
"components.creditAccountHistory.table.details": "Details",
|
|
@@ -55,6 +55,7 @@ export * from './pool-points-indicator';
|
|
|
55
55
|
export * from './rounded-image';
|
|
56
56
|
export * from './rwa-feature';
|
|
57
57
|
export * from './rwa-styles';
|
|
58
|
+
export * from './sdk-status-pill';
|
|
58
59
|
export * from './search-line';
|
|
59
60
|
export * from './segmented-control';
|
|
60
61
|
export * from './select';
|
|
@@ -69,7 +70,6 @@ export * from './spinner-loader';
|
|
|
69
70
|
export * from './split-list';
|
|
70
71
|
export * from './status-dot';
|
|
71
72
|
export * from './status-elements';
|
|
72
|
-
export * from './sync-status-pill';
|
|
73
73
|
export * from './tab-control';
|
|
74
74
|
export * from './table';
|
|
75
75
|
export * from './tabs';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SdkAttachStatus } from './sdk-status';
|
|
2
|
+
import type * as React from "react";
|
|
3
|
+
export interface SdkStatusPillProps {
|
|
4
|
+
/** Where the SDK is in its attach lifecycle. */
|
|
5
|
+
status: SdkAttachStatus;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* SdkStatusPill — whether the SDK is connected, as one dot in an app footer.
|
|
9
|
+
*
|
|
10
|
+
* Deliberately narrow: it reports the **SDK's attach lifecycle** and nothing
|
|
11
|
+
* else. It is not a data-freshness indicator — it will not go grey because a
|
|
12
|
+
* chain lagged or the backend failed, and it says nothing about what is on
|
|
13
|
+
* screen. Screens surface their own failures.
|
|
14
|
+
*
|
|
15
|
+
* Two constraints, both product decisions:
|
|
16
|
+
*
|
|
17
|
+
* - **The palette is green and muted grey only**, never red or amber. See
|
|
18
|
+
* {@link sdkStatusDotVariant}.
|
|
19
|
+
* - **No visible label**, so the tooltip carries the wording and is mirrored
|
|
20
|
+
* into `aria-label` — a screen reader cannot see the colour.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* <AppFooter status={<SdkStatusPill status={attach.status} />} />
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function SdkStatusPill({ status, }: SdkStatusPillProps): React.ReactElement;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Where the SDK is in its attach lifecycle. */
|
|
2
|
+
export type SdkAttachStatus = "loading" | "error" | "ready";
|
|
3
|
+
/** Dot variants this indicator is allowed to use. */
|
|
4
|
+
export type SdkStatusDotVariant = "success" | "default";
|
|
5
|
+
/**
|
|
6
|
+
* The palette rule, as a function rather than a comment: green once the SDK is
|
|
7
|
+
* usable, muted grey otherwise. Never `alert` or `warning` — an alarming dot in
|
|
8
|
+
* permanent view teaches people to ignore it, and the screens surface their own
|
|
9
|
+
* failures. Exported so the rule is pinned by a test rather than by review.
|
|
10
|
+
*/
|
|
11
|
+
export declare function sdkStatusDotVariant(status: SdkAttachStatus): SdkStatusDotVariant;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -57,6 +57,7 @@ export * from './components/pool-points-indicator';
|
|
|
57
57
|
export * from './components/rounded-image';
|
|
58
58
|
export * from './components/rwa-feature';
|
|
59
59
|
export * from './components/rwa-styles';
|
|
60
|
+
export * from './components/sdk-status-pill';
|
|
60
61
|
export * from './components/search-line';
|
|
61
62
|
export * from './components/segmented-control';
|
|
62
63
|
export * from './components/select';
|
|
@@ -71,7 +72,6 @@ export * from './components/spinner-loader';
|
|
|
71
72
|
export * from './components/split-list';
|
|
72
73
|
export * from './components/status-dot';
|
|
73
74
|
export * from './components/status-elements';
|
|
74
|
-
export * from './components/sync-status-pill';
|
|
75
75
|
export * from './components/tab-control';
|
|
76
76
|
export * from './components/table';
|
|
77
77
|
export * from './components/tabs';
|
|
@@ -96,7 +96,6 @@ export * from './configs/variants';
|
|
|
96
96
|
export * from './hooks';
|
|
97
97
|
export type { LocaleKeys } from './locale';
|
|
98
98
|
export * from './locale';
|
|
99
|
-
export type { NetworkById } from './types/footer';
|
|
100
99
|
export { EnglishLocale };
|
|
101
100
|
export * from './utils';
|
|
102
101
|
export * from './utils/a11y';
|
|
@@ -20,14 +20,9 @@ declare const _default: {
|
|
|
20
20
|
"components.footer.built": "Built at ETHGlobal MarketMake hackathon.",
|
|
21
21
|
"components.footer.version": "Version {value}",
|
|
22
22
|
|
|
23
|
-
"components.
|
|
24
|
-
"components.
|
|
25
|
-
"components.
|
|
26
|
-
"components.syncStatus.none": "No data",
|
|
27
|
-
"components.syncStatus.unavailable": "Unavailable",
|
|
28
|
-
"components.syncStatus.ago": "{age} ago",
|
|
29
|
-
"components.syncStatus.sourceOutdated": "{source} data outdated",
|
|
30
|
-
"components.syncStatus.sourceMissing": "no {source} data",
|
|
23
|
+
"components.sdkStatus.connecting": "Connecting…",
|
|
24
|
+
"components.sdkStatus.connected": "Connected",
|
|
25
|
+
"components.sdkStatus.disconnected": "Not connected",
|
|
31
26
|
|
|
32
27
|
"components.creditAccountHistory.table.protocol": "Protocol",
|
|
33
28
|
"components.creditAccountHistory.table.operation": "Operation",
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class t{static root="sync-status-pill-root";static dot="sync-status-pill-dot";static tooltip="sync-status-pill-tooltip";static headline="sync-status-pill-headline";static chainRow="sync-status-pill-chain-row"}exports.SyncStatusPillTest=t;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function h({onchain:t,offchain:e,attach:n="ready",now:r,staleAfterMs:a}){if(n==="loading")return{health:"starting",chains:[],syncedAt:void 0};if(n==="error")return{health:"none",chains:[],syncedAt:void 0};const i=f({onchain:t,offchain:e,now:r,staleAfterMs:a});if(i.length===0)return{health:"starting",chains:i,syncedAt:void 0};const u=v(i);return{health:u,chains:i,syncedAt:g(u,t,e)}}function f({onchain:t,offchain:e,now:n,staleAfterMs:r}){const a=d(t.meta),i=d(e.meta);return[...new Set([...a.keys(),...i.keys()])].map(o=>{const s=l(a.get(o),t.updatedAt,n,r),c=l(i.get(o),e.updatedAt,n,r);return{chainId:o,onchain:s,offchain:c,health:y(s,c)}})}function d(t){const e=new Map;for(const n of t?.chains??[])e.set(n.chainId,n);return e}function l(t,e,n,r){return t?t.status==="error"?"failed":!e||n-e>r||n-t.timestamp*1e3>r?"stale":"ok":"unknown"}function y(t,e){return t==="ok"?"live":t==="stale"||e==="ok"||e==="stale"?"cached":"none"}function v(t){return t.every(e=>e.health==="none")?"none":t.some(e=>e.health==="live")?"live":"cached"}function g(t,e,n){if(t==="live")return e.updatedAt||void 0;if(t==="cached"){const r=[e.updatedAt,n.updatedAt].filter(a=>!!a);return r.length?Math.max(...r):void 0}}function p(t){return t==="live"?"success":"default"}exports.getSyncStatus=h;exports.syncStatusDotVariant=p;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("./constants.cjs"),t=require("./get-sync-status.cjs"),n=require("./sync-status-pill.cjs");exports.SyncStatusPillTest=s.SyncStatusPillTest;exports.getSyncStatus=t.getSyncStatus;exports.syncStatusDotVariant=t.syncStatusDotVariant;exports.SyncStatusPill=n.SyncStatusPill;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),d=require("react"),f=require("../status-dot/status-dot.cjs"),x=require("../tooltip/simple-tooltip.cjs");require("../tooltip/tooltip.cjs");const g=require("../typed-intl/index.cjs");require("@gearbox-protocol/sdk/common-utils");const _=require("../../hooks/use-media-query.cjs"),c=require("./constants.cjs"),p=require("./get-sync-status.cjs"),I=6e4*2.5,v=1e3,w="(hover: none)";function T({onchain:e,offchain:t,attach:s,isSyncing:u=!1,networkById:r}){const{formatMessage:n}=g.useIntlTyped(),l=$(),y=!_.useMediaQuery(w),o=d.useMemo(()=>p.getSyncStatus({onchain:e,offchain:t,attach:s,now:l,staleAfterMs:I}),[e,t,s,l]),S=n({messageId:q[o.health]}),m=o.syncedAt?`${S} · ${n({messageId:"components.syncStatus.ago"},{age:j(l-o.syncedAt)})}`:S,h=a.jsx("output",{className:"-m-1.5 flex size-6 cursor-pointer items-center justify-center","data-testid":c.SyncStatusPillTest.root,"aria-label":m,children:a.jsx(f.StatusDot,{variant:p.syncStatusDotVariant(o.health),size:"xs",pulse:o.health==="starting"||u,"data-testid":c.SyncStatusPillTest.dot})});return a.jsx(x.SimpleTooltip,{title:h,trigger:y?"hover":"click",placement:"top",maxWidth:"280px",children:a.jsxs("div",{className:"flex flex-col gap-1 text-left text-xs","data-testid":c.SyncStatusPillTest.tooltip,children:[a.jsx("span",{className:"font-medium","data-testid":c.SyncStatusPillTest.headline,children:m}),o.chains.map(i=>a.jsx("span",{className:"text-muted-foreground","data-testid":c.SyncStatusPillTest.chainRow,children:`${r[i.chainId]??String(i.chainId)} ${M(i,n)}`},i.chainId))]})})}const q={live:"components.syncStatus.live",starting:"components.syncStatus.starting",cached:"components.syncStatus.cached",none:"components.syncStatus.none"};function M(e,t){if(e.health==="none")return t({messageId:"components.syncStatus.unavailable"});const[s,u,r]=e.health==="live"?[t({messageId:"components.syncStatus.live"}),"offchain",e.offchain]:[t({messageId:"components.syncStatus.cached"}),"onchain",e.onchain],n=P(u,r,t);return n?`${s} · ${n}`:s}function P(e,t,s){if(t==="stale")return s({messageId:"components.syncStatus.sourceOutdated"},{source:e});if(t==="failed"||t==="unknown")return s({messageId:"components.syncStatus.sourceMissing"},{source:e})}function j(e){const t=Math.max(0,Math.round(e/1e3));return t<60?`${t}s`:t<3600?`${Math.round(t/60)}m`:`${Math.round(t/3600)}h`}function $(){const[e,t]=d.useState(()=>Date.now());return d.useEffect(()=>{const s=window.setInterval(()=>t(Date.now()),v);return()=>window.clearInterval(s)},[]),e}exports.SyncStatusPill=T;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
class s {
|
|
2
|
-
static root = "sync-status-pill-root";
|
|
3
|
-
static dot = "sync-status-pill-dot";
|
|
4
|
-
static tooltip = "sync-status-pill-tooltip";
|
|
5
|
-
static headline = "sync-status-pill-headline";
|
|
6
|
-
static chainRow = "sync-status-pill-chain-row";
|
|
7
|
-
}
|
|
8
|
-
export {
|
|
9
|
-
s as SyncStatusPillTest
|
|
10
|
-
};
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
function p({
|
|
2
|
-
onchain: t,
|
|
3
|
-
offchain: e,
|
|
4
|
-
attach: n = "ready",
|
|
5
|
-
now: r,
|
|
6
|
-
staleAfterMs: a
|
|
7
|
-
}) {
|
|
8
|
-
if (n === "loading")
|
|
9
|
-
return { health: "starting", chains: [], syncedAt: void 0 };
|
|
10
|
-
if (n === "error")
|
|
11
|
-
return { health: "none", chains: [], syncedAt: void 0 };
|
|
12
|
-
const i = f({ onchain: t, offchain: e, now: r, staleAfterMs: a });
|
|
13
|
-
if (i.length === 0)
|
|
14
|
-
return { health: "starting", chains: i, syncedAt: void 0 };
|
|
15
|
-
const u = v(i);
|
|
16
|
-
return { health: u, chains: i, syncedAt: y(u, t, e) };
|
|
17
|
-
}
|
|
18
|
-
function f({
|
|
19
|
-
onchain: t,
|
|
20
|
-
offchain: e,
|
|
21
|
-
now: n,
|
|
22
|
-
staleAfterMs: r
|
|
23
|
-
}) {
|
|
24
|
-
const a = d(t.meta), i = d(e.meta);
|
|
25
|
-
return [
|
|
26
|
-
.../* @__PURE__ */ new Set([...a.keys(), ...i.keys()])
|
|
27
|
-
].map((o) => {
|
|
28
|
-
const c = h(
|
|
29
|
-
a.get(o),
|
|
30
|
-
t.updatedAt,
|
|
31
|
-
n,
|
|
32
|
-
r
|
|
33
|
-
), s = h(
|
|
34
|
-
i.get(o),
|
|
35
|
-
e.updatedAt,
|
|
36
|
-
n,
|
|
37
|
-
r
|
|
38
|
-
);
|
|
39
|
-
return {
|
|
40
|
-
chainId: o,
|
|
41
|
-
onchain: c,
|
|
42
|
-
offchain: s,
|
|
43
|
-
health: l(c, s)
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
function d(t) {
|
|
48
|
-
const e = /* @__PURE__ */ new Map();
|
|
49
|
-
for (const n of t?.chains ?? [])
|
|
50
|
-
e.set(n.chainId, n);
|
|
51
|
-
return e;
|
|
52
|
-
}
|
|
53
|
-
function h(t, e, n, r) {
|
|
54
|
-
return t ? t.status === "error" ? "failed" : !e || n - e > r || n - t.timestamp * 1e3 > r ? "stale" : "ok" : "unknown";
|
|
55
|
-
}
|
|
56
|
-
function l(t, e) {
|
|
57
|
-
return t === "ok" ? "live" : t === "stale" || e === "ok" || e === "stale" ? "cached" : "none";
|
|
58
|
-
}
|
|
59
|
-
function v(t) {
|
|
60
|
-
return t.every((e) => e.health === "none") ? "none" : t.some((e) => e.health === "live") ? "live" : "cached";
|
|
61
|
-
}
|
|
62
|
-
function y(t, e, n) {
|
|
63
|
-
if (t === "live")
|
|
64
|
-
return e.updatedAt || void 0;
|
|
65
|
-
if (t === "cached") {
|
|
66
|
-
const r = [e.updatedAt, n.updatedAt].filter(
|
|
67
|
-
(a) => !!a
|
|
68
|
-
);
|
|
69
|
-
return r.length ? Math.max(...r) : void 0;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function g(t) {
|
|
73
|
-
return t === "live" ? "success" : "default";
|
|
74
|
-
}
|
|
75
|
-
export {
|
|
76
|
-
p as getSyncStatus,
|
|
77
|
-
g as syncStatusDotVariant
|
|
78
|
-
};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { SyncStatusPillTest as r } from "./constants.js";
|
|
2
|
-
import { getSyncStatus as a, syncStatusDotVariant as s } from "./get-sync-status.js";
|
|
3
|
-
import { SyncStatusPill as n } from "./sync-status-pill.js";
|
|
4
|
-
export {
|
|
5
|
-
n as SyncStatusPill,
|
|
6
|
-
r as SyncStatusPillTest,
|
|
7
|
-
a as getSyncStatus,
|
|
8
|
-
s as syncStatusDotVariant
|
|
9
|
-
};
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { jsx as a, jsxs as h } from "react/jsx-runtime";
|
|
2
|
-
import { useMemo as S, useState as y, useEffect as g } from "react";
|
|
3
|
-
import { StatusDot as x } from "../status-dot/status-dot.js";
|
|
4
|
-
import { SimpleTooltip as I } from "../tooltip/simple-tooltip.js";
|
|
5
|
-
import "../tooltip/tooltip.js";
|
|
6
|
-
import { useIntlTyped as v } from "../typed-intl/index.js";
|
|
7
|
-
import "@gearbox-protocol/sdk/common-utils";
|
|
8
|
-
import { useMediaQuery as w } from "../../hooks/use-media-query.js";
|
|
9
|
-
import { SyncStatusPillTest as c } from "./constants.js";
|
|
10
|
-
import { getSyncStatus as $, syncStatusDotVariant as A } from "./get-sync-status.js";
|
|
11
|
-
const E = 6e4 * 2.5, M = 1e3, N = "(hover: none)";
|
|
12
|
-
function F({
|
|
13
|
-
onchain: e,
|
|
14
|
-
offchain: t,
|
|
15
|
-
attach: n,
|
|
16
|
-
isSyncing: i = !1,
|
|
17
|
-
networkById: u
|
|
18
|
-
}) {
|
|
19
|
-
const { formatMessage: s } = v(), d = D(), p = !w(N), o = S(
|
|
20
|
-
() => $({
|
|
21
|
-
onchain: e,
|
|
22
|
-
offchain: t,
|
|
23
|
-
attach: n,
|
|
24
|
-
now: d,
|
|
25
|
-
staleAfterMs: E
|
|
26
|
-
}),
|
|
27
|
-
[e, t, n, d]
|
|
28
|
-
), l = s({
|
|
29
|
-
messageId: T[o.health]
|
|
30
|
-
}), m = o.syncedAt ? `${l} · ${s(
|
|
31
|
-
{ messageId: "components.syncStatus.ago" },
|
|
32
|
-
{ age: R(d - o.syncedAt) }
|
|
33
|
-
)}` : l, f = (
|
|
34
|
-
// `<output>` rather than a span with role="status": same implicit live
|
|
35
|
-
// region, and the status is announced without the dot being focusable.
|
|
36
|
-
//
|
|
37
|
-
// The box is `size-6`, not the dot's 6px: a 6px hover/tap target is far
|
|
38
|
-
// under the 24px WCAG 2.5.8 minimum and genuinely hard to hit, while the
|
|
39
|
-
// dot itself has to stay small to keep the footer quiet. So the target is
|
|
40
|
-
// the padding around it.
|
|
41
|
-
//
|
|
42
|
-
// `-m-1.5` keeps that out of the layout in both axes: the box contributes
|
|
43
|
-
// 12px, less than the 16px line beside it, so the footer row keeps its
|
|
44
|
-
// height and the links do not shift.
|
|
45
|
-
/* @__PURE__ */ a(
|
|
46
|
-
"output",
|
|
47
|
-
{
|
|
48
|
-
className: "-m-1.5 flex size-6 cursor-pointer items-center justify-center",
|
|
49
|
-
"data-testid": c.root,
|
|
50
|
-
"aria-label": m,
|
|
51
|
-
children: /* @__PURE__ */ a(
|
|
52
|
-
x,
|
|
53
|
-
{
|
|
54
|
-
variant: A(o.health),
|
|
55
|
-
size: "xs",
|
|
56
|
-
pulse: o.health === "starting" || i,
|
|
57
|
-
"data-testid": c.dot
|
|
58
|
-
}
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
)
|
|
62
|
-
);
|
|
63
|
-
return /* @__PURE__ */ a(
|
|
64
|
-
I,
|
|
65
|
-
{
|
|
66
|
-
title: f,
|
|
67
|
-
trigger: p ? "hover" : "click",
|
|
68
|
-
placement: "top",
|
|
69
|
-
maxWidth: "280px",
|
|
70
|
-
children: /* @__PURE__ */ h(
|
|
71
|
-
"div",
|
|
72
|
-
{
|
|
73
|
-
className: "flex flex-col gap-1 text-left text-xs",
|
|
74
|
-
"data-testid": c.tooltip,
|
|
75
|
-
children: [
|
|
76
|
-
/* @__PURE__ */ a("span", { className: "font-medium", "data-testid": c.headline, children: m }),
|
|
77
|
-
o.chains.map((r) => /* @__PURE__ */ a(
|
|
78
|
-
"span",
|
|
79
|
-
{
|
|
80
|
-
className: "text-muted-foreground",
|
|
81
|
-
"data-testid": c.chainRow,
|
|
82
|
-
children: `${u[r.chainId] ?? String(r.chainId)} ${_(r, s)}`
|
|
83
|
-
},
|
|
84
|
-
r.chainId
|
|
85
|
-
))
|
|
86
|
-
]
|
|
87
|
-
}
|
|
88
|
-
)
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
const T = {
|
|
93
|
-
live: "components.syncStatus.live",
|
|
94
|
-
starting: "components.syncStatus.starting",
|
|
95
|
-
cached: "components.syncStatus.cached",
|
|
96
|
-
none: "components.syncStatus.none"
|
|
97
|
-
};
|
|
98
|
-
function _(e, t) {
|
|
99
|
-
if (e.health === "none")
|
|
100
|
-
return t({ messageId: "components.syncStatus.unavailable" });
|
|
101
|
-
const [n, i, u] = e.health === "live" ? [
|
|
102
|
-
t({ messageId: "components.syncStatus.live" }),
|
|
103
|
-
"offchain",
|
|
104
|
-
e.offchain
|
|
105
|
-
] : [
|
|
106
|
-
t({ messageId: "components.syncStatus.cached" }),
|
|
107
|
-
"onchain",
|
|
108
|
-
e.onchain
|
|
109
|
-
], s = b(i, u, t);
|
|
110
|
-
return s ? `${n} · ${s}` : n;
|
|
111
|
-
}
|
|
112
|
-
function b(e, t, n) {
|
|
113
|
-
if (t === "stale")
|
|
114
|
-
return n(
|
|
115
|
-
{ messageId: "components.syncStatus.sourceOutdated" },
|
|
116
|
-
{ source: e }
|
|
117
|
-
);
|
|
118
|
-
if (t === "failed" || t === "unknown")
|
|
119
|
-
return n(
|
|
120
|
-
{ messageId: "components.syncStatus.sourceMissing" },
|
|
121
|
-
{ source: e }
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
function R(e) {
|
|
125
|
-
const t = Math.max(0, Math.round(e / 1e3));
|
|
126
|
-
return t < 60 ? `${t}s` : t < 3600 ? `${Math.round(t / 60)}m` : `${Math.round(t / 3600)}h`;
|
|
127
|
-
}
|
|
128
|
-
function D() {
|
|
129
|
-
const [e, t] = y(() => Date.now());
|
|
130
|
-
return g(() => {
|
|
131
|
-
const n = window.setInterval(() => t(Date.now()), M);
|
|
132
|
-
return () => window.clearInterval(n);
|
|
133
|
-
}, []), e;
|
|
134
|
-
}
|
|
135
|
-
export {
|
|
136
|
-
F as SyncStatusPill
|
|
137
|
-
};
|
package/dist/esm/types/footer.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { ResponseMetadata } from '@gearbox-protocol/sdk/model';
|
|
2
|
-
/**
|
|
3
|
-
* Health of one source (onchain or offchain) as far as one chain is concerned.
|
|
4
|
-
*
|
|
5
|
-
* `unknown` is not an error: it means this source has not spoken about the
|
|
6
|
-
* chain yet, which is what the first frames of a two-source read look like.
|
|
7
|
-
*/
|
|
8
|
-
export type SourceHealth = "ok" | "stale" | "failed" | "unknown";
|
|
9
|
-
/**
|
|
10
|
-
* What the user sees and can do on one chain.
|
|
11
|
-
*
|
|
12
|
-
* Derived from `onchain` alone — see {@link deriveChainHealth}. `offchain`
|
|
13
|
-
* never moves a chain between these values, it only annotates the row.
|
|
14
|
-
*/
|
|
15
|
-
export type ChainHealth =
|
|
16
|
-
/** Onchain is fresh: what is on screen is current and actions go through. */
|
|
17
|
-
"live"
|
|
18
|
-
/** Onchain lagged or never answered: rows come from the backend. */
|
|
19
|
-
| "cached"
|
|
20
|
-
/** Nobody answered. */
|
|
21
|
-
| "none";
|
|
22
|
-
/** Rolled-up health of the whole app. */
|
|
23
|
-
export type AppSyncHealth = "starting" | "live" | "cached" | "none";
|
|
24
|
-
/** Where the app's single SDK is in its attach lifecycle. */
|
|
25
|
-
export type SdkAttachStatus = "loading" | "error" | "ready";
|
|
26
|
-
/**
|
|
27
|
-
* One source exactly as the SDK returned it, plus the local clock reading of
|
|
28
|
-
* its last success.
|
|
29
|
-
*
|
|
30
|
-
* `meta` is `DataResponse.meta` verbatim — no projection, no intermediate
|
|
31
|
-
* format. The previous footer indicator died because ui-kit owned its own
|
|
32
|
-
* shape (`BlockByChain`) and the app had to hand-translate SDK state into it;
|
|
33
|
-
* the translator outlived nothing and the format outlived its source.
|
|
34
|
-
*/
|
|
35
|
-
export interface SyncSourceInput {
|
|
36
|
-
/** `DataResponse.meta`, or `undefined` while the source has never answered. */
|
|
37
|
-
meta: ResponseMetadata | undefined;
|
|
38
|
-
/**
|
|
39
|
-
* react-query `dataUpdatedAt` in ms. The one number that cannot come from
|
|
40
|
-
* the SDK: `ChainSucceeded.timestamp` is the *block's* time, which answers
|
|
41
|
-
* "how old is the chain state", not "is our polling loop still alive".
|
|
42
|
-
* `0` / `undefined` mean no success yet.
|
|
43
|
-
*/
|
|
44
|
-
updatedAt?: number;
|
|
45
|
-
}
|
|
46
|
-
/** Per-chain outcome, ready to render as one tooltip row. */
|
|
47
|
-
export interface ChainStatus {
|
|
48
|
-
chainId: number;
|
|
49
|
-
/** Decides {@link health}, and through it the dot colour. */
|
|
50
|
-
onchain: SourceHealth;
|
|
51
|
-
/** Never decides colour — only the row's suffix. */
|
|
52
|
-
offchain: SourceHealth;
|
|
53
|
-
health: ChainHealth;
|
|
54
|
-
}
|
|
55
|
-
export interface SyncStatus {
|
|
56
|
-
health: AppSyncHealth;
|
|
57
|
-
chains: ChainStatus[];
|
|
58
|
-
/**
|
|
59
|
-
* Local clock (ms) of the last success behind what is currently shown, or
|
|
60
|
-
* `undefined` when nothing has been shown yet. Feeds the "· 12s ago" in the
|
|
61
|
-
* tooltip header.
|
|
62
|
-
*/
|
|
63
|
-
syncedAt: number | undefined;
|
|
64
|
-
}
|
|
65
|
-
export interface GetSyncStatusProps {
|
|
66
|
-
onchain: SyncSourceInput;
|
|
67
|
-
offchain: SyncSourceInput;
|
|
68
|
-
attach?: SdkAttachStatus;
|
|
69
|
-
/** Reference "now" in ms. */
|
|
70
|
-
now: number;
|
|
71
|
-
/** Age after which a source counts as stale. */
|
|
72
|
-
staleAfterMs: number;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Pure derivation of the sync indicator's state.
|
|
76
|
-
*
|
|
77
|
-
* The whole model rests on one asymmetry: **onchain is the ability to act,
|
|
78
|
-
* offchain is the trimmings** (charts, APY, history). So the colour tracks
|
|
79
|
-
* `onchain` and nothing else — a dead backend under a healthy chain is still
|
|
80
|
-
* green, because the user sees current data and their transactions land. The
|
|
81
|
-
* reverse, a live polling loop over a lagging node, is *not* green: the screen
|
|
82
|
-
* looks current while a transaction would be built against stale state.
|
|
83
|
-
*
|
|
84
|
-
* The roll-up is deliberately optimistic: one dead chain annotates its own row
|
|
85
|
-
* and leaves the dot green. Statuspage-style pessimism ("any component down ⇒
|
|
86
|
-
* system degraded") assumes components of one service; our chains are
|
|
87
|
-
* independent, and greying the dot for a chain the user is not on is a false
|
|
88
|
-
* alarm.
|
|
89
|
-
*/
|
|
90
|
-
export declare function getSyncStatus({ onchain, offchain, attach, now, staleAfterMs, }: GetSyncStatusProps): SyncStatus;
|
|
91
|
-
/** Dot variants this indicator is allowed to use. */
|
|
92
|
-
export type SyncStatusDotVariant = "success" | "default";
|
|
93
|
-
/**
|
|
94
|
-
* The palette rule, as a function rather than a comment: green when the user
|
|
95
|
-
* can act, muted grey otherwise. Never `alert` or `warning` — an alarming dot
|
|
96
|
-
* in permanent view teaches people to ignore it, and the screens surface their
|
|
97
|
-
* own failures. Exported so the rule is pinned by a unit test instead of by
|
|
98
|
-
* inspecting rendered markup.
|
|
99
|
-
*/
|
|
100
|
-
export declare function syncStatusDotVariant(health: AppSyncHealth): SyncStatusDotVariant;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { NetworkById } from '../../types/footer';
|
|
2
|
-
import { SdkAttachStatus, SyncSourceInput } from './get-sync-status';
|
|
3
|
-
import type * as React from "react";
|
|
4
|
-
export interface SyncStatusPillProps {
|
|
5
|
-
onchain: SyncSourceInput;
|
|
6
|
-
offchain: SyncSourceInput;
|
|
7
|
-
attach?: SdkAttachStatus;
|
|
8
|
-
/** A read is in flight — the dot pulses. Orthogonal to health. */
|
|
9
|
-
isSyncing?: boolean;
|
|
10
|
-
/** Only for chain labels in the tooltip. */
|
|
11
|
-
networkById: NetworkById;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* SyncStatusPill — data-freshness indicator for an app footer: one dot, and
|
|
15
|
-
* every word in the tooltip.
|
|
16
|
-
*
|
|
17
|
-
* Takes the SDK's own `DataResponse.meta` for each source, untouched, and
|
|
18
|
-
* derives the status itself. The predecessor (`BlockSyncPill`) owned a bespoke
|
|
19
|
-
* `BlockByChain` shape that each client had to hand-translate SDK state into;
|
|
20
|
-
* the format outlived its source and the translators rotted.
|
|
21
|
-
*
|
|
22
|
-
* Two deliberate constraints:
|
|
23
|
-
*
|
|
24
|
-
* - **The palette is green and muted grey only** — never red or amber, at any
|
|
25
|
-
* input. See {@link syncStatusDotVariant}.
|
|
26
|
-
* - **No visible label.** Colour therefore separates only two of the four
|
|
27
|
-
* states, which makes the tooltip the status rather than a nicety — and it is
|
|
28
|
-
* mirrored into `aria-label`, because a screen reader cannot see colour.
|
|
29
|
-
*
|
|
30
|
-
* Colour tracks `onchain` alone; see {@link getSyncStatus} for why.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* ```tsx
|
|
34
|
-
* <SyncStatusPill
|
|
35
|
-
* onchain={{ meta: onchainResponse?.meta, updatedAt: onchainUpdatedAt }}
|
|
36
|
-
* offchain={{ meta: offchainResponse?.meta, updatedAt: offchainUpdatedAt }}
|
|
37
|
-
* attach="ready"
|
|
38
|
-
* networkById={{ 1: "Ethereum" }}
|
|
39
|
-
* />
|
|
40
|
-
* ```
|
|
41
|
-
*/
|
|
42
|
-
export declare function SyncStatusPill({ onchain, offchain, attach, isSyncing, networkById, }: SyncStatusPillProps): React.ReactElement;
|