UI: init InputError component

This commit is contained in:
Johannes Kirschbauer
2024-12-20 18:10:56 +01:00
parent ff2a08370a
commit dacfd69855

View File

@@ -1,7 +1,7 @@
import cx from "classnames"; import cx from "classnames";
import { createEffect, JSX, splitProps } from "solid-js"; import { JSX, Ref, Show, splitProps } from "solid-js";
import Icon, { IconVariant } from "../icon"; import Icon, { IconVariant } from "../icon";
import { Typography } from "../Typography"; import { Typography, TypographyProps } from "../Typography";
type Variants = "outlined" | "ghost"; type Variants = "outlined" | "ghost";
interface InputBaseProps { interface InputBaseProps {
@@ -17,6 +17,9 @@ interface InputBaseProps {
readonly?: boolean; readonly?: boolean;
error?: boolean; error?: boolean;
icon?: IconVariant; icon?: IconVariant;
/** Overrides the input element */
inputElem?: JSX.Element;
divRef?: Ref<HTMLDivElement>;
} }
const variantBorder: Record<Variants, string> = { const variantBorder: Record<Variants, string> = {
@@ -27,11 +30,7 @@ const variantBorder: Record<Variants, string> = {
const fgStateClasses = cx("aria-disabled:fg-def-4 aria-readonly:fg-def-3"); const fgStateClasses = cx("aria-disabled:fg-def-4 aria-readonly:fg-def-3");
export const InputBase = (props: InputBaseProps) => { export const InputBase = (props: InputBaseProps) => {
const [, inputProps] = splitProps(props, ["class"]); const [internal, inputProps] = splitProps(props, ["class", "divRef"]);
createEffect(() => {
console.log("InputBase", props.value, props.variant);
});
return ( return (
<div <div
class={cx( class={cx(
@@ -56,7 +55,7 @@ export const InputBase = (props: InputBaseProps) => {
// Cursor // Cursor
"aria-readonly:cursor-no-drop", "aria-readonly:cursor-no-drop",
props.class, props.class
)} )}
classList={{ classList={{
[cx("!border !border-semantic-1 !outline-semantic-1")]: !!props.error, [cx("!border !border-semantic-1 !outline-semantic-1")]: !!props.error,
@@ -66,6 +65,7 @@ export const InputBase = (props: InputBaseProps) => {
aria-readonly={props.readonly} aria-readonly={props.readonly}
tabIndex={0} tabIndex={0}
role="textbox" role="textbox"
ref={internal.divRef}
> >
{props.icon && ( {props.icon && (
<i <i
@@ -77,32 +77,39 @@ export const InputBase = (props: InputBaseProps) => {
<Icon icon={props.icon} font-size="inherit" color="inherit" /> <Icon icon={props.icon} font-size="inherit" color="inherit" />
</i> </i>
)} )}
<input <Show when={!props.inputElem} fallback={props.inputElem}>
tabIndex={-1} <input
class="w-full bg-transparent outline-none aria-readonly:cursor-no-drop" tabIndex={-1}
value={props.value} class="w-full bg-transparent outline-none"
type={props.type ? props.type : "text"} value={props.value}
readOnly={props.readonly} type={props.type ? props.type : "text"}
placeholder={`${props.placeholder || ""}`} readOnly={props.readonly}
required={props.required} placeholder={`${props.placeholder || ""}`}
disabled={props.disabled} required={props.required}
aria-invalid={props.error} disabled={props.disabled}
aria-disabled={props.disabled} aria-invalid={props.error}
aria-readonly={props.readonly} aria-disabled={props.disabled}
{...inputProps} aria-readonly={props.readonly}
/> {...inputProps}
/>
</Show>
</div> </div>
); );
}; };
interface InputLabelProps extends JSX.LabelHTMLAttributes<HTMLLabelElement> { export interface InputLabelProps
extends JSX.LabelHTMLAttributes<HTMLLabelElement> {
description?: string; description?: string;
required?: boolean; required?: boolean;
error?: boolean; error?: boolean;
help?: string; help?: string;
labelAction?: JSX.Element;
} }
export const InputLabel = (props: InputLabelProps) => { export const InputLabel = (props: InputLabelProps) => {
const [labelProps, forwardProps] = splitProps(props, ["class"]); const [labelProps, forwardProps] = splitProps(props, [
"class",
"labelAction",
]);
return ( return (
<label <label
class={cx("flex items-center gap-1", labelProps.class)} class={cx("flex items-center gap-1", labelProps.class)}
@@ -112,7 +119,7 @@ export const InputLabel = (props: InputLabelProps) => {
hierarchy="label" hierarchy="label"
size="default" size="default"
weight="bold" weight="bold"
class="!fg-def-1" class="inline-flex gap-1 align-middle !fg-def-1"
classList={{ classList={{
[cx("!fg-semantic-1")]: !!props.error, [cx("!fg-semantic-1")]: !!props.error,
}} }}
@@ -138,9 +145,33 @@ export const InputLabel = (props: InputLabelProps) => {
</span> </span>
)} )}
</Typography> </Typography>
{props.labelAction}
<Typography hierarchy="body" size="xs" weight="normal" color="secondary"> <Typography hierarchy="body" size="xs" weight="normal" color="secondary">
{props.description} {props.description}
</Typography> </Typography>
</label> </label>
); );
}; };
interface InputErrorProps {
error: string;
typographyProps?: TypographyProps;
}
export const InputError = (props: InputErrorProps) => {
const [typoClasses, rest] = splitProps(
props.typographyProps || { class: "" },
["class"]
);
return (
<Typography
hierarchy="body"
// @ts-expect-error: Dependent type is to complex to check how it is coupled to the override for now
size="xxs"
weight="medium"
class={cx("col-span-full px-1 !fg-semantic-4", typoClasses)}
{...rest}
>
{props.error}
</Typography>
);
};