Added z-index helpers + updated docs
This commit is contained in:
parent
01c5d0be93
commit
af5a1526fa
16 changed files with 234 additions and 31 deletions
|
|
@ -5,7 +5,9 @@
|
|||
## Table of Contents
|
||||
|
||||
- [Features](features/README.md)
|
||||
- [Configuration](configuration/README.md)
|
||||
- [Design](design/README.md)
|
||||
- [FAQ](faq/README.md)
|
||||
- [Roadmap](roadmap/README.md)
|
||||
- [Contributing](contributing/README.md)
|
||||
|
||||
|
|
@ -28,7 +30,7 @@
|
|||
|
||||
#### Deploying to GitHub Pages
|
||||
|
||||
1. Open [deploy.sh](../deploy.sh) and change `DOMAIN` to your domain, `COMMIT_MESSAGE` to your commit message and `REPO_URL` to the URL of your repo. Make sure you don't add or remove any quotes when editing these values.
|
||||
1. Open [deploy.sh](../deploy.sh) and change `DOMAIN` to your domain, `COMMIT_MESSAGE` to your commit message and `REPO_URL` to the URL of your repo. Make sure you don't accidentally add or remove any quotes when editing these values.
|
||||
2. Run `npm run deploy` - This will create a new build by running the redeploy script, add a CNAME pointing to your domain and then push the build folder to a branch called `gh-pages`.
|
||||
3. Configure your GitHub Pages to deploy the `gh-pages` branch to your domain.
|
||||
|
||||
|
|
@ -36,6 +38,10 @@ Once the initial setup is complete, you can simply run `npm run deploy` and your
|
|||
|
||||
> Note: When the deployment process is complete, you won't see the changes reflected on your website until a few moments later. This is because GitHub Pages needs to run its own build step before the website updates. You can view details about this process in the `Actions` tab on GitHub.
|
||||
|
||||
### Configuration
|
||||
|
||||
See [docs/configuration](configuration/README.md) for more information.
|
||||
|
||||
### Structure
|
||||
|
||||
- [src](../src) directory
|
||||
|
|
|
|||
11
docs/configuration/README.md
Normal file
11
docs/configuration/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[← Back](../README.md)
|
||||
|
||||
# Configuration
|
||||
|
||||
Prozilla OS can be configured in numerous ways. The most important one being via the application itself, by going to the Settings app or directly changing the config files in the `~/.config` folder.
|
||||
|
||||
As a developer, there are also the following ways to configure Prozilla OS:
|
||||
|
||||
- `src/config` - The `src/config` directory holds all global variables used in the rest of the `src` directory, which are mostly string and number constants, but also includes some arrays and dictionaries that can be adjusted to configure Prozilla oS.
|
||||
- `styles` - Everything related to styles, can be configured in `styles` directory. Most configurations will happen inside `styles/global`, where you can define the fonts, css variables/properties and other details.
|
||||
- `public/config` - This directory has XML files that serve as the default data for config files used by the app in the virtual drive. These can be edited by the user once they're loaded during initialisation.
|
||||
11
docs/faq/README.md
Normal file
11
docs/faq/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[← Back](../README.md)
|
||||
|
||||
# FAQ
|
||||
|
||||
### Q: What's the purpose of Prozilla OS?
|
||||
|
||||
A: Obviously Prozilla OS doesn't solve any issues or anything like that. It's just a fun project I started because I wanted to refine my web development and design skills, as well as my React.js proficiency. At the same time, I also wanted to create an interesting and innovative web application and share it with the community via open source.
|
||||
|
||||
### Q: What tools did you use to make Prozilla OS?
|
||||
|
||||
A: I used Visual Studio Code for coding, Figma for designing, Notion for project management, GitHub for source control. I didn't use any libraries for the main parts of the OS. The libraries I used for specific purposes like certain applications or utilities, can be found in the `package.json` file. I got the icons from Font Awesome and the fonts from Google Fonts.
|
||||
|
|
@ -17,4 +17,5 @@ To see the status and to-do's of each feature, check the [task board](https://pr
|
|||
- [Storage](storage/README.md)
|
||||
- [Taskbar](taskbar/README.md)
|
||||
- [Virtual Drive](virtual-drive//README.md)
|
||||
- [Windows](windows/README.md)
|
||||
- [Windows](windows/README.md)
|
||||
- [Z-index](z-index/README.md)
|
||||
5
docs/features/z-index/README.md
Normal file
5
docs/features/z-index/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
[← Back](../README.md)
|
||||
|
||||
# Z-index
|
||||
|
||||
A collection of helper classes to generate z-indexes that prevent overlapping and make sure everything is displayed in the correct order. One example use case is the windows that all need to be displayed in order and should always be displayed under the taskbar, this is made possible by these helper classes. It works by creating groups that can contain any amount of indexes, each group's offset is defined by the combined length of all preceding groups.
|
||||
|
|
@ -6,18 +6,14 @@ This is a list of planned/in progress features.
|
|||
|
||||
For more detailed information, check the [task board](https://prozilla.notion.site/8325fabca1fb4f9885b6d6dfd5aa64c8?v=1a59f7ce50914f5ea711fe6460e52868&pvs=4) on Notion.
|
||||
|
||||
## Visual Studio Code clone
|
||||
### Visual Studio Code clone
|
||||
|
||||
A fully functional VSC clone called Code Editor.
|
||||
|
||||
## Modals
|
||||
|
||||
Modular windows, including alerts, confirmation dialogs and a file selector.
|
||||
|
||||
## Calculator App
|
||||
### Calculator App
|
||||
|
||||
Simple calculator that can do basic equations.
|
||||
|
||||
## App centre
|
||||
### App centre
|
||||
|
||||
Allows user to download additional apps
|
||||
23
src/App.jsx
23
src/App.jsx
|
|
@ -7,6 +7,7 @@ import { Desktop } from "./components/desktop/Desktop.jsx";
|
|||
import { SettingsManagerProvider } from "./hooks/settings/settingsManagerContext.js";
|
||||
import { ModalsView } from "./components/modals/ModalsView.jsx";
|
||||
import { useEffect } from "react";
|
||||
import { ZIndexManagerProvider } from "./hooks/z-index/zIndexManagerContext.js";
|
||||
|
||||
function App() {
|
||||
useEffect(() => {
|
||||
|
|
@ -23,16 +24,18 @@ function App() {
|
|||
|
||||
return (
|
||||
<VirtualRootProvider>
|
||||
<WindowsManagerProvider>
|
||||
<SettingsManagerProvider>
|
||||
<div className={styles.App}>
|
||||
<Taskbar/>
|
||||
<WindowsView/>
|
||||
<ModalsView/>
|
||||
<Desktop/>
|
||||
</div>
|
||||
</SettingsManagerProvider>
|
||||
</WindowsManagerProvider>
|
||||
<ZIndexManagerProvider>
|
||||
<WindowsManagerProvider>
|
||||
<SettingsManagerProvider>
|
||||
<div className={styles.App}>
|
||||
<Taskbar/>
|
||||
<WindowsView/>
|
||||
<ModalsView/>
|
||||
<Desktop/>
|
||||
</div>
|
||||
</SettingsManagerProvider>
|
||||
</WindowsManagerProvider>
|
||||
</ZIndexManagerProvider>
|
||||
</VirtualRootProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,8 +120,6 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app, modal
|
|||
};
|
||||
}
|
||||
|
||||
console.log(currentFile);
|
||||
|
||||
return (
|
||||
<div className={styles.Container} style={{ fontSize: zoom }}>
|
||||
<HeaderMenu
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ import { TASKBAR_HEIGHT } from "../../config/taskbar.config.js";
|
|||
import { useSettingsManager } from "../../hooks/settings/settingsManagerContext.js";
|
||||
import { SettingsManager } from "../../features/settings/settingsManager.js";
|
||||
import { useWindows } from "../../hooks/windows/windowsContext.js";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager.js";
|
||||
import { useZIndex } from "../../hooks/z-index/zIndex.js";
|
||||
|
||||
export const Taskbar = memo(() => {
|
||||
const ref = useRef(null);
|
||||
|
|
@ -49,6 +51,8 @@ export const Taskbar = memo(() => {
|
|||
</Actions>
|
||||
});
|
||||
const [pins, setPins] = useState([]);
|
||||
const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.TASKBAR, index: 0 });
|
||||
const modalsZIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.TASKBAR, index: 1 });
|
||||
|
||||
const apps = useMemo(() => AppsManager.APPS.sort((appA, appB) => {
|
||||
const indexA = pins.indexOf(appA.id);
|
||||
|
|
@ -119,9 +123,9 @@ export const Taskbar = memo(() => {
|
|||
};
|
||||
|
||||
return (<>
|
||||
<ModalsView modalsManager={modalsManager} modals={modals}/>
|
||||
<ModalsView modalsManager={modalsManager} modals={modals} style={{ zIndex: modalsZIndex }}/>
|
||||
<div
|
||||
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px` }}
|
||||
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px`, zIndex }}
|
||||
className={styles["Taskbar"]}
|
||||
data-allow-context-menu={true}
|
||||
onContextMenu={(event) => {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
height: var(--taskbar-height);
|
||||
background-color: var(--taskbar-color);
|
||||
backdrop-filter: var(--taskbar-filter);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.Taskbar button {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ import { Actions } from "../actions/Actions.jsx";
|
|||
import { useScreenDimensions } from "../../hooks/_utils/screen.js";
|
||||
import { NAME } from "../../config/branding.config.js";
|
||||
import { setViewportIcon, setViewportTitle } from "../../features/_utils/browser.utils.js";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager.js";
|
||||
import { useZIndex } from "../../hooks/z-index/zIndex.js";
|
||||
|
||||
/**
|
||||
* @typedef {object} windowProps
|
||||
|
|
@ -43,7 +45,7 @@ import { setViewportIcon, setViewportTitle } from "../../features/_utils/browser
|
|||
* @param {boolean} props.minimized
|
||||
* @param {Function} props.toggleMinimized
|
||||
*/
|
||||
export const WindowView = memo(({ id, app, size, position, onInteract, options, active, minimized, toggleMinimized }) => {
|
||||
export const WindowView = memo(({ id, app, size, position, onInteract, options, active, minimized, toggleMinimized, index }) => {
|
||||
const windowsManager = useWindowsManager();
|
||||
const nodeRef = useRef(null);
|
||||
const [modalsManager, modals] = useModals();
|
||||
|
|
@ -54,6 +56,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
const [screenWidth, screenHeight] = useScreenDimensions();
|
||||
const [title, setTitle] = useState(app.name);
|
||||
const [iconUrl, setIconUrl] = useState(AppsManager.getAppIconUrl(app.id));
|
||||
const zIndex = useZIndex({ groupIndex: ZIndexManager.GROUPS.WINDOWS, index });
|
||||
|
||||
const { onContextMenu, ShortcutsListener } = useContextMenu({ modalsManager, Actions: (props) =>
|
||||
<Actions {...props}>
|
||||
|
|
@ -123,7 +126,7 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
if (minimized)
|
||||
classNames.push(styles.Minimized);
|
||||
|
||||
return (<>
|
||||
return (<div style={{ zIndex, position: "relative" }}>
|
||||
<ShortcutsListener/>
|
||||
<ModalsView modalsManager={modalsManager} modals={modals} style={{ zIndex: 1 }}/>
|
||||
<Draggable
|
||||
|
|
@ -200,5 +203,5 @@ export const WindowView = memo(({ id, app, size, position, onInteract, options,
|
|||
</div>
|
||||
</div>
|
||||
</Draggable>
|
||||
</>);
|
||||
</div>);
|
||||
});
|
||||
|
|
@ -48,14 +48,17 @@ export const WindowsView = memo(() => {
|
|||
// TO DO: prevent windows from being rerendered when order is changed
|
||||
|
||||
return (<div>
|
||||
{sortedWindows.map(({ id, app, size, position, options, minimized }, index) =>
|
||||
<WindowView
|
||||
{windows.map((window) => {
|
||||
const { id, app, size, position, options, minimized } = window;
|
||||
const index = sortedWindows.indexOf(window);
|
||||
return <WindowView
|
||||
key={id}
|
||||
onInteract={() => { windowsManager.focus(id); }}
|
||||
active={index === sortedWindows.length - 1}
|
||||
id={id}
|
||||
app={app}
|
||||
size={size}
|
||||
index={index}
|
||||
position={position}
|
||||
options={options}
|
||||
minimized={minimized}
|
||||
|
|
@ -64,7 +67,7 @@ export const WindowsView = memo(() => {
|
|||
event.stopPropagation();
|
||||
windowsManager.setMinimized(id, !minimized);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>;
|
||||
})}
|
||||
</div>);
|
||||
});
|
||||
67
src/features/z-index/zIndexGroup.js
Normal file
67
src/features/z-index/zIndexGroup.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { ZIndexManager } from "./zIndexManager.js";
|
||||
|
||||
export class ZIndexGroup {
|
||||
/** @type {number} */
|
||||
length = 0;
|
||||
|
||||
/** @type {number} */
|
||||
offset = 0;
|
||||
|
||||
/** @type {number} */
|
||||
groupIndex = 0;
|
||||
|
||||
/** @type {ZIndexManager} */
|
||||
zIndexManager = null;
|
||||
|
||||
/**
|
||||
* @param {ZIndexManager} zIndexManager
|
||||
* @param {number} initialLength
|
||||
*/
|
||||
constructor(zIndexManager, initialLength) {
|
||||
this.setManager(zIndexManager);
|
||||
this.setLength(initialLength ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ZIndexManager} zIndexManager
|
||||
* @returns {ZIndexGroup}
|
||||
*/
|
||||
setManager(zIndexManager) {
|
||||
this.zIndexManager = zIndexManager;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @returns {ZIndexGroup}
|
||||
*/
|
||||
setOffset(offset) {
|
||||
this.offset = offset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} length
|
||||
* @returns {ZIndexGroup}
|
||||
*/
|
||||
setLength(length) {
|
||||
if (this.length === length)
|
||||
return;
|
||||
|
||||
this.length = length;
|
||||
this.zIndexManager.update();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} index
|
||||
* @returns {number}
|
||||
*/
|
||||
getIndex(index) {
|
||||
if (this.length < index + 1) {
|
||||
this.setLength(index + 1);
|
||||
}
|
||||
|
||||
return this.offset + index;
|
||||
}
|
||||
}
|
||||
45
src/features/z-index/zIndexManager.js
Normal file
45
src/features/z-index/zIndexManager.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { EventEmitter } from "../_utils/event.utils.js";
|
||||
import { ZIndexGroup } from "./zIndexGroup.js";
|
||||
|
||||
export class ZIndexManager extends EventEmitter {
|
||||
static GROUPS = {
|
||||
WINDOWS: 0,
|
||||
TASKBAR: 1,
|
||||
MODALS: 2,
|
||||
};
|
||||
|
||||
static EVENT_NAMES = {
|
||||
INDEX_CHANGE: "indexchange"
|
||||
};
|
||||
|
||||
/** @type {ZIndexGroup[]} */
|
||||
groups = [];
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
for (let i = 0; i < Object.keys(ZIndexManager.GROUPS).length; i++) {
|
||||
const group = new ZIndexGroup(this);
|
||||
this.groups.push(group);
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
let offset = 0;
|
||||
let changed = false;
|
||||
for (let i = 0; i < this.groups.length; i++) {
|
||||
const group = this.groups[i];
|
||||
if (group.offset !== offset) {
|
||||
changed = true;
|
||||
group.setOffset(offset);
|
||||
}
|
||||
offset += group.length;
|
||||
}
|
||||
if (changed) {
|
||||
this.emit(ZIndexManager.EVENT_NAMES.INDEX_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
getIndex(groupIndex, index) {
|
||||
return this.groups[groupIndex].getIndex(index);
|
||||
}
|
||||
}
|
||||
27
src/hooks/z-index/zIndex.js
Normal file
27
src/hooks/z-index/zIndex.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useZIndexManager } from "./zIndexManagerContext.js";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager.js";
|
||||
|
||||
export function useZIndex({ groupIndex, index }) {
|
||||
const initialIndex = (groupIndex * 10) + index;
|
||||
const [zIndex, setZIndex] = useState(initialIndex);
|
||||
const zIndexManager = useZIndexManager();
|
||||
|
||||
useEffect(() => {
|
||||
const updateIndex = () => {
|
||||
const newZIndex = zIndexManager.getIndex(groupIndex, index);
|
||||
|
||||
if (zIndex !== newZIndex)
|
||||
setZIndex(newZIndex);
|
||||
};
|
||||
|
||||
updateIndex();
|
||||
zIndexManager.on(ZIndexManager.EVENT_NAMES.INDEX_CHANGE, updateIndex);
|
||||
|
||||
return () => {
|
||||
zIndexManager.off(ZIndexManager.EVENT_NAMES.INDEX_CHANGE, updateIndex);
|
||||
};
|
||||
}, [groupIndex, index, zIndex, zIndexManager]);
|
||||
|
||||
return zIndex;
|
||||
}
|
||||
24
src/hooks/z-index/zIndexManagerContext.js
Normal file
24
src/hooks/z-index/zIndexManagerContext.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { createContext, useContext } from "react";
|
||||
import { ZIndexManager } from "../../features/z-index/zIndexManager.js";
|
||||
|
||||
const ZIndexManagerContext = createContext();
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {import("react").ElementType} props.children
|
||||
* @returns {import("react").Provider<any>}
|
||||
*/
|
||||
export function ZIndexManagerProvider({ children }) {
|
||||
const zIndexManager = new ZIndexManager();
|
||||
|
||||
return <ZIndexManagerContext.Provider value={zIndexManager}>
|
||||
{children}
|
||||
</ZIndexManagerContext.Provider>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ZIndexManager}
|
||||
*/
|
||||
export function useZIndexManager() {
|
||||
return useContext(ZIndexManagerContext);
|
||||
}
|
||||
Loading…
Reference in a new issue