Files
cloudbeaver/webapp/packages/plugin-projects/src/FolderDialog.tsx
T
ffe3d45017 dbeaver/pro#5789 replace Buttons styles (#3468)
* dbeaver/pro#5789 refactor: add ButtonIcon component instead of Button.Icon

* dbeaver/pro#5789 refactor: button components across whole application to use UIKit button and UIKit variables

- Rewrite Button.css(former button.module.css) file to define button styles for better maintainability and scalability
- Added props to the Button component for Icons: 'size' and 'placement', currently used only in PropertiesTable (Add property)
- Changed button modifiers to 'variant' swapping unelevated mod to primary(default one, so mod just deleted) and outlined mod to variant secondary.
- In one case changed raised mod to danger variant (deleting a license)

* dbeaver/pro#5789 refactor: fix Button component styling and removed redundant css file

* dbeaver/pro#5789 style: clean up button formatting across multiple components

* dbeaver/pro#5789 refactor: update Tailwind CSS import to use layer directive

* dbeaver/pro#5789 refactor[kit]: add disabled state styles for buttons

* dbeaver/pro#5789 refactor: enhance button styles for disabled state

* dbeaver/pro#5789 fix button problems

* dbeaver/pro#5789 fix: change layer name from theme to base in color styles

* dbeaver/pro#5789 refactor: update button padding from 16px to 15px

* dbeaver/pro#5789 refactor: adjust button gap and update InputFiles button props

* dbeaver/pro#5789 style: center align button in SSHKeyUploader component

* dbeaver/pro#5789 fix: update button classes to use inline-flex for better alignment

* dbeaver/pro#5789 fix: remove initial background from tab styles

* dbeaver/pro#5789 fix: move cursor pointer rule to button container

* dbeaver/pro#5789 fix: update checkbox SVG fill property for proper rendering

* dbeaver/pro#5789 fix: add variant prop to Button component for styling options

* dbeaver/pro#5789 fix: add InlineEditor to CSS layer to compete with theme Important! border-color

* dbeaver/pro#5789 fix: add min-width to button styles for consistency

* dbeaver/pro#5789 refactor: adjust margin for trigger element in context menu

first of all it's done to fix autotests and the second reason is to visually balance the cell with menu

* dbeaver/pro#5789 fix: make boolean great again

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
2025-05-27 16:59:01 +08:00

196 lines
5.8 KiB
TypeScript

/*
* CloudBeaver - Cloud Database Manager
* Copyright (C) 2020-2025 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0.
* you may not use this file except in compliance with the License.
*/
import { observable } from 'mobx';
import { observer } from 'mobx-react-lite';
import { useEffect } from 'react';
import {
Button,
CommonDialogBody,
CommonDialogFooter,
CommonDialogHeader,
CommonDialogWrapper,
Container,
Fill,
Form,
InputField,
s,
Translate,
useFocus,
useObservableRef,
useResource,
useS,
useTranslate,
} from '@cloudbeaver/core-blocks';
import type { DialogComponent } from '@cloudbeaver/core-dialogs';
import { type ProjectInfo, ProjectInfoResource } from '@cloudbeaver/core-projects';
import { createPath, throttleAsync } from '@cloudbeaver/core-utils';
import style from './FolderDialog.module.css';
import { ProjectSelect } from './ProjectSelect.js';
interface IFolderDialogState {
value: string;
projectId: string;
folder?: string;
message: string | undefined;
valid: boolean;
payload: FolderDialogPayload;
validationInProgress: boolean;
validate: () => Promise<void>;
setMessage: (message: string) => void;
setProjectId: (projectId: string) => void;
}
export interface IFolderDialogResult {
folder?: string;
name: string;
projectId: string;
}
export interface FolderDialogPayload {
value: string;
projectId: string;
folder?: string;
selectProject: boolean;
objectName?: string;
icon?: string;
bigIcon?: boolean;
viewBox?: string;
confirmActionText?: string;
create?: boolean;
title?: string;
validation?: (result: IFolderDialogResult, setMessage: (message: string) => void) => Promise<boolean> | boolean;
filterProject?: (project: ProjectInfo) => boolean;
}
export const FolderDialog: DialogComponent<FolderDialogPayload, IFolderDialogResult> = observer(function FolderDialog({
payload,
resolveDialog,
rejectDialog,
className,
}) {
const styles = useS(style);
const translate = useTranslate();
const [focusedRef] = useFocus<HTMLFormElement>({ focusFirstChild: true });
const { icon, folder, bigIcon, viewBox, value, projectId, selectProject, objectName, create, confirmActionText, filterProject } = payload;
let { title } = payload;
if (!title) {
title = create ? 'ui_create' : 'ui_rename';
}
title = translate(title);
if (objectName) {
title += ` ${translate(objectName)}`;
}
const state = useObservableRef<IFolderDialogState>(
() => ({
value,
projectId,
folder,
message: undefined,
valid: true,
validationInProgress: false,
validate: throttleAsync(async () => {
const { folder, value, projectId } = state;
state.message = undefined;
state.validationInProgress = true;
let valid: boolean | undefined;
try {
valid = await state.payload.validation?.({ folder, name: value, projectId }, (message: string) => {
if (state.folder === folder && state.value === value && state.projectId === projectId) {
state.setMessage(message);
}
});
} catch (exception: any) {
valid = false;
state.setMessage(exception.message);
}
if (state.folder === folder && state.value === value && state.projectId === projectId) {
state.valid = valid ?? true;
state.validationInProgress = false;
}
}, 300),
setMessage(message) {
this.message = message;
},
setProjectId(projectId) {
this.projectId = projectId;
this.folder = undefined;
},
}),
{
value: observable.ref,
projectId: observable.ref,
validationInProgress: observable.ref,
folder: observable.ref,
valid: observable.ref,
message: observable.ref,
},
{
payload,
},
);
const projectInfoLoader = useResource(FolderDialog, ProjectInfoResource, state.projectId);
async function resolveHandler() {
try {
await state.validate();
if (state.valid) {
resolveDialog({ folder: state.folder, name: state.value, projectId: state.projectId });
}
} catch {}
}
useEffect(() => {
state.validate().catch(() => {});
}, [state.value, state.projectId]);
const errorMessage = state.valid ? ' ' : translate(state.message ?? 'ui_rename_taken_or_invalid');
const subTitle = createPath(projectInfoLoader.data?.name ?? state.projectId, state.folder);
return (
<CommonDialogWrapper size="small" className={className} fixedWidth>
<CommonDialogHeader subTitle={subTitle} title={title} icon={icon} viewBox={viewBox} bigIcon={bigIcon} onReject={rejectDialog} />
<CommonDialogBody>
<Form ref={focusedRef} onSubmit={resolveHandler}>
<Container center gap>
{selectProject && <ProjectSelect value={state.projectId} filter={filterProject} onChange={projectId => state.setProjectId(projectId)} />}
<InputField
name="value"
state={state}
error={!state.valid}
description={errorMessage}
loading={state.validationInProgress}
onChange={() => state.validate().catch(() => {})}
>
{translate('ui_name')}
</InputField>
</Container>
</Form>
</CommonDialogBody>
<CommonDialogFooter className={s(styles, { footer: true })}>
<Button type="button" variant="secondary" onClick={rejectDialog}>
<Translate token="ui_processing_cancel" />
</Button>
<Fill />
<Button type="button" disabled={!state.valid} onClick={resolveHandler}>
<Translate token={confirmActionText || (create ? 'ui_create' : 'ui_rename')} />
</Button>
</CommonDialogFooter>
</CommonDialogWrapper>
);
});