diff options
Diffstat (limited to 'packages/ui/src')
| -rw-r--r-- | packages/ui/src/components/dialog.css | 11 | ||||
| -rw-r--r-- | packages/ui/src/components/icon.tsx | 2 | ||||
| -rw-r--r-- | packages/ui/src/components/list.css | 3 | ||||
| -rw-r--r-- | packages/ui/src/components/select-dialog.tsx | 4 | ||||
| -rw-r--r-- | packages/ui/src/components/text-field.css (renamed from packages/ui/src/components/input.css) | 62 | ||||
| -rw-r--r-- | packages/ui/src/components/text-field.tsx (renamed from packages/ui/src/components/input.tsx) | 42 | ||||
| -rw-r--r-- | packages/ui/src/styles/index.css | 2 |
7 files changed, 98 insertions, 28 deletions
diff --git a/packages/ui/src/components/dialog.css b/packages/ui/src/components/dialog.css index 1c7cd4f41..979906e26 100644 --- a/packages/ui/src/components/dialog.css +++ b/packages/ui/src/components/dialog.css @@ -88,8 +88,19 @@ flex-direction: column; flex: 1; overflow-y: auto; + + &:focus-visible { + outline: none; + } + } + &:focus-visible { + outline: none; } } + + &:focus-visible { + outline: none; + } } } diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 56cef2d8c..ce4bf7556 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -47,6 +47,8 @@ const icons = { "layout-bottom-full": `<path d="M2.5 17.5L2.5 12.2059L17.5 12.2059L17.5 17.5L2.5 17.5Z" fill="currentColor"/><path d="M2.5 17.5L2.5 2.5M2.5 17.5L17.5 17.5M2.5 17.5L2.5 12.2059M2.5 2.5L17.5 2.5M2.5 2.5L2.5 12.2059M17.5 2.5L17.5 17.5M17.5 2.5L17.5 12.2059M17.5 17.5L17.5 12.2059M17.5 12.2059L2.5 12.2059" stroke="currentColor" stroke-linecap="square"/>`, "dot-grid": `<path d="M2.08398 9.16602H3.75065V10.8327H2.08398V9.16602Z" fill="currentColor"/><path d="M10.834 9.16602H9.16732V10.8327H10.834V9.16602Z" fill="currentColor"/><path d="M16.2507 9.16602H17.9173V10.8327H16.2507V9.16602Z" fill="currentColor"/><path d="M2.08398 9.16602H3.75065V10.8327H2.08398V9.16602Z" stroke="currentColor"/><path d="M10.834 9.16602H9.16732V10.8327H10.834V9.16602Z" stroke="currentColor"/><path d="M16.2507 9.16602H17.9173V10.8327H16.2507V9.16602Z" stroke="currentColor"/>`, "circle-check": `<path d="M12.4987 7.91732L8.7487 12.5007L7.08203 10.834M17.9154 10.0007C17.9154 14.3729 14.371 17.9173 9.9987 17.9173C5.62644 17.9173 2.08203 14.3729 2.08203 10.0007C2.08203 5.6284 5.62644 2.08398 9.9987 2.08398C14.371 2.08398 17.9154 5.6284 17.9154 10.0007Z" stroke="currentColor" stroke-linecap="square"/>`, + copy: `<path d="M6.2513 6.24935V2.91602H17.0846V13.7493H13.7513M13.7513 6.24935V17.0827H2.91797V6.24935H13.7513Z" stroke="currentColor" stroke-linecap="round"/>`, + check: `<path d="M5 11.9657L8.37838 14.7529L15 5.83398" stroke="currentColor" stroke-linecap="square"/>`, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/list.css b/packages/ui/src/components/list.css index 783b0ef4a..132824164 100644 --- a/packages/ui/src/components/list.css +++ b/packages/ui/src/components/list.css @@ -105,6 +105,9 @@ &:active { background: var(--surface-raised-base-active); } + &:focus-visible { + outline: none; + } } } } diff --git a/packages/ui/src/components/select-dialog.tsx b/packages/ui/src/components/select-dialog.tsx index efa6c405b..06953168c 100644 --- a/packages/ui/src/components/select-dialog.tsx +++ b/packages/ui/src/components/select-dialog.tsx @@ -3,7 +3,7 @@ import { Dialog, DialogProps } from "./dialog" import { Icon } from "./icon" import { IconButton } from "./icon-button" import { List, ListRef, ListProps } from "./list" -import { Input } from "./input" +import { TextField } from "./text-field" interface SelectDialogProps<T> extends Omit<ListProps<T>, "filter">, @@ -55,7 +55,7 @@ export function SelectDialog<T>(props: SelectDialogProps<T>) { <div data-component="select-dialog-input"> <div data-slot="select-dialog-input-container"> <Icon name="magnifying-glass" /> - <Input + <TextField ref={inputRef} autofocus variant="ghost" diff --git a/packages/ui/src/components/input.css b/packages/ui/src/components/text-field.css index 276e8069b..897050a63 100644 --- a/packages/ui/src/components/input.css +++ b/packages/ui/src/components/text-field.css @@ -40,6 +40,37 @@ letter-spacing: var(--letter-spacing-normal); } + [data-slot="input-wrapper"] { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding-right: 4px; + + border-radius: var(--radius-md); + border: 1px solid var(--border-weak-base); + background: var(--input-base); + + &:focus-within { + /* border/shadow-xs/select */ + box-shadow: + 0 0 0 3px var(--border-weak-selected), + 0 0 0 1px var(--border-selected), + 0 1px 2px -1px rgba(19, 16, 16, 0.25), + 0 1px 2px 0 rgba(19, 16, 16, 0.08), + 0 1px 3px 0 rgba(19, 16, 16, 0.12); + } + + &:has([data-invalid]) { + background: var(--surface-critical-weak); + border: 1px solid var(--border-critical-selected); + } + + &:not(:has([data-slot="input-copy-button"])) { + padding-right: 0; + } + } + [data-slot="input-input"] { color: var(--text-strong); @@ -47,12 +78,11 @@ height: 32px; padding: 2px 12px; align-items: center; - gap: 8px; - align-self: stretch; + flex: 1; + min-width: 0; - border-radius: var(--radius-md); - border: 1px solid var(--border-weak-base); - background: var(--input-base); + background: transparent; + border: none; /* text-14-regular */ font-family: var(--font-family-sans); @@ -64,19 +94,6 @@ &:focus { outline: none; - - /* border/shadow-xs/select */ - box-shadow: - 0 0 0 3px var(--border-weak-selected), - 0 0 0 1px var(--border-selected), - 0 1px 2px -1px rgba(19, 16, 16, 0.25), - 0 1px 2px 0 rgba(19, 16, 16, 0.08), - 0 1px 3px 0 rgba(19, 16, 16, 0.12); - } - - &[data-invalid] { - background: var(--surface-critical-weak); - border: 1px solid var(--border-critical-selected); } &::placeholder { @@ -84,6 +101,15 @@ } } + [data-slot="input-copy-button"] { + flex-shrink: 0; + color: var(--icon-base); + + &:hover { + color: var(--icon-strong-base); + } + } + [data-slot="input-error"] { color: var(--text-on-critical-base); diff --git a/packages/ui/src/components/input.tsx b/packages/ui/src/components/text-field.tsx index 8e2a115c6..63ffb2594 100644 --- a/packages/ui/src/components/input.tsx +++ b/packages/ui/src/components/text-field.tsx @@ -1,8 +1,10 @@ import { TextField as Kobalte } from "@kobalte/core/text-field" -import { Show, splitProps } from "solid-js" +import { createSignal, Show, splitProps } from "solid-js" import type { ComponentProps } from "solid-js" +import { IconButton } from "./icon-button" +import { Tooltip } from "./tooltip" -export interface InputProps +export interface TextFieldProps extends ComponentProps<typeof Kobalte.Input>, Partial< Pick< @@ -20,13 +22,13 @@ export interface InputProps > { label?: string hideLabel?: boolean - hidden?: boolean description?: string error?: string variant?: "normal" | "ghost" + copyable?: boolean } -export function Input(props: InputProps) { +export function TextField(props: TextFieldProps) { const [local, others] = splitProps(props, [ "name", "defaultValue", @@ -39,12 +41,21 @@ export function Input(props: InputProps) { "readOnly", "class", "label", - "hidden", "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" @@ -57,7 +68,6 @@ export function Input(props: InputProps) { required={local.required} disabled={local.disabled} readOnly={local.readOnly} - style={{ height: local.hidden ? 0 : undefined }} validationState={local.validationState} > <Show when={local.label}> @@ -65,7 +75,20 @@ export function Input(props: InputProps) { {local.label} </Kobalte.Label> </Show> - <Kobalte.Input {...others} data-slot="input-input" class={local.class} /> + <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> @@ -73,3 +96,8 @@ export function Input(props: InputProps) { </Kobalte> ) } + +/** @deprecated Use TextField instead */ +export const Input = TextField +/** @deprecated Use TextFieldProps instead */ +export type InputProps = TextFieldProps diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index 918ba9e44..d60082d93 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -21,7 +21,7 @@ @import "../components/provider-icon.css" layer(components); @import "../components/icon.css" layer(components); @import "../components/icon-button.css" layer(components); -@import "../components/input.css" layer(components); +@import "../components/text-field.css" layer(components); @import "../components/list.css" layer(components); @import "../components/logo.css" layer(components); @import "../components/markdown.css" layer(components); |
