Updated CSS class naming convention

This commit is contained in:
Prozilla 2024-06-05 21:46:34 +02:00
parent 0fdd7ad632
commit 7cee514acb
No known key found for this signature in database
GPG key ID: 5858DFE71CAF31EE
78 changed files with 384 additions and 462 deletions

View file

@ -68,9 +68,10 @@ Type | Case | Example
Folders | kebab-case | `virtual-drive`
`.ts` files | camelCase | `virtualRoot.ts`
`.tsx` files | PascalCase | `Desktop.tsx`
`.css` files & files in `public` dir | kebab-case | `global.css`
`.css` files & static assets | kebab-case | `global.css`
Local `.module.css` files | PascalCase | `Desktop.module.css`
Global `.module.css` files | kebab-case | `utils.module.css`
CSS class names | PascalCase | ```.WindowsView```
Variables | camelCase | ```const fooBar = true;```
Global constant variables | MACRO_CASE | ```export const NAME = "ProzillaOS";```
Classes | PascalCase | ```class WindowsManager { }```

View file

@ -9,13 +9,13 @@
<link rel="canonical" href="https://os.prozilla.dev/">
<!-- Favicon -->
<link rel="icon" href="https://os.prozilla.dev/favicon.ico?v=2" sizes="any">
<link rel="icon" type="image/x-icon" sizes="any" href="https://os.prozilla.dev/favicon.ico?v=2">
<link rel="icon" type="image/png" sizes="48x48" href="/favicon-48.png?v=2">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png?v=2">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png?v=2">
<link rel="icon" type="image/png" sizes="192x192" href="/favicon-192.png?v=2">
<link rel="shortcut icon" type="image/svg+xml" href="/icon.svg?v=2"/>
<link rel="apple-touch-icon" href="/favicon-192.png?v=2">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=2">
<link rel="mask-icon" href="/icon.svg?v=2" color="#36b29e"/>
<meta name="msapplication-TileColor" content="#0d1114"/>
<meta name="theme-color" content="#0d1114"/>

View file

