Skip to content

Commit

Permalink
feat: number input use zag
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Mar 14, 2024
1 parent bd15484 commit d7a72a9
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 92 deletions.
6 changes: 5 additions & 1 deletion apps/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,9 @@
"xlsx": "^0.18.5",
"zod": "^3.22.4"
},
"type": "module"
"type": "module",
"dependencies": {
"@zag-js/number-input": "^0.38.1",
"@zag-js/types": "^0.38.1"
}
}
12 changes: 8 additions & 4 deletions apps/frontend/src/lib/cell/CellEditors/base-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { toast } from 'svelte-sonner'

export type SaveCallback = (value: any, preventFocus: boolean) => void

export abstract class BaseEditor<T extends BaseField> implements Edition.EditorBase {
element?: HTMLInputElement | null | undefined
export abstract class BaseEditor<E extends Element, T extends BaseField> implements Edition.EditorBase {
element?: E | null | undefined
editCell?: Edition.EditCell | undefined

constructor(
Expand All @@ -19,15 +19,19 @@ export abstract class BaseEditor<T extends BaseField> implements Edition.EditorB
return this.column.field as T
}

// TODO: describe type
onChange<V = unknown>(value: V) {
// TODO: better check updates
if (value === this.editCell?.model[this.editCell.prop]) {
return
}

if (this.field.required && isEmpty(value)) {
this.element?.blur()
return
}

const result = this.field.valueSchema.safeParse(value)
if (result.success) {
this.element?.blur()
this.saveCallback(result.data, false)
} else {
toast.warning(result.error.flatten((i) => i.message).formErrors.join('\n'))
Expand Down
6 changes: 2 additions & 4 deletions apps/frontend/src/lib/cell/CellEditors/date-editor.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import type { Edition, RevoGrid } from '@revolist/revogrid/dist/types/interfaces'
import type { RevoGrid } from '@revolist/revogrid/dist/types/interfaces'
import type { VNode } from '@revolist/revogrid/dist/types/stencil-public-runtime'
import type { DateField } from '@undb/core'
import delay from 'delay'
import htm from 'htm'
import { BaseEditor } from './base-editor'

export type SaveCallback = (value: Edition.SaveData, preventFocus: boolean) => void

export class DateEditor extends BaseEditor<DateField> {
export class DateEditor extends BaseEditor<HTMLInputElement, DateField> {
private initElement() {
const element = this.element
if (!element) return
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/lib/cell/CellEditors/email-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import delay from 'delay'
import htm from 'htm'
import { BaseEditor } from './base-editor'

export class EmailEditor extends BaseEditor<EmailField> {
export class EmailEditor extends BaseEditor<HTMLInputElement, EmailField> {
public element: HTMLInputElement | null = null
public editCell: Edition.EditCell | undefined = undefined

Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/src/lib/cell/CellEditors/normalizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createNormalizer } from '@zag-js/types'

export const normalizer = createNormalizer((v) => v)
45 changes: 33 additions & 12 deletions apps/frontend/src/lib/cell/CellEditors/number-editor.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,40 @@
import type { Edition, RevoGrid } from '@revolist/revogrid/dist/types/interfaces'
import type { VNode } from '@revolist/revogrid/dist/types/stencil-public-runtime'
import type { CurrencyField, NumberField } from '@undb/core'
import * as numberInput from '@zag-js/number-input'
import delay from 'delay'
import htm from 'htm'
import { BaseEditor } from './base-editor'
import { BaseEditor, type SaveCallback } from './base-editor'
import { normalizer } from './normalizer'

export class NumberEditor extends BaseEditor<NumberField | CurrencyField> {
export class NumberEditor extends BaseEditor<HTMLDivElement, NumberField | CurrencyField> {
public element: HTMLInputElement | null = null
public editCell: Edition.EditCell | undefined = undefined
private api: numberInput.Api | null = null

private initElement() {
const element = this.element
if (!element) return
constructor(
public column: RevoGrid.ColumnRegular,
protected saveCallback: SaveCallback,
) {
super(column, saveCallback)

const service = numberInput.machine({
id: this.column.prop as string,
})
const machine = service.start()

const api = numberInput.connect(machine.state, machine.send, normalizer)

element.focus()
this.api = api
}

private initElement() {
const editCell = this.editCell
if (!editCell) return

element.value = editCell.model[editCell.prop] as string
const value = editCell.model[editCell.prop] as string
this.api?.setValue(Number(value))
this.api?.focus()
}

async componentDidRender() {
Expand All @@ -28,12 +44,17 @@ export class NumberEditor extends BaseEditor<NumberField | CurrencyField> {

render(createComponent: RevoGrid.HyperFunc<VNode>) {
const html = htm.bind(createComponent)
const api = this.api

return html`
<input
type="number"
onblur=${(e: Event) => this.onChange(Number((e.target as HTMLInputElement).value))}
class="border-2 border-primary-300 rounded-none text-gray-900 text-sm focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5"
/>
<div class="flex items-center" ...${api?.rootProps}>
<input
type="number"
...${api?.inputProps}
onchange=${(e: Event) => this.onChange(Number((e.target as HTMLInputElement).value))}
class="border-2 border-primary-300 rounded-none text-gray-900 text-sm focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5"
/>
</div>
`
}
}
2 changes: 1 addition & 1 deletion apps/frontend/src/lib/cell/CellEditors/string-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import delay from 'delay'
import htm from 'htm'
import { BaseEditor } from './base-editor'

export class StringEditor extends BaseEditor<StringField> {
export class StringEditor extends BaseEditor<HTMLInputElement, StringField> {
private initElement() {
const element = this.element
if (!element) return
Expand Down
Loading

0 comments on commit d7a72a9

Please sign in to comment.