feat: Keep state between ClusterModal step changes
This commit is contained in:
parent
650d2e85eb
commit
201ff3fc9d
6 changed files with 89 additions and 46 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue