summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/components/text-field.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/ui/src/components/text-field.tsx')
-rw-r--r--packages/ui/src/components/text-field.tsx103
1 files changed, 103 insertions, 0 deletions
diff --git a/packages/ui/src/components/text-field.tsx b/packages/ui/src/components/text-field.tsx
new file mode 100644
index 000000000..63ffb2594
--- /dev/null
+++ b/packages/ui/src/components/text-field.tsx
@@ -0,0 +1,103 @@
+import { TextField as Kobalte } from "@kobalte/core/text-field"
+import { createSignal, Show, splitProps } from "solid-js"
+import type { ComponentProps } from "solid-js"
+import { IconButton } from "./icon-button"
+import { Tooltip } from "./tooltip"
+
+export interface TextFieldProps
+ extends ComponentProps<typeof Kobalte.Input>,
+ Partial<
+ Pick<
+ ComponentProps<typeof Kobalte>,
+ | "name"
+ | "defaultValue"
+ | "value"
+ | "onChange"
+ | "onKeyDown"
+ | "validationState"
+ | "required"
+ | "disabled"
+ | "readOnly"
+ >
+ > {
+ label?: string
+ hideLabel?: boolean
+ description?: string
+ error?: string
+ variant?: "normal" | "ghost"
+ copyable?: boolean
+}
+
+export function TextField(props: TextFieldProps) {
+ const [local, others] = splitProps(props, [
+ "name",
+ "defaultValue",
+ "value",
+ "onChange",
+ "onKeyDown",
+ "validationState",
+ "required",
+ "disabled",
+ "readOnly",
+ "class",
+ "label",
+ "hideLabel",
+ "description",
+ "error",
+ "variant",
+ "copyable",
+ ])
+ const [copied, setCopied] = createSignal(false)
+
+ async function handleCopy() {
+ const value = local.value ?? local.defaultValue ?? ""
+ await navigator.clipboard.writeText(value)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ }
+
+ return (
+ <Kobalte
+ data-component="input"
+ data-variant={local.variant || "normal"}
+ name={local.name}
+ defaultValue={local.defaultValue}
+ value={local.value}
+ onChange={local.onChange}
+ onKeyDown={local.onKeyDown}
+ required={local.required}
+ disabled={local.disabled}
+ readOnly={local.readOnly}
+ validationState={local.validationState}
+ >
+ <Show when={local.label}>
+ <Kobalte.Label data-slot="input-label" classList={{ "sr-only": local.hideLabel }}>
+ {local.label}
+ </Kobalte.Label>
+ </Show>
+ <div data-slot="input-wrapper">
+ <Kobalte.Input {...others} data-slot="input-input" class={local.class} />
+ <Show when={local.copyable}>
+ <Tooltip value={copied() ? "Copied" : "Copy to clipboard"} placement="top" gutter={8}>
+ <IconButton
+ type="button"
+ icon={copied() ? "check" : "copy"}
+ variant="ghost"
+ onClick={handleCopy}
+ data-slot="input-copy-button"
+ />
+ </Tooltip>
+ </Show>
+ </div>
+ <Show when={local.description}>
+ <Kobalte.Description data-slot="input-description">{local.description}</Kobalte.Description>
+ </Show>
+ <Kobalte.ErrorMessage data-slot="input-error">{local.error}</Kobalte.ErrorMessage>
+ </Kobalte>
+ )
+}
+
+/** @deprecated Use TextField instead */
+export const Input = TextField
+/** @deprecated Use TextFieldProps instead */
+export type InputProps = TextFieldProps