Skip to content

Commit

Permalink
fix(masks): correctly handle months (feb) (epicmaxco#4347)
Browse files Browse the repository at this point in the history
  • Loading branch information
m0ksem authored Jul 29, 2024
1 parent ddb052c commit 91127bc
Show file tree
Hide file tree
Showing 9 changed files with 399 additions and 76 deletions.
157 changes: 157 additions & 0 deletions packages/ui/src/composables/useInputMask/cursor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { type MaskToken } from './mask'
import { describe, it, expect } from 'vitest'

import { Cursor, CursorPosition } from './cursor'

const makeTokens = (str: string) => str.split('').map((char) => ({ static: char === 's' })) as MaskToken[]

const printCursor = (cursor: Cursor<MaskToken>) => {
const str = (cursor as any).tokens.map((t: MaskToken) => t.static ? 's' : 'd') as string[]
const i = cursor.position
str[i] = '|' + (str[i] ?? '')
return str.join('')
}

describe('useInputMask/Cursor', () => {
describe('Before Char', () => {
describe('move forward', () => {
it('should move over one static token', () => {
const cursor = new Cursor(0, makeTokens('dsd'))

expect(printCursor(cursor)).toBe('|dsd')
cursor.moveForward(1, CursorPosition.BeforeChar)
expect(printCursor(cursor)).toBe('ds|d')
})

it('should move over multiple static token', () => {
const cursor = new Cursor(0, makeTokens('dsssd'))

expect(printCursor(cursor)).toBe('|dsssd')
cursor.moveForward(1, CursorPosition.BeforeChar)
expect(printCursor(cursor)).toBe('dsss|d')
})
})

describe('move back', () => {
it('should move over one static token', () => {
const cursor = new Cursor(3, makeTokens('dsd'))

expect(printCursor(cursor)).toBe('dsd|')
cursor.moveBack(1, CursorPosition.BeforeChar)
expect(printCursor(cursor)).toBe('ds|d')
cursor.moveBack(1, CursorPosition.BeforeChar)
expect(printCursor(cursor)).toBe('|dsd')
})

it('should move over multiple static token', () => {
const cursor = new Cursor(5, makeTokens('dsssd'))

cursor.moveBack(1, CursorPosition.BeforeChar)
expect(printCursor(cursor)).toBe('dsss|d')

cursor.moveBack(1, CursorPosition.BeforeChar)
expect(printCursor(cursor)).toBe('|dsssd')
})
})
})

describe('After Char', () => {
describe('move forward', () => {
it('should move over one static token', () => {
const cursor = new Cursor(0, makeTokens('dsd'))

cursor.moveForward(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('d|sd')

cursor.moveForward(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('dsd|')
})

it('should move over multiple static token', () => {
const cursor = new Cursor(0, makeTokens('dsssd'))

cursor.moveForward(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('d|sssd')

cursor.moveForward(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('dsssd|')
})
})
describe('move back', () => {
it('should move over one static token', () => {
const cursor = new Cursor(3, makeTokens('dsd'))

cursor.moveBack(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('d|sd')

cursor.moveBack(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('|dsd')
})

it('should move over multiple static token', () => {
const cursor = new Cursor(5, makeTokens('dsssd'))

cursor.moveBack(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('d|sssd')

cursor.moveBack(1, CursorPosition.AfterChar)
expect(printCursor(cursor)).toBe('|dsssd')
})
})
})

describe('Any Char p/move forward', () => {
describe('move forward', () => {
it('should move over one static token', () => {
const cursor = new Cursor(0, makeTokens('dsd'))

cursor.moveForward(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('d|sd')

cursor.moveForward(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('ds|d')
})

it('should move over multiple static token', () => {
const cursor = new Cursor(0, makeTokens('dsssd'))

cursor.moveForward(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('d|sssd')

cursor.moveForward(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('dsss|d')

cursor.moveForward(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('dsssd|')
})
})

describe('move back', () => {
it('should move over one static token', () => {
const cursor = new Cursor(3, makeTokens('dsd'))

cursor.moveBack(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('ds|d')

cursor.moveBack(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('d|sd')

cursor.moveBack(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('|dsd')
})

it('should move over multiple static token', () => {
const cursor = new Cursor(5, makeTokens('dsssd'))

cursor.moveBack(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('dsss|d')

cursor.moveBack(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('d|sssd')

cursor.moveBack(1, CursorPosition.Any)
expect(printCursor(cursor)).toBe('|dsssd')
})
})
})
})
51 changes: 33 additions & 18 deletions packages/ui/src/composables/useInputMask/cursor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,55 @@ export class Cursor<Token extends MaskToken> extends Number {

for (let i = this.position; i <= this.tokens.length && i >= -1; i += direction) {
const current = this.tokens[i]
const next = this.tokens[i + direction]
const prev = this.tokens[i - direction]
const next = this.tokens[i + direction] as MaskToken || undefined
const prev = this.tokens[i - direction] as MaskToken || undefined

if (amount < 0) {
if (amount === 0) {
this.position = i
return this.position
}

if (!current?.static) {
amount--
if (next === undefined && current === undefined) {
this.position = i
return this.position
}

if (cursorPosition <= CursorPosition.Any) {
if (direction === -1 && !next?.static && current?.static) {
if (cursorPosition === CursorPosition.AfterChar) {
if (current && !current.static && direction > 0) {
amount--
continue
}
if (direction === 1 && !prev?.static && current?.static) {
if (!next?.static && direction < 0 && i !== this.position) {
amount--
if (amount === 0) {
this.position = i
return this.position
}
continue
}
}

if (cursorPosition >= CursorPosition.Any) {
if (direction === 1 && !prev?.static && current === undefined) {
amount--
} else if (direction === 1 && current === undefined && next?.static) {
amount--
} else if (direction === 1 && current === undefined && next === undefined) {
if (cursorPosition === CursorPosition.BeforeChar) {
if (!next?.static) {
amount--
continue
}
}

if (amount < 0) {
this.position = i
return this.position
if (cursorPosition === CursorPosition.Any) {
if ((!current?.static || !next?.static) && direction > 0) {
amount--
continue
}

if (direction < 0) {
if (next && !next.static) {
amount--
if (i !== this.position) {
this.position = i
return this.position
}
}
}
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/ui/src/composables/useInputMask/mask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ export type Mask<Token extends MaskToken = MaskToken, Data = any> = {
},
handleCursor: (selectionStart: Cursor<Token>, selectionEnd: Cursor<Token>, oldTokens: Token[], newTokens: Token[], text: string, data?: Data) => any,
unformat: (text: string, tokens: Token[]) => string,
reverse: boolean
}
Loading

0 comments on commit 91127bc

Please sign in to comment.