Use HTMLElementTagNameMap to unify both rendering and typing.master
@@ -2,10 +2,14 @@ import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
import { Button } from '@tesseract-design/web-base'; | |||
const ActionButtonDerivedElementComponent = 'button' as const; | |||
/** | |||
* Derived HTML element of the {@link ActionButton} component. | |||
*/ | |||
export type ActionButtonDerivedElement = HTMLButtonElement; | |||
export type ActionButtonDerivedElement = HTMLElementTagNameMap[ | |||
typeof ActionButtonDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link ActionButton} component. | |||
@@ -76,7 +80,7 @@ export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionB | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<button | |||
<ActionButtonDerivedElementComponent | |||
{...etcProps} | |||
data-testid="button" | |||
type={type} | |||
@@ -184,7 +188,7 @@ export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionB | |||
</svg> | |||
</span> | |||
)} | |||
</button> | |||
</ActionButtonDerivedElementComponent> | |||
)); | |||
ActionButton.displayName = 'ActionButton' as const; | |||
@@ -2,9 +2,7 @@ import * as React from 'react'; | |||
import { useClientSide, useFallbackId, useProxyInput } from '@modal-sh/react-utils'; | |||
import clsx from 'clsx'; | |||
const DEFAULT_ENHANCED_HEIGHT_PX = 64 as const; | |||
const DEFAULT_NON_ENHANCED_SIDE_HEIGHT_PX = 256 as const; | |||
const FileSelectBoxDefaultPreviewComponentDerivedElementComponent = 'div' as const; | |||
/** | |||
* Common props for the {@link FileSelectBoxProps.previewComponent|previewComponent prop} of the | |||
@@ -29,57 +27,9 @@ export interface CommonPreviewComponentProps<F extends Partial<File> = Partial<F | |||
mini?: boolean; | |||
} | |||
/** | |||
* Derived HTML element of the {@link FileSelectBox} component. | |||
*/ | |||
export type FileSelectBoxDerivedElement = HTMLInputElement; | |||
/** | |||
* Props of the {@link FileSelectBox} component. | |||
*/ | |||
export interface FileSelectBoxProps< | |||
F extends Partial<File> = Partial<File>, | |||
P extends CommonPreviewComponentProps<F> = CommonPreviewComponentProps<F> | |||
> extends Omit<React.HTMLProps<FileSelectBoxDerivedElement>, 'size' | 'type' | 'label' | 'list'> { | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Short textual description indicating the nature of the component's value. | |||
*/ | |||
label?: React.ReactNode, | |||
/** | |||
* Short textual description as guidelines for valid input values. | |||
*/ | |||
hint?: React.ReactNode, | |||
/** | |||
* Should the component be enhanced? | |||
*/ | |||
enhanced?: boolean, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
/** | |||
* Preview component for the selected file(s). | |||
*/ | |||
previewComponent?: React.ElementType<P>, | |||
/** | |||
* Reselect label. | |||
*/ | |||
reselectLabel?: string, | |||
/** | |||
* Clear label. | |||
*/ | |||
clearLabel?: string, | |||
} | |||
export type FileSelectBoxDefaultPreviewComponentDerivedElement = HTMLDivElement; | |||
export type FileSelectBoxDefaultPreviewComponentDerivedElement = HTMLElementTagNameMap[ | |||
typeof FileSelectBoxDefaultPreviewComponentDerivedElementComponent | |||
]; | |||
/** | |||
* Default component for the {@link FileSelectBoxProps.previewComponent|previewComponent prop} | |||
@@ -94,7 +44,7 @@ export const FileSelectBoxDefaultPreviewComponent = React.forwardRef< | |||
enhanced, | |||
disabled, | |||
}, forwardedRef) => ( | |||
<div | |||
<FileSelectBoxDefaultPreviewComponentDerivedElementComponent | |||
data-enhanced={enhanced} | |||
className={clsx({ | |||
'opacity-50': disabled, | |||
@@ -138,12 +88,76 @@ export const FileSelectBoxDefaultPreviewComponent = React.forwardRef< | |||
)} | |||
</> | |||
)} | |||
</div> | |||
</FileSelectBoxDefaultPreviewComponentDerivedElementComponent> | |||
)); | |||
const isButtonElement = (el: HTMLElement): el is HTMLButtonElement => el.tagName === 'BUTTON'; | |||
const DEFAULT_ENHANCED_HEIGHT_PX = 64 as const; | |||
const DEFAULT_NON_ENHANCED_SIDE_HEIGHT_PX = 256 as const; | |||
const FileSelectBoxRootElementComponent = 'div' as const; | |||
const isInputElement = (el: HTMLElement): el is HTMLInputElement => el.tagName === 'INPUT'; | |||
type FileSelectBoxRootElement = HTMLElementTagNameMap[typeof FileSelectBoxRootElementComponent]; | |||
const FileSelectBoxActionElementComponent = 'button' as const; | |||
type FileSelectBoxActionElement = HTMLElementTagNameMap[typeof FileSelectBoxActionElementComponent]; | |||
const FileSelectBoxDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link FileSelectBox} component. | |||
*/ | |||
export type FileSelectBoxDerivedElement = HTMLElementTagNameMap[typeof FileSelectBoxDerivedElementComponent]; | |||
/** | |||
* Props of the {@link FileSelectBox} component. | |||
*/ | |||
export interface FileSelectBoxProps< | |||
F extends Partial<File> = Partial<File>, | |||
P extends CommonPreviewComponentProps<F> = CommonPreviewComponentProps<F> | |||
> extends Omit<React.HTMLProps<FileSelectBoxDerivedElement>, 'size' | 'type' | 'label' | 'list'> { | |||
/** | |||
* Should the component display a border? | |||
*/ | |||
border?: boolean, | |||
/** | |||
* Should the component occupy the whole width of its parent? | |||
*/ | |||
block?: boolean, | |||
/** | |||
* Short textual description indicating the nature of the component's value. | |||
*/ | |||
label?: React.ReactNode, | |||
/** | |||
* Short textual description as guidelines for valid input values. | |||
*/ | |||
hint?: React.ReactNode, | |||
/** | |||
* Should the component be enhanced? | |||
*/ | |||
enhanced?: boolean, | |||
/** | |||
* Is the label hidden? | |||
*/ | |||
hiddenLabel?: boolean, | |||
/** | |||
* Preview component for the selected file(s). | |||
*/ | |||
previewComponent?: React.ElementType<P>, | |||
/** | |||
* Reselect label. | |||
*/ | |||
reselectLabel?: string, | |||
/** | |||
* Clear label. | |||
*/ | |||
clearLabel?: string, | |||
} | |||
const isButtonElement = (el: HTMLElement): el is FileSelectBoxActionElement => el.tagName === 'BUTTON'; | |||
const isInputElement = (el: HTMLElement): el is FileSelectBoxDerivedElement => el.tagName === 'INPUT'; | |||
const isKeyUpEvent = (e: React.SyntheticEvent): e is React.KeyboardEvent => e.type === 'keyup'; | |||
@@ -211,8 +225,8 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
const { defaultRef, handleChange: doSetFileList } = useProxyInput< | |||
React.ChangeEvent<FileSelectBoxDerivedElement> | |||
| React.MouseEvent<HTMLButtonElement> | |||
| React.DragEvent<HTMLDivElement> | |||
| React.MouseEvent<FileSelectBoxActionElement> | |||
| React.DragEvent<FileSelectBoxRootElement> | |||
| React.KeyboardEvent<FileSelectBoxDerivedElement> | |||
| React.ClipboardEvent<FileSelectBoxDerivedElement>, | |||
FileSelectBoxDerivedElement | |||
@@ -270,12 +284,12 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
const labelId = React.useId(); | |||
const id = useFallbackId(idProp); | |||
const cancelEvent: React.DragEventHandler<HTMLDivElement> = React.useCallback((e) => { | |||
const cancelEvent: React.DragEventHandler<FileSelectBoxRootElement> = React.useCallback((e) => { | |||
e.stopPropagation(); | |||
e.preventDefault(); | |||
}, []); | |||
const handleDrop: React.DragEventHandler<HTMLDivElement> = React.useCallback((e) => { | |||
const handleDrop: React.DragEventHandler<FileSelectBoxRootElement> = React.useCallback((e) => { | |||
cancelEvent(e); | |||
doSetFileList(e); | |||
}, [cancelEvent, doSetFileList]); | |||
@@ -312,7 +326,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
}, []); | |||
const handleReselectMouseDown: React.MouseEventHandler< | |||
HTMLButtonElement | |||
FileSelectBoxActionElement | |||
> = React.useCallback(() => { | |||
setAboutToSelect(true); | |||
setTimeout(() => { | |||
@@ -322,7 +336,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
}, [defaultRef]); | |||
const handleReselectMouseUp: React.MouseEventHandler< | |||
HTMLButtonElement | |||
FileSelectBoxActionElement | |||
> = React.useCallback(() => { | |||
const fileInput = defaultRef.current as FileSelectBoxDerivedElement; | |||
if (typeof fileInput.showPicker !== 'function') { | |||
@@ -333,7 +347,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
}, [defaultRef]); | |||
const handleDeleteMouseDown: React.MouseEventHandler< | |||
HTMLButtonElement | |||
FileSelectBoxActionElement | |||
> = React.useCallback(() => { | |||
setAboutToClear(true); | |||
setTimeout(() => { | |||
@@ -366,7 +380,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
const fileListArray = Array.from(fileList ?? []); | |||
return ( | |||
<div | |||
<FileSelectBoxRootElementComponent | |||
className={clsx( | |||
'relative rounded ring-secondary/50 group file-select-box', | |||
'focus-within:ring-4', | |||
@@ -401,7 +415,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
</label> | |||
)} | |||
{clientSide && ( | |||
<button | |||
<FileSelectBoxActionElementComponent | |||
type="button" | |||
disabled={disabled} | |||
tabIndex={-1} | |||
@@ -414,9 +428,9 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
onMouseUp={handleReselectMouseUp} | |||
> | |||
{placeholder} | |||
</button> | |||
</FileSelectBoxActionElementComponent> | |||
)} | |||
<input | |||
<FileSelectBoxDerivedElementComponent | |||
{...etcProps} | |||
id={id} | |||
disabled={disabled} | |||
@@ -513,7 +527,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
className="absolute bottom-0 left-0 w-full text-center h-12 box-border flex" | |||
> | |||
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full"> | |||
<button | |||
<FileSelectBoxActionElementComponent | |||
type="button" | |||
data-testid="reselect" | |||
disabled={disabled} | |||
@@ -533,10 +547,10 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
> | |||
{reselectLabel} | |||
</span> | |||
</button> | |||
</FileSelectBoxActionElementComponent> | |||
</div> | |||
<div className="w-0 flex-auto flex flex-col items-center justify-center h-full"> | |||
<button | |||
<FileSelectBoxActionElementComponent | |||
disabled={disabled} | |||
data-testid="clear" | |||
type="button" | |||
@@ -558,7 +572,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
> | |||
{clearLabel} | |||
</span> | |||
</button> | |||
</FileSelectBoxActionElementComponent> | |||
</div> | |||
</div> | |||
</React.Fragment> | |||
@@ -569,7 +583,7 @@ export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileS | |||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none group-focus-within:border-secondary" | |||
/> | |||
)} | |||
</div> | |||
</FileSelectBoxRootElementComponent> | |||
); | |||
}); | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
import clsx from 'clsx'; | |||
const ComboBoxDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link ComboBox} component. | |||
*/ | |||
export type ComboBoxDerivedElement = HTMLInputElement; | |||
export type ComboBoxDerivedElement = HTMLElementTagNameMap[ | |||
typeof ComboBoxDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link ComboBox} component. | |||
@@ -143,7 +147,7 @@ export const ComboBox = React.forwardRef<ComboBoxDerivedElement, ComboBoxProps>( | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<ComboBoxDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={forwardedRef} | |||
@@ -4,10 +4,14 @@ import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
const DropdownSelectDerivedElementComponent = 'select' as const; | |||
/** | |||
* Derived HTML element of the {@link DropdownSelect} component. | |||
*/ | |||
export type DropdownSelectDerivedElement = HTMLSelectElement; | |||
export type DropdownSelectDerivedElement = HTMLElementTagNameMap[ | |||
typeof DropdownSelectDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link DropdownSelect} component. | |||
@@ -127,7 +131,7 @@ export const DropdownSelect = React.forwardRef<DropdownSelectDerivedElement, Dro | |||
{' '} | |||
</> | |||
)} | |||
<select | |||
<DropdownSelectDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
@@ -150,7 +154,7 @@ export const DropdownSelect = React.forwardRef<DropdownSelectDerivedElement, Dro | |||
)} | |||
> | |||
{children} | |||
</select> | |||
</DropdownSelectDerivedElementComponent> | |||
{hint && ( | |||
<div | |||
data-testid="hint" | |||
@@ -4,10 +4,14 @@ import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
const MenuSelectDerivedElementComponent = 'select' as const; | |||
/** | |||
* Derived HTML element of the {@link MenuSelect} component. | |||
*/ | |||
export type MenuSelectDerivedElement = HTMLSelectElement; | |||
export type MenuSelectDerivedElement = HTMLElementTagNameMap[ | |||
typeof MenuSelectDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link MenuSelect} component. | |||
@@ -137,7 +141,7 @@ export const MenuSelect = React.forwardRef<MenuSelectDerivedElement, MenuSelectP | |||
{' '} | |||
</> | |||
)} | |||
<select | |||
<MenuSelectDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
@@ -4,10 +4,14 @@ import { Button } from '@tesseract-design/web-base'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
const RadioButtonDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link RadioButton} component. | |||
*/ | |||
export type RadioButtonDerivedElement = HTMLInputElement; | |||
export type RadioButtonDerivedElement = HTMLElementTagNameMap[ | |||
typeof RadioButtonDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link RadioButton} component. | |||
@@ -77,9 +81,9 @@ export const RadioButton = React.forwardRef<RadioButtonDerivedElement, RadioButt | |||
return ( | |||
<span | |||
className="contents" | |||
> | |||
<input | |||
className="contents" | |||
> | |||
<RadioButtonDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
type="radio" | |||
@@ -3,10 +3,14 @@ import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
const RadioTickBoxDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link RadioTickBox} component. | |||
*/ | |||
export type RadioTickBoxDerivedElement = HTMLInputElement; | |||
export type RadioTickBoxDerivedElement = HTMLElementTagNameMap[ | |||
typeof RadioTickBoxDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link RadioTickBox} component. | |||
@@ -65,7 +69,7 @@ export const RadioTickBox = React.forwardRef<RadioTickBoxDerivedElement, RadioTi | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
<RadioTickBoxDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
type="radio" | |||
@@ -2,10 +2,14 @@ import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
const ColorPickerDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link ColorPicker} component. | |||
*/ | |||
export type ColorPickerDerivedElement = HTMLInputElement; | |||
export type ColorPickerDerivedElement = HTMLElementTagNameMap[ | |||
typeof ColorPickerDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link ColorPicker} component. | |||
@@ -75,7 +79,7 @@ export const ColorPicker = React.forwardRef< | |||
}, | |||
)} | |||
> | |||
<input | |||
<ColorPickerDerivedElementComponent | |||
{...etcProps} | |||
className={clsx( | |||
'color-picker absolute top-0 left-0 w-full h-full overflow-hidden cursor-pointer disabled:cursor-not-allowed', | |||
@@ -3,10 +3,14 @@ import clsx from 'clsx'; | |||
import Color from 'color'; | |||
import * as convert from 'color-convert'; | |||
const SwatchDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link Swatch} component. | |||
*/ | |||
export type SwatchDerivedElement = HTMLInputElement; | |||
export type SwatchDerivedElement = HTMLElementTagNameMap[ | |||
typeof SwatchDerivedElementComponent | |||
]; | |||
type ColorValue = ConstructorParameters<typeof Color>[0]; | |||
@@ -56,7 +60,7 @@ export const Swatch = React.forwardRef<SwatchDerivedElement, SwatchProps>(({ | |||
)} | |||
style={style} | |||
> | |||
<input | |||
<SwatchDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
type="text" | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const EmailInputDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link EmailInput} component. | |||
*/ | |||
export type EmailInputDerivedElement = HTMLInputElement; | |||
export type EmailInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof EmailInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link EmailInput} component. | |||
@@ -124,7 +128,7 @@ export const EmailInput = React.forwardRef<EmailInputDerivedElement, EmailInputP | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<EmailInputDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={forwardedRef} | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const PatternTextInputDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link PatternTextInput} component. | |||
*/ | |||
export type PatternTextInputDerivedElement = HTMLInputElement; | |||
export type PatternTextInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof PatternTextInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link PatternTextInput} component. | |||
@@ -138,7 +142,7 @@ export const PatternTextInput = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<PatternTextInputDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={forwardedRef} | |||
@@ -4,10 +4,14 @@ import { useClientSide, useFallbackId, useProxyInput } from '@modal-sh/react-uti | |||
import PhoneInput, { Country, Value } from 'react-phone-number-input/input'; | |||
import clsx from 'clsx'; | |||
const PhoneNumberInputDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link PhoneNumberInput} component. | |||
*/ | |||
export type PhoneNumberInputDerivedElement = HTMLInputElement; | |||
export type PhoneNumberInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof PhoneNumberInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link PhoneNumberInput} component. | |||
@@ -155,7 +159,7 @@ export const PhoneNumberInput = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<PhoneNumberInputDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
value={value} | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const UrlInputDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link UrlInput} component. | |||
*/ | |||
export type UrlInputDerivedElement = HTMLInputElement; | |||
export type UrlInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof UrlInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link UrlInput} component. | |||
@@ -108,7 +112,7 @@ export const UrlInput = React.forwardRef<UrlInputDerivedElement, UrlInputProps>( | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<UrlInputDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={forwardedRef} | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
import { useClientSide, useFallbackId } from '@modal-sh/react-utils'; | |||
const MaskedTextInputDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link MaskedTextInput} component. | |||
*/ | |||
export type MaskedTextInputDerivedElement = HTMLInputElement; | |||
export type MaskedTextInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof MaskedTextInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link MaskedTextInput} component. | |||
@@ -191,7 +195,7 @@ export const MaskedTextInput = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<MaskedTextInputDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const MultilineTextInputDerivedElementComponent = 'textarea' as const; | |||
/** | |||
* Derived HTML element of the {@link MultilineTextInput} component. | |||
*/ | |||
export type MultilineTextInputDerivedElement = HTMLTextAreaElement; | |||
export type MultilineTextInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof MultilineTextInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link MultilineTextInput} component. | |||
@@ -116,7 +120,7 @@ export const MultilineTextInput = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<textarea | |||
<MultilineTextInputDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
aria-labelledby={labelId} | |||
@@ -3,10 +3,14 @@ import { TextControl } from '@tesseract-design/web-base'; | |||
import clsx from 'clsx'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const TextInputDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link TextInput} component. | |||
*/ | |||
export type TextInputDerivedElement = HTMLInputElement; | |||
export type TextInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof TextInputDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link TextInput} component. | |||
@@ -135,7 +139,7 @@ export const TextInput = React.forwardRef<TextInputDerivedElement, TextInputProp | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<TextInputDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={forwardedRef} | |||
@@ -1,10 +1,14 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
const BadgeDerivedElementComponent = 'span' as const; | |||
/** | |||
* Derived HTML element of the {@link Badge} component. | |||
*/ | |||
export type BadgeDerivedElement = HTMLSpanElement; | |||
export type BadgeDerivedElement = HTMLElementTagNameMap[ | |||
typeof BadgeDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link Badge} component. | |||
@@ -29,7 +33,7 @@ export const Badge = React.forwardRef<BadgeDerivedElement, BadgeProps>(( | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<span | |||
<BadgeDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
className={clsx( | |||
@@ -47,7 +51,7 @@ export const Badge = React.forwardRef<BadgeDerivedElement, BadgeProps>(( | |||
<span className="relative w-full"> | |||
{children} | |||
</span> | |||
</span> | |||
</BadgeDerivedElementComponent> | |||
)); | |||
Badge.displayName = 'Badge'; | |||
@@ -1,10 +1,20 @@ | |||
import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
const KeyValueTableDerivedElementComponent = 'dl' as const; | |||
/** | |||
* Derived HTML element of the {@link KeyValueTable} component. | |||
*/ | |||
export type KeyValueTableDerivedElement = HTMLDListElement; | |||
export type KeyValueTableDerivedElement = HTMLElementTagNameMap[ | |||
typeof KeyValueTableDerivedElementComponent | |||
]; | |||
const KeyValueTablePropertyElementComponent = 'div' as const; | |||
const KeyValueTableKeyElementComponent = 'dt' as const; | |||
const KeyValueTableValueElementComponent = 'dd' as const; | |||
/** | |||
* Individual property of the {@link KeyValueTable} component. | |||
@@ -51,7 +61,7 @@ export const KeyValueTable = React.forwardRef<KeyValueTableDerivedElement, KeyVa | |||
}, | |||
forwardedRef, | |||
) => ( | |||
<dl | |||
<KeyValueTableDerivedElementComponent | |||
{...etcProps} | |||
className={clsx( | |||
'grid gap-y-1 grid-cols-3', | |||
@@ -61,16 +71,16 @@ export const KeyValueTable = React.forwardRef<KeyValueTableDerivedElement, KeyVa | |||
style={style} | |||
> | |||
{properties.map((property) => typeof property === 'object' && property && ( | |||
<div | |||
<KeyValueTablePropertyElementComponent | |||
key={property.key} | |||
className={clsx('contents', property.className)} | |||
> | |||
<dt | |||
<KeyValueTableKeyElementComponent | |||
className={clsx(hiddenKeys && 'sr-only', 'pr-4')} | |||
> | |||
{property.key} | |||
</dt> | |||
<dd | |||
</KeyValueTableKeyElementComponent> | |||
<KeyValueTableValueElementComponent | |||
{...(property.valueProps ?? {})} | |||
className={clsx( | |||
'm-0 text-ellipsis overflow-hidden', | |||
@@ -80,10 +90,10 @@ export const KeyValueTable = React.forwardRef<KeyValueTableDerivedElement, KeyVa | |||
)} | |||
> | |||
{property.valueProps?.children} | |||
</dd> | |||
</div> | |||
</KeyValueTableValueElementComponent> | |||
</KeyValueTablePropertyElementComponent> | |||
))} | |||
</dl> | |||
</KeyValueTableDerivedElementComponent> | |||
)); | |||
KeyValueTable.displayName = 'KeyValueTable'; | |||
@@ -4,10 +4,14 @@ import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const MenuMultiSelectDerivedElementComponent = 'select' as const; | |||
/** | |||
* Derived HTML element of the {@link MenuMultiSelect} component. | |||
*/ | |||
export type MenuMultiSelectDerivedElement = HTMLSelectElement; | |||
export type MenuMultiSelectDerivedElement = HTMLElementTagNameMap[ | |||
typeof MenuMultiSelectDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link MenuMultiSelect} component. | |||
@@ -144,7 +148,7 @@ export const MenuMultiSelect = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<select | |||
<MenuMultiSelectDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||
@@ -24,10 +24,14 @@ const TAG_INPUT_VALUE_SEPARATOR_MAP: Record<TagInputSeparator, string> = { | |||
'semicolon': ';', | |||
} as const; | |||
const TagInputDerivedElementComponent = ['textarea', 'input'] as const; | |||
/** | |||
* Derived HTML element of the {@link TagInput} component. | |||
*/ | |||
export type TagInputDerivedElement = HTMLTextAreaElement | HTMLInputElement; | |||
export type TagInputDerivedElement = HTMLElementTagNameMap[ | |||
typeof TagInputDerivedElementComponent[number] | |||
]; | |||
/** | |||
* Proxied HTML element of the {@link TagInput} component. | |||
@@ -85,7 +89,7 @@ export interface TagInputProps extends Omit<React.HTMLProps<TagInputDerivedEleme | |||
/** | |||
* Fallback element for non-enhanced mode. | |||
*/ | |||
fallbackElement?: 'textarea' | 'input', | |||
fallbackElement?: typeof TagInputDerivedElementComponent[number], | |||
/** | |||
* Separator used on the value of the input. | |||
*/ | |||
@@ -236,12 +240,14 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( | |||
onBlur, | |||
editOnRemove = false as const, | |||
placeholder, | |||
fallbackElement: FallbackElement = 'textarea' as const, | |||
fallbackElement: FallbackElement = TagInputDerivedElementComponent[0], | |||
...etcProps | |||
}, | |||
forwardedRef, | |||
) => { | |||
const EffectiveFallbackElement = valueSeparator === 'newline' ? 'textarea' : FallbackElement; | |||
const EffectiveFallbackElement = valueSeparator === 'newline' | |||
? TagInputDerivedElementComponent[0] | |||
: FallbackElement; | |||
const { clientSide } = useClientSide({ clientSide: enhancedProp }); | |||
const [tags, setTags] = React.useState<string[]>(() => { | |||
const effectiveValue = value ?? defaultValue; | |||
@@ -537,12 +543,12 @@ TagInput.defaultProps = { | |||
indicator: undefined, | |||
size: 'medium' as const, | |||
variant: 'default' as const, | |||
separator: ['newline'], | |||
separator: ['newline' as const], | |||
border: false as const, | |||
block: false as const, | |||
hiddenLabel: false as const, | |||
enhanced: false as const, | |||
editOnRemove: false as const, | |||
fallbackElement: 'textarea' as const, | |||
fallbackElement: TagInputDerivedElementComponent[0], | |||
valueSeparator: 'newline' as const, | |||
}; |
@@ -4,10 +4,14 @@ import { Button } from '@tesseract-design/web-base'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const ToggleButtonDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link ToggleButton} component. | |||
*/ | |||
export type ToggleButtonDerivedElement = HTMLInputElement; | |||
export type ToggleButtonDerivedElement = HTMLElementTagNameMap[ | |||
typeof ToggleButtonDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link ToggleButton} component. | |||
@@ -101,7 +105,7 @@ export const ToggleButton = React.forwardRef<ToggleButtonDerivedElement, ToggleB | |||
<span | |||
className="contents" | |||
> | |||
<input | |||
<ToggleButtonDerivedElementComponent | |||
{...etcProps} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
type="checkbox" | |||
@@ -3,10 +3,14 @@ import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const ToggleSwitchDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link ToggleSwitch} component. | |||
*/ | |||
export type ToggleSwitchDerivedElement = HTMLInputElement; | |||
export type ToggleSwitchDerivedElement = HTMLElementTagNameMap[ | |||
typeof ToggleSwitchDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link ToggleSwitch} component. | |||
@@ -198,7 +202,7 @@ export const ToggleSwitch = React.forwardRef<ToggleSwitchDerivedElement, ToggleS | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
<ToggleSwitchDerivedElementComponent | |||
{...etcProps} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
type="checkbox" | |||
@@ -3,10 +3,14 @@ import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const ToggleTickBoxDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link ToggleTickBox} component. | |||
*/ | |||
export type ToggleTickBoxDerivedElement = HTMLInputElement; | |||
export type ToggleTickBoxDerivedElement = HTMLElementTagNameMap[ | |||
typeof ToggleTickBoxDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link ToggleTickBox} component. | |||
@@ -87,7 +91,7 @@ export const ToggleTickBox = React.forwardRef<ToggleTickBoxDerivedElement, Toggl | |||
style={style} | |||
data-testid="base" | |||
> | |||
<input | |||
<ToggleTickBoxDerivedElementComponent | |||
{...etcProps} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
type="checkbox" | |||
@@ -2,10 +2,14 @@ import * as React from 'react'; | |||
import clsx from 'clsx'; | |||
import { Button } from '@tesseract-design/web-base'; | |||
const LinkButtonDerivedElementComponent = 'a' as const; | |||
/** | |||
* Derived HTML element of the {@link LinkButton} component. | |||
*/ | |||
export type LinkButtonDerivedElement = HTMLAnchorElement; | |||
export type LinkButtonDerivedElement = HTMLElementTagNameMap[ | |||
typeof LinkButtonDerivedElementComponent | |||
]; | |||
interface LinkButtonCommonProps extends Omit<React.HTMLProps<LinkButtonDerivedElement>, 'href' | 'size'> { | |||
/** | |||
@@ -51,7 +55,7 @@ interface LinkButtonCommonProps extends Omit<React.HTMLProps<LinkButtonDerivedEl | |||
} | |||
interface LinkButtonAnchorProps extends Pick<React.HTMLProps<LinkButtonDerivedElement>, 'href'> { | |||
component: 'a'; | |||
component: typeof LinkButtonDerivedElementComponent; | |||
} | |||
interface LinkButtonComponentType { | |||
@@ -92,7 +96,7 @@ export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonP | |||
compact = false, | |||
className, | |||
block = false, | |||
component: EnabledComponent = 'a' as const, | |||
component: EnabledComponent = LinkButtonDerivedElementComponent, | |||
disabled = false, | |||
href, | |||
style, | |||
@@ -102,7 +106,7 @@ export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonP | |||
}, | |||
forwardedRef, | |||
) => { | |||
const Component = (disabled ? 'button' : EnabledComponent) as 'a'; | |||
const Component = (disabled ? 'button' : EnabledComponent) as typeof LinkButtonDerivedElementComponent; | |||
const extraProps = { | |||
disabled: disabled || undefined, | |||
}; | |||
@@ -237,7 +241,7 @@ LinkButton.defaultProps = { | |||
icon: undefined, | |||
iconAfterChildren: false as const, | |||
// eslint-disable-next-line react/default-props-match-prop-types | |||
component: 'a' as const, | |||
component: LinkButtonDerivedElementComponent, | |||
// eslint-disable-next-line react/default-props-match-prop-types | |||
href: undefined, | |||
}; | |||
@@ -4,10 +4,20 @@ import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useBrowser, useClientSide, useFallbackId } from '@modal-sh/react-utils'; | |||
const NumberSpinnerDerivedElementComponent = 'input' as const; | |||
/** | |||
* Derived HTML element of the {@link NumberSpinner} component. | |||
*/ | |||
export type NumberSpinnerDerivedElement = HTMLInputElement; | |||
export type NumberSpinnerDerivedElement = HTMLElementTagNameMap[ | |||
typeof NumberSpinnerDerivedElementComponent | |||
]; | |||
const NumberSpinnerActionElementComponent = 'button' as const; | |||
type NumberSpinnerActionElement = HTMLElementTagNameMap[ | |||
typeof NumberSpinnerActionElementComponent | |||
]; | |||
/** | |||
* Props of the {@link NumberSpinner} component. | |||
@@ -151,7 +161,7 @@ export const NumberSpinner = React.forwardRef<NumberSpinnerDerivedElement, Numbe | |||
return clickYRef.current < spinnerYRef.current; | |||
}; | |||
const doStepMouse: React.MouseEventHandler<HTMLButtonElement> = (e) => { | |||
const doStepMouse: React.MouseEventHandler<NumberSpinnerActionElement> = (e) => { | |||
if (spinEventSource.current === 'keyboard') { | |||
return; | |||
} | |||
@@ -285,7 +295,7 @@ export const NumberSpinner = React.forwardRef<NumberSpinnerDerivedElement, Numbe | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<NumberSpinnerDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={typeof ref === 'function' ? defaultRef : ref} | |||
@@ -357,7 +367,7 @@ export const NumberSpinner = React.forwardRef<NumberSpinnerDerivedElement, Numbe | |||
</div> | |||
)} | |||
{indicator && ( | |||
<button | |||
<NumberSpinnerActionElementComponent | |||
data-testid="indicator" | |||
type="button" | |||
className={clsx( | |||
@@ -413,7 +423,7 @@ export const NumberSpinner = React.forwardRef<NumberSpinnerDerivedElement, Numbe | |||
Step Down | |||
</span> | |||
</span> | |||
</button> | |||
</NumberSpinnerActionElementComponent> | |||
)} | |||
{border && ( | |||
<span | |||
@@ -43,15 +43,19 @@ export const AVAILABLE_SLIDER_ORIENTATIONS = ['horizontal', 'vertical'] as const | |||
*/ | |||
export type SliderOrientation = typeof AVAILABLE_SLIDER_ORIENTATIONS[number]; | |||
const SliderDerivedElementComponent = 'input'; | |||
/** | |||
* Derived HTML element of the {@link Slider} component. | |||
*/ | |||
export type SliderDerivedElement = HTMLInputElement; | |||
export type SliderDerivedElement = HTMLElementTagNameMap[ | |||
typeof SliderDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link Slider} component. | |||
*/ | |||
export interface SliderProps extends Omit<React.HTMLProps<HTMLInputElement>, 'type'> { | |||
export interface SliderProps extends Omit<React.HTMLProps<SliderDerivedElement>, 'type'> { | |||
/** | |||
* Orientation of the component. | |||
*/ | |||
@@ -245,7 +249,7 @@ export const Slider = React.forwardRef<SliderDerivedElement, SliderProps>(( | |||
forwardedRef, | |||
) => { | |||
const browser = useBrowser(); | |||
const defaultRef = React.useRef<HTMLInputElement>(null); | |||
const defaultRef = React.useRef<SliderDerivedElement>(null); | |||
const ref = forwardedRef ?? defaultRef; | |||
const tickMarkId = React.useId(); | |||
@@ -454,7 +458,7 @@ export const Slider = React.forwardRef<SliderDerivedElement, SliderProps>(( | |||
className="flex slider" | |||
data-orient={orient} | |||
> | |||
<input | |||
<SliderDerivedElementComponent | |||
{...etcProps} | |||
ref={ref} | |||
min={minValue} | |||
@@ -4,10 +4,14 @@ import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const DateDropdownDerivedElementComponent = 'input'; | |||
/** | |||
* Derived HTML element of the {@link DateDropdown} component. | |||
*/ | |||
export type DateDropdownDerivedElement = HTMLInputElement; | |||
export type DateDropdownDerivedElement = HTMLElementTagNameMap[ | |||
typeof DateDropdownDerivedElementComponent | |||
]; | |||
/** | |||
* Props of the {@link DateDropdown} component. | |||
@@ -139,7 +143,7 @@ export const DateDropdown = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<DateDropdownDerivedElementComponent | |||
{...etcProps} | |||
size={length} | |||
ref={forwardedRef} | |||
@@ -4,10 +4,14 @@ import clsx from 'clsx'; | |||
import { PluginCreator } from 'tailwindcss/types/config'; | |||
import { useFallbackId } from '@modal-sh/react-utils'; | |||
const TimeSpinnerDerivedElementComponent = 'input'; | |||
/** | |||
* Derived HTML element of the {@link TimeSpinner} component. | |||
*/ | |||
export type TimeSpinnerDerivedElement = HTMLInputElement; | |||
export type TimeSpinnerDerivedElement = HTMLElementTagNameMap[ | |||
typeof TimeSpinnerDerivedElementComponent | |||
]; | |||
type Digit = (0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9); | |||
@@ -161,7 +165,7 @@ export const TimeSpinner = React.forwardRef< | |||
{' '} | |||
</> | |||
)} | |||
<input | |||
<TimeSpinnerDerivedElementComponent | |||
{...etcProps} | |||
ref={forwardedRef} | |||
id={id} | |||