-
Notifications
You must be signed in to change notification settings - Fork 79
feat(dialog, modal, popover, sheet): add options prop to customize focus-trap #11453
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
3013a4e
feat(dialog): add options prop to customize focus-trap
jcfranco ee6d923
feat(input-date-picker, input-time-picker, modal, popover, sheet): ad…
jcfranco 5fe3bac
drop focusTrapOptions props
jcfranco a6cf46b
feat(modal, popover, sheet): add `focusTrapOptions` prop
jcfranco b9a9a81
improve focusTrapOptions doc
jcfranco 7a58775
allow passing extraContainers to update container elements method
jcfranco 35fae3f
Merge branch 'jcfranco/add-focus-trap-props-for-container-components'…
jcfranco 08d24cc
roll back input time/date picker using useFocusTrap controller
jcfranco ef87d21
update focusTrapOptions doc
jcfranco 4183632
update focusTrapElements()
jcfranco a85a9c6
merge dev
jcfranco ce0e352
restore input-time-picker disabling of initial focus
jcfranco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,14 +18,6 @@ import { | |
slotChangeGetAssignedElements, | ||
slotChangeHasAssignedElement, | ||
} from "../../utils/dom"; | ||
import { | ||
activateFocusTrap, | ||
connectFocusTrap, | ||
deactivateFocusTrap, | ||
FocusTrap, | ||
FocusTrapComponent, | ||
updateFocusTrapElements, | ||
} from "../../utils/focusTrapComponent"; | ||
import { | ||
componentFocusable, | ||
LoadableComponent, | ||
|
@@ -38,6 +30,7 @@ import { Kind, Scale } from "../interfaces"; | |
import { getIconScale } from "../../utils/component"; | ||
import { logger } from "../../utils/logger"; | ||
import { useT9n } from "../../controllers/useT9n"; | ||
import { ExtendedFocusTrapOptions, useFocusTrap } from "../../controllers/useFocusTrap"; | ||
import T9nStrings from "./assets/t9n/messages.en.json"; | ||
import { CSS, ICONS, SLOTS } from "./resources"; | ||
import { styles } from "./modal.scss"; | ||
|
@@ -61,10 +54,7 @@ let initialDocumentOverflowStyle: string = ""; | |
* @slot secondary - A slot for adding a secondary button. | ||
* @slot back - A slot for adding a back button. | ||
*/ | ||
export class Modal | ||
extends LitElement | ||
implements OpenCloseComponent, FocusTrapComponent, LoadableComponent | ||
{ | ||
export class Modal extends LitElement implements OpenCloseComponent, LoadableComponent { | ||
// #region Static Members | ||
|
||
static override styles = styles; | ||
|
@@ -81,7 +71,21 @@ export class Modal | |
this.updateSizeCssVars(); | ||
}); | ||
|
||
focusTrap: FocusTrap; | ||
focusTrap = useFocusTrap<this>({ | ||
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. ✨🎮 ✅✨ Nice. Brings all the rest of the components up to date! |
||
triggerProp: "open", | ||
focusTrapOptions: { | ||
// scrim closes on click, so we let it take over | ||
clickOutsideDeactivates: false, | ||
escapeDeactivates: (event) => { | ||
if (!event.defaultPrevented && !this.escapeDisabled) { | ||
this.open = false; | ||
event.preventDefault(); | ||
} | ||
|
||
return false; | ||
}, | ||
}, | ||
})(this); | ||
|
||
private ignoreOpenChange = false; | ||
|
||
|
@@ -152,6 +156,16 @@ export class Modal | |
/** When `true`, prevents focus trapping. */ | ||
@property({ reflect: true }) focusTrapDisabled = false; | ||
|
||
/** | ||
* Specifies custom focus trap configuration on the component, where | ||
* | ||
* `"allowOutsideClick`" allows outside clicks, | ||
* `"initialFocus"` enables initial focus, | ||
* `"returnFocusOnDeactivate"` returns focus when not active, and | ||
* `"extraContainers"` specifies additional focusable elements external to the trap (e.g., 3rd-party components appending elements to the document body). | ||
*/ | ||
@property() focusTrapOptions; | ||
|
||
/** Sets the component to always be fullscreen. Overrides `widthScale` and `--calcite-modal-width` / `--calcite-modal-height`. */ | ||
@property({ reflect: true }) fullscreen: boolean; | ||
|
||
|
@@ -230,10 +244,16 @@ export class Modal | |
focusFirstTabbable(this.el); | ||
} | ||
|
||
/** Updates the element(s) that are used within the focus-trap of the component. */ | ||
/** | ||
* Updates the element(s) that are included in the focus-trap of the component. | ||
* | ||
* @param extraContainers - Additional elements to include in the focus trap. This is useful for including elements that may have related parts rendered outside the main focus trapping element. | ||
*/ | ||
@method() | ||
async updateFocusTrapElements(): Promise<void> { | ||
updateFocusTrapElements(this); | ||
async updateFocusTrapElements( | ||
extraContainers?: ExtendedFocusTrapOptions["extraContainers"], | ||
): Promise<void> { | ||
this.focusTrap.updateContainerElements(extraContainers); | ||
} | ||
|
||
// #endregion | ||
|
@@ -265,20 +285,6 @@ export class Modal | |
this.mutationObserver?.observe(this.el, { childList: true, subtree: true }); | ||
this.cssVarObserver?.observe(this.el, { attributeFilter: ["style"] }); | ||
this.updateSizeCssVars(); | ||
connectFocusTrap(this, { | ||
focusTrapOptions: { | ||
// scrim closes on click, so we let it take over | ||
clickOutsideDeactivates: false, | ||
escapeDeactivates: (event) => { | ||
if (!event.defaultPrevented && !this.escapeDisabled) { | ||
this.open = false; | ||
event.preventDefault(); | ||
} | ||
|
||
return false; | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
async load(): Promise<void> { | ||
|
@@ -299,10 +305,6 @@ export class Modal | |
To account for this semantics change, the checks for (this.hasUpdated || value != defaultValue) was added in this method | ||
Please refactor your code to reduce the need for this check. | ||
Docs: https://qawebgis.esri.com/arcgis-components/?path=/docs/lumina-transition-from-stencil--docs#watching-for-property-changes */ | ||
if (changes.has("focusTrapDisabled") && (this.hasUpdated || this.focusTrapDisabled !== false)) { | ||
this.handleFocusTrapDisabled(this.focusTrapDisabled); | ||
} | ||
|
||
if ( | ||
(changes.has("hasBack") && (this.hasUpdated || this.hasBack !== false)) || | ||
(changes.has("hasPrimary") && (this.hasUpdated || this.hasPrimary !== false)) || | ||
|
@@ -324,7 +326,6 @@ export class Modal | |
this.removeOverflowHiddenClass(); | ||
this.mutationObserver?.disconnect(); | ||
this.cssVarObserver?.disconnect(); | ||
deactivateFocusTrap(this); | ||
} | ||
|
||
// #endregion | ||
|
@@ -346,18 +347,6 @@ export class Modal | |
} | ||
}; | ||
|
||
private handleFocusTrapDisabled(focusTrapDisabled: boolean): void { | ||
if (!this.open) { | ||
return; | ||
} | ||
|
||
if (focusTrapDisabled) { | ||
deactivateFocusTrap(this); | ||
} else { | ||
activateFocusTrap(this); | ||
} | ||
} | ||
|
||
private handleHeaderSlotChange(event: Event): void { | ||
this.titleEl = slotChangeGetAssignedElements<HTMLElement>(event)[0]; | ||
} | ||
|
@@ -390,7 +379,7 @@ export class Modal | |
onOpen(): void { | ||
this.transitionEl?.classList.remove(CSS.openingIdle, CSS.openingActive); | ||
this.calciteModalOpen.emit(); | ||
activateFocusTrap(this); | ||
this.focusTrap.activate(); | ||
} | ||
|
||
onBeforeClose(): void { | ||
|
@@ -401,7 +390,7 @@ export class Modal | |
onClose(): void { | ||
this.transitionEl?.classList.remove(CSS.closingIdle, CSS.closingActive); | ||
this.calciteModalClose.emit(); | ||
deactivateFocusTrap(this); | ||
this.focusTrap.deactivate(); | ||
} | ||
|
||
private toggleModal(value: boolean): void { | ||
|
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
nice, this looks good 👍