mirror of
https://github.com/hanzoai/cd-ui.git
synced 2026-10-30 20:42:45 +00:00
* refactor(deps): remove `moment` dep and usage `moment` has been [deprecated since Sep 2020](https://momentjs.com/docs/#/-project-status/) and recommends using native `Intl` or newer libraries that make use of native `Intl`, such as `luxon` and `dayjs` - `moment` is also a very large dependency and hence is ripe for pruning and replacement as well - remove `formatTimestamp` function from `v2/utils` as it is unused - it is unused [internally](https://github.com/search?q=repo%3Aargoproj%2Fargo-ui+formattimestamp&type=code) (it is only exported here), unused by [CD](https://github.com/search?q=repo%3Aargoproj%2Fargo-cd%20formattimestamp&type=code), unused by [Workflows](https://github.com/search?q=repo%3Aargoproj%2Fargo-workflows+formattimestamp&type=code) - it's also and unused by Rollouts, which has [two](https://github.com/argoproj/argo-rollouts/blob/c688dd89a7f883c8c004b4024b390104b5be5770/ui/src/app/shared/utils/utils.tsx#L17) [internal](https://github.com/argoproj/argo-rollouts/blob/c688dd89a7f883c8c004b4024b390104b5be5770/ui/src/app/components/rollout/revision.tsx#L21) copies of this function - change `Ticker` component to use plain `Date` instead of `moment` - downstream projects should get a type error on this which should make the switch straightforward - change `Duration` component to use existing `formatDuration` function which doesn't use `moment` - this will look different than the previous version, but the formatting between the two should really be consistent in any case - other option was to just remove this component entirely, since this repo is not actively maintained - leaving the component in instead of deleting it gives a more gradual off-ramp - CD uses this component in [two](https://github.com/argoproj/argo-cd/blob/9ecc5aec2a4dba485eb8b5b0ab75fff8927b3418/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx#L1) [places](https://github.com/argoproj/argo-cd/blob/9ecc5aec2a4dba485eb8b5b0ab75fff8927b3418/ui/src/app/applications/components/application-operation-state/application-operation-state.tsx#L1) and Rollouts uses this in [one place](https://github.com/argoproj/argo-rollouts/blob/c688dd89a7f883c8c004b4024b390104b5be5770/ui/src/app/components/pods/pods.tsx#L3) - Workflows does not use this, it has [its own `DurationPanel` component](https://github.com/argoproj/argo-workflows/blob/1dbc856e51967feb58066a4087a8679b08b87be3/ui/src/app/shared/components/duration-panel.tsx#L7) and its own [internal copy](https://github.com/argoproj/argo-workflows/blob/1dbc856e51967feb58066a4087a8679b08b87be3/ui/src/app/shared/duration.ts#L11) of the `formatDuration` function as well - none of these use the `allowNewLines` prop, so it is safe to remove Signed-off-by: Anton Gilgur <[email protected]> * fix(ui): duration component should div not mult ms -> s Signed-off-by: Anton Gilgur <[email protected]> * fix incorrect parameter naming - apparently `durationMs` was incorrectly named as it actually took seconds, not milliseconds - for backward compat, leave `durationMs`, but also introduce `durationS` - and add JSDoc to indicate this to users of the component Signed-off-by: Anton Gilgur <[email protected]> * use 2 significant figures Signed-off-by: Anton Gilgur <[email protected]> --------- Signed-off-by: Anton Gilgur <[email protected]>
134 lines
3.9 KiB
TypeScript
134 lines
3.9 KiB
TypeScript
import * as React from 'react';
|
|
|
|
export interface Error {
|
|
state: boolean;
|
|
retry: () => void;
|
|
}
|
|
|
|
export function useData<T>(getData: () => Promise<T>, init?: T, callback?: (data: T) => void, dependencies?: any[]): [T, boolean, Error] {
|
|
const [data, setData] = React.useState(init as T);
|
|
const [loading, setLoading] = React.useState(true);
|
|
const [error, setError] = React.useState(false);
|
|
const [retrying, retry] = React.useState(false);
|
|
React.useEffect(() => {
|
|
const fx = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const intData = await getData();
|
|
setLoading(false);
|
|
setData(intData);
|
|
if (callback) {
|
|
callback(intData);
|
|
}
|
|
} catch (e) {
|
|
setError(e);
|
|
}
|
|
};
|
|
fx();
|
|
}, [...(dependencies || []), retrying]);
|
|
return [data as T, loading, {state: error, retry: () => retry(!retrying)} as Error];
|
|
}
|
|
|
|
export const appendSuffixToClasses = (classNames: string, suffix: string): string => {
|
|
const clString = classNames;
|
|
const cl = (clString || '').split(' ') || [];
|
|
const suffixed = [];
|
|
for (const c of cl) {
|
|
if (!c.endsWith(suffix) && c !== ' ' && c !== '') {
|
|
suffixed.push(c + suffix);
|
|
}
|
|
}
|
|
return suffixed.join(' ');
|
|
};
|
|
|
|
export const useClickOutside = (ref: any, callback: () => void) => {
|
|
React.useEffect(() => {
|
|
const handler = (e: any) => {
|
|
if (ref.current && !ref.current.contains(e.target)) {
|
|
callback();
|
|
}
|
|
};
|
|
|
|
document.addEventListener('mousedown', handler);
|
|
return () => document.removeEventListener('mousedown', handler);
|
|
}, [ref, callback]);
|
|
};
|
|
|
|
export const useWidth = (ref: any): number => {
|
|
const [width, setWidth] = React.useState(0);
|
|
React.useEffect(() => {
|
|
setWidth(ref.current ? ref.current.offsetWidth : 0);
|
|
}, [ref]);
|
|
return width;
|
|
};
|
|
|
|
export const useTimeout = (fx: () => void, timeoutMs: number, dependencies: any[]) => {
|
|
React.useEffect(() => {
|
|
const to = setTimeout(fx, timeoutMs);
|
|
return () => clearInterval(to);
|
|
}, dependencies);
|
|
};
|
|
|
|
export const debounce = (fxn: () => any, ms: number) => {
|
|
let timer: any;
|
|
return () => {
|
|
clearTimeout(timer);
|
|
timer = setTimeout(() => {
|
|
timer = null;
|
|
fxn.apply(this);
|
|
}, ms);
|
|
};
|
|
};
|
|
|
|
export function formatDuration(seconds: number, sigfigs = 1) {
|
|
let remainingSeconds = Math.abs(Math.round(seconds));
|
|
let formattedDuration = '';
|
|
const figs = [];
|
|
|
|
if (remainingSeconds > 86400) {
|
|
const days = Math.floor(remainingSeconds / 86400) + 'd';
|
|
figs.push(days);
|
|
formattedDuration += days;
|
|
remainingSeconds = remainingSeconds % 86400;
|
|
}
|
|
|
|
if (remainingSeconds > 3600) {
|
|
const hours = Math.floor(remainingSeconds / 3600) + 'h';
|
|
figs.push(hours);
|
|
formattedDuration += hours;
|
|
remainingSeconds = remainingSeconds % 3600;
|
|
}
|
|
|
|
if (remainingSeconds > 60) {
|
|
const minutes = Math.floor(remainingSeconds / 60) + 'm';
|
|
figs.push(minutes);
|
|
formattedDuration += minutes;
|
|
remainingSeconds = remainingSeconds % 60;
|
|
}
|
|
|
|
if (remainingSeconds > 0 || Math.round(seconds) === 0) {
|
|
figs.push(remainingSeconds + 's');
|
|
formattedDuration += remainingSeconds + 's';
|
|
}
|
|
|
|
if (sigfigs <= figs.length) {
|
|
formattedDuration = '';
|
|
for (let i = 0; i < sigfigs; i++) {
|
|
formattedDuration += figs[i];
|
|
}
|
|
return formattedDuration;
|
|
}
|
|
|
|
return formattedDuration;
|
|
}
|
|
|
|
export const ago = (date: Date) => {
|
|
const secondsAgo = (new Date().getTime() - date.getTime()) / 1000;
|
|
const duration = formatDuration(secondsAgo);
|
|
if (secondsAgo < 0) {
|
|
return 'in ' + duration;
|
|
} else {
|
|
return duration + ' ago';
|
|
}
|
|
};
|