@vue-composable/web
Advanced tools
Comparing version 1.0.0-dev.7 to 1.0.0-dev.8
@@ -23,4 +23,3 @@ 'use strict'; | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = core.isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -45,4 +44,3 @@ handler = core.useDebounce(handler, wait); | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = core.isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -52,3 +50,3 @@ handler = core.useDebounce(handler, wait); | ||
// resize seems only to be fired against the window | ||
const remove = useEvent(window, "resize", handler, eventOptions || { passive: true }); | ||
const remove = core.isClient ? useEvent(window, "resize", handler, eventOptions || { passive: true }) : core.NO_OP; | ||
return { | ||
@@ -69,4 +67,3 @@ height, | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = core.isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -117,3 +114,4 @@ handler = core.useDebounce(handler, wait); | ||
function useWebSocket(url, protocols) { | ||
const ws = new WebSocket(url, protocols); | ||
const supported = core.isClient && 'WebSocket' in window; | ||
let ws = null; | ||
const messageEvent = compositionApi.ref(null); | ||
@@ -127,33 +125,39 @@ const errorEvent = compositionApi.ref(); | ||
let lastMessage = ( Date.now()) || undefined; | ||
ws.addEventListener("message", x => { | ||
messageEvent.value = x; | ||
data.value = x.data; | ||
// if the messages are to quick, we need to warn | ||
/* istanbul ignore else */ | ||
{ | ||
if (Date.now() - lastMessage < 2) { | ||
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' + | ||
" you might not get updated of all the messages." + | ||
' Use "ws..addEventListener("message", handler)" instead'); | ||
let send = core.NO_OP; | ||
let close = core.NO_OP; | ||
if (supported) { | ||
ws = new WebSocket(url, protocols); | ||
ws.addEventListener("message", x => { | ||
messageEvent.value = x; | ||
data.value = x.data; | ||
// if the messages are to quick, we need to warn | ||
/* istanbul ignore else */ | ||
{ | ||
if (Date.now() - lastMessage < 2) { | ||
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' + | ||
" you might not get updated of all the messages." + | ||
' Use "ws..addEventListener("message", handler)" instead'); | ||
} | ||
lastMessage = Date.now(); | ||
} | ||
lastMessage = Date.now(); | ||
} | ||
}); | ||
ws.addEventListener("error", error => { | ||
errorEvent.value = error; | ||
errored.value = true; | ||
}); | ||
ws.addEventListener("close", () => { | ||
isOpen.value = false; | ||
isClosed.value = true; | ||
}); | ||
ws.addEventListener("open", () => { | ||
isOpen.value = true; | ||
isClosed.value = false; | ||
}); | ||
const send = (data) => ws.send(data); | ||
const close = (code, reason) => { | ||
ws.close(code, reason); | ||
}; | ||
}); | ||
ws.addEventListener("error", error => { | ||
errorEvent.value = error; | ||
errored.value = true; | ||
}); | ||
ws.addEventListener("close", () => { | ||
isOpen.value = false; | ||
isClosed.value = true; | ||
}); | ||
ws.addEventListener("open", () => { | ||
isOpen.value = true; | ||
isClosed.value = false; | ||
}); | ||
send = (data) => ws.send(data); | ||
close = (code, reason) => { | ||
ws.close(code, reason); | ||
}; | ||
} | ||
return { | ||
supported, | ||
ws, | ||
@@ -172,2 +176,3 @@ send, | ||
function useIntersectionObserver(refEl, refOptions) { | ||
const supported = core.isClient && 'IntersectionObserver' in window; | ||
const wrappedElement = refEl ? core.wrap(refEl) : undefined; | ||
@@ -188,25 +193,27 @@ const element = wrappedElement && (core.isElement(wrappedElement.value) || !wrappedElement.value) | ||
let observer = compositionApi.ref(); | ||
compositionApi.watch(options, options => { | ||
if (observer.value) { | ||
observer.value.disconnect(); | ||
} | ||
const opts = (options && | ||
options && { | ||
root: core.unwrap(options.root), | ||
rootMargin: core.unwrap(options.rootMargin), | ||
threshold: core.unwrap(options.threshold) | ||
}) || | ||
undefined; | ||
observer.value = new IntersectionObserver(handling, opts); | ||
const targets = elements.value.map(x => x.target); | ||
targets.forEach(observer.value.observe); | ||
}, { deep: true }); | ||
const observe = (element) => { | ||
if (supported) { | ||
compositionApi.watch(options, options => { | ||
if (observer.value) { | ||
observer.value.disconnect(); | ||
} | ||
const opts = (options && | ||
options && { | ||
root: core.unwrap(options.root), | ||
rootMargin: core.unwrap(options.rootMargin), | ||
threshold: core.unwrap(options.threshold) | ||
}) || | ||
undefined; | ||
observer.value = new IntersectionObserver(handling, opts); | ||
const targets = elements.value.map(x => x.target); | ||
targets.forEach(observer.value.observe); | ||
}, { deep: true }); | ||
} | ||
const observe = supported ? (element) => { | ||
const e = core.unwrap(element); | ||
observer.value.observe(e); | ||
}; | ||
const unobserve = (element) => { | ||
} : core.NO_OP; | ||
const unobserve = supported ? (element) => { | ||
const e = core.unwrap(element); | ||
observer.value.unobserve(e); | ||
}; | ||
} : core.NO_OP; | ||
const disconnect = () => observer.value.disconnect(); | ||
@@ -238,2 +245,3 @@ // if the element is passed we should add hooks | ||
return { | ||
supported, | ||
elements, | ||
@@ -248,6 +256,7 @@ observe, | ||
function useNetworkInformation() { | ||
const connection = navigator.connection || | ||
navigator.mozConnection || | ||
navigator.webkitConnection; | ||
const supported = compositionApi.computed(() => !!connection); | ||
const connection = core.isClient ? | ||
navigator.connection || | ||
navigator.mozConnection || | ||
navigator.webkitConnection : false; | ||
const supported = !!connection; | ||
const downlink = compositionApi.ref(0); | ||
@@ -294,3 +303,3 @@ const downlinkMax = compositionApi.ref(0); | ||
function useOnline() { | ||
const supported = "onLine" in navigator; | ||
const supported = core.isClient && "onLine" in navigator; | ||
// not sure how to test this :/ | ||
@@ -319,12 +328,17 @@ if (!supported) { | ||
if (!hidden) { | ||
hidden = compositionApi.ref(document.hidden); | ||
hidden = compositionApi.ref(core.isClient && document.hidden); | ||
} | ||
if (!visibility) { | ||
visibility = compositionApi.ref(document.visibilityState); | ||
document.addEventListener("visibilitychange", () => { | ||
visibility.value = document.visibilityState; | ||
hidden.value = document.hidden; | ||
}, { passive: true } | ||
// true | ||
); | ||
if (core.isClient) { | ||
visibility = compositionApi.ref(document.visibilityState); | ||
document.addEventListener("visibilitychange", () => { | ||
visibility.value = document.visibilityState; | ||
hidden.value = document.hidden; | ||
}, { passive: true } | ||
// true | ||
); | ||
} | ||
else { | ||
visibility = compositionApi.ref(false); | ||
} | ||
} | ||
@@ -341,11 +355,16 @@ return { | ||
if (!language) { | ||
language = compositionApi.ref(navigator.language); | ||
language = core.isClient ? compositionApi.ref(navigator.language) : compositionApi.ref(''); | ||
} | ||
if (!languages) { | ||
languages = compositionApi.ref(navigator.languages); | ||
const change = () => { | ||
language.value = navigator.language; | ||
languages.value = navigator.languages; | ||
}; | ||
window.addEventListener('languagechange', change, { passive: true }); | ||
if (core.isClient) { | ||
languages = compositionApi.ref(navigator.languages); | ||
const change = () => { | ||
language.value = navigator.language; | ||
languages.value = navigator.languages; | ||
}; | ||
window.addEventListener('languagechange', change, { passive: true }); | ||
} | ||
else { | ||
languages = compositionApi.ref([]); | ||
} | ||
} | ||
@@ -358,15 +377,140 @@ return { | ||
function useMatchMedia(query) { | ||
const mediaQueryList = compositionApi.ref(matchMedia(query)); | ||
const matches = compositionApi.ref(mediaQueryList.value.matches); | ||
const process = (e) => { | ||
matches.value = e.matches; | ||
function useBroadcastChannel(name, onBeforeClose) { | ||
const supported = core.isClient && 'BroadcastChannel' in self; | ||
const data = compositionApi.ref(null); | ||
const messageEvent = compositionApi.ref(null); | ||
const errorEvent = compositionApi.ref(null); | ||
const errored = compositionApi.ref(false); | ||
const isClosed = compositionApi.ref(false); | ||
let send = core.NO_OP; | ||
let close = core.NO_OP; | ||
let addListener = core.NO_OP; | ||
/* istanbul ignore else */ | ||
if (supported) { | ||
const bc = new BroadcastChannel(name); | ||
bc.addEventListener('messageerror', (e) => { | ||
errorEvent.value = e; | ||
errored.value = true; | ||
}, core.PASSIVE_EV); | ||
bc.addEventListener('message', (ev) => { | ||
messageEvent.value = ev; | ||
data.value = ev.data; | ||
}, core.PASSIVE_EV); | ||
send = (d) => bc.postMessage(d); | ||
close = () => { | ||
bc.close(); | ||
isClosed.value = true; | ||
}; | ||
addListener = (cb, o) => { | ||
bc.addEventListener('message', cb, o); | ||
compositionApi.onUnmounted(() => bc.removeEventListener('message', cb)); | ||
}; | ||
compositionApi.onUnmounted(() => { | ||
onBeforeClose && onBeforeClose(); | ||
close(); | ||
}); | ||
} | ||
else { | ||
{ | ||
console.warn('[BroadcastChannel] is not supported'); | ||
} | ||
} | ||
return { | ||
supported, | ||
data, | ||
messageEvent, | ||
errorEvent, | ||
errored, | ||
isClosed, | ||
send, | ||
close, | ||
addListener, | ||
}; | ||
mediaQueryList.value.addEventListener("change", process, { passive: true }); | ||
const remove = () => mediaQueryList.value.removeEventListener("change", process); | ||
compositionApi.onUnmounted(remove); | ||
} | ||
function useGeolocation(options) { | ||
const supported = core.isClient && !!navigator.geolocation; | ||
// used to check if the execution is lazy | ||
const lazy = compositionApi.ref(options ? options.immediate === false : undefined); | ||
const error = compositionApi.ref(null); | ||
const timestamp = compositionApi.ref(null); | ||
const coords = compositionApi.ref(null); | ||
const highAccuracy = compositionApi.ref(options && options.enableHighAccuracy || null); | ||
// allow manual control on when the geolocation is requested | ||
let refresh = core.NO_OP; | ||
if (supported) { | ||
const setPosition = (pos) => { | ||
timestamp.value = pos.timestamp; | ||
coords.value = pos.coords; | ||
error.value = null; | ||
}; | ||
const setError = (err) => { | ||
timestamp.value = Date.now(); | ||
coords.value = null; | ||
error.value = err; | ||
}; | ||
const clearWatch = () => lazy.value !== true && watchId && navigator.geolocation.clearWatch(watchId); | ||
let _currentPositionRefresh = () => navigator.geolocation.getCurrentPosition(setPosition, setError, options); | ||
if (lazy.value) { | ||
refresh = () => { | ||
if (lazy.value) { | ||
lazy.value = false; | ||
} | ||
else { | ||
_currentPositionRefresh(); | ||
} | ||
}; | ||
} | ||
else { | ||
// NOTE probably useless?? | ||
refresh = _currentPositionRefresh; | ||
} | ||
let watchId = 0; | ||
compositionApi.onMounted(() => compositionApi.watch([highAccuracy, lazy], (a) => { | ||
clearWatch(); | ||
const enableHighAccuracy = core.isBoolean(a[0]) ? a[0] : options ? options.enableHighAccuracy : undefined; | ||
watchId = navigator.geolocation.watchPosition(setPosition, setError, options ? { ...options, enableHighAccuracy } : { enableHighAccuracy }); | ||
}, { | ||
lazy: lazy.value | ||
})); | ||
compositionApi.onUnmounted(clearWatch); | ||
} | ||
return { | ||
supported, | ||
refresh, | ||
error, | ||
timestamp, | ||
coords, | ||
highAccuracy | ||
}; | ||
} | ||
function useMatchMedia(query) { | ||
const supported = core.isClient ? 'matchMedia' in window : false; | ||
let mediaQueryList = undefined; | ||
let matches = undefined; | ||
let remove = core.NO_OP; | ||
if (supported) { | ||
mediaQueryList = compositionApi.ref(matchMedia(query)); | ||
matches = compositionApi.ref(mediaQueryList.value.matches); | ||
const process = (e) => { | ||
matches.value = e.matches; | ||
}; | ||
mediaQueryList.value.addEventListener("change", process, { passive: true }); | ||
const remove = () => mediaQueryList.value.removeEventListener("change", process); | ||
compositionApi.onUnmounted(remove); | ||
} | ||
else { | ||
/* istanbul ignore else */ | ||
{ | ||
console.warn('[matchMedia] not supported'); | ||
} | ||
mediaQueryList = compositionApi.ref({}); | ||
matches = compositionApi.ref(false); | ||
} | ||
return { | ||
supported, | ||
mediaQueryList, | ||
remove, | ||
matches | ||
matches, | ||
remove | ||
}; | ||
@@ -399,3 +543,3 @@ } | ||
sorted = sorted.sort((a, b) => b - a); | ||
const resize = () => { | ||
const resize = core.isClient ? () => { | ||
const width = window.innerWidth; | ||
@@ -412,5 +556,5 @@ let c = undefined; | ||
current.value = c; | ||
}; | ||
} : core.NO_OP; | ||
const processResize = core.useDebounce(resize, 10); | ||
const remove = () => window.removeEventListener("resize", processResize); | ||
const remove = core.isClient ? () => window.removeEventListener("resize", processResize) : core.NO_OP; | ||
compositionApi.onMounted(() => { | ||
@@ -433,2 +577,168 @@ resize(); | ||
function useSharedRef(name, defaultValue) { | ||
const { addListener, send, close, supported } = useBroadcastChannel(name, () => disconnect()); | ||
const id = Date.now(); | ||
const master = compositionApi.ref(false); | ||
const mind = compositionApi.ref(0 /* HIVE */); | ||
const editable = compositionApi.computed(() => mind.value === 1 /* MASTER */ ? master.value : true); | ||
// who's listening to this broadcast | ||
const targets = compositionApi.ref([]); | ||
const data = compositionApi.ref(defaultValue); | ||
// if the state was updated by an event it sets to true | ||
let updateState = false; | ||
let masterId = undefined; | ||
send({ type: 0 /* INIT */ }); | ||
const ping = () => send({ type: 5 /* PING */, id }); | ||
const disconnect = () => { | ||
if (targets.value.length === 0) | ||
return; | ||
if (master.value) { | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
mind: 1 /* MASTER */, | ||
id: Math.min(...targets.value) | ||
}); | ||
} | ||
send({ | ||
type: 4 /* LEAVE */, | ||
id | ||
}); | ||
}; | ||
const setMind = (t) => { | ||
switch (t) { | ||
case 1 /* MASTER */: { | ||
master.value = true; | ||
break; | ||
} | ||
case 0 /* HIVE */: { | ||
master.value = false; | ||
break; | ||
} | ||
} | ||
mind.value = t; | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
id: id, | ||
mind: mind.value, | ||
}); | ||
}; | ||
addListener((e) => { | ||
switch (e.data.type) { | ||
case 0 /* INIT */: { | ||
send({ | ||
type: 2 /* UPDATE */, | ||
value: data.value, | ||
mind: mind.value | ||
}); | ||
break; | ||
} | ||
case 4 /* LEAVE */: { | ||
const index = targets.value.indexOf(e.data.id); | ||
if (index >= 0) { | ||
targets.value.splice(index, 1); | ||
} | ||
// if master disconnects | ||
if (masterId === e.data.id && targets.value.length > 0) { | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
mind: 1 /* MASTER */, | ||
id: Math.min(id, ...targets.value) | ||
}); | ||
} | ||
break; | ||
} | ||
case 2 /* UPDATE */: { | ||
updateState = true; | ||
data.value = e.data.value; | ||
mind.value = e.data.mind; | ||
break; | ||
} | ||
case 3 /* SET_MIND */: { | ||
mind.value = e.data.mind; | ||
masterId = e.data.mind === 1 /* MASTER */ && e.data.id || undefined; | ||
master.value = masterId === id; | ||
if (master.value) { | ||
targets.value = []; | ||
ping(); | ||
} | ||
break; | ||
} | ||
case 5 /* PING */: { | ||
targets.value = [e.data.id]; | ||
send({ | ||
type: 6 /* PONG */, | ||
id | ||
}); | ||
break; | ||
} | ||
case 6 /* PONG */: { | ||
targets.value.push(e.data.id); | ||
break; | ||
} | ||
} | ||
}, core.PASSIVE_EV); | ||
ping(); | ||
compositionApi.watch(data, (v, o) => { | ||
if (updateState) { | ||
updateState = false; | ||
return; | ||
} | ||
// mind is set to MASTER and we are not master, we shouldn't update! | ||
if (mind.value === 1 /* MASTER */ && master.value === false) { | ||
updateState = true; | ||
data.value = o; | ||
return; | ||
} | ||
send({ | ||
type: 2 /* UPDATE */, | ||
mind: mind.value, | ||
value: core.isObject(v) ? { ...v } : v | ||
}); | ||
updateState = false; | ||
}, { deep: true, lazy: true }); | ||
if (core.isClient) { | ||
window.addEventListener('unload', disconnect, core.PASSIVE_EV); | ||
} | ||
compositionApi.onUnmounted(() => { | ||
disconnect(); | ||
close(); | ||
}); | ||
return { | ||
supported, | ||
id, | ||
data, | ||
master, | ||
mind, | ||
editable, | ||
targets, | ||
ping, | ||
setMind, | ||
addListener | ||
}; | ||
} | ||
let shared = undefined; | ||
function refShared(defaultValue, id) { | ||
const vm = compositionApi.getCurrentInstance(); | ||
const name = id ? id : vm.$vnode.tag; | ||
/* istanbul ignore else */ | ||
{ | ||
if (!shared) { | ||
shared = new Set(); | ||
} | ||
if (shared.has(name)) { | ||
console.warn('[refShared] You can only have one refShared per component, if you need more please assign pass an id refShared(defaultValue, id)'); | ||
} | ||
shared.add(name); | ||
} | ||
const { data, supported } = useSharedRef(name, defaultValue); | ||
/* istanbul ignore next */ | ||
{ | ||
if (!supported) { | ||
console.warn('[refShared] is dependent of BroadcastChannel'); | ||
} | ||
} | ||
return data; | ||
} | ||
const STORAGE_TEST_KEY = '__storage_test__' ; | ||
/* istanbul ignore next */ | ||
@@ -447,3 +757,3 @@ function isQuotaExceededError(e, storage) { | ||
// acknowledge QuotaExceededError only if there's something already stored | ||
(storage && storage.length !== 0); | ||
(storage && storage.length !== 0 || false); | ||
} | ||
@@ -453,3 +763,6 @@ // based on https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API | ||
try { | ||
const x = '__storage_test__'; | ||
if (!storage) { | ||
return false; | ||
} | ||
const x = STORAGE_TEST_KEY; | ||
storage.setItem(x, x); | ||
@@ -473,3 +786,3 @@ storage.removeItem(x); | ||
function useWebStorage(type, serializer = JSON, ms = 10) { | ||
const storage = window[type]; | ||
const storage = core.isClient ? window[type] : undefined; | ||
const supported = storageAvailable(storage); | ||
@@ -479,31 +792,33 @@ const remove = () => storageMap.delete(type); | ||
storageMap = new Map(); | ||
window.addEventListener('storage', (e) => { | ||
if (e.newValue === e.oldValue) { | ||
return; | ||
} | ||
let webStore = storageMap.get('localStorage'); | ||
if (e.storageArea === window.localStorage) { | ||
webStore = storageMap.get('localStorage'); | ||
} | ||
else { | ||
webStore = storageMap.get('sessionStorage'); | ||
} | ||
if (webStore && Object.keys(webStore.$syncKeys).length > 0) { | ||
if (e.key === null) { | ||
webStore.clear(); | ||
if (core.isClient) { | ||
window.addEventListener('storage', (e) => { | ||
if (e.newValue === e.oldValue) { | ||
return; | ||
} | ||
else if (webStore.$syncKeys[e.key]) { | ||
if (e.newValue === null) { | ||
webStore.removeItem(e.key); | ||
let webStore = storageMap.get('localStorage'); | ||
if (e.storageArea === window.localStorage) { | ||
webStore = storageMap.get('localStorage'); | ||
} | ||
else { | ||
webStore = storageMap.get('sessionStorage'); | ||
} | ||
if (webStore && Object.keys(webStore.$syncKeys).length > 0) { | ||
if (e.key === null) { | ||
webStore.clear(); | ||
} | ||
else { | ||
webStore.updateItem(e.key, e.newValue); | ||
else if (webStore.$syncKeys[e.key]) { | ||
if (e.newValue === null) { | ||
webStore.removeItem(e.key); | ||
} | ||
else { | ||
webStore.updateItem(e.key, e.newValue); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
let store = storageMap.get(type); | ||
let quotaError; | ||
if (supported) { | ||
if (supported && storage) { | ||
if (!store) { | ||
@@ -622,2 +937,3 @@ quotaError = compositionApi.ref(false); | ||
} | ||
storage = compositionApi.ref(defaultValue); | ||
} | ||
@@ -656,2 +972,3 @@ return { | ||
} | ||
storage = compositionApi.ref(defaultValue); | ||
} | ||
@@ -677,6 +994,9 @@ return { | ||
exports.refShared = refShared; | ||
exports.storageAvailable = storageAvailable; | ||
exports.useBreakpoint = useBreakpoint; | ||
exports.useBroadcastChannel = useBroadcastChannel; | ||
exports.useEvent = useEvent; | ||
exports.useFetch = useFetch; | ||
exports.useGeolocation = useGeolocation; | ||
exports.useIntersectionObserver = useIntersectionObserver; | ||
@@ -693,4 +1013,5 @@ exports.useLanguage = useLanguage; | ||
exports.useSessionStorage = useSessionStorage; | ||
exports.useSharedRef = useSharedRef; | ||
exports.useStorage = useStorage; | ||
exports.useWebSocket = useWebSocket; | ||
exports.useWebStorage = useWebStorage; |
@@ -23,4 +23,3 @@ 'use strict'; | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = core.isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -45,4 +44,3 @@ handler = core.useDebounce(handler, wait); | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = core.isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -52,3 +50,3 @@ handler = core.useDebounce(handler, wait); | ||
// resize seems only to be fired against the window | ||
const remove = useEvent(window, "resize", handler, eventOptions || { passive: true }); | ||
const remove = core.isClient ? useEvent(window, "resize", handler, eventOptions || { passive: true }) : core.NO_OP; | ||
return { | ||
@@ -69,4 +67,3 @@ height, | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = core.isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -117,3 +114,4 @@ handler = core.useDebounce(handler, wait); | ||
function useWebSocket(url, protocols) { | ||
const ws = new WebSocket(url, protocols); | ||
const supported = core.isClient && 'WebSocket' in window; | ||
let ws = null; | ||
const messageEvent = compositionApi.ref(null); | ||
@@ -125,23 +123,29 @@ const errorEvent = compositionApi.ref(); | ||
const errored = compositionApi.ref(false); | ||
ws.addEventListener("message", x => { | ||
messageEvent.value = x; | ||
data.value = x.data; | ||
}); | ||
ws.addEventListener("error", error => { | ||
errorEvent.value = error; | ||
errored.value = true; | ||
}); | ||
ws.addEventListener("close", () => { | ||
isOpen.value = false; | ||
isClosed.value = true; | ||
}); | ||
ws.addEventListener("open", () => { | ||
isOpen.value = true; | ||
isClosed.value = false; | ||
}); | ||
const send = (data) => ws.send(data); | ||
const close = (code, reason) => { | ||
ws.close(code, reason); | ||
}; | ||
let send = core.NO_OP; | ||
let close = core.NO_OP; | ||
if (supported) { | ||
ws = new WebSocket(url, protocols); | ||
ws.addEventListener("message", x => { | ||
messageEvent.value = x; | ||
data.value = x.data; | ||
}); | ||
ws.addEventListener("error", error => { | ||
errorEvent.value = error; | ||
errored.value = true; | ||
}); | ||
ws.addEventListener("close", () => { | ||
isOpen.value = false; | ||
isClosed.value = true; | ||
}); | ||
ws.addEventListener("open", () => { | ||
isOpen.value = true; | ||
isClosed.value = false; | ||
}); | ||
send = (data) => ws.send(data); | ||
close = (code, reason) => { | ||
ws.close(code, reason); | ||
}; | ||
} | ||
return { | ||
supported, | ||
ws, | ||
@@ -160,2 +164,3 @@ send, | ||
function useIntersectionObserver(refEl, refOptions) { | ||
const supported = core.isClient && 'IntersectionObserver' in window; | ||
const wrappedElement = refEl ? core.wrap(refEl) : undefined; | ||
@@ -176,25 +181,27 @@ const element = wrappedElement && (core.isElement(wrappedElement.value) || !wrappedElement.value) | ||
let observer = compositionApi.ref(); | ||
compositionApi.watch(options, options => { | ||
if (observer.value) { | ||
observer.value.disconnect(); | ||
} | ||
const opts = (options && | ||
options && { | ||
root: core.unwrap(options.root), | ||
rootMargin: core.unwrap(options.rootMargin), | ||
threshold: core.unwrap(options.threshold) | ||
}) || | ||
undefined; | ||
observer.value = new IntersectionObserver(handling, opts); | ||
const targets = elements.value.map(x => x.target); | ||
targets.forEach(observer.value.observe); | ||
}, { deep: true }); | ||
const observe = (element) => { | ||
if (supported) { | ||
compositionApi.watch(options, options => { | ||
if (observer.value) { | ||
observer.value.disconnect(); | ||
} | ||
const opts = (options && | ||
options && { | ||
root: core.unwrap(options.root), | ||
rootMargin: core.unwrap(options.rootMargin), | ||
threshold: core.unwrap(options.threshold) | ||
}) || | ||
undefined; | ||
observer.value = new IntersectionObserver(handling, opts); | ||
const targets = elements.value.map(x => x.target); | ||
targets.forEach(observer.value.observe); | ||
}, { deep: true }); | ||
} | ||
const observe = supported ? (element) => { | ||
const e = core.unwrap(element); | ||
observer.value.observe(e); | ||
}; | ||
const unobserve = (element) => { | ||
} : core.NO_OP; | ||
const unobserve = supported ? (element) => { | ||
const e = core.unwrap(element); | ||
observer.value.unobserve(e); | ||
}; | ||
} : core.NO_OP; | ||
const disconnect = () => observer.value.disconnect(); | ||
@@ -226,2 +233,3 @@ // if the element is passed we should add hooks | ||
return { | ||
supported, | ||
elements, | ||
@@ -236,6 +244,7 @@ observe, | ||
function useNetworkInformation() { | ||
const connection = navigator.connection || | ||
navigator.mozConnection || | ||
navigator.webkitConnection; | ||
const supported = compositionApi.computed(() => !!connection); | ||
const connection = core.isClient ? | ||
navigator.connection || | ||
navigator.mozConnection || | ||
navigator.webkitConnection : false; | ||
const supported = !!connection; | ||
const downlink = compositionApi.ref(0); | ||
@@ -276,3 +285,3 @@ const downlinkMax = compositionApi.ref(0); | ||
function useOnline() { | ||
const supported = "onLine" in navigator; | ||
const supported = core.isClient && "onLine" in navigator; | ||
// not sure how to test this :/ | ||
@@ -301,12 +310,17 @@ if (!supported) { | ||
if (!hidden) { | ||
hidden = compositionApi.ref(document.hidden); | ||
hidden = compositionApi.ref(core.isClient && document.hidden); | ||
} | ||
if (!visibility) { | ||
visibility = compositionApi.ref(document.visibilityState); | ||
document.addEventListener("visibilitychange", () => { | ||
visibility.value = document.visibilityState; | ||
hidden.value = document.hidden; | ||
}, { passive: true } | ||
// true | ||
); | ||
if (core.isClient) { | ||
visibility = compositionApi.ref(document.visibilityState); | ||
document.addEventListener("visibilitychange", () => { | ||
visibility.value = document.visibilityState; | ||
hidden.value = document.hidden; | ||
}, { passive: true } | ||
// true | ||
); | ||
} | ||
else { | ||
visibility = compositionApi.ref(false); | ||
} | ||
} | ||
@@ -323,11 +337,16 @@ return { | ||
if (!language) { | ||
language = compositionApi.ref(navigator.language); | ||
language = core.isClient ? compositionApi.ref(navigator.language) : compositionApi.ref(''); | ||
} | ||
if (!languages) { | ||
languages = compositionApi.ref(navigator.languages); | ||
const change = () => { | ||
language.value = navigator.language; | ||
languages.value = navigator.languages; | ||
}; | ||
window.addEventListener('languagechange', change, { passive: true }); | ||
if (core.isClient) { | ||
languages = compositionApi.ref(navigator.languages); | ||
const change = () => { | ||
language.value = navigator.language; | ||
languages.value = navigator.languages; | ||
}; | ||
window.addEventListener('languagechange', change, { passive: true }); | ||
} | ||
else { | ||
languages = compositionApi.ref([]); | ||
} | ||
} | ||
@@ -340,15 +359,131 @@ return { | ||
function useMatchMedia(query) { | ||
const mediaQueryList = compositionApi.ref(matchMedia(query)); | ||
const matches = compositionApi.ref(mediaQueryList.value.matches); | ||
const process = (e) => { | ||
matches.value = e.matches; | ||
function useBroadcastChannel(name, onBeforeClose) { | ||
const supported = core.isClient && 'BroadcastChannel' in self; | ||
const data = compositionApi.ref(null); | ||
const messageEvent = compositionApi.ref(null); | ||
const errorEvent = compositionApi.ref(null); | ||
const errored = compositionApi.ref(false); | ||
const isClosed = compositionApi.ref(false); | ||
let send = core.NO_OP; | ||
let close = core.NO_OP; | ||
let addListener = core.NO_OP; | ||
/* istanbul ignore else */ | ||
if (supported) { | ||
const bc = new BroadcastChannel(name); | ||
bc.addEventListener('messageerror', (e) => { | ||
errorEvent.value = e; | ||
errored.value = true; | ||
}, core.PASSIVE_EV); | ||
bc.addEventListener('message', (ev) => { | ||
messageEvent.value = ev; | ||
data.value = ev.data; | ||
}, core.PASSIVE_EV); | ||
send = (d) => bc.postMessage(d); | ||
close = () => { | ||
bc.close(); | ||
isClosed.value = true; | ||
}; | ||
addListener = (cb, o) => { | ||
bc.addEventListener('message', cb, o); | ||
compositionApi.onUnmounted(() => bc.removeEventListener('message', cb)); | ||
}; | ||
compositionApi.onUnmounted(() => { | ||
onBeforeClose && onBeforeClose(); | ||
close(); | ||
}); | ||
} | ||
return { | ||
supported, | ||
data, | ||
messageEvent, | ||
errorEvent, | ||
errored, | ||
isClosed, | ||
send, | ||
close, | ||
addListener, | ||
}; | ||
mediaQueryList.value.addEventListener("change", process, { passive: true }); | ||
const remove = () => mediaQueryList.value.removeEventListener("change", process); | ||
compositionApi.onUnmounted(remove); | ||
} | ||
function useGeolocation(options) { | ||
const supported = core.isClient && !!navigator.geolocation; | ||
// used to check if the execution is lazy | ||
const lazy = compositionApi.ref(options ? options.immediate === false : undefined); | ||
const error = compositionApi.ref(null); | ||
const timestamp = compositionApi.ref(null); | ||
const coords = compositionApi.ref(null); | ||
const highAccuracy = compositionApi.ref(options && options.enableHighAccuracy || null); | ||
// allow manual control on when the geolocation is requested | ||
let refresh = core.NO_OP; | ||
if (supported) { | ||
const setPosition = (pos) => { | ||
timestamp.value = pos.timestamp; | ||
coords.value = pos.coords; | ||
error.value = null; | ||
}; | ||
const setError = (err) => { | ||
timestamp.value = Date.now(); | ||
coords.value = null; | ||
error.value = err; | ||
}; | ||
const clearWatch = () => lazy.value !== true && watchId && navigator.geolocation.clearWatch(watchId); | ||
let _currentPositionRefresh = () => navigator.geolocation.getCurrentPosition(setPosition, setError, options); | ||
if (lazy.value) { | ||
refresh = () => { | ||
if (lazy.value) { | ||
lazy.value = false; | ||
} | ||
else { | ||
_currentPositionRefresh(); | ||
} | ||
}; | ||
} | ||
else { | ||
// NOTE probably useless?? | ||
refresh = _currentPositionRefresh; | ||
} | ||
let watchId = 0; | ||
compositionApi.onMounted(() => compositionApi.watch([highAccuracy, lazy], (a) => { | ||
clearWatch(); | ||
const enableHighAccuracy = core.isBoolean(a[0]) ? a[0] : options ? options.enableHighAccuracy : undefined; | ||
watchId = navigator.geolocation.watchPosition(setPosition, setError, options ? { ...options, enableHighAccuracy } : { enableHighAccuracy }); | ||
}, { | ||
lazy: lazy.value | ||
})); | ||
compositionApi.onUnmounted(clearWatch); | ||
} | ||
return { | ||
supported, | ||
refresh, | ||
error, | ||
timestamp, | ||
coords, | ||
highAccuracy | ||
}; | ||
} | ||
function useMatchMedia(query) { | ||
const supported = core.isClient ? 'matchMedia' in window : false; | ||
let mediaQueryList = undefined; | ||
let matches = undefined; | ||
let remove = core.NO_OP; | ||
if (supported) { | ||
mediaQueryList = compositionApi.ref(matchMedia(query)); | ||
matches = compositionApi.ref(mediaQueryList.value.matches); | ||
const process = (e) => { | ||
matches.value = e.matches; | ||
}; | ||
mediaQueryList.value.addEventListener("change", process, { passive: true }); | ||
const remove = () => mediaQueryList.value.removeEventListener("change", process); | ||
compositionApi.onUnmounted(remove); | ||
} | ||
else { | ||
mediaQueryList = compositionApi.ref({}); | ||
matches = compositionApi.ref(false); | ||
} | ||
return { | ||
supported, | ||
mediaQueryList, | ||
remove, | ||
matches | ||
matches, | ||
remove | ||
}; | ||
@@ -381,3 +516,3 @@ } | ||
sorted = sorted.sort((a, b) => b - a); | ||
const resize = () => { | ||
const resize = core.isClient ? () => { | ||
const width = window.innerWidth; | ||
@@ -394,5 +529,5 @@ let c = undefined; | ||
current.value = c; | ||
}; | ||
} : core.NO_OP; | ||
const processResize = core.useDebounce(resize, 10); | ||
const remove = () => window.removeEventListener("resize", processResize); | ||
const remove = core.isClient ? () => window.removeEventListener("resize", processResize) : core.NO_OP; | ||
compositionApi.onMounted(() => { | ||
@@ -415,2 +550,151 @@ resize(); | ||
function useSharedRef(name, defaultValue) { | ||
const { addListener, send, close, supported } = useBroadcastChannel(name, () => disconnect()); | ||
const id = Date.now(); | ||
const master = compositionApi.ref(false); | ||
const mind = compositionApi.ref(0 /* HIVE */); | ||
const editable = compositionApi.computed(() => mind.value === 1 /* MASTER */ ? master.value : true); | ||
// who's listening to this broadcast | ||
const targets = compositionApi.ref([]); | ||
const data = compositionApi.ref(defaultValue); | ||
// if the state was updated by an event it sets to true | ||
let updateState = false; | ||
let masterId = undefined; | ||
send({ type: 0 /* INIT */ }); | ||
const ping = () => send({ type: 5 /* PING */, id }); | ||
const disconnect = () => { | ||
if (targets.value.length === 0) | ||
return; | ||
if (master.value) { | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
mind: 1 /* MASTER */, | ||
id: Math.min(...targets.value) | ||
}); | ||
} | ||
send({ | ||
type: 4 /* LEAVE */, | ||
id | ||
}); | ||
}; | ||
const setMind = (t) => { | ||
switch (t) { | ||
case 1 /* MASTER */: { | ||
master.value = true; | ||
break; | ||
} | ||
case 0 /* HIVE */: { | ||
master.value = false; | ||
break; | ||
} | ||
} | ||
mind.value = t; | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
id: id, | ||
mind: mind.value, | ||
}); | ||
}; | ||
addListener((e) => { | ||
switch (e.data.type) { | ||
case 0 /* INIT */: { | ||
send({ | ||
type: 2 /* UPDATE */, | ||
value: data.value, | ||
mind: mind.value | ||
}); | ||
break; | ||
} | ||
case 4 /* LEAVE */: { | ||
const index = targets.value.indexOf(e.data.id); | ||
if (index >= 0) { | ||
targets.value.splice(index, 1); | ||
} | ||
// if master disconnects | ||
if (masterId === e.data.id && targets.value.length > 0) { | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
mind: 1 /* MASTER */, | ||
id: Math.min(id, ...targets.value) | ||
}); | ||
} | ||
break; | ||
} | ||
case 2 /* UPDATE */: { | ||
updateState = true; | ||
data.value = e.data.value; | ||
mind.value = e.data.mind; | ||
break; | ||
} | ||
case 3 /* SET_MIND */: { | ||
mind.value = e.data.mind; | ||
masterId = e.data.mind === 1 /* MASTER */ && e.data.id || undefined; | ||
master.value = masterId === id; | ||
if (master.value) { | ||
targets.value = []; | ||
ping(); | ||
} | ||
break; | ||
} | ||
case 5 /* PING */: { | ||
targets.value = [e.data.id]; | ||
send({ | ||
type: 6 /* PONG */, | ||
id | ||
}); | ||
break; | ||
} | ||
case 6 /* PONG */: { | ||
targets.value.push(e.data.id); | ||
break; | ||
} | ||
} | ||
}, core.PASSIVE_EV); | ||
ping(); | ||
compositionApi.watch(data, (v, o) => { | ||
if (updateState) { | ||
updateState = false; | ||
return; | ||
} | ||
// mind is set to MASTER and we are not master, we shouldn't update! | ||
if (mind.value === 1 /* MASTER */ && master.value === false) { | ||
updateState = true; | ||
data.value = o; | ||
return; | ||
} | ||
send({ | ||
type: 2 /* UPDATE */, | ||
mind: mind.value, | ||
value: core.isObject(v) ? { ...v } : v | ||
}); | ||
updateState = false; | ||
}, { deep: true, lazy: true }); | ||
if (core.isClient) { | ||
window.addEventListener('unload', disconnect, core.PASSIVE_EV); | ||
} | ||
compositionApi.onUnmounted(() => { | ||
disconnect(); | ||
close(); | ||
}); | ||
return { | ||
supported, | ||
id, | ||
data, | ||
master, | ||
mind, | ||
editable, | ||
targets, | ||
ping, | ||
setMind, | ||
addListener | ||
}; | ||
} | ||
function refShared(defaultValue, id) { | ||
const vm = compositionApi.getCurrentInstance(); | ||
const name = id ? id : vm.$vnode.tag; | ||
const { data, supported } = useSharedRef(name, defaultValue); | ||
return data; | ||
} | ||
const STORAGE_TEST_KEY = ":$"; | ||
/* istanbul ignore next */ | ||
@@ -429,3 +713,3 @@ function isQuotaExceededError(e, storage) { | ||
// acknowledge QuotaExceededError only if there's something already stored | ||
(storage && storage.length !== 0); | ||
(storage && storage.length !== 0 || false); | ||
} | ||
@@ -435,3 +719,6 @@ // based on https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API | ||
try { | ||
const x = '__storage_test__'; | ||
if (!storage) { | ||
return false; | ||
} | ||
const x = STORAGE_TEST_KEY; | ||
storage.setItem(x, x); | ||
@@ -455,3 +742,3 @@ storage.removeItem(x); | ||
function useWebStorage(type, serializer = JSON, ms = 10) { | ||
const storage = window[type]; | ||
const storage = core.isClient ? window[type] : undefined; | ||
const supported = storageAvailable(storage); | ||
@@ -461,31 +748,33 @@ const remove = () => storageMap.delete(type); | ||
storageMap = new Map(); | ||
window.addEventListener('storage', (e) => { | ||
if (e.newValue === e.oldValue) { | ||
return; | ||
} | ||
let webStore = storageMap.get('localStorage'); | ||
if (e.storageArea === window.localStorage) { | ||
webStore = storageMap.get('localStorage'); | ||
} | ||
else { | ||
webStore = storageMap.get('sessionStorage'); | ||
} | ||
if (webStore && Object.keys(webStore.$syncKeys).length > 0) { | ||
if (e.key === null) { | ||
webStore.clear(); | ||
if (core.isClient) { | ||
window.addEventListener('storage', (e) => { | ||
if (e.newValue === e.oldValue) { | ||
return; | ||
} | ||
else if (webStore.$syncKeys[e.key]) { | ||
if (e.newValue === null) { | ||
webStore.removeItem(e.key); | ||
let webStore = storageMap.get('localStorage'); | ||
if (e.storageArea === window.localStorage) { | ||
webStore = storageMap.get('localStorage'); | ||
} | ||
else { | ||
webStore = storageMap.get('sessionStorage'); | ||
} | ||
if (webStore && Object.keys(webStore.$syncKeys).length > 0) { | ||
if (e.key === null) { | ||
webStore.clear(); | ||
} | ||
else { | ||
webStore.updateItem(e.key, e.newValue); | ||
else if (webStore.$syncKeys[e.key]) { | ||
if (e.newValue === null) { | ||
webStore.removeItem(e.key); | ||
} | ||
else { | ||
webStore.updateItem(e.key, e.newValue); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
let store = storageMap.get(type); | ||
let quotaError; | ||
if (supported) { | ||
if (supported && storage) { | ||
if (!store) { | ||
@@ -599,2 +888,5 @@ quotaError = compositionApi.ref(false); | ||
} | ||
else { | ||
storage = compositionApi.ref(defaultValue); | ||
} | ||
return { | ||
@@ -623,2 +915,5 @@ supported, | ||
} | ||
else { | ||
storage = compositionApi.ref(defaultValue); | ||
} | ||
return { | ||
@@ -643,6 +938,9 @@ supported, | ||
exports.refShared = refShared; | ||
exports.storageAvailable = storageAvailable; | ||
exports.useBreakpoint = useBreakpoint; | ||
exports.useBroadcastChannel = useBroadcastChannel; | ||
exports.useEvent = useEvent; | ||
exports.useFetch = useFetch; | ||
exports.useGeolocation = useGeolocation; | ||
exports.useIntersectionObserver = useIntersectionObserver; | ||
@@ -659,4 +957,5 @@ exports.useLanguage = useLanguage; | ||
exports.useSessionStorage = useSessionStorage; | ||
exports.useSharedRef = useSharedRef; | ||
exports.useStorage = useStorage; | ||
exports.useWebSocket = useWebSocket; | ||
exports.useWebStorage = useWebStorage; |
@@ -5,2 +5,14 @@ import { Ref } from '@vue/composition-api'; | ||
export declare interface BroadcastMessageEvent<T> extends MessageEvent { | ||
readonly data: T; | ||
} | ||
export declare interface GeolocationOptions { | ||
/** | ||
* @description Executes request location immediately | ||
* @default true | ||
*/ | ||
immediate?: boolean; | ||
} | ||
export declare interface IntersectionObserverOptions { | ||
@@ -13,2 +25,3 @@ root?: RefTyped<Element> | null; | ||
export declare interface IntersectionObserverResult { | ||
supported: boolean; | ||
elements: Ref<IntersectionObserverEntry[]>; | ||
@@ -67,3 +80,3 @@ observe: (el: RefTyped<Element>) => void; | ||
declare interface NetworkInformationReturn { | ||
readonly supported: Ref<boolean>; | ||
readonly supported: boolean; | ||
/** | ||
@@ -99,2 +112,53 @@ * @description Returns the effective bandwidth estimate in megabits per second, rounded to the nearest multiple of 25 kilobits per seconds | ||
export declare function refShared<T = any>(defaultValue?: RefTyped<T>, id?: string): Ref<RefTyped<T>>; | ||
export declare type RefSharedMessage<T = any> = RefSharedMessageInit | RefSharedMessageSync<T> | RefSharedMessageLeave | RefSharedMessageUpdate<T> | RefSharedMessageSetMind | RefSharedMessagePing | RefSharedMessagePong; | ||
export declare type RefSharedMessageInit = { | ||
type: RefSharedMessageType.INIT; | ||
}; | ||
export declare type RefSharedMessageLeave = { | ||
type: RefSharedMessageType.LEAVE; | ||
id: number; | ||
}; | ||
export declare type RefSharedMessagePing = { | ||
type: RefSharedMessageType.PING; | ||
id: number; | ||
}; | ||
export declare type RefSharedMessagePong = { | ||
type: RefSharedMessageType.PONG; | ||
id: number; | ||
}; | ||
export declare type RefSharedMessageSetMind = { | ||
type: RefSharedMessageType.SET_MIND; | ||
mind: SharedRefMind; | ||
id: number; | ||
}; | ||
export declare type RefSharedMessageSync<T> = { | ||
type: RefSharedMessageType.SYNC; | ||
value: T; | ||
mind: SharedRefMind; | ||
}; | ||
export declare const enum RefSharedMessageType { | ||
INIT = 0, | ||
SYNC = 1, | ||
UPDATE = 2, | ||
SET_MIND = 3, | ||
LEAVE = 4, | ||
PING = 5, | ||
PONG = 6 | ||
} | ||
export declare type RefSharedMessageUpdate<T> = { | ||
type: RefSharedMessageType.UPDATE; | ||
value: T; | ||
mind: SharedRefMind; | ||
}; | ||
export declare type RemoveEventFunction = () => void; | ||
@@ -114,4 +178,9 @@ | ||
export declare function storageAvailable(storage: Storage): boolean; | ||
export declare const enum SharedRefMind { | ||
HIVE = 0, | ||
MASTER = 1 | ||
} | ||
export declare function storageAvailable(storage?: Storage): boolean; | ||
export declare interface StorageSerializer<T = any> { | ||
@@ -127,2 +196,14 @@ stringify(item: T): string; | ||
export declare function useBroadcastChannel<T = any>(name: string, onBeforeClose?: Function): { | ||
supported: boolean; | ||
data: import("@vue/composition-api").Ref<T | null>; | ||
messageEvent: import("@vue/composition-api").Ref<MessageEvent | null>; | ||
errorEvent: import("@vue/composition-api").Ref<MessageEvent | null>; | ||
errored: import("@vue/composition-api").Ref<boolean>; | ||
isClosed: import("@vue/composition-api").Ref<boolean>; | ||
send: (data: T) => void; | ||
close: Function; | ||
addListener: (cb: (ev: BroadcastMessageEvent<T>) => void, options?: boolean | AddEventListenerOptions | undefined) => void; | ||
}; | ||
export declare function useEvent<T extends { | ||
@@ -167,2 +248,11 @@ addEventListener: (name: string, listener: EventListenerOrEventListenerObject) => any; | ||
export declare function useGeolocation(options?: PositionOptions & GeolocationOptions): { | ||
supported: boolean; | ||
refresh: () => void; | ||
error: import("@vue/composition-api").Ref<PositionError | null>; | ||
timestamp: import("@vue/composition-api").Ref<number | null>; | ||
coords: import("@vue/composition-api").Ref<Coordinates | null>; | ||
highAccuracy: import("@vue/composition-api").Ref<boolean>; | ||
}; | ||
export declare function useIntersectionObserver(el: RefElement, options?: RefTyped<IntersectionObserverOptions>): IntersectionObserverResult; | ||
@@ -182,5 +272,6 @@ | ||
export declare function useMatchMedia(query: string): { | ||
mediaQueryList: import("@vue/composition-api").Ref<MediaQueryList>; | ||
supported: boolean; | ||
mediaQueryList: Ref<MediaQueryList>; | ||
matches: Ref<boolean>; | ||
remove: () => void; | ||
matches: import("@vue/composition-api").Ref<boolean>; | ||
}; | ||
@@ -228,2 +319,15 @@ | ||
export declare function useSharedRef<T = any>(name: string, defaultValue?: T): { | ||
supported: boolean; | ||
id: number; | ||
data: Ref<T>; | ||
master: Ref<boolean>; | ||
mind: Ref<SharedRefMind>; | ||
editable: Readonly<Ref<boolean>>; | ||
targets: Ref<number[]>; | ||
ping: () => void; | ||
setMind: (t: SharedRefMind) => void; | ||
addListener: (cb: (ev: import("../web").BroadcastMessageEvent<RefSharedMessage<T>>) => void, options?: boolean | AddEventListenerOptions | undefined) => void; | ||
}; | ||
export declare function useStorage(key: string, defaultValue?: RefTyped<string>): LocalStorageReturn<string>; | ||
@@ -234,3 +338,4 @@ | ||
export declare function useWebSocket(url: string, protocols?: string | string[]): { | ||
ws: WebSocket; | ||
supported: boolean; | ||
ws: WebSocket | null; | ||
send: (data: string | ArrayBuffer | SharedArrayBuffer | Blob | ArrayBufferView) => void; | ||
@@ -237,0 +342,0 @@ close: (code?: number | undefined, reason?: string | undefined) => void; |
@@ -1,3 +0,3 @@ | ||
import { onMounted, onUnmounted, ref, computed, watch } from '@vue/composition-api'; | ||
import { wrap, useDebounce, usePromise, isElement, unwrap, NO_OP, isNumber, debounce, isString, FALSE_OP } from '@vue-composable/core'; | ||
import { onMounted, onUnmounted, ref, computed, watch, getCurrentInstance } from '@vue/composition-api'; | ||
import { wrap, isNumber, useDebounce, isClient, NO_OP, usePromise, isElement, unwrap, PASSIVE_EV, isBoolean, isObject, debounce, isString, FALSE_OP } from '@vue-composable/core'; | ||
@@ -19,4 +19,3 @@ function useEvent(el, name, listener, options) { | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -41,4 +40,3 @@ handler = useDebounce(handler, wait); | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -48,3 +46,3 @@ handler = useDebounce(handler, wait); | ||
// resize seems only to be fired against the window | ||
const remove = useEvent(window, "resize", handler, eventOptions || { passive: true }); | ||
const remove = isClient ? useEvent(window, "resize", handler, eventOptions || { passive: true }) : NO_OP; | ||
return { | ||
@@ -65,4 +63,3 @@ height, | ||
}; | ||
const eventOptions = typeof options === "number" ? undefined : options; | ||
const ms = typeof options === "number" ? options : wait; | ||
const [eventOptions, ms] = isNumber(options) ? [undefined, options] : [options, wait]; | ||
if (ms) { | ||
@@ -113,3 +110,4 @@ handler = useDebounce(handler, wait); | ||
function useWebSocket(url, protocols) { | ||
const ws = new WebSocket(url, protocols); | ||
const supported = isClient && 'WebSocket' in window; | ||
let ws = null; | ||
const messageEvent = ref(null); | ||
@@ -122,34 +120,40 @@ const errorEvent = ref(); | ||
/* istanbul ignore next */ | ||
let lastMessage = ((true !== 'production') && Date.now()) || undefined; | ||
ws.addEventListener("message", x => { | ||
messageEvent.value = x; | ||
data.value = x.data; | ||
// if the messages are to quick, we need to warn | ||
/* istanbul ignore else */ | ||
if ((true !== 'production')) { | ||
if (Date.now() - lastMessage < 2) { | ||
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' + | ||
" you might not get updated of all the messages." + | ||
' Use "ws..addEventListener("message", handler)" instead'); | ||
let lastMessage = ((process.env.NODE_ENV !== 'production') && Date.now()) || undefined; | ||
let send = NO_OP; | ||
let close = NO_OP; | ||
if (supported) { | ||
ws = new WebSocket(url, protocols); | ||
ws.addEventListener("message", x => { | ||
messageEvent.value = x; | ||
data.value = x.data; | ||
// if the messages are to quick, we need to warn | ||
/* istanbul ignore else */ | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
if (Date.now() - lastMessage < 2) { | ||
console.warn('[useWebSocket] message rate is too high, if you are using "data" or "messageEvent"' + | ||
" you might not get updated of all the messages." + | ||
' Use "ws..addEventListener("message", handler)" instead'); | ||
} | ||
lastMessage = Date.now(); | ||
} | ||
lastMessage = Date.now(); | ||
} | ||
}); | ||
ws.addEventListener("error", error => { | ||
errorEvent.value = error; | ||
errored.value = true; | ||
}); | ||
ws.addEventListener("close", () => { | ||
isOpen.value = false; | ||
isClosed.value = true; | ||
}); | ||
ws.addEventListener("open", () => { | ||
isOpen.value = true; | ||
isClosed.value = false; | ||
}); | ||
const send = (data) => ws.send(data); | ||
const close = (code, reason) => { | ||
ws.close(code, reason); | ||
}; | ||
}); | ||
ws.addEventListener("error", error => { | ||
errorEvent.value = error; | ||
errored.value = true; | ||
}); | ||
ws.addEventListener("close", () => { | ||
isOpen.value = false; | ||
isClosed.value = true; | ||
}); | ||
ws.addEventListener("open", () => { | ||
isOpen.value = true; | ||
isClosed.value = false; | ||
}); | ||
send = (data) => ws.send(data); | ||
close = (code, reason) => { | ||
ws.close(code, reason); | ||
}; | ||
} | ||
return { | ||
supported, | ||
ws, | ||
@@ -168,2 +172,3 @@ send, | ||
function useIntersectionObserver(refEl, refOptions) { | ||
const supported = isClient && 'IntersectionObserver' in window; | ||
const wrappedElement = refEl ? wrap(refEl) : undefined; | ||
@@ -184,25 +189,27 @@ const element = wrappedElement && (isElement(wrappedElement.value) || !wrappedElement.value) | ||
let observer = ref(); | ||
watch(options, options => { | ||
if (observer.value) { | ||
observer.value.disconnect(); | ||
} | ||
const opts = (options && | ||
options && { | ||
root: unwrap(options.root), | ||
rootMargin: unwrap(options.rootMargin), | ||
threshold: unwrap(options.threshold) | ||
}) || | ||
undefined; | ||
observer.value = new IntersectionObserver(handling, opts); | ||
const targets = elements.value.map(x => x.target); | ||
targets.forEach(observer.value.observe); | ||
}, { deep: true }); | ||
const observe = (element) => { | ||
if (supported) { | ||
watch(options, options => { | ||
if (observer.value) { | ||
observer.value.disconnect(); | ||
} | ||
const opts = (options && | ||
options && { | ||
root: unwrap(options.root), | ||
rootMargin: unwrap(options.rootMargin), | ||
threshold: unwrap(options.threshold) | ||
}) || | ||
undefined; | ||
observer.value = new IntersectionObserver(handling, opts); | ||
const targets = elements.value.map(x => x.target); | ||
targets.forEach(observer.value.observe); | ||
}, { deep: true }); | ||
} | ||
const observe = supported ? (element) => { | ||
const e = unwrap(element); | ||
observer.value.observe(e); | ||
}; | ||
const unobserve = (element) => { | ||
} : NO_OP; | ||
const unobserve = supported ? (element) => { | ||
const e = unwrap(element); | ||
observer.value.unobserve(e); | ||
}; | ||
} : NO_OP; | ||
const disconnect = () => observer.value.disconnect(); | ||
@@ -228,3 +235,3 @@ // if the element is passed we should add hooks | ||
// if (elements.value.length === 0) { | ||
// (true !== 'production') && console.warn('[IntersectionObserver] no elements provided, did you mount the component?') | ||
// (process.env.NODE_ENV !== 'production') && console.warn('[IntersectionObserver] no elements provided, did you mount the component?') | ||
// return; | ||
@@ -235,2 +242,3 @@ // } | ||
return { | ||
supported, | ||
elements, | ||
@@ -245,6 +253,7 @@ observe, | ||
function useNetworkInformation() { | ||
const connection = navigator.connection || | ||
navigator.mozConnection || | ||
navigator.webkitConnection; | ||
const supported = computed(() => !!connection); | ||
const connection = isClient ? | ||
navigator.connection || | ||
navigator.mozConnection || | ||
navigator.webkitConnection : false; | ||
const supported = !!connection; | ||
const downlink = ref(0); | ||
@@ -273,3 +282,3 @@ const downlinkMax = ref(0); | ||
/* istanbul ignore else */ | ||
if ((true !== 'production')) { | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn("[navigator.connection] not found, networkInformation not available."); | ||
@@ -292,3 +301,3 @@ } | ||
function useOnline() { | ||
const supported = "onLine" in navigator; | ||
const supported = isClient && "onLine" in navigator; | ||
// not sure how to test this :/ | ||
@@ -317,12 +326,17 @@ if (!supported) { | ||
if (!hidden) { | ||
hidden = ref(document.hidden); | ||
hidden = ref(isClient && document.hidden); | ||
} | ||
if (!visibility) { | ||
visibility = ref(document.visibilityState); | ||
document.addEventListener("visibilitychange", () => { | ||
visibility.value = document.visibilityState; | ||
hidden.value = document.hidden; | ||
}, { passive: true } | ||
// true | ||
); | ||
if (isClient) { | ||
visibility = ref(document.visibilityState); | ||
document.addEventListener("visibilitychange", () => { | ||
visibility.value = document.visibilityState; | ||
hidden.value = document.hidden; | ||
}, { passive: true } | ||
// true | ||
); | ||
} | ||
else { | ||
visibility = ref(false); | ||
} | ||
} | ||
@@ -339,11 +353,16 @@ return { | ||
if (!language) { | ||
language = ref(navigator.language); | ||
language = isClient ? ref(navigator.language) : ref(''); | ||
} | ||
if (!languages) { | ||
languages = ref(navigator.languages); | ||
const change = () => { | ||
language.value = navigator.language; | ||
languages.value = navigator.languages; | ||
}; | ||
window.addEventListener('languagechange', change, { passive: true }); | ||
if (isClient) { | ||
languages = ref(navigator.languages); | ||
const change = () => { | ||
language.value = navigator.language; | ||
languages.value = navigator.languages; | ||
}; | ||
window.addEventListener('languagechange', change, { passive: true }); | ||
} | ||
else { | ||
languages = ref([]); | ||
} | ||
} | ||
@@ -356,15 +375,140 @@ return { | ||
function useMatchMedia(query) { | ||
const mediaQueryList = ref(matchMedia(query)); | ||
const matches = ref(mediaQueryList.value.matches); | ||
const process = (e) => { | ||
matches.value = e.matches; | ||
function useBroadcastChannel(name, onBeforeClose) { | ||
const supported = isClient && 'BroadcastChannel' in self; | ||
const data = ref(null); | ||
const messageEvent = ref(null); | ||
const errorEvent = ref(null); | ||
const errored = ref(false); | ||
const isClosed = ref(false); | ||
let send = NO_OP; | ||
let close = NO_OP; | ||
let addListener = NO_OP; | ||
/* istanbul ignore else */ | ||
if (supported) { | ||
const bc = new BroadcastChannel(name); | ||
bc.addEventListener('messageerror', (e) => { | ||
errorEvent.value = e; | ||
errored.value = true; | ||
}, PASSIVE_EV); | ||
bc.addEventListener('message', (ev) => { | ||
messageEvent.value = ev; | ||
data.value = ev.data; | ||
}, PASSIVE_EV); | ||
send = (d) => bc.postMessage(d); | ||
close = () => { | ||
bc.close(); | ||
isClosed.value = true; | ||
}; | ||
addListener = (cb, o) => { | ||
bc.addEventListener('message', cb, o); | ||
onUnmounted(() => bc.removeEventListener('message', cb)); | ||
}; | ||
onUnmounted(() => { | ||
onBeforeClose && onBeforeClose(); | ||
close(); | ||
}); | ||
} | ||
else { | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn('[BroadcastChannel] is not supported'); | ||
} | ||
} | ||
return { | ||
supported, | ||
data, | ||
messageEvent, | ||
errorEvent, | ||
errored, | ||
isClosed, | ||
send, | ||
close, | ||
addListener, | ||
}; | ||
mediaQueryList.value.addEventListener("change", process, { passive: true }); | ||
const remove = () => mediaQueryList.value.removeEventListener("change", process); | ||
onUnmounted(remove); | ||
} | ||
function useGeolocation(options) { | ||
const supported = isClient && !!navigator.geolocation; | ||
// used to check if the execution is lazy | ||
const lazy = ref(options ? options.immediate === false : undefined); | ||
const error = ref(null); | ||
const timestamp = ref(null); | ||
const coords = ref(null); | ||
const highAccuracy = ref(options && options.enableHighAccuracy || null); | ||
// allow manual control on when the geolocation is requested | ||
let refresh = NO_OP; | ||
if (supported) { | ||
const setPosition = (pos) => { | ||
timestamp.value = pos.timestamp; | ||
coords.value = pos.coords; | ||
error.value = null; | ||
}; | ||
const setError = (err) => { | ||
timestamp.value = Date.now(); | ||
coords.value = null; | ||
error.value = err; | ||
}; | ||
const clearWatch = () => lazy.value !== true && watchId && navigator.geolocation.clearWatch(watchId); | ||
let _currentPositionRefresh = () => navigator.geolocation.getCurrentPosition(setPosition, setError, options); | ||
if (lazy.value) { | ||
refresh = () => { | ||
if (lazy.value) { | ||
lazy.value = false; | ||
} | ||
else { | ||
_currentPositionRefresh(); | ||
} | ||
}; | ||
} | ||
else { | ||
// NOTE probably useless?? | ||
refresh = _currentPositionRefresh; | ||
} | ||
let watchId = 0; | ||
onMounted(() => watch([highAccuracy, lazy], (a) => { | ||
clearWatch(); | ||
const enableHighAccuracy = isBoolean(a[0]) ? a[0] : options ? options.enableHighAccuracy : undefined; | ||
watchId = navigator.geolocation.watchPosition(setPosition, setError, options ? { ...options, enableHighAccuracy } : { enableHighAccuracy }); | ||
}, { | ||
lazy: lazy.value | ||
})); | ||
onUnmounted(clearWatch); | ||
} | ||
return { | ||
supported, | ||
refresh, | ||
error, | ||
timestamp, | ||
coords, | ||
highAccuracy | ||
}; | ||
} | ||
function useMatchMedia(query) { | ||
const supported = isClient ? 'matchMedia' in window : false; | ||
let mediaQueryList = undefined; | ||
let matches = undefined; | ||
let remove = NO_OP; | ||
if (supported) { | ||
mediaQueryList = ref(matchMedia(query)); | ||
matches = ref(mediaQueryList.value.matches); | ||
const process = (e) => { | ||
matches.value = e.matches; | ||
}; | ||
mediaQueryList.value.addEventListener("change", process, { passive: true }); | ||
const remove = () => mediaQueryList.value.removeEventListener("change", process); | ||
onUnmounted(remove); | ||
} | ||
else { | ||
/* istanbul ignore else */ | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn('[matchMedia] not supported'); | ||
} | ||
mediaQueryList = ref({}); | ||
matches = ref(false); | ||
} | ||
return { | ||
supported, | ||
mediaQueryList, | ||
remove, | ||
matches | ||
matches, | ||
remove | ||
}; | ||
@@ -397,3 +541,3 @@ } | ||
sorted = sorted.sort((a, b) => b - a); | ||
const resize = () => { | ||
const resize = isClient ? () => { | ||
const width = window.innerWidth; | ||
@@ -410,5 +554,5 @@ let c = undefined; | ||
current.value = c; | ||
}; | ||
} : NO_OP; | ||
const processResize = useDebounce(resize, 10); | ||
const remove = () => window.removeEventListener("resize", processResize); | ||
const remove = isClient ? () => window.removeEventListener("resize", processResize) : NO_OP; | ||
onMounted(() => { | ||
@@ -431,2 +575,168 @@ resize(); | ||
function useSharedRef(name, defaultValue) { | ||
const { addListener, send, close, supported } = useBroadcastChannel(name, () => disconnect()); | ||
const id = Date.now(); | ||
const master = ref(false); | ||
const mind = ref(0 /* HIVE */); | ||
const editable = computed(() => mind.value === 1 /* MASTER */ ? master.value : true); | ||
// who's listening to this broadcast | ||
const targets = ref([]); | ||
const data = ref(defaultValue); | ||
// if the state was updated by an event it sets to true | ||
let updateState = false; | ||
let masterId = undefined; | ||
send({ type: 0 /* INIT */ }); | ||
const ping = () => send({ type: 5 /* PING */, id }); | ||
const disconnect = () => { | ||
if (targets.value.length === 0) | ||
return; | ||
if (master.value) { | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
mind: 1 /* MASTER */, | ||
id: Math.min(...targets.value) | ||
}); | ||
} | ||
send({ | ||
type: 4 /* LEAVE */, | ||
id | ||
}); | ||
}; | ||
const setMind = (t) => { | ||
switch (t) { | ||
case 1 /* MASTER */: { | ||
master.value = true; | ||
break; | ||
} | ||
case 0 /* HIVE */: { | ||
master.value = false; | ||
break; | ||
} | ||
} | ||
mind.value = t; | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
id: id, | ||
mind: mind.value, | ||
}); | ||
}; | ||
addListener((e) => { | ||
switch (e.data.type) { | ||
case 0 /* INIT */: { | ||
send({ | ||
type: 2 /* UPDATE */, | ||
value: data.value, | ||
mind: mind.value | ||
}); | ||
break; | ||
} | ||
case 4 /* LEAVE */: { | ||
const index = targets.value.indexOf(e.data.id); | ||
if (index >= 0) { | ||
targets.value.splice(index, 1); | ||
} | ||
// if master disconnects | ||
if (masterId === e.data.id && targets.value.length > 0) { | ||
send({ | ||
type: 3 /* SET_MIND */, | ||
mind: 1 /* MASTER */, | ||
id: Math.min(id, ...targets.value) | ||
}); | ||
} | ||
break; | ||
} | ||
case 2 /* UPDATE */: { | ||
updateState = true; | ||
data.value = e.data.value; | ||
mind.value = e.data.mind; | ||
break; | ||
} | ||
case 3 /* SET_MIND */: { | ||
mind.value = e.data.mind; | ||
masterId = e.data.mind === 1 /* MASTER */ && e.data.id || undefined; | ||
master.value = masterId === id; | ||
if (master.value) { | ||
targets.value = []; | ||
ping(); | ||
} | ||
break; | ||
} | ||
case 5 /* PING */: { | ||
targets.value = [e.data.id]; | ||
send({ | ||
type: 6 /* PONG */, | ||
id | ||
}); | ||
break; | ||
} | ||
case 6 /* PONG */: { | ||
targets.value.push(e.data.id); | ||
break; | ||
} | ||
} | ||
}, PASSIVE_EV); | ||
ping(); | ||
watch(data, (v, o) => { | ||
if (updateState) { | ||
updateState = false; | ||
return; | ||
} | ||
// mind is set to MASTER and we are not master, we shouldn't update! | ||
if (mind.value === 1 /* MASTER */ && master.value === false) { | ||
updateState = true; | ||
data.value = o; | ||
return; | ||
} | ||
send({ | ||
type: 2 /* UPDATE */, | ||
mind: mind.value, | ||
value: isObject(v) ? { ...v } : v | ||
}); | ||
updateState = false; | ||
}, { deep: true, lazy: true }); | ||
if (isClient) { | ||
window.addEventListener('unload', disconnect, PASSIVE_EV); | ||
} | ||
onUnmounted(() => { | ||
disconnect(); | ||
close(); | ||
}); | ||
return { | ||
supported, | ||
id, | ||
data, | ||
master, | ||
mind, | ||
editable, | ||
targets, | ||
ping, | ||
setMind, | ||
addListener | ||
}; | ||
} | ||
let shared = undefined; | ||
function refShared(defaultValue, id) { | ||
const vm = getCurrentInstance(); | ||
const name = id ? id : vm.$vnode.tag; | ||
/* istanbul ignore else */ | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
if (!shared) { | ||
shared = new Set(); | ||
} | ||
if (shared.has(name)) { | ||
console.warn('[refShared] You can only have one refShared per component, if you need more please assign pass an id refShared(defaultValue, id)'); | ||
} | ||
shared.add(name); | ||
} | ||
const { data, supported } = useSharedRef(name, defaultValue); | ||
/* istanbul ignore next */ | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
if (!supported) { | ||
console.warn('[refShared] is dependent of BroadcastChannel'); | ||
} | ||
} | ||
return data; | ||
} | ||
const STORAGE_TEST_KEY = (process.env.NODE_ENV !== 'production') ? '__storage_test__' : ":$"; | ||
/* istanbul ignore next */ | ||
@@ -445,3 +755,3 @@ function isQuotaExceededError(e, storage) { | ||
// acknowledge QuotaExceededError only if there's something already stored | ||
(storage && storage.length !== 0); | ||
(storage && storage.length !== 0 || false); | ||
} | ||
@@ -451,3 +761,6 @@ // based on https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API | ||
try { | ||
const x = '__storage_test__'; | ||
if (!storage) { | ||
return false; | ||
} | ||
const x = STORAGE_TEST_KEY; | ||
storage.setItem(x, x); | ||
@@ -471,3 +784,3 @@ storage.removeItem(x); | ||
function useWebStorage(type, serializer = JSON, ms = 10) { | ||
const storage = window[type]; | ||
const storage = isClient ? window[type] : undefined; | ||
const supported = storageAvailable(storage); | ||
@@ -477,31 +790,33 @@ const remove = () => storageMap.delete(type); | ||
storageMap = new Map(); | ||
window.addEventListener('storage', (e) => { | ||
if (e.newValue === e.oldValue) { | ||
return; | ||
} | ||
let webStore = storageMap.get('localStorage'); | ||
if (e.storageArea === window.localStorage) { | ||
webStore = storageMap.get('localStorage'); | ||
} | ||
else { | ||
webStore = storageMap.get('sessionStorage'); | ||
} | ||
if (webStore && Object.keys(webStore.$syncKeys).length > 0) { | ||
if (e.key === null) { | ||
webStore.clear(); | ||
if (isClient) { | ||
window.addEventListener('storage', (e) => { | ||
if (e.newValue === e.oldValue) { | ||
return; | ||
} | ||
else if (webStore.$syncKeys[e.key]) { | ||
if (e.newValue === null) { | ||
webStore.removeItem(e.key); | ||
let webStore = storageMap.get('localStorage'); | ||
if (e.storageArea === window.localStorage) { | ||
webStore = storageMap.get('localStorage'); | ||
} | ||
else { | ||
webStore = storageMap.get('sessionStorage'); | ||
} | ||
if (webStore && Object.keys(webStore.$syncKeys).length > 0) { | ||
if (e.key === null) { | ||
webStore.clear(); | ||
} | ||
else { | ||
webStore.updateItem(e.key, e.newValue); | ||
else if (webStore.$syncKeys[e.key]) { | ||
if (e.newValue === null) { | ||
webStore.removeItem(e.key); | ||
} | ||
else { | ||
webStore.updateItem(e.key, e.newValue); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
let store = storageMap.get(type); | ||
let quotaError; | ||
if (supported) { | ||
if (supported && storage) { | ||
if (!store) { | ||
@@ -617,5 +932,6 @@ quotaError = ref(false); | ||
/* istanbul ignore else */ | ||
if ((true !== 'production')) { | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn('[localStorage] is not available'); | ||
} | ||
storage = ref(defaultValue); | ||
} | ||
@@ -639,3 +955,3 @@ return { | ||
/* istanbul ignore else */ | ||
if ((true !== 'production')) { | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
setSync = () => console.warn('sync is not supported, please `useLocalStorage` instead'); | ||
@@ -652,5 +968,6 @@ } | ||
/* istanbul ignore else */ | ||
if ((true !== 'production')) { | ||
if ((process.env.NODE_ENV !== 'production')) { | ||
console.warn('[sessionStorage] is not available'); | ||
} | ||
storage = ref(defaultValue); | ||
} | ||
@@ -676,2 +993,2 @@ return { | ||
export { storageAvailable, useBreakpoint, useEvent, useFetch, useIntersectionObserver, useLanguage, useLocalStorage, useMatchMedia, useNetworkInformation, useOnMouseMove, useOnResize, useOnScroll, useOnline, usePageVisibility, useSessionStorage, useStorage, useWebSocket, useWebStorage }; | ||
export { refShared, storageAvailable, useBreakpoint, useBroadcastChannel, useEvent, useFetch, useGeolocation, useIntersectionObserver, useLanguage, useLocalStorage, useMatchMedia, useNetworkInformation, useOnMouseMove, useOnResize, useOnScroll, useOnline, usePageVisibility, useSessionStorage, useSharedRef, useStorage, useWebSocket, useWebStorage }; |
{ | ||
"name": "@vue-composable/web", | ||
"version": "1.0.0-dev.7", | ||
"version": "1.0.0-dev.8", | ||
"description": "@vue-composable/web", | ||
@@ -37,7 +37,11 @@ "main": "index.js", | ||
"homepage": "https://github.com/pikax/vue-composable/tree/dev/packages/core#readme", | ||
"peerDependencies": { | ||
"@vue/composition-api": "^0.3.4" | ||
}, | ||
"dependencies": { | ||
"@vue-composable/core": "1.0.0-dev.7", | ||
"@vue/composition-api": "^0.3.4", | ||
"@vue-composable/core": "1.0.0-dev.8" | ||
}, | ||
"devDependencies": { | ||
"vue": "^2.6.10" | ||
} | ||
} |
@@ -36,2 +36,3 @@ # @vue-composable/web | ||
- [breakpoint](https://pikax.me/vue-composable/composable/misc/breakpoint) - reactive `breakpoints` based on `window.innerWidth` | ||
- [sharedRef](https://pikax.me/vue-composable/composable/misc/sharedRef) - cross-tab reactive `ref` | ||
@@ -54,2 +55,4 @@ ### Storage | ||
- [Language](https://pikax.me/vue-composable/composable/web/language) - reactive `NavigatorLanguage` | ||
- [BroadcastChannel](https://pikax.me/vue-composable/composable/web/broadcastChannel) - reactive `BroadcastChannel API` | ||
- [Geolocation API](https://pikax.me/vue-composable/composable/web/geolocation) | ||
@@ -56,0 +59,0 @@ ## Contributing |
115196
2
3152
68
1
12
+ Added@vue-composable/core@1.0.0-dev.8(transitive)
- Removed@vue/composition-api@^0.3.4
- Removedvue@^2.6.10
- Removed@vue-composable/core@1.0.0-dev.7(transitive)