@@ -2,7 +2,7 @@ import * as React from 'react'; | |||||
import clsx from 'clsx'; | import clsx from 'clsx'; | ||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | import * as ButtonBase from '@tesseract-design/web-base-button'; | ||||
type ActionButtonDerivedElement = HTMLButtonElement; | |||||
export type ActionButtonDerivedElement = HTMLButtonElement; | |||||
export interface ActionButtonProps extends Omit<React.HTMLProps<ActionButtonDerivedElement>, 'type' | 'size'> { | export interface ActionButtonProps extends Omit<React.HTMLProps<ActionButtonDerivedElement>, 'type' | 'size'> { | ||||
type?: ButtonBase.ButtonType; | type?: ButtonBase.ButtonType; | ||||
@@ -90,11 +90,16 @@ export const ActionButton = React.forwardRef<ActionButtonDerivedElement, ActionB | |||||
)} | )} | ||||
</span> | </span> | ||||
{badge && ( | {badge && ( | ||||
<span | |||||
data-testid="badge" | |||||
> | |||||
{badge} | |||||
</span> | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
data-testid="badge" | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
)} | )} | ||||
{menuItem && ( | {menuItem && ( | ||||
<span | <span | ||||
@@ -0,0 +1,9 @@ | |||||
import * as React from 'react'; | |||||
export interface CommonPreviewProps<F extends Partial<File> = Partial<File>> { | |||||
file?: F; | |||||
disabled?: boolean; | |||||
enhanced?: boolean; | |||||
} | |||||
export type FilePreviewComponent<T extends CommonPreviewProps = CommonPreviewProps> = (props: T) => React.ReactNode; |
@@ -15,14 +15,12 @@ import {SpectrogramCanvas, WaveformCanvas} from '@modal-soft/react-wavesurfer'; | |||||
import {Slider} from '@/categories/number/react'; | import {Slider} from '@/categories/number/react'; | ||||
import {KeyValueTable} from '@/categories/information/react'; | import {KeyValueTable} from '@/categories/information/react'; | ||||
import {useEnhanced} from '@modal-soft/react-utils'; | import {useEnhanced} from '@modal-soft/react-utils'; | ||||
import {CommonPreviewProps} from '@/categories/blob/react/common'; | |||||
type AudioFilePreviewDerivedElement = HTMLAudioElement; | |||||
export type AudioFilePreviewDerivedElement = HTMLAudioElement; | |||||
export interface AudioFilePreviewProps<F extends Partial<File> = Partial<File>> extends Omit<React.HTMLProps<AudioFilePreviewDerivedElement>, 'controls'> { | |||||
file?: F; | |||||
disabled?: boolean; | |||||
enhanced?: boolean; | |||||
} | |||||
export interface AudioFilePreviewProps<F extends Partial<File> = Partial<File>> | |||||
extends Omit<React.HTMLProps<AudioFilePreviewDerivedElement>, 'controls'>, CommonPreviewProps<F> {} | |||||
export const AudioFilePreview = React.forwardRef<AudioFilePreviewDerivedElement, AudioFilePreviewProps>(({ | export const AudioFilePreview = React.forwardRef<AudioFilePreviewDerivedElement, AudioFilePreviewProps>(({ | ||||
file, | file, | ||||
@@ -6,13 +6,12 @@ import clsx from 'clsx'; | |||||
import {KeyValueTable} from '@/categories/information/react'; | import {KeyValueTable} from '@/categories/information/react'; | ||||
import {BinaryDataCanvas} from '@modal-soft/react-binary-data-canvas'; | import {BinaryDataCanvas} from '@modal-soft/react-binary-data-canvas'; | ||||
import {useEnhanced} from '@modal-soft/react-utils'; | import {useEnhanced} from '@modal-soft/react-utils'; | ||||
import {CommonPreviewProps} from '@/categories/blob/react/common'; | |||||
type BinaryFilePreviewDerivedElement = HTMLDivElement; | |||||
export type BinaryFilePreviewDerivedElement = HTMLDivElement; | |||||
export interface BinaryFilePreviewProps<F extends Partial<File> = Partial<File>> extends React.HTMLProps<BinaryFilePreviewDerivedElement> { | |||||
file?: F; | |||||
enhanced?: boolean; | |||||
} | |||||
export interface BinaryFilePreviewProps<F extends Partial<File> = Partial<File>> | |||||
extends React.HTMLProps<BinaryFilePreviewDerivedElement>, CommonPreviewProps<F> {} | |||||
export const BinaryFilePreview = React.forwardRef<BinaryFilePreviewDerivedElement, BinaryFilePreviewProps>(({ | export const BinaryFilePreview = React.forwardRef<BinaryFilePreviewDerivedElement, BinaryFilePreviewProps>(({ | ||||
file, | file, | ||||
@@ -1,13 +1,28 @@ | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import {ContentType, FileWithResolvedContentType, getMimeTypeDescription} from '@/utils/blob'; | |||||
import { FilePreview } from '../FilePreview'; | |||||
import {ContentType, FileWithResolvedContentType, getContentType, getMimeTypeDescription} from '@/utils/blob'; | |||||
import {formatFileSize} from '@/utils/numeral'; | import {formatFileSize} from '@/utils/numeral'; | ||||
import {AudioMiniFilePreview} from '@tesseract-design/web-blob-react'; | |||||
import {AudioFilePreview} from '../AudioFilePreview'; | |||||
import {BinaryFilePreview} from '../BinaryFilePreview'; | |||||
import {ImageFilePreview} from '../ImageFilePreview'; | |||||
import {VideoFilePreview} from '../VideoFilePreview'; | |||||
import {TextFilePreview} from '../TextFilePreview'; | |||||
import {AudioMiniFilePreview} from '../AudioMiniFilePreview'; | |||||
import {delegateTriggerChangeEvent} from '@/utils/event'; | import {delegateTriggerChangeEvent} from '@/utils/event'; | ||||
import clsx from 'clsx'; | import clsx from 'clsx'; | ||||
import {useEnhanced} from '@modal-soft/react-utils'; | import {useEnhanced} from '@modal-soft/react-utils'; | ||||
import {FilePreviewComponent} from '@/categories/blob/react/common'; | |||||
export interface FileButtonProps extends Omit<React.HTMLProps<HTMLInputElement>, 'size' | 'type' | 'style' | 'label' | 'list'> { | |||||
const FILE_PREVIEW_COMPONENTS: Record<ContentType, FilePreviewComponent> = { | |||||
[ContentType.IMAGE]: ImageFilePreview, | |||||
[ContentType.AUDIO]: AudioFilePreview, | |||||
[ContentType.VIDEO]: VideoFilePreview, | |||||
[ContentType.BINARY]: BinaryFilePreview, | |||||
[ContentType.TEXT]: TextFilePreview, | |||||
}; | |||||
export type FileSelectBoxDerivedElement = HTMLInputElement; | |||||
export interface FileSelectBoxProps extends Omit<React.HTMLProps<FileSelectBoxDerivedElement>, 'size' | 'type' | 'style' | 'label' | 'list'> { | |||||
/** | /** | ||||
* Should the component display a border? | * Should the component display a border? | ||||
*/ | */ | ||||
@@ -31,52 +46,7 @@ export interface FileButtonProps extends Omit<React.HTMLProps<HTMLInputElement>, | |||||
hiddenLabel?: boolean, | hiddenLabel?: boolean, | ||||
} | } | ||||
const FilePreviewGrid = ({ | |||||
fileList: files, | |||||
}: { fileList?: FileList }) => { | |||||
if (!files) { | |||||
return null; | |||||
} | |||||
return ( | |||||
<div className="w-full h-full overflow-auto -mx-4 px-4"> | |||||
<div className="w-full grid gap-4 grid-cols-3"> | |||||
{Array.from(files).map((file: File, i) => { | |||||
const f = file as unknown as FileWithResolvedContentType; | |||||
return ( | |||||
<div | |||||
data-testid="selectedFileItem" | |||||
key={i} | |||||
className={`w-full aspect-square rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10`} | |||||
title={[f.name, getMimeTypeDescription(f.type), formatFileSize(f.size)].join(', ')} | |||||
> | |||||
{ | |||||
f.resolvedType === ContentType.IMAGE | |||||
&& typeof f?.url === 'string' | |||||
&& ( | |||||
<img | |||||
className="block w-full h-full object-center object-cover" | |||||
src={f.url} | |||||
alt={f.name} | |||||
data-testid="preview" | |||||
/> | |||||
) | |||||
} | |||||
{ | |||||
f.resolvedType === ContentType.AUDIO | |||||
&& ( | |||||
<AudioMiniFilePreview file={f} /> | |||||
) | |||||
} | |||||
</div> | |||||
); | |||||
})} | |||||
</div> | |||||
</div> | |||||
) | |||||
} | |||||
export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps>( | |||||
export const FileSelectBox = React.forwardRef<FileSelectBoxDerivedElement, FileSelectBoxProps>( | |||||
( | ( | ||||
{ | { | ||||
label = '', | label = '', | ||||
@@ -91,7 +61,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps> | |||||
className, | className, | ||||
id: idProp, | id: idProp, | ||||
...etcProps | ...etcProps | ||||
}: FileButtonProps, | |||||
}: FileSelectBoxProps, | |||||
forwardedRef, | forwardedRef, | ||||
) => { | ) => { | ||||
const { enhanced } = useEnhanced({ enhanced: enhancedProp }); | const { enhanced } = useEnhanced({ enhanced: enhancedProp }); | ||||
@@ -155,7 +125,7 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps> | |||||
}); | }); | ||||
} | } | ||||
const filesCount = React.useMemo(() => fileList?.length ?? 0, [fileList]); | |||||
const filesCount = fileList?.length ?? 0; | |||||
return ( | return ( | ||||
<div | <div | ||||
@@ -174,7 +144,6 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps> | |||||
data-testid="clickArea" | data-testid="clickArea" | ||||
htmlFor={id} | htmlFor={id} | ||||
/> | /> | ||||
<input | <input | ||||
{...etcProps} | {...etcProps} | ||||
id={id} | id={id} | ||||
@@ -237,23 +206,68 @@ export const FileSelectBox = React.forwardRef<HTMLInputElement, FileButtonProps> | |||||
{ | { | ||||
multiple | multiple | ||||
&& ( | && ( | ||||
<FilePreviewGrid fileList={fileList} /> | |||||
<div className="w-full h-full overflow-auto -mx-4 px-4"> | |||||
<div className="w-full grid gap-4 grid-cols-3"> | |||||
{Array.from(fileList ?? []).map((file: File) => { | |||||
const f = file as unknown as FileWithResolvedContentType; | |||||
const fileContentType = getContentType(file.type, file.name); | |||||
return ( | |||||
<div | |||||
data-testid="selectedFileItem" | |||||
key={f?.url ?? f?.name} | |||||
className={`w-full aspect-square rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10`} | |||||
title={[f.name, getMimeTypeDescription(f.type), formatFileSize(f.size)].join(', ')} | |||||
> | |||||
{ | |||||
fileContentType === ContentType.IMAGE | |||||
&& typeof f?.url === 'string' | |||||
&& ( | |||||
<img | |||||
className="block w-full h-full object-center object-cover" | |||||
src={f.url} | |||||
alt={f.name} | |||||
data-testid="preview" | |||||
/> | |||||
) | |||||
} | |||||
{ | |||||
fileContentType === ContentType.AUDIO | |||||
&& ( | |||||
<AudioMiniFilePreview file={f} /> | |||||
) | |||||
} | |||||
</div> | |||||
); | |||||
})} | |||||
</div> | |||||
</div> | |||||
) | ) | ||||
} | } | ||||
{ | { | ||||
!multiple | !multiple | ||||
&& ( | |||||
<div | |||||
className={`w-full h-full`} | |||||
> | |||||
<div data-testid="selectedFileItem" className={`h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10`}> | |||||
<FilePreview | |||||
className="w-full h-full relative" | |||||
fileList={fileList} | |||||
/> | |||||
&& Array.from(fileList ?? []).map((file) => { | |||||
const f = file as unknown as FileWithResolvedContentType; | |||||
const fileContentType = getContentType(file.type, file.name); | |||||
const { [fileContentType]: FilePreviewComponent = BinaryFilePreview } = FILE_PREVIEW_COMPONENTS; | |||||
return ( | |||||
<div | |||||
key={f?.url ?? f?.name} | |||||
className="w-full h-full" | |||||
> | |||||
<div | |||||
data-testid="selectedFileItem" | |||||
className="h-full w-full p-4 box-border rounded overflow-hidden relative before:absolute before:content-[''] before:bg-current before:top-0 before:left-0 before:w-full before:h-full before:opacity-10" | |||||
> | |||||
<FilePreviewComponent | |||||
className="w-full h-full relative" | |||||
file={f} | |||||
enhanced={enhanced} | |||||
disabled={disabled} | |||||
/> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | |||||
) | |||||
) | |||||
}) | |||||
} | } | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -5,111 +5,13 @@ import clsx from 'clsx'; | |||||
import {useFileMetadata, useFileUrl, useImageControls} from '@/categories/blob/react'; | import {useFileMetadata, useFileUrl, useImageControls} from '@/categories/blob/react'; | ||||
import {KeyValueTable} from '@/categories/information/react'; | import {KeyValueTable} from '@/categories/information/react'; | ||||
import {useEnhanced} from '@modal-soft/react-utils'; | import {useEnhanced} from '@modal-soft/react-utils'; | ||||
import {CommonPreviewProps} from '@/categories/blob/react/common'; | |||||
import {Swatch} from '@tesseract-design/web-color-react'; | |||||
type RgbTuple = [number, number, number]; | |||||
export type ImageFilePreviewDerivedElement = HTMLImageElement; | |||||
type SwatchDerivedElement = HTMLInputElement; | |||||
export interface SwatchProps extends Omit<React.HTMLProps<SwatchDerivedElement>, 'color'> { | |||||
color: RgbTuple; | |||||
mode?: 'rgb' | 'hsl'; | |||||
} | |||||
export const useSwatchControls = () => { | |||||
const id = React.useId(); | |||||
const copyColor: React.ReactEventHandler<SwatchDerivedElement> = React.useCallback(async (e) => { | |||||
const { value } = e.currentTarget; | |||||
await window.navigator.clipboard.writeText(value); | |||||
}, []); | |||||
return React.useMemo(() => ({ | |||||
id, | |||||
copyColor, | |||||
}), [id, copyColor]); | |||||
}; | |||||
export const Swatch = React.forwardRef<SwatchDerivedElement, SwatchProps>(({ | |||||
color, | |||||
mode = 'rgb', | |||||
className, | |||||
style, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const { id, copyColor } = useSwatchControls(); | |||||
const colorValue = `${mode}(${color.join(', ')})`; | |||||
return ( | |||||
<span | |||||
className={clsx( | |||||
'inline-block align-middle', | |||||
className, | |||||
)} | |||||
style={style} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={forwardedRef} | |||||
type="text" | |||||
value={colorValue} | |||||
className="sr-only select-all peer" | |||||
readOnly | |||||
id={id} | |||||
onSelect={copyColor} | |||||
/> | |||||
<label | |||||
className={clsx( | |||||
'relative rounded ring-secondary/50 whitespace-nowrap inline-block align-top leading-none cursor-pointer', // todo eyedropper cursor | |||||
'peer-focus:outline-0 peer-focus:ring-4', | |||||
'peer-active:ring-tertiary/50', | |||||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed', | |||||
)} | |||||
title={colorValue} | |||||
htmlFor={id} | |||||
> | |||||
<span | |||||
className="inline-block w-5 h-5 align-middle border border-[#ffffff]" | |||||
> | |||||
<span | |||||
className="block w-full h-full border border-[#000000]" | |||||
style={{ | |||||
backgroundColor: `${mode}(${color.join(' ')})`, | |||||
}} | |||||
/> | |||||
</span> | |||||
<span className="tabular-nums text-xs sr-only"> | |||||
{ | |||||
color | |||||
.map((c) => c | |||||
.toString() | |||||
.padStart(4, ' ') | |||||
.split('') | |||||
.map((cc, j) => ( | |||||
<span | |||||
key={`${cc}:${j}`} | |||||
className={clsx({ | |||||
'opacity-0': cc === ' ', | |||||
})} | |||||
> | |||||
{j === 0 && ' '} | |||||
{cc === ' ' && j > 0 ? '0' : cc} | |||||
</span> | |||||
)) | |||||
) | |||||
} | |||||
</span> | |||||
</label> | |||||
</span> | |||||
); | |||||
}); | |||||
Swatch.displayName = 'Swatch'; | |||||
type ImageFilePreviewDerivedElement = HTMLImageElement; | |||||
export interface ImageFilePreviewProps<F extends Partial<File> = Partial<File>> extends Omit<React.HTMLProps<ImageFilePreviewDerivedElement>, 'src' | 'alt'> { | |||||
file?: F; | |||||
disabled?: boolean; | |||||
enhanced?: boolean; | |||||
} | |||||
export interface ImageFilePreviewProps<F extends Partial<File> = Partial<File>> | |||||
extends Omit<React.HTMLProps<ImageFilePreviewDerivedElement>, 'src' | 'alt'>, CommonPreviewProps<F> {} | |||||
export const ImageFilePreview = React.forwardRef<ImageFilePreviewDerivedElement, ImageFilePreviewProps>(({ | export const ImageFilePreview = React.forwardRef<ImageFilePreviewDerivedElement, ImageFilePreviewProps>(({ | ||||
file, | file, | ||||
@@ -6,13 +6,12 @@ import clsx from 'clsx'; | |||||
import {KeyValueTable} from '@/categories/information/react'; | import {KeyValueTable} from '@/categories/information/react'; | ||||
import {Refractor} from '@modal-soft/react-refractor'; | import {Refractor} from '@modal-soft/react-refractor'; | ||||
import {useEnhanced} from '@modal-soft/react-utils'; | import {useEnhanced} from '@modal-soft/react-utils'; | ||||
import {CommonPreviewProps} from '@/categories/blob/react/common'; | |||||
type TextFilePreviewDerivedComponent = HTMLDivElement; | type TextFilePreviewDerivedComponent = HTMLDivElement; | ||||
export interface TextFilePreviewProps<F extends Partial<File> = Partial<File>> extends React.HTMLProps<TextFilePreviewDerivedComponent> { | |||||
file?: F; | |||||
enhanced?: boolean; | |||||
} | |||||
export interface TextFilePreviewProps<F extends Partial<File> = Partial<File>> | |||||
extends React.HTMLProps<TextFilePreviewDerivedComponent>, CommonPreviewProps<F> {} | |||||
export const TextFilePreview = React.forwardRef<TextFilePreviewDerivedComponent, TextFilePreviewProps>(({ | export const TextFilePreview = React.forwardRef<TextFilePreviewDerivedComponent, TextFilePreviewProps>(({ | ||||
file, | file, | ||||
@@ -6,14 +6,12 @@ import clsx from 'clsx'; | |||||
import {Slider} from '@tesseract-design/web-number-react'; | import {Slider} from '@tesseract-design/web-number-react'; | ||||
import {KeyValueTable} from '@/categories/information/react'; | import {KeyValueTable} from '@/categories/information/react'; | ||||
import {useEnhanced} from '@modal-soft/react-utils'; | import {useEnhanced} from '@modal-soft/react-utils'; | ||||
import {CommonPreviewProps} from '@/categories/blob/react/common'; | |||||
type VideoFilePreviewDerivedComponent = HTMLVideoElement; | |||||
export type VideoFilePreviewDerivedComponent = HTMLVideoElement; | |||||
export interface VideoFilePreviewProps<F extends Partial<File> = Partial<File>> extends Omit<React.HTMLProps<VideoFilePreviewDerivedComponent>, 'controls'> { | |||||
file?: F; | |||||
disabled?: boolean; | |||||
enhanced?: boolean; | |||||
} | |||||
export interface VideoFilePreviewProps<F extends Partial<File> = Partial<File>> | |||||
extends Omit<React.HTMLProps<VideoFilePreviewDerivedComponent>, 'controls'>, CommonPreviewProps<F> {} | |||||
export const VideoFilePreview = React.forwardRef<VideoFilePreviewDerivedComponent, VideoFilePreviewProps>(({ | export const VideoFilePreview = React.forwardRef<VideoFilePreviewDerivedComponent, VideoFilePreviewProps>(({ | ||||
file, | file, | ||||
@@ -0,0 +1,99 @@ | |||||
import * as React from 'react'; | |||||
import clsx from 'clsx'; | |||||
type RgbTuple = [number, number, number]; | |||||
export type SwatchDerivedElement = HTMLInputElement; | |||||
export interface SwatchProps extends Omit<React.HTMLProps<SwatchDerivedElement>, 'color'> { | |||||
color: RgbTuple; | |||||
mode?: 'rgb' | 'hsl'; | |||||
} | |||||
export const useSwatchControls = () => { | |||||
const id = React.useId(); | |||||
const copyColor: React.ReactEventHandler<SwatchDerivedElement> = React.useCallback(async (e) => { | |||||
const { value } = e.currentTarget; | |||||
await window.navigator.clipboard.writeText(value); | |||||
}, []); | |||||
return React.useMemo(() => ({ | |||||
id, | |||||
copyColor, | |||||
}), [id, copyColor]); | |||||
}; | |||||
export const Swatch = React.forwardRef<SwatchDerivedElement, SwatchProps>(({ | |||||
color, | |||||
mode = 'rgb', | |||||
className, | |||||
style, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const { id, copyColor } = useSwatchControls(); | |||||
const colorValue = `${mode}(${color.join(', ')})`; | |||||
return ( | |||||
<span | |||||
className={clsx( | |||||
'inline-block align-middle', | |||||
className, | |||||
)} | |||||
style={style} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={forwardedRef} | |||||
type="text" | |||||
value={colorValue} | |||||
className="sr-only select-all peer" | |||||
readOnly | |||||
id={id} | |||||
onSelect={copyColor} | |||||
/> | |||||
<label | |||||
className={clsx( | |||||
'relative rounded ring-secondary/50 whitespace-nowrap inline-block align-top leading-none cursor-pointer', // todo eyedropper cursor | |||||
'peer-focus:outline-0 peer-focus:ring-4', | |||||
'peer-active:ring-tertiary/50', | |||||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed', | |||||
)} | |||||
title={colorValue} | |||||
htmlFor={id} | |||||
> | |||||
<span | |||||
className="inline-block w-5 h-5 align-middle border border-[#ffffff]" | |||||
> | |||||
<span | |||||
className="block w-full h-full border border-[#000000]" | |||||
style={{ | |||||
backgroundColor: `${mode}(${color.join(' ')})`, | |||||
}} | |||||
/> | |||||
</span> | |||||
<span className="tabular-nums text-xs sr-only"> | |||||
{ | |||||
color | |||||
.map((c) => c | |||||
.toString() | |||||
.padStart(4, ' ') | |||||
.split('') | |||||
.map((cc, j) => ( | |||||
<span | |||||
key={`${cc}:${j}`} | |||||
className={clsx({ | |||||
'opacity-0': cc === ' ', | |||||
})} | |||||
> | |||||
{j === 0 && ' '} | |||||
{cc === ' ' && j > 0 ? '0' : cc} | |||||
</span> | |||||
)) | |||||
) | |||||
} | |||||
</span> | |||||
</label> | |||||
</span> | |||||
); | |||||
}); | |||||
Swatch.displayName = 'Swatch'; |
@@ -0,0 +1 @@ | |||||
export * from './components/Swatch'; |
@@ -74,20 +74,30 @@ export const LinkButton = React.forwardRef<LinkButtonDerivedElement, LinkButtonP | |||||
{children} | {children} | ||||
</span> | </span> | ||||
{subtext && ( | {subtext && ( | ||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | )} | ||||
</span> | </span> | ||||
{badge && ( | {badge && ( | ||||
<span | |||||
data-testid="badge" | |||||
> | |||||
{badge} | |||||
</span> | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
data-testid="badge" | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
)} | )} | ||||
{menuItem && ( | {menuItem && ( | ||||
<span | <span | ||||
@@ -61,141 +61,151 @@ export const ComboBox = React.forwardRef<ComboBoxDerivedElement, ComboBoxProps>( | |||||
variant = 'default' as const, | variant = 'default' as const, | ||||
hiddenLabel = false, | hiddenLabel = false, | ||||
className, | className, | ||||
children, | |||||
...etcProps | ...etcProps | ||||
}: ComboBoxProps, | }: ComboBoxProps, | ||||
ref, | ref, | ||||
) => { | ) => { | ||||
const labelId = React.useId(); | const labelId = React.useId(); | ||||
const datalistId = React.useId(); | |||||
return ( | return ( | ||||
<div | |||||
className={clsx( | |||||
'relative rounded ring-secondary/50', | |||||
'focus-within:ring-4', | |||||
{ | |||||
'block': block, | |||||
'inline-block align-middle': !block, | |||||
}, | |||||
className, | |||||
)} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={ref} | |||||
aria-labelledby={labelId} | |||||
type={type} | |||||
data-testid="input" | |||||
<> | |||||
<datalist | |||||
id={datalistId} | |||||
> | |||||
{children} | |||||
</datalist> | |||||
<div | |||||
className={clsx( | className={clsx( | ||||
'bg-negative rounded-inherit w-full peer block', | |||||
'focus:outline-0', | |||||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||||
{ | |||||
'text-xxs': size === 'small', | |||||
'text-xs': size === 'medium', | |||||
}, | |||||
{ | |||||
'pl-4': variant === 'default', | |||||
'pl-1.5': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pt-4': variant === 'alternate', | |||||
}, | |||||
'relative rounded ring-secondary/50', | |||||
'focus-within:ring-4', | |||||
{ | { | ||||
'pr-4': variant === 'default' && !indicator, | |||||
'pr-1.5': variant === 'alternate' && !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
{ | |||||
'h-10': size === 'small', | |||||
'h-12': size === 'medium', | |||||
'h-16': size === 'large', | |||||
'block': block, | |||||
'inline-block align-middle': !block, | |||||
}, | }, | ||||
className, | |||||
)} | )} | ||||
/> | |||||
{ | |||||
label && ( | |||||
<label | |||||
data-testid="label" | |||||
id={labelId} | |||||
className={clsx( | |||||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', | |||||
{ | |||||
'sr-only': hiddenLabel, | |||||
}, | |||||
{ | |||||
'pr-1': !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
<span className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||||
{label} | |||||
</span> | |||||
</label> | |||||
) | |||||
} | |||||
{hint && ( | |||||
<div | |||||
data-testid="hint" | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={ref} | |||||
aria-labelledby={labelId} | |||||
type={type} | |||||
list={datalistId} | |||||
data-testid="input" | |||||
className={clsx( | className={clsx( | ||||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative', | |||||
'bg-negative rounded-inherit w-full peer block', | |||||
'focus:outline-0', | |||||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||||
{ | |||||
'text-xxs': size === 'small', | |||||
'text-xs': size === 'medium', | |||||
}, | |||||
{ | { | ||||
'bottom-0 pl-4 pb-1': variant === 'default', | |||||
'top-0.5': variant === 'alternate', | |||||
'pl-4': variant === 'default', | |||||
'pl-1.5': variant === 'alternate', | |||||
}, | }, | ||||
{ | { | ||||
'pt-2': variant === 'alternate' && size === 'small', | |||||
'pt-3': variant === 'alternate' && size !== 'small', | |||||
'pt-4': variant === 'alternate', | |||||
}, | }, | ||||
{ | { | ||||
'pr-4': !indicator && variant === 'default', | |||||
'pr-1': !indicator && variant === 'alternate', | |||||
'pr-4': variant === 'default' && !indicator, | |||||
'pr-1.5': variant === 'alternate' && !indicator, | |||||
}, | }, | ||||
{ | { | ||||
'pr-10': indicator && size === 'small', | 'pr-10': indicator && size === 'small', | ||||
'pr-12': indicator && size === 'medium', | 'pr-12': indicator && size === 'medium', | ||||
'pr-16': indicator && size === 'large', | 'pr-16': indicator && size === 'large', | ||||
}, | }, | ||||
{ | |||||
'h-10': size === 'small', | |||||
'h-12': size === 'medium', | |||||
'h-16': size === 'large', | |||||
}, | |||||
)} | )} | ||||
> | |||||
/> | |||||
{ | |||||
label && ( | |||||
<label | |||||
data-testid="label" | |||||
id={labelId} | |||||
className={clsx( | |||||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', | |||||
{ | |||||
'sr-only': hiddenLabel, | |||||
}, | |||||
{ | |||||
'pr-1': !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
<span className="w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||||
{label} | |||||
</span> | |||||
</label> | |||||
) | |||||
} | |||||
{hint && ( | |||||
<div | <div | ||||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||||
data-testid="hint" | |||||
className={clsx( | |||||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative', | |||||
{ | |||||
'bottom-0 pl-4 pb-1': variant === 'default', | |||||
'top-0.5': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pt-2': variant === 'alternate' && size === 'small', | |||||
'pt-3': variant === 'alternate' && size !== 'small', | |||||
}, | |||||
{ | |||||
'pr-4': !indicator && variant === 'default', | |||||
'pr-1': !indicator && variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
)} | |||||
> | > | ||||
{hint} | |||||
<div | |||||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||||
> | |||||
{hint} | |||||
</div> | |||||
</div> | </div> | ||||
</div> | |||||
)} | |||||
{indicator && ( | |||||
<div | |||||
className={clsx( | |||||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none', | |||||
{ | |||||
'w-10': size === 'small', | |||||
'w-12': size === 'medium', | |||||
'w-16': size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
{indicator} | |||||
</div> | |||||
)} | |||||
{ | |||||
border && ( | |||||
<span | |||||
data-testid="border" | |||||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||||
/> | |||||
) | |||||
} | |||||
</div> | |||||
)} | |||||
{indicator && ( | |||||
<div | |||||
className={clsx( | |||||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none', | |||||
{ | |||||
'w-10': size === 'small', | |||||
'w-12': size === 'medium', | |||||
'w-16': size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
{indicator} | |||||
</div> | |||||
)} | |||||
{ | |||||
border && ( | |||||
<span | |||||
data-testid="border" | |||||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||||
/> | |||||
) | |||||
} | |||||
</div> | |||||
</> | |||||
); | ); | ||||
} | } | ||||
); | ); | ||||
@@ -1,23 +1,212 @@ | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | |||||
import clsx from 'clsx'; | |||||
type MenuSelectDerivedElement = HTMLSelectElement; | type MenuSelectDerivedElement = HTMLSelectElement; | ||||
export interface MenuSelectProps extends React.HTMLProps<MenuSelectDerivedElement> { | |||||
export interface MenuSelectProps extends Omit<React.HTMLProps<MenuSelectDerivedElement>, 'size' | 'style' | 'label'> { | |||||
/** | |||||
* 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, | |||||
/** | |||||
* Size of the component. | |||||
*/ | |||||
size?: TextControlBase.TextControlSize, | |||||
/** | |||||
* Additional description, usually graphical, indicating the nature of the component's value. | |||||
*/ | |||||
indicator?: React.ReactNode, | |||||
/** | |||||
* Should the component display a border? | |||||
*/ | |||||
border?: boolean, | |||||
/** | |||||
* Should the component occupy the whole width of its parent? | |||||
*/ | |||||
block?: boolean, | |||||
/** | |||||
* Style of the component. | |||||
*/ | |||||
variant?: TextControlBase.TextControlVariant, | |||||
/** | |||||
* Is the label hidden? | |||||
*/ | |||||
hiddenLabel?: boolean, | |||||
} | } | ||||
export const MenuSelect = React.forwardRef<MenuSelectDerivedElement, MenuSelectProps>(({ | |||||
children, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
return ( | |||||
<div> | |||||
<select | |||||
{...etcProps} | |||||
ref={forwardedRef} | |||||
/> | |||||
</div> | |||||
) | |||||
}); | |||||
/** | |||||
* Component for inputting textual values. | |||||
* | |||||
* This component supports multiline input and adjusts its layout accordingly. | |||||
*/ | |||||
export const MenuSelect = React.forwardRef<MenuSelectDerivedElement, MenuSelectProps>( | |||||
( | |||||
{ | |||||
label, | |||||
hint, | |||||
indicator, | |||||
size = 'medium' as const, | |||||
border = false, | |||||
block = false, | |||||
variant = 'default' as const, | |||||
hiddenLabel = false, | |||||
className, | |||||
...etcProps | |||||
}: MenuSelectProps, | |||||
ref, | |||||
) => { | |||||
const labelId = React.useId(); | |||||
return ( | |||||
<div | |||||
className={clsx( | |||||
'relative rounded ring-secondary/50', | |||||
'focus-within:ring-4', | |||||
{ | |||||
'block': block, | |||||
'inline-block align-middle': !block, | |||||
}, | |||||
className, | |||||
)} | |||||
> | |||||
<select | |||||
{...etcProps} | |||||
ref={ref} | |||||
aria-labelledby={labelId} | |||||
data-testid="input" | |||||
size={2} | |||||
style={{ | |||||
height: 0, | |||||
}} | |||||
className={clsx( | |||||
'bg-negative rounded-inherit w-full peer block', | |||||
'focus:outline-0', | |||||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||||
{ | |||||
'resize': !block, | |||||
'resize-y': block, | |||||
}, | |||||
{ | |||||
'text-xxs': size === 'small', | |||||
'text-xs': size === 'medium', | |||||
}, | |||||
{ | |||||
'pl-4': variant === 'default', | |||||
'pl-1.5': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pt-4': variant === 'alternate' && size === 'small', | |||||
'pt-5': variant === 'alternate' && size === 'medium', | |||||
'pt-8': variant === 'alternate' && size === 'large', | |||||
}, | |||||
{ | |||||
'py-2.5': variant === 'default' && size === 'small', | |||||
'py-3': variant === 'default' && size === 'medium', | |||||
'py-5': variant === 'default' && size === 'large', | |||||
}, | |||||
{ | |||||
'pr-4': variant === 'default' && !indicator, | |||||
'pr-1.5': variant === 'alternate' && !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
{ | |||||
'min-h-10': size === 'small', | |||||
'min-h-12': size === 'medium', | |||||
'min-h-16': size === 'large', | |||||
}, | |||||
)} | |||||
/> | |||||
{ | |||||
label && ( | |||||
<label | |||||
data-testid="label" | |||||
id={labelId} | |||||
className={clsx( | |||||
'absolute z-[1] w-full top-0.5 left-0 pointer-events-none pl-1 text-xxs font-bold peer-disabled:opacity-50 peer-focus:text-secondary text-primary leading-none bg-negative', | |||||
{ | |||||
'sr-only': hiddenLabel, | |||||
}, | |||||
{ | |||||
'pr-1': !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
<span className="block w-full whitespace-nowrap h-[1.1em] overflow-hidden text-ellipsis"> | |||||
{label} | |||||
</span> | |||||
</label> | |||||
) | |||||
} | |||||
{hint && ( | |||||
<div | |||||
data-testid="hint" | |||||
className={clsx( | |||||
'absolute left-0 px-1 pointer-events-none text-xxs peer-disabled:opacity-50 leading-none w-full bg-negative', | |||||
{ | |||||
'bottom-0 pl-4 pb-1': variant === 'default', | |||||
'top-0.5': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pt-2': variant === 'alternate' && size === 'small', | |||||
'pt-3': variant === 'alternate' && size !== 'small', | |||||
}, | |||||
{ | |||||
'pr-4': !indicator && variant === 'default', | |||||
'pr-1': !indicator && variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
<div | |||||
className="opacity-50 whitespace-nowrap w-full h-[1.1em] overflow-hidden text-ellipsis" | |||||
> | |||||
{hint} | |||||
</div> | |||||
</div> | |||||
)} | |||||
{indicator && ( | |||||
<div | |||||
className={clsx( | |||||
'text-center flex items-center justify-center peer-disabled:opacity-50 aspect-square absolute bottom-0 right-0 pointer-events-none', | |||||
{ | |||||
'w-10': size === 'small', | |||||
'w-12': size === 'medium', | |||||
'w-16': size === 'large', | |||||
}, | |||||
)} | |||||
> | |||||
{indicator} | |||||
</div> | |||||
)} | |||||
{ | |||||
border && ( | |||||
<span | |||||
data-testid="border" | |||||
className="absolute z-[1] peer-disabled:opacity-50 inset-0 rounded-inherit border-2 border-primary pointer-events-none peer-focus:border-secondary" | |||||
/> | |||||
) | |||||
} | |||||
</div> | |||||
); | |||||
} | |||||
); | |||||
MenuSelect.displayName = 'MenuSelect'; | MenuSelect.displayName = 'MenuSelect'; |
@@ -1,3 +1,145 @@ | |||||
export const RadioButton = () => ( | |||||
<input type="radio" /> | |||||
); | |||||
import * as React from 'react'; | |||||
import clsx from 'clsx'; | |||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||||
import styles from './style.module.css'; | |||||
export type RadioButtonDerivedElement = HTMLInputElement; | |||||
export interface RadioButtonProps extends Omit<React.InputHTMLAttributes<RadioButtonDerivedElement>, 'type' | 'size'> { | |||||
block?: boolean; | |||||
compact?: boolean; | |||||
size?: ButtonBase.ButtonSize; | |||||
subtext?: React.ReactNode; | |||||
badge?: React.ReactNode; | |||||
variant: ButtonBase.ButtonVariant; | |||||
} | |||||
export const RadioButton = React.forwardRef<RadioButtonDerivedElement, RadioButtonProps>(({ | |||||
children, | |||||
block = false, | |||||
compact = false, | |||||
size = 'medium', | |||||
id: idProp, | |||||
className, | |||||
subtext, | |||||
badge, | |||||
variant, | |||||
style, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const defaultId = React.useId(); | |||||
const id = idProp ?? defaultId; | |||||
return ( | |||||
<> | |||||
<input | |||||
{...etcProps} | |||||
ref={forwardedRef} | |||||
type="radio" | |||||
id={id} | |||||
className={clsx( | |||||
'sr-only peer', | |||||
styles['radio-button'], | |||||
)} | |||||
/> | |||||
<label | |||||
style={style} | |||||
htmlFor={id} | |||||
className={clsx( | |||||
'items-center justify-start rounded overflow-hidden ring-secondary/50 gap-4 leading-none select-none cursor-pointer', | |||||
'peer-focus:outline-0 peer-focus:ring-4 peer-focus:ring-secondary/50', | |||||
'active:ring-tertiary/50 active:ring-4', | |||||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:ring-0', | |||||
'text-primary peer-disabled:text-primary peer-focus:text-secondary peer-checked:text-tertiary active:text-tertiary', | |||||
{ | |||||
'flex w-full': block, | |||||
'inline-flex max-w-full align-middle': !block, | |||||
}, | |||||
{ | |||||
'pl-2 pr-2': compact, | |||||
'pl-4 pr-4': !compact, | |||||
}, | |||||
{ | |||||
'border-2 border-primary peer-disabled:border-primary peer-focus:border-secondary peer-checked:border-tertiary active:border-tertiary' : variant !== 'bare', | |||||
'bg-primary text-negative peer-disabled:bg-primary peer-focus:bg-secondary peer-checked:bg-tertiary active:bg-tertiary': variant === 'filled', | |||||
}, | |||||
{ | |||||
'h-10': size === 'small', | |||||
'h-12': size === 'medium', | |||||
'h-16': size === 'large', | |||||
}, | |||||
className, | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'w-6 h-6 block rounded-full border-2 p-0.5 box-border', | |||||
{ | |||||
'-mr-2': compact, | |||||
'border-current': variant !== 'filled', | |||||
'border-negative': variant === 'filled', | |||||
} | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'w-full h-full rounded-full bg-current', | |||||
{ | |||||
'text-current': variant !== 'filled', | |||||
'text-negative': variant === 'filled', | |||||
}, | |||||
)} | |||||
/> | |||||
</span> | |||||
<span | |||||
className={clsx( | |||||
'contents', | |||||
{ | |||||
'text-current': variant !== 'filled', | |||||
'text-negative': variant === 'filled', | |||||
}, | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'flex-auto min-w-0', | |||||
)} | |||||
> | |||||
<span | |||||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
{subtext && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | |||||
</span> | |||||
{badge && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
data-testid="badge" | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
)} | |||||
</span> | |||||
</label> | |||||
</> | |||||
) | |||||
}); | |||||
RadioButton.displayName = 'RadioButton'; |
@@ -0,0 +1,7 @@ | |||||
.radio-button + label > :first-child > :first-child { | |||||
display: none; | |||||
} | |||||
.radio-button:checked + label > :first-child > :first-child { | |||||
display: block; | |||||
} |
@@ -1,3 +1,88 @@ | |||||
export const RadioTickBox = () => ( | |||||
<input type="radio" /> | |||||
); | |||||
import * as React from 'react'; | |||||
import clsx from 'clsx'; | |||||
import styles from './style.module.css'; | |||||
export type RadioTickBoxDerivedElement = HTMLInputElement; | |||||
export interface RadioTickBoxProps extends Omit<React.InputHTMLAttributes<RadioTickBoxDerivedElement>, 'type' | 'size'> { | |||||
block?: boolean; | |||||
subtext?: React.ReactNode; | |||||
badge?: React.ReactNode; | |||||
} | |||||
export const RadioTickBox = React.forwardRef<RadioTickBoxDerivedElement, RadioTickBoxProps>(({ | |||||
children, | |||||
block = false, | |||||
id: idProp, | |||||
className, | |||||
subtext, | |||||
badge, | |||||
style, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const defaultId = React.useId(); | |||||
const id = idProp ?? defaultId; | |||||
return ( | |||||
<div | |||||
className={clsx( | |||||
'flex gap-x-4 flex-wrap', | |||||
className, | |||||
)} | |||||
style={style} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={forwardedRef} | |||||
type="radio" | |||||
id={id} | |||||
className={clsx( | |||||
'sr-only peer/radio', | |||||
styles['radio-tick-box'], | |||||
)} | |||||
/> | |||||
<label | |||||
htmlFor={id} | |||||
className="peer/children order-2 cursor-pointer peer-disabled/radio:cursor-not-allowed" | |||||
> | |||||
<span | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
</label> | |||||
<label | |||||
htmlFor={id} | |||||
className={clsx( | |||||
'order-1 block rounded-full ring-secondary/50 overflow-hidden gap-4 leading-none select-none cursor-pointer', | |||||
'peer-focus/radio:outline-0 peer-focus/radio:ring-4 peer-focus/radio:ring-secondary/50', | |||||
'active:ring-tertiary/50 active:ring-4', | |||||
'peer-active/children:ring-tertiary/50 peer-active/children:ring-4 peer-active/children:text-tertiary', | |||||
'peer-disabled/radio:opacity-50 peer-disabled/radio:cursor-not-allowed peer-disabled/radio:ring-0', | |||||
'text-primary peer-disabled/radio:text-primary peer-focus/radio:text-secondary peer-checked/radio:text-tertiary active:text-tertiary', | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'w-6 h-6 block rounded-full border-2 p-0.5 box-border border-current', | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'w-full h-full rounded-full bg-current text-current', | |||||
)} | |||||
/> | |||||
</span> | |||||
</label> | |||||
{subtext && ( | |||||
<div | |||||
className="block w-full font-semi-expanded text-xs pl-10 order-3" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</div> | |||||
)} | |||||
</div> | |||||
) | |||||
}); | |||||
RadioTickBox.displayName = 'RadioTickBox'; |
@@ -0,0 +1,7 @@ | |||||
.radio-tick-box + label + label > :first-child > :first-child { | |||||
display: none; | |||||
} | |||||
.radio-tick-box:checked + label + label > :first-child > :first-child { | |||||
display: block; | |||||
} |
@@ -1,6 +1,7 @@ | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | import * as TextControlBase from '@tesseract-design/web-base-textcontrol'; | ||||
import clsx from 'clsx'; | import clsx from 'clsx'; | ||||
import {useEnhanced} from '@/packages/react-utils'; | |||||
type TagInputDerivedElement = HTMLInputElement; | type TagInputDerivedElement = HTMLInputElement; | ||||
@@ -41,6 +42,7 @@ export interface TagInputProps extends Omit<React.HTMLProps<TagInputDerivedEleme | |||||
* Is the label hidden? | * Is the label hidden? | ||||
*/ | */ | ||||
hiddenLabel?: boolean, | hiddenLabel?: boolean, | ||||
enhanced?: boolean, | |||||
} | } | ||||
/** | /** | ||||
@@ -61,11 +63,38 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( | |||||
variant = 'default' as const, | variant = 'default' as const, | ||||
hiddenLabel = false, | hiddenLabel = false, | ||||
className, | className, | ||||
enhanced: enhancedProp = false, | |||||
...etcProps | ...etcProps | ||||
}: TagInputProps, | }: TagInputProps, | ||||
ref, | |||||
forwardedRef, | |||||
) => { | ) => { | ||||
const {enhanced} = useEnhanced({ enhanced: enhancedProp }); | |||||
const defaultRef = React.useRef<TagInputDerivedElement>(null); | |||||
const ref = forwardedRef ?? defaultRef; | |||||
const labelId = React.useId(); | const labelId = React.useId(); | ||||
const dummyInputRef = React.useRef<HTMLInputElement>(null); | |||||
const handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => { | |||||
if (!(typeof ref === 'object' && ref)) { | |||||
return; | |||||
} | |||||
const { current: input } = ref; | |||||
if (!input) { | |||||
return; | |||||
} | |||||
input.focus(); | |||||
}; | |||||
const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => { | |||||
if (!(typeof dummyInputRef === 'object' && dummyInputRef)) { | |||||
return; | |||||
} | |||||
const { current: input } = dummyInputRef; | |||||
if (!input) { | |||||
return; | |||||
} | |||||
input.value = e.currentTarget.value; | |||||
}; | |||||
return ( | return ( | ||||
<div | <div | ||||
@@ -86,36 +115,49 @@ export const TagInput = React.forwardRef<TagInputDerivedElement, TagInputProps>( | |||||
type={type} | type={type} | ||||
data-testid="input" | data-testid="input" | ||||
className={clsx( | className={clsx( | ||||
'bg-negative rounded-inherit w-full peer block', | |||||
'focus:outline-0', | |||||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||||
{ | |||||
'text-xxs': size === 'small', | |||||
'text-xs': size === 'medium', | |||||
}, | |||||
{ | |||||
'pl-4': variant === 'default', | |||||
'pl-1.5': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pt-4': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pr-4': variant === 'default' && !indicator, | |||||
'pr-1.5': variant === 'alternate' && !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
{ | |||||
'h-10': size === 'small', | |||||
'h-12': size === 'medium', | |||||
'h-16': size === 'large', | |||||
}, | |||||
'peer', | |||||
enhanced && 'sr-only', | |||||
)} | )} | ||||
onChange={handleChange} | |||||
/> | /> | ||||
{enhanced && ( | |||||
<input | |||||
ref={dummyInputRef} | |||||
readOnly | |||||
tabIndex={-1} | |||||
onFocus={handleFocus} | |||||
className={clsx( | |||||
'bg-negative rounded-inherit w-full block', | |||||
'focus:outline-0', | |||||
'disabled:opacity-50 disabled:cursor-not-allowed', | |||||
{ | |||||
'text-xxs': size === 'small', | |||||
'text-xs': size === 'medium', | |||||
}, | |||||
{ | |||||
'pl-4': variant === 'default', | |||||
'pl-1.5': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pt-4': variant === 'alternate', | |||||
}, | |||||
{ | |||||
'pr-4': variant === 'default' && !indicator, | |||||
'pr-1.5': variant === 'alternate' && !indicator, | |||||
}, | |||||
{ | |||||
'pr-10': indicator && size === 'small', | |||||
'pr-12': indicator && size === 'medium', | |||||
'pr-16': indicator && size === 'large', | |||||
}, | |||||
{ | |||||
'h-10': size === 'small', | |||||
'h-12': size === 'medium', | |||||
'h-16': size === 'large', | |||||
}, | |||||
)} | |||||
/> | |||||
)} | |||||
{ | { | ||||
label && ( | label && ( | ||||
<label | <label | ||||
@@ -1,3 +1,180 @@ | |||||
export const ToggleButton = () => ( | |||||
<input type="checkbox" /> | |||||
); | |||||
import * as React from 'react'; | |||||
import clsx from 'clsx'; | |||||
import * as ButtonBase from '@tesseract-design/web-base-button'; | |||||
import styles from './style.module.css'; | |||||
export type ToggleButtonDerivedElement = HTMLInputElement; | |||||
export interface ToggleButtonProps extends Omit<React.InputHTMLAttributes<ToggleButtonDerivedElement>, 'type' | 'size'> { | |||||
block?: boolean; | |||||
compact?: boolean; | |||||
size?: ButtonBase.ButtonSize; | |||||
subtext?: React.ReactNode; | |||||
badge?: React.ReactNode; | |||||
variant: ButtonBase.ButtonVariant; | |||||
indeterminate?: boolean; | |||||
} | |||||
export const ToggleButton = React.forwardRef<ToggleButtonDerivedElement, ToggleButtonProps>(({ | |||||
children, | |||||
block = false, | |||||
compact = false, | |||||
size = 'medium', | |||||
id: idProp, | |||||
className, | |||||
subtext, | |||||
badge, | |||||
variant, | |||||
indeterminate = false, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const defaultRef = React.useRef<ToggleButtonDerivedElement>(null); | |||||
const ref = forwardedRef ?? defaultRef; | |||||
const defaultId = React.useId(); | |||||
const id = idProp ?? defaultId; | |||||
React.useEffect(() => { | |||||
if (!(typeof ref === 'object' && ref)) { | |||||
return; | |||||
} | |||||
const { current: element } = ref; | |||||
if (!element) { | |||||
return; | |||||
} | |||||
element.indeterminate = indeterminate; | |||||
}, [indeterminate, ref]); | |||||
return ( | |||||
<> | |||||
<input | |||||
{...etcProps} | |||||
ref={ref} | |||||
type="checkbox" | |||||
id={id} | |||||
className={clsx( | |||||
'sr-only peer', | |||||
styles['toggle-button'], | |||||
)} | |||||
/> | |||||
<label | |||||
htmlFor={id} | |||||
className={clsx( | |||||
'items-center justify-start rounded overflow-hidden ring-secondary/50 gap-4 leading-none select-none cursor-pointer', | |||||
'peer-focus:outline-0 peer-focus:ring-4 peer-focus:ring-secondary/50', | |||||
'active:ring-tertiary/50 active:ring-4', | |||||
'peer-disabled:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:ring-0', | |||||
'text-primary peer-disabled:text-primary peer-focus:text-secondary peer-checked:text-tertiary active:text-tertiary', | |||||
{ | |||||
'flex w-full': block, | |||||
'inline-flex max-w-full align-middle': !block, | |||||
}, | |||||
{ | |||||
'pl-2 pr-2': compact, | |||||
'pl-4 pr-4': !compact, | |||||
}, | |||||
{ | |||||
'border-2 border-primary peer-disabled:border-primary peer-focus:border-secondary peer-checked:border-tertiary active:border-tertiary' : variant !== 'bare', | |||||
'bg-primary text-negative peer-disabled:bg-primary peer-focus:bg-secondary peer-checked:bg-tertiary active:bg-tertiary': variant === 'filled', | |||||
}, | |||||
{ | |||||
'h-10': size === 'small', | |||||
'h-12': size === 'medium', | |||||
'h-16': size === 'large', | |||||
}, | |||||
className, | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'w-6 h-6 block rounded border-2 p-0.5 box-border', | |||||
{ | |||||
'border-current': variant !== 'filled', | |||||
'border-negative': variant === 'filled', | |||||
'-mr-2': compact, | |||||
} | |||||
)} | |||||
> | |||||
<svg | |||||
className={clsx( | |||||
'w-full h-full fill-none stroke-3 linejoin-round linecap-round', | |||||
{ | |||||
'stroke-negative': variant === 'filled', | |||||
'stroke-current': variant !== 'filled', | |||||
} | |||||
)} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="20 6 9 17 4 12" | |||||
/> | |||||
</svg> | |||||
<svg | |||||
className={clsx( | |||||
'w-full h-full fill-none stroke-3 linejoin-round linecap-round', | |||||
{ | |||||
'stroke-negative': variant === 'filled', | |||||
'stroke-current': variant !== 'filled', | |||||
} | |||||
)} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="20 12 4 12" | |||||
/> | |||||
</svg> | |||||
</span> | |||||
<span | |||||
className={clsx( | |||||
'contents', | |||||
{ | |||||
'text-current': variant !== 'filled', | |||||
'text-negative': variant === 'filled', | |||||
}, | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'flex-auto min-w-0', | |||||
)} | |||||
> | |||||
<span | |||||
className="block uppercase font-bold h-[1.1em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded" | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
{subtext && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
className="block h-[1.3em] w-full whitespace-nowrap overflow-hidden text-ellipsis font-semi-expanded font-bold text-xs" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</span> | |||||
</> | |||||
)} | |||||
</span> | |||||
{badge && ( | |||||
<> | |||||
<span className="sr-only"> | |||||
{' - '} | |||||
</span> | |||||
<span | |||||
data-testid="badge" | |||||
> | |||||
{badge} | |||||
</span> | |||||
</> | |||||
)} | |||||
</span> | |||||
</label> | |||||
</> | |||||
) | |||||
}); | |||||
ToggleButton.displayName = 'ToggleButton'; |
@@ -0,0 +1,15 @@ | |||||
.toggle-button + label > :first-child > :first-child { | |||||
display: none; | |||||
} | |||||
.toggle-button:checked + label > :first-child > :first-child { | |||||
display: block; | |||||
} | |||||
.toggle-button + label > :first-child > :first-child + * { | |||||
display: none; | |||||
} | |||||
.toggle-button:indeterminate + label > :first-child > :first-child + * { | |||||
display: block; | |||||
} |
@@ -1,3 +1,121 @@ | |||||
export const ToggleSwitch = () => ( | |||||
<input type="checkbox" /> | |||||
); | |||||
import * as React from 'react'; | |||||
import clsx from 'clsx'; | |||||
import styles from './style.module.css'; | |||||
import {ToggleButtonDerivedElement} from '@/categories/option/react'; | |||||
export type ToggleSwitchDerivedElement = HTMLInputElement; | |||||
export interface ToggleSwitchProps extends Omit<React.InputHTMLAttributes<ToggleSwitchDerivedElement>, 'type' | 'size'> { | |||||
block?: boolean; | |||||
subtext?: React.ReactNode; | |||||
badge?: React.ReactNode; | |||||
indeterminate?: boolean; | |||||
checkedLabel?: React.ReactNode; | |||||
uncheckedLabel?: React.ReactNode; | |||||
} | |||||
export const ToggleSwitch = React.forwardRef<ToggleSwitchDerivedElement, ToggleSwitchProps>(({ | |||||
uncheckedLabel, | |||||
checkedLabel, | |||||
block = false, | |||||
id: idProp, | |||||
className, | |||||
subtext, | |||||
badge, | |||||
style, | |||||
indeterminate = false, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const defaultRef = React.useRef<ToggleButtonDerivedElement>(null); | |||||
const ref = forwardedRef ?? defaultRef; | |||||
const defaultId = React.useId(); | |||||
const id = idProp ?? defaultId; | |||||
React.useEffect(() => { | |||||
if (!(typeof ref === 'object' && ref)) { | |||||
return; | |||||
} | |||||
const { current: element } = ref; | |||||
if (!element) { | |||||
return; | |||||
} | |||||
element.indeterminate = indeterminate; | |||||
}, [indeterminate, ref]); | |||||
return ( | |||||
<div | |||||
className={clsx( | |||||
'flex gap-x-4 flex-wrap', | |||||
className, | |||||
)} | |||||
style={style} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={ref} | |||||
type="checkbox" | |||||
id={id} | |||||
className={clsx( | |||||
'sr-only peer/radio', | |||||
styles['toggle-switch'], | |||||
)} | |||||
/> | |||||
<label | |||||
htmlFor={id} | |||||
className="peer/children order-3 cursor-pointer peer-disabled/radio:cursor-not-allowed" | |||||
> | |||||
<span | |||||
data-testid="children" | |||||
> | |||||
{checkedLabel} | |||||
</span> | |||||
</label> | |||||
<label | |||||
htmlFor={id} | |||||
className="peer/children order-1 cursor-pointer peer-disabled/radio:cursor-not-allowed" | |||||
> | |||||
{uncheckedLabel && ( | |||||
<span | |||||
data-testid="uncheckedLabel" | |||||
> | |||||
{uncheckedLabel} | |||||
</span> | |||||
)} | |||||
</label> | |||||
<label | |||||
htmlFor={id} | |||||
className={clsx( | |||||
'order-2 block rounded-full ring-secondary/50 overflow-hidden gap-4 leading-none select-none cursor-pointer', | |||||
'peer-focus/radio:outline-0 peer-focus/radio:ring-4 peer-focus/radio:ring-secondary/50', | |||||
'active:ring-tertiary/50 active:ring-4', | |||||
'peer-active/children:ring-tertiary/50 peer-active/children:ring-4 peer-active/children:text-tertiary', | |||||
'peer-disabled/radio:opacity-50 peer-disabled/radio:cursor-not-allowed peer-disabled/radio:ring-0', | |||||
'text-primary peer-disabled/radio:text-primary peer-focus/radio:text-secondary peer-checked/radio:text-tertiary active:text-tertiary', | |||||
!uncheckedLabel && '-ml-4', | |||||
)} | |||||
> | |||||
<span> | |||||
<span> | |||||
<span> | |||||
<span /> | |||||
</span> | |||||
</span> | |||||
</span> | |||||
</label> | |||||
{subtext && ( | |||||
<div | |||||
className={clsx( | |||||
'block w-full font-semi-expanded text-xs order-4', | |||||
!uncheckedLabel && 'pl-16', | |||||
uncheckedLabel && 'pt-2', | |||||
)} | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</div> | |||||
)} | |||||
</div> | |||||
) | |||||
}); | |||||
ToggleSwitch.displayName = 'ToggleSwitch'; |
@@ -0,0 +1,118 @@ | |||||
.toggle-switch + label + label + label > :first-child { | |||||
appearance: none; | |||||
cursor: pointer; | |||||
position: relative; | |||||
overflow: hidden; | |||||
height: 1.5em; | |||||
width: 3em; | |||||
display: block; | |||||
box-sizing: border-box; | |||||
border-radius: 9999px; | |||||
color: rgb(var(--color-primary)); | |||||
} | |||||
.toggle-switch:checked + label + label + label > :first-child { | |||||
color: rgb(var(--color-tertiary)); | |||||
} | |||||
.toggle-switch:focus + label + label + label > :first-child { | |||||
color: rgb(var(--color-secondary)); | |||||
} | |||||
.toggle-switch + label:active + label + label > :first-child { | |||||
color: rgb(var(--color-tertiary)); | |||||
} | |||||
.toggle-switch + label + label:active + label > :first-child { | |||||
color: rgb(var(--color-tertiary)); | |||||
} | |||||
.toggle-switch + label + label + label:active > :first-child { | |||||
color: rgb(var(--color-tertiary)); | |||||
} | |||||
.toggle-switch + label + label + label > :first-child > :first-child { | |||||
width: 100%; | |||||
height: 100%; | |||||
background-color: rgb(var(--color-primary) / 50%); | |||||
border-radius: 9999px; | |||||
display: block; | |||||
box-sizing: border-box; | |||||
background-clip: content-box; | |||||
padding: 0.25em; | |||||
appearance: none; | |||||
} | |||||
.toggle-switch:checked + label + label + label > :first-child > :first-child { | |||||
background-color: rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch:focus + label + label + label > :first-child > :first-child { | |||||
background-color: rgb(var(--color-secondary) / 50%); | |||||
} | |||||
.toggle-switch + label:active + label + label > :first-child > :first-child { | |||||
background-color: rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch + label + label:active + label > :first-child > :first-child { | |||||
background-color: rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch + label + label + label:active > :first-child > :first-child { | |||||
background-color: rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch + label + label + label > :first-child > :first-child > :first-child { | |||||
appearance: none; | |||||
border-radius: 9999px; | |||||
display: block; | |||||
width: 100%; | |||||
height: 100%; | |||||
margin: -0.25em; | |||||
box-sizing: border-box; | |||||
background-clip: content-box; | |||||
} | |||||
.toggle-switch + label + label + label > :first-child > :first-child > :first-child > :first-child { | |||||
width: 1.5em; | |||||
height: 1.5em; | |||||
margin: -0.25em 0 0 0; | |||||
display: block; | |||||
border-radius: 9999px; | |||||
background-color: currentColor; | |||||
appearance: none; | |||||
aspect-ratio: 1 / 1; | |||||
z-index: 1; | |||||
position: relative; | |||||
box-shadow: -100000.5em 0 0 100000em rgb(var(--color-primary) / 50%); | |||||
} | |||||
.toggle-switch:checked + label + label + label > :first-child > :first-child > :first-child > :first-child { | |||||
margin-left: calc(100% - 1em); | |||||
} | |||||
.toggle-switch:indeterminate + label + label + label > :first-child > :first-child > :first-child > :first-child { | |||||
margin-left: calc(50% - 0.5em); | |||||
} | |||||
.toggle-switch:checked + label + label + label > :first-child > :first-child > :first-child > :first-child { | |||||
box-shadow: -100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch:focus + label + label + label > :first-child > :first-child > :first-child > :first-child { | |||||
box-shadow: -100000.5em 0 0 100000em rgb(var(--color-secondary) / 50%); | |||||
} | |||||
.toggle-switch + label:active + label + label > :first-child > :first-child > :first-child > :first-child { | |||||
box-shadow: -100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch + label + label:active + label > :first-child > :first-child > :first-child > :first-child { | |||||
box-shadow: -100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
.toggle-switch + label + label + label:active > :first-child > :first-child > :first-child > :first-child { | |||||
box-shadow: -100000.5em 0 0 100000em rgb(var(--color-tertiary) / 50%); | |||||
} | |||||
@@ -1,3 +1,122 @@ | |||||
export const ToggleTickBox = () => ( | |||||
<input type="checkbox" /> | |||||
); | |||||
import * as React from 'react'; | |||||
import clsx from 'clsx'; | |||||
import styles from './style.module.css'; | |||||
import {ToggleButtonDerivedElement} from '@/categories/option/react'; | |||||
export type ToggleTickBoxDerivedElement = HTMLInputElement; | |||||
export interface ToggleTickBoxProps extends Omit<React.InputHTMLAttributes<ToggleTickBoxDerivedElement>, 'type' | 'size'> { | |||||
block?: boolean; | |||||
subtext?: React.ReactNode; | |||||
badge?: React.ReactNode; | |||||
indeterminate?: boolean; | |||||
} | |||||
export const ToggleTickBox = React.forwardRef<ToggleTickBoxDerivedElement, ToggleTickBoxProps>(({ | |||||
children, | |||||
block = false, | |||||
id: idProp, | |||||
className, | |||||
subtext, | |||||
badge, | |||||
style, | |||||
indeterminate = false, | |||||
...etcProps | |||||
}, forwardedRef) => { | |||||
const defaultRef = React.useRef<ToggleButtonDerivedElement>(null); | |||||
const ref = forwardedRef ?? defaultRef; | |||||
const defaultId = React.useId(); | |||||
const id = idProp ?? defaultId; | |||||
React.useEffect(() => { | |||||
if (!(typeof ref === 'object' && ref)) { | |||||
return; | |||||
} | |||||
const { current: element } = ref; | |||||
if (!element) { | |||||
return; | |||||
} | |||||
element.indeterminate = indeterminate; | |||||
}, [indeterminate, ref]); | |||||
return ( | |||||
<div | |||||
className={clsx( | |||||
'flex gap-x-4 flex-wrap', | |||||
className, | |||||
)} | |||||
style={style} | |||||
> | |||||
<input | |||||
{...etcProps} | |||||
ref={ref} | |||||
type="checkbox" | |||||
id={id} | |||||
className={clsx( | |||||
'sr-only peer/radio', | |||||
styles['toggle-tick-box'], | |||||
)} | |||||
/> | |||||
<label | |||||
htmlFor={id} | |||||
className="peer/children order-2 cursor-pointer peer-disabled/radio:cursor-not-allowed" | |||||
> | |||||
<span | |||||
data-testid="children" | |||||
> | |||||
{children} | |||||
</span> | |||||
</label> | |||||
<label | |||||
htmlFor={id} | |||||
className={clsx( | |||||
'order-1 block rounded ring-secondary/50 overflow-hidden gap-4 leading-none select-none cursor-pointer', | |||||
'peer-focus/radio:outline-0 peer-focus/radio:ring-4 peer-focus/radio:ring-secondary/50', | |||||
'active:ring-tertiary/50 active:ring-4', | |||||
'peer-active/children:ring-tertiary/50 peer-active/children:ring-4 peer-active/children:text-tertiary', | |||||
'peer-disabled/radio:opacity-50 peer-disabled/radio:cursor-not-allowed peer-disabled/radio:ring-0', | |||||
'text-primary peer-disabled/radio:text-primary peer-focus/radio:text-secondary peer-checked/radio:text-tertiary active:text-tertiary', | |||||
)} | |||||
> | |||||
<span | |||||
className={clsx( | |||||
'w-6 h-6 block rounded-inherit border-2 p-0.5 box-border border-current', | |||||
)} | |||||
> | |||||
<svg | |||||
className={clsx( | |||||
'w-full h-full fill-none stroke-3 linejoin-round linecap-round stroke-current', | |||||
)} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="20 6 9 17 4 12" | |||||
/> | |||||
</svg> | |||||
<svg | |||||
className={clsx( | |||||
'w-full h-full fill-none stroke-3 linejoin-round linecap-round stroke-current', | |||||
)} | |||||
viewBox="0 0 24 24" | |||||
role="presentation" | |||||
> | |||||
<polyline | |||||
points="20 12 4 12" | |||||
/> | |||||
</svg> | |||||
</span> | |||||
</label> | |||||
{subtext && ( | |||||
<div | |||||
className="block w-full font-semi-expanded text-xs pl-10 order-3" | |||||
data-testid="subtext" | |||||
> | |||||
{subtext} | |||||
</div> | |||||
)} | |||||
</div> | |||||
) | |||||
}); | |||||
ToggleTickBox.displayName = 'ToggleTickBox'; |
@@ -0,0 +1,15 @@ | |||||
.toggle-tick-box + label + label > :first-child > :first-child { | |||||
display: none; | |||||
} | |||||
.toggle-tick-box:checked + label + label > :first-child > :first-child { | |||||
display: block; | |||||
} | |||||
.toggle-tick-box + label + label > :first-child > :first-child + * { | |||||
display: none; | |||||
} | |||||
.toggle-tick-box:indeterminate + label + label > :first-child > :first-child + * { | |||||
display: block; | |||||
} |
@@ -1,6 +1,5 @@ | |||||
import { NextPage } from 'next'; | import { NextPage } from 'next'; | ||||
import * as Option from '@tesseract-design/web-option-react'; | import * as Option from '@tesseract-design/web-option-react'; | ||||
import * as SelectControlBase from '@tesseract-design/web-base-selectcontrol'; | |||||
import { DefaultLayout } from '@/components/DefaultLayout'; | import { DefaultLayout } from '@/components/DefaultLayout'; | ||||
import {ReactNode} from 'react'; | import {ReactNode} from 'react'; | ||||
@@ -625,6 +624,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
variant="bare" | |||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
Button | Button | ||||
@@ -641,7 +641,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
border | |||||
variant="outline" | |||||
block | block | ||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
@@ -650,7 +650,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
border | |||||
variant="filled" | variant="filled" | ||||
block | block | ||||
name="RadioButton" | name="RadioButton" | ||||
@@ -663,6 +662,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
block | block | ||||
disabled | disabled | ||||
name="RadioButton" | name="RadioButton" | ||||
variant="bare" | |||||
> | > | ||||
Button | Button | ||||
</Option.RadioButton> | </Option.RadioButton> | ||||
@@ -679,7 +679,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
border | |||||
variant="outline" | |||||
block | block | ||||
disabled | disabled | ||||
name="RadioButton" | name="RadioButton" | ||||
@@ -689,7 +689,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
border | |||||
variant="filled" | variant="filled" | ||||
block | block | ||||
disabled | disabled | ||||
@@ -708,7 +707,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
border | |||||
variant="outline" | |||||
size="small" | size="small" | ||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
@@ -718,7 +717,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="small" | size="small" | ||||
name="RadioButton" | name="RadioButton" | ||||
@@ -729,7 +727,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
border | |||||
variant="outline" | |||||
size="medium" | size="medium" | ||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
@@ -739,7 +737,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="medium" | size="medium" | ||||
name="RadioButton" | name="RadioButton" | ||||
@@ -750,7 +747,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
border | |||||
variant="outline" | |||||
size="large" | size="large" | ||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
@@ -760,7 +757,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="large" | size="large" | ||||
name="RadioButton" | name="RadioButton" | ||||
@@ -779,7 +775,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="outline" | |||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
Button | Button | ||||
@@ -789,7 +785,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="filled" | variant="filled" | ||||
name="RadioButton" | name="RadioButton" | ||||
> | > | ||||
@@ -800,7 +795,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="outline" | |||||
name="RadioButton" | name="RadioButton" | ||||
subtext={ | subtext={ | ||||
<> | <> | ||||
@@ -815,7 +810,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="filled" | variant="filled" | ||||
name="RadioButton" | name="RadioButton" | ||||
subtext={ | subtext={ | ||||
@@ -831,7 +825,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="outline" | |||||
size="small" | size="small" | ||||
name="RadioButton" | name="RadioButton" | ||||
subtext={ | subtext={ | ||||
@@ -847,7 +841,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.RadioButton | <Option.RadioButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="small" | size="small" | ||||
name="RadioButton" | name="RadioButton" | ||||
@@ -883,203 +876,26 @@ const OptionPage: NextPage<Props> = ({ | |||||
</Option.RadioTickBox> | </Option.RadioTickBox> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<div className="container mx-auto px-4"> | |||||
<h1> | |||||
TagInput | |||||
</h1> | |||||
<div> | |||||
<section> | |||||
<h2> | |||||
Default | |||||
</h2> | |||||
<div> | |||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
size="small" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
size="small" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
size="large" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
size="large" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</section> | |||||
<section> | |||||
<h2>Alternate</h2> | |||||
<div className="grid gap-4 my-4"> | |||||
<div> | <div> | ||||
<div className="grid md:grid-cols-2 gap-4"> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
variant="alternate" | |||||
size="small" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
variant="alternate" | |||||
size="small" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
variant="alternate" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
variant="alternate" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
variant="alternate" | |||||
size="large" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
border | |||||
block | |||||
variant="alternate" | |||||
size="large" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
variant="alternate" | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
<div> | |||||
<Option.TagInput | |||||
enhanced | |||||
variant="alternate" | |||||
border | |||||
label="TagInput" | |||||
hint="Type anything here…" | |||||
indicator="A" | |||||
block | |||||
disabled | |||||
/> | |||||
</div> | |||||
</div> | |||||
<Option.RadioTickBox | |||||
block | |||||
subtext={ | |||||
<> | |||||
<div> | |||||
This is a very long text that may span a couple of lines. | |||||
</div> | |||||
<div> | |||||
The subtext should not be included with the click area of the component. | |||||
</div> | |||||
</> | |||||
} | |||||
name="RadioButton" | |||||
> | |||||
RadioButton | |||||
</Option.RadioTickBox> | |||||
</div> | </div> | ||||
</section> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</section> | </section> | ||||
@@ -1095,6 +911,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div className="grid md:grid-cols-2 gap-4 my-4"> | <div className="grid md:grid-cols-2 gap-4 my-4"> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
variant="bare" | |||||
block | block | ||||
> | > | ||||
Button | Button | ||||
@@ -1110,7 +927,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
border | |||||
variant="outline" | |||||
block | block | ||||
> | > | ||||
Button | Button | ||||
@@ -1118,7 +935,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
border | |||||
variant="filled" | variant="filled" | ||||
block | block | ||||
> | > | ||||
@@ -1127,6 +943,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
variant="bare" | |||||
block | block | ||||
disabled | disabled | ||||
> | > | ||||
@@ -1144,7 +961,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
border | |||||
variant="outline" | |||||
block | block | ||||
disabled | disabled | ||||
> | > | ||||
@@ -1153,7 +970,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
border | |||||
variant="filled" | variant="filled" | ||||
block | block | ||||
disabled | disabled | ||||
@@ -1163,7 +979,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
border | |||||
variant="outline" | |||||
block | block | ||||
indeterminate | indeterminate | ||||
> | > | ||||
@@ -1172,7 +988,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
border | |||||
variant="filled" | variant="filled" | ||||
block | block | ||||
indeterminate | indeterminate | ||||
@@ -1190,7 +1005,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
border | |||||
variant="outline" | |||||
size="small" | size="small" | ||||
> | > | ||||
Button | Button | ||||
@@ -1199,7 +1014,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="small" | size="small" | ||||
> | > | ||||
@@ -1209,7 +1023,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
border | |||||
variant="outline" | |||||
size="medium" | size="medium" | ||||
> | > | ||||
Button | Button | ||||
@@ -1218,7 +1032,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="medium" | size="medium" | ||||
> | > | ||||
@@ -1228,7 +1041,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
border | |||||
variant="outline" | |||||
size="large" | size="large" | ||||
> | > | ||||
Button | Button | ||||
@@ -1237,7 +1050,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<div> | <div> | ||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="large" | size="large" | ||||
> | > | ||||
@@ -1255,7 +1067,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="outline" | |||||
> | > | ||||
Button | Button | ||||
</Option.ToggleButton> | </Option.ToggleButton> | ||||
@@ -1264,7 +1076,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="filled" | variant="filled" | ||||
> | > | ||||
Button | Button | ||||
@@ -1274,7 +1085,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="outline" | |||||
subtext={ | subtext={ | ||||
<> | <> | ||||
Subtext | Subtext | ||||
@@ -1288,7 +1099,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="filled" | variant="filled" | ||||
subtext={ | subtext={ | ||||
<> | <> | ||||
@@ -1303,7 +1113,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="outline" | |||||
size="small" | size="small" | ||||
subtext={ | subtext={ | ||||
<> | <> | ||||
@@ -1318,7 +1128,6 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleButton | <Option.ToggleButton | ||||
block | block | ||||
compact | compact | ||||
border | |||||
variant="filled" | variant="filled" | ||||
size="small" | size="small" | ||||
subtext={ | subtext={ | ||||
@@ -1379,6 +1188,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
<Option.ToggleSwitch | <Option.ToggleSwitch | ||||
block | block | ||||
subtext="Subtext" | subtext="Subtext" | ||||
indeterminate | |||||
checkedLabel="On" | checkedLabel="On" | ||||
uncheckedLabel="Off" | uncheckedLabel="Off" | ||||
/> | /> | ||||
@@ -1441,8 +1251,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1452,8 +1263,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1461,8 +1273,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1471,8 +1284,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1481,8 +1295,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1492,8 +1307,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1502,8 +1318,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1513,8 +1330,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -1531,8 +1349,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1543,8 +1362,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1553,8 +1373,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1564,8 +1385,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1575,8 +1397,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1587,8 +1410,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
label="MultilineTextInput" | label="MultilineTextInput" | ||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1598,8 +1422,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
<div> | <div> | ||||
<Option.ComboBox | <Option.ComboBox | ||||
@@ -1610,8 +1435,9 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
options={options} | |||||
/> | |||||
> | |||||
{options} | |||||
</Option.ComboBox> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -1638,6 +1464,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1648,6 +1475,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1656,6 +1484,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1665,6 +1494,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1674,6 +1504,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1684,6 +1515,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1693,6 +1525,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1703,6 +1536,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -1720,6 +1554,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1731,6 +1566,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1740,6 +1576,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1750,6 +1587,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1760,6 +1598,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
block | block | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1771,6 +1610,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
label="MultilineTextInput" | label="MultilineTextInput" | ||||
hint="Type anything here…" | hint="Type anything here…" | ||||
indicator="A" | indicator="A" | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1781,6 +1621,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
<div> | <div> | ||||
@@ -1792,6 +1633,7 @@ const OptionPage: NextPage<Props> = ({ | |||||
indicator="A" | indicator="A" | ||||
block | block | ||||
disabled | disabled | ||||
enhanced | |||||
/> | /> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
@@ -65,6 +65,9 @@ module.exports = { | |||||
16: '4rem', | 16: '4rem', | ||||
64: '16rem', | 64: '16rem', | ||||
}, | }, | ||||
strokeWidth: { | |||||
3: '3', | |||||
}, | |||||
}, | }, | ||||
}, | }, | ||||
plugins: [], | plugins: [], | ||||
@@ -22,6 +22,7 @@ | |||||
"@tesseract-design/web-base-textcontrol": ["./src/base/textcontrol"], | "@tesseract-design/web-base-textcontrol": ["./src/base/textcontrol"], | ||||
"@tesseract-design/web-action-react": ["./src/categories/action/react"], | "@tesseract-design/web-action-react": ["./src/categories/action/react"], | ||||
"@tesseract-design/web-blob-react": ["./src/categories/blob/react"], | "@tesseract-design/web-blob-react": ["./src/categories/blob/react"], | ||||
"@tesseract-design/web-color-react": ["./src/categories/color/react"], | |||||
"@tesseract-design/web-freeform-react": ["./src/categories/freeform/react"], | "@tesseract-design/web-freeform-react": ["./src/categories/freeform/react"], | ||||
"@tesseract-design/web-information-react": ["./src/categories/information/react"], | "@tesseract-design/web-information-react": ["./src/categories/information/react"], | ||||
"@tesseract-design/web-option-react": ["./src/categories/option/react"], | "@tesseract-design/web-option-react": ["./src/categories/option/react"], | ||||