feat: mobile responsiveness

This commit is contained in:
Jade (Rose) Rowland 2024-10-14 12:45:55 -04:00
parent 96802a271c
commit b0d2115f43
9 changed files with 78 additions and 59 deletions

View File

@ -2,7 +2,7 @@ import cx from '@src/cx.mjs';
export function ConsoleTab({ log }) {
return (
<div id="console-tab" className="break-all px-4 dark:text-white text-stone-900 text-sm py-4">
<div id="console-tab" className="break-all px-4 dark:text-white text-stone-900 text-sm py-2">
<pre aria-hidden="true">{`███████╗████████╗██████╗ ██╗ ██╗██████╗ ███████╗██╗

View File

@ -3,7 +3,7 @@ import useEvent from '@src/useEvent.mjs';
import cx from '@src/cx.mjs';
import { nanoid } from 'nanoid';
import { useCallback, useState } from 'react';
import { setActiveFooter as setTab, useSettings } from '../../../settings.mjs';
import { setPanelPinned, setActiveFooter as setTab, useSettings } from '../../../settings.mjs';
import { ConsoleTab } from './ConsoleTab';
import { FilesTab } from './FilesTab';
import { Reference } from './Reference';
@ -16,50 +16,56 @@ import { ChevronLeftIcon } from '@heroicons/react/16/solid';
const TAURI = typeof window !== 'undefined' && window.__TAURI__;
export function HorizontalPanel({ context }) {
const [pinned, setPinned] = useState(true);
const settings = useSettings();
const tab = settings.activeFooter;
const { isPanelPinned: pinned, activeFooter: tab } = settings;
const [isHovered, setIsHovered] = useState(false);
const showContent = isHovered || pinned;
return (
<PanelNav
onMouseEnter={(x) => setIsHovered(true)}
onMouseLeave={(x) => setIsHovered(false)}
className={cx(
'hover:max-h-[360px] hover:min-h-[360px] flex flex-col overflow-hidden ',
pinned ? `min-h-[360px] max-h-[360px]` : 'min-h-8 max-h-8',
'hover:max-h-[360px] hover:min-h-[360px] justify-between flex flex-col',
pinned ? `min-h-[360px] max-h-[360px]` : 'min-h-10 max-h-10',
)}
>
<div className={cx('group-hover:flex flex-col h-full overflow-auto', pinned ? 'flex' : 'hidden')}>
{showContent && (
<div className="flex h-full overflow-auto">
<PanelContent context={context} tab={tab} />
</div>
<div className="flex justify-between min-h-8 max-h-8">
)}
<div className="flex justify-between min-h-10 max-h-10 pr-2 items-center">
<Tabs setTab={setTab} tab={tab} pinned={pinned} />
<PinButton pinned={pinned} setPinned={setPinned} />
<PinButton pinned={pinned} setPinned={setPanelPinned} />
</div>
</PanelNav>
);
}
export function VerticalPanel({ context }) {
const [pinned, setPinned] = useState(true);
const settings = useSettings();
const tab = settings.activeFooter;
const { isPanelPinned: pinned, activeFooter: tab } = settings;
const [isHovered, setIsHovered] = useState(false);
const showContent = isHovered || pinned;
return (
<PanelNav
className={cx(
'hover:min-w-[600px] hover:max-w-[600px]',
pinned ? `min-w-[600px] max-w-[600px]` : ' min-w-8 max-w-8',
)}
onMouseEnter={(x) => setIsHovered(true)}
onMouseLeave={(x) => setIsHovered(false)}
className={cx('hover:w-[600px]', pinned ? `w-[600px]` : 'w-8')}
>
<div className={cx('group-hover:flex flex-col h-full', pinned ? 'flex' : 'hidden')}>
<div className="flex justify-between">
<div className="flex justify-between w-full ">
<Tabs setTab={setTab} tab={tab} pinned={pinned} />
<PinButton pinned={pinned} setPinned={setPinned} />
<PinButton pinned={pinned} setPinned={setPanelPinned} />
</div>
{showContent && (
<div className="overflow-auto">
<PanelContent context={context} tab={tab} />
</div>
)}
</div>
<div className={cx(pinned ? 'hidden' : 'flex flex-col items-center justify-center h-full group-hover:hidden ')}>
<ChevronLeftIcon className="text-foreground opacity-50 w-6 h-6" />
</div>
@ -71,17 +77,21 @@ const tabNames = {
welcome: 'intro',
patterns: 'patterns',
sounds: 'sounds',
console: 'console',
reference: 'reference',
console: 'console',
settings: 'settings',
};
if (TAURI) {
tabNames.files = 'files';
}
function PanelNav({ children, className }) {
function PanelNav({ children, className, ...props }) {
return (
<nav aria-label="Settings Menu" className={cx('bg-lineHighlight group transition-all', className)}>
<nav
aria-label="Settings Menu"
className={cx('bg-lineHighlight group transition-all overflow-x-auto', className)}
{...props}
>
{children}
</nav>
);
@ -159,7 +169,7 @@ function PinButton({ pinned, setPinned }) {
<button
onClick={() => setPinned(!pinned)}
className={cx(
'text-foreground w-8 h-8 items-center justify-center p-1.5 group-hover:flex',
'text-foreground max-h-8 min-h-8 max-w-8 min-w-8 items-center justify-center p-1.5 group-hover:flex',
pinned ? 'flex' : 'hidden',
)}
aria-label="Pin Settings Menu"
@ -168,6 +178,7 @@ function PinButton({ pinned, setPinned }) {
stroke="currentColor"
fill={'currentColor'}
strokeWidth="0"
className="w-full h-full"
opacity={pinned ? 1 : '.3'}
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"

View File

@ -77,7 +77,7 @@ function PatternButtons({ patterns, activePattern, onClick, started }) {
function ActionButton({ children, onClick, label, labelIsHidden }) {
return (
<button className="hover:opacity-50" onClick={onClick} title={label}>
<button className="hover:opacity-50 text-nowrap" onClick={onClick} title={label}>
{labelIsHidden !== true && label}
{children}
</button>
@ -155,7 +155,7 @@ export function PatternsTab({ context }) {
</div>
)}
<section className="flex overflow-y-scroll max-h-full flex-col pb-4">
<section className="flex overflow-y-scroll max-h-full flex-col">
{patternFilter === patternFilterName.user && (
<PatternButtons
onClick={(id) =>

View File

@ -25,20 +25,21 @@ export function Reference() {
}, [search]);
return (
<div className="flex h-full w-full pt-2 text-foreground overflow-hidden">
<div className="w-42 flex-none h-full overflow-y-auto overflow-x-hidden pr-4">
<div class="w-full ml-2 mb-2 top-0 sticky">
<div className="flex h-full w-full p-2 text-foreground overflow-hidden">
<div className="h-full flex flex-col gap-2 w-1/3 max-w-72 ">
<div class="w-full flex">
<input
className="w-full p-1 bg-background rounded-md"
className="w-full p-1 bg-background rounded-md border-none"
placeholder="Search"
value={search}
onInput={(event) => setSearch(event.target.value)}
/>
</div>
<div className="flex flex-col h-full overflow-y-auto gap-1.5 bg-background bg-opacity-50 rounded-md">
{visibleFunctions.map((entry, i) => (
<a
key={i}
className="cursor-pointer block hover:bg-lineHighlight py-1 px-4"
className="cursor-pointer flex-none hover:bg-lineHighlight overflow-x-hidden px-1 text-ellipsis"
onClick={() => {
const el = document.getElementById(`doc-${i}`);
const container = document.getElementById('reference-container');
@ -49,8 +50,9 @@ export function Reference() {
</a>
))}
</div>
<div className="break-normal w-full h-full overflow-auto pl-4 flex relative" id="reference-container">
<div className="prose dark:prose-invert max-w-full pr-4">
</div>
<div className="break-normal flex-grow flex-col overflow-y-auto overflow-x-hidden px-2 flex relative" id="reference-container">
<div className='prose dark:prose-invert min-w-full px-1 '>
<h2>API Reference</h2>
<p>
This is the long list functions you can use! Remember that you don't need to remember all of those and that

View File

@ -105,7 +105,7 @@ export function SettingsTab({ started }) {
const shouldAlwaysSync = isUdels();
const canChangeAudioDevice = AudioContext.prototype.setSinkId != null;
return (
<div className="text-foreground p-4 space-y-4">
<div className="text-foreground p-4 space-y-4 w-full">
{canChangeAudioDevice && (
<FormItem label="Audio Output Device">
<AudioDeviceSelector

View File

@ -1,7 +1,7 @@
import useEvent from '@src/useEvent.mjs';
import { useStore } from '@nanostores/react';
import { getAudioContext, soundMap, connectToDestination } from '@strudel/webaudio';
import React, { useMemo, useRef, useState } from 'react';
import { useMemo, useRef, useState } from 'react';
import { settingsMap, useSettings } from '../../../settings.mjs';
import { ButtonGroup } from './Forms.jsx';
import ImportSoundsButton from './ImportSoundsButton.jsx';
@ -53,14 +53,14 @@ export function SoundsTab() {
return (
<div id="sounds-tab" className="px-4 flex flex-col w-full h-full dark:text-white text-stone-900">
<div className="w-full ml-2 mb-2 top-0 sticky">
<input
className="w-full p-1 bg-background rounded-md"
className="w-full p-1 bg-background rounded-md pb-2"
placeholder="Search"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<div className="pb-2 flex shrink-0 flex-wrap">
<ButtonGroup
value={soundsFilter}
@ -74,7 +74,8 @@ export function SoundsTab() {
></ButtonGroup>
<ImportSoundsButton onComplete={() => settingsMap.setKey('soundsFilter', 'user')} />
</div>
<div className="min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal pb-4">
<div className="min-h-0 max-h-full grow overflow-auto font-mono text-sm break-normal pb-2">
{soundEntries.map(([name, { data, onTrigger }]) => {
return (
<span

View File

@ -30,7 +30,8 @@ export const defaultSettings = {
isZen: false,
soundsFilter: 'all',
patternFilter: 'community',
panelPosition: 'right',
panelPosition: 'bottom',
isPanelPinned: true,
userPatterns: '{}',
audioDeviceName: defaultAudioDeviceName,
audioEngineTarget: audioEngineTargets.webaudio,
@ -60,6 +61,7 @@ export function useSettings() {
return {
...state,
isZen: parseBoolean(state.isZen),
isPanelPinned: parseBoolean(state.isPanelPinned),
isBracketMatchingEnabled: parseBoolean(state.isBracketMatchingEnabled),
isBracketClosingEnabled: parseBoolean(state.isBracketClosingEnabled),
isLineNumbersDisplayed: parseBoolean(state.isLineNumbersDisplayed),
@ -77,6 +79,7 @@ export function useSettings() {
}
export const setActiveFooter = (tab) => settingsMap.setKey('activeFooter', tab);
export const setPanelPinned = (isPinned) => settingsMap.setKey('isPanelPinned', isPinned);
export const setIsZen = (active) => settingsMap.setKey('isZen', !!active);

View File

@ -52,6 +52,7 @@
}
:root {
--app-height: 100vh;
--app-width: 100vw;
}
#console-tab {

View File

@ -34,6 +34,7 @@ module.exports = {
},
spacing: {
'app-height': 'var(--app-height)',
'app-width': 'var(--app-width)'
},
typography(theme) {
return {