@ -7,7 +7,7 @@
"repository": "https://github.com/Prozilla/ProzillaOS",
"type": "module",
"scripts": {
"start": "vite --port 3000",
"start": "vite --port 3000 --host",
"build": "tsc && vite build",
"serve": "vite preview",
"predeploy": "npm run build",

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -16,10 +16,10 @@
background-color: var(--hover-color);
}
.Button-link {
.ButtonLink {
text-decoration: none;
}
.Button-link > svg {
.ButtonLink > svg {
margin-left: 0.5rem;
}

View file

@ -17,7 +17,7 @@ export function Button(props: ButtonProps) {
className = `${styles.Button} ${className}`;
if (href != null) {
className = `${styles["Button-link"]} ${className}`;
className = `${styles.ButtonLink} ${className}`;
return (<a
href={href}

View file

@ -1,4 +1,4 @@
.Container {
.DropdownButton {
position: relative;
height: 100%;
}

View file

@ -3,13 +3,7 @@ import styles from "./DropdownButton.module.css";
import OutsideClickListener from "../../../hooks/_utils/outsideClick";
import { formatShortcut } from "../../../features/_utils/string.utils";
/**
* @param {object} props
* @param {string} props.label
* @param {Object<string, Function>} props.options
* @param {Object<string, string[]>} props.shortcuts
*/
export function DropdownButton({ label, options, shortcuts }) {
export function DropdownButton({ label, options, shortcuts }: { label: string; options: { [s: string]: Function; }; shortcuts: { [s: string]: string[]; }; }) {
const [open, setOpen] = useState(false);
const [tabIndex, setTabIndex] = useState(-1);
@ -19,7 +13,7 @@ export function DropdownButton({ label, options, shortcuts }) {
return (
<OutsideClickListener onOutsideClick={() => { setOpen(false); }}>
<div className={styles.Container}>
<div className={styles.DropdownButton}>
<button className={styles.Button} tabIndex={0} onClick={() => { setOpen(!open); }}>
{label}
</button>

View file

@ -1,10 +1,10 @@
.Container {
.ProgressBar {
position: relative;
width: 15rem;
max-width: 100%;
min-height: 2rem;
max-height: 100%;
background-color: var(--background-color-3);
background-color: var(--background-color-4);
}
.Fill {

View file

@ -21,7 +21,7 @@ interface ProgressBarProps {
export function ProgressBar({ fillPercentage, fillColor, backgroundColor, align = "left", className = "" }: ProgressBarProps) {
return (
<div className={`${styles.Container} ${className}`} style={{ backgroundColor: backgroundColor }}>
<div className={`${styles.ProgressBar} ${className}`} style={{ backgroundColor: backgroundColor }}>
<div
className={`${styles.Fill} ${align}`}
style={{ backgroundColor: fillColor, "--fill": `${clamp(fillPercentage, 0.1, 100)}%` } as CSSProperties}

View file

@ -1,4 +1,4 @@
.Container {
.Actions {
/* These variables describe the position of the container relative to top left corner of its parent */
--left: 0;
--top: 0;
@ -12,25 +12,25 @@
transition: opacity 100ms ease-out;
}
.Container.Uninitiated {
.Actions.Uninitiated {
opacity: 0;
}
.Container.Align-left {
.Actions.AlignLeft {
--left: 1;
left: unset;
right: 0;
}
.Container.Align-top {
.Actions.AlignTop {
--top: 1;
top: unset;
bottom: 0;
}
.Context-menu.Container {
.ContextMenu.Actions {
--border-radius: var(--border-radius-1);
--padding: 0.375rem;
@ -42,8 +42,8 @@
background-color: var(--background-color-1) !important;
}
.Context-menu .Button,
.Context-menu .Dropdown {
.ContextMenu .Button,
.ContextMenu .Dropdown {
display: flex;
gap: 0.75rem;
justify-content: space-between;
@ -59,26 +59,26 @@
cursor: pointer;
}
.Context-menu .Button:hover,
.Context-menu .Button:focus-visible,
.Context-menu .Dropdown:hover,
.Context-menu .Dropdown:focus-visible {
.ContextMenu .Button:hover,
.ContextMenu .Button:focus-visible,
.ContextMenu .Dropdown:hover,
.ContextMenu .Dropdown:focus-visible {
background-color: color-mix(in srgb, var(--background-color-0) 75%, transparent);
}
.Context-menu .Label {
.ContextMenu .Label {
display: flex;
gap: 0.5rem;
justify-content: center;
align-items: center;
}
.Context-menu .Label p,
.Context-menu .Shortcut {
.ContextMenu .Label p,
.ContextMenu .Shortcut {
margin: 0;
}
.Context-menu .Icon {
.ContextMenu .Icon {
display: flex;
justify-content: center;
align-items: center;
@ -86,40 +86,40 @@
height: 0.875rem;
}
.Context-menu .Icon > svg {
.ContextMenu .Icon > svg {
width: 100%;
height: 100%;
object-fit: contain;
}
.Context-menu .Image-icon {
.ContextMenu .ImageIcon {
position: absolute;
width: 1rem;
height: auto;
aspect-ratio: 1;
}
.Context-menu .Image-icon div {
.ContextMenu .ImageIcon div {
display: flex;
width: 100%;
height: 100%;
}
.Context-menu .Image-icon div > svg {
.ContextMenu .ImageIcon div > svg {
width: 100%;
height: 100%;
object-fit: contain;
}
.Context-menu .Shortcut {
.ContextMenu .Shortcut {
color: var(--foreground-color-1);
}
.Context-menu .Dropdown {
.ContextMenu .Dropdown {
position: relative;
}
.Context-menu .Dropdown .Dropdown-content {
.ContextMenu .Dropdown .DropdownContent {
opacity: 1;
position: absolute;
top: calc(var(--padding) * -1);
@ -132,12 +132,12 @@
cursor: auto;
}
.Context-menu .Dropdown:not(.Active) .Dropdown-content {
.ContextMenu .Dropdown:not(.Active) .DropdownContent {
opacity: 0;
pointer-events: none;
}
.Context-menu .Divider {
.ContextMenu .Divider {
width: calc(100% - 0.5rem);
height: 2px;
border-radius: var(--border-radius-99);
@ -174,11 +174,11 @@
height: 100%;
}
.Header-menu .Dropdown-arrow {
.Header-menu .DropdownArrow {
display: none;
}
.Header-menu .Dropdown-content {
.Header-menu .DropdownContent {
display: flex;
flex-direction: column;
position: absolute;
@ -190,7 +190,7 @@
border-bottom-right-radius: 0.5rem;
}
.Header-menu .Dropdown:not(.Active) .Dropdown-content {
.Header-menu .Dropdown:not(.Active) .DropdownContent {
opacity: 0;
pointer-events: none;
}

View file

@ -2,12 +2,7 @@ import { Children, cloneElement, isValidElement, ReactElement, ReactNode, Ref }
import { useShortcuts } from "../../hooks/_utils/keyboard";
import styles from "./Actions.module.css";
import { useScreenBounds } from "../../hooks/_utils/screen";
export const STYLES = {
CONTEXT_MENU: styles["Context-menu"],
SHORTCUTS_LISTENER: styles["Shortcuts-listener"],
HEADER_MENU: styles["Header-menu"]
};
import { STYLES } from "../../config/actions.config";
export interface ActionProps {
actionId?: string;
@ -84,13 +79,13 @@ export function Actions({ children, className, onAnyTrigger, triggerParams, avoi
if (isListener)
return iterateOverChildren(children) as ReactElement;
const classNames = [styles.Container];
const classNames = [styles.Actions];
if (className != null)
classNames.push(className);
if (alignLeft)
classNames.push(styles["Align-left"]);
classNames.push(styles.AlignLeft);
if (alignTop)
classNames.push(styles["Align-top"]);
classNames.push(styles.AlignTop);
if (!initiated)
classNames.push(styles.Uninitiated);

View file

@ -15,7 +15,7 @@ export const ClickAction = memo(({ actionId, label, shortcut, onTrigger, icon }:
<span className={styles.Label}>
{icon && <div className={styles.Icon}>
{typeof icon == "string"
? <ImagePreview source={icon} className={styles["Image-icon"]}/>
? <ImagePreview source={icon} className={styles.ImageIcon}/>
: <FontAwesomeIcon icon={icon as unknown as IconProp}/>
}
</div>}

View file

@ -32,8 +32,8 @@ export function DropdownAction({ label, icon, children, showOnHover = true }: Dr
{icon && <div className={styles.Icon}><FontAwesomeIcon icon={icon as IconDefinition}/></div>}
<p>{label}</p>
</span>
<div className={styles["Dropdown-arrow"]}><FontAwesomeIcon icon={faCaretRight}/></div>
<div className={styles["Dropdown-content"]}>
<div className={styles.DropdownArrow}><FontAwesomeIcon icon={faCaretRight}/></div>
<div className={styles.DropdownContent}>
{children}
</div>
</div>

View file

@ -1,4 +1,4 @@
.Container {
.HeaderMenu {
display: flex;
width: 100%;
height: 1.5rem;

View file

@ -1,9 +1,10 @@
import styles from "./HeaderMenu.module.css";
import { ActionsProps, Actions, STYLES } from "../../../actions/Actions";
import { ActionsProps, Actions } from "../../../actions/Actions";
import { STYLES } from "../../../../config/actions.config";
export function HeaderMenu({ children, ...props }: ActionsProps) {
return <div className={styles.Container}>
return <div className={styles.HeaderMenu}>
<Actions className={STYLES.HEADER_MENU} {...props}>
{children}
</Actions>

View file

@ -1,4 +1,4 @@
.Container {
.WebView {
display: flex;
justify-content: center;
align-items: center;
@ -6,7 +6,7 @@
height: 100%;
}
.Web-view {
.WebView > iframe {
width: 100%;
height: 100%;
border: none;

View file

@ -35,12 +35,11 @@ export const WebView: FC<WebViewProps> = forwardRef<HTMLIFrameElement>(({ source
setHovered(false);
};
return <div className={styles.Container} onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
return <div className={styles.WebView} onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
<iframe
ref={ref}
src={source}
title={props.title ?? "Web view"}
className={styles["Web-view"]}
referrerPolicy="no-referrer"
sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation allow-same-origin allow-scripts"
{...props}

View file

@ -1,4 +1,4 @@
.Container {
.Browser {
--header-height: 3.5rem;
--nav-bar-height: 2.25rem;
@ -16,7 +16,7 @@
background-color: var(--background-color-0);
}
.Nav-bar {
.NavBar {
display: flex;
gap: 1rem;
justify-content: flex-start;
@ -26,7 +26,7 @@
padding: 1.25rem;
}
.Icon-button {
.IconButton {
--color: var(--foreground-color-0);
position: relative;
@ -43,7 +43,7 @@
cursor: pointer;
}
.Icon-button::after {
.IconButton::after {
content: "";
position: absolute;
top: 0;
@ -57,26 +57,26 @@
transition: all 200ms ease-in-out;
}
.Icon-button:hover::after,
.Icon-button:focus-visible::after {
.IconButton:hover::after,
.IconButton:focus-visible::after {
background-color: rgba(255, 255, 255, 10%);
transform: scale(175%);
}
.Icon-button:disabled {
.IconButton:disabled {
--color: var(--foreground-color-2);
}
.Icon-button svg {
.IconButton svg {
height: 100%;
}
.Icon-button svg path {
.IconButton svg path {
fill: var(--color);
transition: fill 100ms ease-in-out;
}
.Search-bar {
.SearchBar {
flex: 1;
padding: 0.25rem 0.5rem;
background-color: var(--background-color-2);

View file

@ -60,13 +60,13 @@ export function Browser({ startUrl, focus }: BrowserProps) {
}
};
return (<div className={styles.Container}>
return (<div className={styles.Browser}>
<div className={styles.Header}>
<div className={styles["Nav-bar"]}>
<div className={styles.NavBar}>
<button
title="Back"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => { undo(); }}
disabled={!undoAvailable}
>
@ -75,7 +75,7 @@ export function Browser({ startUrl, focus }: BrowserProps) {
<button
title="Forward"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => { redo(); }}
disabled={!redoAvailable}
>
@ -84,7 +84,7 @@ export function Browser({ startUrl, focus }: BrowserProps) {
<button
title="Reload"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={reload}
>
<FontAwesomeIcon icon={faRotateRight}/>
@ -92,7 +92,7 @@ export function Browser({ startUrl, focus }: BrowserProps) {
<button
title="Home"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => { updateUrl(HOME_URL); }}
>
<FontAwesomeIcon icon={faHome}/>
@ -101,13 +101,13 @@ export function Browser({ startUrl, focus }: BrowserProps) {
value={input}
type="text"
aria-label="Search bar"
className={styles["Search-bar"]}
className={styles.SearchBar}
tabIndex={0}
onChange={onInputChange as unknown as ChangeEventHandler}
onKeyDown={onKeyDown as unknown as KeyboardEventHandler}
/>
</div>
<div className={styles["Bookmarks"]}>
<div className={styles.Bookmarks}>
</div>
</div>

View file

@ -1,4 +1,4 @@
.Container {
.Calculator {
display: flex;
flex-direction: column;
height: 100%;
@ -39,7 +39,7 @@
padding: 0.5rem;
}
.Input-row {
.InputRow {
flex: 1;
display: flex;
gap: var(--button-gap);
@ -51,17 +51,17 @@
font-size: 1.5rem;
}
.Input-row:first-of-type .Button {
.InputRow:first-of-type .Button {
--normal-color: var(--background-color-1);
--hover-color: var(--background-color-2);
}
.Input-row .Button:last-of-type {
.InputRow .Button:last-of-type {
--text-color: var(--background-color-0);
--normal-color: var(--blue-0);
--hover-color: var(--blue-b);
--hover-color: var(--blue-1);
}
.Button-large {
.ButtonLarge {
min-width: calc(50% - var(--button-gap) / 2);
}

View file

@ -152,38 +152,38 @@ export function Calculator({ active }: WindowProps) {
if (operation != null)
calculation = `${firstNumber} ${operation} ${secondNumber != null ? secondNumber + " =" : ""}`;
return (<div className={styles.Container}>
return (<div className={styles.Calculator}>
<div className={styles.Output}>
<p className={styles.Calculation}>{calculation}</p>
<p className={styles.Preview}>{input ?? firstNumber}</p>
</div>
<div className={styles.Input}>
<div className={styles["Input-row"]}>
<div className={styles.InputRow}>
<Button className={styles.Button} onClick={reset}>C</Button>
<Button className={styles.Button} onClick={() => { addInput("-"); }}>+/-</Button>
<Button className={styles.Button} onClick={() => { addInput("%"); }}>%</Button>
<Button className={styles.Button} onClick={() => { changeOperation("÷"); }}>÷</Button>
</div>
<div className={styles["Input-row"]}>
<div className={styles.InputRow}>
<Button className={styles.Button} onClick={() => { addInput("7"); }}>7</Button>
<Button className={styles.Button} onClick={() => { addInput("8"); }}>8</Button>
<Button className={styles.Button} onClick={() => { addInput("9"); }}>9</Button>
<Button className={styles.Button} onClick={() => { changeOperation("×"); }}>×</Button>
</div>
<div className={styles["Input-row"]}>
<div className={styles.InputRow}>
<Button className={styles.Button} onClick={() => { addInput("4"); }}>4</Button>
<Button className={styles.Button} onClick={() => { addInput("5"); }}>5</Button>
<Button className={styles.Button} onClick={() => { addInput("6"); }}>6</Button>
<Button className={styles.Button} onClick={() => { changeOperation("-"); }}>-</Button>
</div>
<div className={styles["Input-row"]}>
<div className={styles.InputRow}>
<Button className={styles.Button} onClick={() => { addInput("1"); }}>1</Button>
<Button className={styles.Button} onClick={() => { addInput("2"); }}>2</Button>
<Button className={styles.Button} onClick={() => { addInput("3"); }}>3</Button>
<Button className={styles.Button} onClick={() => { changeOperation("+"); }}>+</Button>
</div>
<div className={styles["Input-row"]}>
<Button className={`${styles.Button} ${styles["Button-large"]}`} onClick={() => { addInput("0"); }}>0</Button>
<div className={styles.InputRow}>
<Button className={`${styles.Button} ${styles.ButtonLarge}`} onClick={() => { addInput("0"); }}>0</Button>
<Button className={styles.Button} onClick={() => { addInput("."); }}>.</Button>
<Button className={styles.Button} onClick={() => { calculate(); }}>=</Button>
</div>

View file

@ -1,4 +1,4 @@
.Container {
.FileExplorer {
--header-height: 3.5rem;
--sidebar-width: 10rem;
--footer-height: 1.75rem;
@ -20,7 +20,7 @@
background-color: var(--background-color-0);
}
.Icon-button {
.IconButton {
--color: var(--foreground-color-0);
position: relative;
@ -34,7 +34,7 @@
cursor: pointer;
}
.Icon-button::after {
.IconButton::after {
content: "";
position: absolute;
top: 0;
@ -48,25 +48,25 @@
transition: all 200ms ease-in-out;
}
.Icon-button:hover::after, .Icon-button:focus-visible::after {
.IconButton:hover::after, .IconButton:focus-visible::after {
background-color: rgba(255, 255, 255, 10%);
transform: scale(150%);
}
.Icon-button:disabled {
--color: var(--foreground-color-2);
.IconButton:disabled {
opacity: 0.75;
}
.Icon-button svg {
.IconButton svg {
height: 100%;
}
.Icon-button svg path {
.IconButton svg path {
fill: var(--color);
transition: fill 100ms ease-in-out;
}
.Path-input {
.PathInput {
flex: 1;
padding: 0.25rem 0.5rem;
background-color: var(--background-color-2);
@ -99,7 +99,7 @@
overflow: hidden;
}
.Nav-button {
.NavButton {
display: flex;
gap: 0.5rem;
align-items: center;
@ -113,11 +113,11 @@
transition: background-color 100ms ease-in-out;
}
.Nav-button:hover, .Nav-button:focus-visible {
.NavButton:hover, .NavButton:focus-visible {
background-color: color-mix(in srgb, var(--background-color-0) 75%, transparent);
}
.Nav-button svg {
.NavButton svg {
height: 1.35rem;
aspect-ratio: 1;
}

View file

@ -176,12 +176,12 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
const itemCount = currentDirectory.getItemCount(showHidden);
return (
<div className={!isSelector ? styles.Container : `${styles.Container} ${styles.Selector}`}>
<div className={!isSelector ? styles.FileExplorer : `${styles.FileExplorer} ${styles.Selector}`}>
<div className={styles.Header}>
<button
title="Back"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => { undo(); }}
disabled={!undoAvailable}
>
@ -190,7 +190,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
<button
title="Forward"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => { redo(); }}
disabled={!redoAvailable}
>
@ -199,7 +199,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
<button
title="Up"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => { changeDirectory(".."); }}
disabled={currentDirectory.isRoot}
>
@ -208,7 +208,7 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
<button
title="New"
tabIndex={0}
className={styles["Icon-button"]}
className={styles.IconButton}
onClick={() => {
// openWindowedModal({
// title: "Error",
@ -235,17 +235,17 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
value={path}
type="text"
aria-label="Path"
className={styles["Path-input"]}
className={styles.PathInput}
tabIndex={0}
onChange={onPathChange as unknown as ChangeEventHandler}
onKeyDown={onKeyDown as unknown as KeyboardEventHandler}
placeholder="Enter a path..."
/>
<ImportButton directory={currentDirectory}/>
<button title="Search" tabIndex={0} className={styles["Icon-button"]}>
<button title="Search" tabIndex={0} className={styles.IconButton}>
<FontAwesomeIcon icon={faSearch}/>
</button>
<button title="Settings" tabIndex={0} className={styles["Icon-button"]}>
<button title="Settings" tabIndex={0} className={styles.IconButton}>
<FontAwesomeIcon icon={faCog}/>
</button>
</div>
@ -281,14 +281,14 @@ export function FileExplorer({ startPath, selectorMode, Footer, onSelectionChang
</div>
{!isSelector
? <span className={styles.Footer}>
<p className={utilStyles["Text-light"]}>
<p className={utilStyles.TextLight}>
{itemCount === 1
? itemCount + " item"
: itemCount + " items"
}
</p>
</span>
: <div className={`${styles.Footer} ${styles["Selector-footer"]}`}>
: <div className={styles.Footer}>
<Footer/>
</div>
}

View file

@ -29,7 +29,7 @@ export default function ImportButton({ directory }: ImportButtonProps): ReactEle
});
};
return <label title="Import" tabIndex={0} className={styles["Icon-button"]}>
return <label title="Import" tabIndex={0} className={styles.IconButton}>
<input
type="file"
id="import"

View file

@ -6,7 +6,7 @@ export function QuickAccessButton({ onClick, icon, name }) {
return (
<button
tabIndex={0}
className={`${styles["Nav-button"]} ${utilStyles["Text-semibold"]}`}
className={`${styles.NavButton} ${utilStyles.TextSemibold}`}
onClick={onClick}
>
<FontAwesomeIcon icon={icon}/>

View file

@ -1,4 +1,4 @@
.Container {
.DirectoryList {
--scale: 1rem;
position: relative;
@ -6,7 +6,7 @@
height: 100%;
}
.File-button, .Folder-button {
.FileButton, .FolderButton {
--gap: 0.25rem;
display: flex;
@ -25,43 +25,43 @@
transition: background-color 100ms ease-in-out;
}
.File-button[data-selected=true],
.Folder-button[data-selected=true] {
.FileButton[data-selected=true],
.FolderButton[data-selected=true] {
background-color: color-mix(in srgb, var(--background-color-0) 40%, transparent) !important;
}
.File-button:hover,
.Folder-button:hover,
.File-button:focus-visible,
.Folder-button:focus-visible {
.FileButton:hover,
.FolderButton:hover,
.FileButton:focus-visible,
.FolderButton:focus-visible {
background-color: color-mix(in srgb, var(--background-color-0) 20%, transparent);
}
.File-button p, .Folder-button p {
.FileButton p, .FolderButton p {
max-width: 100%;
margin: 0;
word-wrap: break-word;
}
.File-icon, .Folder-icon {
.FileIcon, .FolderIcon {
max-height: calc(100% - 1rem - var(--gap));
border-radius: inherit;
}
.File-icon div, .Folder-icon div {
.FileIcon div, .FolderIcon div {
width: 100%;
height: 100%;
border-radius: inherit;
overflow: hidden;
}
.File-icon svg, .Folder-icon svg {
.FileIcon svg, .FolderIcon svg {
width: 50%;
height: auto;
aspect-ratio: 1;
}
.Selection-rect {
.SelectionRect {
opacity: 25%;
position: absolute;
border-radius: var(--border-radius-0);

View file

@ -129,9 +129,9 @@ export function DirectoryList({ directory, showHidden = false, folderClassName,
return { top: y, left: x, width, height };
};
const classNames = [styles.Container];
const folderClassNames = [styles["Folder-button"]];
const fileClassNames = [styles["File-button"]];
const classNames = [styles.DirectoryList];
const folderClassNames = [styles.FolderButton];
const fileClassNames = [styles.FileButton];
if (className)
classNames.push(className);
@ -148,7 +148,7 @@ export function DirectoryList({ directory, showHidden = false, folderClassName,
{...props}
>
{rectSelectStart != null && rectSelectEnd != null
? <div className={styles["Selection-rect"]} style={getRectSelectStyle()}/>
? <div className={styles.SelectionRect} style={getRectSelectStyle()}/>
: null
}
{directory?.getSubFolders(showHidden)?.map((folder) =>
@ -167,7 +167,7 @@ export function DirectoryList({ directory, showHidden = false, folderClassName,
onOpenFolder?.(event, folder);
}}
>
<div className={styles["Folder-icon"]}>
<div className={styles.FolderIcon}>
<ImagePreview source={folder.getIconUrl()} onError={() => { folder.setIconUrl(null); }}/>
</div>
<p>{folder.name}</p>
@ -189,7 +189,7 @@ export function DirectoryList({ directory, showHidden = false, folderClassName,
onOpenFile?.(event, file);
}}
>
<div className={styles["File-icon"]}>
<div className={styles.FileIcon}>
<ImagePreview source={file.getIconUrl()} onError={() => { file.setIconUrl(null); }}/>
</div>
<p>{file.id}</p>

View file

@ -1,16 +1,16 @@
.Image-preview {
.ImagePreview {
height: 100%;
overflow: hidden;
pointer-events: none;
}
.Image-preview > * {
.ImagePreview > * {
width: 100%;
height: 100%;
object-fit: contain;
}
.Image-preview > img {
.ImagePreview > img {
height: auto;
max-height: 100%;
width: auto;

View file

@ -18,7 +18,7 @@ export function ImagePreview({ source, className, onError, ...props }: ImagePrev
onError?.();
};
const classNames = [styles["Image-preview"]];
const classNames = [styles.ImagePreview];
if (className != null)
classNames.push(className);

View file

@ -1,4 +1,4 @@
.Container {
.CircuitView {
display: flex;
width: 100%;
flex-grow: 1;

View file

@ -28,7 +28,7 @@ export function CircuitView() {
<ClickAction label="NOT gate" onTrigger={() => { circuit.addChip(ChipsManager.CHIPS.NOT); }}/>
</DropdownAction>
</HeaderMenu>
<div className={styles.Container}>
<div className={styles.CircuitView}>
<canvas ref={canvasRef} className={styles.Canvas}/>
</div>
</>;

View file

@ -1,4 +1,4 @@
.Container {
.LogicSim {
display: flex;
flex-direction: column;
width: 100%;

View file

@ -2,7 +2,7 @@ import { CircuitView } from "./CircuitView";
import styles from "./LogicSim.module.css";
export function LogicSim() {
return <div className={styles.Container}>
return <div className={styles.LogicSim}>
<CircuitView/>
</div>;
}

View file

@ -1,4 +1,4 @@
.Container {
.MediaViewer {
display: flex;
justify-content: center;
align-items: center;
@ -7,7 +7,7 @@
padding: 2rem;
}
.Container img {
.MediaViewer img {
width: 100%;
height: 100%;
object-fit: contain;

View file

@ -34,7 +34,7 @@ export function MediaViewer({ file, close, setTitle }: MediaViewerProps) {
return (<p>File failed to load.</p>);
return (
<div className={styles.Container}>
<div className={styles.MediaViewer}>
<img src={file.source} alt={file.id} draggable="false"/>
</div>
);

View file

@ -1,4 +1,4 @@
.Container {
.Settings {
--tabs-width: 10rem;
flex: 1;
@ -23,7 +23,7 @@
overflow: hidden;
}
.Tab-button {
.TabButton {
display: flex;
gap: 0.5rem;
align-items: center;
@ -34,25 +34,25 @@
font-size: 0.875rem;
}
.Tab-button.Active-tab {
.TabButton.ActiveTab {
background-color: var(--background-color-0);
}
.Tab-button:not(.Active-tab):hover,
.Tab-button:not(.Active-tab):focus-visible {
.TabButton:not(.ActiveTab):hover,
.TabButton:not(.ActiveTab):focus-visible {
background-color: color-mix(in srgb, var(--background-color-0) 50%, transparent);
}
.Tab-button > svg {
.TabButton > svg {
height: 1.35rem;
aspect-ratio: 1;
}
.Tab-button > p {
.TabButton > p {
margin: 0;
}
.Tab-panel {
.TabPanel {
flex: 1;
display: flex;
flex-direction: column;
@ -63,7 +63,7 @@
overflow: auto;
}
.Tab-panel:not(.Active-panel) {
.TabPanel:not(.ActivePanel) {
display: none;
}
@ -77,15 +77,15 @@
padding-bottom: 1rem;
}
.Option-list {
.OptionList {
gap: 0.5rem;
}
.Option-list .Option {
.OptionList .Option {
padding-bottom: 0;
}
.Option-horizontal {
.OptionHorizontal {
flex-direction: row;
justify-content: space-between;
}
@ -105,28 +105,28 @@
display: flex;
}
.Image-select {
.ImageSelect {
min-width: 10rem;
max-width: 100%;
}
.Image-select > input {
.ImageSelect > input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.Image-select > input:hover + img,
.Image-select > input:focus-visible + img {
.ImageSelect > input:hover + img,
.ImageSelect > input:focus-visible + img {
filter: brightness(110%);
}
.Image-select > input:checked + img {
.ImageSelect > input:checked + img {
outline: 0.25em solid var(--foreground-color-0);
}
.Image-select > img {
.ImageSelect > img {
width: 100%;
height: 100%;
border-radius: var(--border-radius-1);
@ -134,14 +134,14 @@
cursor: pointer;
}
.Button-group {
.ButtonGroup {
display: flex;
gap: 1rem;
flex-wrap: wrap;
margin: 0.75rem 0;
}
.Button-group .Button {
.ButtonGroup .Button {
margin-bottom: 0;
}
@ -154,13 +154,13 @@
border-radius: var(--border-radius-1);
}
.Button-red {
.ButtonDanger {
--text-color: var(--black-1) !important;
--normal-color: var(--red-0) !important;
--hover-color: var(--red-1) !important;
}
.Icon-button {
.IconButton {
--color: var(--foreground-color-0);
position: relative;
@ -174,7 +174,7 @@
cursor: pointer;
}
.Icon-button::after {
.IconButton::after {
content: "";
position: absolute;
top: 0;
@ -188,34 +188,34 @@
transition: all 200ms ease-in-out;
}
.Icon-button:hover::after, .Icon-button:focus-visible::after {
.IconButton:hover::after, .IconButton:focus-visible::after {
background-color: rgba(255, 255, 255, 10%);
transform: scale(150%);
}
.Icon-button:disabled {
.IconButton:disabled {
--color: var(--foreground-color-2);
}
.Icon-button svg {
.IconButton svg {
height: 100%;
}
.Icon-button svg path {
.IconButton svg path {
fill: var(--color);
transition: fill 100ms ease-in-out;
}
.Progress-bar-container {
.ProgressBarContainer {
width: 100%;
max-width: 35rem;
}
.Progress-bar {
.ProgressBar {
width: 100% !important;
}
.Progress-bar-labels {
.ProgressBarLabels {
display: flex;
justify-content: space-between;
width: 100%;

View file

@ -17,38 +17,38 @@ export function Settings({ tab }: SettingsProps) {
return (
<Tabs
defaultIndex={tab ?? 0}
className={styles.Container}
selectedTabClassName={styles["Active-tab"]}
selectedTabPanelClassName={styles["Active-panel"]}
className={styles.Settings}
selectedTabClassName={styles.ActiveTab}
selectedTabPanelClassName={styles.ActivePanel}
>
<TabList className={styles.Tabs}>
<Tab className={styles["Tab-button"]} tabIndex="0">
<Tab className={styles.TabButton} tabIndex="0">
<FontAwesomeIcon icon={faShapes}/>
<p className={utilStyles["Text-semibold"]}>Apps</p>
<p className={utilStyles.TextSemibold}>Apps</p>
</Tab>
<Tab className={styles["Tab-button"]} tabIndex="0">
<Tab className={styles.TabButton} tabIndex="0">
<FontAwesomeIcon icon={faPalette}/>
<p className={utilStyles["Text-semibold"]}>Appearance</p>
<p className={utilStyles.TextSemibold}>Appearance</p>
</Tab>
<Tab className={styles["Tab-button"]} tabIndex="0">
<Tab className={styles.TabButton} tabIndex="0">
<FontAwesomeIcon icon={faHardDrive}/>
<p className={utilStyles["Text-semibold"]}>Storage</p>
<p className={utilStyles.TextSemibold}>Storage</p>
</Tab>
<Tab className={styles["Tab-button"]} tabIndex="0">
<Tab className={styles.TabButton} tabIndex="0">
<FontAwesomeIcon icon={faCircleInfo}/>
<p className={utilStyles["Text-semibold"]}>About</p>
<p className={utilStyles.TextSemibold}>About</p>
</Tab>
</TabList>
<TabPanel className={styles["Tab-panel"]}>
<TabPanel className={styles.TabPanel}>
<AppsSettings/>
</TabPanel>
<TabPanel className={styles["Tab-panel"]}>
<TabPanel className={styles.TabPanel}>
<AppearanceSettings/>
</TabPanel>
<TabPanel className={styles["Tab-panel"]}>
<TabPanel className={styles.TabPanel}>
<StorageTab/>
</TabPanel>
<TabPanel className={styles["Tab-panel"]}>
<TabPanel className={styles.TabPanel}>
<AboutSettings/>
</TabPanel>
</Tabs>

View file

@ -11,12 +11,12 @@ export function AboutSettings() {
const virtualRoot = useVirtualRoot();
return (<>
<div className={styles["Option"]}>
<p className={styles["Label"]}>About {NAME}</p>
<p className={utilStyles["Text-light"]}>{NAME} is a web-based operating system inspired by Ubuntu Linux and Windows made with React.js by Prozilla.</p>
<div className={styles["Button-group"]}>
<div className={styles.Option}>
<p className={styles.Label}>About {NAME}</p>
<p className={utilStyles.TextLight}>{NAME} is a web-based operating system inspired by Ubuntu Linux and Windows made with React.js by Prozilla.</p>
<div className={styles.ButtonGroup}>
<Button
className={`${styles.Button} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${utilStyles.TextBold}`}
onClick={(event: Event) => {
event.preventDefault();
windowsManager.open("text-editor", {
@ -29,7 +29,7 @@ export function AboutSettings() {
Open info.md
</Button>
<Button
className={`${styles.Button} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${utilStyles.TextBold}`}
href="https://github.com/Prozilla/ProzillaOS"
>
View source

View file

@ -41,28 +41,28 @@ export function AppOption({ app, pins, setPins: _setPins }: AppOptionProps) {
</Actions>
});
return <div className={`${styles["Option"]} ${styles["Option-horizontal"]}`}>
<span className={styles["Label"]}>
<ImagePreview className={styles["Icon"]} source={AppsManager.getAppIconUrl(app.id)}/>
return <div className={`${styles.Option} ${styles.OptionHorizontal}`}>
<span className={styles.Label}>
<ImagePreview className={styles.Icon} source={AppsManager.getAppIconUrl(app.id)}/>
{app.name}
</span>
<button className={styles["Icon-button"]} onClick={onContextMenu}>
<button className={styles.IconButton} onClick={onContextMenu}>
<FontAwesomeIcon icon={faEllipsisVertical}/>
</button>
{/* <div className={styles["Button-group"]}>
<Button
className={`${styles.Button} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${utilStyles.TextBold}`}
onClick={() => windowsManager.open(id)}
>
Launch
</Button>
<Button
className={`${styles.Button} ${styles["Button-red"]} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${styles["Button-red"]} ${utilStyles.TextBold}`}
>
Uninstall
</Button>
<Button
className={`${styles.Button} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${utilStyles.TextBold}`}
onClick={() => {
const newPins = [...pins];
if (isPinned) {

View file

@ -40,20 +40,20 @@ export function AppearanceSettings() {
};
return (<>
<div className={styles["Option"]}>
<p className={styles["Label"]}>Theme</p>
<div className={styles["Input"]}>
<select className={styles["Dropdown"]} aria-label="theme" value={theme} onChange={onThemeChange as unknown as ChangeEventHandler}>
<div className={styles.Option}>
<p className={styles.Label}>Theme</p>
<div className={styles.Input}>
<select className={styles.Dropdown} aria-label="theme" value={theme} onChange={onThemeChange as unknown as ChangeEventHandler}>
{Object.entries(THEMES).map(([key, value]) =>
<option key={key} value={key}>{value}</option>
)}
</select>
</div>
</div>
<div className={styles["Option"]}>
<p className={styles["Label"]}>Wallpaper</p>
<div className={styles.Option}>
<p className={styles.Label}>Wallpaper</p>
<Button
className={`${styles.Button} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${utilStyles.TextBold}`}
onClick={() => {
openWindowedModal({
size: DEFAULT_FILE_SELECTOR_SIZE,
@ -70,9 +70,9 @@ export function AppearanceSettings() {
>
Browse
</Button>
<div className={styles["Input"]}>
<div className={styles.Input}>
{(virtualRoot.navigate(WALLPAPERS_PATH) as VirtualFolder)?.getFiles()?.reverse().map(({ id, source }) =>
<label className={styles["Image-select"]} key={id}>
<label className={styles.ImageSelect} key={id}>
<input
type="radio"
value={source}

View file

@ -16,8 +16,8 @@ export function AppsSettings() {
});
}, [settingsManager]);
return <div className={`${styles["Option"]} ${styles["Option-list"]}`}>
<p className={styles["Label"]}>Apps</p>
return <div className={`${styles.Option} ${styles.OptionList}`}>
<p className={styles.Label}>Apps</p>
{AppsManager.APPS.sort((a, b) =>
a.name.toLowerCase().localeCompare(b.name.toLowerCase())
).map((app) =>

View file

@ -17,18 +17,18 @@ export function StorageTab() {
const freeKB = maxKB - usedKB;
return (<>
<div className={`${styles["Option"]} ${styles["Progress-bar-container"]}`}>
<p className={styles["Label"]}>Virtual Drive ({round(maxKB, 1)} KB)</p>
<ProgressBar fillPercentage={usedKB / maxKB * 100} className={styles["Progress-bar"]}/>
<span className={styles["Progress-bar-labels"]}>
<p className={utilStyles["Text-light"]}>{round(usedKB, 1)} KB used</p>
<p className={utilStyles["Text-light"]}>{round(freeKB, 1)} KB free</p>
<div className={`${styles.Option} ${styles.ProgressBarContainer}`}>
<p className={styles.Label}>Virtual Drive ({round(maxKB, 1)} KB)</p>
<ProgressBar fillPercentage={usedKB / maxKB * 100} className={styles.ProgressBar}/>
<span className={styles.ProgressBarLabels}>
<p className={utilStyles.TextLight}>{round(usedKB, 1)} KB used</p>
<p className={utilStyles.TextLight}>{round(freeKB, 1)} KB free</p>
</span>
</div>
<div className={styles["Option"]}>
<p className={styles["Label"]}>Manage data</p>
<div className={styles.Option}>
<p className={styles.Label}>Manage data</p>
<Button
className={`${styles.Button} ${styles["Button-red"]} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${styles.ButtonDanger} ${utilStyles.TextBold}`}
onClick={() => { virtualRoot.reset?.(); }}
>
Reset

View file

@ -1,4 +1,4 @@
.Container {
.TextEditor {
display: flex;
flex-direction: column;
width: 100%;
@ -6,10 +6,10 @@
text-align: start;
}
.Container p,
.Container div,
.Container span,
.Container textarea {
.TextEditor p,
.TextEditor div,
.TextEditor span,
.TextEditor textarea {
font-size: 1em;
}

View file

@ -126,7 +126,7 @@ export function TextEditor({ file, setTitle, setIconUrl, close, mode, app, modal
}
return (
<div className={styles.Container} style={{ fontSize: zoom }}>
<div className={styles.TextEditor} style={{ fontSize: zoom }}>
<HeaderMenu>
<DropdownAction label="File" showOnHover={false}>
<ClickAction label="New" onTrigger={() => { newText(); }} shortcut={["Control", "e"]}/>

View file

@ -1,4 +1,4 @@
.Container {
.Desktop {
position: fixed;
top: 0;
left: 0;

View file

@ -134,7 +134,7 @@ export const Desktop = memo(() => {
return (<>
<ShortcutsListener/>
<div
className={styles.Container}
className={styles.Desktop}
onContextMenu={onContextMenu}
>
{showIcons && <DirectoryList

View file

@ -1,4 +1,4 @@
.Container {
.ModalView {
--position-x: 0;
--position-y: 0;
@ -7,7 +7,7 @@
left: calc(var(--position-x) * 1px);
}
.Container > * {
.ModalView > * {
background: var(--background-color-0);
border-radius: var(--border-radius-1);
box-shadow: var(--window-box-shadow);

View file

@ -33,7 +33,7 @@ export const ModalView: FC<ModalProps> = memo(({ modal }) => {
}, [modal]);
const Container = () => (<div
className={styles.Container}
className={styles.ModalView}
style={{ "--position-x": modal.position.x, "--position-y": modal.position.y } as CSSProperties}
>
<modal.element modal={modal} {...modal.props}/>

View file

@ -1,4 +1,4 @@
.Container {
.ModalsView {
position: relative;
z-index: 11;
}

View file

@ -23,7 +23,7 @@ export const ModalsView = memo(() => {
modalsManager.containerRef = ref;
}, [modalsManager, ref]);
return <div ref={ref} className={styles.Container}>
return <div ref={ref} className={styles.ModalsView}>
{sortedModals?.map((modal) =>
<ModalView key={modal.id} modal={modal}/>
)}

View file

@ -1,4 +1,4 @@
.Container {
.WindowedModal {
--header-height: 2.5rem;
--header-button-hover-color: rgba(255, 255, 255, 5%);

View file

@ -51,7 +51,7 @@ export function WindowedModal({ modal, params, children, ...props }: ModalProps)
nodeRef={nodeRef}
>
<div
className={styles.Container}
className={styles.WindowedModal}
ref={nodeRef}
style={{
width: modal.size.x,
@ -63,7 +63,7 @@ export function WindowedModal({ modal, params, children, ...props }: ModalProps)
className={styles["Window-icon"]}
src={iconUrl}
/>
<p className={utilStyles["Text-semibold"]}>{title}</p>
<p className={utilStyles.TextSemibold}>{title}</p>
<button aria-label="Close" className={`${styles["Header-button"]} ${styles["Exit-button"]}`} tabIndex={0}
onClick={() => { modal.close(); }}>
<FontAwesomeIcon icon={faXmark}/>

View file

@ -1,86 +1,4 @@
.Container {
--header-height: 2.5rem;
--header-button-hover-color: rgba(255, 255, 255, 5%);
position: absolute;
display: flex;
flex-direction: column;
min-width: 300px;
min-height: 150px;
background-color: var(--background-color-2) !important;
resize: both;
overflow: hidden;
}
.Header {
--dialog-icon-size: 1.5rem;
--dialog-icon-margin: 0.75rem;
display: flex;
align-items: center;
height: var(--header-height);
padding: 0.25rem;
padding-left: var(--dialog-icon-margin);
padding-right: 0;
background-color: var(--background-color-1);
cursor: grab;
}
.Dialog-icon,
.Dialog-icon > div,
.Dialog-icon > div > svg {
height: 100%;
width: auto;
}
.Dialog-icon {
height: var(--dialog-icon-size);
margin-right: calc(var(--dialog-icon-margin) - 0.1rem);
}
.Dialog-icon > div {
display: flex;
align-items: center;
}
.Header > p {
user-select: none;
width: auto;
margin: 0;
margin-right: auto;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.Header-button {
display: flex;
align-items: center;
justify-content: center;
height: var(--header-height);
margin: 0;
padding: 0.75rem;
color: var(--foreground-color-0);
background: none;
cursor: pointer;
border: none;
outline: none;
aspect-ratio: 1;
}
.Header-button > svg {
height: 100%;
}
.Exit-button {
--header-button-hover-color: var(--red-0);
}
.Header-button:hover, .Header-button:focus-visible {
background-color: var(--header-button-hover-color);
}
.Dialog-content {
.DialogContent {
position: relative;
display: flex;
flex-direction: column;
@ -91,7 +9,7 @@
overflow: hidden;
}
.Dialog-content button {
.DialogContent button {
margin-bottom: 0.75rem;
padding: 0.5rem 1rem;
width: fit-content;
@ -104,6 +22,6 @@
cursor: pointer;
}
.Dialog-content button:hover, .Dialog-content button:focus-visible {
.DialogContent button:hover, .DialogContent button:focus-visible {
background-color: var(--background-color-1);
}

View file

@ -16,7 +16,7 @@ export function DialogBox({ modal, params, children, ...props }: ModalProps) {
};
return <WindowedModal modal={modal} params={params} onClick={onClick} {...props}>
<div className={styles["Dialog-content"]}>
<div className={styles.DialogContent}>
{children}
</div>
</WindowedModal>;

View file

@ -1,4 +1,4 @@
.Container {
.FileProperties {
display: flex;
flex-direction: column;
justify-content: flex-start;
@ -65,7 +65,7 @@
text-indent: -2rem;
}
.App-icon {
.AppIcon {
display: inline-block;
width: 2rem;
height: 2rem;
@ -74,14 +74,14 @@
margin-right: 0.25rem;
}
.App-icon div {
.AppIcon div {
position: relative;
width: inherit;
height: inherit;
object-fit: contain;
}
.App-icon svg {
.AppIcon svg {
position: absolute;
top: 0;
left: 0;

View file

@ -14,26 +14,26 @@ interface FilePropetiesProps extends ModalProps {
export function FileProperties({ modal, params, file, ...props }: FilePropetiesProps) {
const associatedApp = AppsManager.getAppByFileExtension(file.extension);
return <WindowedModal className={styles.Container} modal={modal} params={params} {...props}>
<span className={styles["Section"]}>
<ImagePreview className={styles["Icon"]} source={file.getIconUrl()}/>
<p className={`${styles["Line"]} ${utilStyles["Text-bold"]}`}>{file.id}</p>
return <WindowedModal className={styles.FileProperties} modal={modal} params={params} {...props}>
<span className={styles.Section}>
<ImagePreview className={styles.Icon} source={file.getIconUrl()}/>
<p className={`${styles.Line} ${utilStyles.TextBold}`}>{file.id}</p>
</span>
<span className={styles["Section"]}>
<p className={styles["Line"]}>Type: {file.getType()}</p>
<span className={styles["Line"]}>
<span className={styles.Section}>
<p className={styles.Line}>Type: {file.getType()}</p>
<span className={styles.Line}>
Opens with:
<ImagePreview className={styles["App-icon"]} source={AppsManager.getAppIconUrl(associatedApp.id)}/>
<ImagePreview className={styles.AppIcon} source={AppsManager.getAppIconUrl(associatedApp.id)}/>
{associatedApp.name}
</span>
</span>
<span className={styles["Section"]}>
<p className={styles["Line"]}>Location: {file.path}</p>
<p className={styles["Line"]}>Size: {StorageManager.getByteSize(file.source ?? file.content)} bytes</p>
<p className={styles["Line"]}>Size on drive: {StorageManager.getByteSize(file.toString())} bytes</p>
<span className={styles.Section}>
<p className={styles.Line}>Location: {file.path}</p>
<p className={styles.Line}>Size: {StorageManager.getByteSize(file.source ?? file.content)} bytes</p>
<p className={styles.Line}>Size on drive: {StorageManager.getByteSize(file.toString())} bytes</p>
</span>
<span className={styles["Section"]}>
<p className={styles["Line"]}>Attributes: {file.isProtected ? "Protected" : "N/A"}</p>
<span className={styles.Section}>
<p className={styles.Line}>Attributes: {file.isProtected ? "Protected" : "N/A"}</p>
</span>
</WindowedModal>;
}

View file

@ -1,4 +1,4 @@
.Container {
.Share {
display: flex;
flex-direction: column;
justify-content: space-between;
@ -8,14 +8,14 @@
overflow-y: auto;
}
.Container > div {
.Share > div {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: flex-start;
}
.Container > div:last-child {
.Share > div:last-child {
gap: 0.5rem;
flex-direction: row;
justify-content: space-between;

View file

@ -82,7 +82,7 @@ export function Share({ modal, params, ...props }: ModalProps) {
});
};
return <WindowedModal className={styles.Container} modal={modal} params={{
return <WindowedModal className={styles.Share} modal={modal} params={{
...params,
title: "Share",
iconUrl: ModalsManager.getModalIconUrl("share"),
@ -122,9 +122,9 @@ export function Share({ modal, params, ...props }: ModalProps) {
</form>
</div>
<div>
<p className={`${styles.Url} ${utilStyles["Text-light"]}`}>{url}</p>
<p className={`${styles.Url} ${utilStyles.TextLight}`}>{url}</p>
<Button
className={`${styles.Button} ${utilStyles["Text-bold"]}`}
className={`${styles.Button} ${utilStyles.TextBold}`}
onClick={() => {
copyToClipboard(url, () => {
alert({

View file

@ -48,25 +48,25 @@
background-color: var(--taskbar-button-hover-color);
}
.Home-container,
.Search-container {
.HomeContainer,
.SearchContainer {
position: relative;
padding: 0 !important;
}
.Home-button * {
.HomeButton * {
fill: var(--foreground-color-0);
filter: none;
}
.Menu-icons,
.App-icons-inner {
.MenuIcons,
.AppIcons {
display: flex;
align-items: center;
height: 100%;
}
.App-icons {
.AppIconsContainer {
position: relative;
width: 100%;
height: auto;
@ -74,7 +74,7 @@
pointer-events: none;
}
.App-icons-inner {
.AppIcons {
position: absolute;
top: 0;
left: 0;
@ -86,30 +86,30 @@
z-index: -1;
}
.App-icons-inner::-webkit-scrollbar {
.AppIcons::-webkit-scrollbar {
display: none;
}
.App-icons-inner > *,
.Menu-button {
.AppIcons > *,
.MenuButton {
height: 100%;
padding: 0.75rem;
}
.App-icons-inner > * > svg,
.Menu-button > svg {
.AppIcons > * > svg,
.MenuButton > svg {
height: 1.25rem;
}
.App-icons-inner div,
.App-icons-inner div > svg,
.Menu-icons div,
.Menu-icons div > svg {
.AppIcons div,
.AppIcons div > svg,
.MenuIcons div,
.MenuIcons div > svg {
height: 100%;
width: auto;
}
.Util-icons {
.UtilIcons {
display: flex;
gap: 0;
justify-content: flex-end;
@ -120,22 +120,22 @@
z-index: -1;
}
.Util-icons > div {
.UtilIcons > div {
height: 100%;
width: min-content;
z-index: -1;
}
.Util-icons > button,
.Util-icons > div > button {
.UtilIcons > button,
.UtilIcons > div > button {
height: 100%;
width: min-content;
margin: 0;
padding: 0.4rem;
}
.Util-icons > button > svg,
.Util-icons > div > button > svg {
.UtilIcons > button > svg,
.UtilIcons > div > button > svg {
height: 1rem;
width: 1rem;
aspect-ratio: 1;
@ -149,7 +149,7 @@
margin-left: 1rem;
}
.Menu-outer {
.MenuContainer {
position: absolute;
right: 0;
bottom: 100%;
@ -157,11 +157,11 @@
overflow: hidden;
}
.Menu-outer:not(.Active) {
.MenuContainer:not(.Active) {
pointer-events: none;
}
.Menu-inner {
.Menu {
opacity: 1;
display: flex;
border-top-left-radius: var(--border-radius-1);
@ -173,13 +173,13 @@
resize: horizontal;
}
.Menu-outer:not(.Active) .Menu-inner {
.MenuContainer:not(.Active) .Menu {
opacity: 0;
transform: translateY(100px);
}
.Menu-inner::after,
.Menu-outer::after {
.Menu::after,
.MenuContainer::after {
content: "";
position: absolute;
top: 0;
@ -190,20 +190,20 @@
pointer-events: none;
}
.Menu-inner::after {
.Menu::after {
opacity: var(--taskbar-menu-opacity);
background-color: var(--taskbar-menu-color);
z-index: -2;
}
.Menu-outer::after {
.MenuContainer::after {
opacity: 1;
transition: opacity 200ms ease-in-out, top 200ms ease-in-out;
backdrop-filter: var(--taskbar-filter);
z-index: -3;
}
.Menu-outer:not(.Active)::after {
.MenuContainer:not(.Active)::after {
opacity: 0;
top: 100px;
}

View file

@ -129,20 +129,20 @@ export const Taskbar = memo(() => {
return (<>
<div
style={{ "--taskbar-height": `${TASKBAR_HEIGHT}px`, zIndex } as CSSProperties}
className={styles["Taskbar"]}
className={styles.Taskbar}
data-allow-context-menu={true}
onContextMenu={(event) => {
if ((event.target as HTMLElement).getAttribute("data-allow-context-menu"))
onContextMenu(event);
}}
>
<div className={styles["Menu-icons"]}>
<div className={styles["Home-container"]}>
<div className={styles.MenuIcons}>
<div className={styles.HomeContainer}>
<OutsideClickListener onOutsideClick={() => { updateShowHome(false); }}>
<button
title="Home"
tabIndex={0}
className={`${styles["Menu-button"]} ${styles["Home-button"]}`}
className={`${styles.MenuButton} ${styles.HomeButton}`}
onClick={() => { updateShowHome(!showHome); }}
>
<ReactSVG src={"/icon.svg"}/>
@ -150,12 +150,12 @@ export const Taskbar = memo(() => {
<HomeMenu active={showHome} setActive={updateShowHome} search={search}/>
</OutsideClickListener>
</div>
<div className={styles["Search-container"]}>
<div className={styles.SearchContainer}>
<OutsideClickListener onOutsideClick={() => { updateShowSearch(false); }}>
<button
title="Search"
tabIndex={0}
className={`${styles["Menu-button"]} ${styles["Search-button"]}`}
className={styles.MenuButton}
onClick={() => { updateShowSearch(!showSearch); }}
>
<FontAwesomeIcon icon={faSearch}/>
@ -170,9 +170,9 @@ export const Taskbar = memo(() => {
</OutsideClickListener>
</div>
</div>
<div className={styles["App-icons"]} data-allow-context-menu={true} style={{ boxShadow }}>
<div className={styles.AppIconsContainer} data-allow-context-menu={true} style={{ boxShadow }}>
<div
className={styles["App-icons-inner"]}
className={styles.AppIcons}
data-allow-context-menu={true}
onScroll={onUpdate as unknown as UIEventHandler}
onResize={onUpdate as unknown as ReactEventHandler}
@ -181,7 +181,7 @@ export const Taskbar = memo(() => {
{apps}
</div>
</div>
<div className={styles["Util-icons"]}>
<div className={styles.UtilIcons}>
<Battery showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>
<Network showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>
<Volume showUtilMenu={showUtilMenu} hideUtilMenus={hideUtilMenus}/>

View file

@ -1,4 +1,4 @@
.App-list {
.AppList {
--scrollbar-color: rgba(0, 0, 0, 25%);
display: flex;
flex-direction: column;
@ -8,7 +8,7 @@
z-index: 0;
}
.App-button {
.AppButton {
display: flex;
gap: 1rem;
width: 100%;
@ -16,18 +16,18 @@
border-radius: var(--border-radius-1);
}
.App-button > div,
.App-button > div > div,
.App-button > div > div > svg {
.AppButton > div,
.AppButton > div > div,
.AppButton > div > div > svg {
width: 2rem;
height: 2rem;
}
.App-button svg {
.AppButton svg {
filter: var(--svg-drop-shadow-0);
}
.App-button > p, .App-button > h2 {
.AppButton > p, .AppButton > h2 {
margin: 0;
white-space: nowrap;
}

View file

@ -1,9 +1,9 @@
.Container-outer {
.HomeMenuContainer {
right: unset;
left: 0;
}
.Container-inner {
.HomeMenu {
min-width: 16rem;
max-width: 32rem;
border-bottom-left-radius: 0;

View file

@ -30,7 +30,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
setTabIndex(active ? 0 : -1);
}, [active]);
const classNames = [styles["Container-outer"], taskbarStyles["Menu-outer"]];
const classNames = [styles.HomeMenuContainer, taskbarStyles.MenuContainer];
if (active)
classNames.push(taskbarStyles.Active);
@ -62,7 +62,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
return (
<div className={classNames.join(" ")}>
<div className={`${styles["Container-inner"]} ${taskbarStyles["Menu-inner"]}`}>
<div className={`${styles.HomeMenu} ${taskbarStyles.Menu}`}>
<div className={styles.Buttons}>
<button title="Shut Down" tabIndex={tabIndex} onClick={() => { closeViewport(true); }}>
<FontAwesomeIcon icon={faPowerOff}/>
@ -97,12 +97,12 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
</button>
</div>
<div className={styles.Apps}>
<h1 className={utilStyles["Text-bold"]}>{NAME}</h1>
<div className={appStyles["App-list"]}>
<h1 className={utilStyles.TextBold}>{NAME}</h1>
<div className={appStyles.AppList}>
{AppsManager.APPS.map(({ name, id }) =>
<button
key={id}
className={appStyles["App-button"]}
className={appStyles.AppButton}
tabIndex={tabIndex}
onClick={() => {
setActive(false);
@ -111,7 +111,7 @@ export function HomeMenu({ active, setActive, search }: HomeMenuProps) {
title={name}
>
<ReactSVG src={AppsManager.getAppIconUrl(id)}/>
<h2 className={utilStyles["Text-regular"]}>{name}</h2>
<h2 className={utilStyles.TextRegular}>{name}</h2>
</button>
)}
</div>

View file

@ -1,4 +1,4 @@
.Container-outer {
.SearchMenuContainer {
position: absolute;
display: flex;
flex-direction: column;
@ -9,7 +9,7 @@
overflow: hidden;
}
.Container-inner {
.SearchMenu {
opacity: 1;
display: flex;
gap: 0.5rem;
@ -27,7 +27,7 @@
resize: horizontal;
}
.Container-inner::after {
.SearchMenu::after {
opacity: var(--taskbar-menu-opacity);
content: "";
position: absolute;
@ -39,16 +39,16 @@
z-index: -2;
}
.Container-outer:not(.Active) {
.SearchMenuContainer:not(.Active) {
pointer-events: none;
}
.Container-outer:not(.Active) .Container-inner {
.SearchMenuContainer:not(.Active) .SearchMenu {
opacity: 0;
transform: translateY(100px);
}
.Container-inner > div {
.SearchMenu > div {
margin-top: auto;
}
@ -64,6 +64,6 @@
font-size: inherit;
}
.Container-inner > div > button:first-child {
.SearchMenu > div > button:first-child {
background-color: var(--taskbar-button-hover-color);
}

View file

@ -44,7 +44,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
setSearchQuery(value);
};
const classNames = [styles["Container-outer"]];
const classNames = [styles.SearchMenuContainer];
if (active && apps)
classNames.push(styles.Active);
@ -66,7 +66,7 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
return (
<div className={classNames.join(" ")}>
<div className={styles["Container-inner"]}>
<div className={styles.SearchMenu}>
<input
ref={inputRef}
className={styles.Input}
@ -77,11 +77,11 @@ export function SearchMenu({ active, setActive, searchQuery, setSearchQuery, inp
spellCheck={false}
placeholder="Search..."
/>
<div className={appStyles["App-list"]}>
<div className={appStyles.AppList}>
{apps?.map(({ name, id }) =>
<button
key={id}
className={appStyles["App-button"]}
className={appStyles.AppButton}
tabIndex={tabIndex}
onClick={() => {
setActive(false);

View file

@ -1,4 +1,4 @@
.Container-outer {
.UtilMenuContainer {
position: absolute;
right: 0;
bottom: 100%;
@ -6,7 +6,7 @@
overflow: hidden;
}
.Container-inner {
.UtilMenu {
opacity: 1;
display: flex;
border-top-left-radius: var(--border-radius-1);
@ -19,7 +19,7 @@
resize: horizontal;
}
.Container-inner::after {
.UtilMenu::after {
opacity: var(--taskbar-menu-opacity);
content: "";
position: absolute;
@ -31,11 +31,11 @@
z-index: -2;
}
.Container-outer:not(.Active) {
.UtilMenuContainer:not(.Active) {
pointer-events: none;
}
.Container-outer:not(.Active) .Container-inner {
.UtilMenuContainer:not(.Active) .UtilMenu {
opacity: 0;
transform: translateY(100px);
}

View file

@ -9,14 +9,14 @@ interface UtilMenuProps {
}
export function UtilMenu({ active, setActive: _setActive, className, children }: UtilMenuProps) {
const classNames = [styles["Container-outer"]];
const classNames = [styles.UtilMenuContainer];
if (active)
classNames.push(styles.Active);
if (className != null)
classNames.push(className);
return (<div className={classNames.join(" ")}>
<div className={styles["Container-inner"]}>
<div className={styles.UtilMenu}>
{children}
</div>
</div>);

View file

@ -172,7 +172,7 @@ export const WindowView: FC<WindowProps> = memo(({ id, app, size, position, onIn
className={styles["Window-icon"]}
src={iconUrl}
/>
<p className={utilStyles["Text-semibold"]}>{title}</p>
<p className={utilStyles.TextSemibold}>{title}</p>
<button aria-label="Minimize" className={styles["Header-button"]} tabIndex={0} id="minimize-window"
onClick={toggleMinimized as MouseEventHandler}
>

View file

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

View file

@ -0,0 +1,15 @@
export function classNames(...args: string[]) {
let className = "";
for (let i = 0; i < args.length; i++) {
const value = args[i];
if (typeof value === "string") {
className += value + " ";
} else if (Array.isArray(value)) {
className += classNames(...value as string[]) + " ";
}
}
return className.trim();
}

View file

@ -0,0 +1,13 @@
import { formatRelativeTime } from "./date.utils";
export class TimeManager {
static startDate: Date;
static reset() {
TimeManager.startDate = new Date();
}
static getUptime(precision = 2) {
return formatRelativeTime(TimeManager.startDate, precision, false);
}
}

View file

@ -1,8 +1,7 @@
import { APPS } from "../../../../config/apps.config";
import { ANSI } from "../../../../config/apps/terminal.config";
import { ANSI_ASCII_LOGO, ANSI_LOGO_COLOR, NAME } from "../../../../config/branding.config";
import { START_DATE } from "../../../../index";
import { formatRelativeTime } from "../../../_utils/date.utils";
import { TimeManager } from "../../../_utils/time.utils";
import AppsManager from "../../appsManager";
import Command from "../command";
@ -36,7 +35,7 @@ export const neofetch = new Command()
`${ANSI.fg.cyan + username + ANSI.reset}@${ANSI.fg.cyan + hostname + ANSI.reset}`,
"-".repeat(rightColumnWidth),
formatLine("os", NAME),
formatLine("uptime", formatRelativeTime(START_DATE, 2, false)),
formatLine("uptime", TimeManager.getUptime(2)),
formatLine("resolution", window.innerWidth + "x" + window.innerHeight),
formatLine("theme", "default"),
formatLine("icons", "Font Awesome"),

View file

@ -1,5 +1,4 @@
import { START_DATE } from "../../../../index";
import { formatRelativeTime } from "../../../_utils/date.utils";
import { TimeManager } from "../../../_utils/time.utils";
import Command from "../command";
@ -8,5 +7,5 @@ export const uptime = new Command()
purpose: "Display the current uptime of the system"
})
.setExecute(() => {
return `Uptime: ${formatRelativeTime(START_DATE, 2, false)}`;
return `Uptime: ${TimeManager.getUptime(2)}`;
});

View file

@ -1,9 +1,10 @@
import { FC, MouseEvent, useCallback } from "react";
import Vector2 from "../../features/math/vector2";
import Modal from "../../features/modals/modal";
import { ActionsProps, STYLES } from "../../components/actions/Actions";
import { ActionsProps } from "../../components/actions/Actions";
import { useModalsManager } from "./modalsManagerContext";
import { ModalProps } from "../../components/modals/ModalView";
import { STYLES } from "../../config/actions.config";
interface UseContextMenuParams {
Actions: FC<ActionsProps>;

View file

@ -2,11 +2,11 @@ import React from "react";
import ReactDOM from "react-dom/client";
import "./styles/global.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { ASCII_LOGO, NAME } from "./config/branding.config";
import { TrackingManager } from "./features/tracking/trackingManager";
import { TimeManager } from "./features/_utils/time.utils";
export const START_DATE = new Date();
TimeManager.reset();
TrackingManager.initialize();
@ -20,9 +20,4 @@ root.render(<React.StrictMode>
const asciiLogoWidth = ASCII_LOGO.split("\n")[1].length;
const welcomeMessage = `Welcome to ${NAME}`;
const space = "\n\n" + " ".repeat(Math.ceil((asciiLogoWidth - welcomeMessage.length) / 2));
console.info(ASCII_LOGO + space + welcomeMessage);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals(() => {});
console.info(ASCII_LOGO + space + welcomeMessage);

View file

@ -1,15 +0,0 @@
import { ReportHandler } from "web-vitals";
const reportWebVitals = (onPerfEntry: ReportHandler) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
void import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View file

@ -1,22 +1,22 @@
.Text-regular {
.TextRegular {
font-size: 1em;
font-weight: 400;
letter-spacing: normal;
}
.Text-light {
.TextLight {
color: var(--foreground-color-1);
font-size: 0.875em;
font-weight: 400;
letter-spacing: normal;
}
.Text-semibold {
.TextSemibold {
font-weight: 500;
letter-spacing: normal;
}
.Text-bold {
.TextBold {
font-weight: 600;
letter-spacing: normal;
}