Merge pull request 'ui: use css modules for TextArea and TextInput' (#5235) from hgl-ui-textfield into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5235
This commit is contained in:
@@ -21,10 +21,17 @@ export type CheckboxProps = FieldProps &
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Checkbox = (props: CheckboxProps) => {
|
export const Checkbox = (props: CheckboxProps) => {
|
||||||
const [local, other] = splitProps(
|
const withDefaults = mergeProps(
|
||||||
mergeProps({ size: "default", orientation: "vertical" } as const, props),
|
{ size: "default", orientation: "vertical" } as const,
|
||||||
["size", "orientation", "inverted", "ghost", "input"],
|
props,
|
||||||
);
|
);
|
||||||
|
const [local, other] = splitProps(withDefaults, [
|
||||||
|
"size",
|
||||||
|
"orientation",
|
||||||
|
"inverted",
|
||||||
|
"ghost",
|
||||||
|
"input",
|
||||||
|
]);
|
||||||
|
|
||||||
const iconChecked = (
|
const iconChecked = (
|
||||||
<Icon
|
<Icon
|
||||||
@@ -66,7 +73,7 @@ export const Checkbox = (props: CheckboxProps) => {
|
|||||||
in={keepTruthy(
|
in={keepTruthy(
|
||||||
local.orientation == "horizontal" && "Orienter-horizontal",
|
local.orientation == "horizontal" && "Orienter-horizontal",
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...withDefaults}
|
||||||
/>
|
/>
|
||||||
<KCheckbox.Input {...local.input} />
|
<KCheckbox.Input {...local.input} />
|
||||||
<KCheckbox.Control class={styles.checkboxControl}>
|
<KCheckbox.Control class={styles.checkboxControl}>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
export interface FieldProps {
|
export interface FieldProps {
|
||||||
class?: string;
|
|
||||||
label?: string;
|
label?: string;
|
||||||
labelWeight?: "bold" | "normal";
|
labelWeight?: "bold" | "normal";
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import styles from "./HostFileInput.module.css";
|
|||||||
import { PolymorphicProps } from "@kobalte/core/polymorphic";
|
import { PolymorphicProps } from "@kobalte/core/polymorphic";
|
||||||
import { FieldProps } from "./Field";
|
import { FieldProps } from "./Field";
|
||||||
import { Orienter } from "./Orienter";
|
import { Orienter } from "./Orienter";
|
||||||
import { createSignal, splitProps } from "solid-js";
|
import { createSignal, mergeProps, splitProps } from "solid-js";
|
||||||
import { Tooltip } from "@kobalte/core/tooltip";
|
import { Tooltip } from "@kobalte/core/tooltip";
|
||||||
import { Typography } from "@/src/components/Typography/Typography";
|
import { Typography } from "@/src/components/Typography/Typography";
|
||||||
import { keepTruthy } from "@/src/util";
|
import { keepTruthy } from "@/src/util";
|
||||||
@@ -24,7 +24,14 @@ export type HostFileInputProps = FieldProps &
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const HostFileInput = (props: HostFileInputProps) => {
|
export const HostFileInput = (props: HostFileInputProps) => {
|
||||||
const [value, setValue] = createSignal<string>(props.value || "");
|
const withDefaults = mergeProps({ value: "" } as const, props);
|
||||||
|
const [local, other] = splitProps(withDefaults, [
|
||||||
|
"size",
|
||||||
|
"orientation",
|
||||||
|
"inverted",
|
||||||
|
"ghost",
|
||||||
|
]);
|
||||||
|
const [value, setValue] = createSignal<string>(other.value);
|
||||||
|
|
||||||
let actualInputElement: HTMLInputElement | undefined;
|
let actualInputElement: HTMLInputElement | undefined;
|
||||||
|
|
||||||
@@ -41,13 +48,6 @@ export const HostFileInput = (props: HostFileInputProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const [local, other] = splitProps(props, [
|
|
||||||
"size",
|
|
||||||
"orientation",
|
|
||||||
"inverted",
|
|
||||||
"ghost",
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField {...other}>
|
<TextField {...other}>
|
||||||
<Orienter
|
<Orienter
|
||||||
@@ -60,7 +60,7 @@ export const HostFileInput = (props: HostFileInputProps) => {
|
|||||||
in={keepTruthy(
|
in={keepTruthy(
|
||||||
local.orientation == "horizontal" && "Orienter-horizontal",
|
local.orientation == "horizontal" && "Orienter-horizontal",
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...withDefaults}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<TextField.Input
|
<TextField.Input
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ const sortedAndUniqueOptions = (options: MachineTag[]) =>
|
|||||||
sortedOptions(uniqueOptions(options));
|
sortedOptions(uniqueOptions(options));
|
||||||
|
|
||||||
export const MachineTags = (props: MachineTagsProps) => {
|
export const MachineTags = (props: MachineTagsProps) => {
|
||||||
const [local, rest] = splitProps(props, ["defaultValue"]);
|
const withDefaults = props;
|
||||||
|
const [local, rest] = splitProps(withDefaults, ["defaultValue"]);
|
||||||
|
|
||||||
// // convert default value string[] into MachineTag[]
|
// // convert default value string[] into MachineTag[]
|
||||||
const defaultValue = sortedAndUniqueOptions(
|
const defaultValue = sortedAndUniqueOptions(
|
||||||
@@ -199,7 +200,7 @@ export const MachineTags = (props: MachineTagsProps) => {
|
|||||||
in={keepTruthy(
|
in={keepTruthy(
|
||||||
props.orientation == "horizontal" && "Orienter-horizontal",
|
props.orientation == "horizontal" && "Orienter-horizontal",
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...withDefaults}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Combobox.HiddenSelect
|
<Combobox.HiddenSelect
|
||||||
|
|||||||
@@ -121,11 +121,11 @@ export const AutoResize: Story = {
|
|||||||
description:
|
description:
|
||||||
"This textarea automatically adjusts its height based on content",
|
"This textarea automatically adjusts its height based on content",
|
||||||
tooltip: "Try typing multiple lines to see it grow",
|
tooltip: "Try typing multiple lines to see it grow",
|
||||||
input: {
|
|
||||||
placeholder: "Start typing to see the textarea grow...",
|
|
||||||
autoResize: true,
|
autoResize: true,
|
||||||
minRows: 2,
|
minRows: 2,
|
||||||
maxRows: 10,
|
maxRows: 10,
|
||||||
|
input: {
|
||||||
|
placeholder: "Start typing to see the textarea grow...",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -134,10 +134,10 @@ export const AutoResizeNoMax: Story = {
|
|||||||
args: {
|
args: {
|
||||||
label: "Auto-resize without max height",
|
label: "Auto-resize without max height",
|
||||||
description: "This textarea grows indefinitely with content",
|
description: "This textarea grows indefinitely with content",
|
||||||
input: {
|
|
||||||
placeholder: "This will grow as much as needed...",
|
|
||||||
autoResize: true,
|
autoResize: true,
|
||||||
minRows: 3,
|
minRows: 3,
|
||||||
|
input: {
|
||||||
|
placeholder: "This will grow as much as needed...",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,38 +7,50 @@ import {
|
|||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Label } from "./Label";
|
import { Label } from "./Label";
|
||||||
import { PolymorphicProps } from "@kobalte/core/polymorphic";
|
import { PolymorphicProps } from "@kobalte/core/polymorphic";
|
||||||
import { createEffect, createSignal, splitProps } from "solid-js";
|
import { createEffect, createSignal, mergeProps, splitProps } from "solid-js";
|
||||||
|
|
||||||
import "./TextInput.css";
|
import styles from "./TextField.module.css";
|
||||||
import { FieldProps } from "./Field";
|
import { FieldProps } from "./Field";
|
||||||
import { Orienter } from "./Orienter";
|
import { Orienter } from "./Orienter";
|
||||||
import { keepTruthy } from "@/src/util";
|
import { keepTruthy } from "@/src/util";
|
||||||
|
|
||||||
export type TextAreaProps = FieldProps &
|
export type TextAreaProps = FieldProps &
|
||||||
TextFieldRootProps & {
|
TextFieldRootProps & {
|
||||||
input: PolymorphicProps<"textarea", TextFieldTextAreaProps<"input">> & {
|
input: PolymorphicProps<"textarea", TextFieldTextAreaProps<"input">>;
|
||||||
autoResize?: boolean;
|
autoResize?: boolean;
|
||||||
minRows?: number;
|
minRows?: number;
|
||||||
maxRows?: number;
|
maxRows?: number;
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
export const TextArea = (props: TextAreaProps) => {
|
export const TextArea = (props: TextAreaProps) => {
|
||||||
|
const withDefaults = mergeProps(
|
||||||
|
{ size: "default", minRows: 1, maxRows: Infinity } as const,
|
||||||
|
props,
|
||||||
|
);
|
||||||
|
const [local, other] = splitProps(withDefaults, [
|
||||||
|
"autoResize",
|
||||||
|
"minRows",
|
||||||
|
"maxRows",
|
||||||
|
"size",
|
||||||
|
"orientation",
|
||||||
|
"inverted",
|
||||||
|
"ghost",
|
||||||
|
"input",
|
||||||
|
]);
|
||||||
|
|
||||||
let textareaRef: HTMLTextAreaElement;
|
let textareaRef: HTMLTextAreaElement;
|
||||||
|
|
||||||
const [lineHeight, setLineHeight] = createSignal(0);
|
const [lineHeight, setLineHeight] = createSignal(0);
|
||||||
|
|
||||||
const autoResize = () => {
|
const autoResize = () => {
|
||||||
const input = props.input;
|
if (!(textareaRef && local.autoResize && lineHeight() > 0)) return;
|
||||||
|
|
||||||
if (!(textareaRef && input.autoResize && lineHeight() > 0)) return;
|
|
||||||
|
|
||||||
// Reset height to auto to get accurate scrollHeight
|
// Reset height to auto to get accurate scrollHeight
|
||||||
textareaRef.style.height = "auto";
|
textareaRef.style.height = "auto";
|
||||||
|
|
||||||
// Calculate min and max heights based on rows
|
// Calculate min and max heights based on rows
|
||||||
const minHeight = (input.minRows || 1) * lineHeight();
|
const minHeight = local.minRows * lineHeight();
|
||||||
const maxHeight = input.maxRows ? input.maxRows * lineHeight() : Infinity;
|
const maxHeight = local.maxRows * lineHeight();
|
||||||
|
|
||||||
// Set the height based on content, respecting min/max
|
// Set the height based on content, respecting min/max
|
||||||
const newHeight = Math.min(
|
const newHeight = Math.min(
|
||||||
@@ -53,7 +65,7 @@ export const TextArea = (props: TextAreaProps) => {
|
|||||||
|
|
||||||
// Set up auto-resize effect
|
// Set up auto-resize effect
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (textareaRef && props.input.autoResize) {
|
if (textareaRef && local.autoResize) {
|
||||||
// Get computed line height
|
// Get computed line height
|
||||||
const computedStyle = window.getComputedStyle(textareaRef);
|
const computedStyle = window.getComputedStyle(textareaRef);
|
||||||
const computedLineHeight = parseFloat(computedStyle.lineHeight);
|
const computedLineHeight = parseFloat(computedStyle.lineHeight);
|
||||||
@@ -68,32 +80,14 @@ export const TextArea = (props: TextAreaProps) => {
|
|||||||
|
|
||||||
// Watch for value changes to trigger resize
|
// Watch for value changes to trigger resize
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (props.input.autoResize && textareaRef) {
|
if (local.autoResize && textareaRef) {
|
||||||
// Access the value to create a dependency
|
// Access the value to create a dependency
|
||||||
const _ = props.value || props.defaultValue || "";
|
const _ = other.value || other.defaultValue;
|
||||||
// Trigger resize on the next tick to ensure DOM is updated
|
// Trigger resize on the next tick to ensure DOM is updated
|
||||||
setTimeout(autoResize, 0);
|
setTimeout(autoResize, 0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const input = props.input;
|
|
||||||
|
|
||||||
// TextField.Textarea already has an `autoResize` prop
|
|
||||||
// We filter our props out to avoid conflicting behaviour
|
|
||||||
const [_, textareaProps] = splitProps(input, [
|
|
||||||
"autoResize",
|
|
||||||
"minRows",
|
|
||||||
"maxRows",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [styleProps, otherProps] = splitProps(props, [
|
|
||||||
"class",
|
|
||||||
"size",
|
|
||||||
"orientation",
|
|
||||||
"inverted",
|
|
||||||
"ghost",
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
ref={(el: HTMLDivElement) => {
|
ref={(el: HTMLDivElement) => {
|
||||||
@@ -102,46 +96,44 @@ export const TextArea = (props: TextAreaProps) => {
|
|||||||
textareaRef = el.querySelector("textarea")! as HTMLTextAreaElement;
|
textareaRef = el.querySelector("textarea")! as HTMLTextAreaElement;
|
||||||
}}
|
}}
|
||||||
class={cx(
|
class={cx(
|
||||||
styleProps.class,
|
styles.textField,
|
||||||
"form-field",
|
local.size != "default" && styles[local.size],
|
||||||
"textarea",
|
local.orientation == "horizontal" && styles[local.orientation],
|
||||||
styleProps.size,
|
|
||||||
styleProps.orientation,
|
|
||||||
{
|
{
|
||||||
inverted: styleProps.inverted,
|
[styles.inverted]: local.inverted,
|
||||||
ghost: styleProps.ghost,
|
[styles.ghost]: local.ghost,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
{...otherProps}
|
{...other}
|
||||||
>
|
>
|
||||||
<Orienter orientation={styleProps.orientation} align={"start"}>
|
<Orienter orientation={local.orientation} align={"start"}>
|
||||||
<Label
|
<Label
|
||||||
labelComponent={TextField.Label}
|
labelComponent={TextField.Label}
|
||||||
descriptionComponent={TextField.Description}
|
descriptionComponent={TextField.Description}
|
||||||
in={keepTruthy(
|
in={keepTruthy(
|
||||||
props.orientation == "horizontal" && "Orienter-horizontal",
|
props.orientation == "horizontal" && "Orienter-horizontal",
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...withDefaults}
|
||||||
/>
|
/>
|
||||||
<TextField.TextArea
|
<TextField.TextArea
|
||||||
class={cx(input.class, {
|
class={cx({
|
||||||
"auto-resize": input.autoResize,
|
[styles.autoResize]: local.autoResize,
|
||||||
})}
|
})}
|
||||||
onInput={(e) => {
|
onInput={(e) => {
|
||||||
autoResize();
|
autoResize();
|
||||||
|
|
||||||
if (!input.onInput) {
|
if (!local.input.onInput) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call original onInput if it exists
|
// Call original onInput if it exists
|
||||||
if (typeof input.onInput === "function") {
|
if (typeof local.input.onInput === "function") {
|
||||||
input.onInput(e);
|
local.input.onInput(e);
|
||||||
} else if (Array.isArray(input.onInput)) {
|
} else if (Array.isArray(local.input.onInput)) {
|
||||||
input.onInput.forEach((handler) => handler(e));
|
local.input.onInput.forEach((handler) => handler(e));
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
{...textareaProps}
|
{...local.input}
|
||||||
/>
|
/>
|
||||||
</Orienter>
|
</Orienter>
|
||||||
</TextField>
|
</TextField>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
div.form-field {
|
.textField {
|
||||||
&.text input,
|
input,
|
||||||
&.textarea textarea {
|
textarea {
|
||||||
@apply w-full px-2 py-1.5 rounded-sm;
|
@apply w-full px-2 py-1.5 rounded-sm;
|
||||||
@apply outline outline-1 outline-def-acc-1 bg-def-1 fg-def-1;
|
@apply outline outline-1 outline-def-acc-1 bg-def-1 fg-def-1;
|
||||||
|
|
||||||
@@ -38,12 +38,12 @@ div.form-field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.textarea textarea {
|
textarea {
|
||||||
&[data-readonly] {
|
&[data-readonly] {
|
||||||
@apply overflow-y-hidden;
|
@apply overflow-y-hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.auto-resize {
|
&.autoResize {
|
||||||
@apply resize-none overflow-y-auto;
|
@apply resize-none overflow-y-auto;
|
||||||
transition: height 0.1s ease-out;
|
transition: height 0.1s ease-out;
|
||||||
}
|
}
|
||||||
@@ -52,48 +52,15 @@ div.form-field {
|
|||||||
&.horizontal {
|
&.horizontal {
|
||||||
@apply flex-row gap-2 justify-between;
|
@apply flex-row gap-2 justify-between;
|
||||||
|
|
||||||
&.text div.input-container,
|
.inputContainer,
|
||||||
&.textarea textarea {
|
textarea {
|
||||||
@apply w-1/2 grow;
|
@apply w-1/2 grow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.text div.input-container {
|
|
||||||
@apply inline-block relative w-full h-[1.875rem];
|
|
||||||
|
|
||||||
/* I'm unsure why I have to do this */
|
|
||||||
@apply leading-none;
|
|
||||||
|
|
||||||
& > input {
|
|
||||||
@apply w-full h-[1.875rem];
|
|
||||||
|
|
||||||
&.has-icon {
|
|
||||||
@apply pl-7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .icon {
|
|
||||||
@apply absolute left-2 top-1/2 transform -translate-y-1/2;
|
|
||||||
@apply w-[0.875rem] h-[0.875rem] pointer-events-none;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .start-component {
|
|
||||||
@apply absolute left-2 top-1/2 transform -translate-y-1/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .end-component {
|
|
||||||
@apply absolute right-2 top-1/2 transform -translate-y-1/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .start-component,
|
|
||||||
& > .end-component {
|
|
||||||
@apply size-fit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.s {
|
&.s {
|
||||||
&.text input,
|
input,
|
||||||
&.textarea textarea {
|
textarea {
|
||||||
@apply px-1.5 py-1;
|
@apply px-1.5 py-1;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
|
||||||
@@ -102,26 +69,18 @@ div.form-field {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.text div.input-container {
|
.inputContainer {
|
||||||
@apply h-[1.25rem];
|
@apply h-[1.25rem];
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@apply h-[1.25rem];
|
@apply h-[1.25rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
input.has-icon {
|
|
||||||
@apply pl-6;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .icon {
|
|
||||||
@apply w-[0.6875rem] h-[0.6875rem];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.inverted {
|
&.inverted {
|
||||||
&.text input,
|
input,
|
||||||
&.textarea textarea {
|
textarea {
|
||||||
@apply bg-inv-1 fg-inv-1 outline-inv-acc-1;
|
@apply bg-inv-1 fg-inv-1 outline-inv-acc-1;
|
||||||
|
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
@@ -151,13 +110,33 @@ div.form-field {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.ghost {
|
&.ghost {
|
||||||
&.text input,
|
input,
|
||||||
&.textarea textarea {
|
textarea {
|
||||||
@apply outline-none;
|
@apply outline-none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
.inputContainer {
|
||||||
@apply outline-none;
|
@apply inline-block relative w-full h-[1.875rem];
|
||||||
}
|
|
||||||
|
/* I'm unsure why I have to do this */
|
||||||
|
@apply leading-none;
|
||||||
|
|
||||||
|
& > input {
|
||||||
|
@apply w-full h-[1.875rem];
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .startComponent {
|
||||||
|
@apply absolute left-2 top-1/2 transform -translate-y-1/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .endComponent {
|
||||||
|
@apply absolute right-2 top-1/2 transform -translate-y-1/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .startComponent,
|
||||||
|
& > .endComponent {
|
||||||
|
@apply size-fit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,10 @@ import {
|
|||||||
TextFieldInputProps,
|
TextFieldInputProps,
|
||||||
TextFieldRootProps,
|
TextFieldRootProps,
|
||||||
} from "@kobalte/core/text-field";
|
} from "@kobalte/core/text-field";
|
||||||
import Icon, { IconVariant } from "@/src/components/Icon/Icon";
|
|
||||||
|
|
||||||
import cx from "classnames";
|
import cx from "classnames";
|
||||||
import { Label } from "./Label";
|
import { Label } from "./Label";
|
||||||
import "./TextInput.css";
|
import styles from "./TextField.module.css";
|
||||||
import { PolymorphicProps } from "@kobalte/core/polymorphic";
|
import { PolymorphicProps } from "@kobalte/core/polymorphic";
|
||||||
import { FieldProps } from "./Field";
|
import { FieldProps } from "./Field";
|
||||||
import { Orienter } from "./Orienter";
|
import { Orienter } from "./Orienter";
|
||||||
@@ -15,6 +14,7 @@ import {
|
|||||||
Component,
|
Component,
|
||||||
createEffect,
|
createEffect,
|
||||||
createSignal,
|
createSignal,
|
||||||
|
mergeProps,
|
||||||
onMount,
|
onMount,
|
||||||
splitProps,
|
splitProps,
|
||||||
} from "solid-js";
|
} from "solid-js";
|
||||||
@@ -22,19 +22,21 @@ import { keepTruthy } from "@/src/util";
|
|||||||
|
|
||||||
export type TextInputProps = FieldProps &
|
export type TextInputProps = FieldProps &
|
||||||
TextFieldRootProps & {
|
TextFieldRootProps & {
|
||||||
icon?: IconVariant;
|
|
||||||
input?: PolymorphicProps<"input", TextFieldInputProps<"input">>;
|
input?: PolymorphicProps<"input", TextFieldInputProps<"input">>;
|
||||||
startComponent?: Component<Pick<FieldProps, "inverted">>;
|
startComponent?: Component<Pick<FieldProps, "inverted">>;
|
||||||
endComponent?: Component<Pick<FieldProps, "inverted">>;
|
endComponent?: Component<Pick<FieldProps, "inverted">>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TextInput = (props: TextInputProps) => {
|
export const TextInput = (props: TextInputProps) => {
|
||||||
const [styleProps, otherProps] = splitProps(props, [
|
const withDefaults = mergeProps({ size: "default" } as const, props);
|
||||||
"class",
|
const [local, other] = splitProps(withDefaults, [
|
||||||
"size",
|
"size",
|
||||||
"orientation",
|
"orientation",
|
||||||
"inverted",
|
"inverted",
|
||||||
"ghost",
|
"ghost",
|
||||||
|
"input",
|
||||||
|
"startComponent",
|
||||||
|
"endComponent",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let inputRef: HTMLInputElement | undefined;
|
let inputRef: HTMLInputElement | undefined;
|
||||||
@@ -73,50 +75,35 @@ export const TextInput = (props: TextInputProps) => {
|
|||||||
return (
|
return (
|
||||||
<TextField
|
<TextField
|
||||||
class={cx(
|
class={cx(
|
||||||
styleProps.class,
|
styles.textField,
|
||||||
"form-field",
|
local.size != "default" && styles[local.size],
|
||||||
"text",
|
local.orientation == "horizontal" && styles[local.orientation],
|
||||||
styleProps.size,
|
|
||||||
styleProps.orientation,
|
|
||||||
{
|
{
|
||||||
inverted: styleProps.inverted,
|
[styles.inverted]: local.inverted,
|
||||||
ghost: styleProps.ghost,
|
[styles.ghost]: local.ghost,
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
{...otherProps}
|
{...other}
|
||||||
>
|
>
|
||||||
<Orienter orientation={styleProps.orientation}>
|
<Orienter orientation={local.orientation}>
|
||||||
<Label
|
<Label
|
||||||
labelComponent={TextField.Label}
|
labelComponent={TextField.Label}
|
||||||
descriptionComponent={TextField.Description}
|
descriptionComponent={TextField.Description}
|
||||||
in={keepTruthy(
|
in={keepTruthy(
|
||||||
props.orientation == "horizontal" && "Orienter-horizontal",
|
props.orientation == "horizontal" && "Orienter-horizontal",
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...withDefaults}
|
||||||
/>
|
/>
|
||||||
<div class="input-container">
|
<div class={styles.inputContainer}>
|
||||||
{props.startComponent && !props.readOnly && (
|
{local.startComponent && !other.readOnly && (
|
||||||
<div ref={startComponentRef} class="start-component">
|
<div ref={startComponentRef} class={styles.startComponent}>
|
||||||
{props.startComponent({ inverted: props.inverted })}
|
{local.startComponent({ inverted: local.inverted })}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{props.icon && !props.readOnly && (
|
<TextField.Input ref={inputRef} {...local.input} />
|
||||||
<Icon
|
{local.endComponent && !other.readOnly && (
|
||||||
icon={props.icon}
|
<div ref={endComponentRef} class={styles.endComponent}>
|
||||||
inverted={styleProps.inverted}
|
{local.endComponent({ inverted: local.inverted })}
|
||||||
color={props.disabled ? "tertiary" : "quaternary"}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<TextField.Input
|
|
||||||
ref={inputRef}
|
|
||||||
{...props.input}
|
|
||||||
class={cx({
|
|
||||||
"has-icon": props.icon && !props.readOnly,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
{props.endComponent && !props.readOnly && (
|
|
||||||
<div ref={endComponentRef} class="end-component">
|
|
||||||
{props.endComponent({ inverted: props.inverted })}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import cx from "classnames";
|
|
||||||
import styles from "./ClanSettingsModal.module.css";
|
import styles from "./ClanSettingsModal.module.css";
|
||||||
import { Modal } from "@/src/components/Modal/Modal";
|
import { Modal } from "@/src/components/Modal/Modal";
|
||||||
import { ClanDetails } from "@/src/hooks/queries";
|
import { ClanDetails } from "@/src/hooks/queries";
|
||||||
@@ -104,7 +103,7 @@ export const ClanSettingsModal = (props: ClanSettingsModalProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
class={cx(styles.modal)}
|
class={styles.modal}
|
||||||
open
|
open
|
||||||
title="Settings"
|
title="Settings"
|
||||||
onClose={props.onClose}
|
onClose={props.onClose}
|
||||||
@@ -112,7 +111,7 @@ export const ClanSettingsModal = (props: ClanSettingsModalProps) => {
|
|||||||
<Form onSubmit={handleSubmit}>{props.children}</Form>
|
<Form onSubmit={handleSubmit}>{props.children}</Form>
|
||||||
)}
|
)}
|
||||||
metaHeader={() => (
|
metaHeader={() => (
|
||||||
<div class={cx(styles.header)}>
|
<div class={styles.header}>
|
||||||
<Typography
|
<Typography
|
||||||
hierarchy="label"
|
hierarchy="label"
|
||||||
family="mono"
|
family="mono"
|
||||||
@@ -127,7 +126,7 @@ export const ClanSettingsModal = (props: ClanSettingsModalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div class={cx(styles.content)}>
|
<div class={styles.content}>
|
||||||
<Show when={errorMessage()}>
|
<Show when={errorMessage()}>
|
||||||
<Alert type="error" title="Error" description={errorMessage()} />
|
<Alert type="error" title="Error" description={errorMessage()} />
|
||||||
</Show>
|
</Show>
|
||||||
@@ -164,11 +163,11 @@ export const ClanSettingsModal = (props: ClanSettingsModalProps) => {
|
|||||||
"A description of this Clan",
|
"A description of this Clan",
|
||||||
)}
|
)}
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
|
autoResize={true}
|
||||||
|
minRows={2}
|
||||||
|
maxRows={4}
|
||||||
input={{
|
input={{
|
||||||
...input,
|
...input,
|
||||||
autoResize: true,
|
|
||||||
minRows: 2,
|
|
||||||
maxRows: 4,
|
|
||||||
placeholder: "No description",
|
placeholder: "No description",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -176,9 +175,9 @@ export const ClanSettingsModal = (props: ClanSettingsModalProps) => {
|
|||||||
</Field>
|
</Field>
|
||||||
</Fieldset>
|
</Fieldset>
|
||||||
|
|
||||||
<div class={cx(styles.remove)}>
|
<div class={styles.remove}>
|
||||||
|
<div class={styles.clanInput}>
|
||||||
<TextInput
|
<TextInput
|
||||||
class={cx(styles.clanInput)}
|
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
onChange={setRemoveValue}
|
onChange={setRemoveValue}
|
||||||
input={{
|
input={{
|
||||||
@@ -191,6 +190,7 @@ export const ClanSettingsModal = (props: ClanSettingsModalProps) => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
hierarchy="primary"
|
hierarchy="primary"
|
||||||
|
|||||||
@@ -126,11 +126,11 @@ export const SectionGeneral = (props: SectionGeneralProps) => {
|
|||||||
readOnly={readOnly(editing, "description")}
|
readOnly={readOnly(editing, "description")}
|
||||||
tooltip={tooltipText("description", fieldsSchema()!)}
|
tooltip={tooltipText("description", fieldsSchema()!)}
|
||||||
orientation="horizontal"
|
orientation="horizontal"
|
||||||
|
autoResize={true}
|
||||||
|
minRows={2}
|
||||||
|
maxRows={4}
|
||||||
input={{
|
input={{
|
||||||
...input,
|
...input,
|
||||||
autoResize: true,
|
|
||||||
minRows: 2,
|
|
||||||
maxRows: 4,
|
|
||||||
placeholder: "No description",
|
placeholder: "No description",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user