Make style responsive

This commit is contained in:
Arnaud 2024-11-22 15:07:40 +01:00
parent 3304a6411f
commit 3f64433d7d
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
37 changed files with 484 additions and 248 deletions

12
package-lock.json generated
View File

@ -9,7 +9,7 @@
"version": "0.0.13",
"license": "MIT",
"dependencies": {
"@codex-storage/marketplace-ui-components": "^0.0.46",
"@codex-storage/marketplace-ui-components": "^0.0.47",
"@codex-storage/sdk-js": "^0.0.16",
"@sentry/browser": "^8.32.0",
"@sentry/react": "^8.31.0",
@ -27,7 +27,7 @@
"@preact/preset-vite": "^2.9.1",
"@svgr/plugin-svgo": "^8.1.0",
"@tanstack/router-plugin": "^1.58.4",
"@types/node": "^22.7.5",
"@types/node": "^22.9.1",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.7.0",
@ -42,7 +42,7 @@
"prettier": "^3.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "5.5.4",
"typescript": "^5.5.4",
"vite": "^5.4.7",
"vite-plugin-svgr": "^4.3.0",
"vitest": "^2.1.4"
@ -395,9 +395,9 @@
}
},
"node_modules/@codex-storage/marketplace-ui-components": {
"version": "0.0.46",
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.46.tgz",
"integrity": "sha512-HEkuKOFjigOOkgY/If68WLXYN89+jJ1bCTwL8dNBt2N5QdFF/KmoKQ67X0SlOFAgxz6DdK4cskd07iLTiT5YUA==",
"version": "0.0.47",
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.47.tgz",
"integrity": "sha512-dcnZsYEFB1IoEx9sSC3Z1gaReLf6SAFmFNvfnBs3U81mAFnOd/nGi7IUY/ipPyN9U+J8nrGlAOsWnzNiPlEOpA==",
"engines": {
"node": ">=18"
},

View File

@ -15,7 +15,8 @@
"preview": "vite preview --host 127.0.0.1 --port 5173",
"format": "prettier --write ./src",
"test": "npx playwright test",
"test:unit": "vitest run"
"test:unit": "vitest run",
"knip": "knip"
},
"keywords": [
"Codex",
@ -25,7 +26,7 @@
"React"
],
"dependencies": {
"@codex-storage/marketplace-ui-components": "^0.0.46",
"@codex-storage/marketplace-ui-components": "^0.0.47",
"@codex-storage/sdk-js": "^0.0.16",
"@sentry/browser": "^8.32.0",
"@sentry/react": "^8.31.0",
@ -43,7 +44,7 @@
"@preact/preset-vite": "^2.9.1",
"@svgr/plugin-svgo": "^8.1.0",
"@tanstack/router-plugin": "^1.58.4",
"@types/node": "^22.7.5",
"@types/node": "^22.9.1",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^8.7.0",
@ -58,7 +59,7 @@
"prettier": "^3.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "5.5.4",
"typescript": "^5.5.4",
"vite": "^5.4.7",
"vite-plugin-svgr": "^4.3.0",
"vitest": "^2.1.4"

View File

@ -1,7 +1,5 @@
<svg
data-testid="icon-success"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg">

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 391 B

View File

@ -19,9 +19,12 @@ import HelpIcon from "../../assets/icons/help.svg?react";
import DisclaimerIcon from "../../assets/icons/disclaimer.svg?react";
import { WalletConnect } from "../WalletLogin/WalletLogin";
import { useNavigate } from "react-router-dom";
import Logo from "../../assets/icons/logo.svg?react";
import { useIsMobile } from "../../hooks/useMobile";
type Props = {
onIconClick: () => void;
onExpanded: (val: boolean) => void;
};
const icons: Record<string, ReactElement> = {
@ -50,12 +53,13 @@ const descriptions: Record<string, string> = {
disclaimer: "Important information.",
};
export function AppBar({ onIconClick }: Props) {
export function AppBar({ onIconClick, onExpanded }: Props) {
const online = useNetwork();
const queryClient = useQueryClient();
const codex = useCodexConnection();
const persistence = usePersistence(codex.enabled);
const navigate = useNavigate();
const isMobile = useIsMobile();
useEffect(() => {
queryClient.invalidateQueries({
@ -80,6 +84,12 @@ export function AppBar({ onIconClick }: Props) {
? "#3EE089"
: "var(--codex-input-color-warning)";
const icon = isMobile ? (
<Logo onClick={() => onExpanded(true)}></Logo>
) : (
icons[title]
);
return (
<>
<div
@ -89,7 +99,7 @@ export function AppBar({ onIconClick }: Props) {
["app-bar--no-persistence", !persistence.enabled]
)}>
<div className="row gap">
<span onClick={onIconClick}>{icons[title]}</span>
<span onClick={onIconClick}>{icon}</span>
<div>
<h1>{title}</h1>

View File

@ -86,4 +86,10 @@
cursor: pointer;
}
}
@media (max-width: 800px) {
aside {
display: none;
}
}
}

View File

@ -20,6 +20,12 @@
.row {
margin-bottom: 16px;
@media screen and (max-width: 800px) {
& {
flex-direction: column;
}
}
}
.group {

View File

@ -185,40 +185,38 @@ export function AvailabilityForm({
</div>
</div>
<div>
<div className="row gap">
<div className="group">
<Input
id="minPrice"
name="minPrice"
type="number"
label="Min price"
min={0}
onChange={onInputChange}
value={availability.minPrice.toString()}
/>
<Tooltip message={"Minimum price to be paid (in amount of tokens)"}>
<InfoIcon></InfoIcon>
</Tooltip>
</div>
<div className="row gap">
<div className="group">
<Input
id="minPrice"
name="minPrice"
type="number"
label="Min price"
min={0}
onChange={onInputChange}
value={availability.minPrice.toString()}
/>
<Tooltip message={"Minimum price to be paid (in amount of tokens)"}>
<InfoIcon></InfoIcon>
</Tooltip>
</div>
<div className="group">
<Input
id="maxCollateral"
name="maxCollateral"
type="number"
label="Max collateral"
min={0}
onChange={onInputChange}
value={availability.maxCollateral.toString()}
/>
<Tooltip
message={
"Maximum collateral user is willing to pay per filled Slot (in amount of tokens)"
}>
<InfoIcon></InfoIcon>
</Tooltip>
</div>
<div className="group">
<Input
id="maxCollateral"
name="maxCollateral"
type="number"
label="Max collateral"
min={0}
onChange={onInputChange}
value={availability.maxCollateral.toString()}
/>
<Tooltip
message={
"Maximum collateral user is willing to pay per filled Slot (in amount of tokens)"
}>
<InfoIcon></InfoIcon>
</Tooltip>
</div>
</div>

View File

@ -14,7 +14,7 @@ export function AvailabilitySuccess({ dispatch }: AvailabilityComponentProps) {
return (
<Placeholder
Icon={<SuccessCircleIcon />}
Icon={<SuccessCircleIcon width={40} height={40} />}
title="Success"
message="The new sale will appear in your sale list. You can safely close this dialog."></Placeholder>
);

View File

@ -1,5 +1,3 @@
.sunburst {
height: 350px;
width: 350px;
margin: auto;
}

View File

@ -4,7 +4,6 @@ import { Strings } from "../../utils/strings";
import { PrettyBytes } from "../../utils/bytes";
import { useEffect, useRef, useState } from "react";
import { CallbackDataParams, ECBasicOption } from "echarts/types/dist/shared";
// Import the echarts core module, which provides the necessary interfaces for using echarts.
import * as echarts from "echarts/core";
// Import bar charts, all suffixed with Chart
@ -19,7 +18,6 @@ type Props = {
};
import { TooltipComponent } from "echarts/components";
import { SVGRenderer } from "echarts/renderers";
echarts.use([SunburstChart, TooltipComponent, SVGRenderer]);
@ -97,80 +95,80 @@ export function Sunburst({ availabilities, space }: Props) {
};
});
const option: ECBasicOption = {
series: {
type: "sunburst",
data: [
...data,
{
name: "Space remaining",
value:
space.quotaMaxBytes -
space.quotaReservedBytes -
space.quotaUsedBytes,
children: [],
itemStyle: {
color: "#2F2F2F",
borderColor: "transparent",
},
tooltip: {
backgroundColor: "#333",
textStyle: {
color: "#fff",
},
formatter: (params: CallbackDataParams) => {
return (
params.marker +
" Space remaining " +
PrettyBytes(
space.quotaMaxBytes -
space.quotaReservedBytes -
space.quotaUsedBytes
)
);
},
},
},
],
radius: [60, "90%"],
itemStyle: {
borderWidth: 1,
},
label: {
show: false,
},
levels: [
{},
{
r0: "35%",
r: "70%",
label: {
align: "right",
},
},
{
r0: "75%",
r: "85%",
itemStyle: {},
label: {
position: "outside",
textShadowBlur: 5,
textShadowColor: "#333",
},
downplay: {
label: {
opacity: 1,
},
},
},
],
},
tooltip: {
// type: "item",
},
};
if (chart.current) {
const option: ECBasicOption = {
series: {
type: "sunburst",
data: [
...data,
{
name: "Space remaining",
value:
space.quotaMaxBytes -
space.quotaReservedBytes -
space.quotaUsedBytes,
children: [],
itemStyle: {
color: "#2F2F2F",
borderColor: "transparent",
},
tooltip: {
backgroundColor: "#333",
textStyle: {
color: "#fff",
},
formatter: (params: CallbackDataParams) => {
return (
params.marker +
" Space remaining " +
PrettyBytes(
space.quotaMaxBytes -
space.quotaReservedBytes -
space.quotaUsedBytes
)
);
},
},
},
],
radius: [60, "90%"],
itemStyle: {
borderWidth: 1,
},
label: {
show: false,
},
levels: [
{},
{
r0: "35%",
r: "70%",
label: {
align: "right",
},
},
{
r0: "75%",
r: "85%",
itemStyle: {},
label: {
position: "outside",
textShadowBlur: 5,
textShadowColor: "#333",
},
downplay: {
label: {
opacity: 1,
},
},
},
],
},
tooltip: {
// type: "item",
},
};
chart.current.setOption(option);
// chart.current.off("click");
// chart.current.on("click", function (params) {
@ -190,5 +188,16 @@ export function Sunburst({ availabilities, space }: Props) {
// });
}
return <div id="chart" ref={div} className="sunburst"></div>;
const size = window.innerWidth > 500 ? 350 : 300;
return (
<div
id="chart"
ref={div}
className="sunburst"
style={{
width: size,
height: size,
}}></div>
);
}

View File

@ -2,7 +2,7 @@
border-radius: 8px;
display: flex;
flex-direction: column;
min-width: 550px;
/* min-width: 550px; */
main {
flex: 1;

View File

@ -13,7 +13,7 @@ export const CustomStateCellRender = ({ state, message }: Props) => {
pending: PurchaseStateIcon,
submitted: PurchaseStateIcon,
started: PurchaseStateIcon,
finished: SuccessCircleIcon,
finished: () => <SuccessCircleIcon width={20} />,
cancelled: ErrorCircleIcon,
errored: ErrorCircleIcon,
};

View File

@ -16,4 +16,10 @@
border: 1px solid #96969633;
}
}
@media (max-width: 800px) {
.folder-button {
display: none;
}
}
}

View File

@ -26,4 +26,15 @@
table thead tr th {
background-color: #14141499;
}
@media (max-width: 800px) {
table th:nth-child(2),
table td:nth-child(2),
table th:nth-child(3),
table td:nth-child(3),
section,
.filters {
display: none;
}
}
}

View File

@ -42,7 +42,7 @@ export function FolderButton({ folders, onFolderToggle }: Props) {
{folders.map(([folder, isActive]) => (
<div key={folder} onClick={() => onFolderToggle(folder)}>
<span>{folder}</span>
{isActive && <SuccessCircleIcon></SuccessCircleIcon>}
{isActive && <SuccessCircleIcon width={20}></SuccessCircleIcon>}
</div>
))}
</div>

View File

@ -116,7 +116,7 @@ export function HealthChecks({ online, onStepValid }: Props) {
{isAddressInvalid ? (
<ErrorCircleIcon width={16} />
) : (
<SuccessCircleIcon />
<SuccessCircleIcon width={20} />
)}
</div>
@ -129,7 +129,7 @@ export function HealthChecks({ online, onStepValid }: Props) {
value={port}
isInvalid={isPortInvalid}
placeholder="8080"></Input>
<SuccessCircleIcon></SuccessCircleIcon>
<SuccessCircleIcon width={20}></SuccessCircleIcon>
</div>
<div className="refresh">

View File

@ -1,6 +1,6 @@
import { attributes } from "../../utils/attributes";
import "./menu.css";
import { ComponentType, useState } from "react";
import { ComponentType, useEffect } from "react";
import { classnames } from "../../utils/classnames";
import HomeIcon from "../../assets/icons/home.svg?react";
import ExpandIcon from "../../assets/icons/expand.svg?react";
@ -19,11 +19,17 @@ import SettingsIcon from "../../assets/icons/settings.svg?react";
import HelpIcon from "../../assets/icons/help.svg?react";
import DisclaimerIcon from "../../assets/icons/disclaimer.svg?react";
import { NavLink } from "react-router-dom";
import { useIsMobile } from "../../hooks/useMobile";
export type MenuItemComponentProps = {
onClick: () => void;
};
export type Props = {
isExpanded: boolean;
onExpanded: (val: boolean) => void;
};
export type MenuItem =
| {
type: "separator";
@ -36,16 +42,28 @@ export type MenuItem =
Component: ComponentType<MenuItemComponentProps>;
};
export function Menu() {
const [isExpanded, setIsExpanded] = useState(true);
export function Menu({ isExpanded, onExpanded }: Props) {
const isMobile = useIsMobile();
const onLogoClick = () => {
if (isExpanded === false) {
setIsExpanded(true);
onExpanded(true);
}
};
const onExpandMenu = () => setIsExpanded(!isExpanded);
useEffect(() => {
if (isMobile) {
onExpanded(false);
}
}, [isMobile, onExpanded]);
const onExpandMenu = () => onExpanded(!isExpanded);
const onClose = () => {
if (isMobile) {
onExpanded(false);
}
};
return (
<>
@ -64,35 +82,38 @@ export function Menu() {
</header>
<div className="items">
<NavLink to="/dashboard" end>
<NavLink onClick={onClose} to="/dashboard" end>
<span>
<HomeIcon />
</span>
<span>Dashboard</span>
</NavLink>
<NavLink to="/dashboard/wallet">
<NavLink onClick={onClose} to="/dashboard/wallet">
<span>
<WalletIcon width={20} height={20} />
</span>
<span>Wallet</span>
</NavLink>
<NavLink to="/dashboard/files">
<NavLink onClick={onClose} to="/dashboard/files">
<span>
<FilesIcon width={20} />
</span>
<span>Files</span>
</NavLink>
<NavLink
to="/dashboard/nodes"
onClick={onClose}
to="#"
aria-disabled={true}
data-title="Coming soon">
data-title="Coming soon"
end>
<span>
<NodesIcon width={20} />
</span>
<span>Nodes</span>
</NavLink>
<NavLink
to="/dashboard/analytics"
onClick={onClose}
to="#"
aria-disabled={true}
title="Coming soon"
data-title="Coming soon">
@ -102,7 +123,8 @@ export function Menu() {
<span>Analytics</span>
</NavLink>
<NavLink
to="/dashboard/device"
onClick={onClose}
to="#"
aria-disabled={true}
title="Coming soon"
data-title="Coming soon">
@ -112,45 +134,45 @@ export function Menu() {
<span>Devices</span>
</NavLink>
<hr />
<NavLink to="/dashboard/purchases">
<NavLink onClick={onClose} to="/dashboard/purchases">
<span>
<PurchaseIcon />
</span>
<span>Purchases</span>
</NavLink>
<NavLink to="/dashboard/availabilities">
<NavLink onClick={onClose} to="/dashboard/availabilities">
<span>
<HostIcon />
</span>
<span>Host</span>
</NavLink>
<hr />
<NavLink to="/dashboard/peers">
<NavLink onClick={onClose} to="/dashboard/peers">
<span>
<PeersIcon width={20} />
</span>
<span>Peers</span>
</NavLink>
<NavLink to="/dashboard/logs">
<NavLink onClick={onClose} to="/dashboard/logs">
<span>
<LogsIcon width={24} />
</span>
<span>Log</span>
</NavLink>
<section></section>
<NavLink to="/dashboard/settings">
<NavLink onClick={onClose} to="/dashboard/settings">
<span>
<SettingsIcon width={24} />
</span>
<span>Settings</span>
</NavLink>
<NavLink to="/dashboard/help">
<NavLink onClick={onClose} to="/dashboard/help">
<span>
<HelpIcon />
</span>
<span>Help</span>
</NavLink>
<NavLink to="/dashboard/disclaimer">
<NavLink onClick={onClose} to="/dashboard/disclaimer">
<span>
<DisclaimerIcon />
</span>

View File

@ -6,33 +6,25 @@
transition: left 0.25s;
position: sticky;
z-index: 10;
view-transition-name: main-menu;
height: 100%;
top: 0;
transition:
width 0.5s,
font-size 0.5s,
left 0.05s;
min-width: 0;
left 0.5s;
width: 272px;
min-width: 80px;
@media (max-width: 1199px) {
@media (max-width: 800px) {
& {
width: 80px;
.items {
a {
width: 26px;
gap: 0;
display: flex;
justify-content: center;
left: -300px;
position: fixed;
z-index: 12;
}
span + span {
font-size: 0;
display: none;
}
}
}
&[aria-expanded] {
left: 0px;
font-size: 12px;
}
}
@ -63,9 +55,11 @@
display: flex;
justify-content: center;
span + span {
font-size: 0;
display: none;
@media (min-width: 801px) {
span + span {
font-size: 0;
display: none;
}
}
}
}
@ -154,17 +148,17 @@
top: 115px;
}
&:has(.active:nth-child(4))::before {
/* &:has(.active:nth-child(4))::before {
top: 158px;
}
} */
/*
&:has(.active:nth-child(5))::before {
top: 201px;
}
&:has(.active:nth-child(6))::before {
top: 244px;
}
} */
&:has(.active:nth-child(8))::before {
top: 339px;
@ -227,7 +221,7 @@
margin-left: 6px;
&:hover:not([aria-disabled="true"]),
&.active {
&.active:not([aria-disabled="true"]) {
background-color: var(--codex-highlight-color);
color: #c7c7c7;
}
@ -244,10 +238,15 @@
span + span {
display: inline-block;
overflow: hidden;
min-width: 0;
}
&.active span:first-child {
@media (min-width: 801px) {
span + span {
min-width: 0;
}
}
&.active:not([aria-disabled="true"]) span:first-child {
color: var(--codex-color-primary);
}
}

View File

@ -12,14 +12,14 @@ export function PeersCard() {
const nodes = data?.table.nodes ?? [];
const actives = PeerUtils.countActives(nodes);
const degrees = PeerUtils.calculareDegrees(nodes);
const percent = PeerUtils.calcularePercent(nodes);
const good = PeerUtils.isGoodQuality(actives);
return (
<div className="peers-card">
<main className="row gap">
<PeersMap nodes={data?.table.nodes || []}></PeersMap>
<PeersChart actives={actives} degrees={degrees}></PeersChart>
<PeersChart actives={actives} percent={percent}></PeersChart>
</main>
<footer>
<PeersQuality good={good}></PeersQuality>

View File

@ -1,23 +1,110 @@
import "./PeersChart.css";
import * as echarts from "echarts/core";
import { TooltipComponent } from "echarts/components";
import { SVGRenderer } from "echarts/renderers";
import { GaugeChart } from "echarts/charts";
import { useEffect, useRef, useState } from "react";
type Props = {
actives: number;
degrees: number;
percent: number;
};
type CustomCSSProperties = React.CSSProperties & {
"--codex-peers-degrees": number;
};
// type CustomCSSProperties = React.CSSProperties & {
// "--codex-peers-degrees": number;
// };
export function PeersChart({ actives, degrees }: Props) {
const style: CustomCSSProperties = {
"--codex-peers-degrees": degrees,
};
echarts.use([GaugeChart, TooltipComponent, SVGRenderer]);
export function PeersChart({ actives, percent }: Props) {
const div = useRef<HTMLDivElement>(null);
const chart = useRef<echarts.EChartsType | null>(null);
const [, setRefresher] = useState(Date.now());
useEffect(() => {
if (div.current && !chart.current) {
chart.current = echarts.init(div.current, null, {
renderer: "svg",
});
setRefresher(Date.now());
}
}, [chart, div]);
// const style: CustomCSSProperties = {
// "--codex-peers-degrees": percent,
// };
if (chart.current) {
const options = {
series: [
{
type: "gauge",
axisLine: {
lineStyle: {
width: 30,
color: [
[0.2, "var(--codex-color-error-hexa)"],
[0.5, "rgb(var(--codex-color-warning))"],
[1, "var(--codex-color-primary)"],
],
},
},
pointer: {
itemStyle: {
color: "auto",
},
},
axisTick: {
distance: -30,
length: 8,
lineStyle: {
color: "#fff",
width: 2,
},
},
splitLine: {
distance: -30,
length: 30,
lineStyle: {
color: "#fff",
width: 2,
},
},
axisLabel: {
color: "inherit",
distance: 40,
fontSize: 0,
},
detail: {
valueAnimation: true,
formatter: actives + "",
color: "inherit",
},
data: [
{
value: percent * 100,
},
],
},
],
};
chart.current.setOption(options);
}
return (
<div style={style} className="peers-chart">
<div></div>
<span>{actives}</span>
</div>
<>
{/* <div style={style} className="peers-chart">
<div></div>
<span>{actives}</span>
</div> */}
<div
id="chart"
ref={div}
className="gauge"
style={{
width: 250,
height: 250,
}}></div>
</>
);
}

View File

@ -10,7 +10,7 @@ export function PeersQuality({ good }: Props) {
if (good) {
return (
<div className="peers-quality">
<SuccessCircleIcon></SuccessCircleIcon>
<SuccessCircleIcon width={20}></SuccessCircleIcon>
<span>Peer connections in good standing. </span>
</div>
);

View File

@ -55,6 +55,14 @@ export const PeerUtils = {
return (actives / total) * 180
},
calcularePercent: (peers: PeerNode[]) => {
const actives = PeerUtils.countActives(peers);
const total = peers.length || 1;
return (actives / total) * 100
},
isGoodQuality(actives: number) {
return actives > 0
},

View File

@ -8,7 +8,7 @@
display: flex;
flex-direction: column;
justify-content: space-around;
width: 500px;
/* width: 500px; */
flex: 1 1 auto;
cursor: pointer;
text-decoration: none;

View File

@ -1,9 +1,4 @@
.storage-request {
.modal dialog {
width: 80%;
max-width: 100% !important;
}
header {
display: flex;
align-items: flex-start;

View File

@ -29,11 +29,3 @@
width: 100%;
}
}
.storageRequestFileChooser-hr {
margin: 1.5rem 0;
}
.storageRequestFileChooser-input {
width: 100%;
}

View File

@ -13,6 +13,13 @@
gap: 16px;
margin-bottom: 16px;
@media screen and (max-width: 801px) {
& {
flex-direction: column;
align-items: flex-start;
}
}
> div {
height: 74px;
position: relative;
@ -58,6 +65,12 @@
cursor: pointer;
transition: 0.35s box-shadow;
@media screen and (max-width: 801px) {
& {
width: 100%;
}
}
&:hover {
box-shadow: 0 0 0 2px var(--codex-preset-border-color);
}
@ -128,6 +141,12 @@
gap: 16px;
margin-bottom: 16px;
@media screen and (max-width: 801px) {
& {
flex-direction: column;
}
}
> * {
flex: 1;
}

View File

@ -17,7 +17,7 @@ export function StorageRequestSuccess({
return (
<Placeholder
Icon={<SuccessCircleIcon />}
Icon={<SuccessCircleIcon width={40} height={40} />}
className="storage-success"
title="Your request is being processed."
message="Processing your request may take some time. Once completed, it will

View File

@ -2,7 +2,7 @@
display: flex;
flex-direction: column;
flex: 1 1 50%;
min-width: 420px;
/* min-width: 420px; */
.card {
position: relative;
@ -69,4 +69,10 @@
footer {
margin-top: 32px;
}
@media (max-width: 600px) {
img {
display: none;
}
}
}

19
src/hooks/useMobile.ts Normal file
View File

@ -0,0 +1,19 @@
import { useEffect, useState } from "react";
export const useIsMobile = () => {
const [isMobile, setIsMobile] = useState(window.innerWidth <= 800);
const checkIsMobile = () => {
setIsMobile(window.innerWidth <= 800);
};
useEffect(() => {
window.addEventListener('resize', checkIsMobile);
return () => {
window.removeEventListener('resize', checkIsMobile);
};
}, []);
return isMobile;
};

View File

@ -4,15 +4,24 @@
flex-wrap: wrap;
gap: 16px;
dialog {
width: 80%;
}
> .card {
flex: 1 1 50%;
}
.table {
@media (max-width: 800px) {
th:nth-child(3),
td:nth-child(3),
th:nth-child(4),
td:nth-child(4),
th:nth-child(5),
td:nth-child(5),
th:nth-child(6),
td:nth-child(6) {
display: none;
}
}
table thead tr th {
background-color: #14141499;
}
@ -61,7 +70,6 @@
aside {
display: flex;
width: 400px;
flex: 1 1 30%;
.card {

View File

@ -43,11 +43,17 @@
h4 {
font-family: Inter;
font-size: 32px;
font-size: 16px;
font-weight: 400;
line-height: 38.73px;
letter-spacing: 0.01em;
color: white;
@media (min-width: 801px) {
& {
font-size: 32px;
line-height: 38.73px;
}
}
}
.emoji {
@ -89,4 +95,11 @@
flex-basis: 66%;
}
}
.gauge {
position: absolute;
left: 0;
right: 0;
margin: auto;
}
}

View File

@ -5,6 +5,13 @@
border-radius: 16px;
margin-bottom: 16px;
@media (max-width: 800px) {
& {
flex-direction: column;
gap: 16px;
}
}
> div:first-child {
padding: 16px;
}

View File

@ -9,13 +9,12 @@
}
> div:first-child {
width: calc(100% - 16px);
width: 100%;
border: 1px solid #96969633;
padding: 16px;
border-radius: 16px;
position: relative;
@media (min-width: 1000px) {
@media (min-width: 801px) {
& {
width: calc(100% - 128px - 16px);
padding: 16px 16px 16px 128px;
@ -26,7 +25,7 @@
ul {
display: none;
@media (min-width: 1000px) {
@media (min-width: 801px) {
& {
list-style-type: none;
width: 71px;
@ -94,24 +93,18 @@
border-radius: 16px;
max-width: 280px;
padding: 16px;
transform: scale(0.7);
/* transform: scale(0.7); */
width: 280px;
@media (max-width: 999px) {
@media (max-width: 800px) {
& {
position: relative;
bottom: -32px;
left: -32px;
width: calc(100% - 32px);
max-width: inherit;
}
}
@media (min-width: 1000px) {
& {
transform: scale(1);
}
}
@media (min-width: 1000px) {
@media (min-width: 801px) {
& {
position: absolute;
bottom: 16px;
@ -170,15 +163,16 @@
}
}
}
}
.peers-chart {
transform: scale(0.5);
@media (min-width: 1000px) {
& {
transform: scale(0.73);
@media (max-width: 800px) {
th:nth-child(2),
td:nth-child(2) {
display: none;
}
}
}
.gauge {
margin: auto;
}
}

View File

@ -63,7 +63,7 @@ export const PeersRoute = () => {
<Cell>
{node.seen ? (
<div className="status--active">
<SuccessCircleIcon /> Active
<SuccessCircleIcon width={20} /> Active
</div>
) : (
<div className="status--inactive">
@ -76,7 +76,7 @@ export const PeersRoute = () => {
});
const actives = PeerUtils.countActives(sorted);
const degrees = PeerUtils.calculareDegrees(sorted);
const percent = PeerUtils.calcularePercent(sorted);
const good = PeerUtils.isGoodQuality(actives);
return (
@ -96,7 +96,7 @@ export const PeersRoute = () => {
<span>Connections</span>
</header>
<main>
<PeersChart actives={actives} degrees={degrees}></PeersChart>
<PeersChart actives={actives} percent={percent}></PeersChart>
</main>
<footer>
<PeersQuality good={good}></PeersQuality>

View File

@ -10,5 +10,18 @@
table thead tr th {
background-color: #14141499;
}
@media (max-width: 800px) {
th:nth-child(2),
td:nth-child(2),
th:nth-child(3),
td:nth-child(3),
th:nth-child(5),
td:nth-child(5),
th:nth-child(6),
td:nth-child(6) {
display: none;
}
}
}
}

View File

@ -1,6 +1,7 @@
.wallet-page {
display: flex;
gap: 16px;
flex-wrap: wrap;
.card {
filter: grayscale(30);
@ -12,7 +13,6 @@
}
.buttons {
display: flex;
justify-content: space-between;
margin-top: 16px;
margin-bottom: 16px;
@ -20,6 +20,13 @@
border-bottom: 1px solid #96969633;
padding-top: 16px;
padding-bottom: 16px;
display: none;
@media (min-width: 801px) {
& {
display: flex;
}
}
div {
display: flex;

View File

@ -4,31 +4,35 @@ import { useState } from "react";
import { AppBar } from "../components/AppBar/AppBar";
import { Backdrop } from "@codex-storage/marketplace-ui-components";
import { Outlet, ScrollRestoration } from "react-router-dom";
import { useIsMobile } from "../hooks/useMobile";
export const Root = () => {
const [hasMobileMenu, setHasMobileMenu] = useState(false);
const isMobile = useIsMobile();
const [isExpanded, setIsExpanded] = useState(!isMobile);
const onExpanded = (val: boolean) => setIsExpanded(val);
const onIconClick = () => {
if (window.innerWidth <= 999) {
setHasMobileMenu(true);
if (isMobile) {
setIsExpanded(true);
}
};
const onClose = () => setHasMobileMenu(false);
const onClose = () => setIsExpanded(false);
return (
<div className="layout">
<Menu></Menu>
<Menu isExpanded={isExpanded} onExpanded={onExpanded}></Menu>
<main>
<AppBar onIconClick={onIconClick} />
<AppBar onIconClick={onIconClick} onExpanded={onExpanded} />
<div>
<ScrollRestoration></ScrollRestoration>
<Outlet />
</div>
</main>
<Backdrop onClose={onClose} open={hasMobileMenu}></Backdrop>
<Backdrop onClose={onClose} open={isExpanded && isMobile}></Backdrop>
</div>
);
};