-
Notifications
You must be signed in to change notification settings - Fork 86
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add KFocusTrap and use it in KModal #799
Changes from 21 commits
18ba260
a37e568
1a30127
dfc92e4
d2fe767
4ce8852
c324310
83e8312
dbec4b6
fb45ecc
bf0c943
02015c7
2aaed51
975b029
1639251
ec0e778
c142e14
c831113
ee9c735
0b16091
db8facf
d68e59a
c7bbe35
46fa2a3
9086148
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,3 +21,4 @@ docs/jsdocs.js | |
# IDE | ||
.idea | ||
kolibri-design-system.iml | ||
docs/pages/playground.vue |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<template> | ||
|
||
<DocsPageTemplate apiDocs> | ||
|
||
<!-- Overview Section --> | ||
<DocsPageSection title="Overview" anchor="#overview"> | ||
<p> | ||
The <code>KFocusTrap</code> component ensures that keyboard focus is trapped within a specific region of the page. | ||
</p> | ||
<p> | ||
When the <code>disabled</code> prop is set to <code>true</code>, focus trapping is disabled, allowing normal tab behavior. The focus trap moves the focus between the first and last elements in the slot content. | ||
</p> | ||
|
||
</DocsPageSection> | ||
|
||
|
||
</DocsPageTemplate> | ||
|
||
</template> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
<template> | ||
|
||
<div> | ||
<!-- Focus trap starting point. If not disabled, focuses this first when tabbing. --> | ||
<div | ||
v-if="!disabled" | ||
class="focus-trap-first" | ||
tabindex="0" | ||
@focus="handleFirstTrapFocus" | ||
></div> | ||
|
||
<!-- Default slot where the focusable content will be rendered --> | ||
<slot></slot> | ||
|
||
<!-- Focus trap ending point. If not disabled, focuses this last when tabbing. --> | ||
<div | ||
v-if="!disabled" | ||
class="focus-trap-last" | ||
tabindex="0" | ||
@focus="handleLastTrapFocus" | ||
></div> | ||
</div> | ||
|
||
</template> | ||
|
||
<script> | ||
lokesh-sagi125 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* This component ensures that focus moves between the first element | ||
* and the last element of content provided by the default slot. | ||
* In disabled state, it only renders whatever has been passed | ||
* to the default slot, and doesn't add any focus trap behavior, | ||
* allowing for flexible use from parent components. | ||
*/ | ||
export default { | ||
name: 'KFocusTrap', | ||
props: { | ||
/** | ||
* Disables the focus trap when set to `true`. Focus will behave normally. | ||
* @type {Boolean} | ||
* @default false | ||
*/ | ||
disabled: { | ||
type: Boolean, | ||
required: false, | ||
default: false, | ||
}, | ||
}, | ||
data() { | ||
return { | ||
isTrapActive: false, // Tracks whether the focus trap is currently active | ||
}; | ||
}, | ||
methods: { | ||
/** | ||
* Called when the first focus trap element receives focus. | ||
* If the trap is not yet active, redirects focus to the first element. | ||
* Otherwise, redirects focus to the last element to enforce the loop. | ||
* @param {Event} e - Focus event | ||
*/ | ||
handleFirstTrapFocus(e) { | ||
e.stopPropagation(); | ||
if (!this.isTrapActive) { | ||
this.focusFirstEl(); | ||
this.isTrapActive = true; | ||
} else { | ||
this.focusLastEl(); | ||
} | ||
}, | ||
/** | ||
* Called when the last focus trap element receives focus. | ||
* Redirects focus to the first element, ensuring focus remains within the trap. | ||
* @param {Event} e - Focus event | ||
*/ | ||
handleLastTrapFocus(e) { | ||
e.stopPropagation(); | ||
this.focusFirstEl(); | ||
}, | ||
/** | ||
* Emits an event to notify the parent component to focus the first element. | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description for the |
||
focusFirstEl() { | ||
this.$emit('shouldFocusFirstEl'); | ||
}, | ||
/** | ||
* Emits an event to notify the parent component to focus the last element. | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description for the |
||
focusLastEl() { | ||
this.$emit('shouldFocusLastEl'); | ||
}, | ||
/** | ||
* @public | ||
* Reset the next focus to the first focus element | ||
*/ | ||
reset() { | ||
this.isTrapActive = false; | ||
}, | ||
}, | ||
}; | ||
|
||
</script> | ||
|
||
<style scoped> | ||
lokesh-sagi125 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
.focus-trap-first, | ||
.focus-trap-last { | ||
outline: none; /* Prevents focus outline */ | ||
} | ||
</style> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the below so that the description for the slot is added in the component's documentation. I've added
@slot
.