feat: Keep state between ClusterModal step changes

This commit is contained in:
LynithDev 2024-07-21 22:11:09 +02:00
parent 650d2e85eb
commit 201ff3fc9d
6 changed files with 89 additions and 46 deletions

View file

@ -3,8 +3,13 @@
.list {
@apply bg-secondary rounded-lg border border-gray-05 p-1 shadow-md shadow-black/30;
@apply flex flex-col gap-2;
@apply flex-col gap-2;
@apply max-h-46 overflow-auto;
@apply hidden;
&.visible {
@apply flex;
}
}
.row {

View file

@ -72,7 +72,7 @@ function Dropdown(props: DropdownProps) {
ref.classList.add(...split.dropdownClass.split(' '));
}}
>
<OverlayScrollbarsComponent class={styles.list}>
<OverlayScrollbarsComponent class={`${styles.list} ${visible() ? styles.visible : ''}`}>
<Index each={items()}>
{(item, index) => (
<div onClick={() => select(index)}>
@ -91,6 +91,7 @@ function Dropdown(props: DropdownProps) {
Dropdown.Row = function (props: ParentProps) {
return (
// TODO(a11y): tabIndex
<div class={styles.row}>
{props.children}
</div>

View file

@ -1,18 +1,23 @@
import { type Accessor, type Context, Match, type ParentProps, type Setter, Switch, createContext, createEffect, createSignal, useContext } from 'solid-js';
import { type Accessor, type Context, Match, type ParentProps, type Setter, Show, Switch, createContext, createEffect, createSignal, on, useContext } from 'solid-js';
import { ArrowRightIcon, Server01Icon } from '@untitled-theme/icons-solid';
import HeaderImage from '../../../../assets/images/header.png';
import FullscreenOverlay, { type FullscreenOverlayProps } from '../FullscreenOverlay';
import ClusterStepOne from './ClusterStepOne';
import { ClusterStepTwo } from './ClusterStepTwo';
import Button from '~ui/components/base/Button';
import type { CreateCluster } from '~bindings';
type PartialCluster = Partial<CreateCluster>;
// 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>,
];
interface ClusterModalContextFunc {
step: Accessor<number>;
setStep: Setter<number>;
partialCluster: Accessor<PartialCluster>;
setPartialCluster: Setter<PartialCluster>;
setVisible: Setter<boolean>;
}
const ClusterModalContext = createContext<ClusterModalContextFunc>() as Context<ClusterModalContextFunc>;
@ -26,39 +31,35 @@ type ClusterModalStagesLength = UnionToArray<keyof typeof ClusterModalStages>['l
export function ClusterModalController(props: ParentProps) {
const [step, setStep] = createSignal<number>(ClusterModalStages.STAGE_1_PROVIDER);
const [visible, setVisible] = createSignal(false);
const [canGoForward, setCanGoForward] = createSignal(false);
const [partialCluster, setPartialCluster] = createSignal<PartialCluster>({});
const stepper: ClusterModalContextFunc = [
const stepper: ClusterModalContextFunc = {
step,
setStep,
partialCluster,
setPartialCluster,
setVisible,
];
};
createEffect(() => {
if (visible() === false)
setStep(ClusterModalStages.STAGE_1_PROVIDER);
});
const stepComponents = [
ClusterStepOne,
ClusterStepTwo,
];
return (
<ClusterModalContext.Provider value={stepper}>
{props.children}
<ClusterCreationModal
visible={visible}
setVisible={setVisible}
step={step}
setStep={setStep}
canGoForward={canGoForward}
buttonIsNext={step() !== ((Object.keys(ClusterModalStages).length / 2) - 1)}
>
{(stepComponents[step()]!({
canGoForward, setCanGoForward,
}))}
</ClusterCreationModal>
{/* Makes sure theres a new instance of the modal */}
<Show when={visible()}>
<ClusterCreationModal
visible={visible}
setVisible={setVisible}
step={step}
setStep={setStep}
buttonIsNext={step() !== ((Object.keys(ClusterModalStages).length / 2) - 1)}
/>
</Show>
</ClusterModalContext.Provider>
);
}
@ -68,24 +69,38 @@ export function useClusterModalController() {
}
export interface ClusterStepProps {
canGoForward: Accessor<boolean>;
setCanGoForward: Setter<boolean>;
visible: Accessor<boolean>;
};
type ClusterCreationModalProps = FullscreenOverlayProps & {
step: Accessor<number>;
setStep: Setter<number>;
buttonIsNext: boolean;
canGoForward: Accessor<boolean>;
};
function ClusterCreationModal(props: ClusterCreationModalProps) {
const [canGoForward, setCanGoForward] = createSignal(false);
// Indexed by the current step
const messages: FixedArray<string, ClusterModalStagesLength> = [
'Provider selection',
'Game Setup',
];
createEffect(on(() => props.step(), () => {
setCanGoForward(false);
}));
const stepComponents = [
ClusterStepOne,
ClusterStepTwo,
].map((component, index) => component({
setCanGoForward,
visible: () => props.step() === index,
}));
const Step = (props: { step: number }) => <>{stepComponents[props.step]}</>;
return (
<FullscreenOverlay
visible={props.visible}
@ -112,7 +127,7 @@ function ClusterCreationModal(props: ClusterCreationModalProps) {
</div>
<div class="flex flex-col border border-white/5 rounded-b-lg">
<div class="p-3">
{props.children}
<Step step={props.step()} />
</div>
<div class="flex flex-row gap-x-2 justify-end pt-0 p-3">
@ -138,7 +153,7 @@ function ClusterCreationModal(props: ClusterCreationModalProps) {
<Button
children="Next"
buttonStyle="primary"
disabled={!props.canGoForward()}
disabled={!canGoForward()}
iconRight={<ArrowRightIcon />}
onClick={() => props.setStep(prev => prev + 1)}
/>
@ -147,7 +162,7 @@ function ClusterCreationModal(props: ClusterCreationModalProps) {
<Button
children="Create"
buttonStyle="primary"
disabled={!props.canGoForward()}
disabled={!canGoForward()}
iconRight={<ArrowRightIcon />}
/>
</Match>

View file

@ -1,5 +1,6 @@
import { User03Icon } from '@untitled-theme/icons-solid';
import { Index, type JSX, createEffect, createSignal } from 'solid-js';
import { on } from 'solid-js';
import type { ClusterStepProps } from './ClusterCreationModal';
import ModrinthIcon from '~assets/logos/modrinth.svg?component-solid';
import CurseforgeIcon from '~assets/logos/curseforge.svg?component-solid';
@ -34,9 +35,11 @@ const providers: Omit<ProviderCardProps, 'selected' | 'setSelected'>[] = [
export default function ClusterStepOne(props: ClusterStepProps) {
const [selected, setSelected] = createSignal<number>();
createEffect(() => {
props.setCanGoForward(selected() !== undefined);
});
const check = () => props.setCanGoForward(selected() !== undefined);
// TODO: refactor this so its handled by the modal, I am lazy and running out of time so thisll do
createEffect(on(() => props.visible(), check));
createEffect(check);
return (
<div class="grid grid-cols-3 gap-2">

View file

@ -1,5 +1,6 @@
import { TextInputIcon } from '@untitled-theme/icons-solid';
import { For, type JSX, splitProps } from 'solid-js';
import { For, type JSX, createEffect, createSignal, on, splitProps } from 'solid-js';
import type { ClusterStepProps } from './ClusterCreationModal';
import Dropdown from '~ui/components/base/Dropdown';
import TextField from '~ui/components/base/TextField';
import VanillaImage from '~assets/logos/vanilla.png';
@ -29,11 +30,27 @@ const loaders: {
},
];
export function ClusterStepTwo() {
export function ClusterStepTwo(props: ClusterStepProps) {
const [name, setName] = createSignal('');
const check = () => {
const hasName = name().length > 0;
props.setCanGoForward(hasName);
};
// TODO: refactor this so its handled by the modal, I am lazy and running out of time so thisll do
createEffect(on(() => props.visible(), check));
createEffect(check);
return (
<div class="flex flex-col gap-y-4">
<Option header="Name">
<TextField placeholder="Default Name" iconLeft={<TextInputIcon />} />
<TextField
onInput={e => setName(e.target.value)}
placeholder="Name"
iconLeft={<TextInputIcon />}
/>
</Option>
<div class="flex flex-row gap-x-2">
@ -41,7 +58,7 @@ export function ClusterStepTwo() {
<Dropdown>
<For each={Array.from({ length: 300 }, (_, i) => i).reverse()}>
{version => (
<p>{version}</p>
<Dropdown.Row>{version}</Dropdown.Row>
)}
</For>
</Dropdown>
@ -50,12 +67,14 @@ export function ClusterStepTwo() {
<Dropdown>
<For each={loaders}>
{loader => (
<div class="flex flex-row gap-x-2">
<div class="w-4 h-4">
<loader.icon />
<Dropdown.Row>
<div class="flex flex-row gap-x-2">
<div class="w-4 h-4">
<loader.icon />
</div>
{loader.name}
</div>
{loader.name}
</div>
</Dropdown.Row>
)}
</For>
</Dropdown>

View file

@ -187,7 +187,7 @@ function ClusterGroup(props: ClusterGroupProps) {
function HomePage() {
const [clusters, { refetch }] = useCommand(bridge.commands.getClusters);
const [step, setStep, setVisible] = useClusterModalController();
const { setVisible } = useClusterModalController();
async function newInstance() {
setVisible(true);