project initiate.
This commit is contained in:
11
src/page-components/device/BleControl.module.css
Normal file
11
src/page-components/device/BleControl.module.css
Normal file
@@ -0,0 +1,11 @@
|
||||
@layer pages {
|
||||
.ble_control {
|
||||
height: calc(var(--spacing) * 12);
|
||||
padding-inline: calc(var(--spacing) * 2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: calc(var(--spacing) * 2);
|
||||
}
|
||||
}
|
||||
17
src/page-components/device/BleControl.tsx
Normal file
17
src/page-components/device/BleControl.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { FC } from 'react';
|
||||
import { BleState } from '../../context/EstimContext';
|
||||
import styles from './BleControl.module.css';
|
||||
|
||||
const BleControl: FC = () => {
|
||||
const bleState = useAtomValue(BleState);
|
||||
|
||||
return (
|
||||
<div className={styles.ble_control}>
|
||||
<button disabled={!bleState.ready || bleState.searching}>Scan</button>
|
||||
<button disabled={!bleState.ready || !bleState.connected}>Disconnect</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BleControl;
|
||||
8
src/page-components/device/DeviceDetail.module.css
Normal file
8
src/page-components/device/DeviceDetail.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
@layer pages {
|
||||
.device_detail {
|
||||
flex: 2;
|
||||
border-radius: calc(var(--border-radius) * 2);
|
||||
padding: calc(var(--spacing) * 2);
|
||||
background-color: var(--color-dark-surface-container);
|
||||
}
|
||||
}
|
||||
8
src/page-components/device/DeviceDetail.tsx
Normal file
8
src/page-components/device/DeviceDetail.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
import { FC } from 'react';
|
||||
import styles from './DeviceDetail.module.css';
|
||||
|
||||
const DeviceDetail: FC = () => {
|
||||
return <div className={styles.device_detail}></div>;
|
||||
};
|
||||
|
||||
export default DeviceDetail;
|
||||
8
src/page-components/device/DeviceList.module.css
Normal file
8
src/page-components/device/DeviceList.module.css
Normal file
@@ -0,0 +1,8 @@
|
||||
@layer pages {
|
||||
.devices {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
}
|
||||
13
src/page-components/device/DeviceList.tsx
Normal file
13
src/page-components/device/DeviceList.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { FC } from 'react';
|
||||
import { ScrollArea } from '../../components/ScrollArea';
|
||||
import styles from './DeviceList.module.css';
|
||||
|
||||
const DeviceList: FC = () => {
|
||||
return (
|
||||
<div className={styles.devices}>
|
||||
<ScrollArea enableY></ScrollArea>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceList;
|
||||
11
src/page-components/play-control/ChannelHost.module.css
Normal file
11
src/page-components/play-control/ChannelHost.module.css
Normal file
@@ -0,0 +1,11 @@
|
||||
@layer pages {
|
||||
.channel_host {
|
||||
flex: 1;
|
||||
padding: calc(var(--spacing) * 2) calc(var(--spacing) * 3);
|
||||
border-radius: calc(var(--border-radius) * 2);
|
||||
background-color: var(--color-dark-surface-container);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: calc(var(--spacing));
|
||||
}
|
||||
}
|
||||
17
src/page-components/play-control/ChannelHost.tsx
Normal file
17
src/page-components/play-control/ChannelHost.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { FC } from 'react';
|
||||
import { Channels } from '../../context/EstimContext';
|
||||
import styles from './ChannelHost.module.css';
|
||||
|
||||
type ChannelHostProps = {
|
||||
channel: Channels;
|
||||
};
|
||||
|
||||
const ChannelHost: FC<ChannelHostProps> = ({ channel }) => {
|
||||
return (
|
||||
<div className={styles.channel_host}>
|
||||
<h3>Channel {channel.toUpperCase()}</h3>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelHost;
|
||||
30
src/page-components/state-bar/BleState.tsx
Normal file
30
src/page-components/state-bar/BleState.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { FC, useMemo } from 'react';
|
||||
import { BleState } from '../../context/EstimContext';
|
||||
import IconBluetooth from '../../icons/IconBluetooth';
|
||||
|
||||
const BleStates: FC = () => {
|
||||
const ble = useAtomValue(BleState);
|
||||
const bleIcon = useMemo(() => {
|
||||
if (ble.ready && !ble.searching && !ble.connected) {
|
||||
return 'material-symbols-light:bluetooth';
|
||||
} else if (ble.ready && ble.searching) {
|
||||
return 'material-symbols-light:bluetooth-searching';
|
||||
} else if (ble.ready && !ble.searching && ble.connected) {
|
||||
return 'material-symbols-light:bluetooth-connected';
|
||||
} else {
|
||||
return 'material-symbols-light:bluetooth-disabled';
|
||||
}
|
||||
}, [ble]);
|
||||
|
||||
return (
|
||||
<IconBluetooth
|
||||
height={16}
|
||||
ready={ble.ready}
|
||||
searching={ble.searching}
|
||||
connected={ble.connected?.length > 0}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BleStates;
|
||||
10
src/page-components/state-bar/ChannelStates.module.css
Normal file
10
src/page-components/state-bar/ChannelStates.module.css
Normal file
@@ -0,0 +1,10 @@
|
||||
@layer pages {
|
||||
.channel_state {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: calc(var(--spacing));
|
||||
font-size: calc(var(--font-size) * 1.4);
|
||||
}
|
||||
}
|
||||
35
src/page-components/state-bar/ChannelStates.tsx
Normal file
35
src/page-components/state-bar/ChannelStates.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { FC } from 'react';
|
||||
import { Channels, ChannelState } from '../../context/EstimContext';
|
||||
import { smallIconProps } from '../../icons/shared-props';
|
||||
import styles from './ChannelStates.module.css';
|
||||
|
||||
const ChannelStates: FC<{ channel: Channels }> = ({ channel }) => {
|
||||
const chState = useAtomValue(ChannelState(channel));
|
||||
|
||||
return (
|
||||
<div className={styles.channel_state}>
|
||||
<span>Ch {channel.toUpperCase()}</span>
|
||||
{chState.playing ? (
|
||||
<Icon icon="material-symbols-light:electric-bolt" {...smallIconProps} />
|
||||
) : (
|
||||
<Icon icon="material-symbols-light:stop" {...smallIconProps} />
|
||||
)}
|
||||
<span>{chState.strength}</span>
|
||||
<Icon icon="material-symbols-light:arrow-upload-progress" {...smallIconProps} />
|
||||
<span>{chState.boostLevel}</span>
|
||||
{chState.playMode === 'shuffle' && (
|
||||
<Icon icon="material-symbols-light:shuffle" {...smallIconProps} />
|
||||
)}
|
||||
{chState.playMode === 'repeat' && (
|
||||
<Icon icon="material-symbols-light:repeat" {...smallIconProps} />
|
||||
)}
|
||||
{chState.playMode === 'repeat-one' && (
|
||||
<Icon icon="material-symbols-light:repeat-one" {...smallIconProps} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelStates;
|
||||
18
src/page-components/state-bar/DeviceStates.tsx
Normal file
18
src/page-components/state-bar/DeviceStates.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { FC } from 'react';
|
||||
import { DeviceState } from '../../context/EstimContext';
|
||||
import IconBattery from '../../icons/IconBattery';
|
||||
import IconRssi from '../../icons/IconRssi';
|
||||
|
||||
const DeviceStates: FC = () => {
|
||||
const deviceState = useAtomValue(DeviceState);
|
||||
|
||||
return (
|
||||
<>
|
||||
<IconRssi height={16} level={deviceState.rssi} />
|
||||
<IconBattery height={16} level={deviceState.battery} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeviceStates;
|
||||
13
src/page-components/state-bar/StateBar.module.css
Normal file
13
src/page-components/state-bar/StateBar.module.css
Normal file
@@ -0,0 +1,13 @@
|
||||
@layer pages {
|
||||
.state_bar {
|
||||
flex: 1;
|
||||
padding-block-start: calc(var(--spacing));
|
||||
padding-inline: calc(var(--spacing) * 2);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: calc(var(--spacing) * 4);
|
||||
font-size: calc(var(--font-size) * 1.6);
|
||||
}
|
||||
}
|
||||
18
src/page-components/state-bar/StateBar.tsx
Normal file
18
src/page-components/state-bar/StateBar.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { FC } from 'react';
|
||||
import BleStates from './BleState';
|
||||
import ChannelStates from './ChannelStates';
|
||||
import DeviceStates from './DeviceStates';
|
||||
import styles from './StateBar.module.css';
|
||||
|
||||
const StateBar: FC = () => {
|
||||
return (
|
||||
<div className={styles.state_bar}>
|
||||
<BleStates />
|
||||
<ChannelStates channel="a" />
|
||||
<ChannelStates channel="b" />
|
||||
<DeviceStates />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StateBar;
|
||||
Reference in New Issue
Block a user