diff --git a/new-log-viewer/public/favicon.svg b/new-log-viewer/public/favicon.svg new file mode 100644 index 00000000..baeb3b46 --- /dev/null +++ b/new-log-viewer/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/ResizeHandle.css b/new-log-viewer/src/components/CentralContainer/Sidebar/ResizeHandle.css new file mode 100644 index 00000000..522a8ce7 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/ResizeHandle.css @@ -0,0 +1,23 @@ +.resize-handle { + cursor: ew-resize; + + z-index: var(--ylv-resize-handle-z-index); + + box-sizing: border-box; + width: var(--ylv-panel-resize-handle-width); + height: 100%; + + /* stylelint-disable-next-line custom-property-pattern */ + background-color: var(--joy-palette-background-surface, #fbfcfe); + /* stylelint-disable-next-line custom-property-pattern */ + border-right: 1px solid var(--joy-palette-neutral-outlinedBorder, #cdd7e1); +} + +.resize-handle-holding, +.resize-handle:hover { + box-sizing: initial; + + /* stylelint-disable-next-line custom-property-pattern */ + background-color: var(--joy-palette-primary-solidHoverBg, #0258a8); + border-right: initial; +} diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/ResizeHandle.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/ResizeHandle.tsx new file mode 100644 index 00000000..c170f2b3 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/ResizeHandle.tsx @@ -0,0 +1,81 @@ +import React, { + useCallback, + useEffect, + useState, +} from "react"; + +import "./ResizeHandle.css"; + + +interface ResizeHandleProps { + onHandleRelease: () => void, + + /** + * Gets triggered when a resize event occurs. + * + * @param resizeHandlePosition The horizontal distance, in pixels, between the mouse pointer + * and the left edge of the viewport. + */ + onResize: (resizeHandlePosition: number) => void, +} + +/** + * A vertical handle for resizing an object. + * + * @param props + * @param props.onResize The method to call when a resize occurs. + * @param props.onHandleRelease + * @return + */ +const ResizeHandle = ({ + onResize, + onHandleRelease, +}: ResizeHandleProps) => { + const [isMouseDown, setIsMouseDown] = useState(false); + + const handleMouseDown = (ev: React.MouseEvent) => { + ev.preventDefault(); + setIsMouseDown(true); + }; + + const handleMouseMove = useCallback((ev: MouseEvent) => { + ev.preventDefault(); + onResize(ev.clientX); + }, [onResize]); + + const handleMouseUp = useCallback((ev: MouseEvent) => { + ev.preventDefault(); + setIsMouseDown(false); + onHandleRelease(); + }, [onHandleRelease]); + + // On `isMouseDown` change, add / remove event listeners. + useEffect(() => { + if (isMouseDown) { + window.addEventListener("mousemove", handleMouseMove); + window.addEventListener("mouseup", handleMouseUp); + } + + return () => { + // Always clean up the event listeners before the hook is re-run due to `isMouseDown` + // changes / when the component is unmounted. + window.removeEventListener("mousemove", handleMouseMove); + window.removeEventListener("mouseup", handleMouseUp); + }; + }, [ + handleMouseMove, + handleMouseUp, + isMouseDown, + ]); + + return ( +
+ ); +}; + + +export default ResizeHandle; diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx new file mode 100644 index 00000000..30dd7444 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +import { + ListItem, + ListItemContent, + ListItemDecorator, + Typography, + TypographyProps, +} from "@mui/joy"; + + +interface CustomListItemProps { + content: string, + icon: React.ReactNode, + slotProps?: { + content?: TypographyProps + }, + title: string +} + +/** + * Renders a custom list item with an icon, a title and a context text. + * + * @param props + * @param props.content + * @param props.icon + * @param props.slotProps + * @param props.title + * @return + */ +const CustomListItem = ({content, icon, slotProps, title}: CustomListItemProps) => ( + + + {icon} + + + + {title} + + + {content} + + + +); + +export default CustomListItem; diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.css b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.css new file mode 100644 index 00000000..8169315f --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.css @@ -0,0 +1,14 @@ +.sidebar-tab-panel { + padding: 0.75rem; +} + +.sidebar-tab-panel-title-container { + user-select: none; + margin-bottom: 0.5rem !important; +} + +.sidebar-tab-panel-title { + font-size: 0.875rem !important; + font-weight: 400 !important; + text-transform: uppercase; +} diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.tsx new file mode 100644 index 00000000..9d1ab3e7 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.tsx @@ -0,0 +1,50 @@ +import React from "react"; + +import { + DialogContent, + DialogTitle, + TabPanel, + Typography, +} from "@mui/joy"; + +import "./CustomTabPanel.css"; + + +interface CustomTabPanelProps { + children: React.ReactNode, + tabName: string, + title: string, +} + +/** + * Renders a customized tab panel to be extended for displaying extra information in the sidebar. + * + * @param props + * @param props.children + * @param props.tabName + * @param props.title + * @return + */ +const CustomTabPanel = ({children, tabName, title}: CustomTabPanelProps) => { + return ( + + + + {title} + + + + {children} + + + ); +}; + + +export default CustomTabPanel; diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/FileInfoTabPanel.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/FileInfoTabPanel.tsx new file mode 100644 index 00000000..7b487f3d --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/FileInfoTabPanel.tsx @@ -0,0 +1,61 @@ +import { + useContext, + useMemo, +} from "react"; + +import { + Divider, + List, +} from "@mui/joy"; + +import AbcIcon from "@mui/icons-material/Abc"; +import StorageIcon from "@mui/icons-material/Storage"; + +import {StateContext} from "../../../../contexts/StateContextProvider"; +import { + TAB_DISPLAY_NAMES, + TAB_NAME, +} from "../../../../typings/tab"; +import {formatSizeInBytes} from "../../../../utils/units"; +import CustomListItem from "./CustomListItem"; +import CustomTabPanel from "./CustomTabPanel"; + + +/** + * Displays a panel containing the file name and on-disk size of the selected file. + * + * @return + */ +const FileInfoTabPanel = () => { + const {fileName, onDiskFileSizeInBytes} = useContext(StateContext); + + const isFileUnloaded = 0 === fileName.length; + const formattedOnDiskSize = useMemo( + () => formatSizeInBytes(onDiskFileSizeInBytes, false), + [onDiskFileSizeInBytes] + ); + + return ( + + {isFileUnloaded ? + "No file is open." : + + } + slotProps={{content: {sx: {wordBreak: "break-word"}}}} + title={"Name"}/> + + } + title={"On-disk Size"}/> + } + + ); +}; + +export default FileInfoTabPanel; diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.css b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.css new file mode 100644 index 00000000..11fe18bd --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.css @@ -0,0 +1,10 @@ +.sidebar-tab-button { + justify-content: center !important; + width: 48px; + height: 48px; + padding: 0 !important; +} + +.sidebar-tab-button-icon { + font-size: 32px !important; +} diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.tsx new file mode 100644 index 00000000..846860f2 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.tsx @@ -0,0 +1,57 @@ +import { + Tab, + Tooltip, +} from "@mui/joy"; + +import {SvgIconComponent} from "@mui/icons-material"; + +import { + TAB_DISPLAY_NAMES, + TAB_NAME, +} from "../../../../typings/tab"; + +import "./TabButton.css"; + + +interface TabButtonProps { + tabName: TAB_NAME, + Icon: SvgIconComponent, + onTabButtonClick: (tabName: TAB_NAME) => void +} + +/** + * Renders a tooltip-wrapped tab button. + * + * @param props + * @param props.tabName + * @param props.Icon + * @param props.onTabButtonClick + * @return + */ +const TabButton = ({tabName, Icon, onTabButtonClick}: TabButtonProps) => { + const handleClick = () => { + onTabButtonClick(tabName); + }; + + return ( + + + + + + ); +}; + +export default TabButton; diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.css b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.css new file mode 100644 index 00000000..f9b4e2c3 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.css @@ -0,0 +1,9 @@ +.sidebar-tabs { + flex-grow: 1; + width: calc(100% - var(--ylv-panel-resize-handle-width)); + height: 100%; +} + +.sidebar-tab-list-spacing { + flex-grow: 1; +} diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx new file mode 100644 index 00000000..c8a262ed --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx @@ -0,0 +1,105 @@ +import { + forwardRef, + useState, +} from "react"; + +import { + TabList, + Tabs, +} from "@mui/joy"; + +import {SvgIconComponent} from "@mui/icons-material"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; +import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined"; + +import {TAB_NAME} from "../../../../typings/tab"; +import SettingsModal from "../../../modals/SettingsModal"; +import FileInfoTabPanel from "./FileInfoTabPanel"; +import TabButton from "./TabButton"; + +import "./index.css"; + + +/** + * Lists information for each tab. + */ +const TABS_INFO_LIST: Readonly> = Object.freeze([ + {tabName: TAB_NAME.FILE_INFO, Icon: InfoOutlinedIcon}, +]); + +interface SidebarTabsProps { + activeTabName: TAB_NAME, + onActiveTabNameChange: (newValue: TAB_NAME) => void, +} + +/** + * Displays a set of tabs in a vertical orientation. + * + * @param tabListRef Reference object used to access the TabList DOM element. + * @return + */ +const SidebarTabs = forwardRef(( + { + activeTabName, + onActiveTabNameChange, + }, + tabListRef +) => { + const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); + + const handleSettingsModalClose = () => { + setIsSettingsModalOpen(false); + }; + + const handleTabButtonClick = (tabName: TAB_NAME) => { + switch (tabName) { + case TAB_NAME.SETTINGS: + setIsSettingsModalOpen(true); + break; + default: + onActiveTabNameChange(tabName); + } + }; + + return ( + <> + + + {TABS_INFO_LIST.map(({tabName, Icon}) => ( + + ))} + + {/* Forces the settings tab to bottom of sidebar. */} +
+ + + + + + + + ); +}); + +SidebarTabs.displayName = "SidebarTabs"; +export default SidebarTabs; diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/index.css b/new-log-viewer/src/components/CentralContainer/Sidebar/index.css new file mode 100644 index 00000000..323fb710 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/index.css @@ -0,0 +1,3 @@ +.sidebar-tabs-container { + display: flex; +} diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx new file mode 100644 index 00000000..f6a96d32 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx @@ -0,0 +1,108 @@ +import { + useCallback, + useRef, + useState, +} from "react"; + +import {TAB_NAME} from "../../../typings/tab"; +import ResizeHandle from "./ResizeHandle"; +import SidebarTabs from "./SidebarTabs"; + +import "./index.css"; + + +const PANEL_DEFAULT_WIDTH_IN_PIXELS = 360; +const PANEL_CLIP_THRESHOLD_IN_PIXELS = 250; +const PANEL_MAX_WIDTH_TO_WINDOW_WIDTH_RATIO = 0.8; + +/** + * Gets width of the panel from body style properties. + * + * @return the width in pixels as a number. + */ +const getPanelWidth = () => parseInt( + document.body.style.getPropertyValue("--ylv-panel-width"), + 10 +); + +/** + * Sets width of the panel in body style properties. + * + * @param newValue in pixels. + */ +const setPanelWidth = (newValue: number) => { + document.body.style.setProperty("--ylv-panel-width", `${newValue}px`); +}; + + +/** + * Renders a sidebar component that displays tabbed panels and a resize handle. + * The active tab can be changed and the sidebar can be resized by dragging the handle. + * + * @return + */ +const Sidebar = () => { + const [activeTabName, setActiveTabName] = useState(TAB_NAME.FILE_INFO); + + const tabListRef = useRef(null); + + const handleActiveTabNameChange = useCallback((tabName: TAB_NAME) => { + if (null === tabListRef.current) { + console.error("Unexpected null tabListRef.current"); + + return; + } + + if (activeTabName === tabName) { + setActiveTabName(TAB_NAME.NONE); + setPanelWidth(tabListRef.current.clientWidth); + + return; + } + setActiveTabName(tabName); + setPanelWidth(PANEL_DEFAULT_WIDTH_IN_PIXELS); + }, [activeTabName]); + + const handleResizeHandleRelease = useCallback(() => { + if (getPanelWidth() === tabListRef.current?.clientWidth) { + setActiveTabName(TAB_NAME.NONE); + } + }, []); + + const handleResize = useCallback((resizeHandlePosition: number) => { + if (null === tabListRef.current) { + console.error("Unexpected null tabListRef.current"); + + return; + } + if ( + tabListRef.current.clientWidth + PANEL_CLIP_THRESHOLD_IN_PIXELS > + resizeHandlePosition + ) { + // If the resize handle is positioned to the right of the 's right edge + // with a clipping threshold accounted, close the panel. + setPanelWidth(tabListRef.current.clientWidth); + } else if ( + resizeHandlePosition < window.innerWidth * PANEL_MAX_WIDTH_TO_WINDOW_WIDTH_RATIO + ) { + // If the resize handle is positioned to the left of 80% of the window's width, + // update the panel width with the distance between the mouse pointer and the + // window's left edge. + setPanelWidth(resizeHandlePosition); + } + }, []); + + return ( +
+ + {TAB_NAME.NONE !== activeTabName && } +
+ ); +}; + +export default Sidebar; diff --git a/new-log-viewer/src/components/CentralContainer/index.css b/new-log-viewer/src/components/CentralContainer/index.css new file mode 100644 index 00000000..7252d1dc --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/index.css @@ -0,0 +1,9 @@ +:root { + --ylv-panel-width: 360px; +} + +.central-container { + display: grid; + grid-template-columns: var(--ylv-panel-width) calc(100vw - var(--ylv-panel-width)); + width: 100vw; +} diff --git a/new-log-viewer/src/components/CentralContainer/index.tsx b/new-log-viewer/src/components/CentralContainer/index.tsx new file mode 100644 index 00000000..810aa489 --- /dev/null +++ b/new-log-viewer/src/components/CentralContainer/index.tsx @@ -0,0 +1,24 @@ +import DropFileContainer from "../DropFileContainer"; +import Editor from "../Editor"; +import Sidebar from "./Sidebar"; + +import "./index.css"; + + +/** + * Located in the center of the . + * + * @return + */ +const CentralContainer = () => { + return ( +
+ + + + +
+ ); +}; + +export default CentralContainer; diff --git a/new-log-viewer/src/components/DropFileContainer/index.css b/new-log-viewer/src/components/DropFileContainer/index.css index 6d6ca94b..0b653f15 100644 --- a/new-log-viewer/src/components/DropFileContainer/index.css +++ b/new-log-viewer/src/components/DropFileContainer/index.css @@ -2,11 +2,6 @@ position: relative; } -.drop-file-children { - width: 100%; - height: 100%; -} - .hover-mask { position: absolute; z-index: var(--ylv-drop-file-container-hover-mask-z-index); diff --git a/new-log-viewer/src/components/DropFileContainer/index.tsx b/new-log-viewer/src/components/DropFileContainer/index.tsx index a355c9e8..f37fa6fa 100644 --- a/new-log-viewer/src/components/DropFileContainer/index.tsx +++ b/new-log-viewer/src/components/DropFileContainer/index.tsx @@ -70,10 +70,7 @@ const DropFileContainer = ({children}: DropFileContextProviderProps) => { onDragOver={handleDrag} onDrop={handleDrop} > -
+
{children} {isFileHovering && (
{ modeStorageKey={CONFIG_KEY.THEME} theme={APP_THEME} > -
- - - - - -
+ + + ); }; diff --git a/new-log-viewer/src/components/MenuBar/index.css b/new-log-viewer/src/components/MenuBar/index.css index c9f0f0c6..08fe4c66 100644 --- a/new-log-viewer/src/components/MenuBar/index.css +++ b/new-log-viewer/src/components/MenuBar/index.css @@ -1,6 +1,28 @@ .menu-bar { + z-index: var(--ylv-menu-bar-z-index); + display: flex; flex-direction: row; align-items: center; + height: var(--ylv-status-bar-height); + + /* stylelint-disable-next-line custom-property-pattern */ + box-shadow: 0 1px 0 0 var(--joy-palette-neutral-outlinedBorder); +} + +.menu-bar-logo-container { + display: flex; + justify-content: center; + width: 48px; + height: var(--ylv-status-bar-height); +} + +.menu-bar-open-file-icon { + font-size: 24px !important; +} + +.menu-bar-filename { + flex-grow: 1; + padding-left: 10px; } diff --git a/new-log-viewer/src/components/MenuBar/index.tsx b/new-log-viewer/src/components/MenuBar/index.tsx index e2a21487..aac5c979 100644 --- a/new-log-viewer/src/components/MenuBar/index.tsx +++ b/new-log-viewer/src/components/MenuBar/index.tsx @@ -1,26 +1,20 @@ -import { - useContext, - useState, -} from "react"; +import {useContext} from "react"; import { Divider, + IconButton, Sheet, - Stack, + Tooltip, Typography, } from "@mui/joy"; -import DescriptionIcon from "@mui/icons-material/Description"; -import FileOpenIcon from "@mui/icons-material/FileOpen"; -import SettingsIcon from "@mui/icons-material/Settings"; +import FolderOpenIcon from "@mui/icons-material/FolderOpen"; import {StateContext} from "../../contexts/StateContextProvider"; import {CURSOR_CODE} from "../../typings/worker"; import {openFile} from "../../utils/file"; -import SettingsModal from "../modals/SettingsModal"; import ExportLogsButton from "./ExportLogsButton"; import NavigationBar from "./NavigationBar"; -import SmallIconButton from "./SmallIconButton"; import "./index.css"; @@ -33,55 +27,51 @@ import "./index.css"; const MenuBar = () => { const {fileName, loadFile} = useContext(StateContext); - const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false); - - const handleOpenFileButtonClick = () => { + const handleOpenFile = () => { openFile((file) => { loadFile(file, {code: CURSOR_CODE.LAST_EVENT, args: null}); }); }; - const handleSettingsModalClose = () => { - setIsSettingsModalOpen(false); - }; - - const handleSettingsModalOpen = () => { - setIsSettingsModalOpen(true); - }; - return ( <> - + {"yscope-small-logo"} +
+ + + + + + + + + + - - - {fileName} - - + {fileName} + - - - - - - - + - ); }; diff --git a/new-log-viewer/src/components/StatusBar/index.css b/new-log-viewer/src/components/StatusBar/index.css index 770b5336..d732d42e 100644 --- a/new-log-viewer/src/components/StatusBar/index.css +++ b/new-log-viewer/src/components/StatusBar/index.css @@ -1,5 +1,6 @@ .status-bar { position: absolute; + z-index: var(--ylv-status-bar-z-index); bottom: 0; display: flex; @@ -7,6 +8,9 @@ align-items: center; width: 100%; + + /* stylelint-disable-next-line custom-property-pattern */ + box-shadow: 0 -1px 0 0 var(--joy-palette-neutral-outlinedBorder); } .status-message { diff --git a/new-log-viewer/src/contexts/StateContextProvider.tsx b/new-log-viewer/src/contexts/StateContextProvider.tsx index 407adc96..dec854c2 100644 --- a/new-log-viewer/src/contexts/StateContextProvider.tsx +++ b/new-log-viewer/src/contexts/StateContextProvider.tsx @@ -1,4 +1,4 @@ -/* eslint max-lines: ["error", 450] */ +/* eslint max-lines: ["error", 500] */ import React, { createContext, useCallback, @@ -53,6 +53,7 @@ interface StateContextType { logData: string, numEvents: number, numPages: number, + onDiskFileSizeInBytes: number, pageNum: number, exportLogs: () => void, @@ -69,9 +70,10 @@ const STATE_DEFAULT: Readonly = Object.freeze({ beginLineNumToLogEventNum: new Map(), exportProgress: null, fileName: "", - logData: "Loading...", + logData: "No file is open.", numEvents: 0, numPages: 0, + onDiskFileSizeInBytes: 0, pageNum: 0, exportLogs: () => null, @@ -228,6 +230,8 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { const [logData, setLogData] = useState(STATE_DEFAULT.logData); const [numEvents, setNumEvents] = useState(STATE_DEFAULT.numEvents); const [numPages, setNumPages] = useState(STATE_DEFAULT.numPages); + const [onDiskFileSizeInBytes, setOnDiskFileSizeInBytes] = + useState(STATE_DEFAULT.onDiskFileSizeInBytes); const [pageNum, setPageNum] = useState(STATE_DEFAULT.pageNum); const beginLineNumToLogEventNumRef = useRef(STATE_DEFAULT.beginLineNumToLogEventNum); @@ -254,6 +258,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { case WORKER_RESP_CODE.LOG_FILE_INFO: setFileName(args.fileName); setNumEvents(args.numEvents); + setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes); break; case WORKER_RESP_CODE.NOTIFICATION: // eslint-disable-next-line no-warning-comments @@ -322,6 +327,9 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { decoderOptions: getConfig(CONFIG_KEY.DECODER_OPTIONS), }); + setFileName("Loading..."); + setLogData("Loading..."); + setOnDiskFileSizeInBytes(STATE_DEFAULT.onDiskFileSizeInBytes); setExportProgress(STATE_DEFAULT.exportProgress); }, [ handleMainWorkerResp, @@ -427,6 +435,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => { logData: logData, numEvents: numEvents, numPages: numPages, + onDiskFileSizeInBytes: onDiskFileSizeInBytes, pageNum: pageNum, exportLogs: exportLogs, diff --git a/new-log-viewer/src/index.css b/new-log-viewer/src/index.css index e354c3b9..ecf9a72c 100644 --- a/new-log-viewer/src/index.css +++ b/new-log-viewer/src/index.css @@ -18,6 +18,7 @@ html { /* size globals */ --ylv-status-bar-height: 32px; --ylv-menu-bar-height: 32px; + --ylv-panel-resize-handle-width: 4px; /* z-index globals * @@ -26,6 +27,9 @@ html { * .monaco-editor .minimap { z-index: 5; } * ``` */ + --ylv-resize-handle-z-index: 1; + --ylv-menu-bar-z-index: 6; + --ylv-status-bar-z-index: 6; --ylv-drop-file-container-hover-mask-z-index: 10; --ylv-drop-file-container-hover-message-z-index: 11; } diff --git a/new-log-viewer/src/services/LogFileManager/index.ts b/new-log-viewer/src/services/LogFileManager/index.ts index 271ba2bd..b0012b27 100644 --- a/new-log-viewer/src/services/LogFileManager/index.ts +++ b/new-log-viewer/src/services/LogFileManager/index.ts @@ -35,6 +35,8 @@ class LogFileManager { readonly #fileName: string; + readonly #onDiskFileSizeInBytes: number; + #decoder: Decoder; #numEvents: number = 0; @@ -45,14 +47,17 @@ class LogFileManager { * * @param decoder * @param fileName + * @param onDiskFileSizeInBytes * @param pageSize Page size for setting up pagination. */ constructor ( decoder: Decoder, fileName: string, + onDiskFileSizeInBytes: number, pageSize: number, ) { this.#fileName = fileName; + this.#onDiskFileSizeInBytes = onDiskFileSizeInBytes; this.#pageSize = pageSize; this.#decoder = decoder; @@ -70,6 +75,10 @@ class LogFileManager { return this.#fileName; } + get onDiskFileSizeInBytes () { + return this.#onDiskFileSizeInBytes; + } + get numEvents () { return this.#numEvents; } @@ -91,7 +100,7 @@ class LogFileManager { const {fileName, fileData} = await loadFile(fileSrc); const decoder = await LogFileManager.#initDecoder(fileName, fileData, decoderOptions); - return new LogFileManager(decoder, fileName, pageSize); + return new LogFileManager(decoder, fileName, fileData.length, pageSize); } /** diff --git a/new-log-viewer/src/services/MainWorker.ts b/new-log-viewer/src/services/MainWorker.ts index 56313e47..751d4cfc 100644 --- a/new-log-viewer/src/services/MainWorker.ts +++ b/new-log-viewer/src/services/MainWorker.ts @@ -69,6 +69,7 @@ onmessage = async (ev: MessageEvent) => { postResp(WORKER_RESP_CODE.LOG_FILE_INFO, { fileName: LOG_FILE_MANAGER.fileName, numEvents: LOG_FILE_MANAGER.numEvents, + onDiskFileSizeInBytes: LOG_FILE_MANAGER.onDiskFileSizeInBytes, }); postResp( WORKER_RESP_CODE.PAGE_DATA, diff --git a/new-log-viewer/src/typings/tab.ts b/new-log-viewer/src/typings/tab.ts new file mode 100644 index 00000000..b2e8b1bf --- /dev/null +++ b/new-log-viewer/src/typings/tab.ts @@ -0,0 +1,19 @@ +enum TAB_NAME { + NONE = "none", + FILE_INFO = "fileInfo", + SETTINGS = "settings", +} + +/** + * Maps the TAB_NAME enum values to their corresponding display names. + */ +const TAB_DISPLAY_NAMES: Record = Object.freeze({ + [TAB_NAME.NONE]: "None", + [TAB_NAME.FILE_INFO]: "File info", + [TAB_NAME.SETTINGS]: "Settings", +}); + +export { + TAB_DISPLAY_NAMES, + TAB_NAME, +}; diff --git a/new-log-viewer/src/typings/worker.ts b/new-log-viewer/src/typings/worker.ts index 24b2afb9..9ceeea4c 100644 --- a/new-log-viewer/src/typings/worker.ts +++ b/new-log-viewer/src/typings/worker.ts @@ -105,6 +105,7 @@ type WorkerRespMap = { [WORKER_RESP_CODE.LOG_FILE_INFO]: { fileName: string, numEvents: number, + onDiskFileSizeInBytes: number, }, [WORKER_RESP_CODE.PAGE_DATA]: { beginLineNumToLogEventNum: BeginLineNumToLogEventNumMap,