diff --git a/composables/useFocusTrap.js b/composables/useFocusTrap.js
new file mode 100644
index 000000000..93ac9cfc1
--- /dev/null
+++ b/composables/useFocusTrap.js
@@ -0,0 +1,42 @@
+/**
+ * @see https://www.telerik.com/blogs/how-to-trap-focus-modal-vue-3
+ */
+import { customRef } from "vue";
+import { createFocusTrap } from "focus-trap";
+
+const useFocusTrap = focusTrapArgs => {
+ const trapRef = customRef((track, trigger) => {
+ let $trapEl = null;
+ return {
+ get() {
+ track();
+ return $trapEl;
+ },
+ set(value) {
+ $trapEl = value;
+ value ? initFocusTrap(focusTrapArgs) : clearFocusTrap();
+ trigger();
+ },
+ };
+ });
+
+ let trap = null;
+ const initFocusTrap = focusTrapArgs => {
+ if (!trapRef.value) return;
+ trap = createFocusTrap(trapRef.value, focusTrapArgs);
+ trap.activate();
+ };
+
+ const clearFocusTrap = () => {
+ trap?.deactivate();
+ trap = null;
+ };
+
+ return {
+ trapRef,
+ initFocusTrap,
+ clearFocusTrap,
+ };
+};
+
+export default useFocusTrap;
diff --git a/layouts/default.vue b/layouts/default.vue
new file mode 100644
index 000000000..729f173ed
--- /dev/null
+++ b/layouts/default.vue
@@ -0,0 +1,9 @@
+
+
+
+ Test Vue Components
+ Test Web Components
+
+
+
+
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 9abc4e160..e69f73d61 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -37,6 +37,10 @@ export default defineNuxtConfig({
name: 'ConnectKeyActions',
path: '@/components/KeyActions.ce',
},
+ {
+ name: 'ConnectModals',
+ path: '@/components/Modals.ce',
+ },
{
name: 'ConnectUserProfile',
path: '@/components/UserProfile.ce',
diff --git a/package-lock.json b/package-lock.json
index 3b89c2a39..8c734e03b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2940,6 +2940,14 @@
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
},
+ "focus-trap": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.4.3.tgz",
+ "integrity": "sha512-BgSSbK4GPnS2VbtZ50VtOv1Sti6DIkj3+LkVjiWMNjLeAp1SH1UlLx3ULu/DCu4vq5R4/uvTm+zrvsMsuYmGLg==",
+ "requires": {
+ "tabbable": "^6.1.2"
+ }
+ },
"follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
@@ -6341,6 +6349,11 @@
}
}
},
+ "tabbable": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.1.2.tgz",
+ "integrity": "sha512-qCN98uP7i9z0fIS4amQ5zbGBOq+OSigYeGvPy7NDk8Y9yncqDZ9pRPgfsc2PJIVM9RrJj7GIfuRgmjoUU9zTHQ=="
+ },
"tailwind-config-viewer": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/tailwind-config-viewer/-/tailwind-config-viewer-1.7.2.tgz",
diff --git a/package.json b/package.json
index e6c5f69a1..63880b684 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,8 @@
"@pinia/nuxt": "^0.4.11",
"@vueuse/components": "^10.1.2",
"crypto-js": "^4.1.1",
- "dayjs": "^1.11.7"
+ "dayjs": "^1.11.7",
+ "focus-trap": "^7.4.3"
},
"overrides": {
"vue": "latest"
diff --git a/pages/index.vue b/pages/index.vue
new file mode 100644
index 000000000..308686789
--- /dev/null
+++ b/pages/index.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
Vue Components
+
UserProfileCe
+
+
+
DownloadApiLogsCe
+
+
+
AuthCe
+
+
+
KeyActionsCe
+
+
+
WanIpCheckCe
+
+
+
ModalsCe
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/webComponents.vue b/pages/webComponents.vue
new file mode 100644
index 000000000..3c31577ad
--- /dev/null
+++ b/pages/webComponents.vue
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
Web Components
+ UserProfileCe
+
+
+ DownloadApiLogsCe
+
+
+ AuthCe
+
+
+ KeyActionsCe
+
+
+ WanIpCheckCe
+
+
+ ModalsCe
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/store/callback.ts b/store/callback.ts
index 9401f3ed2..5661bae42 100644
--- a/store/callback.ts
+++ b/store/callback.ts
@@ -18,6 +18,7 @@ export const useCallbackStore = defineStore('callback', () => {
// const encryptKey = config.public.callbackKey;
const encryptKey = 'Uyv2o8e*FiQe8VeLekTqyX6Z*8XonB';
// state
+ const currentUrl = ref();
const callbackFeedbackVisible = ref
(false);
const decryptedData = ref();
const encryptedMessage = ref('');
@@ -40,8 +41,8 @@ export const useCallbackStore = defineStore('callback', () => {
const watcher = () => {
console.debug('[watcher]');
const currentUrl = new URL(window.location);
- console.debug('[watcher]', currentUrl);
const callbackValue = currentUrl.searchParams.get('data');
+ console.debug('[watcher]', { callbackValue });
if (!callbackValue) {
return console.debug('[watcher] no callback to handle');
}
@@ -66,15 +67,23 @@ export const useCallbackStore = defineStore('callback', () => {
}
};
- const hide = () => callbackFeedbackVisible.value = false;
- const show = () => callbackFeedbackVisible.value = true;
+ const hide = () => {
+ console.debug('[hide]');
+ callbackFeedbackVisible.value = false;
+ };
+ const show = () => {
+ console.debug('[show]');
+ callbackFeedbackVisible.value = true;
+ }
const toggle = useToggle(callbackFeedbackVisible);
- /**
- * @todo consider removing query string once actions are done
- */
watch(callbackFeedbackVisible, (newVal, _oldVal) => {
- console.debug('[callbackFeedbackVisible]', newVal, _oldVal);
+ console.debug('[callbackFeedbackVisible]', newVal);
+ // removing query string once actions are done so users can't refresh the page and go through the same actions
+ if (newVal === false) {
+ console.debug('[callbackFeedbackVisible] push history w/o query');
+ window.history.pushState(null, '', window.location.pathname);
+ }
});
return {