Skip to content

Commit

Permalink
feat(Editable): use Primitive for input to enable textarea
Browse files Browse the repository at this point in the history
  • Loading branch information
epr3 committed Jun 26, 2024
1 parent bed3a30 commit e8877b0
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 20 deletions.
35 changes: 19 additions & 16 deletions packages/radix-vue/src/Editable/EditableInput.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<script lang="ts">
export interface EditableInputProps {
/** The type of the input */
type?: 'text' | 'email' | 'password' | 'search' | 'tel' | 'url'
import { Primitive, type PrimitiveProps, usePrimitiveElement } from '@/Primitive'
export interface EditableInputProps extends PrimitiveProps {
}
</script>

<script setup lang="ts">
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { computed, nextTick, onMounted, watch } from 'vue'
import { injectEditableRootContext } from './EditableRoot.vue'
import { useKbd } from '@/shared'
withDefaults(defineProps<EditableInputProps>(), {
type: 'text',
const props = withDefaults(defineProps<EditableInputProps>(), {
as: 'input',
})
const kbd = useKbd()
Expand All @@ -22,23 +22,23 @@ const disabled = computed(() => context.disabled.value)
const placeholder = computed(() => context.placeholder.value?.edit)
const inputRef = ref<HTMLInputElement | undefined>()
const { primitiveElement, currentElement: inputRef } = usePrimitiveElement()
onMounted(() => {
context.inputRef.value = inputRef.value
context.inputRef.value = inputRef.value as HTMLInputElement
if (context.startWithEditMode.value) {
inputRef.value?.focus()
context.inputRef.value?.focus()
if (context.selectOnFocus.value)
inputRef.value?.select()
context.inputRef.value?.select()
}
})
watch(context.isEditing, (value) => {
if (value) {
nextTick(() => {
inputRef.value?.focus()
context.inputRef.value?.focus()
if (context.selectOnFocus.value)
inputRef.value?.select()
context.inputRef.value?.select()
})
}
})
Expand All @@ -50,10 +50,10 @@ function handleSubmitKeyDown(event: KeyboardEvent) {
</script>

<template>
<input
ref="inputRef"
v-model="context.modelValue.value"
:type="type"
<Primitive
ref="primitiveElement"
v-bind="props"
:value="context.modelValue.value"
:placeholder="placeholder"
:disabled="disabled"
:data-disabled="disabled ? '' : undefined"
Expand All @@ -62,7 +62,10 @@ function handleSubmitKeyDown(event: KeyboardEvent) {
aria-label="editable input"
:hidden="context.autoResize.value ? undefined : !context.isEditing.value"
:style="context.autoResize.value ? { all: 'unset', gridArea: '1 / 1 / auto / auto', visibility: !context.isEditing.value ? 'hidden' : undefined } : undefined"
@input="context.modelValue.value = $event.target.value"
@keydown.enter.space="handleSubmitKeyDown"
@keydown.esc="context.cancel"
>
<slot />
</Primitive>
</template>
41 changes: 37 additions & 4 deletions packages/radix-vue/src/Editable/EditableRoot.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { PrimitiveProps } from '@/Primitive'
import { createContext, useDirection } from '@/shared'
import { createContext, useDirection, useFormControl } from '@/shared'
import type { Direction } from '@/shared/types'
import { DismissableLayer, type DismissableLayerEmits } from '@/DismissableLayer'
Expand Down Expand Up @@ -57,6 +57,8 @@ export interface EditableRootProps extends PrimitiveProps {
id?: string
/** The name of the field */
name?: string
/** When `true`, indicates that the user must set the value before the owning form can be submitted. */
required?: boolean
}
export type EditableRootEmits = Omit<DismissableLayerEmits, 'escapeKeyDown'> & {
Expand All @@ -74,18 +76,23 @@ export const [injectEditableRootContext, provideEditableRootContext]

<script setup lang="ts">
import { type Ref, computed, ref, toRefs } from 'vue'
import { Primitive } from '@/Primitive'
import { Primitive, usePrimitiveElement } from '@/Primitive'
import { useVModel } from '@vueuse/core'
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<EditableRootProps>(), {
defaultValue: '',
defaultValue: undefined,
as: 'div',
disabled: false,
submitMode: 'blur',
activationMode: 'focus',
selectOnFocus: false,
placeholder: 'Enter text...',
autoResize: false,
required: false,
})
const emits = defineEmits<EditableRootEmits>()
Expand Down Expand Up @@ -120,17 +127,22 @@ const {
selectOnFocus,
readonly,
autoResize,
required,
} = toRefs(props)
const inputRef = ref<HTMLInputElement | undefined>()
const dir = useDirection(propDir)
const isEditing = ref(startWithEditMode.value ?? false)
const modelValue = useVModel(props, 'modelValue', emits, {
defaultValue: defaultValue.value,
defaultValue: defaultValue.value ?? '',
passive: (props.modelValue === undefined) as false,
})
const { primitiveElement, currentElement } = usePrimitiveElement()
const isFormControl = useFormControl(currentElement)
const placeholder = computed(() => {
return typeof propPlaceholder.value === 'string' ? { edit: propPlaceholder.value, preview: propPlaceholder.value } : propPlaceholder.value
})
Expand Down Expand Up @@ -207,10 +219,13 @@ provideEditableRootContext({
@dismiss="handleDismiss"
>
<Primitive
v-bind="$attrs"
ref="primitiveElement"
:as="as"
:as-child="asChild"
:dir="dir"
>
{{ modelValue }}
<slot
:model-value="modelValue"
:is-editing="isEditing"
Expand All @@ -221,4 +236,22 @@ provideEditableRootContext({
/>
</Primitive>
</DismissableLayer>

<input
v-if="isFormControl"
type="text"
tabindex="-1"
aria-hidden
:value="modelValue"
:name="name"
:disabled="disabled"
:required="required"
:style="{
transform: 'translateX(-100%)',
position: 'absolute',
pointerEvents: 'none',
opacity: 0,
margin: 0,
}"
>
</template>

0 comments on commit e8877b0

Please sign in to comment.