modified: src/app/component/Bus.tsx

modified:   src/app/component/BusesList.tsx
	modified:   src/app/component/Strip.tsx
	modified:   src/app/page.tsx
	modified:   src/utils/DetectMobile.ts
This commit is contained in:
Maksym 2024-08-07 23:34:13 +02:00
parent 7a65c76757
commit 42ccd675b8
5 changed files with 142 additions and 91 deletions

View File

@ -1,18 +1,31 @@
"use client";
import { Button, ButtonGroup, Grid, Input, Slider, Stack } from "@mui/material";
import {
Button,
ButtonGroup,
Grid,
Input,
Slider,
Stack,
TextField,
} from "@mui/material";
import React from "react";
import { StripBusOutput, StripBusOutputEvent } from "./StripBusOutput";
import { EventCounter } from "@/utils/EventCounter";
export interface BusProps {
values?: Partial<BusState>;
values?: Partial<Omit<BusState, StateDefaultValueException>>;
showInputdB?: boolean;
name: string;
onChange: (event: BusEvent) => any;
}
type StateDefaultValueException = "showInputdB";
export interface BusState {
gain: number;
muted: boolean;
selected: boolean;
showInputdB: boolean;
}
export type BusEvent = BusMuted | BusGainChanged | BusSelectToggle;
@ -32,26 +45,29 @@ export interface BusSelectToggle {
export class Bus extends React.Component<BusProps, BusState> {
private eventCounter = new EventCounter<"onSliderResetDefaults">();
private defaultValues: BusState = {
private defaultValues: Omit<BusState, StateDefaultValueException> = {
gain: 0,
selected: false,
muted: false,
};
constructor(props: BusProps) {
super(props);
const { values: initialValues } = props;
let { values: initialValues } = props;
const getValue = (name: keyof typeof this.defaultValues) => {
if (initialValues === undefined) return this.defaultValues[name];
return initialValues[name] !== undefined
? this.defaultValues[name]
: initialValues[name];
};
this.state = Object.fromEntries(
Object.entries(this.defaultValues).map(([name]) => [
name,
getValue(name as keyof typeof this.defaultValues) as any,
])
) as BusState;
this.state = {
...Object.fromEntries(
Object.entries(this.defaultValues).map(([name]) => [
name,
getValue(name as keyof typeof this.defaultValues) as any,
])
),
showInputdB: props.showInputdB !== undefined ? props.showInputdB : true,
} as BusState;
this.eventCounter.on({
callback: this.onResetDefaults.bind(this),
count: 2,
@ -69,11 +85,9 @@ export class Bus extends React.Component<BusProps, BusState> {
}
}
onGainChange(event: Event | React.ChangeEvent<HTMLInputElement>) {
let changedGain =
parseFloat((event.target as HTMLInputElement).value);
if(changedGain > 12) changedGain = 12
else if(changedGain < -60) changedGain = -60;
let changedGain = parseFloat((event.target as HTMLInputElement).value);
if (changedGain > 12) changedGain = 12;
else if (changedGain < -60) changedGain = -60;
this.setState({
gain: changedGain,
});
@ -117,20 +131,30 @@ export class Bus extends React.Component<BusProps, BusState> {
render() {
return (
<>
<Stack alignItems={"center"} direction={"row"} spacing={0}>
<Input
inputProps={{
"aria-labelledby": "input-slider",
itemType: "number",
style:{padding: 0}
}}
value={this.state.gain}
onWheel={(e) => this.onStripWheel(e)}
onClick={(e) => this.stopPropagation(e)}
onChange={(e) => this.onGainChange(e as unknown as Event)}
size="small"
sx={{ width: "50px" }}
/>
<Stack
alignItems={"center"}
direction={"row"}
spacing={0}
sx={{ width: "inherit" }}
>
{this.state.showInputdB && (
<TextField
// inputProps={{
// "aria-labelledby": "input-slider",
// itemType: "number",
// style:{padding: 0}
// }}
value={this.state.gain}
onWheel={(e) => this.onStripWheel(e)}
onClick={(e) => this.stopPropagation(e)}
onChange={(e) => this.onGainChange(e as unknown as Event)}
size="small"
label={this.props.name}
type="number"
variant="outlined"
sx={{ minWidth: "100px", maxWidth: "100px" }}
/>
)}
<Slider
sx={{
margin: "5px",

View File

@ -1,4 +1,4 @@
import { Stack, Theme, Typography, useTheme, withTheme } from "@mui/material";
import { Chip, Stack, Theme, Typography, useTheme, withTheme } from "@mui/material";
import React from "react";
import { Bus } from "./Bus";
import { range } from "@/utils/Range";
@ -22,17 +22,16 @@ export class BusesList extends React.Component<BusesListProps, BusesListState> {
) {
const [busId] = args;
const isPhysical = busId < this.props.physical;
const name = `${isPhysical ? "A" : "B"}${
isPhysical ? busId + 1 : busId - this.props.physical + 1
}`;
return (
<React.Fragment
key={`${isPhysical ? "A" : "B"}${
isPhysical ? busId + 1 : busId - this.props.physical + 1
}`}
<Stack direction={"row"} sx={{width:"100%"}} alignItems={"center"}
key={name}
>
<Typography variant="caption">{`${isPhysical ? "A" : "B"}${
isPhysical ? busId + 1 : busId - this.props.physical + 1
}`}</Typography>
<Bus onChange={console.log} />
</React.Fragment>
{/* <Chip variant="outlined" sx={{marginInlineEnd: "15px"}} label={name}/> */}
<Bus name={name} onChange={console.log} />
</Stack>
);
}
render(): React.ReactNode {

View File

@ -7,6 +7,7 @@ import {
Input,
Slider,
Stack,
TextField,
} from "@mui/material";
import React from "react";
import { StripBusOutput, StripBusOutputEvent } from "./StripBusOutput";
@ -16,13 +17,17 @@ export interface StripProps {
physicalBuses: number;
virtualBuses: number;
values?: Partial<StripState>;
showOutputdB?: boolean;
name: string;
onChange: (event: StripEvent) => any;
}
type StateDefaultValueException = "showOutputdB";
export interface StripState {
gain: number;
outputBuses: boolean[];
muted: boolean;
showOutputdB: boolean;
}
export type StripEvent = StripBusOutputChanged | StripMuted | StripGainChanged;
@ -41,7 +46,7 @@ export interface StripMuted {
}
export class Strip extends React.Component<StripProps, StripState> {
private defaultValues: StripState;
private defaultValues: Omit<StripState, StateDefaultValueException>;
private eventCounter = new EventCounter<"onSliderResetDefaults">();
constructor(props: StripProps) {
super(props);
@ -59,12 +64,16 @@ export class Strip extends React.Component<StripProps, StripState> {
? this.defaultValues[name]
: initialValues[name];
};
this.state = Object.fromEntries(
Object.entries(this.defaultValues).map(([name]) => [
name,
getValue(name as keyof typeof this.defaultValues) as any,
])
) as StripState;
this.state = {
...(Object.fromEntries(
Object.entries(this.defaultValues).map(([name]) => [
name,
getValue(name as keyof typeof this.defaultValues) as any,
])
) as Omit<StripState, StateDefaultValueException>),
showOutputdB:
props.showOutputdB !== undefined ? props.showOutputdB : true,
};
this.eventCounter.on({
callback: this.onResetDefaults.bind(this),
count: 2,
@ -135,18 +144,21 @@ export class Strip extends React.Component<StripProps, StripState> {
return (
<>
<Stack direction={"row"}>
<Stack alignItems={"center"} style={{ maxWidth: "min-content" }}>
<Input
inputProps={{
"aria-labelledby": "input-slider",
itemType: "number",
style: { padding: 0 },
}}
<Stack alignItems={"center"} style={{ maxWidth: "70px" }}>
<TextField
// inputProps={{
// "aria-labelledby": "input-slider",
// itemType: "number",
// style: { padding: 0 },
// }}
value={this.state.gain}
onWheel={(e) => this.onStripWheel(e)}
onClick={(e) => this.stopPropagation(e)}
size="small"
sx={{ marginInline: "5px" }}
type="number"
label={this.props.name}
// sx={{minWidth: "4em", maxWidth: "5em"}}
// fullWidth
/>
<Slider
sx={{
@ -171,7 +183,7 @@ export class Strip extends React.Component<StripProps, StripState> {
/>
</Stack>
<Stack>
<ButtonGroup orientation="vertical" sx={{paddingInlineStart:"5px"}}>
<ButtonGroup orientation="vertical">
{[
...Array(this.props.physicalBuses + this.props.virtualBuses),
].map((_v, i) => (

View File

@ -26,7 +26,13 @@ function random(min: number, max: number, floor: boolean = true) {
}
export default function Home() {
const theme = useMemo(() => createTheme({ palette: { mode: "dark",background:{default: "black"} } }), []);
const theme = useMemo(
() =>
createTheme({
palette: { mode: "dark", background: { default: "#000" } },
}),
[]
);
function onStripEvent(event: StripEvent) {
console.log(event);
}
@ -36,9 +42,7 @@ export default function Home() {
}, []);
const [snackBarVisible, setSnackBarVisibility] = useState(false);
const breakPoints = useMemo(() => theme.breakpoints.values, []);
const [width, setWidth] = useState(
1000
);
const [width, setWidth] = useState(1000);
const eventCounter = useMemo(() => {
const eventCounter = new EventCounter<"emptySpaceClick">();
eventCounter.on({
@ -93,6 +97,7 @@ export default function Home() {
const buses = { virtual: 3, physical: 5 };
const amountOfStrips = strips.physical + strips.virtual;
const amountOfBuses = buses.physical + buses.virtual;
const virtualInputNames = useMemo(() => ["VAIO", "AUX", "VAIO3"], []);
if (typeof document !== "undefined") {
document.onclick = () => eventCounter.emit("emptySpaceClick");
window.onresize = () => {
@ -115,8 +120,8 @@ export default function Home() {
);
return (
<ThemeProvider theme={theme}>
<Container
id="container"
<Container
id="container"
maxWidth={"xl"}
style={{ height: "100%", paddingInline: "10px" }}
>
@ -129,35 +134,44 @@ export default function Home() {
display={"flex"}
alignItems={"center"}
justifyContent={"space-evenly"}
columns={{ xs: 8, sm: 12, md: 16, lg: 12 }}
columns={{ xs: 8, sm: 12, md: 16, lg: 14 }}
>
{[...Array(amountOfStrips)].map((_v, stripId) => (
<React.Fragment
key={`${stripId < amountOfStrips ? "a" : "b"}${
stripId < amountOfStrips
? stripId + 1
: stripId + 1 - amountOfStrips
}`}
>
<Grid
lgOffset={stripId === strips.physical ? 2 : 0}
mdOffset={stripId === strips.physical ? 0.5 : 0}
sx={{ minWidth: "fit-content" }}
>
<Typography variant="overline">{`Strip #${
stripId + 1
}`}</Typography>
<Strip
onChange={(ev) => onStripEvent(ev)}
physicalBuses={buses.physical}
virtualBuses={buses.virtual}
/>
</Grid>
</React.Fragment>
))}
{[...Array(amountOfStrips)].map((_v, stripId) => {
const isPhysical = stripId < strips.physical;
const name = `${isPhysical ? "A" : "B"}${
isPhysical ? stripId + 1 : stripId + 1 - strips.physical
}`;
return (
<React.Fragment key={name}>
<Grid
lgOffset={stripId === strips.physical ? 2 : 0}
mdOffset={stripId === strips.physical ? 0.8 : 0}
sx={{ minWidth: "fit-content" }}
>
{/* <Typography variant="overline">{`Strip #${
stripId + 1
}`}</Typography> */}
<Strip
onChange={(ev) => onStripEvent(ev)}
physicalBuses={buses.physical}
virtualBuses={buses.virtual}
name={
isPhysical
? `#${stripId + 1}`
: virtualInputNames[stripId - strips.physical]
}
/>
</Grid>
</React.Fragment>
);
})}
</Grid>
<Typography variant="h5">Outputs</Typography>
<BusesList width={width} physical={buses.physical} virtual={buses.virtual} />
<BusesList
width={width}
physical={buses.physical}
virtual={buses.virtual}
/>
</div>
</Container>
<Snackbar
@ -168,15 +182,16 @@ export default function Home() {
message={
<>
<Typography fontWeight={600}>Tips:</Typography>
<Typography component={"span"}>
<Typography component={"span"}>
<Typography variant={"button"} fontWeight={500}>
Double {isItMobileDevice? "tap": "click"}
Double {isItMobileDevice ? "tap" : "click"}
</Typography>{" "}
on any gain slider to reset it's value to 0 dB<br/>
on any gain slider to reset it's value to 0 dB
<br />
</Typography>
<Typography component={"span"}>
<Typography variant={"button"} fontWeight={500}>
Triple {isItMobileDevice? "tap": "click"}
Triple {isItMobileDevice ? "tap" : "click"}
</Typography>{" "}
on empty space for toggle fullscreen
</Typography>

View File

@ -1,5 +1,6 @@
export function isItMobile(userAgent: string) {
return (
userAgent.includes("Android") ||
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
userAgent
) ||