mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
feat(core-blocks): RadioGroup component
This commit is contained in:
@@ -6,10 +6,15 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { observer } from 'mobx-react';
|
||||
import { useContext, useCallback } from 'react';
|
||||
import styled, { css } from 'reshadow';
|
||||
|
||||
import { useStyles, composes } from '@cloudbeaver/core-theming';
|
||||
|
||||
import { FormContext } from './FormContext';
|
||||
import { RadioGroupContext } from './RadioGroupContext';
|
||||
|
||||
const radioStyles = composes(
|
||||
css`
|
||||
radio {
|
||||
@@ -52,21 +57,92 @@ const radioMod = {
|
||||
),
|
||||
};
|
||||
|
||||
type RadioProps = React.InputHTMLAttributes<HTMLInputElement> & {
|
||||
type BaseProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value' | 'checked'> & {
|
||||
mod?: (keyof typeof radioMod)[];
|
||||
}
|
||||
|
||||
export function Radio({
|
||||
type ControlledProps = BaseProps & {
|
||||
value?: string | number;
|
||||
checked?: boolean;
|
||||
onChange?(value: string | number, name: string): any;
|
||||
|
||||
state?: never;
|
||||
}
|
||||
|
||||
type ObjectProps<TKey extends keyof TState, TState> = BaseProps & {
|
||||
name: TKey;
|
||||
value: TState[TKey];
|
||||
state: TState;
|
||||
onChange?(value: TState[TKey], name: TKey): any;
|
||||
|
||||
checked?: never;
|
||||
}
|
||||
|
||||
type RadioType = {
|
||||
(props: ControlledProps): JSX.Element;
|
||||
<TKey extends keyof TState, TState>(props: ObjectProps<TKey, TState>): JSX.Element;
|
||||
}
|
||||
|
||||
export const Radio: RadioType = observer(function Radio({
|
||||
name: controlledName,
|
||||
value,
|
||||
state,
|
||||
id: controlledId,
|
||||
checked: controlledChecked,
|
||||
onChange,
|
||||
mod,
|
||||
id,
|
||||
className,
|
||||
children,
|
||||
...rest
|
||||
}: RadioProps) {
|
||||
}: ControlledProps | ObjectProps<any, any>) {
|
||||
const formContext = useContext(FormContext);
|
||||
const context = useContext(RadioGroupContext);
|
||||
|
||||
const name = context?.name || controlledName;
|
||||
|
||||
const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (!event.target.checked) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state) {
|
||||
state[name] = value;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
context.onChange(value);
|
||||
} else if (formContext) {
|
||||
formContext.onChange(value, name);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange(value, name);
|
||||
}
|
||||
}, [value, context, state, name, formContext, onChange]);
|
||||
|
||||
const id = controlledId ?? `${name}_${value}`;
|
||||
let checked = controlledChecked;
|
||||
|
||||
if (context) {
|
||||
checked = context.value === value;
|
||||
}
|
||||
|
||||
if (state) {
|
||||
checked = state[name] === value;
|
||||
}
|
||||
|
||||
return styled(useStyles(radioStyles, ...(mod || []).map(mod => radioMod[mod])))(
|
||||
<field as="div" className={className}>
|
||||
<radio as="div">
|
||||
<input type="radio" id={id} {...rest}/>
|
||||
<input
|
||||
{...rest}
|
||||
type="radio"
|
||||
id={id}
|
||||
name={name}
|
||||
value={value}
|
||||
checked={checked}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<radio-background as="div">
|
||||
<radio-outer-circle as="div"/>
|
||||
<radio-inner-circle as="div"/>
|
||||
@@ -76,4 +152,4 @@ export function Radio({
|
||||
<label htmlFor={id}>{children}</label>
|
||||
</field>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { observer } from 'mobx-react';
|
||||
import {
|
||||
useCallback, useMemo, useState, useContext
|
||||
} from 'react';
|
||||
|
||||
import { FormContext } from './FormContext';
|
||||
import { RadioGroupContext, IRadioGroupContext } from './RadioGroupContext';
|
||||
|
||||
type BaseProps = React.PropsWithChildren<{
|
||||
name: string;
|
||||
}>
|
||||
|
||||
type ControlledProps<T> = BaseProps & {
|
||||
value?: T;
|
||||
onChange?(value: T, name: string): any;
|
||||
|
||||
state?: never;
|
||||
}
|
||||
|
||||
type ObjectProps<TKey extends keyof TState, TState> = BaseProps & {
|
||||
name: TKey;
|
||||
state: TState;
|
||||
onChange?(value: TState[TKey], name: TKey): any;
|
||||
|
||||
value?: never;
|
||||
}
|
||||
|
||||
type RadioGroupType = {
|
||||
<T>(props: ControlledProps<T>): JSX.Element;
|
||||
<TKey extends keyof TState, TState>(props: ObjectProps<TKey, TState>): JSX.Element;
|
||||
}
|
||||
|
||||
export const RadioGroup: RadioGroupType = observer(function RadioGroup({
|
||||
name,
|
||||
value: controlledValue,
|
||||
state,
|
||||
onChange,
|
||||
children,
|
||||
}: ControlledProps<string | number> | ObjectProps<any, any>) {
|
||||
const formContext = useContext(FormContext);
|
||||
const [selfValue, setValue] = useState<string | number>();
|
||||
|
||||
const handleChange = useCallback((value: string | number) => {
|
||||
if (state) {
|
||||
state[name] = value;
|
||||
} else {
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange(value, name);
|
||||
}
|
||||
|
||||
if (formContext) {
|
||||
formContext.onChange(value, name);
|
||||
}
|
||||
|
||||
}, [name, state, formContext, onChange]);
|
||||
|
||||
const value = state ? state[name] : controlledValue ?? selfValue;
|
||||
|
||||
const context: IRadioGroupContext = useMemo(() => ({
|
||||
name,
|
||||
value,
|
||||
onChange: handleChange,
|
||||
}), [value, value, handleChange]);
|
||||
|
||||
return (
|
||||
<RadioGroupContext.Provider value={context}>
|
||||
{children}
|
||||
</RadioGroupContext.Provider>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* cloudbeaver - Cloud Database Manager
|
||||
* Copyright (C) 2020 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 { createContext } from 'react';
|
||||
|
||||
export interface IRadioGroupContext {
|
||||
value: string | number | undefined;
|
||||
name: string;
|
||||
onChange(value: string| number): any;
|
||||
}
|
||||
|
||||
export const RadioGroupContext = createContext<IRadioGroupContext | undefined>(undefined);
|
||||
@@ -19,6 +19,8 @@ export * from './FormControls/FormContext';
|
||||
export * from './FormControls/InputField';
|
||||
export * from './FormControls/InputGroup';
|
||||
export * from './FormControls/Radio';
|
||||
export * from './FormControls/RadioGroup';
|
||||
export * from './FormControls/RadioGroupContext';
|
||||
export * from './FormControls/ShadowInput';
|
||||
export * from './FormControls/SubmittingForm';
|
||||
export * from './FormControls/Switch';
|
||||
|
||||
Reference in New Issue
Block a user