-
-
Notifications
You must be signed in to change notification settings - Fork 113
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(primitives): add withAssign, deprecate withReducers
* feat(primitives): add withAssign, deprecate withReducers * refactor(primitives): withReducers -> withAssign * chore(primitives): cleanup * refactor(primitives): type -> interface * refactor(primitives): ditch withReducers * refactor(primitives): codestyle --------- Co-authored-by: krulod <chaurka.noyan@gmail.com>
- Loading branch information
Showing
15 changed files
with
365 additions
and
244 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
import './reatomArray.test' | ||
import './reatomRecord.test' | ||
import './reatomEnum.test' | ||
import './reatomString.test' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,46 @@ | ||
import { atom, AtomMut } from '@reatom/core' | ||
import { withReducers, WithReducers } from './withReducers' | ||
import { Action, AtomMut, action, atom } from '@reatom/core' | ||
import { withAssign } from './withAssign' | ||
|
||
export type ArrayAtom<T> = WithReducers< | ||
AtomMut<Array<T>>, | ||
{ | ||
toReversed(state: Array<T>): Array<T> | ||
toSorted(state: Array<T>, compareFn?: (a: T, b: T) => number): Array<T> | ||
toSpliced( | ||
state: Array<T>, | ||
start: number, | ||
deleteCount: number, | ||
...items: T[] | ||
): Array<T> | ||
with(state: Array<T>, index: number, value: T): Array<T> | ||
} | ||
> | ||
export interface ArrayAtom<T> extends AtomMut<Array<T>> { | ||
toReversed: Action<[], T[]> | ||
toSorted: Action<[compareFn?: (a: T, b: T) => number], T[]> | ||
toSpliced: Action<[start: number, deleteCount: number, ...items: T[]], T[]> | ||
with: Action<[i: number, value: T], T[]> | ||
} | ||
|
||
export const reatomArray = <T>( | ||
initState = new Array<T>(), | ||
initState = [] as T[], | ||
name?: string, | ||
): ArrayAtom<T> => { | ||
return atom(initState, name).pipe( | ||
withReducers({ | ||
toReversed: (state) => state.slice(0).reverse(), | ||
toSorted: (state, compareFn) => state.slice(0).sort(compareFn), | ||
toSpliced: (state, start, deleteCount, ...items) => { | ||
state = state.slice(0) | ||
state.splice(start, deleteCount, ...items) | ||
|
||
return state | ||
}, | ||
with: (state, index, value) => { | ||
if (Object.is(state.at(index), value)) return state | ||
|
||
state = state.slice(0) | ||
state[index] = value | ||
|
||
return state | ||
}, | ||
}), | ||
): ArrayAtom<T> => | ||
atom(initState, name).pipe( | ||
withAssign((target, name) => ({ | ||
toReversed: action( | ||
(ctx) => target(ctx, (prev) => prev.slice().reverse()), | ||
`${name}.toReversed`, | ||
), | ||
toSorted: action( | ||
(ctx, compareFn?: (a: T, b: T) => number) => | ||
target(ctx, (prev) => prev.slice().sort(compareFn)), | ||
`${name}.toSorted`, | ||
), | ||
toSpliced: action( | ||
(ctx, start: number, deleteCount: number, ...items: T[]) => | ||
target(ctx, (state) => { | ||
state = state.slice() | ||
state.splice(start, deleteCount, ...items) | ||
return state | ||
}), | ||
`${name}.toSpliced`, | ||
), | ||
with: action( | ||
(ctx, i: number, value: T) => | ||
target(ctx, (state) => { | ||
if (Object.is(state.at(i), value)) return state | ||
state = state.slice() | ||
state[i] = value | ||
return state | ||
}), | ||
`${name}.with`, | ||
), | ||
})), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,22 @@ | ||
import { atom, AtomMut } from '@reatom/core' | ||
import { withReducers, WithReducers } from './withReducers' | ||
import { Action, AtomMut, action, atom } from '@reatom/core' | ||
import { withAssign } from './withAssign' | ||
|
||
export type BooleanReducers = { | ||
toggle: () => boolean | ||
setTrue: () => boolean | ||
setFalse: () => boolean | ||
reset: () => boolean | ||
export interface BooleanAtom extends AtomMut<boolean> { | ||
toggle: Action<[], boolean> | ||
setTrue: Action<[], true> | ||
setFalse: Action<[], false> | ||
reset: Action<[], boolean> | ||
} | ||
|
||
export type BooleanAtom = WithReducers<AtomMut<boolean>, BooleanReducers> | ||
|
||
export const reatomBoolean = (initState = false, name?: string): BooleanAtom => | ||
atom(initState, name).pipe( | ||
withReducers({ | ||
toggle: (state) => !state, | ||
setTrue: () => true, | ||
setFalse: () => false, | ||
reset: () => initState, | ||
}), | ||
export const reatomBoolean = (init = false, name?: string): BooleanAtom => | ||
atom(init, name).pipe( | ||
withAssign((target, name) => ({ | ||
toggle: action((ctx) => target(ctx, (prev) => !prev), `${name}.toggle`), | ||
setTrue: action((ctx) => target(ctx, true) as true, `${name}.setTrue`), | ||
setFalse: action( | ||
(ctx) => target(ctx, false) as false, | ||
`${name}.setFalse`, | ||
), | ||
reset: action((ctx) => target(ctx, init), `${name}.reset`), | ||
})), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,71 @@ | ||
import { atom, AtomMut, Rec } from '@reatom/core' | ||
import { withReducers, WithReducers } from './withReducers' | ||
import { action, Action, atom, AtomMut, Ctx, throwReatomError } from '@reatom/core' | ||
|
||
export type EnumFormat = 'camelCase' | 'snake_case' | ||
|
||
export type EnumAtom< | ||
T extends string, | ||
Format extends 'camelCase' | 'snake_case' = 'camelCase', | ||
> = WithReducers< | ||
AtomMut<T>, | ||
{ | ||
[K in T as Format extends 'camelCase' | ||
? `set${Capitalize<K>}` | ||
: Format extends 'snake_case' | ||
? `set_${K}` | ||
: never]: () => K | ||
} & { | ||
reset: () => T | ||
} | ||
> & { | ||
Format extends EnumFormat = 'camelCase', | ||
> = AtomMut<T> & { | ||
[Variant in T as Format extends 'camelCase' | ||
? `set${Capitalize<Variant>}` | ||
: Format extends 'snake_case' | ||
? `set_${Variant}` | ||
: never]: Action<[], Variant> | ||
} & { | ||
reset: Action<[], T> | ||
enum: { [K in T]: K } | ||
} | ||
|
||
export type EnumAtomOptions< | ||
T extends string, | ||
Format extends 'camelCase' | 'snake_case' = 'camelCase', | ||
Format extends EnumFormat = 'camelCase', | ||
> = { | ||
name?: string | ||
format?: Format | ||
initState?: T extends any ? T : never | ||
initState?: T | ||
} | ||
|
||
export const reatomEnum = < | ||
T extends string, | ||
const T extends string, | ||
Format extends 'camelCase' | 'snake_case' = 'camelCase', | ||
>( | ||
variants: ReadonlyArray<T>, | ||
options: string | EnumAtomOptions<T, Format> = {}, | ||
): EnumAtom<T, Format> => { | ||
) => { | ||
const { | ||
name, | ||
format = 'camelCase' as Format, | ||
initState = variants[0], | ||
}: EnumAtomOptions<T, Format> = typeof options === 'string' | ||
? { name: options } | ||
: options | ||
const cases = {} as Rec | ||
const reducers = {} as Rec | ||
? { name: options } | ||
: options | ||
|
||
const stateAtom = atom(initState, name) as EnumAtom<T, Format> | ||
const enumAtom: typeof stateAtom = Object.assign((ctx: Ctx, update: any) => { | ||
const state = stateAtom(ctx, update) | ||
throwReatomError(!variants.includes(state), `invalid enum value "${state}" for "${name}" enum`) | ||
return state | ||
}, stateAtom) | ||
const cases = (enumAtom.enum = {} as { [K in T]: K }) | ||
|
||
enumAtom.reset = action((ctx) => enumAtom(ctx, initState!), `${name}.reset`) | ||
|
||
for (const variant of variants) { | ||
cases[variant] = variant | ||
|
||
const reducerName = variant.replace( | ||
const setterName = variant.replace( | ||
/^./, | ||
(firstLetter) => | ||
'set' + | ||
(format === 'camelCase' | ||
? firstLetter.toUpperCase() | ||
: `_${firstLetter}`), | ||
) | ||
reducers[reducerName] = () => variant | ||
} | ||
|
||
reducers.reset = () => initState | ||
; (enumAtom as any)[setterName] = action( | ||
(ctx) => enumAtom(ctx, variant)!, | ||
`${name}.${setterName}`, | ||
) | ||
} | ||
|
||
// @ts-expect-error | ||
return Object.assign(atom(initState, name).pipe(withReducers(reducers)), { | ||
enum: cases, | ||
}) | ||
return enumAtom as EnumAtom<T, Format> | ||
} |
Oops, something went wrong.