From 639d382e43bed18e869216ed925e73f1e8c16f0e Mon Sep 17 00:00:00 2001 From: laggingreflex Date: Mon, 29 Oct 2018 16:46:38 +0530 Subject: [PATCH] feat: use URL instead of path/params [breaking change] This uses the native [URL](-like) object instead of separate path/params. [URL]: https://developer.mozilla.org/en-US/docs/Web/API/URL --- src/index.js | 2 +- src/url.js | 54 +++++++++++++++++++++++++++++++++++------------- src/utils.js | 47 ----------------------------------------- types/index.d.ts | 7 +------ 4 files changed, 42 insertions(+), 68 deletions(-) delete mode 100644 src/utils.js diff --git a/src/index.js b/src/index.js index 6a261e3..115f73e 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,3 @@ -export { params, path, setParams, setPath } from './url' +export { url } from './url' export { storage, setStorage } from './storage' export { default as scheduler } from './scheduler' diff --git a/src/url.js b/src/url.js index 9287c13..2ddb6e7 100644 --- a/src/url.js +++ b/src/url.js @@ -1,25 +1,51 @@ import { observable, observe } from '@nx-js/observer-util' import scheduler from './scheduler' -import { toQuery, toParams, toPathArray, toPathString } from './utils' -export const params = observable(toParams(location.search)) -export const path = observable(toPathArray(location.pathname)) +export const url = observable(createUrl()) -export function setParams (newParams) { - for (let key of Object.keys(params)) { - delete params[key] +const unWritableKeys = ['origin', 'searchParams'] + +observe(syncUrl, { scheduler }) + +function createUrl (href = location.href) { + const url = new URL(href) + const ret = {} + for (const key in url) { + ret[key] = url[key] } - Object.assign(params, newParams) + ret.params = createParams(url.searchParams) + return ret } -export function setPath (newPath) { - path.length = 0 - path.push(...newPath) +function createParams (searchParams) { + /* Convert URLSearchParams object into plain key-value object */ + return Array.from(searchParams.entries()).reduce((params, [key, value]) => ({ + ...params, + [key]: value + }), {}) } -function syncUrl () { - const url = toPathString(path) + toQuery(params) + location.hash - history.replaceState(history.state, '', url) +function updateParams (params, searchParams) { + /* Update the URLSearchParams object from plain key-value object */ + for (const key in params) { + searchParams.set(key, params[key]) + } } -observe(syncUrl, { scheduler }) +function updateUrl (oldUrl, href = oldUrl.href) { + const newUrl = new URL(href) + for (const key in newUrl) { + if (unWritableKeys.includes(key)) continue + newUrl[key] = oldUrl[key] + /* URL does internal adjustments, like automatically add "#" to .hash (even if the user hadn't) */ + /* So the updated properties need to be copied back for consistency and expected behaviour */ + oldUrl[key] = newUrl[key] + } + updateParams(oldUrl.params, newUrl.searchParams) + return createUrl(newUrl.href) +} + +function syncUrl () { + const newUrl = updateUrl(url) + history.replaceState(history.state, '', newUrl.href) +} diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 4248390..0000000 --- a/src/utils.js +++ /dev/null @@ -1,47 +0,0 @@ -export function toPathArray (path) { - return path.split('/').filter(notEmpty) -} - -export function toPathString (path) { - return '/' + path.filter(notEmpty).join('/') -} - -export function toQuery (params) { - const queryTokens = [] - - for (let key in params) { - let value = params[key] - if (value !== undefined) { - key = encodeURIComponent(key) - value = encodeURIComponent(JSON.stringify(value)) - queryTokens.push(`${key}=${value}`) - } - } - return queryTokens.length ? '?' + queryTokens.join('&') : '' -} - -export function toParams (queryString) { - const queryTokens = queryString - .slice(1) - .split('&') - .filter(notEmpty) - - const params = {} - for (let token of queryTokens) { - const keyValue = token.split('=') - if (keyValue.length === 2) { - const key = decodeURIComponent(keyValue[0]) - const value = decodeURIComponent(keyValue[1]) - try { - params[key] = JSON.parse(value) - } catch (err) { - params[key] = value - } - } - } - return params -} - -function notEmpty (token) { - return token !== '' -} diff --git a/types/index.d.ts b/types/index.d.ts index ffe1c5a..c06931c 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -8,10 +8,5 @@ declare module 'react-easy-params' { } const storage: ObjectObject - const params: PrimitiveObject - const path: Array - - function setParams(obj: PrimitiveObject): void - function setStorage(obj: ObjectObject): void - function setPath(array: Array): void + const url: ObjectObject }