1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

feat(system): path to upgrade standalone to BE [EE-4071] (#8095)

This commit is contained in:
Chaim Lev-Ari 2022-12-11 08:58:22 +02:00 committed by GitHub
parent 756ac034ec
commit 5cbf52377d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
73 changed files with 1374 additions and 421 deletions

View file

@ -12,15 +12,31 @@ export default {
interface TextFieldProps {
label: string;
tooltip?: string;
vertical?: boolean;
required?: boolean;
error?: string;
}
export { TextField, SelectField };
function TextField({ label, tooltip = '' }: TextFieldProps) {
function TextField({
label,
tooltip = '',
required,
error,
vertical,
}: TextFieldProps) {
const [value, setValue] = useState('');
const inputId = 'input';
return (
<FormControl inputId={inputId} label={label} tooltip={tooltip}>
<FormControl
inputId={inputId}
label={label}
tooltip={tooltip}
required={required}
errors={error}
size={vertical ? 'vertical' : undefined}
>
<Input
id={inputId}
type="text"
@ -34,9 +50,18 @@ function TextField({ label, tooltip = '' }: TextFieldProps) {
TextField.args = {
label: 'label',
tooltip: '',
vertical: false,
required: false,
error: '',
};
function SelectField({ label, tooltip = '' }: TextFieldProps) {
function SelectField({
label,
tooltip = '',
vertical,
required,
error,
}: TextFieldProps) {
const options = [
{ value: 1, label: 'one' },
{ value: 2, label: 'two' },
@ -44,7 +69,14 @@ function SelectField({ label, tooltip = '' }: TextFieldProps) {
const [value, setValue] = useState(0);
const inputId = 'input';
return (
<FormControl inputId={inputId} label={label} tooltip={tooltip}>
<FormControl
inputId={inputId}
label={label}
tooltip={tooltip}
size={vertical ? 'vertical' : undefined}
required={required}
errors={error}
>
<Select
className="form-control"
value={value}
@ -58,4 +90,7 @@ function SelectField({ label, tooltip = '' }: TextFieldProps) {
SelectField.args = {
label: 'select',
tooltip: '',
vertical: false,
required: false,
error: '',
};

View file

@ -5,9 +5,7 @@ import { Tooltip } from '@@/Tip/Tooltip';
import { FormError } from '../FormError';
import styles from './FormControl.module.css';
export type Size = 'xsmall' | 'small' | 'medium' | 'large';
export type Size = 'xsmall' | 'small' | 'medium' | 'large' | 'vertical';
export interface Props {
inputId?: string;
@ -29,7 +27,12 @@ export function FormControl({
required,
}: PropsWithChildren<Props>) {
return (
<div className={clsx('form-group', styles.container)}>
<div
className={clsx(
'form-group',
'after:content-[""] after:clear-both after:table' // to fix issues with float
)}
>
<label
htmlFor={inputId}
className={clsx(sizeClassLabel(size), 'control-label', 'text-left')}
@ -62,6 +65,8 @@ function sizeClassLabel(size?: Size) {
return 'col-sm-4 col-lg-3';
case 'xsmall':
return 'col-sm-2';
case 'vertical':
return '';
default:
return 'col-sm-3 col-lg-2';
}
@ -75,6 +80,8 @@ function sizeClassChildren(size?: Size) {
return 'col-sm-8 col-lg-9';
case 'xsmall':
return 'col-sm-8';
case 'vertical':
return '';
default:
return 'col-sm-9 col-lg-10';
}

View file

@ -0,0 +1,22 @@
.close {
color: var(--button-close-color);
opacity: var(--button-opacity);
padding: 0;
cursor: pointer;
background: transparent;
border: 0;
appearance: none;
font-size: 21px;
font-weight: bold;
line-height: 1;
text-shadow: 0 1px 0 #fff;
filter: alpha(opacity=20);
}
.close:hover,
.close:focus {
color: var(--button-close-color);
opacity: var(--button-opacity-hover);
}

View file

@ -0,0 +1,21 @@
import clsx from 'clsx';
import styles from './CloseButton.module.css';
export function CloseButton({
onClose,
className,
}: {
onClose: () => void;
className?: string;
}) {
return (
<button
type="button"
className={clsx(styles.close, className, 'absolute top-2 right-2')}
onClick={() => onClose()}
>
×
</button>
);
}

View file

@ -0,0 +1,20 @@
.modal-dialog {
width: 450px;
display: inline-block;
text-align: left;
vertical-align: middle;
}
.modal-content {
background-color: var(--bg-modal-content-color);
padding: 20px;
position: relative;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 6px;
outline: 0;
box-shadow: 0 5px 15px rgb(0 0 0 / 50%);
}

View file

@ -0,0 +1,53 @@
import { DialogContent, DialogOverlay } from '@reach/dialog';
import clsx from 'clsx';
import { createContext, PropsWithChildren, useContext } from 'react';
import { CloseButton } from './CloseButton';
import styles from './Modal.module.css';
const Context = createContext<boolean | null>(null);
Context.displayName = 'ModalContext';
export function useModalContext() {
const context = useContext(Context);
if (!context) {
throw new Error('should be nested under Modal');
}
return context;
}
interface Props {
onDismiss?(): void;
'aria-label'?: string;
'aria-labelledby'?: string;
}
export function Modal({
children,
onDismiss,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledBy,
}: PropsWithChildren<Props>) {
return (
<Context.Provider value>
<DialogOverlay
isOpen
className="flex items-center justify-center z-50"
onDismiss={onDismiss}
role="dialog"
>
<DialogContent
aria-label={ariaLabel}
aria-labelledby={ariaLabelledBy}
className={clsx(styles.modalDialog, 'p-0 bg-transparent')}
>
<div className={clsx(styles.modalContent, 'relative')}>
{children}
{onDismiss && <CloseButton onClose={onDismiss} />}
</div>
</DialogContent>
</DialogOverlay>
</Context.Provider>
);
}

View file

@ -0,0 +1,4 @@
.modal-body {
padding: 10px 0px;
border-bottom: none;
}

View file

@ -0,0 +1,9 @@
import { PropsWithChildren } from 'react';
import { useModalContext } from './Modal';
import styles from './ModalBody.module.css';
export function ModalBody({ children }: PropsWithChildren<unknown>) {
useModalContext();
return <div className={styles.modalBody}>{children}</div>;
}

View file

@ -0,0 +1,5 @@
.modal-footer {
padding: 10px 0px;
border-top: none;
display: flex;
}

View file

@ -0,0 +1,15 @@
import clsx from 'clsx';
import { PropsWithChildren } from 'react';
import { useModalContext } from './Modal';
import styles from './ModalFooter.module.css';
export function ModalFooter({ children }: PropsWithChildren<unknown>) {
useModalContext();
return (
<div className={clsx(styles.modalFooter, 'flex justify-end')}>
{children}
</div>
);
}

View file

@ -0,0 +1,19 @@
.modal-header {
margin-bottom: 10px;
padding: 0px;
border-bottom: none;
}
.background-error {
padding-top: 55px;
background-image: url(~assets/images/icon-error.svg);
background-repeat: no-repeat;
background-position: top left;
}
.background-warning {
padding-top: 55px;
background-image: url(~assets/images/icon-warning.svg);
background-repeat: no-repeat;
background-position: top left;
}

View file

@ -0,0 +1,33 @@
import clsx from 'clsx';
import { ReactNode } from 'react';
import { ModalType } from './types';
import { useModalContext } from './Modal';
import styles from './ModalHeader.module.css';
interface Props {
title: ReactNode;
modalType?: ModalType;
}
export function ModalHeader({ title, modalType }: Props) {
useModalContext();
return (
<div className={styles.modalHeader}>
{modalType && (
<div
className={clsx({
[styles.backgroundError]: modalType === ModalType.Destructive,
[styles.backgroundWarning]: modalType === ModalType.Warn,
})}
/>
)}
{typeof title === 'string' ? (
<h5 className="font-bold">{title}</h5>
) : (
title
)}
</div>
);
}

View file

@ -0,0 +1,18 @@
import { Modal as MainComponent } from './Modal';
import { ModalHeader } from './ModalHeader';
import { ModalBody } from './ModalBody';
import { ModalFooter } from './ModalFooter';
interface WithSubComponents {
Header: typeof ModalHeader;
Body: typeof ModalBody;
Footer: typeof ModalFooter;
}
const Modal = MainComponent as typeof MainComponent & WithSubComponents;
Modal.Header = ModalHeader;
Modal.Body = ModalBody;
Modal.Footer = ModalFooter;
export { Modal };

View file

@ -0,0 +1,6 @@
export type OnSubmit<TResult> = (result?: TResult) => void;
export enum ModalType {
Warn = 'warning',
Destructive = 'error',
}