mirror of
https://github.com/SlimeVR/SlimeVR-Server.git
synced 2026-04-06 02:01:58 +02:00
new palette
This commit is contained in:
80
package-lock.json
generated
80
package-lock.json
generated
@@ -9,6 +9,7 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@fontsource/work-sans": "^4.5.7",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@tauri-apps/api": "^1.0.0-rc.3",
|
||||
@@ -79,6 +80,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.0",
|
||||
"@tailwindcss/typography": "^0.5.2",
|
||||
"@tauri-apps/cli": "^1.0.0-rc.8",
|
||||
"@types/math3d": "^0.2.3",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
@@ -86,6 +88,7 @@
|
||||
"cross-env": "^7.0.3",
|
||||
"node-polyfill-webpack-plugin": "^1.1.4",
|
||||
"postcss": "^8.4.12",
|
||||
"tailwind-scrollbar": "^1.3.1",
|
||||
"tailwindcss": "^3.0.23"
|
||||
}
|
||||
},
|
||||
@@ -1985,6 +1988,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@fontsource/work-sans": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/work-sans/-/work-sans-4.5.7.tgz",
|
||||
"integrity": "sha512-DlVEYsShbL0ZUV96yPhie6rJN3eeCta4iI6UbLdbLptlLnkoryfbMIqeQLe+o7OsIoMNWqHTunKMW0x1BmUNpw=="
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.9.5",
|
||||
"license": "Apache-2.0",
|
||||
@@ -3006,6 +3014,20 @@
|
||||
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz",
|
||||
"integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || insiders"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "1.0.0-rc.3",
|
||||
"license": "Apache-2.0 OR MIT",
|
||||
@@ -10567,10 +10589,22 @@
|
||||
"version": "4.17.21",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.memoize": {
|
||||
"version": "4.1.2",
|
||||
"license": "MIT"
|
||||
@@ -14303,6 +14337,15 @@
|
||||
"version": "3.2.4",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwind-scrollbar": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-1.3.1.tgz",
|
||||
"integrity": "sha512-FeYuLxLtCRMO4PmjPJCzm5wQouFro2BInZXKPxqg54DR/55NAHoS8uNYWMiRG5l6qsLkWBfVEM34gq2XAQUwVg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tailwindcss": ">1.9.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.0.23",
|
||||
"dev": true,
|
||||
@@ -16879,6 +16922,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@fontsource/work-sans": {
|
||||
"version": "4.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/work-sans/-/work-sans-4.5.7.tgz",
|
||||
"integrity": "sha512-DlVEYsShbL0ZUV96yPhie6rJN3eeCta4iI6UbLdbLptlLnkoryfbMIqeQLe+o7OsIoMNWqHTunKMW0x1BmUNpw=="
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.9.5",
|
||||
"requires": {
|
||||
@@ -17466,6 +17514,17 @@
|
||||
"mini-svg-data-uri": "^1.2.3"
|
||||
}
|
||||
},
|
||||
"@tailwindcss/typography": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.2.tgz",
|
||||
"integrity": "sha512-coq8DBABRPFcVhVIk6IbKyyHUt7YTEC/C992tatFB+yEx5WGBQrCgsSFjxHUr8AWXphWckadVJbominEduYBqw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2"
|
||||
}
|
||||
},
|
||||
"@tauri-apps/api": {
|
||||
"version": "1.0.0-rc.3",
|
||||
"requires": {
|
||||
@@ -22086,9 +22145,21 @@
|
||||
"lodash": {
|
||||
"version": "4.17.21"
|
||||
},
|
||||
"lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha1-wCUTUV4wna3dTCTGDP3c9ZdtkRU=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8"
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.memoize": {
|
||||
"version": "4.1.2"
|
||||
},
|
||||
@@ -24290,6 +24361,15 @@
|
||||
"symbol-tree": {
|
||||
"version": "3.2.4"
|
||||
},
|
||||
"tailwind-scrollbar": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-1.3.1.tgz",
|
||||
"integrity": "sha512-FeYuLxLtCRMO4PmjPJCzm5wQouFro2BInZXKPxqg54DR/55NAHoS8uNYWMiRG5l6qsLkWBfVEM34gq2XAQUwVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tailwindcss": ">1.9.6"
|
||||
}
|
||||
},
|
||||
"tailwindcss": {
|
||||
"version": "3.0.23",
|
||||
"dev": true,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.16.0",
|
||||
"@fontsource/work-sans": "^4.5.7",
|
||||
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@tauri-apps/api": "^1.0.0-rc.3",
|
||||
|
||||
25
src/App.tsx
25
src/App.tsx
@@ -8,7 +8,7 @@ import {
|
||||
import { Overview } from './components/Overview';
|
||||
import { BodyProportions } from './components/proportions/BodyProportions';
|
||||
import { AppContextProvider } from './components/providers/AppContext';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { DataFeedConfigT, DataFeedMessage, DeviceDataMaskT, StartDataFeedT, TrackerDataMaskT } from 'solarxr-protocol';
|
||||
import { MainLayoutRoute } from './components/MainLayout';
|
||||
import { SettingsLayoutRoute } from './components/settings/SettingsLayout';
|
||||
@@ -18,6 +18,8 @@ import { Serial } from './components/settings/pages/Serial';
|
||||
|
||||
import { listen } from '@tauri-apps/api/event'
|
||||
import type { Event } from '@tauri-apps/api/event'
|
||||
import { Button } from './components/commons/Button';
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
|
||||
function Layout() {
|
||||
const { sendDataFeedPacket } = useWebsocketAPI();
|
||||
@@ -72,6 +74,7 @@ function Layout() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
function App() {
|
||||
const websocketAPI = useProvideWebsocketApi();
|
||||
|
||||
@@ -97,16 +100,32 @@ function App() {
|
||||
});
|
||||
}, [])
|
||||
|
||||
const updateCorners = () => {
|
||||
// Check if the window is maximized to remove rounded corners
|
||||
const body = document.getElementsByTagName('body');
|
||||
if (!body) return;
|
||||
appWindow.isMaximized().then((maximized) => {
|
||||
body[0].style.borderRadius = maximized ? '0' : `15px`;
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', updateCorners);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updateCorners)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<WebSocketApiContext.Provider value={websocketAPI}>
|
||||
<AppContextProvider>
|
||||
<Router>
|
||||
<div className='bg-primary-1 h-full w-full overflow-hidden'>
|
||||
<div className='h-full w-full text-default bg-purple-gray-900 '>
|
||||
<div className='flex-col h-full'>
|
||||
{!websocketAPI.isConnected && (
|
||||
<>
|
||||
<Navbar></Navbar>
|
||||
<div className='flex w-full h-full justify-center items-center text-white p-2'>Connection lost to server</div>
|
||||
<div className='flex w-full h-full justify-center items-center p-2'>Connection lost to server</div>
|
||||
</>
|
||||
)}
|
||||
{websocketAPI.isConnected && <Layout></Layout>}
|
||||
|
||||
@@ -18,10 +18,9 @@ export function BVHButton() {
|
||||
|
||||
useRPCPacket(RpcMessage.RecordBVHStatus, (data: RecordBVHStatusT) => {
|
||||
setRecording(data.recording);
|
||||
console.log('receiving', data);
|
||||
})
|
||||
|
||||
return (
|
||||
<BigButton text={recording ? 'Recording...' : 'Record BVH'} icon={<RecordIcon />} onClick={toggleBVH}></BigButton>
|
||||
<BigButton text={recording ? 'Recording...' : 'Record BVH'} className="fill-purple-100" icon={<RecordIcon />} onClick={toggleBVH}></BigButton>
|
||||
)
|
||||
}
|
||||
@@ -23,8 +23,8 @@ export function MainLayoutRoute({ children }: { children: ReactChild }) {
|
||||
</>
|
||||
</Navbar>
|
||||
<div ref={ref} className='flex-grow' style={{ height: layoutHeight }}>
|
||||
<div className="flex bg-primary-1 h-full ">
|
||||
<div className="flex flex-grow gap-10 flex-col bg-primary-2 rounded-tr-3xl">
|
||||
<div className="flex h-full ">
|
||||
<div className="flex flex-grow gap-10 flex-col rounded-tr-3xl bg-purple-gray-800">
|
||||
{children}
|
||||
</div>
|
||||
<div className="flex flex-col px-8 w-60 gap-8 pb-5 overflow-y-auto">
|
||||
@@ -40,8 +40,8 @@ export function MainLayoutRoute({ children }: { children: ReactChild }) {
|
||||
<div className='flex flex-grow flex-col justify-end'>
|
||||
{/* <Button variant='primary' className='w-full'>Debug</Button> */}
|
||||
<NavLink to="/settings/trackers" className="flex gap-5 group cursor-pointer">
|
||||
<div className="flex bg-primary-4 rounded-full p-2 fill-purple-300 group-hover:fill-white"><GearIcon></GearIcon></div>
|
||||
<div className="flex flex-col justify-around text-purple-300 group-hover:text-white font-bold">Settings</div>
|
||||
<div className="flex rounded-full p-2 fill-purple-gray-300 bg-purple-gray-700 group-hover:fill-white"><GearIcon></GearIcon></div>
|
||||
<div className="flex flex-col justify-around text-section-indicator group-hover:text-purple-gray-300">Settings</div>
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,15 +8,15 @@ export function AppModal({ children, name, ...props }: { children?: ReactChild,
|
||||
return (
|
||||
<ReactModal
|
||||
{...props}
|
||||
overlayClassName="fixed top-0 right-0 left-0 bottom-0 bg-black bg-opacity-60 flex justify-center items-center overflow-y-auto border-none"
|
||||
className="items-center w-full max-w-2xl h-full md:h-auto relative rounded-lg bg-primary-3 shadow-lg text-white border-none"
|
||||
overlayClassName="fixed top-0 right-0 left-0 bottom-0 flex bg-purple-gray-900 bg-opacity-60 justify-center items-center overflow-y-auto border-none"
|
||||
className="items-center w-full max-w-2xl h-full md:h-auto bg-purple-gray-700 relative rounded-lg shadow-lg border-none"
|
||||
>
|
||||
<div className="flex justify-between items-start p-5 rounded-t border-b-2 border-primary-1">
|
||||
<h3 className="text-xl font-semibold lg:text-2x">
|
||||
<h3 className="text-extra-emphasised">
|
||||
{name}
|
||||
</h3>
|
||||
<div className="flex">
|
||||
<IconButton icon={<CrossIcon></CrossIcon>} onClick={props.onRequestClose}></IconButton>
|
||||
<IconButton icon={<CrossIcon></CrossIcon>} className="fill-purple-gray-200" onClick={props.onRequestClose}></IconButton>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
|
||||
@@ -4,15 +4,11 @@ import {
|
||||
useMatch,
|
||||
NavLink,
|
||||
} from "react-router-dom";
|
||||
import { CubeIcon } from './commons/icon/CubeIcon';
|
||||
import { GearIcon } from './commons/icon/GearIcon';
|
||||
import { SlimeVRIcon } from './commons/icon/SimevrIcon';
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
import { MinimiseIcon } from './commons/icon/MinimiseIcon';
|
||||
import { MaximiseIcon } from './commons/icon/MaximiseIcon';
|
||||
import { CloseIcon } from './commons/icon/CloseIcon';
|
||||
import { useWebsocketAPI } from '../hooks/websocket-api';
|
||||
|
||||
|
||||
export function NavButton({ to, children, match, icon }: { to: string, children: ReactChild, match?: string, icon: ReactChild }) {
|
||||
|
||||
@@ -21,11 +17,11 @@ export function NavButton({ to, children, match, icon }: { to: string, children:
|
||||
});
|
||||
|
||||
return (
|
||||
<NavLink to={to} className={classnames("flex flex-grow flex-row gap-3 py-3 px-8 rounded-t-md group font-bold select-text", { 'bg-primary-2 ': doesMatch, 'hover:bg-primary-3': !doesMatch })}>
|
||||
<NavLink to={to} className={classnames("flex flex-grow flex-row gap-3 py-3 px-8 rounded-t-md group select-text text-emphasised", { 'bg-purple-gray-800 ': doesMatch, 'hover:bg-purple-gray-600': !doesMatch })}>
|
||||
<div className="flex align-middle justify-center justify-items-center flex-col">
|
||||
<div className={classnames("fill-primary-3 group-hover:fill-white", { 'fill-misc-3': doesMatch })}>{icon}</div>
|
||||
<div className={classnames("group-hover:fill-accent-lighter ", { 'fill-accent-lighter': doesMatch, 'fill-purple-gray-600': !doesMatch })}>{icon}</div>
|
||||
</div>
|
||||
<div className={classnames("flex text-md ", { 'text-white': doesMatch, 'text-gray-400': !doesMatch })}>{children}</div>
|
||||
<div className={classnames("flex", { 'text-purple-gray-100': doesMatch, 'text-purple-gray-300': !doesMatch })}>{children}</div>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
@@ -33,27 +29,27 @@ export function NavButton({ to, children, match, icon }: { to: string, children:
|
||||
|
||||
export function Navbar({ children }: { children?: ReactChild }) {
|
||||
return (
|
||||
<div data-tauri-drag-region className='flex bg-primary-1 gap-2'>
|
||||
<div data-tauri-drag-region className='flex gap-2 min-h-[56px]'>
|
||||
<div className="flex px-8 py-2 pt-3 justify-around" data-tauri-drag-region>
|
||||
<div className="flex flex-row gap-3" data-tauri-drag-region>
|
||||
<NavLink to="/" className="flex justify-around flex-col select-all" data-tauri-drag-region>
|
||||
<SlimeVRIcon></SlimeVRIcon>
|
||||
</NavLink>
|
||||
<div className="flex text-white text-xl justify-around flex-col font-bold" data-tauri-drag-region>SlimeVR</div>
|
||||
<div className="flex justify-around flex-col text-extra-emphasised" data-tauri-drag-region>SlimeVR</div>
|
||||
</div>
|
||||
</div>
|
||||
{children && <div className="flex px-5 gap-2 pt-2">
|
||||
{children}
|
||||
</div>}
|
||||
<div className="flex flex-grow justify-end px-2 gap-2" data-tauri-drag-region>
|
||||
<div className='flex flex-col justify-around text-white' onClick={() => appWindow.minimize()}>
|
||||
<MinimiseIcon className="hover:bg-primary-5 rounded-full"></MinimiseIcon>
|
||||
<div className='flex flex-col justify-around ' onClick={() => appWindow.minimize()}>
|
||||
<MinimiseIcon className="rounded-full hover:bg-purple-gray-700"></MinimiseIcon>
|
||||
</div>
|
||||
<div className='flex flex-col justify-around text-white' onClick={() => appWindow.toggleMaximize()}>
|
||||
<MaximiseIcon className="hover:bg-primary-5 rounded-full"></MaximiseIcon>
|
||||
<div className='flex flex-col justify-around ' onClick={() => appWindow.toggleMaximize()}>
|
||||
<MaximiseIcon className="rounded-full hover:bg-purple-gray-700"></MaximiseIcon>
|
||||
</div>
|
||||
<div className='flex flex-col justify-around text-white' onClick={() => appWindow.close()}>
|
||||
<CloseIcon className="hover:bg-primary-5 rounded-full"></CloseIcon>
|
||||
<div className='flex flex-col justify-around ' onClick={() => appWindow.close()}>
|
||||
<CloseIcon className="rounded-full hover:bg-purple-gray-700"></CloseIcon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,7 +34,7 @@ export function Overview() {
|
||||
|
||||
return (
|
||||
<div className="overflow-y-auto flex flex-col gap-8">
|
||||
<div className="flex text-white text-2xl px-8 pt-8 font-bold">
|
||||
<div className="flex px-8 pt-8 text-secondary-heading">
|
||||
Assigned Trackers
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5 sm:grid-cols-1 px-8">
|
||||
@@ -42,7 +42,7 @@ export function Overview() {
|
||||
</div>
|
||||
{unasignedTrackers.length > 0 &&
|
||||
<>
|
||||
<div className="flex text-white text-2xl px-8 pt-8 font-bold">
|
||||
<div className="flex px-8 pt-8 text-secondary-heading">
|
||||
Unassigned Trackers
|
||||
</div>
|
||||
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-5 sm:grid-cols-1 px-8">
|
||||
|
||||
@@ -3,9 +3,9 @@ import React, { ReactChild } from "react";
|
||||
|
||||
export function BigButton({ text, icon, disabled, onClick, ...props }: { text: string, icon: ReactChild } & React.AllHTMLAttributes<HTMLButtonElement>) {
|
||||
return (
|
||||
<button disabled={disabled} onClick={onClick} {...props} type="button" className={classNames("flex w-full flex-col rounded-md hover:bg-primary-5 bg-primary-4 py-10 gap-5 cursor-pointer items-center", { 'bg-gray-500 hover:bg-gray-500 cursor-not-allowed': disabled}, props.className)}>
|
||||
<button disabled={disabled} onClick={onClick} {...props} type="button" className={classNames("flex w-full flex-col rounded-md hover:bg-primary-5 py-10 gap-5 cursor-pointer items-center bg-purple-gray-700 hover:bg-purple-gray-600", { ' hover:bg-purple-gray-300 bg-purple-gray-300 cursor-not-allowed': disabled}, props.className)}>
|
||||
<div className="flex justify-around">{icon}</div>
|
||||
<div className="flex justify-around text-2xl text-white font-bold">{text}</div>
|
||||
<div className="flex justify-around text-field-title">{text}</div>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -3,20 +3,13 @@ import { ReactChild, useMemo } from "react";
|
||||
|
||||
|
||||
|
||||
export function Button({ children, variant, disabled, ...props }: { children: ReactChild, variant: 'primary' | 'secondary' } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||
export function Button({ children, variant, disabled, ...props }: { children: ReactChild, variant: 'primary' } & React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||
|
||||
const classes = useMemo(() => {
|
||||
const variantsMap = {
|
||||
primary: 'text-white bg-primary-5 hover:bg-primary-1 focus:ring-4 focus:outline-none focus:ring-primary-2',
|
||||
secondary: 'text-white hover:bg-primary-1 focus:ring-primary-2'
|
||||
primary: classNames('text-field-title focus:ring-4 focus:outline-none focus:ring-primary-2', { 'bg-purple-gray-600 hover:bg-purple-gray-500': !disabled, 'bg-purple-gray-900 hover:bg-purple-gray-900 text-section-indicator': disabled }),
|
||||
}
|
||||
|
||||
const variantsMapDisabled = {
|
||||
primary: 'bg-gray-800 hover:bg-gray-800',
|
||||
secondary: 'bg-gray-800 hover:bg-gray-800'
|
||||
}
|
||||
|
||||
return classNames(variantsMap[variant], 'focus:ring-4 rounded-lg text-sm px-5 py-2.5 text-center font-medium', disabled ? variantsMapDisabled[variant] : false);
|
||||
return classNames(variantsMap[variant], 'focus:ring-4 rounded-lg px-5 py-2.5 text-center font-medium');
|
||||
|
||||
}, [variant, disabled])
|
||||
return <button type="button" {...props} className={classes} disabled={disabled}>{children}</button>
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { ReactChild } from "react";
|
||||
|
||||
export function IconButton({ icon, className, ...props }: { icon: ReactChild, className?: string } & React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div {...props} className={classNames("px-2 rounded-full h-8 w-8 flex justify-center items-center hover:bg-gray-500 fill-gray-200", className)}>
|
||||
<div {...props} className={classNames("px-2 rounded-full h-8 w-8 flex justify-center items-center hover:bg-purple-gray-600", className)}>
|
||||
{icon}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import classNames from 'classnames';
|
||||
import { forwardRef, useId } from 'react'
|
||||
|
||||
export const CheckBox = forwardRef<HTMLInputElement, { label: string }>(({ label, ...props }, ref) => {
|
||||
export const CheckBox = forwardRef<HTMLInputElement, { label: string, outlined?: boolean }>(({ label, outlined, ...props }, ref) => {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-3 text-white">
|
||||
<input ref={ref} id={id} {...props} className="flex flex-col rounded-sm " type="checkbox" />
|
||||
<label htmlFor={id}>{label}</label>
|
||||
<div className={classNames('flex items-center gap-3', { 'rounded-md bg-purple-gray-700 pl-4 text-emphasised': outlined })}>
|
||||
<input ref={ref} id={id} {...props} className="flex flex-col rounded-sm text-accent-lighter focus:ring-purple-gray-700" type="checkbox" />
|
||||
<label htmlFor={id} className="w-full py-3">{label}</label>
|
||||
</div>
|
||||
)
|
||||
});
|
||||
@@ -9,7 +9,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(({type, placeholde
|
||||
return <input
|
||||
type={type}
|
||||
ref={ref}
|
||||
className="text-white bg-primary-5 rounded-lg border-primary-4 focus:border-primary-1"
|
||||
className="rounded-lg bg-purple-gray-500 border-purple-gray-500 focus:border-purple-gray-500 placeholder:text-purple-gray-300 text-field-title"
|
||||
placeholder={placeholder}
|
||||
{...props}
|
||||
/>;
|
||||
|
||||
@@ -11,13 +11,13 @@ export function NumberSelector({ label, valueLabelFormat, control, name, min, ma
|
||||
const variantsMap = {
|
||||
smol: {
|
||||
container: 'flex flex-col gap-1',
|
||||
label: 'flex text-sm text-white',
|
||||
value: 'flex justify-center items-center w-10 text-white text-lg font-bold'
|
||||
label: 'flex text-field-title',
|
||||
value: 'flex justify-center items-center w-10 text-field-title'
|
||||
},
|
||||
big: {
|
||||
container: 'flex flex-row gap-5 ',
|
||||
label: 'flex flex-grow text-lg font-bold text-white justify-start items-center',
|
||||
value: 'flex justify-center items-center w-16 text-white text-lg font-bold'
|
||||
container: 'flex flex-row gap-5',
|
||||
label: 'flex flex-grow justify-start items-center text-field-title',
|
||||
value: 'flex justify-center items-center w-16 text-field-title'
|
||||
}
|
||||
};
|
||||
return variantsMap[variant];
|
||||
|
||||
@@ -4,9 +4,9 @@ export type SelectOption = { label: string, value: any };
|
||||
|
||||
export const Select = React.forwardRef<HTMLSelectElement, { options: SelectOption[], label?: string }>(({ label, options, ...props }, ref) => {
|
||||
return (
|
||||
<div className="flex flex-col gap-1 text-white">
|
||||
{label && <span className="text-sm ">{label}</span>}
|
||||
<select {...props} ref={ref} className="w-full mt-0 rounded-md bg-primary-5 border-primary-1 shadow-sm focus:border-gray-300 focus:ring focus:ring-gray-200 focus:ring-opacity-50">
|
||||
<div className="flex flex-col gap-1 ">
|
||||
{label && <span className="text-field-title">{label}</span>}
|
||||
<select {...props} ref={ref} className="w-full mt-0 rounded-md border-purple-gray-600 text-field-title bg-purple-gray-600 shadow-sm focus:border-purple-gray-600 focus:ring focus:ring-purple-gray-600 focus:ring-opacity-50">
|
||||
{options && options.map(({ label, value }) => <option value={value} key={value}>{label}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -6,13 +6,13 @@ export function BatteryIcon({ value }: { value: number }) {
|
||||
const col = useMemo(() => {
|
||||
|
||||
const colorsMap: { [key: number]: string } = {
|
||||
0.4: 'fill-misc-1',
|
||||
0.2: 'fill-misc-4',
|
||||
0: 'fill-misc-2',
|
||||
0.4: 'fill-status-online',
|
||||
0.2: 'fill-status-warning',
|
||||
0: 'fill-status-error',
|
||||
}
|
||||
|
||||
const val = Object.keys(colorsMap).filter(key => +key < value).sort((a, b) => +b - +a)[0];
|
||||
return colorsMap[+val] || 'fill-gray-800';
|
||||
return colorsMap[+val] || 'fill-primary-gray-600';
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
|
||||
|
||||
export function CloseIcon({ className }: { className?: string }) {
|
||||
export function CloseIcon({ className, size = 35 }: { className?: string, size?: number }) {
|
||||
return (
|
||||
<svg width="35" height="35" className={className} viewBox="0 0 31 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width={size} height={size} className={className} viewBox="0 0 31 29" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19.3804 17.8804L12.619 11.119" stroke="#C0A1D8" strokeLinecap="round"/>
|
||||
<path d="M12.6196 17.8804L19.381 11.119" stroke="#C0A1D8" strokeLinecap="round"/>
|
||||
</svg>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
|
||||
export function CrossIcon() {
|
||||
return ( <svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd"></path></svg>)
|
||||
return ( <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd"></path></svg>)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
export function RecordIcon() {
|
||||
return (
|
||||
<svg width="33" height="29" className="fill-white" viewBox="0 0 24 24">
|
||||
<svg width="33" height="29" viewBox="0 0 24 24">
|
||||
<path d="M20.84 2.18L16.91 2.96L19.65 6.5L21.62 6.1L20.84 2.18M13.97 3.54L12 3.93L14.75 7.46L16.71 7.07L13.97 3.54M9.07 4.5L7.1 4.91L9.85 8.44L11.81 8.05L9.07 4.5M4.16 5.5L3.18 5.69A2 2 0 0 0 1.61 8.04L2 10L6.9 9.03L4.16 5.5M2 10V20C2 21.11 2.9 22 4 22H20C21.11 22 22 21.11 22 20V10H2Z" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
@@ -11,13 +11,13 @@ export function WifiIcon({ value }: { value: number }) {
|
||||
const col = useMemo(() => {
|
||||
|
||||
const colorsMap: { [key: number]: string } = {
|
||||
0.4: 'fill-misc-1',
|
||||
0.2: 'fill-misc-4',
|
||||
0: 'fill-misc-2',
|
||||
0.4: 'fill-status-online',
|
||||
0.2: 'fill-status-warning',
|
||||
0: 'fill-status-error',
|
||||
}
|
||||
|
||||
const val = Object.keys(colorsMap).filter(key => +key < percent).sort((a, b) => +b - +a)[0];
|
||||
return colorsMap[+val] || 'fill-gray-800';
|
||||
return colorsMap[+val] || 'fill-primary-gray-600';
|
||||
}, [percent])
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,8 +3,6 @@ import { Button } from "../commons/Button";
|
||||
import { AppModal } from "../Modal";
|
||||
|
||||
|
||||
|
||||
|
||||
export function AutomaticCalibration() {
|
||||
|
||||
const [isOpen, setOpen] = useState(false);
|
||||
@@ -23,7 +21,7 @@ export function AutomaticCalibration() {
|
||||
<Button variant="primary">Start Calibration</Button>
|
||||
</div>
|
||||
<div className="flex w-full justify-between mt-5">
|
||||
<Button variant="primary">Close</Button>
|
||||
<Button variant="primary" onClick={() => setOpen(false)}>Close</Button>
|
||||
<Button variant="primary" disabled>Apply values</Button>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -102,29 +102,29 @@ export function BodyProportions() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col overflow-y-auto">
|
||||
<div className="flex text-2xl px-8 pt-8 text-white font-bold">
|
||||
<div className="flex px-8 pt-8 text-secondary-heading">
|
||||
Body Proportions
|
||||
</div>
|
||||
<div className="flex p-5 gap-8 justify-center">
|
||||
<div className="bg-primary-1 h-1/2 sticky top-5 rounded-lg w-72 justify-center p-10 hidden sm:flex">
|
||||
<div className="h-1/2 sticky top-5 rounded-lg w-72 justify-center p-10 hidden sm:flex bg-purple-gray-900">
|
||||
<BodyView selectedBodyPart={selectedBodyPart || 0}></BodyView>
|
||||
</div>
|
||||
<div className="flex-col flex gap-4">
|
||||
<div className="flex gap-3">
|
||||
<div className="flex gap-3 ">
|
||||
<AutomaticCalibration></AutomaticCalibration>
|
||||
<Button variant="primary" onClick={handleResetAll}>Reset all</Button>
|
||||
</div>
|
||||
<div className="flex flex-col rounded-lg py-2 text-white">
|
||||
<div className="font-semibold text-xl">Precision</div>
|
||||
<div className="bg-primary-5 flex text-white rounded-lg overflow-hidden flex-grow h-10 cursor-pointer">
|
||||
<div onClick={() => setStep(0.5)} className={classNames("hover:bg-primary-1 flex flex-grow justify-center items-center", { 'bg-primary-3': step === 0.5 })}>0.5</div>
|
||||
<div onClick={() => setStep(1)} className={classNames("hover:bg-primary-1 flex flex-grow justify-center items-center", { 'bg-primary-3': step === 1 })}>1</div>
|
||||
<div onClick={() => setStep(5)} className={classNames("hover:bg-primary-1 flex flex-grow justify-center items-center", { 'bg-primary-3': step === 5 })}>5</div>
|
||||
<div className="flex flex-col rounded-lg py-2">
|
||||
<div className="text-field-title">Precision</div>
|
||||
<div className="flex rounded-lg overflow-hidden flex-grow h-10 cursor-pointer bg-purple-gray-600 text-field-title">
|
||||
<div onClick={() => setStep(0.5)} className={classNames("hover:bg-purple-gray-500 flex flex-grow justify-center items-center ", { 'bg-purple-gray-400': step === 0.5 })}>0.5</div>
|
||||
<div onClick={() => setStep(1)} className={classNames("hover:bg-purple-gray-500 flex flex-grow justify-center items-center", { 'bg-purple-gray-400': step === 1 })}>1</div>
|
||||
<div onClick={() => setStep(5)} className={classNames("hover:bg-purple-gray-500 flex flex-grow justify-center items-center", { 'bg-purple-gray-400': step === 5 })}>5</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
{bodyParts.map(({label, bone}) =>
|
||||
<div key={bone} onMouseEnter={() => onMouseEnterSelector(bone)} className={classNames('px-3 rounded-lg py-2', { 'bg-primary-1 ': bone === selectedBodyPart })}>
|
||||
<div key={bone} onMouseEnter={() => onMouseEnterSelector(bone)} className={classNames('px-3 rounded-lg py-2', { 'bg-purple-gray-700 ': bone === selectedBodyPart })}>
|
||||
<NumberSelector
|
||||
variant="big"
|
||||
control={control}
|
||||
|
||||
@@ -13,14 +13,14 @@ export function SettingsLayoutRoute({ children }: { children: ReactChild }) {
|
||||
<>
|
||||
<Navbar></Navbar>
|
||||
<div ref={ref} className='flex-grow' style={{ height: layoutHeight }}>
|
||||
<div className="flex bg-primary-1 h-full ">
|
||||
<div className="flex h-full">
|
||||
<SettingsSidebar></SettingsSidebar>
|
||||
<div className="flex flex-grow gap-10 flex-col bg-primary-2 rounded-tl-3xl overflow-hidden">
|
||||
<div className="flex flex-grow gap-10 flex-col rounded-tl-3xl overflow-hidden bg-purple-gray-800">
|
||||
<div className="relative overflow-y-auto overflow-x-hidden">
|
||||
{children}
|
||||
<div className="absolute top-0 right-0 p-5">
|
||||
<NavLink to="/" className="flex gap-5 group cursor-pointer">
|
||||
<div className="flex bg-primary-4 rounded-full p-2 fill-purple-300 group-hover:fill-white"><CloseIcon></CloseIcon></div>
|
||||
<div className="flex rounded-full bg-purple-gray-600 fill-purple-gray-100 group-hover:fill-purple-gray-200"><CloseIcon size={50}></CloseIcon></div>
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,15 +5,15 @@ import { NavLink } from "react-router-dom"
|
||||
export function SettingsSidebar() {
|
||||
return (
|
||||
<div className="flex flex-col px-8 w-72 gap-8 pb-5 overflow-y-auto">
|
||||
<div className="flex flex-col gap-3 pt-4 text-purple-100 font-bold">
|
||||
<div className="flex">TRACKER SETTINGS</div>
|
||||
<div className="flex flex-col gap-2 font-medium">
|
||||
<NavLink to="/settings/trackers" state={{ scrollTo: 'steamvr' }} className="pl-5 py-2 hover:bg-primary-5 rounded-lg">SteamVR</NavLink>
|
||||
<NavLink to="/settings/trackers" state={{ scrollTo: 'filtering' }} className="pl-5 py-2 hover:bg-primary-5 rounded-lg">Filtering</NavLink>
|
||||
<NavLink to="/settings/serial" className="pl-5 py-2 hover:bg-primary-5 rounded-lg">Serial</NavLink>
|
||||
<div className="flex flex-col gap-3 pt-8 text-section-indicator">
|
||||
<div className="flex text-">TRACKER SETTINGS</div>
|
||||
<div className="flex flex-col gap-2 font-medium text-extra-emphasised">
|
||||
<NavLink to="/settings/trackers" state={{ scrollTo: 'steamvr' }} className="pl-5 py-2 hover:text-field-title hover:bg-purple-gray-700 rounded-lg">SteamVR</NavLink>
|
||||
<NavLink to="/settings/trackers" state={{ scrollTo: 'filtering' }} className="pl-5 py-2 hover:text-field-title hover:bg-purple-gray-700 rounded-lg">Filtering</NavLink>
|
||||
<NavLink to="/settings/serial" className="pl-5 py-2 hover:text-field-title hover:bg-purple-gray-700 rounded-lg">Serial</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="flex flex-col gap-3 pt-4 text-purple-100 font-bold">
|
||||
{/* <div className="flex flex-col gap-3 pt-4 ">
|
||||
<div className="flex">USER INTERFACE</div>
|
||||
<div className="flex flex-col gap-2 font-medium">
|
||||
<NavLink to="" className="pl-3 py-2 hover:bg-primary-5 rounded-lg">Widgets</NavLink>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { CloseSerialRequestT, OpenSerialRequestT, RpcMessage, SerialUpdateResponseT, SetWifiRequestT } from "solarxr-protocol";
|
||||
import { useLayout } from "../../../hooks/layout";
|
||||
@@ -19,7 +19,7 @@ export function Serial() {
|
||||
// const consoleRef = useRef<HTMLPreElement>(null);
|
||||
const [consoleContent, setConsole] = useState("");
|
||||
const [isSerialOpen, setSerialOpen] = useState(false);
|
||||
const { register, reset, handleSubmit } = useForm<WifiForm>({ defaultValues: {} });
|
||||
const { register, handleSubmit } = useForm<WifiForm>({ defaultValues: {} });
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
@@ -78,7 +78,7 @@ export function Serial() {
|
||||
|
||||
return (
|
||||
<form className="flex flex-col h-full gap-2" onSubmit={handleSubmit(sendWifiCredentials)}>
|
||||
<div ref={consoleRef} style={{ height: layoutHeight, width: layoutWidth }} className="overflow-x-auto overflow-y-auto flex bg-gray-800 text-white select-text">
|
||||
<div ref={consoleRef} style={{ height: layoutHeight, width: layoutWidth }} className="overflow-x-auto overflow-y-auto flex select-text pl-3">
|
||||
<pre>
|
||||
{isSerialOpen ? consoleContent : 'Connection to serial lost, Reconnecting...'}
|
||||
</pre>
|
||||
|
||||
@@ -89,30 +89,30 @@ export function TrackersSettings() {
|
||||
return (
|
||||
<form className="px-8 flex flex-col gap-20 py-4" ref={pageRef}>
|
||||
<div className="flex flex-col gap-2" id="steamvr">
|
||||
<div className="flex text-white text-3xl font-bold gap-5">
|
||||
<div className="flex gap-5 text-secondary-heading">
|
||||
SteamVR Trackers
|
||||
</div>
|
||||
<div className="flex text-gray-300 flex-col">
|
||||
<div className="flex flex-col text-default">
|
||||
<p>Enable or disable specific tracking parts.</p>
|
||||
<p>Useful if you want more control over what SlimeVR does.</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 text-gray-300 text-xl font-bold gap-5 pt-5">
|
||||
<div className="bg-primary-4 p-3 rounded-md"><CheckBox {...register('trackers.waist')} label="Waist" /></div>
|
||||
<div className="bg-primary-4 p-3 rounded-md"><CheckBox {...register('trackers.chest')} label="Chest"/></div>
|
||||
<div className="bg-primary-4 p-3 rounded-md"><CheckBox {...register('trackers.legs')} label="Legs"/></div>
|
||||
<div className="bg-primary-4 p-3 rounded-md"><CheckBox {...register('trackers.knees')} label="Knees"/></div>
|
||||
<div className="bg-primary-4 p-3 rounded-md"><CheckBox {...register('trackers.elbows')} label="Elbows"/></div>
|
||||
<div className="grid grid-cols-2 gap-5 pt-5">
|
||||
<CheckBox outlined {...register('trackers.waist')} label="Waist" />
|
||||
<CheckBox outlined {...register('trackers.chest')} label="Chest"/>
|
||||
<CheckBox outlined {...register('trackers.legs')} label="Legs"/>
|
||||
<CheckBox outlined {...register('trackers.knees')} label="Knees"/>
|
||||
<CheckBox outlined {...register('trackers.elbows')} label="Elbows"/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2" id="filtering">
|
||||
<div className="flex text-white text-3xl font-bold gap-5">
|
||||
<div className="flex gap-5 text-secondary-heading">
|
||||
Filtering
|
||||
</div>
|
||||
<div className="flex text-gray-300 flex-col">
|
||||
<div className="flex flex-col text-default">
|
||||
<p>I don't know what this does, but I'm</p>
|
||||
<p>designing something for it anyway</p>
|
||||
</div>
|
||||
<div className="flex text-gray-300 text-xl font-bold gap-5 pt-5">
|
||||
<div className="flex gap-5 pt-5">
|
||||
<Select {...register('filtering.type')} label="Filtering Type" options={[{ label: 'None', value: 0 }, { label: 'Interpolation', value: 1 }, {label: 'Extrapolation', value: 2 }]}></Select>
|
||||
<NumberSelector variant="smol" control={control} name="filtering.intensity" label="Intensity" valueLabelFormat={(value) => `${value}%`} min={0} max={100} step={10}></NumberSelector>
|
||||
<NumberSelector variant="smol" control={control} name="filtering.ticks" label="Ticks" min={0} max={80} step={1}></NumberSelector>
|
||||
|
||||
@@ -16,12 +16,12 @@ export function TrackerCard({ tracker, device }: { tracker: TrackerDataT, devic
|
||||
|
||||
const statusClass = useMemo(() => {
|
||||
const statusMap: { [key: number]: string } = {
|
||||
[TrackerStatus.NONE]: 'bg-cyan-800',
|
||||
[TrackerStatus.BUSY]: 'bg-misc-4',
|
||||
[TrackerStatus.ERROR]: 'bg-misc-2',
|
||||
[TrackerStatus.DISCONNECTED]: 'bg-gray-800',
|
||||
[TrackerStatus.OCCLUDED]: 'bg-misc-4',
|
||||
[TrackerStatus.OK]: 'bg-misc-1'
|
||||
[TrackerStatus.NONE]: 'bg-purple-gray-900',
|
||||
[TrackerStatus.BUSY]: 'bg-status-warning',
|
||||
[TrackerStatus.ERROR]: 'bg-status-error',
|
||||
[TrackerStatus.DISCONNECTED]: 'bg-purple-gray-900',
|
||||
[TrackerStatus.OCCLUDED]: 'bg-status-warning',
|
||||
[TrackerStatus.OK]: 'bg-status-online'
|
||||
}
|
||||
return statusMap[tracker.status];
|
||||
}, [tracker.status]);
|
||||
@@ -47,39 +47,38 @@ export function TrackerCard({ tracker, device }: { tracker: TrackerDataT, devic
|
||||
return (
|
||||
<TrackerSettings tracker={tracker} device={device} >
|
||||
<div className={classNames("flex rounded-l-md rounded-r-xl", statusClass)}>
|
||||
<div className="flex bg-primary-4 rounded-r-md py-3 ml-1 pr-2 pl-4 w-full gap-3">
|
||||
<div className="flex rounded-r-md py-3 ml-[5px] pr-4 pl-4 w-full gap-4 bg-purple-gray-700">
|
||||
<div className="flex flex-grow flex-col truncate gap-2">
|
||||
<div className="flex text-white font-bold">{trackerName}</div>
|
||||
<div className="flex flex-row gap-4 ">
|
||||
<div className="flex text-field-title">{trackerName}</div>
|
||||
<div className="flex flex-row gap-4 text-default">
|
||||
{device && device.hardwareStatus &&
|
||||
<>
|
||||
<div className="flex gap-2 flex-grow">
|
||||
{device.hardwareStatus.rssi && <div className="flex flex-col justify-around">
|
||||
<WifiIcon value={device.hardwareStatus?.rssi} />
|
||||
</div>}
|
||||
{device.hardwareStatus.ping && <div className="flex text-gray-400 text-sm w-10">{device.hardwareStatus.ping} ms</div>}
|
||||
{device.hardwareStatus.ping && <div className="flex w-10">{device.hardwareStatus.ping} ms</div>}
|
||||
</div>
|
||||
{device.hardwareStatus.batteryPctEstimate &&
|
||||
<div className="flex w-1/3 gap-2">
|
||||
<div className="flex flex-col justify-around">
|
||||
<BatteryIcon value={device.hardwareStatus.batteryPctEstimate / 100}/>
|
||||
</div>
|
||||
<div className="flex text-gray-400 text-sm">{((device.hardwareStatus.batteryPctEstimate)).toFixed(0)} %</div>
|
||||
<div className="flex">{((device.hardwareStatus.batteryPctEstimate)).toFixed(0)} %</div>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
}
|
||||
|
||||
<div className="flex w-1/3 gap-0.5 justify-around flex-col">
|
||||
<div className="w-full bg-gray-200 rounded-full h-1 dark:bg-gray-700">
|
||||
<div className="bg-misc-3 h-1 rounded-full" style={{width: `${velocity * 100}%`}}></div>
|
||||
<div className="flex w-1/3 gap-0.5 justify-around flex-col">
|
||||
<div className="w-full rounded-full h-1 bg-purple-gray-600">
|
||||
<div className="h-1 rounded-full bg-accent-darker" style={{width: `${velocity * 100}%`}}></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{tracker.info?.editable &&
|
||||
<div className="flex flex-col flex-shrink justify-around">
|
||||
<IconButton icon={<GearIcon/>}/>
|
||||
<IconButton className="fill-purple-gray-300" icon={<GearIcon/>}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,6 @@ export function useProvideAppContext(): AppContext {
|
||||
state,
|
||||
dispatch,
|
||||
setDebug: (value: boolean) => dispatch({ type: 'debug', value }),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
body {
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-family: "Work Sans", sans-serif;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
user-select: none;
|
||||
@@ -21,3 +23,28 @@ html {
|
||||
}
|
||||
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: theme('colors.purple-gray.900');
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: theme('colors.purple-gray.600');
|
||||
border-radius: 50px;
|
||||
border-radius: 50px;
|
||||
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: theme('colors.purple-gray.400');
|
||||
border-radius: 50px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: theme('colors.purple-gray.300');
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import * as ReactDOMClient from 'react-dom/client';
|
||||
import "@fontsource/work-sans/variable.css";
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
@@ -1,25 +1,60 @@
|
||||
const plugin = require('tailwindcss/plugin')
|
||||
|
||||
const rem = (px) => `${(px / 12).toFixed(4)}rem`
|
||||
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{js,jsx,ts,tsx}",],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: {
|
||||
1: '#201527',
|
||||
2: '#26192E',
|
||||
3: '#2F2037',
|
||||
4: '#382740',
|
||||
5: '#432E4D'
|
||||
'accent': {
|
||||
darker: '#831ECC',
|
||||
lighter: '#C06FFB'
|
||||
},
|
||||
misc: {
|
||||
1: '#50E897',
|
||||
2: '#FF486E',
|
||||
3: '#A44FED',
|
||||
4: '#D5C055'
|
||||
}
|
||||
'status': {
|
||||
online: '#9AFF76',
|
||||
warning: '#FFB257',
|
||||
error: '#FF6464'
|
||||
},
|
||||
'purple-gray': {
|
||||
900: "#160B1D",
|
||||
800: "#261730",
|
||||
700: "#3F2A4F",
|
||||
600: "#593E6C",
|
||||
500: "#6E5084",
|
||||
400: "#8E6BA7",
|
||||
300: "#C0A1D8",
|
||||
200: "#EFE2F9",
|
||||
100: "#FFFFFF"
|
||||
},
|
||||
},
|
||||
fontSize: {
|
||||
DEFAULT: rem(12),
|
||||
},
|
||||
fontWeight: {
|
||||
DEFAULT: 400,
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
plugin(function({ addUtilities, theme }) {
|
||||
|
||||
const textConfig = (fontSize, fontWeight, color) => ({
|
||||
color,
|
||||
fontSize,
|
||||
fontWeight
|
||||
})
|
||||
|
||||
addUtilities({
|
||||
'.text-heading': textConfig(rem(35), 700, theme('colors.purple-gray.100')),
|
||||
'.text-secondary-heading': textConfig(rem(25), 700, theme('colors.purple-gray.100')),
|
||||
'.text-field-title': textConfig(rem(12), 700, theme('colors.purple-gray.100')),
|
||||
'.text-extra-emphasised': textConfig(rem(12), 600, theme('colors.purple-gray.100')),
|
||||
'.text-emphasised': textConfig(rem(12), 400, theme('colors.purple-gray.100')),
|
||||
'.text-default': textConfig(rem(12), 400, theme('colors.purple-gray.300')),
|
||||
'.text-section-indicator': textConfig(rem(12), 700, theme('colors.purple-gray.400'))
|
||||
})
|
||||
})
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user