feat: begin work on Cluster creation modal

This commit is contained in:
LynithDev 2024-07-19 22:12:11 +02:00
parent 060f1faa10
commit 9c7b895d4c
7 changed files with 189 additions and 77 deletions

View file

@ -6,6 +6,7 @@ import ErrorBoundary from './components/ErrorBoundary';
import AnimatedRoutes from './components/AnimatedRoutes';
import NotificationOverlay from './components/overlay/notifications/NotificationOverlay';
import { SettingsProvider } from './hooks/useSettings';
import { ClusterModalController } from './components/overlay/cluster/ClusterCreationModal';
import { AppInfo } from '~utils/program-info';
function App(props: ParentProps) {
@ -15,28 +16,30 @@ function App(props: ParentProps) {
});
return (
<SettingsProvider>
<main class="flex flex-col bg-primary w-full min-h-screen overflow-hidden h-screen max-h-screen text-fg-primary">
<WindowFrame />
<div class="flex flex-col px-8">
<Navbar />
</div>
<div class="relative h-full w-full overflow-hidden">
<div class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden">
<ErrorBoundary>
<OverlayScrollbarsComponent class="os-hide-horizontal-scrollbar absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden overflow-y-auto px-8 pb-8">
<AnimatedRoutes>
{props.children}
</AnimatedRoutes>
</OverlayScrollbarsComponent>
</ErrorBoundary>
<ClusterModalController>
<SettingsProvider>
<main class="flex flex-col bg-primary w-full min-h-screen overflow-hidden h-screen max-h-screen text-fg-primary">
<WindowFrame />
<div class="flex flex-col px-8">
<Navbar />
</div>
</div>
<NotificationOverlay />
</main>
</SettingsProvider>
<div class="relative h-full w-full overflow-hidden">
<div class="absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden">
<ErrorBoundary>
<OverlayScrollbarsComponent class="os-hide-horizontal-scrollbar absolute top-0 left-0 flex flex-col h-full w-full overflow-x-hidden overflow-y-auto px-8 pb-8">
<AnimatedRoutes>
{props.children}
</AnimatedRoutes>
</OverlayScrollbarsComponent>
</ErrorBoundary>
</div>
</div>
<NotificationOverlay />
</main>
</SettingsProvider>
</ClusterModalController>
);
}

View file

