modified: package-lock.json
modified: package.json modified: src/app/component/Bus.tsx new file: src/app/component/BusesList.tsx modified: src/app/component/Strip.tsx modified: src/app/component/StripBusOutput.tsx deleted: src/app/globals.css modified: src/app/page.tsx new file: src/utils/DetectMobile.ts modified: src/utils/EventCounter.ts new file: src/utils/Range.ts modified: tsconfig.json
This commit is contained in:
parent
16b2de18a9
commit
7a65c76757
7
package-lock.json
generated
7
package-lock.json
generated
|
@ -10,6 +10,7 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@fontsource/roboto": "^5.0.14",
|
||||
"@mui/material": "^5.15.20",
|
||||
"next": "14.2.4",
|
||||
"next-pwa": "^5.6.0",
|
||||
|
@ -1966,6 +1967,12 @@
|
|||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz",
|
||||
"integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw=="
|
||||
},
|
||||
"node_modules/@fontsource/roboto": {
|
||||
"version": "5.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.14.tgz",
|
||||
"integrity": "sha512-zHAxlTTm9RuRn9/StwclFJChf3z9+fBrOxC3fw71htjHP1BgXNISwRjdJtAKAmMe5S2BzgpnjkQR93P9EZYI/Q==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.11.14",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.5",
|
||||
"@fontsource/roboto": "^5.0.14",
|
||||
"@mui/material": "^5.15.20",
|
||||
"next": "14.2.4",
|
||||
"next-pwa": "^5.6.0",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import { Button, ButtonGroup, Grid, Input, Slider, Stack } from "@mui/material";
|
||||
import React from "react";
|
||||
import { StripBusOutput, StripBusOutputEvent } from "./StripBusOutput";
|
||||
import { EventCounter } from "@/utils/EventCounter";
|
||||
|
||||
export interface BusProps {
|
||||
values?: Partial<BusState>;
|
||||
|
@ -30,28 +31,36 @@ export interface BusSelectToggle {
|
|||
}
|
||||
|
||||
export class Bus extends React.Component<BusProps, BusState> {
|
||||
private eventCounter = new EventCounter<"onSliderResetDefaults">();
|
||||
private defaultValues: BusState = {
|
||||
gain: 0,
|
||||
selected: false,
|
||||
muted: false,
|
||||
};
|
||||
constructor(props: BusProps) {
|
||||
super(props);
|
||||
const { values: initialValues } = props;
|
||||
const defaultValues: BusState = {
|
||||
gain: 0,
|
||||
selected: false,
|
||||
muted: false,
|
||||
};
|
||||
const getValue = (name: keyof typeof defaultValues) => {
|
||||
if (initialValues === undefined) return defaultValues[name];
|
||||
const getValue = (name: keyof typeof this.defaultValues) => {
|
||||
if (initialValues === undefined) return this.defaultValues[name];
|
||||
return initialValues[name] !== undefined
|
||||
? defaultValues[name]
|
||||
? this.defaultValues[name]
|
||||
: initialValues[name];
|
||||
};
|
||||
this.state = Object.fromEntries(
|
||||
Object.entries(defaultValues).map(([name]) => [
|
||||
Object.entries(this.defaultValues).map(([name]) => [
|
||||
name,
|
||||
getValue(name as keyof typeof defaultValues) as any,
|
||||
getValue(name as keyof typeof this.defaultValues) as any,
|
||||
])
|
||||
) as BusState;
|
||||
this.eventCounter.on({
|
||||
callback: this.onResetDefaults.bind(this),
|
||||
count: 2,
|
||||
name: "onSliderResetDefaults",
|
||||
});
|
||||
}
|
||||
onResetDefaults() {
|
||||
this.setState({ gain: this.defaultValues.gain });
|
||||
}
|
||||
onResetDefaults(e: React.MouseEvent) {}
|
||||
onKeyDown(event: React.KeyboardEvent) {
|
||||
console.log(event);
|
||||
|
||||
|
@ -59,12 +68,12 @@ export class Bus extends React.Component<BusProps, BusState> {
|
|||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const multiplier = (event as unknown as React.KeyboardEvent).shiftKey
|
||||
? 2
|
||||
: 1;
|
||||
const changedGain =
|
||||
parseFloat((event.target as HTMLInputElement).value) * multiplier;
|
||||
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;
|
||||
this.setState({
|
||||
gain: changedGain,
|
||||
});
|
||||
|
@ -76,7 +85,12 @@ export class Bus extends React.Component<BusProps, BusState> {
|
|||
onStripWheel(event: React.WheelEvent) {
|
||||
console.log(event);
|
||||
}
|
||||
onMuteToggle() {
|
||||
onSliderClick(event: React.MouseEvent) {
|
||||
this.stopPropagation(event);
|
||||
this.eventCounter.emit("onSliderResetDefaults");
|
||||
}
|
||||
onMuteToggle(event: React.MouseEvent) {
|
||||
this.stopPropagation(event);
|
||||
this.setState(
|
||||
(state) => ({ muted: !state.muted }),
|
||||
() => {
|
||||
|
@ -84,7 +98,8 @@ export class Bus extends React.Component<BusProps, BusState> {
|
|||
}
|
||||
);
|
||||
}
|
||||
onSelectToggle() {
|
||||
onSelectToggle(event: React.MouseEvent) {
|
||||
this.stopPropagation(event);
|
||||
this.setState(
|
||||
(state) => ({ selected: !state.selected }),
|
||||
() => {
|
||||
|
@ -95,6 +110,10 @@ export class Bus extends React.Component<BusProps, BusState> {
|
|||
}
|
||||
);
|
||||
}
|
||||
stopPropagation(event: Event | React.MouseEvent) {
|
||||
if (event instanceof Event) event.stopImmediatePropagation();
|
||||
else event.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
|
@ -103,9 +122,12 @@ export class Bus extends React.Component<BusProps, BusState> {
|
|||
inputProps={{
|
||||
"aria-labelledby": "input-slider",
|
||||
itemType: "number",
|
||||
style:{padding: 0}
|
||||
}}
|
||||
value={this.state.gain}
|
||||
onWheel={(ev) => this.onStripWheel(ev)}
|
||||
onWheel={(e) => this.onStripWheel(e)}
|
||||
onClick={(e) => this.stopPropagation(e)}
|
||||
onChange={(e) => this.onGainChange(e as unknown as Event)}
|
||||
size="small"
|
||||
sx={{ width: "50px" }}
|
||||
/>
|
||||
|
@ -115,29 +137,31 @@ export class Bus extends React.Component<BusProps, BusState> {
|
|||
marginInline: "15px",
|
||||
}}
|
||||
// orientation="vertical"
|
||||
color={
|
||||
(this.state.gain > 0 && this.state.gain < 12 && "warning") ||
|
||||
(this.state.gain >= 12 && "error") ||
|
||||
(this.state.gain < 0 && "info") ||
|
||||
"primary"
|
||||
}
|
||||
defaultValue={0.0}
|
||||
step={0.1}
|
||||
min={-60}
|
||||
max={12}
|
||||
value={this.state.gain}
|
||||
onChange={(ev) =>
|
||||
this.onChange(
|
||||
ev as unknown as React.ChangeEvent<HTMLInputElement>
|
||||
)
|
||||
}
|
||||
onKeyDown={(ev) => this.onKeyDown(ev)}
|
||||
onChange={(e) => this.onGainChange(e)}
|
||||
onClick={(e) => this.onSliderClick(e)}
|
||||
/>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
onClick={() => this.onMuteToggle()}
|
||||
onClick={(e) => this.onMuteToggle(e)}
|
||||
variant={this.state.muted ? "contained" : "outlined"}
|
||||
color={"error"}
|
||||
>
|
||||
Mute
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => this.onSelectToggle()}
|
||||
onClick={(e) => this.onSelectToggle(e)}
|
||||
sx={{}}
|
||||
variant={this.state.selected ? "contained" : "outlined"}
|
||||
color={"warning"}
|
||||
|
|
61
src/app/component/BusesList.tsx
Normal file
61
src/app/component/BusesList.tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import { Stack, Theme, Typography, useTheme, withTheme } from "@mui/material";
|
||||
import React from "react";
|
||||
import { Bus } from "./Bus";
|
||||
import { range } from "@/utils/Range";
|
||||
|
||||
export interface BusesListProps {
|
||||
physical: number;
|
||||
virtual: number;
|
||||
width: number;
|
||||
}
|
||||
export interface BusesListState {
|
||||
}
|
||||
|
||||
export class BusesList extends React.Component<BusesListProps, BusesListState> {
|
||||
constructor(props: BusesListProps) {
|
||||
super(props);
|
||||
}
|
||||
mapCallback(
|
||||
...args: Parameters<
|
||||
Parameters<Parameters<typeof Array<number>>["map"]>["0"]
|
||||
>
|
||||
) {
|
||||
const [busId] = args;
|
||||
const isPhysical = busId < this.props.physical;
|
||||
return (
|
||||
<React.Fragment
|
||||
key={`${isPhysical ? "A" : "B"}${
|
||||
isPhysical ? busId + 1 : busId - this.props.physical + 1
|
||||
}`}
|
||||
>
|
||||
<Typography variant="caption">{`${isPhysical ? "A" : "B"}${
|
||||
isPhysical ? busId + 1 : busId - this.props.physical + 1
|
||||
}`}</Typography>
|
||||
<Bus onChange={console.log} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
render(): React.ReactNode {
|
||||
if (this.props.width > 600) {
|
||||
return (
|
||||
<Stack direction={"row"} spacing={this.props.width > 1200 ? 4 : 2}>
|
||||
<Stack sx={{ width: "50%" }}>
|
||||
{range(this.props.physical).map(this.mapCallback.bind(this))}
|
||||
</Stack>
|
||||
<Stack sx={{ width: "50%" }}>
|
||||
{range(this.props.virtual, this.props.physical).map(
|
||||
this.mapCallback.bind(this)
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Stack>
|
||||
{range(this.props.physical + this.props.virtual).map(
|
||||
this.mapCallback.bind(this)
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import {
|
|||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import { StripBusOutput, StripBusOutputEvent } from "./StripBusOutput";
|
||||
import { EventCounter } from "@/utils/EventCounter";
|
||||
|
||||
export interface StripProps {
|
||||
physicalBuses: number;
|
||||
|
@ -40,32 +41,42 @@ export interface StripMuted {
|
|||
}
|
||||
|
||||
export class Strip extends React.Component<StripProps, StripState> {
|
||||
private defaultValues: StripState;
|
||||
private eventCounter = new EventCounter<"onSliderResetDefaults">();
|
||||
constructor(props: StripProps) {
|
||||
super(props);
|
||||
const { values: initialValues } = props;
|
||||
const defaultValues: StripState = {
|
||||
this.defaultValues = {
|
||||
gain: 0,
|
||||
outputBuses: [...Array(props.physicalBuses + props.virtualBuses)].map(
|
||||
(_v, k) => false
|
||||
),
|
||||
muted: false,
|
||||
};
|
||||
const getValue = (name: keyof typeof defaultValues) => {
|
||||
if (initialValues === undefined) return defaultValues[name];
|
||||
const getValue = (name: keyof typeof this.defaultValues) => {
|
||||
if (initialValues === undefined) return this.defaultValues[name];
|
||||
return initialValues[name] !== undefined
|
||||
? defaultValues[name]
|
||||
? this.defaultValues[name]
|
||||
: initialValues[name];
|
||||
};
|
||||
this.state = Object.fromEntries(
|
||||
Object.entries(defaultValues).map(([name]) => [
|
||||
Object.entries(this.defaultValues).map(([name]) => [
|
||||
name,
|
||||
getValue(name as keyof typeof defaultValues) as any,
|
||||
getValue(name as keyof typeof this.defaultValues) as any,
|
||||
])
|
||||
) as StripState;
|
||||
this.eventCounter.on({
|
||||
callback: this.onResetDefaults.bind(this),
|
||||
count: 2,
|
||||
name: "onSliderResetDefaults",
|
||||
});
|
||||
}
|
||||
onResetDefaults(e: React.MouseEvent) {
|
||||
e.stopPropagation();
|
||||
this.setState({ gain: 0 });
|
||||
onResetDefaults() {
|
||||
this.setState({ gain: this.defaultValues.gain });
|
||||
}
|
||||
onSliderClick(event: React.MouseEvent) {
|
||||
this.stopPropagation(event);
|
||||
this.eventCounter.emit("onSliderResetDefaults");
|
||||
}
|
||||
onKeyDown(event: React.KeyboardEvent) {
|
||||
console.log(event);
|
||||
|
@ -74,7 +85,8 @@ export class Strip extends React.Component<StripProps, StripState> {
|
|||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
onChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
onGainChange(event: Event) {
|
||||
this.stopPropagation(event);
|
||||
const multiplier = (event as unknown as React.KeyboardEvent).shiftKey
|
||||
? 2
|
||||
: 1;
|
||||
|
@ -106,7 +118,8 @@ export class Strip extends React.Component<StripProps, StripState> {
|
|||
onStripWheel(event: React.WheelEvent) {
|
||||
console.log(event);
|
||||
}
|
||||
onMuteToggle() {
|
||||
onMuteToggle(event: React.MouseEvent) {
|
||||
this.stopPropagation(event);
|
||||
this.setState(
|
||||
(state) => ({ muted: !state.muted }),
|
||||
() => {
|
||||
|
@ -114,6 +127,10 @@ export class Strip extends React.Component<StripProps, StripState> {
|
|||
}
|
||||
);
|
||||
}
|
||||
stopPropagation(event: Event | React.MouseEvent) {
|
||||
if (event instanceof Event) event.stopImmediatePropagation();
|
||||
else event.nativeEvent.stopImmediatePropagation();
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<>
|
||||
|
@ -123,17 +140,25 @@ export class Strip extends React.Component<StripProps, StripState> {
|
|||
inputProps={{
|
||||
"aria-labelledby": "input-slider",
|
||||
itemType: "number",
|
||||
style: { padding: 0 },
|
||||
}}
|
||||
value={this.state.gain}
|
||||
onWheel={(ev) => this.onStripWheel(ev)}
|
||||
onWheel={(e) => this.onStripWheel(e)}
|
||||
onClick={(e) => this.stopPropagation(e)}
|
||||
size="small"
|
||||
sx={{ marginInline: "5px" }}
|
||||
/>
|
||||
<Slider
|
||||
sx={{
|
||||
margin: "5px",
|
||||
marginBlock: "15px",
|
||||
marginBlockStart: "15px",
|
||||
}}
|
||||
color={
|
||||
(this.state.gain > 0 && this.state.gain < 12 && "warning") ||
|
||||
(this.state.gain >= 12 && "error") ||
|
||||
(this.state.gain < 0 && "info") ||
|
||||
"primary"
|
||||
}
|
||||
orientation="vertical"
|
||||
defaultValue={0.0}
|
||||
step={0.1}
|
||||
|
@ -141,26 +166,12 @@ export class Strip extends React.Component<StripProps, StripState> {
|
|||
max={12}
|
||||
value={this.state.gain}
|
||||
aria-label="Temperature"
|
||||
onChange={(ev) =>
|
||||
this.onChange(
|
||||
ev as unknown as React.ChangeEvent<HTMLInputElement>
|
||||
)
|
||||
}
|
||||
onKeyDown={(ev) => this.onKeyDown(ev)}
|
||||
onChange={(e) => this.onGainChange(e)}
|
||||
onClick={(e) => this.onSliderClick(e)}
|
||||
/>
|
||||
|
||||
<Button
|
||||
onClick={() => this.onMuteToggle()}
|
||||
variant={this.state.muted ? "contained" : "outlined"}
|
||||
color={"error"}
|
||||
style={{ paddingInline: "0px", maxWidth: "5px" }}
|
||||
size="large"
|
||||
>
|
||||
Mute
|
||||
</Button>
|
||||
</Stack>
|
||||
<Stack>
|
||||
<ButtonGroup orientation="vertical">
|
||||
<ButtonGroup orientation="vertical" sx={{paddingInlineStart:"5px"}}>
|
||||
{[
|
||||
...Array(this.props.physicalBuses + this.props.virtualBuses),
|
||||
].map((_v, i) => (
|
||||
|
@ -176,10 +187,18 @@ export class Strip extends React.Component<StripProps, StripState> {
|
|||
: i - this.props.physicalBuses
|
||||
}
|
||||
isVirtual={i < this.props.physicalBuses ? false : true}
|
||||
onChange={(ev) => this.onBusChange(ev)}
|
||||
onChange={(e) => this.onBusChange(e)}
|
||||
default={{ enabled: this.state.outputBuses[i] }}
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
onClick={(e) => this.onMuteToggle(e)}
|
||||
variant={this.state.muted ? "contained" : "outlined"}
|
||||
color={"error"}
|
||||
size="small"
|
||||
>
|
||||
Mute
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
|
|
@ -30,6 +30,7 @@ export class StripBusOutput extends React.Component<StripProps, StripState> {
|
|||
};
|
||||
}
|
||||
onClick(event: React.MouseEvent) {
|
||||
event.nativeEvent.stopImmediatePropagation()
|
||||
this.setState(
|
||||
(state) => ({ enabled: !state.enabled }),
|
||||
() => {
|
||||
|
@ -47,8 +48,8 @@ export class StripBusOutput extends React.Component<StripProps, StripState> {
|
|||
<>
|
||||
<Button
|
||||
size="small"
|
||||
sx={{ width: "5px", height: "35px" }}
|
||||
onClick={(ev) => this.onClick(ev)}
|
||||
sx={{ height: "35px" }}
|
||||
onClick={(e) => this.onClick(e)}
|
||||
variant={this.state.enabled ? "contained" : "outlined"}
|
||||
>
|
||||
{this.props.isVirtual ? "B" : "A"}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--foreground-rgb: 0, 0, 0;
|
||||
--background-start-rgb: 214, 219, 220;
|
||||
--background-end-rgb: 255, 255, 255;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
168
src/app/page.tsx
168
src/app/page.tsx
|
@ -16,6 +16,9 @@ import {
|
|||
} from "@mui/material";
|
||||
import { Bus } from "./component/Bus";
|
||||
import Grid from "@mui/material/Unstable_Grid2";
|
||||
import { EventCounter } from "@/utils/EventCounter";
|
||||
import { BusesList } from "./component/BusesList";
|
||||
import { isItMobile } from "@/utils/DetectMobile";
|
||||
|
||||
function random(min: number, max: number, floor: boolean = true) {
|
||||
const value = Math.random() * (max - min + 1) + min;
|
||||
|
@ -23,7 +26,7 @@ function random(min: number, max: number, floor: boolean = true) {
|
|||
}
|
||||
|
||||
export default function Home() {
|
||||
const theme = useMemo(() => createTheme({ palette: { mode: "dark" } }), []);
|
||||
const theme = useMemo(() => createTheme({ palette: { mode: "dark",background:{default: "black"} } }), []);
|
||||
function onStripEvent(event: StripEvent) {
|
||||
console.log(event);
|
||||
}
|
||||
|
@ -34,61 +37,64 @@ export default function Home() {
|
|||
const [snackBarVisible, setSnackBarVisibility] = useState(false);
|
||||
const breakPoints = useMemo(() => theme.breakpoints.values, []);
|
||||
const [width, setWidth] = useState(
|
||||
typeof document !== "undefined" ? window.innerWidth : 0
|
||||
1000
|
||||
);
|
||||
const eventCounter = useMemo(() => {
|
||||
const eventCounter = new EventCounter<"emptySpaceClick">();
|
||||
eventCounter.on({
|
||||
callback: () => {
|
||||
if (document.fullscreenElement === null)
|
||||
document.documentElement.requestFullscreen();
|
||||
else document.exitFullscreen();
|
||||
},
|
||||
count: 3,
|
||||
name: "emptySpaceClick",
|
||||
});
|
||||
return eventCounter;
|
||||
}, []);
|
||||
const isItMobileDevice = useMemo(() => isItMobile(navigator.userAgent), []);
|
||||
useEffect(() => {
|
||||
setSnackBarVisibility(!isSnackBarHidden);
|
||||
setWidth(window.innerWidth);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const values = {} as Record<string, [number, "bigger" | "smaller" | "equal"]>;
|
||||
type valuesKey = keyof typeof breakPoints;
|
||||
for (const key in breakPoints) {
|
||||
if (Object.prototype.hasOwnProperty.call(breakPoints, key)) {
|
||||
const breakPointWidth = breakPoints[key as keyof typeof breakPoints];
|
||||
values[key as valuesKey] =
|
||||
[breakPointWidth, breakPointWidth === width
|
||||
? "equal"
|
||||
: breakPointWidth > width
|
||||
? "bigger"
|
||||
: "smaller"];
|
||||
}
|
||||
}
|
||||
console.clear()
|
||||
console.log(
|
||||
width,
|
||||
`Closest: ${
|
||||
Object.entries(breakPoints).reduce((acc, i) => {
|
||||
const equation = Math.abs(i[1] - width) <= Math.abs(acc[1] - width) ? i : acc;
|
||||
return equation;
|
||||
})[0]
|
||||
}`,
|
||||
);
|
||||
console.table(values);
|
||||
}, [width]);
|
||||
// useEffect(() => {
|
||||
// const values = {} as Record<
|
||||
// string,
|
||||
// [number, "bigger" | "smaller" | "equal"]
|
||||
// >;
|
||||
// type valuesKey = keyof typeof breakPoints;
|
||||
// for (const key in breakPoints) {
|
||||
// if (Object.prototype.hasOwnProperty.call(breakPoints, key)) {
|
||||
// const breakPointWidth = breakPoints[key as keyof typeof breakPoints];
|
||||
// values[key as valuesKey] = [
|
||||
// breakPointWidth,
|
||||
// breakPointWidth === width
|
||||
// ? "equal"
|
||||
// : breakPointWidth > width
|
||||
// ? "bigger"
|
||||
// : "smaller",
|
||||
// ];
|
||||
// }
|
||||
// }
|
||||
// console.clear();
|
||||
// console.log(
|
||||
// width,
|
||||
// `Closest: ${
|
||||
// Object.entries(breakPoints).reduce((acc, i) => {
|
||||
// const equation =
|
||||
// Math.abs(i[1] - width) <= Math.abs(acc[1] - width) ? i : acc;
|
||||
// return equation;
|
||||
// })[0]
|
||||
// }`
|
||||
// );
|
||||
// console.table(values);
|
||||
// }, [width]);
|
||||
const strips = { virtual: 3, physical: 5 };
|
||||
const buses = { virtual: 3, physical: 5 };
|
||||
const amountOfStrips = strips.physical + strips.virtual;
|
||||
const amountOfBuses = buses.physical + buses.virtual;
|
||||
let clicks = 0;
|
||||
let timeout: NodeJS.Timeout | undefined;
|
||||
if (typeof document !== "undefined") {
|
||||
document.onclick = (e) => {
|
||||
clicks++;
|
||||
if (timeout !== undefined) {
|
||||
clearTimeout(timeout);
|
||||
timeout = undefined;
|
||||
}
|
||||
timeout = setTimeout(() => (clicks = 0), 250);
|
||||
console.log(e);
|
||||
if (clicks < 2) return;
|
||||
clearTimeout(timeout);
|
||||
clicks = 0;
|
||||
|
||||
// e.stopPropagation();
|
||||
|
||||
// if (document.fullscreenElement !== null) document.exitFullscreen();
|
||||
// else document.body.requestFullscreen({ navigationUI: "hide" });
|
||||
};
|
||||
document.onclick = () => eventCounter.emit("emptySpaceClick");
|
||||
window.onresize = () => {
|
||||
setWidth(window.innerWidth);
|
||||
};
|
||||
|
@ -109,17 +115,21 @@ export default function Home() {
|
|||
);
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<Container maxWidth={"xl"} sx={{ height: "100%", paddingInline: "5px" }}>
|
||||
<Container
|
||||
id="container"
|
||||
maxWidth={"xl"}
|
||||
style={{ height: "100%", paddingInline: "10px" }}
|
||||
>
|
||||
<div>
|
||||
<CssBaseline />
|
||||
<Typography variant="h5">Inputs</Typography>
|
||||
<Grid
|
||||
container
|
||||
spacing={width ? 0 : 1}
|
||||
// spacing={width ? 0 : 1}
|
||||
display={"flex"}
|
||||
alignItems={"center"}
|
||||
justifyContent={"space-evenly"}
|
||||
columns={{ xs: 8, sm: 12, md: 5, lg: 12 }}
|
||||
columns={{ xs: 8, sm: 12, md: 16, lg: 12 }}
|
||||
>
|
||||
{[...Array(amountOfStrips)].map((_v, stripId) => (
|
||||
<React.Fragment
|
||||
|
@ -129,22 +139,9 @@ export default function Home() {
|
|||
: stripId + 1 - amountOfStrips
|
||||
}`}
|
||||
>
|
||||
{stripId === amountOfStrips && width > breakPoints.lg && (
|
||||
<Grid>
|
||||
<Divider
|
||||
sx={{ marginBlock: "15px", borderWidth: "1px" }}
|
||||
variant="fullWidth"
|
||||
orientation="vertical"
|
||||
>
|
||||
Virtual Inputs
|
||||
</Divider>
|
||||
</Grid>
|
||||
)}
|
||||
<Grid
|
||||
lg={stripId + 1 === strips.physical ? 2 : 0}
|
||||
xs={1}
|
||||
sm={1}
|
||||
md={0}
|
||||
lgOffset={stripId === strips.physical ? 2 : 0}
|
||||
mdOffset={stripId === strips.physical ? 0.5 : 0}
|
||||
sx={{ minWidth: "fit-content" }}
|
||||
>
|
||||
<Typography variant="overline">{`Strip #${
|
||||
|
@ -159,29 +156,8 @@ export default function Home() {
|
|||
</React.Fragment>
|
||||
))}
|
||||
</Grid>
|
||||
|
||||
<Typography variant="h5">Outputs</Typography>
|
||||
<Grid container spacing={1} direction={"column"}>
|
||||
{[...Array(amountOfBuses)].map((_v, busId) => (
|
||||
<Grid
|
||||
key={`${busId < buses.physical ? "a" : "b"}${
|
||||
busId < buses.physical
|
||||
? busId + 1
|
||||
: busId + 1 - buses.physical
|
||||
}`}
|
||||
xs={12}
|
||||
>
|
||||
<Typography variant="overline">{`Bus ${
|
||||
busId < buses.physical ? "a" : "b"
|
||||
}${
|
||||
busId < buses.physical
|
||||
? busId + 1
|
||||
: busId + 1 - buses.physical
|
||||
}`}</Typography>
|
||||
<Bus onChange={console.log} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
<BusesList width={width} physical={buses.physical} virtual={buses.virtual} />
|
||||
</div>
|
||||
</Container>
|
||||
<Snackbar
|
||||
|
@ -189,7 +165,23 @@ export default function Home() {
|
|||
TransitionComponent={Slide}
|
||||
transitionDuration={250}
|
||||
open={snackBarVisible}
|
||||
message="Triple tap on empty space for toggle fullscreen"
|
||||
message={
|
||||
<>
|
||||
<Typography fontWeight={600}>Tips:</Typography>
|
||||
<Typography component={"span"}>
|
||||
<Typography variant={"button"} fontWeight={500}>
|
||||
Double {isItMobileDevice? "tap": "click"}
|
||||
</Typography>{" "}
|
||||
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"}
|
||||
</Typography>{" "}
|
||||
on empty space for toggle fullscreen
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
action={snackBarActionButton}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
|
|
10
src/utils/DetectMobile.ts
Normal file
10
src/utils/DetectMobile.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
export function isItMobile(userAgent: string) {
|
||||
return (
|
||||
/(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
|
||||
) ||
|
||||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
|
||||
userAgent.substring(0, 4)
|
||||
)
|
||||
);
|
||||
}
|
|
@ -1,29 +1,55 @@
|
|||
interface EventListener {
|
||||
name: keyof EventCounter["events"];
|
||||
callback: Function | Promise<any>;
|
||||
callback: Function;
|
||||
count: number;
|
||||
}
|
||||
type EventName = string;
|
||||
type EventInfo = number[];
|
||||
|
||||
export class EventCounter {
|
||||
events: Record<EventName, EventInfo> = {};
|
||||
type EventInfo = number;
|
||||
|
||||
export class EventCounter<T extends string> {
|
||||
events: Record<T, EventInfo> = {} as Record<T, EventInfo>;
|
||||
timer: Record<EventName, NodeJS.Timeout | undefined> = {};
|
||||
listeners: Record<EventName, EventListener[]> = {};
|
||||
|
||||
private checkListeners(eventName: EventListener["name"]) {
|
||||
this.events[eventName];
|
||||
constructor(public timeout: number = 200) {}
|
||||
|
||||
private getMaxClicksLimit(eventName: T) {
|
||||
if(this.listeners[eventName] === undefined) return 0;
|
||||
return Math.max(...this.listeners[eventName].map((e) => e.count));
|
||||
}
|
||||
private countEmits(eventName: EventListener["name"], periodOfTime: number) {}
|
||||
emit(eventName: EventListener["name"]) {
|
||||
|
||||
this.events[eventName].length >= 100 && this.events[eventName].shift();
|
||||
}
|
||||
on(args: EventListener) {
|
||||
|
||||
private emitListeners(eventName: T) {
|
||||
const counter = this.events[eventName];
|
||||
this.events[eventName] = 0;
|
||||
const listeners = this.listeners[eventName].filter(
|
||||
(e) => e.count === counter
|
||||
);
|
||||
for (const listener of listeners) {
|
||||
listener.callback();
|
||||
}
|
||||
}
|
||||
|
||||
emit(eventName: T) {
|
||||
if (eventName in this.events === undefined) this.events[eventName] = 0;
|
||||
if (this.timer[eventName] !== undefined) {
|
||||
clearTimeout(this.timer[eventName]);
|
||||
this.timer[eventName] = undefined;
|
||||
}
|
||||
const timeoutCallback = () => {
|
||||
this.emitListeners(eventName);
|
||||
this.timer[eventName] = undefined;
|
||||
};
|
||||
this.events[eventName]++;
|
||||
console.log(this.events);
|
||||
if (this.events[eventName] >= this.getMaxClicksLimit(eventName))
|
||||
return timeoutCallback();
|
||||
|
||||
this.timer[eventName] = setTimeout(timeoutCallback, this.timeout);
|
||||
}
|
||||
on(args: EventListener & { name: T }) {
|
||||
const { name: eventName, callback, count } = args;
|
||||
if (eventName in this.events === undefined) this.events[eventName] = [];
|
||||
if (!(eventName in this.events)) this.events[eventName] = 0;
|
||||
if (!Object.prototype.hasOwnProperty.call(this.listeners, eventName))
|
||||
this.listeners[eventName] = [];
|
||||
this.listeners[eventName].push({ callback, count, name: eventName });
|
||||
this.listeners[eventName].push({ callback, count });
|
||||
}
|
||||
}
|
||||
|
|
3
src/utils/Range.ts
Normal file
3
src/utils/Range.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export function range(size: number, from: number = 0) {
|
||||
return [...Array(size).keys()].map((i) => i + from);
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2023",
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
|
|
Loading…
Reference in New Issue
Block a user