Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Editable): update model value only on submit #1538

Merged
merged 5 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions docs/content/components/editable.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,27 @@ Contains the cancel trigger of the editable component.

<!-- @include: @/meta/EditableCancelTrigger.md -->

## Examples

### Change only on submit

By default the component will submit when `blur` event triggers. We can modify the `submit-mode` prop to alter this behavior.
In this case, we want to submit only when user click on `EditableSubmitTrigger`, so we change the submit mode to `none`.

```vue line=2,8
<template>
<EditableRoot submit-mode="none">
<EditableArea>
<EditablePreview />
<EditableInput />
</EditableArea>
<EditableEditTrigger />
<EditableSubmitTrigger />
<EditableCancelTrigger />
</EditableRoot>
</template>
```

## Accessibility

### Keyboard Interactions
Expand Down
10 changes: 7 additions & 3 deletions packages/radix-vue/src/Editable/Editable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ describe('editable', () => {
})

it('submits the value when pressing enter', async () => {
const { input, preview, rerender } = setup({ editableProps: { modelValue: '' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data }) } })
const { input, preview, rerender } = setup({ editableProps: { modelValue: '', submitMode: 'enter' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data }) } })

await userEvent.type(input, 'New Value')
await userEvent.keyboard(kbd.ENTER)
Expand All @@ -94,8 +94,10 @@ describe('editable', () => {
it('submits the value on blur', async () => {
const { input, preview, rerender } = setup({ editableProps: { modelValue: '', submitMode: 'blur' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data, submitMode: 'blur' }) } })

await userEvent.dblClick(preview)
await userEvent.type(input, 'New Value')
await userEvent.tab()
await userEvent.click(document.children[0])

expect(preview).toBeVisible()
expect(preview).toHaveTextContent('New Value')
})
Expand All @@ -112,8 +114,10 @@ describe('editable', () => {
it('submits the value on blur if submitMode is both', async () => {
const { input, preview, rerender } = setup({ editableProps: { modelValue: '', submitMode: 'both' }, emits: { 'onUpdate:modelValue': (data: string) => rerender({ modelValue: data, submitMode: 'blur' }) } })

await userEvent.dblClick(preview)
await userEvent.type(input, 'New Value')
await userEvent.tab()
await userEvent.click(document.children[0])

expect(preview).toBeVisible()
expect(preview).toHaveTextContent('New Value')
})
Expand Down
4 changes: 2 additions & 2 deletions packages/radix-vue/src/Editable/EditableInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function handleSubmitKeyDown(event: KeyboardEvent) {
<Primitive
ref="primitiveElement"
v-bind="props"
:value="context.modelValue.value"
:value="context.inputValue.value"
:placeholder="placeholder"
:disabled="disabled"
:maxlength="context.maxLength.value"
Expand All @@ -63,7 +63,7 @@ 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"
@input="context.inputValue.value = $event.target.value"
@keydown.enter.space="handleSubmitKeyDown"
@keydown.esc="context.cancel"
>
Expand Down
15 changes: 11 additions & 4 deletions packages/radix-vue/src/Editable/EditableRoot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type EditableRootContext = {
maxLength: Ref<number | undefined>
disabled: Ref<boolean>
modelValue: Ref<string | undefined>
inputValue: Ref<string | undefined>
placeholder: Ref<{ edit: string, preview: string }>
isEditing: Ref<boolean>
submitMode: Ref<SubmitMode>
Expand Down Expand Up @@ -75,7 +76,7 @@ export const [injectEditableRootContext, provideEditableRootContext]
</script>

<script setup lang="ts">
import { type Ref, computed, ref, toRefs } from 'vue'
import { type Ref, computed, ref, toRefs, watch } from 'vue'
import { Primitive, usePrimitiveElement } from '@/Primitive'
import { useVModel } from '@vueuse/core'

Expand Down Expand Up @@ -146,21 +147,26 @@ const placeholder = computed(() => {
return typeof propPlaceholder.value === 'string' ? { edit: propPlaceholder.value, preview: propPlaceholder.value } : propPlaceholder.value
})

const previousValue = ref(modelValue.value)
const inputValue = ref(modelValue.value)

watch(() => modelValue.value, () => {
inputValue.value = modelValue.value
}, { immediate: true, deep: true })

function cancel() {
modelValue.value = previousValue.value
isEditing.value = false
emits('update:state', 'cancel')
}

function edit() {
isEditing.value = true
inputValue.value = modelValue.value

emits('update:state', 'edit')
}

function submit() {
previousValue.value = modelValue.value
modelValue.value = inputValue.value
isEditing.value = false

emits('update:state', 'submit')
Expand Down Expand Up @@ -196,6 +202,7 @@ provideEditableRootContext({
isEditing,
maxLength,
modelValue,
inputValue,
placeholder,
edit,
cancel,
Expand Down
Loading