Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
"file-saver": "^2.0.5",
"formik": "^2.2.6",
"javascript-stringify": "^2.1.0",
"js-base64": "^3.7.5",
"json-url": "^3.1.0",
"jsoncrush": "1.1.6",
"ka-table": "^7.1.3",
"litepicker": "^2.0.11",
"lodash.get": "^4.4.2",
Expand Down
12 changes: 3 additions & 9 deletions src/components/VirtuosoDataGrid/DataGridToolbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,21 +151,15 @@ const DataGridToolbar = ({

const [dataGridState, dataGridDispatch] = useContext(DataGridContext);

const [filterValues, setFilterValues] = useUrlState({ queryKey: `${gridId}-filters`, disable: !useUrlAsState });
const [filterDisplay, setFilterDisplay] = useState({});

const [searchField, setSearchField] = useState("");

useEffect(() => {
setSearchField(filterValues?.search);
}, [filterValues?.search]);



const [filterValues, setFilterValues] = useUrlState({ queryKey: `${gridId}-filters`, disable: !useUrlAsState });

const changeFilter = useCallback(
debounce((filterKey, filterValue) => {
const filterV = { ...filterValues, [filterKey]: filterValue }
setFilterValues(filterV);
// setSearchField(filterV?.search ?? "")
onFilterChange(filterV)
}, 500),
[filterValues],
Expand Down
153 changes: 125 additions & 28 deletions src/components/hooks/useUrlState.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
import React, { useCallback, useMemo, useRef, useState } from "react";

import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import _JSONUrl from "json-url";
import JSONCrush from "jsoncrush";
import moment from "moment";
import { base64ArrayBuffer } from "../DocumentGallery/b64util";
import { Base64 } from "js-base64";
import isEmpty from "lodash.isempty";

export function useUrlState({ queryKey, defaultValue, disable = false }) {
const ENCODE_TYPES = {
crush: 'crush',
b64: 'b64',
lzma: 'lzma',
}
export function useUrlState({ queryKey, defaultValue, disable = false, encode = ENCODE_TYPES.crush }) {
const JSONUrl = _JSONUrl('lzma');
const [state, setState] = useState(defaultValue);
const ref = useRef(state);
const [url, setUrl] = useState('');
const [qsValue, setQsValue] = useState();

const setQueryString = useCallback((qsValue) => {
const newurl =
Expand All @@ -18,46 +30,131 @@ export function useUrlState({ queryKey, defaultValue, disable = false }) {
}
}, [disable]);

const getQueryStringValue = useMemo(() => {
const qs = new URLSearchParams(window.location.search);
const pValue = qs.get(queryKey) || state || null
let convertedValue = pValue;
if (pValue) {
try {
convertedValue = JSON.parse(pValue);
const calltype = Object.prototype.toString.call(convertedValue)
if (calltype === '[object String]') {
const getQueryStringValue = useCallback(
async () => {
const qs = new URLSearchParams(window?.location?.search);
const pValue = qs.get(queryKey) || state || null
// const uncrush = JSONCrush.uncrush(decodeURIComponent(window?.location?.search))?.substring(1);

let convertedValue = '';
let getParam = ''
let uncrush = '';
if (pValue) {
try {
switch (encode) {
case ENCODE_TYPES.crush:
uncrush = JSONCrush.uncrush(decodeURIComponent(pValue))
break;
case ENCODE_TYPES.b64:
uncrush = Base64.decode(pValue)
break;
case ENCODE_TYPES.lzma:
uncrush = await JSONUrl.decompress(pValue)
break;
default:
break;
}
try {
getParam = uncrush ? JSON.parse(uncrush) : {};
} catch (error) {
convertedValue = uncrush;

try {
const isDate = moment(new Date(convertedValue)).isValid();
convertedValue = isDate ? moment(convertedValue).toDate() : convertedValue;
} catch (dateparseError) {
}

ref.current = convertedValue;
setQsValue(convertedValue);
return
}

try {
const isDate = moment(new Date(convertedValue)).isValid();
convertedValue = isDate ? moment(convertedValue).toDate() : convertedValue;
} catch (dateparseError) {
convertedValue = JSON.parse(getParam);
} catch (error) {
convertedValue = getParam;
try {
const isDate = moment(new Date(convertedValue)).isValid();
convertedValue = isDate ? moment(convertedValue).toDate() : convertedValue;
} catch (dateparseError) {
}
ref.current = convertedValue
setQsValue(convertedValue);
return
}

} catch (error) {
convertedValue = getParam;
}
} catch (error) {
convertedValue = pValue;
ref.current = convertedValue;
}
ref.current = convertedValue;
}
return ref.current;
}, [queryKey, state]);
setQsValue(convertedValue);
}, [JSONUrl, encode, queryKey, state]);

useEffect(
() => {
getQueryStringValue()
}, [window?.location?.search]
);

const dispatchFromUrl = useCallback(
function (val) {
async function (val) {
const JSONUrl = _JSONUrl('lzma');
const returnValue = typeof val === "function" ? val(ref.current) : val;
const parsedValue = typeof returnValue === "object" ? JSON.stringify(returnValue) : returnValue;
let crushValue = '';
switch (encode) {
case ENCODE_TYPES.crush:
crushValue = JSONCrush.crush(parsedValue)
break;
case ENCODE_TYPES.b64:
crushValue = Base64.encode(JSON.stringify(parsedValue))
break;
case ENCODE_TYPES.lzma:
crushValue = await JSONUrl.compress(JSON.stringify(parsedValue))
break;
default:
break;
}

const qs = new URLSearchParams(window.location.search);
setUrl({
LZMA_compression: {
raw: await JSONUrl.compress(JSON.stringify(parsedValue)),
urlencoded: new URLSearchParams(await JSONUrl.compress(JSON.stringify(parsedValue)))?.toString(),
},
JS_Crush: {
raw: JSONCrush.crush(JSON.stringify(parsedValue)),
urlencoded: new URLSearchParams(JSONCrush.crush(JSON.stringify(parsedValue)))?.toString(),
},
B64: {
raw: Base64.encode(JSON.stringify(parsedValue)),
urlencoded: new URLSearchParams(Base64.encode(JSON.stringify(parsedValue)))?.toString(),
},
})
const qs = new URLSearchParams(window?.location?.search);
let values = {};
for (var value of qs.keys()) {
if (value === queryKey) continue; // skip the queryKey parameter
values[value] = qs.get(value);
}
const mergedValues = {
...values,
[queryKey]: parsedValue
[queryKey]: crushValue
}

let qsString = ''
if (isEmpty(values)) {
qsString = `?${queryKey}=${crushValue}`
} else {
const preQs = new URLSearchParams(values);
const preQsValue = preQs.toString()
qsString = `?${preQsValue}&${queryKey}=${crushValue}`
}
const newQs = new URLSearchParams(mergedValues);
const newQsValue = newQs.toString()
setQueryString(`?${newQsValue}`);

// const newQs = new URLSearchParams(mergedValues);
// const newQsValue = newQs.toString()
setQueryString(qsString)
setState(returnValue);
},
[queryKey, setQueryString]
Expand All @@ -72,6 +169,6 @@ export function useUrlState({ queryKey, defaultValue, disable = false }) {
if (disable) {
return [state, dispatch, ref]
} else {
return [getQueryStringValue, dispatchFromUrl, ref];
return [qsValue, dispatchFromUrl, ref, url];
}
}
44 changes: 33 additions & 11 deletions src/components/hooks/useUrlState.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const useURLStateStory = {
export default useURLStateStory;

export const Default = ({ ...args }) => {
const [queryTerm, setQueryTerm, queryTermRef] = useUrlState({queryKey:'queryTerm'});
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'queryTerm'});
return (
<div>
<TextField
Expand All @@ -27,7 +27,7 @@ export const Default = ({ ...args }) => {
};

export const Disable = ({ ...args }) => {
const [queryTerm, setQueryTerm, queryTermRef] = useUrlState({queryKey:'queryTerm', disable: true});
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'queryTerm', disable: true});
return (
<div>
<TextField
Expand All @@ -41,8 +41,8 @@ export const Disable = ({ ...args }) => {
};

export const Multiple = ({ ...args }) => {
const [queryTerm, setQueryTerm, queryTermRef] = useUrlState({queryKey:'queryTerm'});
const [queryTerm2, setQueryTerm2, queryTerm2Ref] = useUrlState({queryKey:'queryTerm2'});
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'queryTerm'});
const [queryTerm2, setQueryTerm2, queryTerm2Ref, url2] = useUrlState({queryKey:'queryTerm2'});
useEffect(() => {
console.log('should only be called once per load', queryTerm, queryTerm2)
}, [queryTerm, queryTerm2]);
Expand All @@ -58,42 +58,64 @@ export const Multiple = ({ ...args }) => {
label="Query Term 2"
value={queryTerm2}
onChange={(e) => setQueryTerm2(e.target.value)} />
<ReactJsonView src={{queryTerm, queryTerm2}}/>
<ReactJsonView src={{queryTerm, queryTerm2, url}}/>
</div>
);
};

export const JSON = ({ ...args }) => {
const [queryTerm, setQueryTerm, queryTermRef] = useUrlState({queryKey:'jsonTerm'});
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'jsonTerm'});
return (
<div>
<TextField
variant="standard"
label="Query Term"
value={queryTerm?.wrap?.value}
onChange={(e) => setQueryTerm({ wrap: { value: e.target.value } })} />
<ReactJsonView src={{ queryTerm }} />
<ReactJsonView src={{ queryTerm, url }} />
</div>
);
}

export const DateRange = ({ ...args }) => {
const [queryTerm, setQueryTerm, queryTermRef] = useUrlState({queryKey:'dateRangeTerm'});
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'dateRangeTerm'});
return (
<div>
<DateTimeRangePicker value={queryTerm} onChange={v => setQueryTerm(v)} />
<ReactJsonView src={{ queryTerm }} />
<ReactJsonView src={{ queryTerm, url }} />
</div>
)
}
export const Date = ({ ...args }) => {
const [queryTerm, setQueryTerm, queryTermRef] = useUrlState({queryKey:'dateTerm'});
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'dateTerm'});
return (
<div>
<input type={"date"} value={queryTerm} onChange={v => {
setQueryTerm(v.target.value)
}} />
<ReactJsonView src={{ queryTerm }} />
<ReactJsonView src={{ queryTerm, url }} />
</div>
)
}

export const MedicalGridFilterExample = ({...args}) => {
const [queryTerm, setQueryTerm, queryTermRef, url] = useUrlState({queryKey:'medicalGridFilter'});
useEffect(
() => {
setQueryTerm({
laboratory: [
9,
1,
],
startDate: "2023-08-01T08:51:52.000Z",
endDate: "2023-09-30T08:51:52.000Z",
})
}, []
);
return (
<div>
<ReactJsonView src={{ parameters: queryTerm }} name="Internal Query Parameters"/>
<ReactJsonView src={{ rendered_url_params: url }} name="External Parameters in URL"/>
</div>
);
}
Loading