diff --git a/src/components/LiveLog.tsx b/src/components/LiveLog.tsx new file mode 100644 index 0000000..3e2a074 --- /dev/null +++ b/src/components/LiveLog.tsx @@ -0,0 +1,86 @@ +import { createContext, ReactNode, useCallback, useContext, useMemo, useState } from 'react' +import { CloseOutlined } from '@ant-design/icons' +import { Button } from 'antd' + +import { FunctionalValue, getFunctionalValue, isDev } from '@utils' + +import '@styles/components/live_log.less' + +export type LogType = 'info' | 'warn' | 'error' | 'critical' +export type VarType = { value: any, type: LogType } + +export type LogContext = (varName: string, value: FunctionalValue<(prev?: any) => any>, type?: FunctionalValue<(prev: LogType) => LogType>) => void + +export const LiveLogContext = createContext(() => {}) + +const renderVar = (name: string, value: any, type: LogType, remove: (name: string) => void) => { + if (typeof value === 'object') + value = JSON.stringify(value) + + return ( +
+ remove(name)}> + {name}: + {String(value)} +
+ ) +} + +export type LiveLogProps = { + placement?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' + children: JSX.Element +} + +export const LiveLog = ({ placement = 'top-left', children }: LiveLogProps) => { + if (!isDev()) return children + + const [logMsgs, setLogMsgs] = useState>({}) + + const clear = useCallback(() => setLogMsgs({}), []) + + const removeVar = useCallback((name: string) => { + setLogMsgs((prev) => { + if (!(name in prev)) return prev + const newMsgs = { ...prev } + delete newMsgs[name] + return newMsgs + }) + }, []) + + const log: LogContext = useCallback((varName, value, type) => { + setLogMsgs((prev) => { + const newValue = getFunctionalValue(value)(prev[varName]?.value) + const newType = getFunctionalValue(type)(prev[varName]?.type) + return { + ...prev, + [varName]: { + type: newType || 'info', + value: newValue, + } + } + }) + }, []) + + const logInner = useMemo(() => Object.entries(logMsgs).map(([name, value]) => value && renderVar(name, value.value, value.type, removeVar)), [logMsgs, removeVar]) + const isEmpty = useMemo(() => Object.keys(logMsgs).length <= 0, [logMsgs]) + return ( + + {isEmpty || ( +
+
+ Непрерывные значения + +
+
+ {logInner} +
+
+ )} + {children} +
+ ) +} + +export const useLiveLog = () => useContext(LiveLogContext) + +export default LiveLog diff --git a/src/index.tsx b/src/index.tsx index 12aaff2..41c64e4 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,6 +3,7 @@ import { ConfigProvider } from 'antd' import { createRoot } from 'react-dom/client' import React from 'react' +import LiveLog from '@components/LiveLog' import { getUser } from '@utils' import { OpenAPI } from '@api' @@ -22,7 +23,9 @@ const root = createRoot(container) root.render( - + + + ) diff --git a/src/pages/Well/Telemetry/TelemetryView/index.jsx b/src/pages/Well/Telemetry/TelemetryView/index.jsx index 7bd4e13..9647f99 100644 --- a/src/pages/Well/Telemetry/TelemetryView/index.jsx +++ b/src/pages/Well/Telemetry/TelemetryView/index.jsx @@ -33,6 +33,7 @@ import SpinPicDisabled from '@images/SpinDisabled.png' import '@styles/pages/telemetry_view.less' import '@styles/pages/message.less' +import { useLiveLog } from '@asb/components/LiveLog' const { Option } = Select @@ -90,6 +91,8 @@ const TelemetryView = memo(() => { const [archiveMode, setArchiveMode] = useState(false) + const log = useLiveLog() + const onStatusChanged = useCallback((value) => updateWell({ idState: value }), [well]) const handleDataSaub = useCallback((data, replace = false) => { @@ -116,6 +119,7 @@ const TelemetryView = memo(() => { }, []) const onWheel = useCallback((e) => { + log('Wheel count', (prev) => prev ? prev + 1 : 1, 'warn') if (!archiveMode && e.deltaY < 0) { setArchiveMode(true) } else if (archiveMode) { diff --git a/src/styles/components/live_log.less b/src/styles/components/live_log.less new file mode 100644 index 0000000..cd80c23 --- /dev/null +++ b/src/styles/components/live_log.less @@ -0,0 +1,53 @@ +.log-box { + position: fixed; + z-index: 1000; + background-color: rgba(0,0,0,.5); + border-radius: 5px; + padding: 10px; + color: white; + display: flex; + flex-direction: column; + align-items: stretch; + overflow-x: hidden; + width: 300px; + + &.log-box-top-left { top: 10px; left: 10px; } + &.log-box-top-right { top: 10px; right: 10px; } + &.log-box-bottom-left { bottom: 10px; left: 10px; } + &.log-box-bottom-right { bottom: 10px; right: 10px; } + + & .log-box-label { + display: flex; + justify-content: space-between; + + & span { + font-size: 1.25em; + } + } + + & .log-box-content { + flex: 1; + display: flex; + flex-direction: column; + align-items: stretch; + gap: 5px; + + & .log-row { + display: flex; + gap: 10px; + + & .log-close { + cursor: pointer; + } + + & .log-name { + text-decoration: underline; + } + + &.log-info { color: white } + &.log-warn { color: #faad14 } + &.log-error { color: #ff4d4f } + &.log-critical { color: #ff4d4f; font-weight: bolder; } + } + } +}