mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-13 08:06:41 +00:00
133 lines
4.2 KiB
Vue
133 lines
4.2 KiB
Vue
<script setup lang="ts">
|
|
import { computed, inject, useAttrs } from 'vue'
|
|
import { formFieldKey } from './BaseForm/context'
|
|
|
|
type InputSize = 'sm' | 'md' | 'lg'
|
|
|
|
defineOptions({ inheritAttrs: false })
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
placeholder?: string
|
|
type?: string
|
|
disabled?: boolean
|
|
size?: InputSize
|
|
}>(),
|
|
{
|
|
placeholder: '',
|
|
type: 'text',
|
|
disabled: false,
|
|
size: 'md',
|
|
},
|
|
)
|
|
|
|
const model = defineModel<string>({ default: '' })
|
|
const attrs = useAttrs()
|
|
const field = inject(formFieldKey, null)
|
|
|
|
const controlId = computed(() => typeof attrs.id === 'string' ? attrs.id : field?.controlId.value)
|
|
const invalid = computed(() => Boolean(
|
|
field?.invalid.value || attrs['aria-invalid'] === true || attrs['aria-invalid'] === 'true',
|
|
))
|
|
const describedBy = computed(() => [
|
|
typeof attrs['aria-describedby'] === 'string' ? attrs['aria-describedby'] : undefined,
|
|
field?.describedBy.value,
|
|
].filter(Boolean).join(' ') || undefined)
|
|
|
|
const rootAttrs = computed(() => ({ class: attrs.class, style: attrs.style }))
|
|
const controlAttrs = computed(() => Object.fromEntries(
|
|
Object.entries(attrs).filter(([key]) => ![
|
|
'class',
|
|
'style',
|
|
'id',
|
|
'aria-describedby',
|
|
'aria-invalid',
|
|
'aria-required',
|
|
].includes(key)),
|
|
))
|
|
|
|
const sizeClasses: Record<InputSize, string> = {
|
|
sm: 'h-cp-control-sm gap-2 px-3 text-xs',
|
|
md: 'h-cp-control-md gap-2.5 px-3.5 text-[13px]',
|
|
lg: 'h-cp-control-lg gap-3 px-4 text-[14px]',
|
|
}
|
|
|
|
const containerClasses = computed(() => [
|
|
'relative inline-flex w-full min-w-0 items-center overflow-visible rounded-cp-control border-0 text-cp-primary shadow-cp-input transition-[background-color,box-shadow,color] duration-[160ms] motion-reduce:transition-none',
|
|
sizeClasses[props.size],
|
|
props.disabled
|
|
? 'cursor-not-allowed bg-cp-disabled text-cp-disabled-text shadow-none'
|
|
: invalid.value
|
|
? 'bg-(--cp-input-error-soft-bg) shadow-cp-input-error'
|
|
: [
|
|
'bg-[var(--cp-input-current-bg,var(--cp-input-context-bg))]',
|
|
'hover:bg-[var(--cp-input-current-bg-hover,var(--cp-input-context-bg-hover))] hover:shadow-cp-input-hover',
|
|
'focus-within:bg-(--cp-input-soft-bg-focus) focus-within:shadow-cp-input-focus',
|
|
],
|
|
])
|
|
|
|
const iconClasses = computed(() => [
|
|
'inline-flex shrink-0',
|
|
props.disabled
|
|
? 'text-cp-disabled-icon'
|
|
: invalid.value
|
|
? 'text-cp-danger'
|
|
: 'text-cp-muted-text',
|
|
])
|
|
|
|
function updateModel(event: Event) {
|
|
model.value = (event.target as HTMLInputElement).value
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div v-bind="rootAttrs" class="min-w-0">
|
|
<span class="base-input__control" :class="containerClasses">
|
|
<span v-if="$slots.prefix" :class="iconClasses" aria-hidden="true">
|
|
<slot name="prefix" />
|
|
</span>
|
|
<input
|
|
v-bind="controlAttrs"
|
|
:id="controlId"
|
|
:value="model"
|
|
class="base-input__field h-full min-w-0 flex-1 border-0 bg-transparent font-emphasis leading-[1.15] text-cp-primary outline-0 placeholder:text-cp-muted-text disabled:cursor-not-allowed disabled:text-cp-disabled-text"
|
|
:placeholder="placeholder"
|
|
:type="type"
|
|
:disabled="disabled"
|
|
:required="field?.required.value || undefined"
|
|
:aria-describedby="describedBy"
|
|
:aria-invalid="invalid || undefined"
|
|
:aria-required="field?.required.value || undefined"
|
|
@input="updateModel"
|
|
>
|
|
<span v-if="$slots.suffix" :class="iconClasses">
|
|
<slot name="suffix" />
|
|
</span>
|
|
</span>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.base-input__control {
|
|
--base-input-autofill-bg: var(--cp-input-current-bg, var(--cp-input-context-bg));
|
|
}
|
|
|
|
.base-input__control:hover {
|
|
--base-input-autofill-bg: var(--cp-input-current-bg-hover, var(--cp-input-context-bg-hover));
|
|
}
|
|
|
|
.base-input__control:focus-within {
|
|
--base-input-autofill-bg: var(--cp-input-soft-bg-focus);
|
|
}
|
|
|
|
.base-input__field:-webkit-autofill,
|
|
.base-input__field:-webkit-autofill:hover,
|
|
.base-input__field:-webkit-autofill:focus,
|
|
.base-input__field:autofill {
|
|
caret-color: var(--cp-text-primary);
|
|
-webkit-text-fill-color: var(--cp-text-primary) !important;
|
|
-webkit-box-shadow: 0 0 0 1000px var(--base-input-autofill-bg) inset !important;
|
|
box-shadow: 0 0 0 1000px var(--base-input-autofill-bg) inset !important;
|
|
}
|
|
</style>
|