diff --git a/app/vmui/packages/vmui/public/index.html b/app/vmui/packages/vmui/public/index.html index 2cf2a83bc..ca8d5d575 100644 --- a/app/vmui/packages/vmui/public/index.html +++ b/app/vmui/packages/vmui/public/index.html @@ -3,7 +3,7 @@ - + = ({ const overflowX = leftOnChart + tooltipWidth >= width ? tooltipWidth + (2 * margin) : 0; const overflowY = topOnChart + tooltipHeight >= height ? tooltipHeight + (2 * margin) : 0; - setPosition({ + const position = { top: topOnChart + tooltipOffset.top + margin - overflowY, left: leftOnChart + tooltipOffset.left + margin - overflowX - }); + }; + + if (position.left < 0) position.left = 20; + if (position.top < 0) position.top = 20; + + setPosition(position); }; useEffect(calcPosition, [u, value, dataTime, seriesIdx, tooltipOffset, tooltipRef]); @@ -170,7 +175,7 @@ const ChartTooltip: FC = ({ style={{ background: color }} />

- {metricName}: + {metricName}: {valueFormat} {unit}

diff --git a/app/vmui/packages/vmui/src/components/Chart/Legend/LegendItem/style.scss b/app/vmui/packages/vmui/src/components/Chart/Legend/LegendItem/style.scss index 5b31b4c15..df35f5da6 100644 --- a/app/vmui/packages/vmui/src/components/Chart/Legend/LegendItem/style.scss +++ b/app/vmui/packages/vmui/src/components/Chart/Legend/LegendItem/style.scss @@ -6,7 +6,7 @@ grid-gap: $padding-small; align-items: start; justify-content: start; - padding: $padding-small $padding-large $padding-small $padding-small; + padding: $padding-small; background-color: $color-background-block; cursor: pointer; transition: 0.2s ease; @@ -30,9 +30,9 @@ &-info { font-weight: normal; + word-break: break-all; &__label { - } &__free-fields { diff --git a/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx b/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx index c9d008f9c..df6251229 100644 --- a/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx +++ b/app/vmui/packages/vmui/src/components/Chart/LineChart/LineChart.tsx @@ -55,6 +55,7 @@ const LineChart: FC = ({ const [xRange, setXRange] = useState({ min: period.start, max: period.end }); const [yRange, setYRange] = useState([0, 1]); const [uPlotInst, setUPlotInst] = useState(); + const [startTouchDistance, setStartTouchDistance] = useState(0); const layoutSize = useResize(container); const [showTooltip, setShowTooltip] = useState(false); @@ -84,6 +85,7 @@ const LineChart: FC = ({ left: parseFloat(u.over.style.left), top: parseFloat(u.over.style.top) }); + u.over.addEventListener("mousedown", e => { const { ctrlKey, metaKey, button } = e; const leftClick = button === 0; @@ -94,6 +96,10 @@ const LineChart: FC = ({ } }); + u.over.addEventListener("touchstart", e => { + dragChart({ u, e, setPanning, setPlotScale, factor }); + }); + u.over.addEventListener("wheel", e => { if (!e.ctrlKey && !e.metaKey) return; e.preventDefault(); @@ -235,6 +241,47 @@ const LineChart: FC = ({ }; }, [xRange]); + const handleTouchStart = (e: TouchEvent) => { + if (e.touches.length !== 2) return; + e.preventDefault(); + + const dx = e.touches[0].clientX - e.touches[1].clientX; + const dy = e.touches[0].clientY - e.touches[1].clientY; + setStartTouchDistance(Math.sqrt(dx * dx + dy * dy)); + }; + + const handleTouchMove = (e: TouchEvent) => { + if (e.touches.length !== 2 || !uPlotInst) return; + e.preventDefault(); + + const dx = e.touches[0].clientX - e.touches[1].clientX; + const dy = e.touches[0].clientY - e.touches[1].clientY; + const endTouchDistance = Math.sqrt(dx * dx + dy * dy); + const diffDistance = startTouchDistance - endTouchDistance; + + const max = (uPlotInst.scales.x.max || xRange.max); + const min = (uPlotInst.scales.x.min || xRange.min); + const dur = max - min; + const dir = (diffDistance > 0 ? -1 : 1); + + const zoomFactor = dur / 50 * dir; + uPlotInst.batch(() => setPlotScale({ + u: uPlotInst, + min: min + zoomFactor, + max: max - zoomFactor + })); + }; + + useEffect(() => { + window.addEventListener("touchmove", handleTouchMove); + window.addEventListener("touchstart", handleTouchStart); + + return () => { + window.removeEventListener("touchmove", handleTouchMove); + window.removeEventListener("touchstart", handleTouchStart); + }; + }, [uPlotInst, startTouchDistance]); + useEffect(() => updateChart(typeChartUpdate.data), [data]); useEffect(() => updateChart(typeChartUpdate.xRange), [xRange]); useEffect(() => updateChart(typeChartUpdate.yRange), [yaxis]); @@ -256,6 +303,10 @@ const LineChart: FC = ({ "vm-line-chart": true, "vm-line-chart_panning": isPanning })} + style={{ + minWidth: `${layoutSize.width || 400}px`, + minHeight: `${height || 500}px` + }} >
{ +const GlobalSettings: FC<{showTitle?: boolean}> = ({ showTitle }) => { + const { isMobile } = useDeviceDetect(); const appModeEnable = getAppModeEnable(); const { serverUrl: stateServerUrl } = useAppState(); @@ -49,7 +51,10 @@ const GlobalSettings: FC = () => { }, [stateServerUrl]); return <> - + {open && ( -
+
{!appModeEnable && (
= ({ limits, onChange , on
{fields.map(f => ( - +
+ +
))}
diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/LimitsConfigurator/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/LimitsConfigurator/style.scss index ec09e85a5..bfc394f43 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/LimitsConfigurator/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/LimitsConfigurator/style.scss @@ -12,10 +12,14 @@ } &__inputs { - display: grid; - grid-template-columns: repeat(3, 1fr); + display: flex; + flex-wrap: wrap; align-items: center; justify-content: space-between; gap: $padding-global; + + div { + flex-grow: 1; + } } } diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx index 2023aedf3..d72f4920c 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration.tsx @@ -1,7 +1,7 @@ import React, { FC, useState, useRef, useEffect, useMemo } from "preact/compat"; import { useAppDispatch, useAppState } from "../../../../state/common/StateContext"; import { useTimeDispatch } from "../../../../state/time/TimeStateContext"; -import { ArrowDownIcon, StorageIcons } from "../../../Main/Icons"; +import { ArrowDownIcon, StorageIcon } from "../../../Main/Icons"; import Button from "../../../Main/Button/Button"; import "./style.scss"; import { replaceTenantId } from "../../../../utils/default-server-url"; @@ -9,9 +9,11 @@ import classNames from "classnames"; import Popper from "../../../Main/Popper/Popper"; import { getAppModeEnable } from "../../../../utils/app-mode"; import Tooltip from "../../../Main/Tooltip/Tooltip"; +import useDeviceDetect from "../../../../hooks/useDeviceDetect"; const TenantsConfiguration: FC<{accountIds: string[]}> = ({ accountIds }) => { const appModeEnable = getAppModeEnable(); + const { isMobile } = useDeviceDetect(); const { tenantId: tenantIdState, serverUrl } = useAppState(); const dispatch = useAppDispatch(); @@ -71,8 +73,8 @@ const TenantsConfiguration: FC<{accountIds: string[]}> = ({ accountIds }) => { variant="contained" color="primary" fullWidth - startIcon={} - endIcon={( + startIcon={} + endIcon={!isMobile ? (
= ({ accountIds }) => { >
- )} + ) : undefined} onClick={toggleOpenOptions} > - {tenantIdState} + {!isMobile && tenantIdState}
diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/Timezones.tsx b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/Timezones.tsx index 65fae3aca..ee7c7b45d 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/Timezones.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/Timezones.tsx @@ -91,6 +91,7 @@ const Timezones: FC = ({ timezoneState, onChange }) => { buttonRef={targetRef} placement="bottom-left" onClose={handleCloseList} + fullWidth >
diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss index 7ef3e64d2..65017f8c2 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/Timezones/style.scss @@ -46,7 +46,6 @@ } &-list { - min-width: 600px; max-height: 200px; background-color: $color-background-block; border-radius: $border-radius-medium; diff --git a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss index c44512929..376efd7cf 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss +++ b/app/vmui/packages/vmui/src/components/Configurators/GlobalSettings/style.scss @@ -1,12 +1,25 @@ @use "src/styles/variables" as *; .vm-server-configurator { - display: grid; + display: flex; + flex-direction: column; align-items: center; gap: $padding-medium; width: 600px; + &_mobile { + grid-auto-rows: min-content; + align-items: flex-start; + height: 100%; + width: 100%; + } + + @media (max-width: 768px) { + width: 100%; + } + &__input { + width: 100%; &_server { display: grid; @@ -34,4 +47,10 @@ margin-left: auto; margin-right: 0; } + + &_mobile &__footer { + align-items: flex-end; + flex-grow: 1; + width: 100%; + } } diff --git a/app/vmui/packages/vmui/src/components/Configurators/StepConfigurator/StepConfigurator.tsx b/app/vmui/packages/vmui/src/components/Configurators/StepConfigurator/StepConfigurator.tsx index a6c13e7fa..e7f553490 100644 --- a/app/vmui/packages/vmui/src/components/Configurators/StepConfigurator/StepConfigurator.tsx +++ b/app/vmui/packages/vmui/src/components/Configurators/StepConfigurator/StepConfigurator.tsx @@ -111,7 +111,12 @@ const StepConfigurator: FC = () => { startIcon={} onClick={toggleOpenOptions} > - STEP {customStep} +

+ STEP +

+ {customStep} +

+

({ title: value, value })); const ThemeControl = () => { + const { isMobile } = useDeviceDetect(); const { theme } = useAppState(); const dispatch = useAppDispatch(); @@ -14,11 +17,19 @@ const ThemeControl = () => { }; return ( -
+
Theme preferences
-
+
{ + const windowSize = useResize(document.body); const dispatch = useTimeDispatch(); const appModeEnable = getAppModeEnable(); @@ -83,17 +85,20 @@ export const ExecutionControls: FC = () => {
- - +); + +export default MenuBurger; diff --git a/app/vmui/packages/vmui/src/components/Main/MenuBurger/style.scss b/app/vmui/packages/vmui/src/components/Main/MenuBurger/style.scss new file mode 100644 index 000000000..5a5de006c --- /dev/null +++ b/app/vmui/packages/vmui/src/components/Main/MenuBurger/style.scss @@ -0,0 +1,133 @@ +@use "src/styles/variables" as *; + +$width-line: 2px; + +.vm-menu-burger { + position: relative; + border: none; + background: none; + width: 18px; + height: 18px; + padding: 0; + outline: none; + cursor: pointer; + transform-style: preserve-3d; + + &:after { + content: ''; + position: absolute; + left: -6px; + top: -6px; + width: calc(100% + 12px); + height: calc(100% + 12px); + background-color: rgba($color-black, 0.1); + border-radius: 50%; + transform: scale(0) translateZ(-2px); + transition: transform 140ms ease-in-out; + } + + &:hover { + &:after { + transform: scale(1) translateZ(-2px); + } + } + + span { + display: block; + top: 50%; + transform: translateY(-50%); + border-top: $width-line solid #fff; + transition: transform 0.3s ease, border-color 0.3s ease; + + &, + &:before, + &:after { + position: absolute; + left: 0; + width: 100%; + height: $width-line; + border-radius: 6px; + } + + &:before, + &:after { + content: ''; + top: 0; + background: $color-white; + animation-duration: 0.6s; + animation-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1); + animation-fill-mode: forwards; + } + + &:before { + animation-name: topLineBurger; + } + + &:after { + animation-name: bottomLineBurger; + + } + } + + &_opened span { + border-color: transparent; + } + + &_opened span:before { + animation-name: topLineCross; + } + + &_opened span:after { + animation-name: bottomLineCross; + } +} + +@keyframes topLineCross { + 0% { + transform: translateY(-7px); + } + 50% { + transform: translateY(0px); + } + 100% { + width: 60%; + transform: translateY(-2px) translateX(30%) rotate(45deg); + } +} + +@keyframes bottomLineCross { + 0% { + transform: translateY(3px); + } + 50% { + transform: translateY(0px); + } + 100% { + width: 60%; + transform: translateY(-2px) translateX(30%) rotate(-45deg); + } +} + +@keyframes topLineBurger { + 0% { + transform: translateY(0px) rotate(45deg); + } + 50% { + transform: rotate(0deg); + } + 100% { + transform: translateY(-7px) rotate(0deg); + } +} + +@keyframes bottomLineBurger { + 0% { + transform: translateY(0px) rotate(-45deg); + } + 50% { + transform: rotate(0deg); + } + 100% { + transform: translateY(3px) rotate(0deg); + } +} diff --git a/app/vmui/packages/vmui/src/components/Main/Modal/Modal.tsx b/app/vmui/packages/vmui/src/components/Main/Modal/Modal.tsx index eff12139f..4799c233b 100644 --- a/app/vmui/packages/vmui/src/components/Main/Modal/Modal.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Modal/Modal.tsx @@ -4,6 +4,8 @@ import { CloseIcon } from "../Icons"; import Button from "../Button/Button"; import { ReactNode, MouseEvent } from "react"; import "./style.scss"; +import useDeviceDetect from "../../../hooks/useDeviceDetect"; +import classNames from "classnames"; interface ModalProps { title?: string @@ -12,6 +14,7 @@ interface ModalProps { } const Modal: FC = ({ title, children, onClose }) => { + const { isMobile } = useDeviceDetect(); const handleKeyUp = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); @@ -22,16 +25,21 @@ const Modal: FC = ({ title, children, onClose }) => { }; useEffect(() => { + document.body.style.overflow = "hidden"; window.addEventListener("keyup", handleKeyUp); return () => { + document.body.style.overflow = "auto"; window.removeEventListener("keyup", handleKeyUp); }; }, []); return ReactDOM.createPortal((
diff --git a/app/vmui/packages/vmui/src/components/Main/Modal/style.scss b/app/vmui/packages/vmui/src/components/Main/Modal/style.scss index ba740d76a..7e89e845d 100644 --- a/app/vmui/packages/vmui/src/components/Main/Modal/style.scss +++ b/app/vmui/packages/vmui/src/components/Main/Modal/style.scss @@ -14,12 +14,28 @@ $padding-modal: 22px; justify-content: center; background: rgba($color-black, 0.55); + &_mobile &-content { + min-height: calc($vh * 100); + max-height: calc($vh * 100); + width: 100vw; + border-radius: 0; + + &-body { + display: grid; + align-items: flex-start; + min-height: 100%; + } + } + &-content { + display: grid; + grid-template-rows: auto 1fr; + align-items: flex-start; padding: $padding-modal; background: $color-background-block; box-shadow: 0 0 24px rgba($color-black, 0.07); border-radius: $border-radius-small; - max-height: 90vh; + max-height: calc($vh * 90); overflow: auto; &-header { @@ -44,6 +60,8 @@ $padding-modal: 22px; cursor: pointer; } } + + &-body {} } } diff --git a/app/vmui/packages/vmui/src/components/Main/Popper/Popper.tsx b/app/vmui/packages/vmui/src/components/Main/Popper/Popper.tsx index 833d3719b..ea7ea8d5a 100644 --- a/app/vmui/packages/vmui/src/components/Main/Popper/Popper.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Popper/Popper.tsx @@ -99,12 +99,20 @@ const Popper: FC = ({ if (isOverflowLeft) position.left = buttonPos.left + offsetLeft; if (fullWidth) position.width = `${buttonPos.width}px`; + if (position.top < 0) position.top = 20; return position; },[buttonRef, placement, isOpen, children, fullWidth]); if (clickOutside) useClickOutside(popperRef, () => setIsOpen(false), buttonRef); + useEffect(() => { + if (!popperRef.current || !isOpen) return; + const { right, width } = popperRef.current.getBoundingClientRect(); + if (right > window.innerWidth) popperRef.current.style.left = `${window.innerWidth - 20 -width}px`; + }, [isOpen, popperRef]); + + const popperClasses = classNames({ "vm-popper": true, "vm-popper_open": isOpen, diff --git a/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/ShortcutKeys.tsx b/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/ShortcutKeys.tsx index 7dfd619e7..1f2f589f2 100644 --- a/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/ShortcutKeys.tsx +++ b/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/ShortcutKeys.tsx @@ -1,5 +1,5 @@ import React, { FC, useState } from "preact/compat"; -import { isMacOs } from "../../../utils/detect-os"; +import { isMacOs } from "../../../utils/detect-device"; import { getAppModeEnable } from "../../../utils/app-mode"; import Button from "../Button/Button"; import { KeyboardIcon } from "../Icons"; @@ -69,7 +69,9 @@ const keyList = [ } ]; -const ShortcutKeys: FC = () => { +const title = "Shortcut keys"; + +const ShortcutKeys: FC<{showTitle?: boolean}> = ({ showTitle }) => { const [openList, setOpenList] = useState(false); const appModeEnable = getAppModeEnable(); @@ -83,7 +85,8 @@ const ShortcutKeys: FC = () => { return <> {openList && ( diff --git a/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/style.scss b/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/style.scss index fa8c67156..c86947ce0 100644 --- a/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/style.scss +++ b/app/vmui/packages/vmui/src/components/Main/ShortcutKeys/style.scss @@ -3,6 +3,10 @@ .vm-shortcuts { min-width: 400px; + @media (max-width: 500px) { + min-width: 100%; + } + &-section { margin-bottom: $padding-medium; @@ -17,12 +21,20 @@ display: grid; gap: $padding-global; + @media (max-width: 500px) { + gap: $padding-medium; + } + &-item { display: grid; grid-template-columns: 210px 1fr; align-items: center; gap: $padding-small; + @media (max-width: 500px) { + grid-template-columns: 1fr; + } + &__key { display: flex; align-items: center; diff --git a/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts b/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts index 1de5cdfa4..b7ec42ba6 100644 --- a/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts +++ b/app/vmui/packages/vmui/src/components/Main/ThemeProvider/ThemeProvider.ts @@ -39,6 +39,7 @@ export const ThemeProvider: FC = ({ onLoaded }) => { const { clientWidth, clientHeight } = document.documentElement; setCssVariable("scrollbar-width", `${innerWidth - clientWidth}px`); setCssVariable("scrollbar-height", `${innerHeight - clientHeight}px`); + setCssVariable("vh", `${innerHeight * 0.01}px`); }; const setContrastText = () => { diff --git a/app/vmui/packages/vmui/src/components/Main/Tooltip/Tooltip.tsx b/app/vmui/packages/vmui/src/components/Main/Tooltip/Tooltip.tsx index b9e7459f6..3eef672f9 100644 --- a/app/vmui/packages/vmui/src/components/Main/Tooltip/Tooltip.tsx +++ b/app/vmui/packages/vmui/src/components/Main/Tooltip/Tooltip.tsx @@ -3,6 +3,7 @@ import ReactDOM from "react-dom"; import "./style.scss"; import { ReactNode } from "react"; import { ExoticComponent } from "react"; +import useDeviceDetect from "../../../hooks/useDeviceDetect"; interface TooltipProps { children: ReactNode @@ -19,6 +20,7 @@ const Tooltip: FC = ({ placement = "bottom-center", offset = { top: 6, left: 0 } }) => { + const { isMobile } = useDeviceDetect(); const [isOpen, setIsOpen] = useState(false); const [popperSize, setPopperSize] = useState({ width: 0, height: 0 }); @@ -121,7 +123,7 @@ const Tooltip: FC = ({ {children} - {isOpen && ReactDOM.createPortal(( + {!isMobile && isOpen && ReactDOM.createPortal((
{ + const mobileAgent = isMobileAgent(); + const smallWidth = window.innerWidth < 500; + setMobile(mobileAgent || smallWidth); + }, [windowSize]); + + return { isMobile }; +} diff --git a/app/vmui/packages/vmui/src/hooks/useResize.ts b/app/vmui/packages/vmui/src/hooks/useResize.ts index 0fb9bea55..c3b10ca48 100644 --- a/app/vmui/packages/vmui/src/hooks/useResize.ts +++ b/app/vmui/packages/vmui/src/hooks/useResize.ts @@ -14,7 +14,7 @@ const useResize = (node: HTMLElement | null): {width: number, height: number} => return () => { if (node) observer.unobserve(node); }; - }, []); + }, [node]); return windowSize; }; diff --git a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx index 49adfceda..6cd531018 100644 --- a/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx +++ b/app/vmui/packages/vmui/src/pages/CardinalityPanel/CardinalityConfigurator/CardinalityConfigurator.tsx @@ -118,24 +118,26 @@ const CardinalityConfigurator: FC = ({ at {date}{match && for series selector {match}}. Show top {topN} entries per table.
- - - Documentation - - - - Example of using - +
-
+
{activeTab === 0 && ( = ({ rows, title, columns, defaultOr
-
+
{activeTab === 0 && ( { {loading && }
-
- - +
+
+ +
+
+ +
diff --git a/app/vmui/packages/vmui/src/pages/TopQueries/style.scss b/app/vmui/packages/vmui/src/pages/TopQueries/style.scss index 2a1a55f34..0b99cc478 100644 --- a/app/vmui/packages/vmui/src/pages/TopQueries/style.scss +++ b/app/vmui/packages/vmui/src/pages/TopQueries/style.scss @@ -9,10 +9,16 @@ display: grid; gap: $padding-small; - &__fields { - display: grid; - grid-template-columns: 1fr auto; + &-fields { + display: flex; + align-items: center; + flex-wrap: wrap; gap: $padding-medium; + + &__item { + flex-grow: 1; + min-width: 200px; + } } &-bottom { diff --git a/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/JsonForm.tsx b/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/JsonForm.tsx index 3b1fa39ca..507083496 100644 --- a/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/JsonForm.tsx +++ b/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/JsonForm.tsx @@ -7,6 +7,7 @@ import { ErrorTypes } from "../../../types"; import classNames from "classnames"; import { useSnack } from "../../../contexts/Snackbar"; import { CopyIcon, RestartIcon } from "../../../components/Main/Icons"; +import useDeviceDetect from "../../../hooks/useDeviceDetect"; interface JsonFormProps { defaultJson?: string @@ -28,6 +29,7 @@ const JsonForm: FC = ({ onUpload, }) => { const { showInfoMessage } = useSnack(); + const { isMobile } = useDeviceDetect(); const [json, setJson] = useState(defaultJson); const [title, setTitle] = useState(defaultTile); @@ -77,7 +79,8 @@ const JsonForm: FC = ({
{displayTitle && ( diff --git a/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/style.scss b/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/style.scss index d57235e7d..acaaf9395 100644 --- a/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/style.scss +++ b/app/vmui/packages/vmui/src/pages/TracePage/JsonForm/style.scss @@ -2,15 +2,21 @@ .vm-json-form { display: grid; - grid-template-rows: auto calc(70vh - 78px - ($padding-medium*3)) auto; + grid-template-rows: auto calc(($vh * 70) - 78px - ($padding-medium*3)) auto; gap: $padding-global; width: 70vw; max-width: 1000px; max-height: 900px; overflow: hidden; + &_mobile { + width: 100%; + min-height: 100%; + grid-template-rows: auto 1fr auto; + } + &_one-field { - grid-template-rows: calc(70vh - 78px - ($padding-medium*3)) auto; + grid-template-rows: calc(($vh * 70) - 78px - ($padding-medium*3)) auto; } .vm-text-field_textarea { @@ -29,6 +35,14 @@ justify-content: space-between; gap: $padding-small; + @media (max-width: 500px) { + flex-direction: column; + + button { + flex-grow: 1; + } + } + &__controls { flex-grow: 1; display: flex; @@ -36,10 +50,22 @@ justify-content: flex-start; gap: $padding-small; + @media (max-width: 500px) { + grid-template-columns: repeat(2, 1fr); + justify-content: center; + width: 100%; + } + &_right { display: grid; grid-template-columns: repeat(2, 90px); justify-content: flex-end; + + @media (max-width: 500px) { + grid-template-columns: repeat(2, 1fr); + justify-content: center; + width: 100%; + } } } } diff --git a/app/vmui/packages/vmui/src/pages/TracePage/style.scss b/app/vmui/packages/vmui/src/pages/TracePage/style.scss index b871ae2c6..56e05b0c6 100644 --- a/app/vmui/packages/vmui/src/pages/TracePage/style.scss +++ b/app/vmui/packages/vmui/src/pages/TracePage/style.scss @@ -3,9 +3,12 @@ .vm-trace-page { display: flex; flex-direction: column; - padding: $padding-global; min-height: 100%; + @media (max-width: 768px) { + padding: $padding-medium 0; + } + &-controls { display: grid; grid-template-columns: 1fr 1fr; @@ -21,6 +24,11 @@ gap: $padding-global; margin-bottom: $padding-medium; + @media (max-width: 768px) { + grid-template-columns: 1fr; + padding: 0 $padding-medium; + } + &-errors { display: grid; align-items: flex-start; @@ -28,6 +36,10 @@ grid-template-columns: 1fr; gap: $padding-medium; + @media (max-width: 768px) { + grid-row: 2; + } + &-item { position: relative; display: grid; diff --git a/app/vmui/packages/vmui/src/styles/components/table.scss b/app/vmui/packages/vmui/src/styles/components/table.scss index c7e7e36f2..4f794baa8 100644 --- a/app/vmui/packages/vmui/src/styles/components/table.scss +++ b/app/vmui/packages/vmui/src/styles/components/table.scss @@ -16,9 +16,6 @@ } &_header { - position: sticky; - top: 0; - z-index: 2; } &_selected { diff --git a/app/vmui/packages/vmui/src/styles/core.scss b/app/vmui/packages/vmui/src/styles/core.scss index b262574c9..87d5eba7d 100644 --- a/app/vmui/packages/vmui/src/styles/core.scss +++ b/app/vmui/packages/vmui/src/styles/core.scss @@ -13,12 +13,13 @@ html, body, #root { } body { - overflow: scroll; + overflow: auto; } * { font: inherit; cursor: inherit; + touch-action: pan-x pan-y; } code { diff --git a/app/vmui/packages/vmui/src/styles/variables.scss b/app/vmui/packages/vmui/src/styles/variables.scss index 4e889fb2d..67f5df6fd 100644 --- a/app/vmui/packages/vmui/src/styles/variables.scss +++ b/app/vmui/packages/vmui/src/styles/variables.scss @@ -61,3 +61,4 @@ $box-shadow: var(--box-shadow); $box-shadow-popper: var(--box-shadow-popper); $color-hover-black: var(--color-hover-black); +$vh: var(--vh); diff --git a/app/vmui/packages/vmui/src/utils/detect-device.ts b/app/vmui/packages/vmui/src/utils/detect-device.ts new file mode 100644 index 000000000..d21c989e5 --- /dev/null +++ b/app/vmui/packages/vmui/src/utils/detect-device.ts @@ -0,0 +1,29 @@ +const desktopOs = { + windows: "Windows", + mac: "Mac OS", + linux: "Linux" +}; + +export const getOs = () => { + return Object.values(desktopOs).find(os => navigator.userAgent.indexOf(os) >= 0) || "unknown"; +}; + +export const isMacOs = () => { + return getOs() === desktopOs.mac; +}; + +export const isMobileAgent = () => { + const mobileUserAgents = [ + "Android", + "webOS", + "iPhone", + "iPad", + "iPod", + "BlackBerry", + "Windows Phone", + ]; + + // check for common mobile user agents + const matches = mobileUserAgents.map(m => navigator.userAgent.match(new RegExp(m, "i"))); + return matches.some(m => m); +}; diff --git a/app/vmui/packages/vmui/src/utils/detect-os.ts b/app/vmui/packages/vmui/src/utils/detect-os.ts deleted file mode 100644 index ee0ca40d2..000000000 --- a/app/vmui/packages/vmui/src/utils/detect-os.ts +++ /dev/null @@ -1,13 +0,0 @@ -const desktopOs = { - windows: "Windows", - mac: "Mac OS", - linux: "Linux" -}; - -export const getOs = () : string => { - return Object.values(desktopOs).find(os => navigator.userAgent.indexOf(os) >= 0) || "unknown"; -}; - -export const isMacOs = (): boolean => { - return getOs() === desktopOs.mac; -}; \ No newline at end of file diff --git a/app/vmui/packages/vmui/src/utils/time.ts b/app/vmui/packages/vmui/src/utils/time.ts index 235080801..c2429f4ac 100644 --- a/app/vmui/packages/vmui/src/utils/time.ts +++ b/app/vmui/packages/vmui/src/utils/time.ts @@ -145,7 +145,10 @@ export const checkDurationLimit = (dur: string): string => { return dur; }; -export const dateFromSeconds = (epochTimeInSeconds: number): Date => dayjs(epochTimeInSeconds * 1000).toDate(); +export const dateFromSeconds = (epochTimeInSeconds: number): Date => { + const date = dayjs(epochTimeInSeconds * 1000); + return date.isValid() ? date.toDate() : new Date(); +}; const getYesterday = () => dayjs().tz().subtract(1, "day").endOf("day").toDate(); const getToday = () => dayjs().tz().endOf("day").toDate(); diff --git a/app/vmui/packages/vmui/src/utils/uplot/events.ts b/app/vmui/packages/vmui/src/utils/uplot/events.ts index 22ae690e6..6410f701f 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/events.ts +++ b/app/vmui/packages/vmui/src/utils/uplot/events.ts @@ -2,23 +2,33 @@ import { DragArgs } from "./types"; export const dragChart = ({ e, factor = 0.85, u, setPanning, setPlotScale }: DragArgs): void => { e.preventDefault(); + const isMouseEvent = e instanceof MouseEvent; + setPanning(true); - const leftStart = e.clientX; + const leftStart = isMouseEvent ? e.clientX : e.touches[0].clientX; const xUnitsPerPx = u.posToVal(1, "x") - u.posToVal(0, "x"); const scXMin = u.scales.x.min || 0; const scXMax = u.scales.x.max || 0; - const mouseMove = (e: MouseEvent) => { + const mouseMove = (e: MouseEvent | TouchEvent) => { + const isMouseEvent = e instanceof MouseEvent; + if (!isMouseEvent && e.touches.length > 1) return; e.preventDefault(); - const dx = xUnitsPerPx * ((e.clientX - leftStart) * factor); + + const clientX = isMouseEvent ? e.clientX : e.touches[0].clientX; + const dx = xUnitsPerPx * ((clientX - leftStart) * factor); setPlotScale({ u, min: scXMin - dx, max: scXMax - dx }); }; const mouseUp = () => { setPanning(false); document.removeEventListener("mousemove", mouseMove); document.removeEventListener("mouseup", mouseUp); + document.removeEventListener("touchmove", mouseMove); + document.removeEventListener("touchend", mouseUp); }; document.addEventListener("mousemove", mouseMove); document.addEventListener("mouseup", mouseUp); + document.addEventListener("touchmove", mouseMove); + document.addEventListener("touchend", mouseUp); }; diff --git a/app/vmui/packages/vmui/src/utils/uplot/helpers.ts b/app/vmui/packages/vmui/src/utils/uplot/helpers.ts index b095c86f7..9a2c363a3 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/helpers.ts +++ b/app/vmui/packages/vmui/src/utils/uplot/helpers.ts @@ -79,7 +79,7 @@ export const sizeAxis = (u: uPlot, values: string[], axisIdx: number, cycleNum: let axisSize = 6 + (axis?.ticks?.size || 0) + (axis.gap || 0); const longestVal = (values ?? []).reduce((acc, val) => val.length > acc.length ? val : acc, ""); - if (longestVal != "") axisSize += getTextWidth(longestVal, u.ctx.font); + if (longestVal != "") axisSize += getTextWidth(longestVal, "10px Arial"); return Math.ceil(axisSize); }; diff --git a/app/vmui/packages/vmui/src/utils/uplot/plugin.js b/app/vmui/packages/vmui/src/utils/uplot/plugin.js index a2e23e970..ba7ba0616 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/plugin.js +++ b/app/vmui/packages/vmui/src/utils/uplot/plugin.js @@ -1,6 +1,7 @@ /* eslint-disable */ import uPlot from "uplot"; import {getCssVariable} from "../theme"; +import {sizeAxis} from "./helpers"; export const seriesBarsPlugin = (opts) => { let pxRatio; @@ -262,7 +263,9 @@ export const seriesBarsPlugin = (opts) => { }, values: u => u.data[0], gap: 15, - size: ori === 0 ? 40 : 150, + size: sizeAxis, + stroke: getCssVariable("color-text"), + font: "10px Arial", labelSize: 20, grid: {show: false}, ticks: {show: false}, diff --git a/app/vmui/packages/vmui/src/utils/uplot/types.ts b/app/vmui/packages/vmui/src/utils/uplot/types.ts index e46850228..7da1135d3 100644 --- a/app/vmui/packages/vmui/src/utils/uplot/types.ts +++ b/app/vmui/packages/vmui/src/utils/uplot/types.ts @@ -8,7 +8,7 @@ export interface HideSeriesArgs { } export interface DragArgs { - e: MouseEvent, + e: MouseEvent | TouchEvent, u: uPlot, factor: number, setPanning: (enable: boolean) => void,