Fixed bugs

This commit is contained in:
Prozilla 2024-06-14 21:07:54 +02:00
parent a396cbaf88
commit ea4e274798
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
15 changed files with 248 additions and 173 deletions

View file

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -30,6 +30,8 @@
bottom: 0; bottom: 0;
} }
/* Context menu */
.ContextMenu.Actions { .ContextMenu.Actions {
--border-radius: var(--border-radius-1); --border-radius: var(--border-radius-1);
--padding: 0.375rem; --padding: 0.375rem;
@ -147,20 +149,22 @@
.ContextMenu .TextDisplay { .ContextMenu .TextDisplay {
margin: 0; margin: 0;
padding: 0.25rem 0.5rem; padding: 0.25rem 0.75rem;
color: var(--foreground-color-1); color: var(--foreground-color-1);
font-size: 0.875rem; font-size: 0.875rem;
text-align: start; text-align: start;
white-space: nowrap; white-space: nowrap;
} }
.Header-menu { /* Header menu */
.HeaderMenu {
display: flex; display: flex;
width: inherit; width: inherit;
height: inherit; height: inherit;
} }
.Header-menu .Dropdown { .HeaderMenu .Dropdown {
position: relative; position: relative;
display: block; display: block;
width: auto; width: auto;
@ -173,63 +177,105 @@
cursor: pointer; cursor: pointer;
} }
.Header-menu .Dropdown:hover, .Header-menu .Dropdown:focus-visible { .HeaderMenu .Dropdown:hover,
.HeaderMenu .Dropdown:focus-visible,
.HeaderMenu .Dropdown.Active {
background-color: rgba(255, 255, 255, 5%); background-color: rgba(255, 255, 255, 5%);
} }
.Header-menu .Dropdown > .Label { .HeaderMenu .Dropdown > .Label {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
height: 100%; height: 100%;
} }
.Header-menu .DropdownArrow { .HeaderMenu .DropdownArrow {
display: none; display: none;
} }
.Header-menu .DropdownContent { .HeaderMenu .DropdownContent {
opacity: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
position: absolute; position: absolute;
top: 100%; top: 100%;
left: 0; left: 0;
padding: 0.35rem; padding: 0.25rem;
background-color: var(--background-color-1); background-color: var(--background-color-1);
border-bottom-left-radius: 0.5rem; border-bottom-left-radius: var(--border-radius-1);
border-bottom-right-radius: 0.5rem; border-bottom-right-radius: var(--border-radius-1);
transition: opacity 100ms ease-out;
cursor: default;
} }
.Header-menu .Dropdown:not(.Active) .DropdownContent { .HeaderMenu .Dropdown:not(.Active) .DropdownContent {
opacity: 0; opacity: 0;
pointer-events: none; pointer-events: none;
} }
.Header-menu .Button { .HeaderMenu .Button {
--icon-size: 1.25rem;
--icon-gap: 0.5rem;
display: flex; display: flex;
gap: 0.75rem; gap: 1.5rem;
justify-content: space-between; justify-content: space-between;
align-items: center;
width: 100%; width: 100%;
padding: 0.25rem 0.5rem; padding: 0.25rem 0.5rem;
background: none; background: none;
border: none; border: none;
border-radius: var(--border-radius-1); border-radius: var(--border-radius-1);
outline: none; outline: none;
font-size: 0.85rem; font-size: 0.875rem;
text-align: start; text-align: start;
white-space: nowrap; white-space: nowrap;
cursor: pointer; cursor: pointer;
} }
.Header-menu .Button:hover, .Header-menu .Button:focus-visible { .HeaderMenu .Button:not(:disabled):hover,
.HeaderMenu .Button:not(:disabled):focus-visible {
background-color: color-mix(in srgb, var(--background-color-0) 75%, transparent); background-color: color-mix(in srgb, var(--background-color-0) 75%, transparent);
} }
.Header-menu .Dropdown > .Label > p, .Header-menu .Button > .Label > p { .HeaderMenu .Button:disabled {
cursor: default;
}
.HeaderMenu .Button > .Label {
display: flex;
gap: var(--icon-gap);
flex-direction: row-reverse;
justify-content: flex-start;
align-items: center;
}
.HeaderMenu .Button > .Label .Icon div,
.HeaderMenu .Button > .Label .Icon svg {
height: var(--icon-size);
width: var(--icon-size);
}
.HeaderMenu .Dropdown > .Label > p,
.HeaderMenu .Button > .Label > p {
margin: 0; margin: 0;
} }
.Header-menu .Shortcut { .HeaderMenu .Button:disabled > .Label > p {
color: var(--foreground-color-1);
}
.HeaderMenu .Shortcut {
color: var(--foreground-color-1); color: var(--foreground-color-1);
margin: 0; margin: 0;
font-size: 0.875rem;
}
.HeaderMenu .Divider {
width: calc(100% - 0.5rem);
height: 2px;
border-radius: var(--border-radius-99);
background-color: var(--foreground-color-2);
margin: 0.25rem auto;
} }

View file

@ -11,6 +11,7 @@ export interface ActionProps {
shortcut?: string[]; shortcut?: string[];
onTrigger?: (event?: Event, triggerParams?: unknown, ...args: unknown[]) => void; onTrigger?: (event?: Event, triggerParams?: unknown, ...args: unknown[]) => void;
children?: ReactNode; children?: ReactNode;
disabled?: boolean;
} }
export interface ActionsProps { export interface ActionsProps {
@ -48,15 +49,18 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
actionId++; actionId++;
const { label, shortcut, onTrigger } = child.props as ActionProps; const { label, shortcut, disabled, onTrigger } = child.props as ActionProps;
const onTriggerOverride = (event: Event, ...args: unknown[]) => { const onTriggerOverride = (event: Event, ...args: unknown[]) => {
if (disabled)
return;
onAnyTrigger?.(event, triggerParams, ...args); onAnyTrigger?.(event, triggerParams, ...args);
onTrigger?.(event, triggerParams, ...args); onTrigger?.(event, triggerParams, ...args);
}; };
// Register shortcut // Register shortcut
if (label != null && onTrigger != null) { if (!disabled && label != null && onTrigger != null) {
options[actionId] = onTriggerOverride; options[actionId] = onTriggerOverride;
if (shortcut != null) if (shortcut != null)
@ -71,7 +75,8 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
...child.props, ...child.props,
actionId, actionId,
children: iterateOverChildren((child.props as ActionProps).children), children: iterateOverChildren((child.props as ActionProps).children),
onTrigger: onTriggerOverride onTrigger: onTriggerOverride,
disabled
} as ActionProps); } as ActionProps);
}); });

