Improved event handling
gitea/salix/dev This commit looks good Details

This commit is contained in:
Juan Ferrer 2019-10-24 10:17:32 +02:00
parent 2a4d3ae43d
commit 4685b4166b
31 changed files with 79 additions and 92 deletions

View File

@ -48,5 +48,5 @@
vn-id="drop-down" vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(item)" on-select="$ctrl.onDropDownSelect(item)"
on-data-ready="$ctrl.onDataReady()" on-data-ready="$ctrl.onDataReady()"
on-close-start="$ctrl.focus()"> on-close-start="$ctrl.onDropDownClose()">
</vn-drop-down> </vn-drop-down>

View File

@ -209,6 +209,10 @@ export default class Autocomplete extends Field {
this.field = value; this.field = value;
} }
onDropDownClose() {
setTimeout(() => this.focus());
}
onContainerKeyDown(event) { onContainerKeyDown(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
@ -224,13 +228,10 @@ export default class Autocomplete extends Field {
else else
return; return;
} }
event.preventDefault();
} }
onContainerClick(event) { onContainerClick(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
this.showDropDown(); this.showDropDown();
} }

View File

@ -15,6 +15,5 @@
</button> </button>
<vn-drop-down <vn-drop-down
vn-id="drop-down" vn-id="drop-down"
on-select="$ctrl.onDropDownSelect(item)" on-select="$ctrl.onDropDownSelect(item)">
ng-click="$ctrl.onDropDownClick($event)">
</vn-drop-down> </vn-drop-down>

View File

@ -44,15 +44,10 @@ export default class ButtonMenu extends Button {
onClick(event) { onClick(event) {
if (this.disabled) return; if (this.disabled) return;
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
this.emit('open'); this.emit('open');
this.showDropDown(); this.showDropDown();
} }
onDropDownClick(event) {
event.preventDefault();
}
onDropDownSelect(item) { onDropDownSelect(item) {
const value = item[this.valueField]; const value = item[this.valueField];
this.field = value; this.field = value;

View File

@ -6,11 +6,8 @@ export default class Button extends FormInput {
constructor($element, $scope) { constructor($element, $scope) {
super($element, $scope); super($element, $scope);
this.design = 'colored'; this.design = 'colored';
this.input = this.element.querySelector('button'); this.initTabIndex();
this.classList.add('vn-button');
let element = this.element;
element.tabIndex = 0;
element.classList.add('vn-button');
this.element.addEventListener('keyup', e => this.onKeyup(e)); this.element.addEventListener('keyup', e => this.onKeyup(e));
this.element.addEventListener('click', e => this.onClick(e)); this.element.addEventListener('click', e => this.onClick(e));
} }
@ -21,16 +18,17 @@ export default class Button extends FormInput {
} }
onKeyup(event) { onKeyup(event) {
if (event.code == 'Space') if (event.defaultPrevented) return;
this.onClick(event); switch (event.code) {
case 'Space':
case 'Enter':
return this.element.click();
}
} }
onClick(event) { onClick(event) {
if (event.defaultPrevented) return; if (this.disabled)
// event.preventDefault(); event.stopImmediatePropagation();
// FIXME: Don't stop event propagation
if (this.disabled) event.stopImmediatePropagation();
} }
} }
Button.$inject = ['$element', '$scope']; Button.$inject = ['$element', '$scope'];

View File

@ -1 +0,0 @@
<div></div>

View File

@ -218,18 +218,15 @@ export default class DropDown extends Component {
onLoadMoreClick(event) { onLoadMoreClick(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
this.model.loadMore(); this.model.loadMore();
} }
onContainerClick(event) { onContainerClick(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
let index = getPosition(this.$.ul, event); let index = getPosition(this.$.ul, event);
if (index != -1) { if (index != -1)
event.preventDefault();
this.selectOption(index); this.selectOption(index);
} }
}
onDocKeyDown(event) { onDocKeyDown(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;

View File

@ -155,7 +155,6 @@ export default class Field extends FormInput {
onClick() { onClick() {
// if (event.defaultPrevented) return; // if (event.defaultPrevented) return;
// event.preventDefault();
if (this.input !== document.activeElement) if (this.input !== document.activeElement)
this.focus(); this.focus();
@ -173,7 +172,6 @@ export default class Field extends FormInput {
onClear(event) { onClear(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
this.field = null; this.field = null;
this.input.dispatchEvent(new Event('change')); this.input.dispatchEvent(new Event('change'));
} }

View File

@ -88,6 +88,7 @@ export default class FormInput extends Component {
} }
select() { select() {
if (this.inputEl.select)
this.inputEl.select(); this.inputEl.select();
} }
@ -95,6 +96,11 @@ export default class FormInput extends Component {
this.inputEl.focus(); this.inputEl.focus();
} }
initTabIndex() {
if (!this.element.hasAttribute('tabindex'))
this.element.tabIndex = 0;
}
refreshTabIndex() { refreshTabIndex() {
this.inputEl.tabIndex = this.disabled ? -1 : this.tabIndex; this.inputEl.tabIndex = this.disabled ? -1 : this.tabIndex;
} }

View File

@ -68,7 +68,6 @@ export default class InputNumber extends Field {
onStep(event, way) { onStep(event, way) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
if (way == 'up') if (way == 'up')
this.stepUp(); this.stepUp();

View File

@ -190,7 +190,6 @@ export default class Popover extends Component {
onBgMouseDown(event) { onBgMouseDown(event) {
if (event == this.lastMouseEvent || event.defaultPrevented) return; if (event == this.lastMouseEvent || event.defaultPrevented) return;
event.preventDefault();
this.hide(); this.hide();
} }
} }

View File

@ -65,7 +65,6 @@ export default class Controller extends Component {
openPanel(event) { openPanel(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
this.$panelScope = this.$.$new(); this.$panelScope = this.$.$new();
this.$panel = this.$compile(`<${this.panel}/>`)(this.$panelScope); this.$panel = this.$compile(`<${this.panel}/>`)(this.$panelScope);

View File

@ -81,18 +81,6 @@ describe('Component vnSearchbar', () => {
}); });
}); });
describe('openPanel()', () => {
it(`should do nothing if the event is prevented`, () => {
let event = {
defaultPrevented: true,
preventDefault: jasmine.createSpy('preventDefault')
};
controller.openPanel(event);
expect(event.preventDefault).not.toHaveBeenCalledWith();
});
});
describe('onPopoverClose()', () => { describe('onPopoverClose()', () => {
it(`should get rid of $panel and $panelScope`, () => { it(`should get rid of $panel and $panelScope`, () => {
controller.$panel = { controller.$panel = {

View File

@ -10,24 +10,20 @@ import './style.scss';
export default class Toggle extends FormInput { export default class Toggle extends FormInput {
constructor($element, $) { constructor($element, $) {
super($element, $); super($element, $);
this.initTabIndex();
let element = this.element; this.classList.add('vn-toggle');
element.tabIndex = 0; this.element.addEventListener('click', e => this.onClick(e));
element.addEventListener('click', e => this.onClick(e)); this.element.addEventListener('keydown', e => this.onKeydown(e));
element.addEventListener('keydown', e => this.onKeydown(e));
element.classList.add('vn-toggle');
} }
onKeydown(event) { onKeydown(event) {
if (event.code == 'Space') if (!event.defaultPrevented && event.code == 'Space')
this.onClick(event); this.element.click();
} }
onClick(event) { onClick(event) {
if (!this.editable || event.defaultPrevented) if (!this.editable || event.defaultPrevented)
return true; return true;
event.preventDefault();
} }
changed() { changed() {

View File

@ -235,7 +235,6 @@ export function directive($document, $compile, $templateRequest) {
$element[0].title = ''; $element[0].title = '';
$element.on('mouseover', function(event) { $element.on('mouseover', function(event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
tooltip.show($element[0]); tooltip.show($element[0]);
}); });
$element.on('mouseout', function() { $element.on('mouseout', function() {

View File

@ -9,12 +9,12 @@
<section class="buttons" ng-if="::!$ctrl.treeview.readOnly"> <section class="buttons" ng-if="::!$ctrl.treeview.readOnly">
<vn-icon-button translate-attr="::{title: 'Remove'}" <vn-icon-button translate-attr="::{title: 'Remove'}"
icon="delete" icon="delete"
ng-click="$ctrl.treeview.onRemove($ctrl.item)" ng-click="$ctrl.treeview.onRemove($event, $ctrl.item)"
ng-if="$ctrl.item.parent"> ng-if="$ctrl.item.parent">
</vn-icon-button> </vn-icon-button>
<vn-icon-button translate-attr="::{title: 'Create'}" <vn-icon-button translate-attr="::{title: 'Create'}"
icon="add_circle" icon="add_circle"
ng-click="$ctrl.treeview.onCreate($ctrl.item)"> ng-click="$ctrl.treeview.onCreate($event, $ctrl.item)">
</vn-icon-button> </vn-icon-button>
</section> </section>
</div> </div>

View File

@ -2,7 +2,7 @@
<ul ng-if="$ctrl.items"> <ul ng-if="$ctrl.items">
<li ng-repeat="item in $ctrl.items"> <li ng-repeat="item in $ctrl.items">
<vn-treeview-child item="item" ng-class="{expanded: item.active}" <vn-treeview-child item="item" ng-class="{expanded: item.active}"
ng-click="$ctrl.onClick($event, item)"> ng-click="$ctrl.treeview.onToggle($event, item)">
</vn-treeview-child> </vn-treeview-child>
<vn-treeview-childs <vn-treeview-childs
items="item.childs"> items="item.childs">

View File

@ -1,13 +1,6 @@
import ngModule from '../../module'; import ngModule from '../../module';
import Component from '../../lib/component';
class Controller extends Component { class Controller {}
onClick(event, item) {
if (event.defaultPrevented || !item.sons) return;
event.preventDefault();
this.treeview.onToggle(item);
}
}
ngModule.component('vnTreeviewChilds', { ngModule.component('vnTreeviewChilds', {
template: require('./childs.html'), template: require('./childs.html'),

View File

@ -141,7 +141,12 @@ export default class Treeview extends Component {
}); });
} }
onToggle(item) { onToggle(event, item) {
let ignoreEvent = event.defaultPrevented
|| event == this.lastActionEvent
|| !item.sons;
if (ignoreEvent) return;
if (item.active) if (item.active)
this.fold(item); this.fold(item);
else else
@ -175,7 +180,8 @@ export default class Treeview extends Component {
}).then(() => item.active = true); }).then(() => item.active = true);
} }
onRemove(item) { onRemove(event, item) {
this.lastActionEvent = event;
if (this.removeFunc) if (this.removeFunc)
this.removeFunc({$item: item}); this.removeFunc({$item: item});
} }
@ -191,7 +197,8 @@ export default class Treeview extends Component {
if (parent) parent.sons--; if (parent) parent.sons--;
} }
onCreate(parent) { onCreate(event, parent) {
this.lastActionEvent = event;
if (this.createFunc) if (this.createFunc)
this.createFunc({$parent: parent}); this.createFunc({$parent: parent});
} }

View File

@ -102,11 +102,16 @@ describe('Component vnTreeview', () => {
spyOn(controller, 'fold'); spyOn(controller, 'fold');
spyOn(controller, 'unfold'); spyOn(controller, 'unfold');
const item = {name: 'My item'}; let event = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
const item = {name: 'My item', sons: 1};
controller.onToggle(item); controller.onToggle(event, item);
item.active = true; item.active = true;
controller.onToggle(item); controller.onToggle(event, item);
expect(controller.unfold).toHaveBeenCalledWith(item); expect(controller.unfold).toHaveBeenCalledWith(item);
expect(controller.fold).toHaveBeenCalledWith(item); expect(controller.fold).toHaveBeenCalledWith(item);
@ -148,9 +153,15 @@ describe('Component vnTreeview', () => {
describe('onRemove()', () => { describe('onRemove()', () => {
it(`should call the removeFunc() method`, () => { it(`should call the removeFunc() method`, () => {
let event = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
spyOn(controller, 'removeFunc'); spyOn(controller, 'removeFunc');
const item = {name: 'My item'}; const item = {name: 'My item'};
controller.onRemove(item); controller.onRemove(event, item);
expect(controller.removeFunc).toHaveBeenCalledWith({$item: item}); expect(controller.removeFunc).toHaveBeenCalledWith({$item: item});
}); });
@ -172,9 +183,15 @@ describe('Component vnTreeview', () => {
describe('onCreate()', () => { describe('onCreate()', () => {
it(`should call the createFunc() method`, () => { it(`should call the createFunc() method`, () => {
let event = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window
});
spyOn(controller, 'createFunc'); spyOn(controller, 'createFunc');
const parent = {name: 'My item'}; const parent = {name: 'My item'};
controller.onCreate(parent); controller.onCreate(event, parent);
expect(controller.createFunc).toHaveBeenCalledWith({$parent: parent}); expect(controller.createFunc).toHaveBeenCalledWith({$parent: parent});
}); });

View File

@ -5,8 +5,8 @@ import './style.scss';
export default class WdayPicker extends FormInput { export default class WdayPicker extends FormInput {
constructor($element, $scope, vnWeekDays) { constructor($element, $scope, vnWeekDays) {
super($element, $scope); super($element, $scope);
this.input = {};
this.days = vnWeekDays.locales; this.days = vnWeekDays.locales;
this.initTabIndex();
} }
} }
WdayPicker.$inject = ['$element', '$scope', 'vnWeekDays']; WdayPicker.$inject = ['$element', '$scope', 'vnWeekDays'];

View File

@ -1,8 +1,6 @@
@import "effects"; @import "effects";
vn-wday-picker { vn-wday-picker {
margin-top: $spacing-sm;
margin-bottom: $spacing-md;
text-align: center; text-align: center;
& > span { & > span {

View File

@ -2,15 +2,14 @@ import ngModule from '../module';
directive.$inject = ['$document']; directive.$inject = ['$document'];
export function directive($document) { export function directive($document) {
let modifiers = ["alt", "ctrl", "meta", "shift"]; let modifiers = ['alt', 'ctrl', 'meta', 'shift'];
function checkAttributes($attrs) { function checkAttributes($attrs) {
let shortcut = $attrs.vnBind.split(' '); let shortcut = $attrs.vnBind.split(' ');
let keys = {}; let keys = {};
if (shortcut[0] == "") { if (shortcut[0] == '')
throw new Error('vnBind: Binding keys not defined'); throw new Error('vnBind: Binding keys not defined');
}
if (shortcut.length == 1) { if (shortcut.length == 1) {
keys.altKey = true; keys.altKey = true;
@ -41,7 +40,8 @@ export function directive($document) {
let correctShortcut = true; let correctShortcut = true;
for (const key in shortcut) { for (const key in shortcut) {
correctShortcut = correctShortcut && shortcut[key] == event[key]; correctShortcut = correctShortcut
&& shortcut[key] == event[key];
} }
if (correctShortcut) { if (correctShortcut) {
event.preventDefault(); event.preventDefault();
@ -49,7 +49,7 @@ export function directive($document) {
} }
} }
$document.on("keyup", onKeyUp); $document.on('keyup', onKeyUp);
$element.on('$destroy', function() { $element.on('$destroy', function() {
$document.off('keyup', onKeyUp); $document.off('keyup', onKeyUp);
}); });

View File

@ -13,11 +13,11 @@ export default function directive() {
restrict: 'A', restrict: 'A',
link: function($scope, $element, $attrs) { link: function($scope, $element, $attrs) {
$element.on('click', function(event) { $element.on('click', function(event) {
if (event.defaultPrevented) return;
let dialogKey = kebabToCamel($attrs.vnDialog); let dialogKey = kebabToCamel($attrs.vnDialog);
let dialog = $scope[dialogKey]; let dialog = $scope[dialogKey];
if (dialog instanceof Dialog) if (dialog instanceof Dialog)
dialog.show(); dialog.show();
event.preventDefault();
}); });
} }
}; };

View File

@ -12,13 +12,13 @@ export function directive() {
restrict: 'A', restrict: 'A',
link: function($scope, $element, $attrs) { link: function($scope, $element, $attrs) {
$element.on('click', function(event) { $element.on('click', function(event) {
if (event.defaultPrevented) return;
let popoverKey = kebabToCamel($attrs.vnPopover); let popoverKey = kebabToCamel($attrs.vnPopover);
let popover = $scope[popoverKey]; let popover = $scope[popoverKey];
if (popover instanceof Popover) { if (popover instanceof Popover) {
popover.parent = $element[0]; popover.parent = $element[0];
popover.show(); popover.show();
} }
event.preventDefault();
}); });
} }
}; };

View File

@ -44,7 +44,7 @@ export function directive($timeout) {
restrict: 'A', restrict: 'A',
link: function($scope, $element, $attrs) { link: function($scope, $element, $attrs) {
$element.on('click', function(event) { $element.on('click', function(event) {
event.preventDefault(); if (event.defaultPrevented) return;
let src = $attrs.zoomImage || $attrs.src; let src = $attrs.zoomImage || $attrs.src;
if (src) if (src)

View File

@ -39,7 +39,6 @@ export default class SideMenu {
onEscape(event) { onEscape(event) {
if (!event.defaultPrevented && event.key == 'Escape') { if (!event.defaultPrevented && event.key == 'Escape') {
event.preventDefault();
this.hide(); this.hide();
this.$.$digest(); this.$.$digest();
} }

View File

@ -116,7 +116,8 @@
</vn-vertical> </vn-vertical>
<vn-wday-picker <vn-wday-picker
ng-if="$ctrl.eventType != 'day'" ng-if="$ctrl.eventType != 'day'"
ng-model="$ctrl.selected.wdays"> ng-model="$ctrl.selected.wdays"
class="vn-mt-sm vn-mb-md">
</vn-wday-picker> </vn-wday-picker>
<vn-date-picker <vn-date-picker
ng-if="$ctrl.eventType == 'day'" ng-if="$ctrl.eventType == 'day'"

View File

@ -62,7 +62,6 @@ class Controller extends Section {
onEditClick(row, event) { onEditClick(row, event) {
if (event.defaultPrevented) return; if (event.defaultPrevented) return;
event.preventDefault();
this.edit(row); this.edit(row);
} }

View File

@ -19,6 +19,7 @@
ng-model="item.selected" ng-model="item.selected"
on-change="$ctrl.onSelection(value, item)" on-change="$ctrl.onSelection(value, item)"
triple-state="true" triple-state="true"
ng-click="$event.preventDefault()"
label="{{::item.name}}"> label="{{::item.name}}">
</vn-check> </vn-check>
</vn-treeview> </vn-treeview>

View File

@ -29,10 +29,9 @@
</vn-textfield> </vn-textfield>
<vn-none> <vn-none>
<vn-icon-button <vn-icon-button
pointer
class="vn-my-md"
vn-tooltip="Remove contact" vn-tooltip="Remove contact"
icon="delete" icon="delete"
tabindex="-1"
ng-click="model.remove($index)"> ng-click="model.remove($index)">
</vn-icon-button> </vn-icon-button>
</vn-none> </vn-none>