@ -52,7 +52,7 @@ function WindowFrame() {
<TitlebarButton icon={XCloseIcon} onClick={() => quit()} danger />
</div>
<Modal
<Modal.Simple
title="Close OneLauncher?"
visible={isModalVisible}
setVisible={setModalVisible}

View file

@ -8,19 +8,27 @@ import { Portal } from 'solid-js/web';
type FullscreenOverlayProps = {
visible: Accessor<boolean>;
setVisible: Setter<boolean>;
mount?: Node | undefined;
zIndex?: number;
} & ParentProps;
function FullscreenOverlay(props: FullscreenOverlayProps) {
function onBackdropClick() {
props.setVisible(false);
function onBackdropClick(this: HTMLDivElement, e: MouseEvent) {
e.preventDefault();
if (e.target === this)
props.setVisible(false);
}
return (
<Portal>
<div class={`fixed z-[1000] top-0 left-0 w-screen h-screen bg-black/60 backdrop-blur-sm backdrop-grayscale transition-opacity ${props.visible() ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}>
<div class="absolute w-full h-full" onClick={() => onBackdropClick()} />
<Portal {...(props.mount ? { mount: props.mount } : {})}>
<div
style={{ 'z-index': props.zIndex || 1000 }}
class={`fixed top-0 left-0 w-screen h-screen bg-black/60 backdrop-blur-sm backdrop-grayscale transition-opacity ${props.visible() ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none'}`}
>
{/* New element to prevent from having to call stopPropagation in child elements */}
{/* <div class="absolute w-full h-full" onClick={() => onBackdropClick()} /> */}
<div class="absolute top-0 left-0 w-screen h-screen">
<div class="absolute top-0 left-0 w-screen h-screen" onClick={onBackdropClick}>
{props.children}
</div>

View file

@ -2,14 +2,71 @@ import type {
Accessor,
JSX,
ParentProps,
Ref,
Setter,
} from 'solid-js';
import { For, mergeProps } from 'solid-js';
import { For } from 'solid-js';
import { mergeRefs } from '@solid-primitives/refs';
import Button from '../base/Button';
import FullscreenOverlay from './FullscreenOverlay';
export type ModalProps = {
visible: Accessor<boolean>;
setVisible: Setter<boolean>;
zIndex?: number | undefined;
mount?: Node | undefined;
ref?: Ref<HTMLDivElement> | undefined;
} & ParentProps;
// TODO: Implement some sort of stacking control?
function Modal(props: ModalProps) {
return (
<FullscreenOverlay
visible={props.visible}
setVisible={props.setVisible}
mount={props.mount}
zIndex={props.zIndex || 1000}
>
<div ref={mergeRefs(props.ref)} class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<div class="bg-primary border border-white/5 p-4 rounded-lg text-center flex flex-col gap-y-2 min-w-xs">
{props.children}
</div>
</div>
</FullscreenOverlay>
);
}
type ModalButtonProps = string | JSX.Element;
export type ModalSimpleProps = {
title: string;
buttons?: ModalButtonProps[];
} & ModalProps;
Modal.Simple = function (props: ModalSimpleProps) {
return (
<Modal
ref={props.ref}
setVisible={props.setVisible}
visible={props.visible}
mount={props.mount}
zIndex={props.zIndex}
>
<h2>{props.title}</h2>
<div class="flex flex-col">
{props.children}
</div>
<div class="flex flex-row gap-x-4 [&>*]:flex-1">
<For each={props.buttons}>
{button => (
<ModalButton props={button} />
)}
</For>
</div>
</Modal>
);
};
function ModalButton(button: { props: ModalButtonProps }) {
return (
<>
@ -26,38 +83,4 @@ function ModalButton(button: { props: ModalButtonProps }) {
);
}
type ModalProps = {
title: string;
visible: Accessor<boolean>;
setVisible: Setter<boolean>;
buttons?: ModalButtonProps[];
} & ParentProps;
function Modal(props: ModalProps) {
const merged: ModalProps = mergeProps({ buttons: ['Ok'] }, props);
return (
<FullscreenOverlay
visible={props.visible}
setVisible={props.setVisible}
>
<div class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
<div class="bg-primary border border-white/5 p-4 rounded-lg text-center flex flex-col gap-y-2 min-w-xs">
<h2>{props.title}</h2>
<div class="flex flex-col">
{props.children}
</div>
<div class="flex flex-row gap-x-4 [&>*]:flex-1">
<For each={merged.buttons}>
{button => (
<ModalButton props={button} />
)}
</For>
</div>
</div>
</div>
</FullscreenOverlay>
);
}
export default Modal;

View file

@ -0,0 +1,67 @@
import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createSignal, useContext } from 'solid-js';
import { Server01Icon } from '@untitled-theme/icons-solid';
import Modal, { type ModalProps } from '../Modal';
import HeaderImage from '../../../../assets/images/header.png';
// Why the fuck do I need to use a context for all this???
// TODO: Rewrite for use with the new Modal stacking system
type ClusterModalContextFunc = [
step: Accessor<number>,
setStep: Setter<number>,
setVisible: Setter<boolean>,
];
const ClusterModalContext = createContext<ClusterModalContextFunc>() as Context<ClusterModalContextFunc>;
export function ClusterModalController(props: ParentProps) {
const [step, setStep] = createSignal(1);
const [visible, setVisible] = createSignal(false);
const stepper: ClusterModalContextFunc = [
step,
setStep,
setVisible,
];
return (
<ClusterModalContext.Provider value={stepper}>
{props.children}
<ClusterCreationModal visible={visible} setVisible={setVisible} step={step()} />
</ClusterModalContext.Provider>
);
}
export function useClusterModalController() {
return useContext(ClusterModalContext);
}
type ClusterCreationModalProps = ModalProps & {
step: number;
};
function ClusterCreationModal(props: ClusterCreationModalProps) {
// const StepComponent = () => (
// <Switch>
// <Match when={step() === 1}>
// <ClusterStepOne />
// </Match>
// </Switch>
// );
return (
<Modal {...props}>
<div class="relative h-22">
<div class="absolute top-0 left-0 w-full">
<img src={HeaderImage} alt="Header Image" />
</div>
<div class="absolute top-0 left-0 w-full flex flex-row justify-center items-center gap-x-2">
<Server01Icon />
<div class="flex flex-col">
<h1>New Cluster</h1>
<span>agagwag</span>
</div>
</div>
</div>
</Modal>
);
}

View file

@ -0,0 +1,23 @@
import type { JSX } from 'solid-js';
export default function ClusterStepOne() {
return (
<div class="grid grid-cols-3">
helo
</div>
);
}
interface ProviderCardProps {
icon: () => JSX.Element;
name: string;
};
function ProviderCard(props: ProviderCardProps) {
return (
<div class="flex flex-col justify-center items-center gap-y-2">
<props.icon />
<span>{props.name}</span>
</div>
);
}

View file

@ -19,6 +19,7 @@ import type { Cluster } from '~bindings';
import useCommand from '~ui/hooks/useCommand';
import { bridge } from '~imports';
import { upperFirst } from '~utils/primitives';
import { useClusterModalController } from '~ui/components/overlay/cluster/ClusterCreationModal';
// TODO: Replace this into it's own component
// possibly with the core ui lirbary with autogenerated and optimized svgs pls
@ -186,23 +187,10 @@ function ClusterGroup(props: ClusterGroupProps) {
function HomePage() {
const [clusters, { refetch }] = useCommand(bridge.commands.getClusters);
const [step, setStep, setVisible] = useClusterModalController();
async function newInstance() {
// bridge.commands.createCluster({
// });
// await createCluster({
// name: '1.0 cluster',
// version: 'a1.0.11',
// client: {
// type: 'Vanilla',
// },
// cover: null,
// group: null,
// });
// await refetch();
setVisible(true);
}
return (