View file

@ -10,8 +10,12 @@ interface ClickActionProps extends ActionProps {
icon?: string | object; icon?: string | object;
} }
export const ClickAction = memo(({ actionId, label, shortcut, onTrigger, icon }: ClickActionProps) => { export const ClickAction = memo(({ actionId, label, shortcut, disabled, onTrigger, icon }: ClickActionProps) => {
return (<button key={actionId} className={styles.Button} tabIndex={0} onClick={onTrigger as unknown as MouseEventHandler}> const classNames = [styles.Button];
if (disabled)
classNames.push(styles.Disabled);
return (<button key={actionId} className={classNames.join(" ")} tabIndex={0} disabled={disabled} onClick={onTrigger as unknown as MouseEventHandler}>
<span className={styles.Label}> <span className={styles.Label}>
{icon && <div className={styles.Icon}> {icon && <div className={styles.Icon}>
{typeof icon == "string" {typeof icon == "string"

View file

@ -1,10 +1,14 @@
import styles from "./HeaderMenu.module.css"; import styles from "./HeaderMenu.module.css";
import { ActionsProps, Actions } from "../../../actions/Actions"; import { ActionsProps, Actions } from "../../../actions/Actions";
import { STYLES } from "../../../../config/actions.config"; import { STYLES } from "../../../../config/actions.config";
import { useZIndex } from "../../../../hooks/z-index/zIndex";
import { ZIndexManager } from "../../../../features/z-index/zIndexManager";
export function HeaderMenu({ children, ...props }: ActionsProps) { export function HeaderMenu({ children, ...props }: ActionsProps) {
return <div className={styles.HeaderMenu}> const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.MODALS, index: 0 });
return <div className={styles.HeaderMenu} style={{ zIndex }}>
<Actions className={STYLES.HEADER_MENU} {...props}> <Actions className={STYLES.HEADER_MENU} {...props}>
{children} {children}
</Actions> </Actions>

View file

@ -22,6 +22,8 @@ import App from "../../../features/apps/app";
import WindowsManager from "../../../features/windows/windowsManager"; import WindowsManager from "../../../features/windows/windowsManager";
import { MarkdownBlockquote } from "./overrides/MarkdownBlockquote"; import { MarkdownBlockquote } from "./overrides/MarkdownBlockquote";
import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext"; import { useVirtualRoot } from "../../../hooks/virtual-drive/virtualRootContext";
import { APP_NAMES } from "../../../config/apps.config";
import { Divider } from "../../actions/actions/Divider";
const OVERRIDES = { const OVERRIDES = {
a: MarkdownLink, a: MarkdownLink,
@ -53,6 +55,7 @@ export function TextEditor({ file, path, setTitle, setIconUrl, close, mode, app,
const [content, setContent] = useState(file?.content ?? ""); const [content, setContent] = useState(file?.content ?? "");
const [unsavedChanges, setUnsavedChanges] = useState(file == null); const [unsavedChanges, setUnsavedChanges] = useState(file == null);
const [zoom, setZoom] = useState(DEFAULT_ZOOM); const [zoom, setZoom] = useState(DEFAULT_ZOOM);
const [initialised, setInitialised] = useState(false);
const { openWindowedModal } = useWindowedModal(); const { openWindowedModal } = useWindowedModal();
useEffect(() => { useEffect(() => {
@ -103,14 +106,14 @@ export function TextEditor({ file, path, setTitle, setIconUrl, close, mode, app,
}, [currentFile, setTitle, unsavedChanges, currentMode, app.name]); }, [currentFile, setTitle, unsavedChanges, currentMode, app.name]);
useEffect(() => { useEffect(() => {
if (currentFile == null && path != null) { if (!initialised && currentFile == null && path != null) {
const newFile = virtualRoot.navigate(path); const newFile = virtualRoot.navigate(path);
if (newFile == null || !newFile.isFile()) if (newFile == null || !newFile.isFile())
return; return;
setCurrentFile(newFile as VirtualFile); setCurrentFile(newFile as VirtualFile);
path = null; setInitialised(true);
} }
}, [path, currentFile]); }, [path, currentFile]);
@ -176,16 +179,22 @@ export function TextEditor({ file, path, setTitle, setIconUrl, close, mode, app,
/> />
}); });
}} shortcut={["Control", "o"]}/> }} shortcut={["Control", "o"]}/>
<Divider/>
<ClickAction label="Save" onTrigger={() => { saveText(); }} shortcut={["Control", "s"]}/> <ClickAction label="Save" onTrigger={() => { saveText(); }} shortcut={["Control", "s"]}/>
<ClickAction label={`Reveal in ${APP_NAMES.FILE_EXPLORER}`} disabled={currentFile == null} onTrigger={() => {
currentFile.parent.open(windowsManager);
}}/>
<Divider/>
<ClickAction label="Quit" onTrigger={() => { close(); }} shortcut={["Control", "q"]}/> <ClickAction label="Quit" onTrigger={() => { close(); }} shortcut={["Control", "q"]}/>
</DropdownAction> </DropdownAction>
<DropdownAction label="View" showOnHover={false}> <DropdownAction label="View" showOnHover={false}>
<ClickAction label={currentMode === "view" ? "Edit mode" : "Preview mode"} onTrigger={() => { <ClickAction label={currentMode === "view" ? "Edit mode" : "Preview mode"} onTrigger={() => {
setCurrentMode(currentMode === "view" ? "edit" : "view"); setCurrentMode(currentMode === "view" ? "edit" : "view");
}} shortcut={["Control", "v"]}/> }} shortcut={["Control", "v"]}/>
<Divider/>
<ClickAction label="Zoom in" onTrigger={() => { setZoom(zoom + ZOOM_FACTOR); }} shortcut={["Control", "+"]}/> <ClickAction label="Zoom in" onTrigger={() => { setZoom(zoom + ZOOM_FACTOR); }} shortcut={["Control", "+"]}/>
<ClickAction label="Zoom out" onTrigger={() => { setZoom(zoom - ZOOM_FACTOR); }} shortcut={["Control", "-"]}/> <ClickAction label="Zoom out" onTrigger={() => { setZoom(zoom - ZOOM_FACTOR); }} shortcut={["Control", "-"]}/>
<ClickAction label="Reset Zoom" onTrigger={() => { setZoom(DEFAULT_ZOOM); }} shortcut={["Control", "0"]}/> <ClickAction label="Reset Zoom" disabled={zoom == DEFAULT_ZOOM} onTrigger={() => { setZoom(DEFAULT_ZOOM); }} shortcut={["Control", "0"]}/>
</DropdownAction> </DropdownAction>
</HeaderMenu> </HeaderMenu>
{currentMode === "view" {currentMode === "view"

View file

@ -55,6 +55,14 @@
padding: 0 !important; padding: 0 !important;
} }
.HomeContainer,
.HomeContainer > div,
.SearchContainer,
.SearchContainer > div {
height: 100%;
width: auto;
}
.HomeButton * { .HomeButton * {
fill: var(--foreground-color-0); fill: var(--foreground-color-0);
filter: none; filter: none;
@ -102,10 +110,10 @@
height: 1.25rem; height: 1.25rem;
} }
.AppIcons div, .AppIcons > button div,
.AppIcons div > svg, .AppIcons > button div > svg,
.MenuIcons div, .MenuButton div,
.MenuIcons div > svg { .MenuButton div > svg {
height: 100%; height: 100%;
width: auto; width: auto;
} }

View file

@ -1,4 +1,4 @@
import { CSSProperties, memo, ReactEventHandler, UIEventHandler, useEffect, useMemo, useRef, useState } from "react"; import { CSSProperties, memo, ReactEventHandler, UIEventHandler, useEffect, useRef, useState } from "react";
import styles from "./Taskbar.module.css"; import styles from "./Taskbar.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faCog, faSearch } from "@fortawesome/free-solid-svg-icons"; import { faCog, faSearch } from "@fortawesome/free-solid-svg-icons";
@ -24,6 +24,7 @@ import { SettingsManager } from "../../features/settings/settingsManager";
import { useWindows } from "../../hooks/windows/windowsContext"; import { useWindows } from "../../hooks/windows/windowsContext";
import { ZIndexManager } from "../../features/z-index/zIndexManager"; import { ZIndexManager } from "../../features/z-index/zIndexManager";
import { useZIndex } from "../../hooks/z-index/zIndex"; import { useZIndex } from "../../hooks/z-index/zIndex";
import App from "../../features/apps/app";
export const Taskbar = memo(() => { export const Taskbar = memo(() => {
const ref = useRef(null); const ref = useRef(null);
@ -48,38 +49,31 @@ export const Taskbar = memo(() => {
}}/> }}/>
</Actions> </Actions>
}); });
const [pins, setPins] = useState([]); const [apps, setApps] = useState<App[]>([]);
const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.TASKBAR, index: 0 }); const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.TASKBAR, index: 0 });
const apps = useMemo(() => AppsManager.APPS.sort((appA, appB) => {
const indexA = pins.indexOf(appA.id);
const indexB = pins.indexOf(appB.id);
if (indexA < 0 && indexB > 0) {
return 1;
} else if (indexA > 0 && indexB < 0) {
return -1;
} else if (indexA < 0 && indexB < 0) {
return 0;
} else {
return indexA - indexB;
}
}).map((app) => {
const isActive = windows.map((window) => window.app.id).includes(app.id);
const shouldBeShown = (pins.includes(app.id) || isActive);
return (<AppButton
windowsManager={windowsManager}
pins={pins}
app={app}
key={app.id}
active={isActive}
visible={shouldBeShown}
/>);
}), [pins, windows, windowsManager]);
useEffect(() => { useEffect(() => {
const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.taskbar); const settings = settingsManager.get(SettingsManager.VIRTUAL_PATHS.taskbar);
void settings.get("pins", (pins) => { void settings.get("pins", (pinList: string) => {
setPins(pins.split(",")); const pins = pinList.split(",");
const newApps = AppsManager.APPS.sort((appA, appB) => {
const indexA = pins.indexOf(appA.id);
const indexB = pins.indexOf(appB.id);
if (indexA < 0 && indexB > 0) {
return 1;
} else if (indexA > 0 && indexB < 0) {
return -1;
} else if (indexA < 0 && indexB < 0) {
return 0;
} else {
return indexA - indexB;
}
}).map((app) => {
app.isPinned = pins.includes(app.id);
return app;
});
setApps(newApps);
}); });
}, [settingsManager]); }, [settingsManager]);
@ -95,6 +89,7 @@ export const Taskbar = memo(() => {
const updateShowSearch = (show: boolean) => { const updateShowSearch = (show: boolean) => {
setShowSearch(show); setShowSearch(show);
if (show) { if (show) {
if (searchQuery !== "") { if (searchQuery !== "") {
setSearchQuery(""); setSearchQuery("");
@ -126,68 +121,76 @@ export const Taskbar = memo(() => {
updateShowSearch(true); updateShowSearch(true);
}; };
return (<> return <div
<div style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px`, zIndex } as CSSProperties}
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px`, zIndex } as CSSProperties} className={styles.Taskbar}
className={styles.Taskbar} data-allow-context-menu={true}
data-allow-context-menu={true} onContextMenu={(event) => {
onContextMenu={(event) => { if ((event.target as HTMLElement).getAttribute("data-allow-context-menu"))
if ((event.target as HTMLElement).getAttribute("data-allow-context-menu")) onContextMenu(event);
onContextMenu(event); }}
}} >
> <div className={styles.MenuIcons}>
<div className={styles.MenuIcons}> <div className={styles.HomeContainer}>
<div className={styles.HomeContainer}> <OutsideClickListener onOutsideClick={() => { updateShowHome(false); }}>
<OutsideClickListener onOutsideClick={() => { updateShowHome(false); }}> <button
<button title="Home"
title="Home" tabIndex={0}
tabIndex={0} className={`${styles.MenuButton} ${styles.HomeButton}`}
className={`${styles.MenuButton} ${styles.HomeButton}`} onClick={() => { updateShowHome(!showHome); }}
onClick={() => { updateShowHome(!showHome); }} >
> <ReactSVG src={"/icon.svg"}/>
<ReactSVG src={"/icon.svg"}/> </button>
</button> <HomeMenu active={showHome} setActive={updateShowHome} search={search}/>
<HomeMenu active={showHome} setActive={updateShowHome} search={search}/> </OutsideClickListener>
</OutsideClickListener>
</div>
<div className={styles.SearchContainer}>
<OutsideClickListener onOutsideClick={() => { updateShowSearch(false); }}>
<button
title="Search"
tabIndex={0}
className={styles.MenuButton}
onClick={() => { updateShowSearch(!showSearch); }}
>
<FontAwesomeIcon icon={faSearch}/>
</button>
<SearchMenu
active={showSearch}
setActive={updateShowSearch}
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
inputRef={inputRef}
/>
</OutsideClickListener>
</div>
</div> </div>
<div className={styles.AppIconsContainer} data-allow-context-menu={true} style={{ boxShadow }}> <div className={styles.SearchContainer}>
<div <OutsideClickListener onOutsideClick={() => { updateShowSearch(false); }}>
className={styles.AppIcons} <button
data-allow-context-menu={true} title="Search"
onScroll={onUpdate as unknown as UIEventHandler} tabIndex={0}
onResize={onUpdate as unknown as ReactEventHandler} className={styles.MenuButton}
ref={ref} onClick={() => { updateShowSearch(!showSearch); }}
> >
{apps} <FontAwesomeIcon icon={faSearch}/>
</div> </button>
</div> <SearchMenu
<div className={styles.UtilIcons}> active={showSearch}
<Battery showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/> setActive={updateShowSearch}
<Network showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/> searchQuery={searchQuery}
<Volume showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/> setSearchQuery={setSearchQuery}
<Calendar showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/> inputRef={inputRef}
<button title="Show Desktop" id="desktop-button" onClick={() => { windowsManager.minimizeAll(); }}/> />
</OutsideClickListener>
</div> </div>
</div> </div>
</>); <div className={styles.AppIconsContainer} data-allow-context-menu={true} style={{ boxShadow }}>
<div
className={styles.AppIcons}
data-allow-context-menu={true}
onScroll={onUpdate as unknown as UIEventHandler}
onResize={onUpdate as unknown as ReactEventHandler}
ref={ref}
>
{apps.map((app) => {
const isActive = windows.map((window) => window.app.id).includes(app.id);
const shouldBeShown = (app.isPinned || isActive);
return (<AppButton
windowsManager={windowsManager}
app={app}
key={app.id}
active={isActive}
visible={shouldBeShown}
/>);
})}
</div>
</div>
<div className={styles.UtilIcons}>
<Battery showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>
<Network showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>
<Volume showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>
<Calendar showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>
<button title="Show Desktop" id="desktop-button" onClick={() => { windowsManager.minimizeAll(); }}/>
</div>
</div>;
}); });

View file

@ -15,21 +15,18 @@ import WindowsManager from "../../../features/windows/windowsManager";
interface AppButtonProps { interface AppButtonProps {
app: App; app: App;
windowsManager: WindowsManager; windowsManager: WindowsManager;
pins: string[];
active: boolean; active: boolean;
visible: boolean; visible: boolean;
} }
export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, pins, active, visible }: AppButtonProps) => { export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, active, visible }: AppButtonProps) => {
const isPinned = pins.includes(app.id);
const settingsManager = useSettingsManager(); const settingsManager = useSettingsManager();
const { onContextMenu } = useContextMenu({ Actions: (props) => const { onContextMenu } = useContextMenu({ Actions: (props) =>
<Actions avoidTaskbar={false} {...props}> <Actions avoidTaskbar={false} {...props}>
<ClickAction label={app.name} icon={AppsManager.getAppIconUrl(app.id)} onTrigger={() => { <ClickAction label={app.name} icon={AppsManager.getAppIconUrl(app.id)} onTrigger={() => {
windowsManager.open(app.id); windowsManager.open(app.id);
}}/> }}/>
<ClickAction label={isPinned ? "Unpin from taskbar" : "Pin to taskbar"} icon={faThumbTack} onTrigger={() => { {/* <ClickAction label={isPinned ? "Unpin from taskbar" : "Pin to taskbar"} icon={faThumbTack} onTrigger={() => {
const newPins = [...pins]; const newPins = [...pins];
if (isPinned) { if (isPinned) {
removeFromArray(app.id, pins); removeFromArray(app.id, pins);
@ -42,7 +39,7 @@ export const AppButton: FC<AppButtonProps> = memo(({ app, windowsManager, pins,
}}/> }}/>
{active && <ClickAction label="Close window" icon={faTimes} onTrigger={() => { {active && <ClickAction label="Close window" icon={faTimes} onTrigger={() => {
windowsManager.close(windowsManager.getAppWindowId(app.id)); windowsManager.close(windowsManager.getAppWindowId(app.id));
}}/>} }}/>} */}
</Actions> </Actions>
}); });

View file

@ -99,7 +99,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
<div className={styles.Apps}> <div className={styles.Apps}>
<h1 className={utilStyles.TextBold}>{NAME}</h1> <h1 className={utilStyles.TextBold}>{NAME}</h1>
<div className={appStyles.AppList}> <div className={appStyles.AppList}>
{AppsManager.APPS.map(({ name, id }) => {AppsManager.APPS.sort((a, b) => a.name.localeCompare(b.name)).map(({ name, id }) =>
<button <button
key={id} key={id}
className={appStyles.AppButton} className={appStyles.AppButton}

View file

@ -4,25 +4,24 @@
flex-direction: column; flex-direction: column;
left: 0; left: 0;
bottom: 100%; bottom: 100%;
height: auto !important;
max-height: 20rem; max-height: 20rem;
overflow: hidden; overflow: hidden;
} }
.SearchMenu { .SearchMenu {
opacity: 1; opacity: 0;
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
flex-direction: column-reverse; flex-direction: column;
min-width: 13rem; min-width: 13rem;
max-width: 19rem; max-width: 19rem;
padding: 0.5rem; padding: 0.5rem;
margin-top: 100px;
border-top-left-radius: var(--border-radius-1); border-top-left-radius: var(--border-radius-1);
border-top-right-radius: var(--border-radius-1); border-top-right-radius: var(--border-radius-1);
border-bottom-right-radius: var(--border-radius-1); border-bottom-right-radius: var(--border-radius-1);
backdrop-filter: var(--taskbar-filter); backdrop-filter: var(--taskbar-filter);
transform: none; transition: opacity 200ms ease-in-out, margin-top 200ms ease-in-out;
transition: opacity 200ms ease-in-out, transform 200ms ease-in-out;
overflow: hidden; overflow: hidden;
resize: horizontal; resize: horizontal;
} }
@ -39,13 +38,13 @@
z-index: -2; z-index: -2;
} }
.SearchMenuContainer:not(.Active) { .SearchMenuContainer.Active .SearchMenu {
pointer-events: none; opacity: 1;
margin-top: 0px;
} }
.SearchMenuContainer:not(.Active) .SearchMenu { .SearchMenuContainer:not(.Active) {
opacity: 0; pointer-events: none;
transform: translateY(100px);
} }
.SearchMenu > div { .SearchMenu > div {

View file

@ -2,10 +2,10 @@ import styles from "./SearchMenu.module.css";
import appStyles from "./AppList.module.css"; import appStyles from "./AppList.module.css";
import AppsManager from "../../../features/apps/appsManager"; import AppsManager from "../../../features/apps/appsManager";
import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext"; import { useWindowsManager } from "../../../hooks/windows/windowsManagerContext";
import { ReactSVG } from "react-svg";
import { ChangeEventHandler, useEffect, useState } from "react"; import { ChangeEventHandler, useEffect, useState } from "react";
import { useKeyboardListener } from "../../../hooks/_utils/keyboard"; import { useKeyboardListener } from "../../../hooks/_utils/keyboard";
import App from "../../../features/apps/app"; import App from "../../../features/apps/app";
import { ReactSVG } from "react-svg";
interface SearchMenuProps { interface SearchMenuProps {
active: boolean; active: boolean;
@ -32,8 +32,9 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
}, [inputRef]); }, [inputRef]);
useEffect(() => { useEffect(() => {
setApps(AppsManager.APPS.filter(({ name }) => setApps(AppsManager.APPS.filter(({ name, id }) =>
name.toLowerCase().includes(searchQuery.toLowerCase().trim()) name.toLowerCase().includes(searchQuery.toLowerCase().trim())
|| id.toLowerCase().includes(searchQuery.toLowerCase().trim())
).sort((a, b) => ).sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase()) a.name.toLowerCase().localeCompare(b.name.toLowerCase())
)); ));
@ -45,7 +46,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
}; };
const classNames = [styles.SearchMenuContainer]; const classNames = [styles.SearchMenuContainer];
if (active && apps) if (active && apps != null)
classNames.push(styles.Active); classNames.push(styles.Active);
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
@ -64,36 +65,34 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
useKeyboardListener({ onKeyDown }); useKeyboardListener({ onKeyDown });
return ( return <div className={classNames.join(" ")}>
<div className={classNames.join(" ")}> <div className={styles.SearchMenu}>
<div className={styles.SearchMenu}> <div className={appStyles.AppList}>
<input {apps?.map(({ name, id }) =>
ref={inputRef} <button
className={styles.Input} key={id}
aria-label="Search query" className={appStyles.AppButton}
tabIndex={tabIndex} tabIndex={tabIndex}
value={searchQuery} onClick={() => {
onChange={onChange as unknown as ChangeEventHandler} setActive(false);
spellCheck={false} windowsManager.open(id);
placeholder="Search..." }}
/> >
<div className={appStyles.AppList}> <ReactSVG src={AppsManager.getAppIconUrl(id)}/>
{apps?.map(({ name, id }) => <p>{name}</p>
<button </button>
key={id} )}
className={appStyles.AppButton}
tabIndex={tabIndex}
onClick={() => {
setActive(false);
windowsManager.open(id);
}}
>
<ReactSVG src={AppsManager.getAppIconUrl(id)}/>
<p>{name}</p>
</button>
)}
</div>
</div> </div>
<input
ref={inputRef}
className={styles.Input}
aria-label="Search query"
tabIndex={tabIndex}
value={searchQuery}
onChange={onChange as unknown as ChangeEventHandler}
spellCheck={false}
placeholder="Search..."
/>
</div> </div>
); </div>;
} }

View file

@ -2,6 +2,6 @@ import styles from "../components/actions/Actions.module.css";
export const STYLES = { export const STYLES = {
CONTEXT_MENU: styles.ContextMenu, CONTEXT_MENU: styles.ContextMenu,
SHORTCUTS_LISTENER: styles["Shortcuts-listener"], SHORTCUTS_LISTENER: styles.ShortcutsListener,
HEADER_MENU: styles["Header-menu"] HEADER_MENU: styles.HeaderMenu
}; };

View file

@ -10,6 +10,7 @@ export default class App {
size: Vector2 size: Vector2
}; };
isActive: boolean = false; isActive: boolean = false;
isPinned?: boolean;
/** /**
* @param windowOptions - Default window options * @param windowOptions - Default window options

View file

@ -25,7 +25,7 @@ export default class AppsManager {
source: "https://prozilla.dev/wordle", source: "https://prozilla.dev/wordle",
size: new Vector2(400, 650) size: new Vector2(400, 650)
}), }),
new App("Balls", "balls", WebView, { new App("Ball Maze", "ball-maze", WebView, {
source: "https://prozilla.dev/ball-maze", source: "https://prozilla.dev/ball-maze",
size: new Vector2(600, 600) size: new Vector2(600, 600)
}), }),