Skip to content

Commit

Permalink
feat: go to previous style form by pressing shift+tab (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
ktsn committed Nov 29, 2018
1 parent 27bf37d commit 8e6dca4
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 8 deletions.
10 changes: 8 additions & 2 deletions src/view/components/StyleDeclaration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ export default Vue.extend({
this.$emit('update:value', value)
},
finishInputProp(rawProp: string, meta: { reason: string }): void {
finishInputProp(
rawProp: string,
meta: { reason: string; shiftKey: boolean }
): void {
this.$emit('input-end:prop', meta)
const prop = rawProp.trim()
Expand All @@ -73,7 +76,10 @@ export default Vue.extend({
}
},
finishInputValue(rawValue: string, meta: { reason: string }): void {
finishInputValue(
rawValue: string,
meta: { reason: string; shiftKey: boolean }
): void {
this.$emit('input-end:value', meta)
const value = rawValue.trim()
Expand Down
33 changes: 31 additions & 2 deletions src/view/components/StyleInformation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,14 @@ export default Vue.extend({
rule: number,
decl: number,
type: string,
meta: { reason: string }
meta: { reason: string; shiftKey: boolean }
): void {
// If the user end the input by pressing enter or tab key,
// focus on the next form.
if (meta.reason === 'enter' || meta.reason === 'tab') {
// If it is shift+tab, focus on the prev form.
if (meta.reason === 'tab' && meta.shiftKey) {
this.focusOnPrevForm(rule, decl, type)
} else if (meta.reason === 'enter' || meta.reason === 'tab') {
this.focusOnNextForm(rule, decl, type)
}
Expand Down Expand Up @@ -200,6 +203,32 @@ export default Vue.extend({
}
},
focusOnPrevForm(rule: number, decl: number, type: string): void {
if (type === 'value') {
this.autoFocusTarget = {
rule,
declaration: decl,
type: 'prop'
}
return
}
if (type === 'prop') {
const nextDecl = decl - 1
if (nextDecl >= 0) {
this.autoFocusTarget = {
rule,
declaration: nextDecl,
type: 'value'
}
return
}
// Focus across another rule's declaration is not supported yet
this.autoFocusTarget = undefined
}
},
shouldFocusFor(rule: number, decl: number): 'prop' | 'value' | null {
const f = this.autoFocusTarget
return f && f.rule === rule && f.declaration === decl ? f.type : null
Expand Down
7 changes: 6 additions & 1 deletion src/view/components/StyleValue.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,12 @@ export default Vue.extend({
this.editing = false
const el = event.currentTarget as HTMLDivElement
this.$emit('input-end', el.textContent, { reason })
const anyEvent: any = event
this.$emit('input-end', el.textContent, {
reason,
shiftKey: !!anyEvent.shiftKey
})
}
},
Expand Down
30 changes: 30 additions & 0 deletions tests/view/StyleInformation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ describe('StyleInformation', () => {
expect(toDeclarationHtml(wrapper)).toMatchSnapshot()
})

it('moves from value to prop', () => {
const wrapper = create()
wrapper
.findAll(StyleDeclaration)
.at(0)
.vm.$emit('input-end:value', { reason: 'tab', shiftKey: true })

expect(toDeclarationHtml(wrapper)).toMatchSnapshot()
})

it('moves from value to next prop value', () => {
const wrapper = create()
wrapper
Expand All @@ -86,6 +96,26 @@ describe('StyleInformation', () => {
expect(toDeclarationHtml(wrapper)).toMatchSnapshot()
})

it('moves from prop to prev declaration value', () => {
const wrapper = create()
wrapper
.findAll(StyleDeclaration)
.at(1)
.vm.$emit('input-end:prop', { reason: 'tab', shiftKey: true })

expect(toDeclarationHtml(wrapper)).toMatchSnapshot()
})

it('removes focus if it cannot go back', () => {
const wrapper = create()
wrapper
.findAll(StyleDeclaration)
.at(0)
.vm.$emit('input-end:prop', { reason: 'tab', shiftKey: true })

expect(toDeclarationHtml(wrapper)).toMatchSnapshot()
})

it('adds a new declaration and moves focus to it', () => {
const wrapper = create()
wrapper
Expand Down
31 changes: 28 additions & 3 deletions tests/view/StyleValue/basic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('StyleValue basic', () => {
expect(wrapper.attributes()!.contenteditable).not.toBe('true')
expect(wrapper.emitted('input-end')[0]).toEqual([
'20px',
{ reason: 'blur' }
{ reason: 'blur', shiftKey: false }
])
})

Expand All @@ -95,7 +95,7 @@ describe('StyleValue basic', () => {
expect(wrapper.attributes()!.contenteditable).not.toBe('true')
expect(wrapper.emitted('input-end')[0]).toEqual([
'20px',
{ reason: 'enter' }
{ reason: 'enter', shiftKey: false }
])
})

Expand All @@ -114,7 +114,32 @@ describe('StyleValue basic', () => {
key: 'Tab'
})
expect(wrapper.attributes()!.contenteditable).not.toBe('true')
expect(wrapper.emitted('input-end')[0]).toEqual(['20px', { reason: 'tab' }])
expect(wrapper.emitted('input-end')[0]).toEqual([
'20px',
{ reason: 'tab', shiftKey: false }
])
})

it('should include shift key state when end editing', async () => {
const wrapper = mount(StyleValue, {
propsData: {
value: '20px'
}
})
wrapper.trigger('click')
expect(wrapper.attributes()!.contenteditable).toBe('true')

await wrapper.vm.$nextTick()

wrapper.trigger('keydown', {
key: 'Tab',
shiftKey: true
})
expect(wrapper.attributes()!.contenteditable).not.toBe('true')
expect(wrapper.emitted('input-end')[0]).toEqual([
'20px',
{ reason: 'tab', shiftKey: true }
])
})

it('should update editing content when prop is updated', async () => {
Expand Down
15 changes: 15 additions & 0 deletions tests/view/__snapshots__/StyleInformation.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ exports[`StyleInformation moving focus does not move focus if editing is ended b
<div styledeclarationstub="true" prop="font-size" value="22px"></div>
`;

exports[`StyleInformation moving focus moves from prop to prev declaration value 1`] = `
<div styledeclarationstub="true" prop="color" value="red" autofocus="value"></div>
<div styledeclarationstub="true" prop="font-size" value="22px"></div>
`;

exports[`StyleInformation moving focus moves from prop to value 1`] = `
<div styledeclarationstub="true" prop="color" value="red" autofocus="value"></div>
<div styledeclarationstub="true" prop="font-size" value="22px"></div>
Expand All @@ -38,3 +43,13 @@ exports[`StyleInformation moving focus moves from value to next prop value 1`] =
<div styledeclarationstub="true" prop="color" value="red"></div>
<div styledeclarationstub="true" prop="font-size" value="22px" autofocus="prop"></div>
`;

exports[`StyleInformation moving focus moves from value to prop 1`] = `
<div styledeclarationstub="true" prop="color" value="red" autofocus="prop"></div>
<div styledeclarationstub="true" prop="font-size" value="22px"></div>
`;

exports[`StyleInformation moving focus removes focus if it cannot go back 1`] = `
<div styledeclarationstub="true" prop="color" value="red"></div>
<div styledeclarationstub="true" prop="font-size" value="22px"></div>
`;

0 comments on commit 8e6dca4

Please sign in to comment.