Skip to content

Commit

Permalink
fix(modal): do autofocus on content change
Browse files Browse the repository at this point in the history
The autofocus feature does not work when the content of a modal or flyout is loaded/changed after the modal is shown (and contains an input after the change)
  • Loading branch information
lubber-de authored Jan 16, 2023
1 parent 338898a commit 1373e46
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 13 deletions.
66 changes: 60 additions & 6 deletions src/definitions/modules/flyout.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,11 @@
resize: function () {
module.setup.heights();
},
focus: function () {
if (module.is.visible() && settings.autofocus && settings.dimPage) {
requestAnimationFrame(module.set.autofocus);
}
},
clickaway: function (event) {
if (settings.closable) {
var
Expand Down Expand Up @@ -357,6 +362,9 @@
$closeIcon
.on('keyup' + elementNamespace, module.event.closeKeyUp)
;
$window
.on('focus' + elementNamespace, module.event.focus)
;
},
clickaway: function () {
module.verbose('Adding clickaway events to context', $context);
Expand Down Expand Up @@ -477,9 +485,42 @@
observeChanges: function () {
if ('MutationObserver' in window) {
observer = new MutationObserver(function (mutations) {
module.refreshInputs();
var collectNodes = function (parent) {
var nodes = [];
for (var c = 0, cl = parent.length; c < cl; c++) {
Array.prototype.push.apply(nodes, collectNodes(parent[c].childNodes));
nodes.push(parent[c]);
}

return nodes;
},
shouldRefreshInputs = false
;
mutations.every(function (mutation) {
if (mutation.type === 'attributes') {
if (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input')) {
shouldRefreshInputs = true;
}
} else {
// mutationobserver only provides the parent nodes
// so let's collect all childs as well to find nested inputs
var $addedInputs = $(collectNodes(mutation.addedNodes)).filter('a[href], [tabindex], :input:enabled').filter(':visible'),
$removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
if ($addedInputs.length > 0 || $removedInputs.length > 0) {
shouldRefreshInputs = true;
}
}

return !shouldRefreshInputs;
});

if (shouldRefreshInputs) {
module.refreshInputs();
}
});
observer.observe(element, {
attributeFilter: ['class', 'disabled'],
attributes: true,
childList: true,
subtree: true,
});
Expand Down Expand Up @@ -508,15 +549,23 @@
if (!settings.dimPage) {
return;
}
$inputs = $module.find('[tabindex], :input').filter(':visible').filter(function () {
$inputs = $module.find('[tabindex], :input:enabled').filter(':visible').filter(function () {
return $(this).closest('.disabled').length === 0;
});
$module.removeAttr('tabindex');
if ($inputs.length === 0) {
$inputs = $module;
$module.attr('tabindex', -1);
}
$inputs.first()
.on('keydown' + elementNamespace, module.event.inputKeyDown.first)
;
$inputs.last()
.on('keydown' + elementNamespace, module.event.inputKeyDown.last)
;
if (settings.autofocus && $inputs.filter(':focus').length === 0) {
module.set.autofocus();
}
},

setup: {
Expand Down Expand Up @@ -611,9 +660,6 @@
}
module.save.focus();
module.refreshInputs();
if (settings.autofocus) {
module.set.autofocus();
}
});
settings.onChange.call(element);
} else {
Expand Down Expand Up @@ -788,10 +834,18 @@
autofocus: function () {
var
$autofocus = $inputs.filter('[autofocus]'),
$rawInputs = $inputs.filter(':input'),
$input = $autofocus.length > 0
? $autofocus.first()
: ($inputs.length > 1 ? $inputs.filter(':not(i.close)') : $inputs).first()
: ($rawInputs.length > 0
? $rawInputs
: $inputs.filter(':not(i.close)')
).first()
;
// check if only the close icon is remaining
if ($input.length === 0 && $inputs.length > 0) {
$input = $inputs.first();
}
if ($input.length > 0) {
$input.trigger('focus');
}
Expand Down
1 change: 1 addition & 0 deletions src/definitions/modules/flyout.less
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
overflow-y: hidden;
z-index: @topLayer;
background: #fff;
outline: none;
}

/* GPU Layers for Child Elements */
Expand Down
68 changes: 61 additions & 7 deletions src/definitions/modules/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,48 @@
observeChanges: function () {
if ('MutationObserver' in window) {
observer = new MutationObserver(function (mutations) {
if (settings.observeChanges) {
var collectNodes = function (parent) {
var nodes = [];
for (var c = 0, cl = parent.length; c < cl; c++) {
Array.prototype.push.apply(nodes, collectNodes(parent[c].childNodes));
nodes.push(parent[c]);
}

return nodes;
},
shouldRefresh = false,
shouldRefreshInputs = false
;
mutations.every(function (mutation) {
if (mutation.type === 'attributes') {
if (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input')) {
shouldRefreshInputs = true;
}
} else {
shouldRefresh = true;
// mutationobserver only provides the parent nodes
// so let's collect all childs as well to find nested inputs
var $addedInputs = $(collectNodes(mutation.addedNodes)).filter('a[href], [tabindex], :input:enabled').filter(':visible'),
$removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
if ($addedInputs.length > 0 || $removedInputs.length > 0) {
shouldRefreshInputs = true;
}
}

return !shouldRefreshInputs;
});

if (shouldRefresh && settings.observeChanges) {
module.debug('DOM tree modified, refreshing');
module.refresh();
}
module.refreshInputs();
if (shouldRefreshInputs) {
module.refreshInputs();
}
});
observer.observe(element, {
attributeFilter: ['class', 'disabled'],
attributes: true,
childList: true,
subtree: true,
});
Expand Down Expand Up @@ -286,15 +321,23 @@
.off('keydown' + elementEventNamespace)
;
}
$inputs = $module.find('[tabindex], :input').filter(':visible').filter(function () {
$inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
return $(this).closest('.disabled').length === 0;
});
$module.removeAttr('tabindex');
if ($inputs.length === 0) {
$inputs = $module;
$module.attr('tabindex', -1);
}
$inputs.first()
.on('keydown' + elementEventNamespace, module.event.inputKeyDown.first)
;
$inputs.last()
.on('keydown' + elementEventNamespace, module.event.inputKeyDown.last)
;
if (settings.autofocus && $inputs.filter(':focus').length === 0) {
module.set.autofocus();
}
},

attachEvents: function (selector, event) {
Expand Down Expand Up @@ -328,6 +371,7 @@
;
$window
.on('resize' + elementEventNamespace, module.event.resize)
.on('focus' + elementEventNamespace, module.event.focus)
;
},
scrollLock: function () {
Expand Down Expand Up @@ -485,6 +529,11 @@
requestAnimationFrame(module.refresh);
}
},
focus: function () {
if ($dimmable.dimmer('is active') && module.is.active() && settings.autofocus) {
requestAnimationFrame(module.set.autofocus);
}
},
},

toggle: function () {
Expand Down Expand Up @@ -574,9 +623,6 @@
module.save.focus();
module.set.active();
module.refreshInputs();
if (settings.autofocus) {
module.set.autofocus();
}
callback();
},
})
Expand Down Expand Up @@ -990,10 +1036,18 @@
autofocus: function () {
var
$autofocus = $inputs.filter('[autofocus]'),
$rawInputs = $inputs.filter(':input'),
$input = $autofocus.length > 0
? $autofocus.first()
: ($inputs.length > 1 ? $inputs.filter(':not(i.close)') : $inputs).first()
: ($rawInputs.length > 0
? $rawInputs
: $inputs.filter(':not(i.close)')
).first()
;
// check if only the close icon is remaining
if ($input.length === 0 && $inputs.length > 0) {
$input = $inputs.first();
}
if ($input.length > 0) {
$input.trigger('focus');
}
Expand Down
1 change: 1 addition & 0 deletions src/definitions/modules/modal.less
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
border-radius: @borderRadius;
user-select: text;
will-change: top, left, margin, transform, opacity;
outline: none;
}

.ui.modal > :first-child:not(.close):not(.dimmer),
Expand Down

0 comments on commit 1373e46

Please sign in to comment.