mirror of
https://github.com/imezx/Warp.git
synced 2025-04-24 23:20:02 +00:00
666 lines
19 KiB
JavaScript
666 lines
19 KiB
JavaScript
'use strict';
|
|
|
|
var shared = require('@vueuse/shared');
|
|
var Schema = require('async-validator');
|
|
var vueDemi = require('vue-demi');
|
|
var axios = require('axios');
|
|
var changeCase$1 = require('change-case');
|
|
var Cookie = require('universal-cookie');
|
|
var drauu = require('drauu');
|
|
var core = require('@vueuse/core');
|
|
var focusTrap = require('focus-trap');
|
|
var Fuse = require('fuse.js');
|
|
var idbKeyval = require('idb-keyval');
|
|
var jwt_decode = require('jwt-decode');
|
|
var nprogress = require('nprogress');
|
|
var QRCode = require('qrcode');
|
|
var Sortable = require('sortablejs');
|
|
|
|
const AsyncValidatorSchema = Schema.default || Schema;
|
|
function useAsyncValidator(value, rules, options = {}) {
|
|
const {
|
|
validateOption = {},
|
|
immediate = true,
|
|
manual = false
|
|
} = options;
|
|
const valueRef = shared.toRef(value);
|
|
const errorInfo = vueDemi.shallowRef(null);
|
|
const isFinished = vueDemi.ref(true);
|
|
const pass = vueDemi.ref(!immediate || manual);
|
|
const errors = vueDemi.computed(() => {
|
|
var _a;
|
|
return ((_a = errorInfo.value) == null ? void 0 : _a.errors) || [];
|
|
});
|
|
const errorFields = vueDemi.computed(() => {
|
|
var _a;
|
|
return ((_a = errorInfo.value) == null ? void 0 : _a.fields) || {};
|
|
});
|
|
const validator = vueDemi.computed(() => new AsyncValidatorSchema(shared.toValue(rules)));
|
|
const execute = async () => {
|
|
isFinished.value = false;
|
|
pass.value = false;
|
|
try {
|
|
await validator.value.validate(valueRef.value, validateOption);
|
|
pass.value = true;
|
|
errorInfo.value = null;
|
|
} catch (err) {
|
|
errorInfo.value = err;
|
|
} finally {
|
|
isFinished.value = true;
|
|
}
|
|
return {
|
|
pass: pass.value,
|
|
errorInfo: errorInfo.value,
|
|
errors: errors.value,
|
|
errorFields: errorFields.value
|
|
};
|
|
};
|
|
if (!manual) {
|
|
vueDemi.watch(
|
|
[valueRef, validator],
|
|
() => execute(),
|
|
{ immediate, deep: true }
|
|
);
|
|
}
|
|
const shell = {
|
|
isFinished,
|
|
pass,
|
|
errors,
|
|
errorInfo,
|
|
errorFields,
|
|
execute
|
|
};
|
|
function waitUntilFinished() {
|
|
return new Promise((resolve, reject) => {
|
|
shared.until(isFinished).toBe(true).then(() => resolve(shell)).catch((error) => reject(error));
|
|
});
|
|
}
|
|
return {
|
|
...shell,
|
|
then(onFulfilled, onRejected) {
|
|
return waitUntilFinished().then(onFulfilled, onRejected);
|
|
}
|
|
};
|
|
}
|
|
|
|
function useAxios(...args) {
|
|
const url = typeof args[0] === "string" ? args[0] : void 0;
|
|
const argsPlaceholder = typeof url === "string" ? 1 : 0;
|
|
const defaultOptions = {
|
|
immediate: !!argsPlaceholder,
|
|
shallow: true
|
|
};
|
|
let defaultConfig = {};
|
|
let instance = axios;
|
|
let options = defaultOptions;
|
|
const isAxiosInstance = (val) => !!(val == null ? void 0 : val.request);
|
|
if (args.length > 0 + argsPlaceholder) {
|
|
if (isAxiosInstance(args[0 + argsPlaceholder]))
|
|
instance = args[0 + argsPlaceholder];
|
|
else
|
|
defaultConfig = args[0 + argsPlaceholder];
|
|
}
|
|
if (args.length > 1 + argsPlaceholder) {
|
|
if (isAxiosInstance(args[1 + argsPlaceholder]))
|
|
instance = args[1 + argsPlaceholder];
|
|
}
|
|
if (args.length === 2 + argsPlaceholder && !isAxiosInstance(args[1 + argsPlaceholder]) || args.length === 3 + argsPlaceholder)
|
|
options = args[args.length - 1] || defaultOptions;
|
|
const {
|
|
initialData,
|
|
shallow,
|
|
onSuccess = shared.noop,
|
|
onError = shared.noop,
|
|
immediate,
|
|
resetOnExecute = false
|
|
} = options;
|
|
const response = vueDemi.shallowRef();
|
|
const data = (shallow ? vueDemi.shallowRef : vueDemi.ref)(initialData);
|
|
const isFinished = vueDemi.ref(false);
|
|
const isLoading = vueDemi.ref(false);
|
|
const isAborted = vueDemi.ref(false);
|
|
const error = vueDemi.shallowRef();
|
|
const cancelTokenSource = axios.CancelToken.source;
|
|
let cancelToken = cancelTokenSource();
|
|
const abort = (message) => {
|
|
if (isFinished.value || !isLoading.value)
|
|
return;
|
|
cancelToken.cancel(message);
|
|
cancelToken = cancelTokenSource();
|
|
isAborted.value = true;
|
|
isLoading.value = false;
|
|
isFinished.value = false;
|
|
};
|
|
const loading = (loading2) => {
|
|
isLoading.value = loading2;
|
|
isFinished.value = !loading2;
|
|
};
|
|
const resetData = () => {
|
|
if (resetOnExecute)
|
|
data.value = initialData;
|
|
};
|
|
const waitUntilFinished = () => new Promise((resolve, reject) => {
|
|
shared.until(isFinished).toBe(true).then(() => error.value ? reject(error.value) : resolve(result));
|
|
});
|
|
const promise = {
|
|
then: (...args2) => waitUntilFinished().then(...args2),
|
|
catch: (...args2) => waitUntilFinished().catch(...args2)
|
|
};
|
|
let executeCounter = 0;
|
|
const execute = (executeUrl = url, config = {}) => {
|
|
error.value = void 0;
|
|
const _url = typeof executeUrl === "string" ? executeUrl : url != null ? url : config.url;
|
|
if (_url === void 0) {
|
|
error.value = new axios.AxiosError(axios.AxiosError.ERR_INVALID_URL);
|
|
isFinished.value = true;
|
|
return promise;
|
|
}
|
|
resetData();
|
|
abort();
|
|
loading(true);
|
|
executeCounter += 1;
|
|
const currentExecuteCounter = executeCounter;
|
|
isAborted.value = false;
|
|
instance(_url, { ...defaultConfig, ...typeof executeUrl === "object" ? executeUrl : config, cancelToken: cancelToken.token }).then((r) => {
|
|
if (isAborted.value)
|
|
return;
|
|
response.value = r;
|
|
const result2 = r.data;
|
|
data.value = result2;
|
|
onSuccess(result2);
|
|
}).catch((e) => {
|
|
error.value = e;
|
|
onError(e);
|
|
}).finally(() => {
|
|
var _a;
|
|
(_a = options.onFinish) == null ? void 0 : _a.call(options);
|
|
if (currentExecuteCounter === executeCounter)
|
|
loading(false);
|
|
});
|
|
return promise;
|
|
};
|
|
if (immediate && url)
|
|
execute();
|
|
const result = {
|
|
response,
|
|
data,
|
|
error,
|
|
isFinished,
|
|
isLoading,
|
|
cancel: abort,
|
|
isAborted,
|
|
isCanceled: isAborted,
|
|
abort,
|
|
execute
|
|
};
|
|
return {
|
|
...result,
|
|
...promise
|
|
};
|
|
}
|
|
|
|
var changeCase = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
camelCase: changeCase$1.camelCase,
|
|
capitalCase: changeCase$1.capitalCase,
|
|
constantCase: changeCase$1.constantCase,
|
|
dotCase: changeCase$1.dotCase,
|
|
headerCase: changeCase$1.headerCase,
|
|
noCase: changeCase$1.noCase,
|
|
paramCase: changeCase$1.paramCase,
|
|
pascalCase: changeCase$1.pascalCase,
|
|
pathCase: changeCase$1.pathCase,
|
|
sentenceCase: changeCase$1.sentenceCase,
|
|
snakeCase: changeCase$1.snakeCase
|
|
});
|
|
|
|
function useChangeCase(input, type, options) {
|
|
if (typeof input === "function")
|
|
return vueDemi.computed(() => changeCase[type](shared.toValue(input), options));
|
|
const text = vueDemi.ref(input);
|
|
return vueDemi.computed({
|
|
get() {
|
|
return changeCase[type](text.value, options);
|
|
},
|
|
set(value) {
|
|
text.value = value;
|
|
}
|
|
});
|
|
}
|
|
|
|
function createCookies(req) {
|
|
const universalCookie = new Cookie(req ? req.headers.cookie : null);
|
|
return (dependencies, { doNotParse = false, autoUpdateDependencies = false } = {}) => useCookies(dependencies, { doNotParse, autoUpdateDependencies }, universalCookie);
|
|
}
|
|
function useCookies(dependencies, { doNotParse = false, autoUpdateDependencies = false } = {}, cookies = new Cookie()) {
|
|
const watchingDependencies = autoUpdateDependencies ? [...dependencies || []] : dependencies;
|
|
let previousCookies = cookies.getAll({ doNotParse: true });
|
|
const touches = vueDemi.ref(0);
|
|
const onChange = () => {
|
|
const newCookies = cookies.getAll({ doNotParse: true });
|
|
if (shouldUpdate(
|
|
watchingDependencies || null,
|
|
newCookies,
|
|
previousCookies
|
|
))
|
|
touches.value++;
|
|
previousCookies = newCookies;
|
|
};
|
|
cookies.addChangeListener(onChange);
|
|
shared.tryOnScopeDispose(() => {
|
|
cookies.removeChangeListener(onChange);
|
|
});
|
|
return {
|
|
/**
|
|
* Reactive get cookie by name. If **autoUpdateDependencies = true** then it will update watching dependencies
|
|
*/
|
|
get: (...args) => {
|
|
if (autoUpdateDependencies && watchingDependencies && !watchingDependencies.includes(args[0]))
|
|
watchingDependencies.push(args[0]);
|
|
touches.value;
|
|
return cookies.get(args[0], { doNotParse, ...args[1] });
|
|
},
|
|
/**
|
|
* Reactive get all cookies
|
|
*/
|
|
getAll: (...args) => {
|
|
touches.value;
|
|
return cookies.getAll({ doNotParse, ...args[0] });
|
|
},
|
|
set: (...args) => cookies.set(...args),
|
|
remove: (...args) => cookies.remove(...args),
|
|
addChangeListener: (...args) => cookies.addChangeListener(...args),
|
|
removeChangeListener: (...args) => cookies.removeChangeListener(...args)
|
|
};
|
|
}
|
|
function shouldUpdate(dependencies, newCookies, oldCookies) {
|
|
if (!dependencies)
|
|
return true;
|
|
for (const dependency of dependencies) {
|
|
if (newCookies[dependency] !== oldCookies[dependency])
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function useDrauu(target, options) {
|
|
const drauuInstance = vueDemi.ref();
|
|
let disposables = [];
|
|
const onChangedHook = core.createEventHook();
|
|
const onCanceledHook = core.createEventHook();
|
|
const onCommittedHook = core.createEventHook();
|
|
const onStartHook = core.createEventHook();
|
|
const onEndHook = core.createEventHook();
|
|
const canUndo = vueDemi.ref(false);
|
|
const canRedo = vueDemi.ref(false);
|
|
const altPressed = vueDemi.ref(false);
|
|
const shiftPressed = vueDemi.ref(false);
|
|
const brush = vueDemi.ref({
|
|
color: "black",
|
|
size: 3,
|
|
arrowEnd: false,
|
|
cornerRadius: 0,
|
|
dasharray: void 0,
|
|
fill: "transparent",
|
|
mode: "draw",
|
|
...options == null ? void 0 : options.brush
|
|
});
|
|
vueDemi.watch(brush, () => {
|
|
const instance = drauuInstance.value;
|
|
if (instance) {
|
|
instance.brush = brush.value;
|
|
instance.mode = brush.value.mode;
|
|
}
|
|
}, { deep: true });
|
|
const undo = () => {
|
|
var _a;
|
|
return (_a = drauuInstance.value) == null ? void 0 : _a.undo();
|
|
};
|
|
const redo = () => {
|
|
var _a;
|
|
return (_a = drauuInstance.value) == null ? void 0 : _a.redo();
|
|
};
|
|
const clear = () => {
|
|
var _a;
|
|
return (_a = drauuInstance.value) == null ? void 0 : _a.clear();
|
|
};
|
|
const cancel = () => {
|
|
var _a;
|
|
return (_a = drauuInstance.value) == null ? void 0 : _a.cancel();
|
|
};
|
|
const load = (svg) => {
|
|
var _a;
|
|
return (_a = drauuInstance.value) == null ? void 0 : _a.load(svg);
|
|
};
|
|
const dump = () => {
|
|
var _a;
|
|
return (_a = drauuInstance.value) == null ? void 0 : _a.dump();
|
|
};
|
|
const cleanup = () => {
|
|
var _a;
|
|
disposables.forEach((dispose) => dispose());
|
|
(_a = drauuInstance.value) == null ? void 0 : _a.unmount();
|
|
};
|
|
const syncStatus = () => {
|
|
if (drauuInstance.value) {
|
|
canUndo.value = drauuInstance.value.canUndo();
|
|
canRedo.value = drauuInstance.value.canRedo();
|
|
altPressed.value = drauuInstance.value.altPressed;
|
|
shiftPressed.value = drauuInstance.value.shiftPressed;
|
|
}
|
|
};
|
|
vueDemi.watch(
|
|
() => core.unrefElement(target),
|
|
(el) => {
|
|
if (!el || typeof SVGSVGElement === "undefined" || !(el instanceof SVGSVGElement))
|
|
return;
|
|
if (drauuInstance.value)
|
|
cleanup();
|
|
drauuInstance.value = drauu.createDrauu({ el, ...options });
|
|
syncStatus();
|
|
disposables = [
|
|
drauuInstance.value.on("canceled", () => onCanceledHook.trigger()),
|
|
drauuInstance.value.on("committed", (node) => onCommittedHook.trigger(node)),
|
|
drauuInstance.value.on("start", () => onStartHook.trigger()),
|
|
drauuInstance.value.on("end", () => onEndHook.trigger()),
|
|
drauuInstance.value.on("changed", () => {
|
|
syncStatus();
|
|
onChangedHook.trigger();
|
|
})
|
|
];
|
|
},
|
|
{ flush: "post" }
|
|
);
|
|
shared.tryOnScopeDispose(() => cleanup());
|
|
return {
|
|
drauuInstance,
|
|
load,
|
|
dump,
|
|
clear,
|
|
cancel,
|
|
undo,
|
|
redo,
|
|
canUndo,
|
|
canRedo,
|
|
brush,
|
|
onChanged: onChangedHook.on,
|
|
onCommitted: onCommittedHook.on,
|
|
onStart: onStartHook.on,
|
|
onEnd: onEndHook.on,
|
|
onCanceled: onCanceledHook.on
|
|
};
|
|
}
|
|
|
|
function useFocusTrap(target, options = {}) {
|
|
let trap;
|
|
const { immediate, ...focusTrapOptions } = options;
|
|
const hasFocus = vueDemi.ref(false);
|
|
const isPaused = vueDemi.ref(false);
|
|
const activate = (opts) => trap && trap.activate(opts);
|
|
const deactivate = (opts) => trap && trap.deactivate(opts);
|
|
const pause = () => {
|
|
if (trap) {
|
|
trap.pause();
|
|
isPaused.value = true;
|
|
}
|
|
};
|
|
const unpause = () => {
|
|
if (trap) {
|
|
trap.unpause();
|
|
isPaused.value = false;
|
|
}
|
|
};
|
|
vueDemi.watch(
|
|
() => core.unrefElement(target),
|
|
(el) => {
|
|
if (!el)
|
|
return;
|
|
trap = focusTrap.createFocusTrap(el, {
|
|
...focusTrapOptions,
|
|
onActivate() {
|
|
hasFocus.value = true;
|
|
if (options.onActivate)
|
|
options.onActivate();
|
|
},
|
|
onDeactivate() {
|
|
hasFocus.value = false;
|
|
if (options.onDeactivate)
|
|
options.onDeactivate();
|
|
}
|
|
});
|
|
if (immediate)
|
|
activate();
|
|
},
|
|
{ flush: "post" }
|
|
);
|
|
core.tryOnScopeDispose(() => deactivate());
|
|
return {
|
|
hasFocus,
|
|
isPaused,
|
|
activate,
|
|
deactivate,
|
|
pause,
|
|
unpause
|
|
};
|
|
}
|
|
|
|
function useFuse(search, data, options) {
|
|
const createFuse = () => {
|
|
var _a, _b;
|
|
return new Fuse(
|
|
(_a = shared.toValue(data)) != null ? _a : [],
|
|
(_b = shared.toValue(options)) == null ? void 0 : _b.fuseOptions
|
|
);
|
|
};
|
|
const fuse = vueDemi.ref(createFuse());
|
|
vueDemi.watch(
|
|
() => {
|
|
var _a;
|
|
return (_a = shared.toValue(options)) == null ? void 0 : _a.fuseOptions;
|
|
},
|
|
() => {
|
|
fuse.value = createFuse();
|
|
},
|
|
{ deep: true }
|
|
);
|
|
vueDemi.watch(
|
|
() => shared.toValue(data),
|
|
(newData) => {
|
|
fuse.value.setCollection(newData);
|
|
},
|
|
{ deep: true }
|
|
);
|
|
const results = vueDemi.computed(() => {
|
|
const resolved = shared.toValue(options);
|
|
if ((resolved == null ? void 0 : resolved.matchAllWhenSearchEmpty) && !shared.toValue(search))
|
|
return shared.toValue(data).map((item, index) => ({ item, refIndex: index }));
|
|
const limit = resolved == null ? void 0 : resolved.resultLimit;
|
|
return fuse.value.search(shared.toValue(search), limit ? { limit } : void 0);
|
|
});
|
|
return {
|
|
fuse,
|
|
results
|
|
};
|
|
}
|
|
|
|
function useIDBKeyval(key, initialValue, options = {}) {
|
|
const {
|
|
flush = "pre",
|
|
deep = true,
|
|
shallow = false,
|
|
onError = (e) => {
|
|
console.error(e);
|
|
},
|
|
writeDefaults = true
|
|
} = options;
|
|
const isFinished = vueDemi.ref(false);
|
|
const data = (shallow ? vueDemi.shallowRef : vueDemi.ref)(initialValue);
|
|
const rawInit = shared.toValue(initialValue);
|
|
async function read() {
|
|
try {
|
|
const rawValue = await idbKeyval.get(key);
|
|
if (rawValue === void 0) {
|
|
if (rawInit !== void 0 && rawInit !== null && writeDefaults)
|
|
await idbKeyval.set(key, rawInit);
|
|
} else {
|
|
data.value = rawValue;
|
|
}
|
|
} catch (e) {
|
|
onError(e);
|
|
}
|
|
isFinished.value = true;
|
|
}
|
|
read();
|
|
async function write() {
|
|
try {
|
|
if (data.value == null) {
|
|
await idbKeyval.del(key);
|
|
} else {
|
|
if (Array.isArray(data.value))
|
|
await idbKeyval.update(key, () => JSON.parse(JSON.stringify(data.value)));
|
|
else if (typeof data.value === "object")
|
|
await idbKeyval.update(key, () => ({ ...data.value }));
|
|
else
|
|
await idbKeyval.update(key, () => data.value);
|
|
}
|
|
} catch (e) {
|
|
onError(e);
|
|
}
|
|
}
|
|
const {
|
|
pause: pauseWatch,
|
|
resume: resumeWatch
|
|
} = core.watchPausable(data, () => write(), { flush, deep });
|
|
async function setData(value) {
|
|
pauseWatch();
|
|
data.value = value;
|
|
await write();
|
|
resumeWatch();
|
|
}
|
|
return {
|
|
set: setData,
|
|
isFinished,
|
|
data
|
|
};
|
|
}
|
|
|
|
function useJwt(encodedJwt, options = {}) {
|
|
const {
|
|
onError,
|
|
fallbackValue = null
|
|
} = options;
|
|
const decodeWithFallback = (encodedJwt2, options2) => {
|
|
try {
|
|
return jwt_decode(encodedJwt2, options2);
|
|
} catch (err) {
|
|
onError == null ? void 0 : onError(err);
|
|
return fallbackValue;
|
|
}
|
|
};
|
|
const header = vueDemi.computed(() => decodeWithFallback(shared.toValue(encodedJwt), { header: true }));
|
|
const payload = vueDemi.computed(() => decodeWithFallback(shared.toValue(encodedJwt)));
|
|
return {
|
|
header,
|
|
payload
|
|
};
|
|
}
|
|
|
|
function useNProgress(currentProgress = null, options) {
|
|
const progress = vueDemi.ref(currentProgress);
|
|
const isLoading = vueDemi.computed({
|
|
set: (load) => load ? nprogress.start() : nprogress.done(),
|
|
get: () => typeof progress.value === "number" && progress.value < 1
|
|
});
|
|
if (options)
|
|
nprogress.configure(options);
|
|
const setProgress = nprogress.set;
|
|
nprogress.set = (n) => {
|
|
progress.value = n;
|
|
return setProgress.call(nprogress, n);
|
|
};
|
|
vueDemi.watchEffect(() => {
|
|
if (typeof progress.value === "number" && shared.isClient)
|
|
setProgress.call(nprogress, progress.value);
|
|
});
|
|
shared.tryOnScopeDispose(nprogress.remove);
|
|
return {
|
|
isLoading,
|
|
progress,
|
|
start: nprogress.start,
|
|
done: nprogress.done,
|
|
remove: () => {
|
|
progress.value = null;
|
|
nprogress.remove();
|
|
}
|
|
};
|
|
}
|
|
|
|
function useQRCode(text, options) {
|
|
const src = shared.toRef(text);
|
|
const result = vueDemi.ref("");
|
|
vueDemi.watch(
|
|
src,
|
|
async (value) => {
|
|
if (src.value && shared.isClient)
|
|
result.value = await QRCode.toDataURL(value, options);
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
return result;
|
|
}
|
|
|
|
function useSortable(el, list, options = {}) {
|
|
let sortable;
|
|
const { document = core.defaultDocument, ...resetOptions } = options;
|
|
const defaultOptions = {
|
|
onUpdate: (e) => {
|
|
moveArrayElement(list, e.oldIndex, e.newIndex);
|
|
}
|
|
};
|
|
const start = () => {
|
|
const target = typeof el === "string" ? document == null ? void 0 : document.querySelector(el) : core.unrefElement(el);
|
|
if (!target || sortable !== void 0)
|
|
return;
|
|
sortable = new Sortable(target, { ...defaultOptions, ...resetOptions });
|
|
};
|
|
const stop = () => {
|
|
sortable == null ? void 0 : sortable.destroy();
|
|
sortable = void 0;
|
|
};
|
|
const option = (name, value) => {
|
|
if (value !== void 0)
|
|
sortable == null ? void 0 : sortable.option(name, value);
|
|
else
|
|
return sortable == null ? void 0 : sortable.option(name);
|
|
};
|
|
core.tryOnMounted(start);
|
|
core.tryOnScopeDispose(stop);
|
|
return { stop, start, option };
|
|
}
|
|
function moveArrayElement(list, from, to) {
|
|
const _valueIsRef = vueDemi.isRef(list);
|
|
const array = _valueIsRef ? [...core.toValue(list)] : core.toValue(list);
|
|
if (to >= 0 && to < array.length) {
|
|
const element = array.splice(from, 1)[0];
|
|
vueDemi.nextTick(() => {
|
|
array.splice(to, 0, element);
|
|
if (_valueIsRef)
|
|
list.value = array;
|
|
});
|
|
}
|
|
}
|
|
|
|
exports.createCookies = createCookies;
|
|
exports.moveArrayElement = moveArrayElement;
|
|
exports.useAsyncValidator = useAsyncValidator;
|
|
exports.useAxios = useAxios;
|
|
exports.useChangeCase = useChangeCase;
|
|
exports.useCookies = useCookies;
|
|
exports.useDrauu = useDrauu;
|
|
exports.useFocusTrap = useFocusTrap;
|
|
exports.useFuse = useFuse;
|
|
exports.useIDBKeyval = useIDBKeyval;
|
|
exports.useJwt = useJwt;
|
|
exports.useNProgress = useNProgress;
|
|
exports.useQRCode = useQRCode;
|
|
exports.useSortable = useSortable;
|