@workflow/web-shared 5.0.0-beta.10 → 5.0.0-beta.11
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/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -1
- package/dist/components/new-trace-viewer/components/event-list.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/event-list.js +9 -4
- package/dist/components/new-trace-viewer/components/split-pane.d.ts +3 -2
- package/dist/components/new-trace-viewer/components/split-pane.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/split-pane.js +10 -4
- package/dist/components/new-trace-viewer/components/timeline.d.ts +1 -1
- package/dist/components/new-trace-viewer/components/timeline.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/components/timeline.js +6 -8
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts +2 -0
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/trace-viewer-skeleton.js +19 -0
- package/dist/components/new-trace-viewer/components/use-row-window.d.ts +20 -0
- package/dist/components/new-trace-viewer/components/use-row-window.d.ts.map +1 -0
- package/dist/components/new-trace-viewer/components/use-row-window.js +69 -0
- package/dist/components/new-trace-viewer/context.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/context.js +2 -2
- package/dist/components/new-trace-viewer/trace-viewer.d.ts +4 -1
- package/dist/components/new-trace-viewer/trace-viewer.d.ts.map +1 -1
- package/dist/components/new-trace-viewer/trace-viewer.js +19 -6
- package/dist/components/trace-viewer-new.d.ts +5 -1
- package/dist/components/trace-viewer-new.d.ts.map +1 -1
- package/dist/components/trace-viewer-new.js +7 -8
- package/dist/components/ui/icon-button.d.ts +8 -2
- package/dist/components/ui/icon-button.d.ts.map +1 -1
- package/dist/components/ui/icon-button.js +4 -4
- package/dist/components/ui/kbd.d.ts +6 -0
- package/dist/components/ui/kbd.d.ts.map +1 -0
- package/dist/components/ui/kbd.js +6 -0
- package/dist/components/ui/tooltip.d.ts +10 -0
- package/dist/components/ui/tooltip.d.ts.map +1 -0
- package/dist/components/ui/tooltip.js +12 -0
- package/dist/hooks/use-intersection-observer.d.ts +17 -0
- package/dist/hooks/use-intersection-observer.d.ts.map +1 -0
- package/dist/hooks/use-intersection-observer.js +29 -0
- package/dist/hooks/use-load-more-on-scroll.d.ts +21 -0
- package/dist/hooks/use-load-more-on-scroll.d.ts.map +1 -0
- package/dist/hooks/use-load-more-on-scroll.js +28 -0
- package/package.json +3 -3
- package/src/components/index.ts +1 -0
- package/src/components/new-trace-viewer/components/event-list.tsx +19 -11
- package/src/components/new-trace-viewer/components/split-pane.tsx +15 -2
- package/src/components/new-trace-viewer/components/timeline.tsx +27 -19
- package/src/components/new-trace-viewer/components/trace-viewer-skeleton.tsx +94 -0
- package/src/components/new-trace-viewer/components/use-row-window.ts +93 -0
- package/src/components/new-trace-viewer/context.tsx +1 -3
- package/src/components/new-trace-viewer/trace-viewer.tsx +89 -21
- package/src/components/trace-viewer-new.tsx +20 -12
- package/src/components/ui/icon-button.tsx +7 -11
- package/src/components/ui/kbd.tsx +21 -0
- package/src/components/ui/tooltip.tsx +47 -0
- package/src/hooks/use-intersection-observer.ts +57 -0
- package/src/hooks/use-load-more-on-scroll.ts +49 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type VariantProps } from 'class-variance-authority';
|
|
2
|
-
import
|
|
2
|
+
import * as React from 'react';
|
|
3
3
|
declare const iconButtonVariants: (props?: ({
|
|
4
4
|
variant?: "muted" | "tertiary" | null | undefined;
|
|
5
5
|
size?: "small" | "tiny" | null | undefined;
|
|
@@ -8,6 +8,12 @@ export type IconButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
|
8
8
|
'aria-label': string;
|
|
9
9
|
type?: 'button' | 'submit' | 'reset';
|
|
10
10
|
};
|
|
11
|
-
export declare
|
|
11
|
+
export declare const IconButton: React.ForwardRefExoticComponent<Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type"> & VariantProps<(props?: ({
|
|
12
|
+
variant?: "muted" | "tertiary" | null | undefined;
|
|
13
|
+
size?: "small" | "tiny" | null | undefined;
|
|
14
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
15
|
+
'aria-label': string;
|
|
16
|
+
type?: "button" | "submit" | "reset";
|
|
17
|
+
} & React.RefAttributes<HTMLButtonElement>>;
|
|
12
18
|
export { iconButtonVariants };
|
|
13
19
|
//# sourceMappingURL=icon-button.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icon-button.d.ts","sourceRoot":"","sources":["../../../src/components/ui/icon-button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"icon-button.d.ts","sourceRoot":"","sources":["../../../src/components/ui/icon-button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,QAAA,MAAM,kBAAkB;;;8EAyBvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC,EAC7C,MAAM,CACP,GACC,YAAY,CAAC,OAAO,kBAAkB,CAAC,GAAG;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;CACtC,CAAC;AAEJ,eAAO,MAAM,UAAU;;;;kBAJL,MAAM;WACb,QAAQ,GAAG,QAAQ,GAAG,OAAO;2CAYvC,CAAC;AAGF,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { cva } from 'class-variance-authority';
|
|
3
|
+
import * as React from 'react';
|
|
3
4
|
import { cn } from '../../lib/utils';
|
|
4
5
|
const iconButtonVariants = cva([
|
|
5
6
|
'm-0 inline-flex shrink-0 appearance-none items-center justify-center border-0 bg-transparent p-0 align-baseline font-inherit no-underline [background:none] [-webkit-appearance:none] [-webkit-tap-highlight-color:transparent]',
|
|
@@ -22,8 +23,7 @@ const iconButtonVariants = cva([
|
|
|
22
23
|
size: 'tiny',
|
|
23
24
|
},
|
|
24
25
|
});
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
}
|
|
26
|
+
export const IconButton = React.forwardRef(({ className, variant, size, type = 'button', ...props }, ref) => (_jsx("button", { ref: ref, type: type, className: cn(iconButtonVariants({ variant, size, className })), ...props })));
|
|
27
|
+
IconButton.displayName = 'IconButton';
|
|
28
28
|
export { iconButtonVariants };
|
|
29
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaWNvbi1idXR0b24uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29tcG9uZW50cy91aS9pY29uLWJ1dHRvbi50c3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFBRSxHQUFHLEVBQXFCLE1BQU0sMEJBQTBCLENBQUM7QUFDbEUsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXJDLE1BQU0sa0JBQWtCLEdBQUcsR0FBRyxDQUM1QjtJQUNFLGlPQUFpTztJQUNqTyw0Q0FBNEM7SUFDNUMsd0VBQXdFO0lBQ3hFLDRIQUE0SDtDQUM3SCxFQUNEO0lBQ0UsUUFBUSxFQUFFO1FBQ1IsT0FBTyxFQUFFO1lBQ1AsUUFBUSxFQUNOLDBJQUEwSTtZQUM1SSxLQUFLLEVBQ0gsMElBQTBJO1NBQzdJO1FBQ0QsSUFBSSxFQUFFO1lBQ0osSUFBSSxFQUFFLCtDQUErQztZQUNyRCxLQUFLLEVBQUUsNENBQTRDO1NBQ3BEO0tBQ0Y7SUFDRCxlQUFlLEVBQUU7UUFDZixPQUFPLEVBQUUsVUFBVTtRQUNuQixJQUFJLEVBQUUsTUFBTTtLQUNiO0NBQ0YsQ0FDRixDQUFDO0FBV0YsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQyxVQUFVLENBQ3hDLENBQUMsRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxJQUFJLEdBQUcsUUFBUSxFQUFFLEdBQUcsS0FBSyxFQUFFLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUNoRSxpQkFDRSxHQUFHLEVBQUUsR0FBRyxFQUNSLElBQUksRUFBRSxJQUFJLEVBQ1YsU0FBUyxFQUFFLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQyxLQUMzRCxLQUFLLEdBQ1QsQ0FDSCxDQUNGLENBQUM7QUFDRixVQUFVLENBQUMsV0FBVyxHQUFHLFlBQVksQ0FBQztBQUV0QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGN2YSwgdHlwZSBWYXJpYW50UHJvcHMgfSBmcm9tICdjbGFzcy12YXJpYW5jZS1hdXRob3JpdHknO1xuaW1wb3J0ICogYXMgUmVhY3QgZnJvbSAncmVhY3QnO1xuaW1wb3J0IHsgY24gfSBmcm9tICcuLi8uLi9saWIvdXRpbHMnO1xuXG5jb25zdCBpY29uQnV0dG9uVmFyaWFudHMgPSBjdmEoXG4gIFtcbiAgICAnbS0wIGlubGluZS1mbGV4IHNocmluay0wIGFwcGVhcmFuY2Utbm9uZSBpdGVtcy1jZW50ZXIganVzdGlmeS1jZW50ZXIgYm9yZGVyLTAgYmctdHJhbnNwYXJlbnQgcC0wIGFsaWduLWJhc2VsaW5lIGZvbnQtaW5oZXJpdCBuby11bmRlcmxpbmUgW2JhY2tncm91bmQ6bm9uZV0gWy13ZWJraXQtYXBwZWFyYW5jZTpub25lXSBbLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOnRyYW5zcGFyZW50XScsXG4gICAgJ3RyYW5zaXRpb24tY29sb3JzIGR1cmF0aW9uLTE1MCBlYXNlLWluLW91dCcsXG4gICAgJ2VuYWJsZWQ6Y3Vyc29yLXBvaW50ZXIgZGlzYWJsZWQ6Y3Vyc29yLW5vdC1hbGxvd2VkIGRpc2FibGVkOm9wYWNpdHktNDAnLFxuICAgICdmb2N1cy12aXNpYmxlOm91dGxpbmUgZm9jdXMtdmlzaWJsZTpvdXRsaW5lLTIgZm9jdXMtdmlzaWJsZTpvdXRsaW5lLVt2YXIoLS1kcy1mb2N1cy1jb2xvcildIGZvY3VzLXZpc2libGU6b3V0bGluZS1vZmZzZXQtMicsXG4gIF0sXG4gIHtcbiAgICB2YXJpYW50czoge1xuICAgICAgdmFyaWFudDoge1xuICAgICAgICB0ZXJ0aWFyeTpcbiAgICAgICAgICAndGV4dC1ncmF5LTkwMCBlbmFibGVkOmhvdmVyOmJnLWdyYXktYWxwaGEtMjAwIGVuYWJsZWQ6aG92ZXI6dGV4dC1ncmF5LTEwMDAgZW5hYmxlZDphY3RpdmU6YmctZ3JheS1hbHBoYS0yMDAgZm9jdXMtdmlzaWJsZTp0ZXh0LWdyYXktMTAwMCcsXG4gICAgICAgIG11dGVkOlxuICAgICAgICAgICd0ZXh0LWdyYXktOTAwIGVuYWJsZWQ6aG92ZXI6YmctZ3JheS1hbHBoYS0yMDAgZW5hYmxlZDpob3Zlcjp0ZXh0LWdyYXktMTAwMCBlbmFibGVkOmFjdGl2ZTpiZy1ncmF5LWFscGhhLTIwMCBmb2N1cy12aXNpYmxlOnRleHQtZ3JheS0xMDAwJyxcbiAgICAgIH0sXG4gICAgICBzaXplOiB7XG4gICAgICAgIHRpbnk6ICdoLTYgdy02IHJvdW5kZWQtWzRweF0gWyZfc3ZnXTpoLTQgWyZfc3ZnXTp3LTQnLFxuICAgICAgICBzbWFsbDogJ2gtOCB3LTggcm91bmRlZC1tZCBbJl9zdmddOmgtNCBbJl9zdmddOnctNCcsXG4gICAgICB9LFxuICAgIH0sXG4gICAgZGVmYXVsdFZhcmlhbnRzOiB7XG4gICAgICB2YXJpYW50OiAndGVydGlhcnknLFxuICAgICAgc2l6ZTogJ3RpbnknLFxuICAgIH0sXG4gIH1cbik7XG5cbmV4cG9ydCB0eXBlIEljb25CdXR0b25Qcm9wcyA9IE9taXQ8XG4gIFJlYWN0LkJ1dHRvbkhUTUxBdHRyaWJ1dGVzPEhUTUxCdXR0b25FbGVtZW50PixcbiAgJ3R5cGUnXG4+ICZcbiAgVmFyaWFudFByb3BzPHR5cGVvZiBpY29uQnV0dG9uVmFyaWFudHM+ICYge1xuICAgICdhcmlhLWxhYmVsJzogc3RyaW5nO1xuICAgIHR5cGU/OiAnYnV0dG9uJyB8ICdzdWJtaXQnIHwgJ3Jlc2V0JztcbiAgfTtcblxuZXhwb3J0IGNvbnN0IEljb25CdXR0b24gPSBSZWFjdC5mb3J3YXJkUmVmPEhUTUxCdXR0b25FbGVtZW50LCBJY29uQnV0dG9uUHJvcHM+KFxuICAoeyBjbGFzc05hbWUsIHZhcmlhbnQsIHNpemUsIHR5cGUgPSAnYnV0dG9uJywgLi4ucHJvcHMgfSwgcmVmKSA9PiAoXG4gICAgPGJ1dHRvblxuICAgICAgcmVmPXtyZWZ9XG4gICAgICB0eXBlPXt0eXBlfVxuICAgICAgY2xhc3NOYW1lPXtjbihpY29uQnV0dG9uVmFyaWFudHMoeyB2YXJpYW50LCBzaXplLCBjbGFzc05hbWUgfSkpfVxuICAgICAgey4uLnByb3BzfVxuICAgIC8+XG4gIClcbik7XG5JY29uQnV0dG9uLmRpc3BsYXlOYW1lID0gJ0ljb25CdXR0b24nO1xuXG5leHBvcnQgeyBpY29uQnV0dG9uVmFyaWFudHMgfTtcbiJdfQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kbd.d.ts","sourceRoot":"","sources":["../../../src/components/ui/kbd.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,wBAAgB,GAAG,CAAC,EAClB,QAAQ,EACR,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,SAAS,CAWZ"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../../lib/utils';
|
|
3
|
+
export function Kbd({ children, className, }) {
|
|
4
|
+
return (_jsx("kbd", { className: cn('inline-flex h-5 min-w-5 items-center justify-center rounded bg-background-100/20 px-1 text-center font-sans text-xs leading-none', className), children: children }));
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoia2JkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvdWkva2JkLnRzeCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQ0EsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXJDLE1BQU0sVUFBVSxHQUFHLENBQUMsRUFDbEIsUUFBUSxFQUNSLFNBQVMsR0FJVjtJQUNDLE9BQU8sQ0FDTCxjQUNFLFNBQVMsRUFBRSxFQUFFLENBQ1gsa0lBQWtJLEVBQ2xJLFNBQVMsQ0FDVixZQUVBLFFBQVEsR0FDTCxDQUNQLENBQUM7QUFDSixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHR5cGUgeyBSZWFjdE5vZGUgfSBmcm9tICdyZWFjdCc7XG5pbXBvcnQgeyBjbiB9IGZyb20gJy4uLy4uL2xpYi91dGlscyc7XG5cbmV4cG9ydCBmdW5jdGlvbiBLYmQoe1xuICBjaGlsZHJlbixcbiAgY2xhc3NOYW1lLFxufToge1xuICBjaGlsZHJlbjogUmVhY3ROb2RlO1xuICBjbGFzc05hbWU/OiBzdHJpbmc7XG59KTogUmVhY3ROb2RlIHtcbiAgcmV0dXJuIChcbiAgICA8a2JkXG4gICAgICBjbGFzc05hbWU9e2NuKFxuICAgICAgICAnaW5saW5lLWZsZXggaC01IG1pbi13LTUgaXRlbXMtY2VudGVyIGp1c3RpZnktY2VudGVyIHJvdW5kZWQgYmctYmFja2dyb3VuZC0xMDAvMjAgcHgtMSB0ZXh0LWNlbnRlciBmb250LXNhbnMgdGV4dC14cyBsZWFkaW5nLW5vbmUnLFxuICAgICAgICBjbGFzc05hbWVcbiAgICAgICl9XG4gICAgPlxuICAgICAge2NoaWxkcmVufVxuICAgIDwva2JkPlxuICApO1xufVxuIl19
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
declare const TooltipProvider: React.FC<TooltipPrimitive.TooltipProviderProps>;
|
|
4
|
+
declare const Tooltip: React.FC<TooltipPrimitive.TooltipProps>;
|
|
5
|
+
declare const TooltipTrigger: React.ForwardRefExoticComponent<TooltipPrimitive.TooltipTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
6
|
+
declare const TooltipContent: React.ForwardRefExoticComponent<Omit<TooltipPrimitive.TooltipContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
7
|
+
showArrow?: boolean;
|
|
8
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
9
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
10
|
+
//# sourceMappingURL=tooltip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.d.ts","sourceRoot":"","sources":["../../../src/components/ui/tooltip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,gBAAgB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,QAAA,MAAM,eAAe,iDAA4B,CAAC;AAElD,QAAA,MAAM,OAAO,yCAAwB,CAAC;AAEtC,QAAA,MAAM,cAAc,gHAA2B,CAAC;AAEhD,QAAA,MAAM,cAAc;gBAGJ,OAAO;wCA4BtB,CAAC;AAGF,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
7
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
8
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
9
|
+
const TooltipContent = React.forwardRef(({ className, sideOffset = 8, showArrow = true, children, ...props }, ref) => (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn('z-[99999] flex items-center gap-1 rounded-[10px] bg-gray-1000 px-2 py-1.5 !text-label-13 text-background-100 shadow-md select-none', className), ...props, children: [children, showArrow ? (_jsx(TooltipPrimitive.Arrow, { width: 11, height: 5, className: "fill-gray-1000" })) : null] }) })));
|
|
10
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
11
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHRpcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3VpL3Rvb2x0aXAudHN4Il0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVksQ0FBQzs7QUFFYixPQUFPLEtBQUssZ0JBQWdCLE1BQU0seUJBQXlCLENBQUM7QUFDNUQsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxFQUFFLEVBQUUsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXJDLE1BQU0sZUFBZSxHQUFHLGdCQUFnQixDQUFDLFFBQVEsQ0FBQztBQUVsRCxNQUFNLE9BQU8sR0FBRyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUM7QUFFdEMsTUFBTSxjQUFjLEdBQUcsZ0JBQWdCLENBQUMsT0FBTyxDQUFDO0FBRWhELE1BQU0sY0FBYyxHQUFHLEtBQUssQ0FBQyxVQUFVLENBTXJDLENBQ0UsRUFBRSxTQUFTLEVBQUUsVUFBVSxHQUFHLENBQUMsRUFBRSxTQUFTLEdBQUcsSUFBSSxFQUFFLFFBQVEsRUFBRSxHQUFHLEtBQUssRUFBRSxFQUNuRSxHQUFHLEVBQ0gsRUFBRSxDQUFDLENBQ0gsS0FBQyxnQkFBZ0IsQ0FBQyxNQUFNLGNBQ3RCLE1BQUMsZ0JBQWdCLENBQUMsT0FBTyxJQUN2QixHQUFHLEVBQUUsR0FBRyxFQUNSLFVBQVUsRUFBRSxVQUFVLEVBQ3RCLFNBQVMsRUFBRSxFQUFFLENBQ1gsb0lBQW9JLEVBQ3BJLFNBQVMsQ0FDVixLQUNHLEtBQUssYUFFUixRQUFRLEVBQ1IsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUNYLEtBQUMsZ0JBQWdCLENBQUMsS0FBSyxJQUNyQixLQUFLLEVBQUUsRUFBRSxFQUNULE1BQU0sRUFBRSxDQUFDLEVBQ1QsU0FBUyxFQUFDLGdCQUFnQixHQUMxQixDQUNILENBQUMsQ0FBQyxDQUFDLElBQUksSUFDaUIsR0FDSCxDQUMzQixDQUNGLENBQUM7QUFDRixjQUFjLENBQUMsV0FBVyxHQUFHLGdCQUFnQixDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUM7QUFFbEUsT0FBTyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLGVBQWUsRUFBRSxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBjbGllbnQnO1xuXG5pbXBvcnQgKiBhcyBUb29sdGlwUHJpbWl0aXZlIGZyb20gJ0ByYWRpeC11aS9yZWFjdC10b29sdGlwJztcbmltcG9ydCAqIGFzIFJlYWN0IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7IGNuIH0gZnJvbSAnLi4vLi4vbGliL3V0aWxzJztcblxuY29uc3QgVG9vbHRpcFByb3ZpZGVyID0gVG9vbHRpcFByaW1pdGl2ZS5Qcm92aWRlcjtcblxuY29uc3QgVG9vbHRpcCA9IFRvb2x0aXBQcmltaXRpdmUuUm9vdDtcblxuY29uc3QgVG9vbHRpcFRyaWdnZXIgPSBUb29sdGlwUHJpbWl0aXZlLlRyaWdnZXI7XG5cbmNvbnN0IFRvb2x0aXBDb250ZW50ID0gUmVhY3QuZm9yd2FyZFJlZjxcbiAgUmVhY3QuRWxlbWVudFJlZjx0eXBlb2YgVG9vbHRpcFByaW1pdGl2ZS5Db250ZW50PixcbiAgUmVhY3QuQ29tcG9uZW50UHJvcHNXaXRob3V0UmVmPHR5cGVvZiBUb29sdGlwUHJpbWl0aXZlLkNvbnRlbnQ+ICYge1xuICAgIHNob3dBcnJvdz86IGJvb2xlYW47XG4gIH1cbj4oXG4gIChcbiAgICB7IGNsYXNzTmFtZSwgc2lkZU9mZnNldCA9IDgsIHNob3dBcnJvdyA9IHRydWUsIGNoaWxkcmVuLCAuLi5wcm9wcyB9LFxuICAgIHJlZlxuICApID0+IChcbiAgICA8VG9vbHRpcFByaW1pdGl2ZS5Qb3J0YWw+XG4gICAgICA8VG9vbHRpcFByaW1pdGl2ZS5Db250ZW50XG4gICAgICAgIHJlZj17cmVmfVxuICAgICAgICBzaWRlT2Zmc2V0PXtzaWRlT2Zmc2V0fVxuICAgICAgICBjbGFzc05hbWU9e2NuKFxuICAgICAgICAgICd6LVs5OTk5OV0gZmxleCBpdGVtcy1jZW50ZXIgZ2FwLTEgcm91bmRlZC1bMTBweF0gYmctZ3JheS0xMDAwIHB4LTIgcHktMS41ICF0ZXh0LWxhYmVsLTEzIHRleHQtYmFja2dyb3VuZC0xMDAgc2hhZG93LW1kIHNlbGVjdC1ub25lJyxcbiAgICAgICAgICBjbGFzc05hbWVcbiAgICAgICAgKX1cbiAgICAgICAgey4uLnByb3BzfVxuICAgICAgPlxuICAgICAgICB7Y2hpbGRyZW59XG4gICAgICAgIHtzaG93QXJyb3cgPyAoXG4gICAgICAgICAgPFRvb2x0aXBQcmltaXRpdmUuQXJyb3dcbiAgICAgICAgICAgIHdpZHRoPXsxMX1cbiAgICAgICAgICAgIGhlaWdodD17NX1cbiAgICAgICAgICAgIGNsYXNzTmFtZT1cImZpbGwtZ3JheS0xMDAwXCJcbiAgICAgICAgICAvPlxuICAgICAgICApIDogbnVsbH1cbiAgICAgIDwvVG9vbHRpcFByaW1pdGl2ZS5Db250ZW50PlxuICAgIDwvVG9vbHRpcFByaW1pdGl2ZS5Qb3J0YWw+XG4gIClcbik7XG5Ub29sdGlwQ29udGVudC5kaXNwbGF5TmFtZSA9IFRvb2x0aXBQcmltaXRpdmUuQ29udGVudC5kaXNwbGF5TmFtZTtcblxuZXhwb3J0IHsgVG9vbHRpcCwgVG9vbHRpcFRyaWdnZXIsIFRvb2x0aXBDb250ZW50LCBUb29sdGlwUHJvdmlkZXIgfTtcbiJdfQ==
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
interface UseIntersectionObserverArgs extends Omit<IntersectionObserverInit, 'root'> {
|
|
3
|
+
/**
|
|
4
|
+
* Scroll container the target is observed within. Defaults to the viewport.
|
|
5
|
+
* Passed as a ref so it resolves after commit (when the node exists).
|
|
6
|
+
*/
|
|
7
|
+
rootRef?: RefObject<Element | null>;
|
|
8
|
+
freezeOnceVisible?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Returns the latest IntersectionObserverEntry for `elementRef`.
|
|
12
|
+
* Ported from vercel/front's `@vercel/hooks` `useIntersectionObserver`, with
|
|
13
|
+
* `root` threaded as a ref so we can observe inside a custom scroll container.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useIntersectionObserver(elementRef: RefObject<Element | null>, { threshold, rootRef, rootMargin, freezeOnceVisible, }?: UseIntersectionObserverArgs): IntersectionObserverEntry | undefined;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=use-intersection-observer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-intersection-observer.d.ts","sourceRoot":"","sources":["../../src/hooks/use-intersection-observer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,UAAU,2BACR,SAAQ,IAAI,CAAC,wBAAwB,EAAE,MAAM,CAAC;IAC9C;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACpC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,UAAU,EAAE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,EACrC,EACE,SAAa,EACb,OAAO,EACP,UAAiB,EACjB,iBAAyB,GAC1B,GAAE,2BAAgC,GAClC,yBAAyB,GAAG,SAAS,CA4BvC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the latest IntersectionObserverEntry for `elementRef`.
|
|
5
|
+
* Ported from vercel/front's `@vercel/hooks` `useIntersectionObserver`, with
|
|
6
|
+
* `root` threaded as a ref so we can observe inside a custom scroll container.
|
|
7
|
+
*/
|
|
8
|
+
export function useIntersectionObserver(elementRef, { threshold = 0, rootRef, rootMargin = '0%', freezeOnceVisible = false, } = {}) {
|
|
9
|
+
const [entry, setEntry] = useState();
|
|
10
|
+
const frozen = entry?.isIntersecting && freezeOnceVisible;
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const node = elementRef?.current;
|
|
13
|
+
const hasIOSupport = !!window.IntersectionObserver;
|
|
14
|
+
if (!hasIOSupport || frozen || !node)
|
|
15
|
+
return;
|
|
16
|
+
const updateEntry = ([entry]) => {
|
|
17
|
+
setEntry(entry);
|
|
18
|
+
};
|
|
19
|
+
const observer = new IntersectionObserver(updateEntry, {
|
|
20
|
+
threshold,
|
|
21
|
+
root: rootRef?.current ?? null,
|
|
22
|
+
rootMargin,
|
|
23
|
+
});
|
|
24
|
+
observer.observe(node);
|
|
25
|
+
return () => observer.disconnect();
|
|
26
|
+
}, [elementRef, threshold, rootRef, rootMargin, frozen]);
|
|
27
|
+
return entry;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlLWludGVyc2VjdGlvbi1vYnNlcnZlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ob29rcy91c2UtaW50ZXJzZWN0aW9uLW9ic2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVksQ0FBQztBQUdiLE9BQU8sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBWTVDOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsdUJBQXVCLENBQ3JDLFVBQXFDLEVBQ3JDLEVBQ0UsU0FBUyxHQUFHLENBQUMsRUFDYixPQUFPLEVBQ1AsVUFBVSxHQUFHLElBQUksRUFDakIsaUJBQWlCLEdBQUcsS0FBSyxNQUNNLEVBQUU7SUFFbkMsTUFBTSxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsR0FBRyxRQUFRLEVBQTZCLENBQUM7SUFFaEUsTUFBTSxNQUFNLEdBQUcsS0FBSyxFQUFFLGNBQWMsSUFBSSxpQkFBaUIsQ0FBQztJQUUxRCxTQUFTLENBQUMsR0FBRyxFQUFFO1FBQ2IsTUFBTSxJQUFJLEdBQUcsVUFBVSxFQUFFLE9BQU8sQ0FBQztRQUVqQyxNQUFNLFlBQVksR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDLG9CQUFvQixDQUFDO1FBRW5ELElBQUksQ0FBQyxZQUFZLElBQUksTUFBTSxJQUFJLENBQUMsSUFBSTtZQUFFLE9BQU87UUFFN0MsTUFBTSxXQUFXLEdBQUcsQ0FBQyxDQUFDLEtBQUssQ0FBOEIsRUFBUSxFQUFFO1lBQ2pFLFFBQVEsQ0FBQyxLQUFLLENBQUMsQ0FBQztRQUNsQixDQUFDLENBQUM7UUFFRixNQUFNLFFBQVEsR0FBRyxJQUFJLG9CQUFvQixDQUFDLFdBQVcsRUFBRTtZQUNyRCxTQUFTO1lBQ1QsSUFBSSxFQUFFLE9BQU8sRUFBRSxPQUFPLElBQUksSUFBSTtZQUM5QixVQUFVO1NBQ1gsQ0FBQyxDQUFDO1FBRUgsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUV2QixPQUFPLEdBQUcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNyQyxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQztJQUV6RCxPQUFPLEtBQUssQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIGNsaWVudCc7XG5cbmltcG9ydCB0eXBlIHsgUmVmT2JqZWN0IH0gZnJvbSAncmVhY3QnO1xuaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gJ3JlYWN0JztcblxuaW50ZXJmYWNlIFVzZUludGVyc2VjdGlvbk9ic2VydmVyQXJnc1xuICBleHRlbmRzIE9taXQ8SW50ZXJzZWN0aW9uT2JzZXJ2ZXJJbml0LCAncm9vdCc+IHtcbiAgLyoqXG4gICAqIFNjcm9sbCBjb250YWluZXIgdGhlIHRhcmdldCBpcyBvYnNlcnZlZCB3aXRoaW4uIERlZmF1bHRzIHRvIHRoZSB2aWV3cG9ydC5cbiAgICogUGFzc2VkIGFzIGEgcmVmIHNvIGl0IHJlc29sdmVzIGFmdGVyIGNvbW1pdCAod2hlbiB0aGUgbm9kZSBleGlzdHMpLlxuICAgKi9cbiAgcm9vdFJlZj86IFJlZk9iamVjdDxFbGVtZW50IHwgbnVsbD47XG4gIGZyZWV6ZU9uY2VWaXNpYmxlPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIHRoZSBsYXRlc3QgSW50ZXJzZWN0aW9uT2JzZXJ2ZXJFbnRyeSBmb3IgYGVsZW1lbnRSZWZgLlxuICogUG9ydGVkIGZyb20gdmVyY2VsL2Zyb250J3MgYEB2ZXJjZWwvaG9va3NgIGB1c2VJbnRlcnNlY3Rpb25PYnNlcnZlcmAsIHdpdGhcbiAqIGByb290YCB0aHJlYWRlZCBhcyBhIHJlZiBzbyB3ZSBjYW4gb2JzZXJ2ZSBpbnNpZGUgYSBjdXN0b20gc2Nyb2xsIGNvbnRhaW5lci5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZUludGVyc2VjdGlvbk9ic2VydmVyKFxuICBlbGVtZW50UmVmOiBSZWZPYmplY3Q8RWxlbWVudCB8IG51bGw+LFxuICB7XG4gICAgdGhyZXNob2xkID0gMCxcbiAgICByb290UmVmLFxuICAgIHJvb3RNYXJnaW4gPSAnMCUnLFxuICAgIGZyZWV6ZU9uY2VWaXNpYmxlID0gZmFsc2UsXG4gIH06IFVzZUludGVyc2VjdGlvbk9ic2VydmVyQXJncyA9IHt9XG4pOiBJbnRlcnNlY3Rpb25PYnNlcnZlckVudHJ5IHwgdW5kZWZpbmVkIHtcbiAgY29uc3QgW2VudHJ5LCBzZXRFbnRyeV0gPSB1c2VTdGF0ZTxJbnRlcnNlY3Rpb25PYnNlcnZlckVudHJ5PigpO1xuXG4gIGNvbnN0IGZyb3plbiA9IGVudHJ5Py5pc0ludGVyc2VjdGluZyAmJiBmcmVlemVPbmNlVmlzaWJsZTtcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnN0IG5vZGUgPSBlbGVtZW50UmVmPy5jdXJyZW50O1xuXG4gICAgY29uc3QgaGFzSU9TdXBwb3J0ID0gISF3aW5kb3cuSW50ZXJzZWN0aW9uT2JzZXJ2ZXI7XG5cbiAgICBpZiAoIWhhc0lPU3VwcG9ydCB8fCBmcm96ZW4gfHwgIW5vZGUpIHJldHVybjtcblxuICAgIGNvbnN0IHVwZGF0ZUVudHJ5ID0gKFtlbnRyeV06IEludGVyc2VjdGlvbk9ic2VydmVyRW50cnlbXSk6IHZvaWQgPT4ge1xuICAgICAgc2V0RW50cnkoZW50cnkpO1xuICAgIH07XG5cbiAgICBjb25zdCBvYnNlcnZlciA9IG5ldyBJbnRlcnNlY3Rpb25PYnNlcnZlcih1cGRhdGVFbnRyeSwge1xuICAgICAgdGhyZXNob2xkLFxuICAgICAgcm9vdDogcm9vdFJlZj8uY3VycmVudCA/PyBudWxsLFxuICAgICAgcm9vdE1hcmdpbixcbiAgICB9KTtcblxuICAgIG9ic2VydmVyLm9ic2VydmUobm9kZSk7XG5cbiAgICByZXR1cm4gKCkgPT4gb2JzZXJ2ZXIuZGlzY29ubmVjdCgpO1xuICB9LCBbZWxlbWVudFJlZiwgdGhyZXNob2xkLCByb290UmVmLCByb290TWFyZ2luLCBmcm96ZW5dKTtcblxuICByZXR1cm4gZW50cnk7XG59XG4iXX0=
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
2
|
+
interface UseLoadMoreOnScrollOptions {
|
|
3
|
+
hasMore: boolean;
|
|
4
|
+
isLoadingMore: boolean;
|
|
5
|
+
/** Scroll container the sentinel lives in. Defaults to the viewport. */
|
|
6
|
+
rootRef?: RefObject<Element | null>;
|
|
7
|
+
rootMargin?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Triggers `loadMore` when a sentinel element scrolls into view.
|
|
11
|
+
* Returns a ref to attach to an invisible sentinel `<div>` placed after the
|
|
12
|
+
* list content. Ported from vercel/front's `@vercel/hooks`
|
|
13
|
+
* `useLoadMoreOnScroll`.
|
|
14
|
+
*
|
|
15
|
+
* Because it keys off `isIntersecting`/`isLoadingMore`, it self-continues:
|
|
16
|
+
* if the sentinel is still in view once a page finishes loading, the next
|
|
17
|
+
* page is requested automatically.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useLoadMoreOnScroll(loadMore: () => void, { hasMore, isLoadingMore, rootRef, rootMargin, }: UseLoadMoreOnScrollOptions): RefObject<HTMLDivElement | null>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=use-load-more-on-scroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-load-more-on-scroll.d.ts","sourceRoot":"","sources":["../../src/hooks/use-load-more-on-scroll.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,UAAU,0BAA0B;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,IAAI,EACpB,EACE,OAAO,EACP,aAAa,EACb,OAAO,EACP,UAAoB,GACrB,EAAE,0BAA0B,oCAiB9B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useIntersectionObserver } from './use-intersection-observer';
|
|
4
|
+
/**
|
|
5
|
+
* Triggers `loadMore` when a sentinel element scrolls into view.
|
|
6
|
+
* Returns a ref to attach to an invisible sentinel `<div>` placed after the
|
|
7
|
+
* list content. Ported from vercel/front's `@vercel/hooks`
|
|
8
|
+
* `useLoadMoreOnScroll`.
|
|
9
|
+
*
|
|
10
|
+
* Because it keys off `isIntersecting`/`isLoadingMore`, it self-continues:
|
|
11
|
+
* if the sentinel is still in view once a page finishes loading, the next
|
|
12
|
+
* page is requested automatically.
|
|
13
|
+
*/
|
|
14
|
+
export function useLoadMoreOnScroll(loadMore, { hasMore, isLoadingMore, rootRef, rootMargin = '400px', }) {
|
|
15
|
+
const sentinelRef = useRef(null);
|
|
16
|
+
const entry = useIntersectionObserver(sentinelRef, { rootRef, rootMargin });
|
|
17
|
+
const loadMoreRef = useRef(loadMore);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
loadMoreRef.current = loadMore;
|
|
20
|
+
}, [loadMore]);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (entry?.isIntersecting && hasMore && !isLoadingMore) {
|
|
23
|
+
loadMoreRef.current();
|
|
24
|
+
}
|
|
25
|
+
}, [entry?.isIntersecting, hasMore, isLoadingMore]);
|
|
26
|
+
return sentinelRef;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXNlLWxvYWQtbW9yZS1vbi1zY3JvbGwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaG9va3MvdXNlLWxvYWQtbW9yZS1vbi1zY3JvbGwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWSxDQUFDO0FBR2IsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsTUFBTSxPQUFPLENBQUM7QUFDMUMsT0FBTyxFQUFFLHVCQUF1QixFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFVdEU7Ozs7Ozs7OztHQVNHO0FBQ0gsTUFBTSxVQUFVLG1CQUFtQixDQUNqQyxRQUFvQixFQUNwQixFQUNFLE9BQU8sRUFDUCxhQUFhLEVBQ2IsT0FBTyxFQUNQLFVBQVUsR0FBRyxPQUFPLEdBQ087SUFFN0IsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFpQixJQUFJLENBQUMsQ0FBQztJQUNqRCxNQUFNLEtBQUssR0FBRyx1QkFBdUIsQ0FBQyxXQUFXLEVBQUUsRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQztJQUU1RSxNQUFNLFdBQVcsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDckMsU0FBUyxDQUFDLEdBQUcsRUFBRTtRQUNiLFdBQVcsQ0FBQyxPQUFPLEdBQUcsUUFBUSxDQUFDO0lBQ2pDLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7SUFFZixTQUFTLENBQUMsR0FBRyxFQUFFO1FBQ2IsSUFBSSxLQUFLLEVBQUUsY0FBYyxJQUFJLE9BQU8sSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ3ZELFdBQVcsQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUN4QixDQUFDO0lBQ0gsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLGNBQWMsRUFBRSxPQUFPLEVBQUUsYUFBYSxDQUFDLENBQUMsQ0FBQztJQUVwRCxPQUFPLFdBQVcsQ0FBQztBQUNyQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiJ3VzZSBjbGllbnQnO1xuXG5pbXBvcnQgdHlwZSB7IFJlZk9iamVjdCB9IGZyb20gJ3JlYWN0JztcbmltcG9ydCB7IHVzZUVmZmVjdCwgdXNlUmVmIH0gZnJvbSAncmVhY3QnO1xuaW1wb3J0IHsgdXNlSW50ZXJzZWN0aW9uT2JzZXJ2ZXIgfSBmcm9tICcuL3VzZS1pbnRlcnNlY3Rpb24tb2JzZXJ2ZXInO1xuXG5pbnRlcmZhY2UgVXNlTG9hZE1vcmVPblNjcm9sbE9wdGlvbnMge1xuICBoYXNNb3JlOiBib29sZWFuO1xuICBpc0xvYWRpbmdNb3JlOiBib29sZWFuO1xuICAvKiogU2Nyb2xsIGNvbnRhaW5lciB0aGUgc2VudGluZWwgbGl2ZXMgaW4uIERlZmF1bHRzIHRvIHRoZSB2aWV3cG9ydC4gKi9cbiAgcm9vdFJlZj86IFJlZk9iamVjdDxFbGVtZW50IHwgbnVsbD47XG4gIHJvb3RNYXJnaW4/OiBzdHJpbmc7XG59XG5cbi8qKlxuICogVHJpZ2dlcnMgYGxvYWRNb3JlYCB3aGVuIGEgc2VudGluZWwgZWxlbWVudCBzY3JvbGxzIGludG8gdmlldy5cbiAqIFJldHVybnMgYSByZWYgdG8gYXR0YWNoIHRvIGFuIGludmlzaWJsZSBzZW50aW5lbCBgPGRpdj5gIHBsYWNlZCBhZnRlciB0aGVcbiAqIGxpc3QgY29udGVudC4gUG9ydGVkIGZyb20gdmVyY2VsL2Zyb250J3MgYEB2ZXJjZWwvaG9va3NgXG4gKiBgdXNlTG9hZE1vcmVPblNjcm9sbGAuXG4gKlxuICogQmVjYXVzZSBpdCBrZXlzIG9mZiBgaXNJbnRlcnNlY3RpbmdgL2Bpc0xvYWRpbmdNb3JlYCwgaXQgc2VsZi1jb250aW51ZXM6XG4gKiBpZiB0aGUgc2VudGluZWwgaXMgc3RpbGwgaW4gdmlldyBvbmNlIGEgcGFnZSBmaW5pc2hlcyBsb2FkaW5nLCB0aGUgbmV4dFxuICogcGFnZSBpcyByZXF1ZXN0ZWQgYXV0b21hdGljYWxseS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHVzZUxvYWRNb3JlT25TY3JvbGwoXG4gIGxvYWRNb3JlOiAoKSA9PiB2b2lkLFxuICB7XG4gICAgaGFzTW9yZSxcbiAgICBpc0xvYWRpbmdNb3JlLFxuICAgIHJvb3RSZWYsXG4gICAgcm9vdE1hcmdpbiA9ICc0MDBweCcsXG4gIH06IFVzZUxvYWRNb3JlT25TY3JvbGxPcHRpb25zXG4pIHtcbiAgY29uc3Qgc2VudGluZWxSZWYgPSB1c2VSZWY8SFRNTERpdkVsZW1lbnQ+KG51bGwpO1xuICBjb25zdCBlbnRyeSA9IHVzZUludGVyc2VjdGlvbk9ic2VydmVyKHNlbnRpbmVsUmVmLCB7IHJvb3RSZWYsIHJvb3RNYXJnaW4gfSk7XG5cbiAgY29uc3QgbG9hZE1vcmVSZWYgPSB1c2VSZWYobG9hZE1vcmUpO1xuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGxvYWRNb3JlUmVmLmN1cnJlbnQgPSBsb2FkTW9yZTtcbiAgfSwgW2xvYWRNb3JlXSk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBpZiAoZW50cnk/LmlzSW50ZXJzZWN0aW5nICYmIGhhc01vcmUgJiYgIWlzTG9hZGluZ01vcmUpIHtcbiAgICAgIGxvYWRNb3JlUmVmLmN1cnJlbnQoKTtcbiAgICB9XG4gIH0sIFtlbnRyeT8uaXNJbnRlcnNlY3RpbmcsIGhhc01vcmUsIGlzTG9hZGluZ01vcmVdKTtcblxuICByZXR1cm4gc2VudGluZWxSZWY7XG59XG4iXX0=
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workflow/web-shared",
|
|
3
3
|
"description": "Shared components for Workflow Observability UI",
|
|
4
|
-
"version": "5.0.0-beta.
|
|
4
|
+
"version": "5.0.0-beta.11",
|
|
5
5
|
"private": false,
|
|
6
6
|
"files": [
|
|
7
7
|
"dist",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"streamdown": "2.3.0",
|
|
54
54
|
"tailwind-merge": "3.5.0",
|
|
55
55
|
"tailwindcss": "4",
|
|
56
|
-
"@workflow/core": "5.0.0-beta.
|
|
56
|
+
"@workflow/core": "5.0.0-beta.11",
|
|
57
57
|
"@workflow/utils": "5.0.0-beta.3",
|
|
58
|
-
"@workflow/world": "5.0.0-beta.
|
|
58
|
+
"@workflow/world": "5.0.0-beta.6"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@biomejs/biome": "^2.4.4",
|
package/src/components/index.ts
CHANGED
|
@@ -38,3 +38,4 @@ export { MenuDropdown, type MenuDropdownOption } from './ui/menu-dropdown';
|
|
|
38
38
|
export { Spinner } from './ui/spinner';
|
|
39
39
|
export { WorkflowTraceViewer } from './workflow-trace-view';
|
|
40
40
|
export { NewTraceViewer } from './trace-viewer-new';
|
|
41
|
+
export { TraceViewerSkeleton } from './new-trace-viewer/components/trace-viewer-skeleton';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Circle } from 'lucide-react';
|
|
2
|
+
import { useRef } from 'react';
|
|
2
3
|
import { cn } from '../../../lib/utils';
|
|
3
4
|
import type { Span } from '../../trace-viewer/types';
|
|
4
5
|
import { formatDuration } from '../../trace-viewer/util/timing';
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
import { isSpanDimmedBySearch, type SpanSearchResult } from '../search';
|
|
12
13
|
import { getSpanDurationMs } from '../utils';
|
|
13
14
|
import { MiddleTruncate } from './middle-truncate/middle-truncate';
|
|
15
|
+
import { ROW_HEIGHT_PX, useRowWindow } from './use-row-window';
|
|
14
16
|
|
|
15
17
|
interface EventStyle {
|
|
16
18
|
icon: React.ComponentType<{ className?: string }>;
|
|
@@ -103,23 +105,29 @@ const EventList = ({
|
|
|
103
105
|
searchResult: SpanSearchResult;
|
|
104
106
|
onSelectSpan: (spanId: string) => void;
|
|
105
107
|
}) => {
|
|
108
|
+
const listRef = useRef<HTMLUListElement>(null);
|
|
109
|
+
const { start, end } = useRowWindow(listRef, spans.length, ROW_HEIGHT_PX);
|
|
110
|
+
|
|
106
111
|
return (
|
|
107
112
|
<ul
|
|
113
|
+
ref={listRef}
|
|
108
114
|
id="event-list"
|
|
109
115
|
role="tree"
|
|
110
116
|
className="block min-h-0 overflow-visible divide-y divide-gray-alpha-400 border-b border-gray-alpha-400"
|
|
117
|
+
style={{
|
|
118
|
+
paddingTop: start * ROW_HEIGHT_PX,
|
|
119
|
+
paddingBottom: (spans.length - end) * ROW_HEIGHT_PX,
|
|
120
|
+
}}
|
|
111
121
|
>
|
|
112
|
-
{spans.map((span) =>
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
})}
|
|
122
|
+
{spans.slice(start, end).map((span) => (
|
|
123
|
+
<EventRow
|
|
124
|
+
key={span.spanId}
|
|
125
|
+
span={span}
|
|
126
|
+
isSelected={span.spanId === activeSpanId}
|
|
127
|
+
isDimmed={isSpanDimmedBySearch(span.spanId, searchResult)}
|
|
128
|
+
onSelectSpan={onSelectSpan}
|
|
129
|
+
/>
|
|
130
|
+
))}
|
|
123
131
|
</ul>
|
|
124
132
|
);
|
|
125
133
|
};
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
Children,
|
|
5
5
|
type ReactNode,
|
|
6
6
|
type PointerEvent as ReactPointerEvent,
|
|
7
|
+
type RefObject,
|
|
7
8
|
useCallback,
|
|
8
9
|
useEffect,
|
|
9
10
|
useRef,
|
|
@@ -28,6 +29,7 @@ export interface SplitPaneProps {
|
|
|
28
29
|
startHeader?: ReactNode;
|
|
29
30
|
/** Fixed (non-scrolling) header rendered above the end pane. */
|
|
30
31
|
endHeader?: ReactNode;
|
|
32
|
+
scrollContainerRef?: RefObject<HTMLDivElement | null>;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export function SplitPane({
|
|
@@ -36,6 +38,7 @@ export function SplitPane({
|
|
|
36
38
|
defaultStartWidth = DEFAULT_START_PX,
|
|
37
39
|
startHeader,
|
|
38
40
|
endHeader,
|
|
41
|
+
scrollContainerRef,
|
|
39
42
|
}: SplitPaneProps) {
|
|
40
43
|
const parts = Children.toArray(children);
|
|
41
44
|
if (parts.length !== 2) {
|
|
@@ -57,6 +60,16 @@ export function SplitPane({
|
|
|
57
60
|
};
|
|
58
61
|
}, []);
|
|
59
62
|
|
|
63
|
+
const setContainerRef = useCallback(
|
|
64
|
+
(node: HTMLDivElement | null) => {
|
|
65
|
+
containerRef.current = node;
|
|
66
|
+
if (scrollContainerRef) {
|
|
67
|
+
scrollContainerRef.current = node;
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
[scrollContainerRef]
|
|
71
|
+
);
|
|
72
|
+
|
|
60
73
|
const clampPx = useCallback((px: number) => {
|
|
61
74
|
const el = containerRef.current;
|
|
62
75
|
if (!el) return px;
|
|
@@ -152,7 +165,7 @@ export function SplitPane({
|
|
|
152
165
|
<div>{endHeader}</div>
|
|
153
166
|
</div>
|
|
154
167
|
<div
|
|
155
|
-
ref={
|
|
168
|
+
ref={setContainerRef}
|
|
156
169
|
className={cn(
|
|
157
170
|
'grid flex-1 min-h-0 overflow-x-hidden overflow-y-auto',
|
|
158
171
|
isDragging && 'select-none'
|
|
@@ -169,7 +182,7 @@ export function SplitPane({
|
|
|
169
182
|
|
|
170
183
|
return (
|
|
171
184
|
<div
|
|
172
|
-
ref={
|
|
185
|
+
ref={setContainerRef}
|
|
173
186
|
className={cn(
|
|
174
187
|
'grid h-full min-h-0 content-start overflow-x-hidden overflow-y-auto',
|
|
175
188
|
isDragging && 'select-none',
|
|
@@ -6,14 +6,15 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
6
6
|
import { cn } from '../../../lib/utils';
|
|
7
7
|
import type { Span } from '../../trace-viewer/types';
|
|
8
8
|
import { formatDuration, getHighResInMs } from '../../trace-viewer/util/timing';
|
|
9
|
-
import type { Segment, SegmentStatus, TimeMarker } from '../utils';
|
|
10
9
|
import { isSpanDimmedBySearch, type SpanSearchResult } from '../search';
|
|
10
|
+
import type { Segment, SegmentStatus, TimeMarker } from '../utils';
|
|
11
11
|
import {
|
|
12
12
|
computeSpanGaps,
|
|
13
13
|
computeSpanSegments,
|
|
14
14
|
getResourceColor,
|
|
15
15
|
getSpanDurationMs,
|
|
16
16
|
} from '../utils';
|
|
17
|
+
import { ROW_HEIGHT_PX, useRowWindow } from './use-row-window';
|
|
17
18
|
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// Constants
|
|
@@ -365,10 +366,6 @@ export { TimelineBar };
|
|
|
365
366
|
// DeltaIndicator (Alt-key gap overlay)
|
|
366
367
|
// ---------------------------------------------------------------------------
|
|
367
368
|
|
|
368
|
-
// Row and indicator sizes are local to DeltaIndicator since it's the only
|
|
369
|
-
// place that needs to compute Y positions from a row index. ROW_HEIGHT must
|
|
370
|
-
// match the `h-10` (40px) row used in TimelineBar.
|
|
371
|
-
const DELTA_ROW_HEIGHT_PX = 40;
|
|
372
369
|
const DELTA_CAP_HEIGHT_PX = 8;
|
|
373
370
|
// Vertical offset to sit the indicator inside the gap between row N and N+1,
|
|
374
371
|
// aligned with where the bar starts in the next row (rows center a 24px bar
|
|
@@ -386,7 +383,7 @@ const DeltaIndicator = memo(function DeltaIndicator({
|
|
|
386
383
|
label: string;
|
|
387
384
|
rowIndex: number;
|
|
388
385
|
}) {
|
|
389
|
-
const centerY = DELTA_ROW_OFFSET_PX + (rowIndex + 1) *
|
|
386
|
+
const centerY = DELTA_ROW_OFFSET_PX + (rowIndex + 1) * ROW_HEIGHT_PX;
|
|
390
387
|
|
|
391
388
|
return (
|
|
392
389
|
<div
|
|
@@ -473,6 +470,11 @@ export function Timeline({
|
|
|
473
470
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
474
471
|
const viewDuration = viewEnd - viewStart;
|
|
475
472
|
const timelineWidth = Math.max(0, containerWidth - TIMELINE_PADDING_PX * 2);
|
|
473
|
+
const { start, end } = useRowWindow(
|
|
474
|
+
containerRef,
|
|
475
|
+
spans.length,
|
|
476
|
+
ROW_HEIGHT_PX
|
|
477
|
+
);
|
|
476
478
|
|
|
477
479
|
useEffect(() => {
|
|
478
480
|
const el = containerRef.current;
|
|
@@ -490,7 +492,11 @@ export function Timeline({
|
|
|
490
492
|
);
|
|
491
493
|
|
|
492
494
|
return (
|
|
493
|
-
<div
|
|
495
|
+
<div
|
|
496
|
+
ref={containerRef}
|
|
497
|
+
className="relative h-full overflow-hidden"
|
|
498
|
+
style={{ minHeight: spans.length * ROW_HEIGHT_PX }}
|
|
499
|
+
>
|
|
494
500
|
<div
|
|
495
501
|
aria-hidden
|
|
496
502
|
className="absolute inset-y-0 pointer-events-none"
|
|
@@ -518,18 +524,20 @@ export function Timeline({
|
|
|
518
524
|
/>
|
|
519
525
|
</div>
|
|
520
526
|
)}
|
|
521
|
-
{
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
527
|
+
<div style={{ transform: `translateY(${start * ROW_HEIGHT_PX}px)` }}>
|
|
528
|
+
{spans.slice(start, end).map((span) => (
|
|
529
|
+
<TimelineBar
|
|
530
|
+
key={span.spanId}
|
|
531
|
+
span={span}
|
|
532
|
+
viewStart={viewStart}
|
|
533
|
+
viewDuration={viewDuration}
|
|
534
|
+
containerWidth={timelineWidth}
|
|
535
|
+
isSelected={selectedId === span.spanId}
|
|
536
|
+
isDimmed={isSpanDimmedBySearch(span.spanId, searchResult)}
|
|
537
|
+
onSelect={onSelect}
|
|
538
|
+
/>
|
|
539
|
+
))}
|
|
540
|
+
</div>
|
|
533
541
|
{altHeld && (
|
|
534
542
|
<div
|
|
535
543
|
aria-hidden
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Skeleton } from '../../ui/skeleton';
|
|
2
|
+
|
|
3
|
+
const COL_TEMPLATE = '340px 1px minmax(50px, 1fr)';
|
|
4
|
+
|
|
5
|
+
const ROWS: { id: string; name: number; off: number; bar: number }[] = [
|
|
6
|
+
{ id: 'r0', name: 62, off: 0, bar: 72 },
|
|
7
|
+
{ id: 'r1', name: 78, off: 6, bar: 48 },
|
|
8
|
+
{ id: 'r2', name: 50, off: 10, bar: 55 },
|
|
9
|
+
{ id: 'r3', name: 84, off: 18, bar: 30 },
|
|
10
|
+
{ id: 'r4', name: 45, off: 18, bar: 42 },
|
|
11
|
+
{ id: 'r5', name: 66, off: 34, bar: 38 },
|
|
12
|
+
{ id: 'r6', name: 55, off: 41, bar: 25 },
|
|
13
|
+
{ id: 'r7', name: 40, off: 50, bar: 30 },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const HEADER_MARKERS = ['m0', 'm1', 'm2', 'm3'];
|
|
17
|
+
|
|
18
|
+
const HeaderDivider = () => (
|
|
19
|
+
<div className="flex justify-center">
|
|
20
|
+
<span aria-hidden className="h-full w-px bg-gray-alpha-400" />
|
|
21
|
+
</div>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export function TraceViewerSkeleton() {
|
|
25
|
+
return (
|
|
26
|
+
<output
|
|
27
|
+
aria-busy="true"
|
|
28
|
+
aria-label="Loading trace"
|
|
29
|
+
className="flex flex-col w-full h-full min-h-0 bg-background-100"
|
|
30
|
+
>
|
|
31
|
+
<span className="sr-only">Loading trace…</span>
|
|
32
|
+
|
|
33
|
+
{/* Header row: search header | divider | timeline header */}
|
|
34
|
+
<div
|
|
35
|
+
className="shrink-0 grid"
|
|
36
|
+
style={{ gridTemplateColumns: COL_TEMPLATE }}
|
|
37
|
+
>
|
|
38
|
+
<div className="h-10 min-h-10 flex items-center border-b border-gray-alpha-400 pl-4 pr-2 gap-1.5">
|
|
39
|
+
<Skeleton className="w-3.5 h-3.5 shrink-0 rounded-sm" />
|
|
40
|
+
<Skeleton className="h-3.5 w-40" />
|
|
41
|
+
</div>
|
|
42
|
+
<HeaderDivider />
|
|
43
|
+
<div className="h-10 min-h-10 flex items-end border-b border-gray-alpha-400 px-4 pb-1 gap-2">
|
|
44
|
+
<div className="relative flex-1 flex items-end justify-between">
|
|
45
|
+
{HEADER_MARKERS.map((id) => (
|
|
46
|
+
<Skeleton key={id} className="h-3.5 w-9" />
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
{/* Content row: event list | gutter | timeline */}
|
|
53
|
+
<div
|
|
54
|
+
className="grid flex-1 min-h-0 overflow-hidden"
|
|
55
|
+
style={{ gridTemplateColumns: COL_TEMPLATE }}
|
|
56
|
+
>
|
|
57
|
+
{/* Sidebar event rows */}
|
|
58
|
+
<div className="block overflow-visible">
|
|
59
|
+
<ul className="block divide-y divide-gray-alpha-400 border-b border-gray-alpha-400">
|
|
60
|
+
{ROWS.map((row) => (
|
|
61
|
+
<li key={row.id} className="h-10 flex items-center pl-4 pr-2">
|
|
62
|
+
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
63
|
+
<Skeleton className="w-4 h-4 shrink-0 rounded-sm" />
|
|
64
|
+
<Skeleton
|
|
65
|
+
className="h-3.5"
|
|
66
|
+
style={{ width: `${row.name}%` }}
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
<Skeleton className="ml-2 h-3.5 w-10 shrink-0" />
|
|
70
|
+
</li>
|
|
71
|
+
))}
|
|
72
|
+
</ul>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
{/* Gutter */}
|
|
76
|
+
<span aria-hidden className="h-full w-px bg-gray-alpha-400" />
|
|
77
|
+
|
|
78
|
+
{/* Timeline bars */}
|
|
79
|
+
<div className="relative min-h-0">
|
|
80
|
+
{ROWS.map((row) => (
|
|
81
|
+
<div key={row.id} className="relative h-10">
|
|
82
|
+
<div className="absolute inset-x-4 inset-y-0">
|
|
83
|
+
<Skeleton
|
|
84
|
+
className="absolute top-1/2 -translate-y-1/2 h-6 rounded-[0.25rem]"
|
|
85
|
+
style={{ left: `${row.off}%`, width: `${row.bar}%` }}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</output>
|
|
93
|
+
);
|
|
94
|
+
}
|