Skip to content

Commit

Permalink
some progress
Browse files Browse the repository at this point in the history
  • Loading branch information
asmyshlyaev177 committed Jun 27, 2024
1 parent 7fd2d7e commit 756c69e
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 35 deletions.
42 changes: 17 additions & 25 deletions packages/example-nextjs/src/app/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,32 @@
'use client';
import React from 'react';
import { useSearchParams, useRouter, usePathname } from 'next/navigation';
import { useRouter, usePathname } from 'next/navigation';
import { Button } from './Button';
import { Field } from './Field';
import { encode, decode } from 'encoder';

const form = {
name: '',
age: 0,
'agree to terms': false,
};
import { useUrlState } from './useUrlState';
import { form } from './form';

// TODO: need some api to make it user friendly
// UrlState class that extends URLSearchParams?
// toObj and fromObj ? to.array() to.object()?
function getObj<T> (params: URLSearchParams) {
return Object.fromEntries(
[...params.entries()].map(([key, value]) => [
key,
decode(value)
]),
) as T;

function usePrevious<T>(val: T) {
const value = React.useRef<T>()

React.useEffect(() => {
value.current = val
}, [val])

return value.current
}


export const Form = ({ className }: { className?: string }) => {
const router = useRouter();
const pathname = usePathname();
const searchParams= useSearchParams();

const parsedParams = getObj<typeof form>(searchParams);
const [state, setState] = React.useState(parsedParams);
const { parse, stringify } = useUrlState(form)

const [state, setState] = React.useState(() => parse());


const handleChange = React.useCallback(
Expand All @@ -45,11 +41,7 @@ export const Form = ({ className }: { className?: string }) => {
);

const handleSave = () => {
const params = new URLSearchParams(searchParams.toString());
Object.entries(state).forEach(([key, value]) => {
params.set(String(key), encode(value));
});
router.push(`${pathname}?${params.toString()}`);
router.push(`${pathname}?${stringify(state)}`);
};

// // set URI when state change
Expand Down Expand Up @@ -107,7 +99,7 @@ export const Form = ({ className }: { className?: string }) => {
};


interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { }
const Input = ({ className, ...props }: InputProps) => {
return <input className={`text-black ${className} ${props.type === 'checkbox' ? 'hover:cursor-pointer w-8 h-8' : 'w-full'}`} {...props} />;
};
14 changes: 4 additions & 10 deletions packages/example-nextjs/src/app/Status.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
'use client';
import React from 'react';
import { useSearchParams } from 'next/navigation';
import { Field } from './Field';
import { decode } from 'encoder';
import { useUrlState } from './useUrlState';
import { form } from './form'

export const Status = ({ className }: { className?: string }) => {
const searchParams = useSearchParams();
const state = getObj(searchParams);
const { parse } = useUrlState(form)

return (
<Field className={className}>
<h2 className='text-semi-bold text-lg'>Types of data are presered</h2>
<pre className="h-full p-2 rounded-sm bg-slate-100
dark:text-black text-wrap break-words whitespace-pre-wrap self-stretch">
{JSON.stringify(state, null, 2)}
{JSON.stringify(parse(), null, 2)}
</pre>
</Field>
);
};

const getObj = (params: URLSearchParams) =>
Object.fromEntries(
[...params.entries()].map(([key, value]) => [key, decode(value)]),
);
5 changes: 5 additions & 0 deletions packages/example-nextjs/src/app/form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const form: {name: string, age: number | string, 'agree to terms': boolean} = {
name: '',
age: "",
'agree to terms': false,
};
41 changes: 41 additions & 0 deletions packages/example-nextjs/src/app/useUrlState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useSearchParams } from 'next/navigation';
import React from 'react'
import { encode, decode } from 'encoder';


// TODO: tests
export function useUrlState<T> (obj?: T) {
const searchParams = useSearchParams();

const stringify = React.useCallback(function(state: T): string {
const params = new URLSearchParams();
if (Array.isArray(state)) {
state.forEach(([key, value]) => params.set(String(key), encode(value)) )
} else if (!!state && typeof state === 'object' ) {
Object.entries(state).forEach(([key, value]) => {
// TODO: fix ts
const initialVal = (obj as typeof state)?.[key as keyof state]
if (value !== initialVal ) {
params.set(String(key), encode(value));
} else {
params.delete(key)
}
})
} else {
params.set('value', encode(state))
}

return params.toString()
}, [])

const parse = React.useCallback(function() {
// TODO: array, obj, primitive
return {...obj, ...(Object.fromEntries(
[...searchParams.entries()].map(([key, value]) => [
key,
decode(value) || obj?.[key]
])) as T)}
}, [searchParams])

return { parse, stringify }
}

0 comments on commit 756c69e

Please sign in to comment.