feat: game startup is completed
This commit is contained in:
parent
f2f1e2019c
commit
ccf34585d0
13 changed files with 337 additions and 83 deletions
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
|
|
@ -12,7 +12,7 @@
|
|||
"args": [
|
||||
"build",
|
||||
"--profile=dev-debug",
|
||||
"--manifest-path=./apps/desktop/Cargo.toml",
|
||||
"--manifest-path=./apps/onelauncher/desktop/Cargo.toml",
|
||||
"--no-default-features"
|
||||
],
|
||||
"problemMatcher": "$rustc"
|
||||
|
|
|
|||
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
|
|
@ -41,7 +41,7 @@
|
|||
},
|
||||
"isBackground": true,
|
||||
"command": "pnpm",
|
||||
"args": ["frontend", "dev", "--mode=development"],
|
||||
"args": ["onelauncher:frontend", "dev", "--mode=development"],
|
||||
"runOptions": {
|
||||
"instanceLimit": 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ sea-orm = { version = "1.1", features = [
|
|||
"with-chrono",
|
||||
"with-json",
|
||||
"with-uuid",
|
||||
"sqlite-use-returning-for-3_35",
|
||||
] }
|
||||
sea-orm-migration = { version = "1.1", features = [
|
||||
"sqlx-sqlite",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,212 @@
|
|||
import type { JSX } from 'react';
|
||||
import DefaultBanner from '@/assets/images/default_banner.png';
|
||||
import LauncherIcon from '@/components/content/LauncherIcon';
|
||||
import Modal from '@/components/overlay/Modal';
|
||||
import { bindings } from '@/main';
|
||||
import { LAUNCHER_IMPORT_TYPES } from '@/utils';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
import { Button, Dropdown, TextField } from '@onelauncher/common/components';
|
||||
import { Server01Icon, User03Icon } from '@untitled-theme/icons-react';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
const STEPS = [
|
||||
{
|
||||
id: 'loader',
|
||||
title: 'Select Provider',
|
||||
},
|
||||
{
|
||||
id: 'info',
|
||||
title: 'Game Setup',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export function NewClusterCreate() {
|
||||
const [currentStepIndex, setCurrentStepIndex] = useState(0);
|
||||
const [formData, setFormData] = useState({
|
||||
loader: '',
|
||||
clusterName: '',
|
||||
clusterVersion: '',
|
||||
});
|
||||
|
||||
const handleNext = useCallback(async () => {
|
||||
if (currentStepIndex < STEPS.length - 1)
|
||||
setCurrentStepIndex(currentStepIndex + 1);
|
||||
}, [currentStepIndex]);
|
||||
|
||||
const handleBack = useCallback(() => {
|
||||
if (currentStepIndex > 0)
|
||||
setCurrentStepIndex(currentStepIndex - 1);
|
||||
}, [currentStepIndex]);
|
||||
|
||||
const currentStepConfig = STEPS[currentStepIndex];
|
||||
let stepContent: JSX.Element;
|
||||
|
||||
switch (currentStepConfig.id) {
|
||||
case 'loader': {
|
||||
stepContent = (
|
||||
<ClusterLoader
|
||||
onSelectLoader={loader => setFormData(prev => ({ ...prev, loader }))}
|
||||
selectedLoader={formData.loader}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'info': {
|
||||
stepContent = (
|
||||
<ClusterInformation
|
||||
clusterName={formData.clusterName}
|
||||
clusterVersion={formData.clusterVersion}
|
||||
onClusterNameChange={name => setFormData(prev => ({ ...prev, clusterName: name }))}
|
||||
onClusterVersionChange={version =>
|
||||
setFormData(prev => ({ ...prev, clusterVersion: version }))}
|
||||
/>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const isFirstStep = currentStepIndex === 0;
|
||||
const isLastInputStep = currentStepIndex === STEPS.length - 1;
|
||||
const nextButtonText = isLastInputStep ? 'Create Cluster' : 'Next';
|
||||
|
||||
let isNextDisabled = false;
|
||||
if (currentStepConfig.id === 'loader' && !formData.loader)
|
||||
isNextDisabled = true;
|
||||
else if (
|
||||
currentStepConfig.id === 'info'
|
||||
&& (!formData.clusterName.trim() || !formData.clusterVersion.trim())
|
||||
)
|
||||
isNextDisabled = true;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal.Trigger>
|
||||
<Button
|
||||
children="New Cluster"
|
||||
/>
|
||||
|
||||
<Modal>
|
||||
<div className="min-w-sm flex flex-col rounded-lg bg-page text-center">
|
||||
<div className="theme-OneLauncher-Dark relative h-25 flex">
|
||||
<div className="absolute left-0 top-0 h-full w-full">
|
||||
<img alt="Header Image" className="h-full w-full rounded-t-lg" src={DefaultBanner} />
|
||||
</div>
|
||||
<div
|
||||
className="absolute left-0 top-0 h-full flex w-full flex-row items-center justify-start gap-x-4 bg-[radial-gradient(at_center,#00000077,transparent)] px-10"
|
||||
>
|
||||
<Server01Icon className="h-8 w-8 text-fg-primary" />
|
||||
<div className="flex flex-col items-start justify-center">
|
||||
<h1 className="h-10 text-fg-primary -mt-2">New Cluster</h1>
|
||||
<span className="text-fg-primary">{STEPS[currentStepIndex].title}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col rounded-b-lg border border-white/5">
|
||||
<div className="p-3">
|
||||
{stepContent}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row justify-end gap-x-2 p-3 pt-0">
|
||||
<Button
|
||||
children="Back"
|
||||
color="secondary"
|
||||
isDisabled={isFirstStep}
|
||||
onClick={handleBack}
|
||||
/>
|
||||
<Button
|
||||
children={nextButtonText}
|
||||
color="primary"
|
||||
isDisabled={isNextDisabled}
|
||||
onClick={handleNext}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</Modal.Trigger>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface ClusterLoaderProps {
|
||||
selectedLoader: string;
|
||||
onSelectLoader: (loader: string) => void;
|
||||
}
|
||||
|
||||
function ClusterLoader({ selectedLoader, onSelectLoader }: ClusterLoaderProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
<ProviderCard
|
||||
icon={<User03Icon />}
|
||||
name="New"
|
||||
selected={selectedLoader === 'new'}
|
||||
setSelected={() => onSelectLoader('new')}
|
||||
/>
|
||||
|
||||
{LAUNCHER_IMPORT_TYPES.map((data, i) => (
|
||||
<ProviderCard
|
||||
icon={<LauncherIcon launcher={data} />}
|
||||
key={i}
|
||||
name={data}
|
||||
selected={selectedLoader === data}
|
||||
setSelected={() => onSelectLoader(data)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
interface ProviderCardProps {
|
||||
setSelected: () => void;
|
||||
selected: boolean;
|
||||
icon: JSX.Element;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function ProviderCard(props: ProviderCardProps) {
|
||||
const { selected, setSelected, icon, name } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col justify-center items-center gap-y-3 py-2 px-4 hover:bg-component-bg-hover active:bg-component-bg-pressed rounded-lg ${selected ? 'bg-component-bg' : ''}`}
|
||||
onClick={() => setSelected()}
|
||||
>
|
||||
<div className="h-8 w-8 flex items-center justify-center [&>svg]:(w-8 h-8!)">
|
||||
{icon}
|
||||
</div>
|
||||
<span>{name}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ClusterInformationProps {
|
||||
clusterName: string;
|
||||
onClusterNameChange: (name: string) => void;
|
||||
clusterVersion: string;
|
||||
onClusterVersionChange: (version: string) => void;
|
||||
}
|
||||
|
||||
function ClusterInformation({
|
||||
clusterName,
|
||||
onClusterNameChange,
|
||||
clusterVersion,
|
||||
onClusterVersionChange,
|
||||
}: ClusterInformationProps) {
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<TextField
|
||||
className="w-full"
|
||||
onChange={e => onClusterNameChange(e.target.value)}
|
||||
placeholder="My Awesome Server"
|
||||
value={clusterName}
|
||||
/>
|
||||
<Dropdown placeholder="Select a version">
|
||||
<Dropdown.Item>Slmalr</Dropdown.Item>
|
||||
<Dropdown.Item>Slmalr 2</Dropdown.Item>
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import Modal from '@/components/overlay/Modal';
|
||||
import { Button, TextField } from '@onelauncher/common/components';
|
||||
import { Button, Dropdown, TextField } from '@onelauncher/common/components';
|
||||
import { createFileRoute } from '@tanstack/react-router';
|
||||
|
||||
export const Route = createFileRoute('/app/browser/')({
|
||||
|
|
@ -22,10 +22,10 @@ function RouteComponent() {
|
|||
</Modal>
|
||||
</Modal.Trigger>
|
||||
|
||||
{/* <Dropdown>
|
||||
<Dropdown label="Select a version">
|
||||
<Dropdown.Item>Slmalr</Dropdown.Item>
|
||||
<Dropdown.Item>Slmalr 2</Dropdown.Item>
|
||||
</Dropdown> */}
|
||||
</Dropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import type { Model } from '@/bindings.gen';
|
||||
import DefaultBanner from '@/assets/images/default_banner.png';
|
||||
import DefaultInstancePhoto from '@/assets/images/default_instance_cover.jpg';
|
||||
import { NewClusterCreate } from '@/components/launcher/cluster/ClusterCreation';
|
||||
import Modal from '@/components/overlay/Modal';
|
||||
import { bindings } from '@/main';
|
||||
import { useCommand } from '@onelauncher/common';
|
||||
|
|
@ -28,7 +29,7 @@ function RouteComponent() {
|
|||
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<div className="flex flex-row items-center gap-x-2">
|
||||
<ClusterCreate />
|
||||
<NewClusterCreate />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -114,7 +115,7 @@ function ClusterCreate() {
|
|||
const result = useCommand('createCluster', () => bindings.core.createCluster({
|
||||
icon: null,
|
||||
mc_loader: 'vanilla',
|
||||
mc_version: '1.20.1',
|
||||
mc_version: '1.8.9',
|
||||
name: 'Test Cluster',
|
||||
mc_loader_version: null,
|
||||
}), {
|
||||
|
|
@ -223,40 +224,44 @@ function ClusterCard({
|
|||
};
|
||||
|
||||
return (
|
||||
<Link
|
||||
search={{
|
||||
id,
|
||||
}} to="/app/cluster"
|
||||
>
|
||||
<div
|
||||
className="group relative h-[152px] flex flex-col rounded-xl border border-component-border/5 bg-component-bg active:bg-component-bg-pressed hover:bg-component-bg-hover"
|
||||
<>
|
||||
<Link
|
||||
search={{
|
||||
id,
|
||||
}} to="/app/cluster"
|
||||
>
|
||||
<div className="relative flex-1 overflow-hidden rounded-t-xl">
|
||||
<div
|
||||
className="absolute h-full w-full transition-transform group-hover:!scale-110"
|
||||
>
|
||||
<img
|
||||
className="h-full w-full object-cover"
|
||||
src={DefaultInstancePhoto}
|
||||
/>
|
||||
<div
|
||||
className="group relative h-[152px] flex flex-col rounded-xl border border-component-border/5 bg-component-bg active:bg-component-bg-pressed hover:bg-component-bg-hover"
|
||||
>
|
||||
<div className="relative flex-1 overflow-hidden rounded-t-xl">
|
||||
<div
|
||||
className="absolute h-full w-full transition-transform group-hover:!scale-110"
|
||||
>
|
||||
<img
|
||||
className="h-full w-full object-cover"
|
||||
src={DefaultInstancePhoto}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="z-10 flex flex-row items-center justify-between gap-x-3 p-3">
|
||||
<div className="h-full flex flex-col gap-1.5 overflow-hidden">
|
||||
<p className="h-4 text-ellipsis whitespace-nowrap font-medium">{name}</p>
|
||||
<p className="h-4 text-xs">
|
||||
{mc_loader}
|
||||
{' '}
|
||||
{mc_version}
|
||||
{/* {' '}
|
||||
<div className="z-10 flex flex-row items-center justify-between gap-x-3 p-3">
|
||||
<div className="h-full flex flex-col gap-1.5 overflow-hidden">
|
||||
<p className="h-4 text-ellipsis whitespace-nowrap font-medium">{name}</p>
|
||||
<p className="h-4 text-xs">
|
||||
{mc_loader}
|
||||
{' '}
|
||||
{mc_version}
|
||||
{/* {' '}
|
||||
{props.packages.mods && `• ${props.mods} mods`} */}
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* <LaunchButton cluster={props} iconOnly /> */}
|
||||
<Button onClick={handleLaunch} size="icon"><PlayIcon /></Button>
|
||||
{/* <LaunchButton cluster={props} iconOnly /> */}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</Link>
|
||||
|
||||
<Button onClick={handleLaunch} size="icon"><PlayIcon /></Button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ export const Route = createFileRoute('/onboarding/login')({
|
|||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const ctx = useOnboardingContext();
|
||||
const [profile, setProfile] = useState<MinecraftCredentials>();
|
||||
const [errorMessage, setErrorMessage] = useState<string>('');
|
||||
const result = useCommand('openMsaLogin', bindings.core.openMsaLogin, {
|
||||
|
|
@ -33,8 +32,6 @@ function RouteComponent() {
|
|||
}
|
||||
|
||||
setProfile(data.data);
|
||||
|
||||
ctx.setIsForwardButtonEnabled(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ function RouteComponent() {
|
|||
]);
|
||||
|
||||
return (
|
||||
<OnboardingContext value={ctxValue}>
|
||||
<OnboardingContext.Provider value={ctxValue}>
|
||||
<div className="w-full flex flex-col items-center h-screen bg-background-primary text-text-primary">
|
||||
<div className="h-0.5 w-full">
|
||||
<div
|
||||
|
|
@ -263,7 +263,7 @@ function RouteComponent() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</OnboardingContext>
|
||||
</OnboardingContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,5 +5,17 @@ export const Route = createFileRoute('/onboarding/summary')({
|
|||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/onboarding/summary"!</div>;
|
||||
return (
|
||||
<div className="grid grid-cols-2 h-full w-full flex-col items-start justify-center gap-x-16 gap-y-2">
|
||||
<h1 className="text-6xl">
|
||||
Prepare OneLauncher
|
||||
</h1>
|
||||
|
||||
<h3>Are you sure you want to proceed with the following tasks?</h3>
|
||||
|
||||
<div>
|
||||
<pre>tasks</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,10 @@ export default defineConfig(
|
|||
rules: {
|
||||
'sort-imports': 'off', // Replaced by perfectionist/sort-named-imports',
|
||||
'import/order': 'off', // Replaced by perfectionist/sort-named-imports',
|
||||
'style/jsx-max-props-per-line': ['error', { maximum: 3}],
|
||||
'style/jsx-max-props-per-line': ['error', { maximum: 3 }],
|
||||
'ts/no-use-before-define': 'off',
|
||||
'style/function-paren-newline': ['error', 'consistent']
|
||||
'style/function-paren-newline': ['error', 'consistent'],
|
||||
'react/no-context-provider': 'off',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -206,26 +206,57 @@ pub async fn locate_java() -> LauncherResult<HashMap<PathBuf, JavaInfo>> {
|
|||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn internal_locate_java() -> Vec<PathBuf> {
|
||||
let mut found = Vec::new();
|
||||
let mut java_homes = Vec::new();
|
||||
|
||||
// epic common paths
|
||||
let common_paths = [
|
||||
r"C:\Program Files\Java",
|
||||
r"C:\Program Files (x86)\Java",
|
||||
r"C:\Program Files\OpenJDK",
|
||||
r"C:\Program Files\Eclipse Adoptium",
|
||||
r"C:\Program Files\Zulu",
|
||||
r"C:\Program Files\Amazon Corretto",
|
||||
];
|
||||
|
||||
for base_path in common_paths {
|
||||
if let Ok(entries) = std::fs::read_dir(base_path) {
|
||||
for entry in entries.flatten() {
|
||||
if entry.file_type().map_or(false, |ft| ft.is_dir()) {
|
||||
let java_exe = entry.path().join("bin").join("java.exe");
|
||||
if java_exe.exists() {
|
||||
java_homes.push(java_exe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// env vars
|
||||
if let Ok(java_home) = std::env::var("JAVA_HOME") {
|
||||
let path_buf = PathBuf::from(java_home);
|
||||
if path_buf.join("bin").join("java.exe").exists() {
|
||||
java_homes.push(path_buf);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Implement Registry scanning
|
||||
// TODO(windows): More paths for Java installations
|
||||
|
||||
let paths = vec![
|
||||
r"C:/Program Files/Java",
|
||||
r"C:/Program Files/Eclipse Adoptium",
|
||||
r"C:/Program Files (x86)/Java",
|
||||
r"C:/Program Files (x86)/Eclipse Adoptium",
|
||||
];
|
||||
|
||||
for path in paths {
|
||||
let path = PathBuf::from(path).join(get_java_bin());
|
||||
if path.exists() {
|
||||
found.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
found
|
||||
if let Ok(path) = std::env::var("PATH") {
|
||||
for path_entry in path.split(';') {
|
||||
let java_exe = PathBuf::from(path_entry).join("java.exe");
|
||||
if java_exe.exists() {
|
||||
|
||||
if let Some(bin_dir) = java_exe.parent() {
|
||||
if let Some(java_home) = bin_dir.parent() {
|
||||
let java_home_path = java_home.to_path_buf();
|
||||
if !java_homes.contains(&java_home_path) {
|
||||
java_homes.push(java_home_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java_homes
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use interpulse::api::minecraft::Version;
|
||||
use onelauncher_entity::{icon::Icon, loader::GameLoader};
|
||||
use onelauncher_entity::{icon::Icon, loader::GameLoader, prelude::entity};
|
||||
use tauri::{AppHandle, Runtime};
|
||||
|
||||
use crate::{api::{self, cluster::dao::ClusterId}, error::LauncherResult, store::{credentials::MinecraftCredentials, Core}};
|
||||
|
|
@ -59,7 +58,6 @@ pub trait TauriLauncherApi {
|
|||
|
||||
#[taurpc(alias = "openMsaLogin")]
|
||||
async fn open_msa_login<R: Runtime>(app_handle: AppHandle<R>) -> LauncherResult<Option<MinecraftCredentials>>;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -224,5 +222,4 @@ impl TauriLauncherApi for TauriLauncherApiImpl {
|
|||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
import type {
|
||||
SelectProps as AriaComboBoxProps,
|
||||
ListBoxItemProps as AriaListBoxItemProps,
|
||||
SelectProps as AriaSelectProps,
|
||||
} from 'react-aria-components';
|
||||
import type { VariantProps } from 'tailwind-variants';
|
||||
import type { ClassNameString } from '../types/global';
|
||||
import {
|
||||
Select as AriaComboBox,
|
||||
Label as AriaLabel,
|
||||
ListBox as AriaListBox,
|
||||
ListBoxItem as AriaListBoxItem,
|
||||
Popover as AriaPopover,
|
||||
Select as AriaSelect,
|
||||
SelectValue,
|
||||
} from 'react-aria-components';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { tv } from 'tailwind-variants';
|
||||
import { Button } from '.';
|
||||
|
||||
export interface ComboBoxWrapperProps<T extends object> extends Omit<AriaComboBoxProps<T>, 'className'>, ClassNameString {
|
||||
export interface ComboBoxWrapperProps<T extends object> extends Omit<AriaSelectProps<T>, 'className'>, ClassNameString {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
const dropdownVariants = tv({
|
||||
base: [
|
||||
'flex flex-col border border-component-bg-hover w-fit rounded-lg focus:outline-none',
|
||||
'relative h-full',
|
||||
],
|
||||
});
|
||||
|
||||
|
|
@ -34,21 +35,18 @@ export function Dropdown<T extends object>({
|
|||
...rest
|
||||
}: ComboBoxWrapperProps<T>) {
|
||||
return (
|
||||
<AriaComboBox
|
||||
className={dropdownVariants({ className })}
|
||||
{...rest}
|
||||
>
|
||||
<AriaSelect {...rest} className={dropdownVariants({ className })}>
|
||||
{label && <AriaLabel>{label}</AriaLabel>}
|
||||
<div className="flex">
|
||||
<Button className="border w-full border-component-bg py-1" color="ghost">
|
||||
<SelectValue />
|
||||
<Button className="size-6">▼</Button>
|
||||
</div>
|
||||
<AriaPopover className="border border-orange-400">
|
||||
<AriaListBox className="bg-blue-500 p-2 w-38">
|
||||
<span aria-hidden="true">▼</span>
|
||||
</Button>
|
||||
<AriaPopover className="border border-component-bg rounded-lg p-1">
|
||||
<AriaListBox className="max-h-47 overflow-auto flex-col gap-1">
|
||||
{children}
|
||||
</AriaListBox>
|
||||
</AriaPopover>
|
||||
</AriaComboBox>
|
||||
</AriaSelect>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +57,7 @@ Dropdown.Item = ({
|
|||
...rest
|
||||
}: AriaListBoxItemProps & ClassNameString) => (
|
||||
<AriaListBoxItem
|
||||
className={className}
|
||||
className={twMerge('hover:bg-component-bg-hover px-2 py-1 rounded-md w-full', className)}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue