From 5e50756b35be25ff713f54682b2d0dd216de4e21 Mon Sep 17 00:00:00 2001 From: Jonathan Boiser Date: Thu, 31 Jan 2019 12:38:21 -0800 Subject: [PATCH 1/4] Vendor modality.js helper and modify for vuex --- kolibri/core/assets/src/core-app/index.js | 1 + .../core/assets/src/core-app/vuexModality.js | 112 ++++++++++++++++++ .../core/assets/src/state/modules/theme.js | 12 ++ 3 files changed, 125 insertions(+) create mode 100644 kolibri/core/assets/src/core-app/vuexModality.js diff --git a/kolibri/core/assets/src/core-app/index.js b/kolibri/core/assets/src/core-app/index.js index be21bd3b87d..a701e58e650 100644 --- a/kolibri/core/assets/src/core-app/index.js +++ b/kolibri/core/assets/src/core-app/index.js @@ -11,6 +11,7 @@ require('kolibri.urls').default.setUp(); require('purecss/build/base-min.css'); require('../styles/main.scss'); require('../styles/globalDynamicStyles'); +require('./vuexModality'); // Required to setup Keen UI, should be imported only once in your project require('keen-ui/src/bootstrap'); diff --git a/kolibri/core/assets/src/core-app/vuexModality.js b/kolibri/core/assets/src/core-app/vuexModality.js new file mode 100644 index 00000000000..87d165d2476 --- /dev/null +++ b/kolibri/core/assets/src/core-app/vuexModality.js @@ -0,0 +1,112 @@ +/* global kolibriGlobal */ +/** + * Adapted from https://github.com/alice/modality + * Version: 1.0.2 + * + * For Kolibri, this mirrors in Vuex the DOM updates done by modality.js in Keen-UI, + * which gives the code in theme.js easier access to the current modality. + */ + +document.addEventListener('DOMContentLoaded', () => { + let hadKeyboardEvent = false; + const keyboardModalityWhitelist = [ + 'input:not([type])', + 'input[type=text]', + 'input[type=number]', + 'input[type=date]', + 'input[type=time]', + 'input[type=datetime]', + 'textarea', + '[role=textbox]', + '[supports-modality=keyboard]', + ].join(','); + + let isHandlingKeyboardThrottle; + + const matcher = (() => { + const el = document.body; + + if (el.matchesSelector) { + return el.matchesSelector; + } + + if (el.webkitMatchesSelector) { + return el.webkitMatchesSelector; + } + + if (el.mozMatchesSelector) { + return el.mozMatchesSelector; + } + + if (el.msMatchesSelector) { + return el.msMatchesSelector; + } + + /* eslint-disable-next-line */ + console.error("Couldn't find any matchesSelector method on document.body."); + })(); + + const disableFocusRingByDefault = function() { + const css = 'body:not([modality=keyboard]) :focus { outline: none; }'; + const head = document.head || document.getElementsByTagName('head')[0]; + const style = document.createElement('style'); + + style.type = 'text/css'; + style.id = 'disable-focus-ring'; + + if (style.styleSheet) { + style.styleSheet.cssText = css; + } else { + style.appendChild(document.createTextNode(css)); + } + + head.insertBefore(style, head.firstChild); + }; + + const focusTriggersKeyboardModality = function(el) { + let triggers = false; + + if (matcher) { + triggers = + matcher.call(el, keyboardModalityWhitelist) && matcher.call(el, ':not([readonly])'); + } + + return triggers; + }; + + disableFocusRingByDefault(); + + document.body.addEventListener( + 'keydown', + () => { + hadKeyboardEvent = true; + + if (isHandlingKeyboardThrottle) { + clearTimeout(isHandlingKeyboardThrottle); + } + + isHandlingKeyboardThrottle = setTimeout(() => { + hadKeyboardEvent = false; + }, 100); + }, + true + ); + + document.body.addEventListener( + 'focus', + e => { + if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) { + kolibriGlobal.coreVue.vuex.store.commit('SET_MODALITY', 'keyboard'); + } + }, + true + ); + + document.body.addEventListener( + 'blur', + () => { + kolibriGlobal.coreVue.vuex.store.commit('SET_MODALITY', null); + }, + true + ); +}); diff --git a/kolibri/core/assets/src/state/modules/theme.js b/kolibri/core/assets/src/state/modules/theme.js index 248f2f6a966..5471fe46b94 100644 --- a/kolibri/core/assets/src/state/modules/theme.js +++ b/kolibri/core/assets/src/state/modules/theme.js @@ -24,6 +24,7 @@ const initialState = { '$core-grey': '#e0e0e0', '$core-loading': '#03a9f4', + modality: null, }; export default { @@ -90,7 +91,15 @@ export default { $coreLoading(state) { return state['$core-loading']; }, + // Should only use these styles to outline stuff that will be focused + // on keyboard-tab-focus $coreOutline(state) { + if (state.modality !== 'keyboard') { + return { + outline: 'none', + }; + } + return { outlineColor: darken(state['$core-action-light'], 0.1), outlineStyle: 'solid', @@ -106,5 +115,8 @@ export default { RESET_THEME_VALUE(state, varName) { state[varName] = initialState[varName]; }, + SET_MODALITY(state, modality) { + state.modality = modality; + }, }, }; From 0fedb107107ed7fbffdeaef40a1a2f6bf9927a70 Mon Sep 17 00:00:00 2001 From: Jonathan Boiser Date: Thu, 31 Jan 2019 12:36:50 -0800 Subject: [PATCH 2/4] Use checkbox col class in user tale --- kolibri/core/assets/src/views/ImmersiveToolbar.vue | 1 - .../facility_management/assets/src/views/UserTable.vue | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kolibri/core/assets/src/views/ImmersiveToolbar.vue b/kolibri/core/assets/src/views/ImmersiveToolbar.vue index 944ab0e83a9..80534a490a0 100644 --- a/kolibri/core/assets/src/views/ImmersiveToolbar.vue +++ b/kolibri/core/assets/src/views/ImmersiveToolbar.vue @@ -119,7 +119,6 @@ ...mapGetters([ '$coreGrey200', '$coreGrey300', - '$coreOutline', '$coreActionDark', '$coreActionNormal', '$coreTextDefault', diff --git a/kolibri/plugins/facility_management/assets/src/views/UserTable.vue b/kolibri/plugins/facility_management/assets/src/views/UserTable.vue index 1cc9806dc7a..b955b5da5fc 100644 --- a/kolibri/plugins/facility_management/assets/src/views/UserTable.vue +++ b/kolibri/plugins/facility_management/assets/src/views/UserTable.vue @@ -5,7 +5,7 @@ - + - + Date: Thu, 31 Jan 2019 12:41:26 -0800 Subject: [PATCH 3/4] Add $coreOutlineAnyModality for one non-focus usage --- kolibri/core/assets/src/state/modules/theme.js | 10 ++++++++++ kolibri/core/assets/src/views/KNavbar/index.vue | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/kolibri/core/assets/src/state/modules/theme.js b/kolibri/core/assets/src/state/modules/theme.js index 5471fe46b94..82689b462ee 100644 --- a/kolibri/core/assets/src/state/modules/theme.js +++ b/kolibri/core/assets/src/state/modules/theme.js @@ -107,6 +107,16 @@ export default { outlineOffset: '4px', }; }, + // Should use this when the outline needs to be applied regardless + // of modality + $coreOutlineAnyModality(state) { + return { + outlineColor: darken(state['$core-action-light'], 0.1), + outlineStyle: 'solid', + outlineWidth: '3px', + outlineOffset: '4px', + }; + }, }, mutations: { SET_CORE_THEME(state, theme) { diff --git a/kolibri/core/assets/src/views/KNavbar/index.vue b/kolibri/core/assets/src/views/KNavbar/index.vue index 9d5b74c56eb..74a0f85802b 100644 --- a/kolibri/core/assets/src/views/KNavbar/index.vue +++ b/kolibri/core/assets/src/views/KNavbar/index.vue @@ -65,7 +65,7 @@ }; }, computed: { - ...mapGetters(['$coreActionNormal', '$coreOutline']), + ...mapGetters(['$coreActionNormal', '$coreOutlineAnyModality']), maxWidth() { return this.enoughSpace ? this.elementWidth : this.elementWidth - 38 * 2; }, @@ -76,7 +76,7 @@ }, scrollButton() { return { - ':hover': this.$coreOutline, + ':hover': this.$coreOutlineAnyModality, }; }, }, From e5e0520236b206f4ec4920b50d0930148b1f3620 Mon Sep 17 00:00:00 2001 From: Jonathan Boiser Date: Thu, 31 Jan 2019 15:24:06 -0800 Subject: [PATCH 4/4] Update snaps --- .../assets/test/views/__snapshots__/app-bar.spec.js.snap | 2 +- .../assets/test/views/__snapshots__/side-nav.spec.js.snap | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kolibri/core/assets/test/views/__snapshots__/app-bar.spec.js.snap b/kolibri/core/assets/test/views/__snapshots__/app-bar.spec.js.snap index 55ca5edd383..1495431761e 100644 --- a/kolibri/core/assets/test/views/__snapshots__/app-bar.spec.js.snap +++ b/kolibri/core/assets/test/views/__snapshots__/app-bar.spec.js.snap @@ -4,7 +4,7 @@ exports[`app bar component drop down user menu should show the language modal li
-
@@ -50,7 +50,7 @@ exports[`side nav component should be hidden if navShown is false 1`] = ` exports[`side nav component should show nothing if no components are added and user is not logged in 1`] = `
-