new palette

This commit is contained in:
lucas lelievre
2022-04-29 00:56:01 +02:00
parent 8f1be283a4
commit cccf57d37a
31 changed files with 282 additions and 135 deletions

80
package-lock.json generated
View File

@@ -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,

View File

@@ -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",

View File

@@ -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>}

View File

@@ -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>
)
}

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>
)
}

View File

@@ -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>

View File

@@ -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>
)

View File

@@ -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>
)
});

View File

@@ -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}
/>;

View File

@@ -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];

View File

@@ -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>

View File

@@ -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 (

View File

@@ -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>

View File

@@ -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>)
}

View File

@@ -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>
)

View File

@@ -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 (

View File

@@ -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>
</>

View File

@@ -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}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -37,7 +37,6 @@ export function useProvideAppContext(): AppContext {
state,
dispatch,
setDebug: (value: boolean) => dispatch({ type: 'debug', value }),
}
}

View File

@@ -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');
}

View File

@@ -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';

View File

@@ -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'))
})
})
],
}