diff --git a/pkgs/clan-app/ui/src/components/Modal/Modal.module.css b/pkgs/clan-app/ui/src/components/Modal/Modal.module.css index 9818c9331..cf367de38 100644 --- a/pkgs/clan-app/ui/src/components/Modal/Modal.module.css +++ b/pkgs/clan-app/ui/src/components/Modal/Modal.module.css @@ -37,7 +37,8 @@ } .backdrop { - @apply absolute left-0 top-0 z-50 size-full bg-black opacity-40; + @apply absolute top-0 left-0 w-full h-svh flex justify-center items-center backdrop-blur-0 z-50; + -webkit-backdrop-filter: blur(4px); } .contentWrapper { diff --git a/pkgs/clan-app/ui/src/components/Modal/Modal.stories.tsx b/pkgs/clan-app/ui/src/components/Modal/Modal.stories.tsx index d208b5d51..d83c3abcb 100644 --- a/pkgs/clan-app/ui/src/components/Modal/Modal.stories.tsx +++ b/pkgs/clan-app/ui/src/components/Modal/Modal.stories.tsx @@ -1,7 +1,7 @@ import { TagProps } from "@/src/components/Tag/Tag"; import { Meta, StoryObj } from "@kachurun/storybook-solid"; import { fn } from "storybook/test"; -import { Modal, ModalContext, ModalProps } from "@/src/components/Modal/Modal"; +import { Modal, ModalProps } from "@/src/components/Modal/Modal"; import { Fieldset, FieldsetFieldProps } from "@/src/components/Form/Fieldset"; import { TextInput } from "@/src/components/Form/TextInput"; import { TextArea } from "@/src/components/Form/TextArea"; @@ -21,7 +21,7 @@ export const Default: Story = { args: { title: "Example Modal", onClose: fn(), - children: ({ close }: ModalContext) => ( + children: (
{(props: FieldsetFieldProps) => ( diff --git a/pkgs/clan-app/ui/src/components/Modal/Modal.tsx b/pkgs/clan-app/ui/src/components/Modal/Modal.tsx index cfe31ce80..86b0af901 100644 --- a/pkgs/clan-app/ui/src/components/Modal/Modal.tsx +++ b/pkgs/clan-app/ui/src/components/Modal/Modal.tsx @@ -1,4 +1,11 @@ -import { Component, JSX, Show } from "solid-js"; +import { + Component, + JSX, + Show, + createContext, + createSignal, + useContext, +} from "solid-js"; import { Dialog as KDialog } from "@kobalte/core/dialog"; import styles from "./Modal.module.css"; import { Typography } from "../Typography/Typography"; @@ -6,15 +13,25 @@ import Icon from "../Icon/Icon"; import cx from "classnames"; import { Dynamic } from "solid-js/web"; -export interface ModalContext { - close(): void; -} +export type ModalContextType = { + portalRef: HTMLDivElement; +}; + +const ModalContext = createContext(); + +export const useModalContext = () => { + const context = useContext(ModalContext); + if (!context) { + return null; + } + return context as ModalContextType; +}; export interface ModalProps { id?: string; title: string; onClose: () => void; - children: (ctx: ModalContext) => JSX.Element; + children: JSX.Element; mount?: Node; class?: string; metaHeader?: Component; @@ -23,6 +40,7 @@ export interface ModalProps { } export const Modal = (props: ModalProps) => { + const [portalRef, setPortalRef] = createSignal(); return ( @@ -60,12 +78,11 @@ export const Modal = (props: ModalProps) => {
- {props.children({ - close: () => { - props.onClose(); - }, - })} + + {props.children} +
diff --git a/pkgs/clan-app/ui/src/components/Select/Select.module.css b/pkgs/clan-app/ui/src/components/Select/Select.module.css index b942288fd..c6c511e9c 100644 --- a/pkgs/clan-app/ui/src/components/Select/Select.module.css +++ b/pkgs/clan-app/ui/src/components/Select/Select.module.css @@ -60,11 +60,11 @@ /* Option elements (typically
  • ) */ & [role="option"] { - @apply p-2 rounded-sm flex items-center gap-1 flex-shrink-0; + @apply w-full p-2 rounded-sm flex items-center gap-1 flex-shrink-0; &[data-highlighted], &:focus-visible { - @apply outline outline-1 outline-inv-2; + @apply outline outline-1 outline-inv-2 outline-offset-[-1px]; } &:hover { @@ -77,6 +77,10 @@ } & [role="listbox"] { + width: var(--kb-popper-anchor-width); + overflow-x: hidden; + overflow-y: scroll; + &:focus-visible { @apply outline-none; } diff --git a/pkgs/clan-app/ui/src/components/Select/Select.stories.tsx b/pkgs/clan-app/ui/src/components/Select/Select.stories.tsx index 38baef30b..8ea5edf02 100644 --- a/pkgs/clan-app/ui/src/components/Select/Select.stories.tsx +++ b/pkgs/clan-app/ui/src/components/Select/Select.stories.tsx @@ -36,7 +36,10 @@ export const Default: Story = { description: "Choose your favorite pet from the list", }, options: [ - { value: "dog", label: "Doggy" }, + { + value: "dog", + label: "DoggyDoggyDoggyDoggyDoggyDoggy DoggyDoggyDoggyDoggyDoggy", + }, { value: "cat", label: "Catty" }, { value: "fish", label: "Fishy" }, { value: "bird", label: "Birdy" }, diff --git a/pkgs/clan-app/ui/src/components/Select/Select.tsx b/pkgs/clan-app/ui/src/components/Select/Select.tsx index f29410d97..f2b624348 100644 --- a/pkgs/clan-app/ui/src/components/Select/Select.tsx +++ b/pkgs/clan-app/ui/src/components/Select/Select.tsx @@ -6,6 +6,7 @@ import { createEffect, createSignal, JSX, Show, splitProps } from "solid-js"; import styles from "./Select.module.css"; import { Typography } from "../Typography/Typography"; import cx from "classnames"; +import { useModalContext } from "../Modal/Modal"; export interface Option { value: string; @@ -79,6 +80,13 @@ export const Select = (props: SelectProps) => { setValue(options().find((option) => props.value === option.value)); }); + const modalContext = useModalContext(); + const defaultMount = + props.portalProps?.mount || modalContext?.portalRef || document.body; + + createEffect(() => { + console.debug("Select component mounted at:", defaultMount); + }); return ( { - + - + + {() => ( + + > + {(state) => ( + + {state.selectedOption().label} + + )} + + + + + + )} + {/* TODO: Display error next to the problem */} diff --git a/pkgs/clan-app/ui/src/routes/Clan/Clan.module.css b/pkgs/clan-app/ui/src/routes/Clan/Clan.module.css index 03d5032c3..b15bfaed3 100644 --- a/pkgs/clan-app/ui/src/routes/Clan/Clan.module.css +++ b/pkgs/clan-app/ui/src/routes/Clan/Clan.module.css @@ -3,11 +3,6 @@ transition: opacity 0.5s ease; } -.createBackdrop { - @apply absolute top-0 left-0 w-full h-svh flex justify-center items-center backdrop-blur-0 z-50; - -webkit-backdrop-filter: blur(4px); -} - .createModal { @apply min-w-96; } diff --git a/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx b/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx index dcede56a2..2ccd51577 100644 --- a/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx +++ b/pkgs/clan-app/ui/src/routes/Clan/Clan.tsx @@ -54,55 +54,43 @@ interface MockProps { } const MockCreateMachine = (props: MockProps) => { - let container: Node; - const [form, { Form, Field, FieldArray }] = createForm(); return ( -
    (container = el)} class={cx(styles.createBackdrop)}> - { - reset(form); - props.onClose(); - }} - class={cx(styles.createModal)} - title="Create Machine" - > - {() => ( - - - {(field, props) => ( - <> - - - )} - - -
    - - -
    - - )} -
    -
    + required={true} + input={{ ...props, placeholder: "name", autofocus: true }} + /> + + )} + + +
    + + +
    + + ); }; diff --git a/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx b/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx index 3ab6ed412..4d8f429e0 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/Install.stories.tsx @@ -201,6 +201,7 @@ type Story = StoryObj; export const Init: Story = { description: "Welcome step for the installation workflow", args: { + open: true, machineName: "Test Machine", initialStep: "init", }, @@ -208,6 +209,7 @@ export const Init: Story = { export const CreateInstallerProse: Story = { description: "Prose step for creating an installer", args: { + open: true, machineName: "Test Machine", initialStep: "create:prose", }, @@ -215,6 +217,7 @@ export const CreateInstallerProse: Story = { export const CreateInstallerImage: Story = { description: "Configure the image to install", args: { + open: true, machineName: "Test Machine", initialStep: "create:image", }, @@ -222,6 +225,7 @@ export const CreateInstallerImage: Story = { export const CreateInstallerDisk: Story = { description: "Select a disk to install the image on", args: { + open: true, machineName: "Test Machine", initialStep: "create:disk", }, @@ -229,6 +233,7 @@ export const CreateInstallerDisk: Story = { export const CreateInstallerProgress: Story = { description: "Showed while the USB stick is being flashed", args: { + open: true, machineName: "Test Machine", initialStep: "create:progress", }, @@ -236,6 +241,7 @@ export const CreateInstallerProgress: Story = { export const CreateInstallerDone: Story = { description: "Installation done step", args: { + open: true, machineName: "Test Machine", initialStep: "create:done", }, @@ -243,6 +249,7 @@ export const CreateInstallerDone: Story = { export const InstallConfigureAddress: Story = { description: "Installation configure address step", args: { + open: true, machineName: "Test Machine", initialStep: "install:address", }, @@ -250,6 +257,7 @@ export const InstallConfigureAddress: Story = { export const InstallCheckHardware: Story = { description: "Installation check hardware step", args: { + open: true, machineName: "Test Machine", initialStep: "install:check-hardware", }, @@ -257,6 +265,7 @@ export const InstallCheckHardware: Story = { export const InstallSelectDisk: Story = { description: "Select disk to install the system on", args: { + open: true, machineName: "Test Machine", initialStep: "install:disk", }, @@ -264,6 +273,7 @@ export const InstallSelectDisk: Story = { export const InstallVars: Story = { description: "Fill required credentials and data for the installation", args: { + open: true, machineName: "Test Machine", initialStep: "install:data", }, @@ -271,6 +281,7 @@ export const InstallVars: Story = { export const InstallSummary: Story = { description: "Summary of the installation steps", args: { + open: true, machineName: "Test Machine", initialStep: "install:summary", }, @@ -278,6 +289,7 @@ export const InstallSummary: Story = { export const InstallProgress: Story = { description: "Shown while the installation is in progress", args: { + open: true, machineName: "Test Machine", initialStep: "install:progress", }, @@ -285,6 +297,7 @@ export const InstallProgress: Story = { export const InstallDone: Story = { description: "Shown after the installation is done", args: { + open: true, machineName: "Test Machine", initialStep: "install:done", }, diff --git a/pkgs/clan-app/ui/src/workflows/Install/install.tsx b/pkgs/clan-app/ui/src/workflows/Install/install.tsx index b4fb82dd8..fccc98c97 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/install.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/install.tsx @@ -97,7 +97,7 @@ export const InstallModal = (props: InstallModalProps) => { // @ts-expect-error some steps might not have disablePadding={stepper.currentStep()?.isSplash} > - {(ctx) => } + props.onClose} /> ); diff --git a/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx b/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx index 997b03691..5752d89b4 100644 --- a/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx +++ b/pkgs/clan-app/ui/src/workflows/Install/steps/createInstaller.tsx @@ -142,18 +142,11 @@ const ConfigureImage = () => { throw new Error("No data returned from api call"); }; - let content: Node; - return (
    { - content = el; - }} - > +
    {(field, input) => (