mobile styling

This commit is contained in:
Tobias Merkle 2024-05-07 12:11:58 -04:00
parent 78aab0004a
commit 9cb72b0aee
24 changed files with 280 additions and 176 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,8 +9,8 @@
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
<script type="module" crossorigin src="/assets/index-ChfKWsQl.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DGu1_BHy.css">
<script type="module" crossorigin src="/assets/index-BMzDi7wQ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-1PguYU6W.css">
</head>
<body>

View File

@ -9,8 +9,8 @@
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
<script type="module" crossorigin src="/assets/index-ChfKWsQl.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DGu1_BHy.css">
<script type="module" crossorigin src="/assets/index-BMzDi7wQ.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-1PguYU6W.css">
</head>
<body>

View File

@ -1,10 +1,29 @@
import useHomepageStore from "../store/homepageStore"
import classNames from "classnames"
import useHomepageStore, { HomepageApp } from "../store/homepageStore"
import { isMobileCheck } from "../utilities/dimensions"
import AppDisplay from "./AppDisplay"
import usePersistentStore from "../store/persistentStore"
import { useEffect, useState } from "react"
const AllApps: React.FC = () => {
const { apps } = useHomepageStore()
return <div className='flex-center flex-wrap gap-8 overflow-y-auto flex-grow self-stretch px-16'>
{apps.length === 0 ? <div>Loading apps...</div> : apps.map(app => <AppDisplay app={app} />)}
const { favoriteApps } = usePersistentStore()
const isMobile = isMobileCheck()
const [undockedApps, setUndockedApps] = useState<HomepageApp[]>([])
useEffect(() => {
setUndockedApps(apps.filter(a => !favoriteApps[a.package_name]))
}, [apps, favoriteApps])
return <div className={classNames('flex-center flex-wrap overflow-y-auto flex-grow self-stretch', {
'px-8 gap-4': isMobile,
'px-16 gap-8': !isMobile
})}>
{undockedApps.length === 0
? (apps && apps.length === 0)
? <div>Loading apps...</div>
: <></>
: undockedApps.map(app => <AppDisplay app={app} />)}
</div>
}

View File

@ -1,9 +1,10 @@
import classNames from "classnames"
import ColorDot from "./HexNum"
import ColorDot from "./ColorDot"
import { HomepageApp } from "../store/homepageStore"
import { FaHeart, FaRegHeart } from "react-icons/fa6"
import { useState } from "react"
import usePersistentStore from "../store/persistentStore"
import { isMobileCheck } from "../utilities/dimensions"
interface AppDisplayProps {
app: HomepageApp
@ -12,8 +13,13 @@ interface AppDisplayProps {
const AppDisplay: React.FC<AppDisplayProps> = ({ app }) => {
const { favoriteApp } = usePersistentStore();
const [isHovered, setIsHovered] = useState(false)
const isMobile = isMobileCheck()
return <a
className={classNames("flex-col-center gap-2 relative hover:opacity-90", { 'cursor-pointer': app.path, 'pointer-events-none': !app.path })}
className={classNames("flex-col-center gap-2 relative hover:opacity-90", {
'cursor-pointer': app.path,
'pointer-events-none': !app.path,
})}
id={app.package_name}
href={app.path}
onMouseEnter={() => setIsHovered(true)}
@ -22,7 +28,10 @@ const AppDisplay: React.FC<AppDisplayProps> = ({ app }) => {
{app.base64_icon
? <img
src={app.base64_icon}
className='h-32 w-32 rounded'
className={classNames('rounded', {
'h-12 w-12': isMobile,
'h-32 w-32': !isMobile
})}
/>
: <ColorDot num={app.state?.our_version || '0'} />}
<h6>{app.label}</h6>

View File

@ -2,6 +2,8 @@ import useHomepageStore, { HomepageApp } from "../store/homepageStore"
import AppDisplay from "./AppDisplay"
import usePersistentStore from "../store/persistentStore"
import { useEffect, useState } from "react"
import { isMobileCheck } from "../utilities/dimensions"
import classNames from "classnames"
const AppsDock: React.FC = () => {
const { apps } = useHomepageStore()
@ -12,8 +14,15 @@ const AppsDock: React.FC = () => {
setDockedApps(apps.filter(a => favoriteApps[a.package_name]))
}, [apps, favoriteApps])
return <div className='flex-center flex-wrap obox border border-orange gap-8 !rounded-xl'>
{dockedApps.length === 0 ? <div>Favorite an app to pin it to your dock.</div> : dockedApps.map(app => <AppDisplay app={app} />)}
const isMobile = isMobileCheck()
return <div className={classNames('flex-center flex-wrap border border-orange bg-orange/25 p-2 rounded !rounded-xl', {
'gap-8 mb-4': !isMobile,
'gap-4 mb-2': isMobile
})}>
{dockedApps.length === 0
? <div>Favorite an app to pin it to your dock.</div>
: dockedApps.map(app => <AppDisplay app={app} />)}
</div>
}

View File

@ -1,6 +1,7 @@
import classNames from 'classnames'
import React from 'react'
import { hexToRgb, hslToRgb, rgbToHex, rgbToHsl } from '../utils/colors'
import { isMobileCheck } from '../utilities/dimensions'
interface ColorDotProps extends React.HTMLAttributes<HTMLSpanElement> {
num: string,
@ -10,6 +11,8 @@ const ColorDot: React.FC<ColorDotProps> = ({
num,
...props
}) => {
const isMobile = isMobileCheck()
num = (num || '').replace(/(0x|\.)/g, '')
while (num.length < 6) {
@ -27,7 +30,10 @@ const ColorDot: React.FC<ColorDotProps> = ({
return (
<div {...props} className={classNames('flex', props.className)}>
<div
className='m-0 align-self-center h-32 w-32 border border-8 rounded-full outline-black'
className={classNames('m-0 align-self-center border rounded-full outline-black', {
'h-32 w-32 border-8': !isMobile,
'h-12 w-12 border-3': isMobile
})}
style={{
borderTopColor: leftColor,
borderRightColor: rightColor,

View File

@ -11,7 +11,7 @@ export const Modal: React.FC<Props> = ({ title, onClose, children }) => {
const isMobile = isMobileCheck()
return (
<div className="flex fixed top-0 left-0 w-full h-full bg-black bg-opacity-50 place-items-center place-content-center backdrop-blur-md">
<div className={classNames("flex flex-col rounded-lg bg-black py-4 shadow-lg", {
<div className={classNames("flex flex-col rounded-lg bg-black py-4 shadow-lg max-h-screen overflow-y-auto", {
'min-w-[500px] px-8 w-1/2': !isMobile,
'px-4 w-full': isMobile
})}>

View File

@ -3,6 +3,7 @@ import { FaEye, FaEyeSlash } from "react-icons/fa6"
import { useState } from "react"
import usePersistentStore from "../store/persistentStore"
import useHomepageStore from "../store/homepageStore"
import { isMobileCheck } from "../utilities/dimensions"
interface WidgetProps {
package_name: string,
@ -14,24 +15,28 @@ const Widget: React.FC<WidgetProps> = ({ package_name, widget, forceLarge }) =>
const { apps } = useHomepageStore()
const { widgetSettings, toggleWidgetVisibility } = usePersistentStore()
const [isHovered, setIsHovered] = useState(false)
const isMobile = isMobileCheck()
const isLarge = forceLarge || widgetSettings[package_name]?.size === "large"
const isSmall = !widgetSettings[package_name]?.size || widgetSettings[package_name]?.size === "small"
return <div
className={classNames("self-stretch flex-col-center shadow-lg rounded-lg relative", {
"max-w-1/2 min-w-1/2": forceLarge || widgetSettings[package_name]?.size === "large",
"max-w-1/4 min-w-1/4": !widgetSettings[package_name]?.size || widgetSettings[package_name]?.size === "small"
"max-w-1/2 min-w-1/2": isLarge && !isMobile,
"max-w-1/4 min-w-1/4": isSmall && !isMobile,
'w-full': isMobile
})}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<h3 className="flex-center my-2">
<h6 className="flex-center my-2">
{apps.find(app => app.package_name === package_name)?.label || package_name}
</h3>
</h6>
<iframe
srcDoc={widget || ""}
className="grow self-stretch"
data-widget-code={widget}
/>
{isHovered && <button
className="absolute -top-2 -right-2 icon"
className="absolute top-0 left-2 icon"
onClick={() => toggleWidgetVisibility(package_name)}
>
{widgetSettings[package_name]?.hide ? <FaEye /> : <FaEyeSlash />}

View File

@ -3,18 +3,28 @@ import useHomepageStore from "../store/homepageStore"
import Widget from "./Widget"
import WidgetsSettingsModal from "./WidgetsSettingsModal"
import usePersistentStore from "../store/persistentStore"
import { isMobileCheck } from "../utilities/dimensions"
import classNames from "classnames"
const Widgets = () => {
const { apps, showWidgetsSettings, setShowWidgetsSettings } = useHomepageStore()
const { widgetSettings } = usePersistentStore();
return <div className="flex-col-center m-4 gap-4 flex-grow self-stretch relative">
const isMobile = isMobileCheck()
return <div className={classNames("flex-col-center flex-grow self-stretch relative", {
'm-4': !isMobile,
'm-2': isMobile
})}>
<button
className="icon ml-4 absolute top-0 right-4"
onClick={() => setShowWidgetsSettings(true)}
>
<FaGear />
</button>
<div className="flex-center flex-wrap gap-4 flex-grow self-stretch">
<div className={classNames("flex-center flex-wrap flex-grow self-stretch", {
'gap-2': isMobile,
'gap-4': !isMobile
})}>
{apps
.filter(app => app.widget)
.map(({ widget, package_name }, _i, _appsWithWidgets) => !widgetSettings[package_name]?.hide && <Widget

View File

@ -12,39 +12,37 @@ const WidgetsSettingsModal = () => {
title='Widget Settings'
onClose={() => setShowWidgetsSettings(false)}
>
<div className="flex-col-center">
{apps.filter(app => app.widget).map(({ label, package_name }) => <div className="flex items-start">
<div className="flex-col-center gap-4 mt-4">
{apps.filter(app => app.widget).map(({ label, package_name }) => <div className="flex items-start bg-white/10 rounded p-2 self-stretch">
<h4 className="mr-4">{label}</h4>
<div className="flex-col-center gap-6">
<div className="flex-center gap-4">
<div className="flex-col-center gap-4 grow">
<div className="flex-center gap-2">
<span>Show widget</span>
<button
className="icon"
onClick={() => toggleWidgetVisibility(package_name)}
>
{!widgetSettings[package_name]?.hide ? <FaCheck /> : <FaX />}
</button>
<label>Show widget</label>
</div>
<div className="flex-col-center gap-4">
<div className="flex-center gap-2">
<span>Widget size</span>
<div className="flex-center items-stretch gap-4">
<button
className={classNames({
'clear': widgetSettings[package_name]?.size === 'large'
})}
onClick={() => setWidgetSize(package_name, 'small')}
>
Small
</button>
<button
className={classNames({
'clear': widgetSettings[package_name]?.size !== 'large'
})}
onClick={() => setWidgetSize(package_name, 'large')}
>
Large
</button>
</div>
<button
className={classNames({
'clear': widgetSettings[package_name]?.size === 'large'
})}
onClick={() => setWidgetSize(package_name, 'small')}
>
Small
</button>
<button
className={classNames({
'clear': widgetSettings[package_name]?.size !== 'large'
})}
onClick={() => setWidgetSize(package_name, 'large')}
>
Large
</button>
</div>
</div>
</div>)}

View File

@ -6,6 +6,8 @@ import { FaGear, FaV } from 'react-icons/fa6'
import AppsDock from '../components/AppsDock'
import AllApps from '../components/AllApps'
import Widgets from '../components/Widgets'
import { isMobileCheck } from '../utilities/dimensions'
import classNames from 'classnames'
interface AppStoreApp {
package: string,
@ -17,6 +19,7 @@ interface AppStoreApp {
function Homepage() {
const [our, setOur] = useState('')
const { apps, setApps, isHosted, fetchHostedStatus } = useHomepageStore()
const isMobile = isMobileCheck()
useEffect(() => {
fetch('/apps')
@ -57,8 +60,14 @@ function Homepage() {
}, [our])
return (
<div className="flex-col-center relative h-screen w-screen overflow-hidden special-bg-homepage">
<h5 className='absolute top-8 left-8 right-8 flex gap-4 c'>
<div className={classNames("flex-col-center relative w-screen overflow-hidden special-bg-homepage", {
'h-screen': !isMobile,
'min-h-screen': isMobile
})}>
<h5 className={classNames('absolute flex gap-4 c', {
'top-8 left-8 right-8': !isMobile,
'top-2 left-2 right-2': isMobile
})}>
{isHosted && <a
href={`https://${our.replace('.os', '')}.hosting.kinode.net/`}
className='button icon'
@ -73,11 +82,16 @@ function Homepage() {
<FaGear />
</a>
</h5>
<div className="flex-col-center gap-6 mt-8 mx-0 mb-16">
<h3 className='text-center'>Welcome to</h3>
<KinodeText />
<KinodeBird />
</div>
{isMobile
? <div className='flex-center gap-4 p-8 mt-8 max-w-screen'>
<KinodeBird />
<KinodeText />
</div>
: <div className={classNames("flex-col-center mx-0 gap-6 mt-8 mb-16")}>
<h3 className='text-center'>Welcome to</h3>
<KinodeText />
<KinodeBird />
</div>}
<AppsDock />
<AllApps />
<Widgets />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -11,8 +11,8 @@
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1.00001, viewport-fit=cover" />
<script type="module" crossorigin src="/assets/index-zSMkg4LY.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-UCAeMvu5.css">
<script type="module" crossorigin src="/assets/index-DzEOiZ-5.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B00cPdAQ.css">
</head>
<body>

View File

@ -1,14 +1,24 @@
import classNames from "classnames";
import kinodeLogo from "../assets/kinode.svg";
import { isMobileCheck } from "../utils/dimensions";
export const KinodeTitle: React.FC<{ prefix: string, showLogo?: boolean }> = ({ prefix, showLogo }) => {
const isMobile = isMobileCheck()
return (
<div className="mb-4 flex flex-col c">
<h1>{prefix}</h1>
{showLogo && <>
<h1 className="display text-5xl mt-10 mb-8 ml-4">Kinode<span className="text-xs">&reg;</span></h1>
<img src={kinodeLogo} className="w-32 h-32" />
<h1
className={classNames("display", {
'text-5xl mt-10 mb-8 ml-4': !isMobile,
'text-3xl mt-5 mb-4 ml-2': isMobile
})}>Kinode<span className="text-xs">&reg;</span></h1>
<img src={kinodeLogo} className={classNames({
'w-32 h-32': !isMobile,
'w-16 h-16': isMobile,
})} />
</>}
</div>
);
};

View File

@ -9,6 +9,8 @@ import { OPTIMISM_OPT_HEX, SEPOLIA_OPT_HEX } from "../constants/chainId";
import sepoliaLogo from "../assets/sepolia.png";
import optimismLogo from "../assets/optimism.png";
import { Tooltip } from "./Tooltip";
import { isMobileCheck } from "../utils/dimensions";
import classNames from "classnames";
const { useIsActivating, useChainId } = hooks;
@ -70,6 +72,8 @@ function KinodeHeader({
alert("You can change your connected account in your wallet.");
}, []);
const isMobile = isMobileCheck()
return (
<>
<div id="signup-form-header" className="flex flex-col">
@ -77,7 +81,10 @@ function KinodeHeader({
nodeChainId === OPTIMISM_OPT_HEX) && (
<Tooltip
position="left"
className="!absolute top-8 right-8 z-10"
className={classNames("!absolute z-10", {
'top-8 right-8': !isMobile,
'top-2 right-2': isMobile
})}
button={nodeChainId === SEPOLIA_OPT_HEX ? (
<img
alt="sepolia"
@ -114,7 +121,9 @@ function KinodeHeader({
/>
) : (
<div className="flex flex-col gap-8 my-4">
<h5 className="flex c">
<h5 className={classNames("flex c", {
'flex-wrap text-center max-w-3/4 gap-2': isMobile
})}>
{!isActivating && 'You must connect to a browser wallet to continue.'}
{isActivating ? (

View File

@ -152,5 +152,13 @@ button:disabled {
}
.network-icon {
@apply w-12 h-12 filter grayscale;
@apply filter grayscale;
&.isMobile {
@apply w-8 h-8;
}
&:not(.isMobile) {
@apply w-12 h-12;
}
}

View File

@ -11,6 +11,8 @@ import DirectCheckbox from "../components/DirectCheckbox";
import { useNavigate } from "react-router-dom";
import { Tooltip } from "../components/Tooltip";
import { KinodeTitle } from "../components/KinodeTitle";
import { isMobileCheck } from "../utils/dimensions";
import classNames from "classnames";
const { useProvider } = hooks;
@ -184,6 +186,8 @@ function Login({
const isDirect = Boolean(routers?.length === 0);
const isMobile = isMobileCheck()
return (
<>
<KinodeHeader
@ -198,7 +202,9 @@ function Login({
) : (
<form
id="signup-form"
className="flex flex-col w-full max-w-[450px]"
className={classNames("flex flex-col w-full max-w-[450px]", {
'p-2': isMobile
})}
onSubmit={handleLogin}
>
<div className="self-stretch mb-2 flex flex-col">

View File

@ -0,0 +1 @@
export const isMobileCheck = () => window.innerWidth <= 600