feat: Close confirmation dialog
This commit is contained in:
parent
6fdea8c033
commit
b2226a07e7
9 changed files with 156 additions and 34 deletions
|
|
@ -30,6 +30,7 @@
|
|||
"eslint-plugin-solid": "^0.13.1",
|
||||
"postcss": "^8.4.35",
|
||||
"postcss-easings": "^4.0.0",
|
||||
"sass": "^1.72.0",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.0.2",
|
||||
"vite": "^5.0.0",
|
||||
|
|
|
|||
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
|
|
@ -55,6 +55,9 @@ devDependencies:
|
|||
postcss-easings:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0(postcss@8.4.35)
|
||||
sass:
|
||||
specifier: ^1.72.0
|
||||
version: 1.72.0
|
||||
tailwindcss:
|
||||
specifier: ^3.4.1
|
||||
version: 3.4.1
|
||||
|
|
@ -63,7 +66,7 @@ devDependencies:
|
|||
version: 5.4.2
|
||||
vite:
|
||||
specifier: ^5.0.0
|
||||
version: 5.1.6
|
||||
version: 5.1.6(sass@1.72.0)
|
||||
vite-plugin-solid:
|
||||
specifier: ^2.8.0
|
||||
version: 2.10.1(solid-js@1.8.15)(vite@5.1.6)
|
||||
|
|
@ -2374,6 +2377,10 @@ packages:
|
|||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
/immutable@4.3.5:
|
||||
resolution: {integrity: sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==}
|
||||
dev: true
|
||||
|
||||
/import-fresh@3.3.0:
|
||||
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -3127,6 +3134,16 @@ packages:
|
|||
is-regex: 1.1.4
|
||||
dev: true
|
||||
|
||||
/sass@1.72.0:
|
||||
resolution: {integrity: sha512-Gpczt3WA56Ly0Mn8Sl21Vj94s1axi9hDIzDFn9Ph9x3C3p4nNyvsqJoQyVXKou6cBlfFWEgRW4rT8Tb4i3XnVA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chokidar: 3.6.0
|
||||
immutable: 4.3.5
|
||||
source-map-js: 1.0.2
|
||||
dev: true
|
||||
|
||||
/semver@6.3.1:
|
||||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
|
|
@ -3555,13 +3572,13 @@ packages:
|
|||
merge-anything: 5.1.7
|
||||
solid-js: 1.8.15
|
||||
solid-refresh: 0.6.3(solid-js@1.8.15)
|
||||
vite: 5.1.6
|
||||
vite: 5.1.6(sass@1.72.0)
|
||||
vitefu: 0.2.5(vite@5.1.6)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite@5.1.6:
|
||||
/vite@5.1.6(sass@1.72.0):
|
||||
resolution: {integrity: sha512-yYIAZs9nVfRJ/AiOLCA91zzhjsHUgMjB+EigzFb6W2XTLO8JixBCKCjvhKZaye+NKYHCrkv3Oh50dH9EdLU2RA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
hasBin: true
|
||||
|
|
@ -3592,6 +3609,7 @@ packages:
|
|||
esbuild: 0.19.12
|
||||
postcss: 8.4.35
|
||||
rollup: 4.13.0
|
||||
sass: 1.72.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
|
@ -3604,7 +3622,7 @@ packages:
|
|||
vite:
|
||||
optional: true
|
||||
dependencies:
|
||||
vite: 5.1.6
|
||||
vite: 5.1.6(sass@1.72.0)
|
||||
dev: true
|
||||
|
||||
/which-boxed-primitive@1.0.2:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,15 @@
|
|||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
body {
|
||||
@apply text-gray-400 text-md;
|
||||
}
|
||||
|
||||
/* start lynith's native experience */
|
||||
* { user-select: none; }
|
||||
*:hover { cursor: default; }
|
||||
/* end lynith's native experience */
|
||||
/* end lynith's native experience */
|
||||
|
||||
h1 { @apply text-2xl font-bold; }
|
||||
h2 { @apply text-xxl font-bold; }
|
||||
h3 { @apply text-xl font-bold; }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import WindowFrame from './components/native/WindowFrame';
|
|||
|
||||
function App(props: ParentProps) {
|
||||
return (
|
||||
<main class='bg-bg-primary min-h-screen text-white'>
|
||||
<main class='bg-primary min-h-screen text-white'>
|
||||
<WindowFrame />
|
||||
{props.children}
|
||||
</main>
|
||||
|
|
|
|||
16
src/ui/components/base/Button.module.scss
Normal file
16
src/ui/components/base/Button.module.scss
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
.button {
|
||||
@apply border border-white/5
|
||||
bg-component-bg hover:bg-component-bg-hover active:bg-component-bg-pressed;
|
||||
|
||||
&.size--sm {
|
||||
|
||||
}
|
||||
|
||||
&.size-md {
|
||||
@apply py-2 px-4 rounded-lg;
|
||||
}
|
||||
|
||||
&.size-lg {
|
||||
|
||||
}
|
||||
}
|
||||
19
src/ui/components/base/Button.tsx
Normal file
19
src/ui/components/base/Button.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { JSX, ParentProps, mergeProps } from 'solid-js';
|
||||
import styles from './Button.module.scss';
|
||||
|
||||
type ButtonProps = JSX.ButtonHTMLAttributes<HTMLButtonElement> & ParentProps & {
|
||||
size?: 'sm' | 'md' | 'lg',
|
||||
style?: 'primary' | 'secondary' | 'danger', // TODO
|
||||
};
|
||||
|
||||
function Button(props: ButtonProps) {
|
||||
const merged = mergeProps({ size: 'md' }, props);
|
||||
|
||||
return (
|
||||
<button class={`${styles.button} ${styles[`size-${merged.size}`]}`} {...props}>
|
||||
{props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default Button;
|
||||
|
|
@ -2,18 +2,19 @@ import { Window } from '@tauri-apps/api/window';
|
|||
import { MinusIcon, XCloseIcon } from '@untitled-theme/icons-solid';
|
||||
import { JSX, createSignal } from 'solid-js';
|
||||
import Modal from '../overlay/Modal';
|
||||
import Button from '../base/Button';
|
||||
|
||||
type ButtonProps = {
|
||||
type TitlebarButtonProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
icon: (any: any) => JSX.Element,
|
||||
onClick: (event: MouseEvent) => void,
|
||||
danger?: boolean,
|
||||
};
|
||||
|
||||
function Button(props: ButtonProps) {
|
||||
function TitlebarButton(props: TitlebarButtonProps) {
|
||||
return (
|
||||
<button class='flex items-center justify-center w-8 h-8 group' onClick={(e) => props.onClick(e)}>
|
||||
<div class='rounded-lg group-hover:bg-bg-primary p-1'>
|
||||
<div class='rounded-lg group-hover:bg-primary p-1'>
|
||||
<props.icon class={`w-[18px] h-[18px] stroke-slate ${props.danger ? 'group-hover:stroke-danger' : 'group-hover:stroke-white'}`} />
|
||||
</div>
|
||||
</button>
|
||||
|
|
@ -21,16 +22,25 @@ function Button(props: ButtonProps) {
|
|||
}
|
||||
|
||||
function WindowFrame() {
|
||||
const [isCloseModalVisible, setIsCloseModalVisible] = createSignal(false);
|
||||
const [isModalVisible, setModalVisible] = createSignal(false);
|
||||
|
||||
const minimize = () => Window.getCurrent().minimize();
|
||||
const quit = () => Window.getCurrent().close();
|
||||
|
||||
return (
|
||||
<div data-tauri-drag-region class="flex flex-row gap-2 justify-end items-center w-screen h-8 bg-bg-secondary pr-4">
|
||||
<Button icon={MinusIcon} onClick={() => Window.getCurrent().minimize()} />
|
||||
<Button icon={XCloseIcon} onClick={() => {
|
||||
setIsCloseModalVisible(!isCloseModalVisible());
|
||||
}} danger />
|
||||
<div data-tauri-drag-region class="flex flex-row gap-2 justify-end items-center w-screen h-8 bg-secondary pr-4">
|
||||
<TitlebarButton icon={MinusIcon} onClick={() => minimize()} />
|
||||
<TitlebarButton icon={XCloseIcon} onClick={() => setModalVisible(true)} danger />
|
||||
|
||||
<Modal title='Close' visible={isCloseModalVisible} setVisible={setIsCloseModalVisible} />
|
||||
<Modal
|
||||
title='Close OneLauncher?'
|
||||
visible={isModalVisible}
|
||||
setVisible={setModalVisible}
|
||||
buttons={[
|
||||
<Button onClick={() => setModalVisible(false)}>No</Button>,
|
||||
<Button onClick={() => quit()}>Yes</Button>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,71 @@
|
|||
import {
|
||||
Accessor,
|
||||
ParentProps, Setter, Show, createEffect, createSignal,
|
||||
ParentProps, Setter, Show, createEffect, createSignal, For,
|
||||
mergeProps,
|
||||
JSX,
|
||||
} from 'solid-js';
|
||||
import { Portal } from 'solid-js/web';
|
||||
import Button from '../base/Button';
|
||||
|
||||
type ModalButton = {
|
||||
text: string,
|
||||
class?: string,
|
||||
onClick?: (event: MouseEvent) => void,
|
||||
};
|
||||
type ModalButtonProps = string | JSX.Element;
|
||||
|
||||
function ModalButton(button: { props: ModalButtonProps }) {
|
||||
return <>
|
||||
{typeof button.props === 'string' ? (
|
||||
<Button>
|
||||
{button.props}
|
||||
</Button>
|
||||
) : (
|
||||
button.props
|
||||
)}
|
||||
</>;
|
||||
}
|
||||
|
||||
type ModalProps = {
|
||||
title: string,
|
||||
visible: Accessor<boolean>,
|
||||
setVisible: Setter<boolean>,
|
||||
buttons?: ModalButton[],
|
||||
buttons?: ModalButtonProps[],
|
||||
} & ParentProps;
|
||||
|
||||
function Modal(props: ModalProps) {
|
||||
const [localVisible, setLocalVisible] = createSignal(false);
|
||||
const [animate, setAnimate] = createSignal('animate-fade-in');
|
||||
const merged: ModalProps = mergeProps({ buttons: ['Ok'] }, props);
|
||||
|
||||
createEffect(() => {
|
||||
if (props.visible()) {
|
||||
setAnimate('animate-fade-in');
|
||||
setLocalVisible(true);
|
||||
} else {
|
||||
setAnimate('animate-fade-out');
|
||||
setTimeout(() => {
|
||||
setLocalVisible(false);
|
||||
}, 150);
|
||||
}
|
||||
});
|
||||
|
||||
function onBackdropClick() {
|
||||
setAnimate('animate-fade-out pointer-events-none');
|
||||
setTimeout(() => {
|
||||
props.setVisible(false);
|
||||
setAnimate('animate-fade-in pointer-events-auto');
|
||||
}, 150);
|
||||
props.setVisible(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Show when={props.visible()}>
|
||||
<Show when={localVisible()}>
|
||||
<Portal>
|
||||
<div class={`fixed z-[1000] top-0 left-0 w-screen h-screen bg-black/50 backdrop-blur-md ${animate()}`}>
|
||||
<div class='absolute w-full h-full' onClick={() => onBackdropClick()} />
|
||||
|
||||
<div class='absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
|
||||
<div class='bg-white p-4'>
|
||||
<h1>Hello World</h1>
|
||||
<div class='bg-primary border border-white/5 p-4 rounded-lg text-center flex flex-col gap-y-1'>
|
||||
<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>
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,36 @@
|
|||
export default {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
fontSize: {
|
||||
// Based off 16px
|
||||
sm: '0.688rem',
|
||||
md: '0.875rem',
|
||||
lg: '1rem',
|
||||
xl: '1.25rem',
|
||||
xxl: '1.5rem',
|
||||
'2xl': '2rem',
|
||||
},
|
||||
colors: {
|
||||
white: '#ffffff',
|
||||
black: '#000000',
|
||||
|
||||
slate: '#9BA1A6',
|
||||
danger: '#E5484D',
|
||||
bg: {
|
||||
primary: '#11171C',
|
||||
secondary: '#192026',
|
||||
|
||||
gray: {
|
||||
400: '#D5DBFF',
|
||||
},
|
||||
|
||||
component: {
|
||||
bg: {
|
||||
DEFAULT: '#1A2229',
|
||||
hover: '#171F25',
|
||||
pressed: '#222C35',
|
||||
},
|
||||
},
|
||||
|
||||
primary: '#11171C',
|
||||
secondary: '#192026',
|
||||
},
|
||||
extend: {
|
||||
animation: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue