From 29efb3e8acdcb6a2ad0280f9f2157419f8e1ae14 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 10:24:05 -0500 Subject: [PATCH 01/11] Add useKContextMenu --- lib/composables/_useKContextMenu.js | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 lib/composables/_useKContextMenu.js diff --git a/lib/composables/_useKContextMenu.js b/lib/composables/_useKContextMenu.js new file mode 100644 index 000000000..17fcbfc29 --- /dev/null +++ b/lib/composables/_useKContextMenu.js @@ -0,0 +1,49 @@ +import '../composition-api'; //Due to @vue/composition-api shortcomings, add plugin prior to use in kolibri, studio and tests + +import { onMounted, onBeforeUnmount, ref, getCurrentInstance, computed } from '@vue/composition-api'; + +const activeMenu = ref(null); + +export default function useKContextMenu() { + + const clientX = ref(0); + const clientY = ref(0); + + const instance = getCurrentInstance(); + const id = instance.uid; + + const isActive = computed(() => activeMenu.value === id); + + + function showMenu(event) { + event.preventDefault(); + activeMenu.value = id; + clientX.value = event.clientX; + clientY.value = event.clientY; + } + + function hideMenu() { + if (activeMenu.value === id) { + activeMenu.value = null; + } + } + + + onMounted(() => { + const parent = instance.proxy.$el.parentElement; + parent.addEventListener('contextmenu', showMenu); + window.addEventListener('click', hideMenu); + }); + + onBeforeUnmount(() => { + const parent = instance.proxy.$el.parentElement; + parent.removeEventListener('contextmenu', showMenu); + window.removeEventListener('click', hideMenu); + }); + + return { + clientX, + clientY, + isActive, + }; +}; From 054167595aaff14e2dc6ac6f61661c88907fc639 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 10:26:39 -0500 Subject: [PATCH 02/11] Add KDropdownMenu support for context menus --- lib/KDropdownMenu.vue | 59 +++++++++++++++++++++++++++++++++++++++--- lib/keen/UiPopover.vue | 45 +++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 4 deletions(-) diff --git a/lib/KDropdownMenu.vue b/lib/KDropdownMenu.vue index 824d992c1..2e606b679 100644 --- a/lib/KDropdownMenu.vue +++ b/lib/KDropdownMenu.vue @@ -1,10 +1,16 @@ diff --git a/lib/keen/UiPopover.vue b/lib/keen/UiPopover.vue index 63c253b47..62b158737 100644 --- a/lib/keen/UiPopover.vue +++ b/lib/keen/UiPopover.vue @@ -81,6 +81,8 @@ }, }, zIndex: Number, + positionX: Number, + positionY: Number, }, data() { @@ -188,10 +190,51 @@ * @public */ open() { - if (this.tip) { + if (!this.tip) { + return; + } + if (this.positionX || this.positionY) { + this.openInPosition(this.positionX, this.positionY); + } else { this.tip.show(); } }, + openInPosition(x, y) { + if (!this.tip.popperInstance) { + this.tip.show(); // Ensure popperInstance is created + } + + const isVerticalPlacement = ['top', 'bottom'].includes(this.position.split('-')[0]); + const variation = this.position.split('-')[1]; + const size = isVerticalPlacement ? this.$el.clientWidth : this.$el.clientHeight; + const middle = size / 2; + + const positionVariationMap = { + start: 0, + end: size, + }; + let verticalIncrease; + let horizontalIncrease; + if (isVerticalPlacement) { + verticalIncrease = 0; + horizontalIncrease = variation ? positionVariationMap[variation] : middle; + } else { + verticalIncrease = variation ? positionVariationMap[variation] : middle; + horizontalIncrease = 0; + } + + this.tip.popperInstance.reference.getBoundingClientRect = function getBoundingClientRect() { + return { + width: isVerticalPlacement ? size : 0, + height: isVerticalPlacement ? 0 : size, + top: y - verticalIncrease, + bottom: y + verticalIncrease, + left: x - horizontalIncrease, + right: x + horizontalIncrease + }; + }; + this.tip.show(); + }, close(options = { returnFocus: true }) { if (this.tip) { From 70648bb400ba32020c8b2feae65e354af56c1988 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 10:41:36 -0500 Subject: [PATCH 03/11] Add comments --- lib/KDropdownMenu.vue | 4 ++++ lib/keen/UiPopover.vue | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/KDropdownMenu.vue b/lib/KDropdownMenu.vue index 2e606b679..0e16e2f30 100644 --- a/lib/KDropdownMenu.vue +++ b/lib/KDropdownMenu.vue @@ -87,6 +87,10 @@ ].includes(val); }, }, + /** + * Whether or not the dropdown is a context menu, if true, the dropdown will open when + * the user right-clicks the parent element + */ isContextMenu : { type: Boolean, default: false, diff --git a/lib/keen/UiPopover.vue b/lib/keen/UiPopover.vue index 62b158737..da78e6a98 100644 --- a/lib/keen/UiPopover.vue +++ b/lib/keen/UiPopover.vue @@ -204,6 +204,8 @@ this.tip.show(); // Ensure popperInstance is created } + // This logic is similar to what tippy.js uses to position the popup near the cursor. + // see https://github.com/atomiks/tippyjs/blob/v4.3.5/src/createTippy.ts#L395 const isVerticalPlacement = ['top', 'bottom'].includes(this.position.split('-')[0]); const variation = this.position.split('-')[1]; const size = isVerticalPlacement ? this.$el.clientWidth : this.$el.clientHeight; @@ -223,7 +225,7 @@ horizontalIncrease = 0; } - this.tip.popperInstance.reference.getBoundingClientRect = function getBoundingClientRect() { + this.tip.popperInstance.reference.getBoundingClientRect = () => { return { width: isVerticalPlacement ? size : 0, height: isVerticalPlacement ? 0 : size, From 999becf4de00c7492e2877b5a18d2118b18737ad Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 12:40:47 -0500 Subject: [PATCH 04/11] Add docs --- docs/pages/kdropdownmenu.vue | 51 ++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/docs/pages/kdropdownmenu.vue b/docs/pages/kdropdownmenu.vue index 68c97ebcf..b535ef5cb 100644 --- a/docs/pages/kdropdownmenu.vue +++ b/docs/pages/kdropdownmenu.vue @@ -12,6 +12,57 @@

Please see the on the buttons page for more details about how to use with a button, and a code example.

+ +

Context menu

+ +

+ This component can be also used to create a context menu, which is a dropdown menu that appears when a user right-clicks on an element. +

+ + +

+ For example, you can right click on this paragraph to see a context menu. +

+ +
+ + +

+ Note that just one context menu can be open at a time. If you right-click on this paragraph, any other context menu will close. +

+ +
+ +

+ To achieve this, set the isContextMenu prop to true. + The context menu will then be attached to the parent element. +

+ +
+

...

+ +
+
From dd2ed75d4314a9a861a559be2b1201999a95a757 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 12:46:04 -0500 Subject: [PATCH 05/11] Lint files and add changelog --- CHANGELOG.md | 22 ++++++ lib/KDropdownMenu.vue | 114 ++++++++++++++-------------- lib/composables/_useKContextMenu.js | 13 ++-- 3 files changed, 87 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ef96652..0cef15f30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ Changelog is rather internal in nature. See release notes for the public overvie ## Version 3.x.x (`release-v3` branch) +- [#583] + - **Description:** KDropdownMenu menu support to show context menus with `isContextMenu` prop. + - **Products impact:** new API. + - **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/571. + - **Components:** KDropdownMenu. + - **Breaking:** no. + - **Impacts a11y:** no. + - **Guidance:** -. + +[#583]: https://github.com/learningequality/kolibri-design-system/pull/583 + +- [#583] + - **Description:** New `useKContextMenu` private composable + - **Products impact:** - . + - **Addresses:** - . + - **Components:** - . + - **Breaking:** - . + - **Impacts a11y:** - . + - **Guidance:** -. + +[#583]: https://github.com/learningequality/kolibri-design-system/pull/583 + - [#611] - **Description:** Adds a new `maxWidth` prop - **Products impact:** new API diff --git a/lib/KDropdownMenu.vue b/lib/KDropdownMenu.vue index 0e16e2f30..8e6819a07 100644 --- a/lib/KDropdownMenu.vue +++ b/lib/KDropdownMenu.vue @@ -1,36 +1,36 @@ diff --git a/lib/composables/_useKContextMenu.js b/lib/composables/_useKContextMenu.js index 17fcbfc29..0da9feeb8 100644 --- a/lib/composables/_useKContextMenu.js +++ b/lib/composables/_useKContextMenu.js @@ -1,11 +1,16 @@ import '../composition-api'; //Due to @vue/composition-api shortcomings, add plugin prior to use in kolibri, studio and tests -import { onMounted, onBeforeUnmount, ref, getCurrentInstance, computed } from '@vue/composition-api'; +import { + ref, + computed, + onMounted, + onBeforeUnmount, + getCurrentInstance, +} from '@vue/composition-api'; const activeMenu = ref(null); export default function useKContextMenu() { - const clientX = ref(0); const clientY = ref(0); @@ -14,7 +19,6 @@ export default function useKContextMenu() { const isActive = computed(() => activeMenu.value === id); - function showMenu(event) { event.preventDefault(); activeMenu.value = id; @@ -28,7 +32,6 @@ export default function useKContextMenu() { } } - onMounted(() => { const parent = instance.proxy.$el.parentElement; parent.addEventListener('contextmenu', showMenu); @@ -46,4 +49,4 @@ export default function useKContextMenu() { clientY, isActive, }; -}; +} From 4ebf0685822d59a7e482d3e7dc325b155d374449 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 12:57:35 -0500 Subject: [PATCH 06/11] Fix actions --- CHANGELOG.md | 2 +- lib/composables/_useKContextMenu.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cef15f30..046f96b46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ Changelog is rather internal in nature. See release notes for the public overvie - [#583] - **Description:** KDropdownMenu menu support to show context menus with `isContextMenu` prop. - **Products impact:** new API. - - **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/571. + - **Addresses:** https://github.com/learningequality/kolibri-design-system/issues/571, https://github.com/learningequality/studio/issues/4403. - **Components:** KDropdownMenu. - **Breaking:** no. - **Impacts a11y:** no. diff --git a/lib/composables/_useKContextMenu.js b/lib/composables/_useKContextMenu.js index 0da9feeb8..59dd8a7ea 100644 --- a/lib/composables/_useKContextMenu.js +++ b/lib/composables/_useKContextMenu.js @@ -1,4 +1,4 @@ -import '../composition-api'; //Due to @vue/composition-api shortcomings, add plugin prior to use in kolibri, studio and tests +import '../composition-api.js'; //Due to @vue/composition-api shortcomings, add plugin prior to use in kolibri, studio and tests import { ref, From 7edf6000c0f8ec5f843dd6646b33b410698dd5f3 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Wed, 20 Mar 2024 13:43:14 -0500 Subject: [PATCH 07/11] Add null check --- lib/composables/_useKContextMenu.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/composables/_useKContextMenu.js b/lib/composables/_useKContextMenu.js index 59dd8a7ea..4ea36ab69 100644 --- a/lib/composables/_useKContextMenu.js +++ b/lib/composables/_useKContextMenu.js @@ -40,7 +40,9 @@ export default function useKContextMenu() { onBeforeUnmount(() => { const parent = instance.proxy.$el.parentElement; - parent.removeEventListener('contextmenu', showMenu); + if (parent) { + parent.removeEventListener('contextmenu', showMenu); + } window.removeEventListener('click', hideMenu); }); From f31e9ccef30aea45e96ed14d89b1a383998a056e Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Mon, 25 Mar 2024 08:30:52 -0500 Subject: [PATCH 08/11] Propagate event object on KDropdownMenu select --- lib/KDropdownMenu.vue | 4 ++-- lib/keen/UiMenu.vue | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/KDropdownMenu.vue b/lib/KDropdownMenu.vue index 8e6819a07..6543fa870 100644 --- a/lib/KDropdownMenu.vue +++ b/lib/KDropdownMenu.vue @@ -192,11 +192,11 @@ this.closePopover(); } }, - handleSelection(selection) { + handleSelection(selection, $event) { /** * Emitted when an option is selected */ - this.$emit('select', selection); + this.$emit('select', selection, $event); this.closePopover(); }, closePopover() { diff --git a/lib/keen/UiMenu.vue b/lib/keen/UiMenu.vue index 6221d95b5..7da1beb51 100644 --- a/lib/keen/UiMenu.vue +++ b/lib/keen/UiMenu.vue @@ -22,8 +22,8 @@ :target="option[keys.target]" :type="option[keys.type]" - @click.native="selectOption(option)" - @keydown.enter.native="selectOption(option)" + @click.native="($event) => selectOption(option, $event)" + @keydown.enter.native="($event) => selectOption(option, $event)" @keydown.esc.native.esc="closeMenu" :style="[ activeOutline ]" @@ -107,12 +107,12 @@ }, methods: { - selectOption(option) { + selectOption(option, $event) { if (option.disabled || option.type === 'divider') { return; } - this.$emit('select', option); + this.$emit('select', option, $event); this.closeMenu(); }, closeMenu() { From da594a8f60377dc5797814ddc7e71937c3e9f75b Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Mon, 25 Mar 2024 08:34:48 -0500 Subject: [PATCH 09/11] Update changelog --- CHANGELOG.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 046f96b46..11e18e579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,8 +13,6 @@ Changelog is rather internal in nature. See release notes for the public overvie - **Impacts a11y:** no. - **Guidance:** -. -[#583]: https://github.com/learningequality/kolibri-design-system/pull/583 - - [#583] - **Description:** New `useKContextMenu` private composable - **Products impact:** - . @@ -24,6 +22,15 @@ Changelog is rather internal in nature. See release notes for the public overvie - **Impacts a11y:** - . - **Guidance:** -. +- [#583] + - **Description:** Expose the event object as second argument on KDropdownMenu's select event. + - **Products impact:** updated API. + - **Addresses:** - . + - **Components:** KDropdownMenu. + - **Breaking:** no. + - **Impacts a11y:** no. + - **Guidance:** -. + [#583]: https://github.com/learningequality/kolibri-design-system/pull/583 - [#611] From b2620315c9d54ba389cae6c540ac35e31226eb74 Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Mon, 25 Mar 2024 12:59:41 -0500 Subject: [PATCH 10/11] Add a header slot --- CHANGELOG.md | 9 +++++++++ lib/KDropdownMenu.vue | 3 +++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11e18e579..f34668db0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,15 @@ Changelog is rather internal in nature. See release notes for the public overvie - **Impacts a11y:** no. - **Guidance:** -. + - [#583] + - **Description:** KDropdownMenu menu support to show a header slot. + - **Products impact:** new API. + - **Addresses:** - . + - **Components:** KDropdownMenu. + - **Breaking:** no. + - **Impacts a11y:** no. + - **Guidance:** -. + [#583]: https://github.com/learningequality/kolibri-design-system/pull/583 - [#611] diff --git a/lib/KDropdownMenu.vue b/lib/KDropdownMenu.vue index 6543fa870..65728c0e3 100644 --- a/lib/KDropdownMenu.vue +++ b/lib/KDropdownMenu.vue @@ -15,6 +15,8 @@ @close="handleClose" @open="handleOpen" > + + + From dcb98c10d165bf38572ae9a54f9134b870fd7acc Mon Sep 17 00:00:00 2001 From: Alex Velez Date: Mon, 25 Mar 2024 13:03:19 -0500 Subject: [PATCH 11/11] Fix chagelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f34668db0..14371275b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,7 @@ Changelog is rather internal in nature. See release notes for the public overvie - **Impacts a11y:** no. - **Guidance:** -. - - [#583] +- [#583] - **Description:** KDropdownMenu menu support to show a header slot. - **Products impact:** new API. - **Addresses:** - .