Skip to content

Commit

Permalink
feat(DatePicker, DateRangePicker, MultiSelect, TimePicker): improve k…
Browse files Browse the repository at this point in the history
…eyboard support
  • Loading branch information
mrholek committed May 27, 2024
1 parent 6556c6b commit 3f247ab
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 6 deletions.
8 changes: 8 additions & 0 deletions js/src/date-range-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const DATA_KEY = 'coreui.date-range-picker'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'

const ESCAPE_KEY = 'Escape'
const TAB_KEY = 'Tab'
const RIGHT_MOUSE_BUTTON = 2

Expand All @@ -32,6 +33,7 @@ const EVENT_END_DATE_CHANGE = `endDateChange${EVENT_KEY}`
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_INPUT = 'input'
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
const EVENT_RESIZE = 'resize'
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
Expand Down Expand Up @@ -327,6 +329,12 @@ class DateRangePicker extends BaseComponent {
}
})

EventHandler.on(this._element, EVENT_KEYDOWN, event => {
if (event.key === ESCAPE_KEY) {
this.hide()
}
})

EventHandler.on(this._startInput, EVENT_CLICK, () => {
this._selectEndDate = false
this._calendar.update(this._getCalendarConfig())
Expand Down
40 changes: 34 additions & 6 deletions js/src/multi-select.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ import BaseComponent from './base-component.js'
import Data from './dom/data.js'
import EventHandler from './dom/event-handler.js'
import SelectorEngine from './dom/selector-engine.js'
import { defineJQueryPlugin, isRTL } from './util/index.js'
import {
defineJQueryPlugin,
getNextActiveElement,
isVisible,
isRTL
} from './util/index.js'

/**
* ------------------------------------------------------------------------
Expand All @@ -23,8 +28,11 @@ const DATA_KEY = 'coreui.multi-select'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'

const ESCAPE_KEY = 'Escape'
const TAB_KEY = 'Tab'
const RIGHT_MOUSE_BUTTON = 2
const ARROW_UP_KEY = 'ArrowUp'
const ARROW_DOWN_KEY = 'ArrowDown'
const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button

const SELECTOR_CLEANER = '.form-multi-select-cleaner'
const SELECTOR_OPTGROUP = '.form-multi-select-optgroup'
Expand All @@ -34,6 +42,7 @@ const SELECTOR_OPTIONS_EMPTY = '.form-multi-select-options-empty'
const SELECTOR_SEARCH = '.form-multi-select-search'
const SELECTOR_SELECT = '.form-multi-select'
const SELECTOR_SELECTION = '.form-multi-select-selection'
const SELECTOR_VISIBLE_ITEMS = '.form-multi-select-options .form-multi-select-option:not(.disabled):not(:disabled)'

const EVENT_CHANGED = `changed${EVENT_KEY}`
const EVENT_CLICK = `click${EVENT_KEY}`
Expand Down Expand Up @@ -261,6 +270,12 @@ class MultiSelect extends BaseComponent {
}
})

EventHandler.on(this._clone, EVENT_KEYDOWN, event => {
if (event.key === ESCAPE_KEY) {
this.hide()
}
})

EventHandler.on(this._indicatorElement, EVENT_CLICK, event => {
event.preventDefault()
event.stopPropagation()
Expand Down Expand Up @@ -306,9 +321,11 @@ class MultiSelect extends BaseComponent {

if (key === 13) {
this._onOptionsClick(event.target)
if (this._config.search) {
SelectorEngine.findOne(SELECTOR_SEARCH, this._clone).focus()
}
}

if ([ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {
event.preventDefault()
this._selectMenuItem(event)
}
})
}
Expand Down Expand Up @@ -881,6 +898,18 @@ class MultiSelect extends BaseComponent {
}
}

_selectMenuItem({ key, target }) {
const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))

if (!items.length) {
return
}

// if target isn't included in items (e.g. when expanding the dropdown)
// allow cycling to get the last item in case key equals ARROW_UP_KEY
getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()
}

// Static

static multiSelectInterface(element, config) {
Expand Down Expand Up @@ -949,7 +978,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
}
}
})

EventHandler.on(document, EVENT_CLICK_DATA_API, MultiSelect.clearMenus)
EventHandler.on(document, EVENT_KEYUP_DATA_API, MultiSelect.clearMenus)

Expand Down
8 changes: 8 additions & 0 deletions js/src/time-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'

const ENTER_KEY = 'Enter'
const ESCAPE_KEY = 'Escape'
const SPACE_KEY = 'Space'
const TAB_KEY = 'Tab'
const RIGHT_MOUSE_BUTTON = 2
Expand All @@ -38,6 +39,7 @@ const EVENT_CLICK = `click${EVENT_KEY}`
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_INPUT = 'input'
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_SUBMIT = 'submit'
Expand Down Expand Up @@ -257,6 +259,12 @@ class TimePicker extends BaseComponent {
}
})

EventHandler.on(this._element, EVENT_KEYDOWN, event => {
if (event.key === ESCAPE_KEY) {
this.hide()
}
})

EventHandler.on(this._element, 'timeChange.coreui.time-picker', () => {
if (this._config.variant === 'roll') {
this._setUpRolls()
Expand Down

0 comments on commit 3f247ab

Please sign in to comment.