mirror of
https://github.com/eliasstepanik/strudel.git
synced 2026-01-11 05:38:35 +00:00
feat: mobile responsiveness
This commit is contained in:
parent
96802a271c
commit
b0d2115f43
@ -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">{`███████╗████████╗██████╗ ██╗ ██╗██████╗ ███████╗██╗
|
||||
██╔════╝╚══██╔══╝██╔══██╗██║ ██║██╔══██╗██╔════╝██║
|
||||
███████╗ ██║ ██████╔╝██║ ██║██║ ██║█████╗ ██║
|
||||
|
||||
@ -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')}>
|
||||
<PanelContent context={context} tab={tab} />
|
||||
</div>
|
||||
<div className="flex justify-between min-h-8 max-h-8">
|
||||
{showContent && (
|
||||
<div className="flex h-full overflow-auto">
|
||||
<PanelContent context={context} tab={tab} />
|
||||
</div>
|
||||
)}
|
||||
<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} />
|
||||
</div>
|
||||
|
||||
<div className="overflow-auto">
|
||||
<PanelContent context={context} tab={tab} />
|
||||
<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"
|
||||
|
||||
@ -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) =>
|
||||
|
||||
@ -25,32 +25,34 @@ 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>
|
||||
{visibleFunctions.map((entry, i) => (
|
||||
<a
|
||||
key={i}
|
||||
className="cursor-pointer block hover:bg-lineHighlight py-1 px-4"
|
||||
onClick={() => {
|
||||
const el = document.getElementById(`doc-${i}`);
|
||||
const container = document.getElementById('reference-container');
|
||||
container.scrollTo(0, el.offsetTop);
|
||||
}}
|
||||
>
|
||||
{entry.name} {/* <span className="text-gray-600">{entry.meta.filename}</span> */}
|
||||
</a>
|
||||
))}
|
||||
<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 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');
|
||||
container.scrollTo(0, el.offsetTop);
|
||||
}}
|
||||
>
|
||||
{entry.name} {/* <span className="text-gray-600">{entry.meta.filename}</span> */}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</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 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
|
||||
@ -78,7 +80,7 @@ export function Reference() {
|
||||
))}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
<input
|
||||
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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
}
|
||||
:root {
|
||||
--app-height: 100vh;
|
||||
--app-width: 100vw;
|
||||
}
|
||||
|
||||
#console-tab {
|
||||
|
||||
@ -34,6 +34,7 @@ module.exports = {
|
||||
},
|
||||
spacing: {
|
||||
'app-height': 'var(--app-height)',
|
||||
'app-width': 'var(--app-width)'
|
||||
},
|
||||
typography(theme) {
|
||||
return {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user