Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | 1x 1x 30881x 30881x 31218x 30881x 30881x 30881x 30881x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 30881x 30881x 30881x 30881x 31217x 31217x 31217x 31217x 31217x 31217x 31217x 30881x 30881x 30881x 336x 336x 336x 30881x 30881x 30881x 30881x 1x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 30881x 336x 336x 336x 336x 336x 336x 336x 30881x 30881x 30881x 30881x 30881x 1x 2156x 2156x 2156x 2156x 2156x 1x 1x | import { isTestEnv } from '@/shared/config'
import { Nullable } from '@tmk/ui-kit'
import { SetStateAction, WritableAtom } from 'jotai'
import { atomWithReset, atomWithStorage, RESET } from 'jotai/utils'
import Router from 'next/router'
export type StorageType = 'hash' | 'query' | 'none'
export interface FilterAtomParams {
name: string
initialValue: unknown
}
export interface AtomWithHashOptions<Value> {
storageType?: StorageType
serialize?: (val: Value) => string
deserialize?: (str: string | null) => Value
delayInit?: boolean
replaceState?: boolean
subscribe?: (callback: () => void) => () => void
}
export const atomWithUrlLocation = <Value>(key: string, initialValue: Value, options: AtomWithHashOptions<Value>) => {
const isStorageTypeHash = options?.storageType === 'hash'
const getURLParams = () =>
isStorageTypeHash ? new URLSearchParams(location.hash.slice(1)) : new URLSearchParams(location.search.slice(1))
const urlMark = isStorageTypeHash ? '#' : '?'
const serialize = options?.serialize || JSON.stringify
const deserialize = options?.deserialize || JSON.parse
function updateQuery(key: string): void
function updateQuery(key: string, newValue?: Value): void {
const searchParams = getURLParams()
newValue ? searchParams.set(key, serialize(newValue)) : searchParams.delete(key)
const currentState = history.state
const searchParamsString = searchParams.toString()
const as = isStorageTypeHash
? currentState.as
: history.state?.as?.replace(/(#|\?)(.+)?/gm, '') + (searchParamsString ? urlMark + searchParamsString : '')
const historyState = { ...currentState, as, key: key + Date.now() }
if (options?.replaceState || history.state?.as === historyState.as) {
history.replaceState(historyState, '', as)
} else {
history.pushState(historyState, '', as)
}
}
const subscribe =
options?.subscribe ||
(callback => {
window.addEventListener('popstate', callback)
return () => {
window.removeEventListener('popstate', callback)
}
})
const queryStorage = {
getItem: (key: string) => {
if (typeof window === 'undefined') {
return initialValue
}
const searchParams = getURLParams()
const storedValue = searchParams.get(key)
if (storedValue === null) {
return initialValue
}
return deserialize(storedValue)
},
setItem: updateQuery,
removeItem: updateQuery,
subscribe: (key: string, setValue: (value: Value) => void) => {
const callback = () => {
const searchParams = getURLParams()
const parameter = searchParams.get(key)
if (parameter !== null) {
setValue(deserialize(parameter))
} else {
setValue(initialValue)
}
}
return subscribe(callback)
},
...(options?.delayInit && { delayInit: true }),
}
return atomWithStorage(key, initialValue, queryStorage)
}
export const atomWithStorageFactory = <Value>(
key: string,
initialValue: Value,
options?: AtomWithHashOptions<Value>
): WritableAtom<Value, SetStateAction<Value> | typeof RESET> => {
const eventName = options?.storageType === 'hash' ? 'hashchange' : 'popstate'
if (options?.storageType === 'none') {
return atomWithReset(initialValue)
}
return atomWithUrlLocation(
key,
initialValue,
Object.assign(
{
delayInit: false,
subscribe: callback => {
isTestEnv || Router.events.on('routeChangeComplete', callback)
window.addEventListener(eventName, callback)
return () => {
isTestEnv || Router.events.off('routeChangeComplete', callback)
window.removeEventListener(eventName, callback)
}
},
} as AtomWithHashOptions<Value>,
options
)
)
}
export const filterAtomsFactory = <FiltersContent extends Record<string, FiltersContent[keyof FiltersContent]>>(
filters: FiltersContent,
options?: AtomWithHashOptions<FiltersContent[keyof FiltersContent]>
) =>
Object.fromEntries(
Object.entries(filters).map(([key, value]) => [key, atomWithStorageFactory(key, value, options)])
) as {
[key in keyof FiltersContent]: WritableAtom<FiltersContent[key], SetStateAction<FiltersContent[key]> | typeof RESET>
}
export const getNextOrderSort = (prevValue: Nullable<string>, defaultSort: unknown): Nullable<string> => {
if (prevValue === null) return defaultSort as Nullable<string>
if (prevValue?.includes('desc')) return prevValue.replace('desc', 'asc') as Nullable<string>
if (prevValue?.includes('asc')) return null as Nullable<string>
return null
}
export const getTwoStatesSort = (
sort: Nullable<string>,
defaultSort: string,
nextSort: string,
key = 'order[date]'
) => {
const currentSort = JSON.parse(sort as string)[key]
if (currentSort === 'asc') return defaultSort
if (currentSort === 'desc') return nextSort
return null
}
|