Metric.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, {CSSProperties, lazy, Suspense, useMemo} from 'react';
  14. import {Data} from "plotly.js";
  15. import {useClassNames, useDynamicJsonProperty, useDynamicProperty} from "../../utils/hooks";
  16. import {extractPrefix, extractSuffix, extractFormatSpecifier} from "../../utils/formatConversion";
  17. import {TaipyBaseProps, TaipyHoverProps} from "./utils";
  18. import Box from "@mui/material/Box";
  19. import Skeleton from "@mui/material/Skeleton";
  20. const Plot = lazy(() => import("react-plotly.js"));
  21. interface MetricProps extends TaipyBaseProps, TaipyHoverProps {
  22. type?: string
  23. min?: number
  24. max?: number
  25. value?: number
  26. defaultValue?: number
  27. delta?: number
  28. defaultDelta?: number
  29. threshold?: number
  30. defaultThreshold?: number
  31. testId?: string
  32. defaultLayout?: string;
  33. layout?: string;
  34. defaultStyle?: string;
  35. style?: string;
  36. width?: string | number;
  37. height?: string | number;
  38. showValue?: boolean;
  39. format?: string;
  40. deltaFormat?: string;
  41. }
  42. const emptyLayout = {} as Record<string, Record<string, unknown>>;
  43. const defaultStyle = {position: "relative", display: "inline-block"};
  44. const Metric = (props: MetricProps) => {
  45. const {
  46. width = "100%",
  47. height,
  48. showValue = true
  49. } = props;
  50. const value = useDynamicProperty(props.value, props.defaultValue, 0)
  51. const threshold = useDynamicProperty(props.threshold, props.defaultThreshold, undefined)
  52. const delta = useDynamicProperty(props.delta, props.defaultDelta, undefined)
  53. const className = useClassNames(props.libClassName, props.dynamicClassName, props.className);
  54. const baseLayout = useDynamicJsonProperty(props.layout, props.defaultLayout || "", emptyLayout);
  55. const baseStyle = useDynamicJsonProperty(props.style, props.defaultStyle || "", defaultStyle);
  56. const data = useMemo(() => {
  57. return [
  58. {
  59. domain: {x: [0, 1], y: [0, 1]},
  60. value: value,
  61. type: "indicator",
  62. mode: "gauge" + (showValue ? "+number" : "") + (delta !== undefined ? "+delta" : ""),
  63. number: {
  64. prefix: extractPrefix(props.format),
  65. suffix: extractSuffix(props.format),
  66. valueformat: extractFormatSpecifier(props.format),
  67. },
  68. delta: {
  69. reference: typeof value === 'number' && typeof delta === 'number' ? value - delta : undefined,
  70. prefix: extractPrefix(props.deltaFormat),
  71. suffix: extractSuffix(props.deltaFormat),
  72. valueformat: extractFormatSpecifier(props.deltaFormat)
  73. },
  74. gauge: {
  75. axis: {
  76. range: [
  77. props.min ?? 0,
  78. props.max ?? 100
  79. ]
  80. },
  81. shape: props.type === "linear" ? "bullet" : "angular",
  82. threshold: {
  83. line: {color: "red", width: 4},
  84. thickness: 0.75,
  85. value: threshold
  86. }
  87. },
  88. }
  89. ];
  90. }, [
  91. props.format,
  92. props.deltaFormat,
  93. props.min,
  94. props.max,
  95. props.type,
  96. value,
  97. showValue,
  98. delta,
  99. threshold
  100. ]);
  101. const style = useMemo(
  102. () =>
  103. height === undefined
  104. ? ({...baseStyle, width: width} as CSSProperties)
  105. : ({...baseStyle, width: width, height: height} as CSSProperties),
  106. [baseStyle, height, width]
  107. );
  108. const skelStyle = useMemo(() => ({...style, minHeight: "7em"}), [style]);
  109. return (
  110. <Box data-testid={props.testId} className={className}>
  111. <Suspense fallback={<Skeleton key="skeleton" sx={skelStyle}/>}>
  112. <Plot
  113. data={data as Data[]}
  114. layout={baseLayout}
  115. style={style}
  116. useResizeHandler
  117. />
  118. </Suspense>
  119. </Box>
  120. );
  121. }
  122. export default Metric;