Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhancement: Option to display some actions in a popup menu instead of the actions column #40

73 changes: 73 additions & 0 deletions examples/src/components/examples/ExamplePopupMenuActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<template>
<div>
<InfineonDatatable
:data="rows"
:columns="columns"
:default-sort="{ key: 'name', type: 'D' }"
:can-edit="true"
:additional-actions="[ {
title: 'Print to Console.log',
label: 'Console.log',
action: logToConsole,
icon: ['fas', 'exclamation-triangle']},
]"
:popup-menu-actions="[ {
title: 'Open an alert box',
label: 'Alert',
action: showAlert,
icon: ['fas', 'terminal']
},
{
title: 'Open an alert box',
label: 'Alert',
action: showAlert,
icon: ['fas', 'terminal'],
canCloseMenu: true
} ]"
@on-menu-button-click="onMenuOpened"
/>
<!-- don't forget to load any fontawesome icons in /plugins/fontawesome.js!! -->
</div>
</template>

<script setup>
import { InfineonDatatable } from '../../../../lib';

const logToConsole = (row) => {
// eslint-disable-next-line no-console
console.log('we print to current row to console');
// eslint-disable-next-line no-console
console.log(row);
};

const showAlert = (row) => {
// eslint-disable-next-line no-alert
alert(JSON.stringify(row));
};

const onMenuOpened = (row) => {
console.log(`Menu triggered for row ${row.id}`)
}

const rows = [
{ id: 1, name: 'item1' },
{ id: 2, name: 'item2' },
{ id: 3, name: 'item3' },
{ id: 4, name: 'item4' },
];

const columns = [
{
key: 'id',
title: 'ID column title',
sortable: true,
sortType: 'NUMBER',
},
{
key: 'name',
title: 'Name',
sortable: true,
sortType: 'STRING',
},
];
</script>
4 changes: 4 additions & 0 deletions examples/src/components/global/TheHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ const links = ref([
label: 'Additional Actions',
routeName: 'exampleAdditionalActions',
},
{
label: 'Popup Menu Actions',
routeName: 'examplePopupMenuActions',
},
{
label: 'CSV Export',
routeName: 'exampleCsvExport',
Expand Down
6 changes: 6 additions & 0 deletions examples/src/router/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ExampleEdit from '../components/examples/ExampleEdit.vue';
import ExampleHideColumns from '../components/examples/ExampleHideColumns.vue';
import ExampleStoreHiddenColumnsPerView from '../components/examples/ExampleStoreHiddenColumnsPerView.vue';
import ExampleAdditionalActions from '../components/examples/ExampleAdditionalActions.vue';
import ExamplePopupMenuActions from '../components/examples/ExamplePopupMenuActions.vue';
import ExampleDynamicColumnTitle from '../components/examples/ExampleDynamicColumnTitle.vue';
import ExampleConditionallyHideColumns from '../components/examples/ExampleConditionallyHideColumns.vue';
import ExampleCsvExport from '../components/examples/ExampleCsvExport.vue';
Expand Down Expand Up @@ -57,6 +58,11 @@ const router = createRouter({
name: 'exampleConditionallyHideAdditionalActions',
component: ExampleConditionallyHideAdditionalActions,
},
{
path: '/example-popup-menu-actions',
name: 'examplePopupMenuActions',
component: ExamplePopupMenuActions,
},
{
path: '/example-conditionally-hide-columns',
name: 'exampleConditionallyHideColumns',
Expand Down
15 changes: 13 additions & 2 deletions lib/datatable/InfineonDatatable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,13 @@
:row-is-in-edit-mode="(row.id) === (rowInEditMode?.id)"
:can-edit="canEdit"
:additional-actions="additionalActions"
:popup-menu-actions="popupMenuActions"
:is-menu-open="(row.id) === (rowMenuIsOpen?.id)"
@start-edit-row="startEditRow"
@save-row="saveRow"
@cancel-row="cancelRow"
@edit-mode-value="editModeValue"
@on-menu-button-click="closeOtherActionsPopupMenus"
>
<template
v-for="(_, name) in $slots"
Expand Down Expand Up @@ -158,9 +161,9 @@ const props = defineProps({
// [ { label: '', action: (row) => {}, icon: ['fas', 'list-ol'] } ]
additionalExportColumns: { type: Array, default: () => [] },
// customColHidden: { type: String, default: 'Custom column' },

popupMenuActions: { type: Array, default: () => [] },
});
const emit = defineEmits(['saveRow', 'editModeValue', 'cancelRow']);
const emit = defineEmits(['saveRow', 'editModeValue', 'cancelRow', 'onMenuButtonClick']);

const {
data, columns, localStorageKey,
Expand All @@ -169,6 +172,7 @@ const sortColumn = ref(props.defaultSort);
const currentPage = ref(0);
const pageSize = ref(10);
const rowInEditMode = ref(undefined);
const rowMenuIsOpen = ref(undefined);
const count = ref(0);

const hiddenColumnKeys = ref([]);
Expand Down Expand Up @@ -240,6 +244,8 @@ const processedData = computed(() => {
return sliced;
});

const closeRowMenusMap = ref(new Map(processedData.value.map((_, index) => [index, false])));

// store hidden columns for a predefined property localStorageKey - if not defined, use default
const hiddenColumnsLocalStorageKey = computed(() => localStorageKey.value || realColumns
.value.map((c) => c.key).sort().join());
Expand Down Expand Up @@ -279,6 +285,11 @@ function editModeValue(row) {
emit('editModeValue', row);
}

function closeOtherActionsPopupMenus(row) {
rowMenuIsOpen.value = row ? row : undefined;
emit('onMenuButtonClick', row);
}

function cancelRow(row) {
emit('cancelRow', row);
rowInEditMode.value = undefined;
Expand Down
122 changes: 110 additions & 12 deletions lib/datatable/InfineonDatatableRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,67 @@
class="btn btn-outline-primary btn-sm"
:class="{'ms-1': idx > 0}"
:title="additionalAction.title"
:style="additionalAction.style ? additionalAction.style : ''"
@click="additionalAction.action(row)"
>
<font-awesome-icon
v-if="additionalAction.icon"
:icon="additionalAction.icon"
/>
<span v-if="additionalAction.label">
{{ additionalAction.label }}
</span>
<div style="display: flex;">
<div
:style="additionalAction.label ? 'margin-right: 4px;' : ''"
>
<font-awesome-icon
v-if="additionalAction.icon"
:icon="additionalAction.icon"
/>
</div>
<span v-if="additionalAction.label">
{{ additionalAction.label }}
</span>
</div>
</button>
</template>
<button
v-if="canEdit && (popupMenuActions.length > 0)"
ref="menuButtonRef"
class="btn btn-outline-primary btn-sm me-1"
:style="additionalActions.length > 0 ? 'margin-left: 4px;' : ''"
style="position: relative"
@click="showPopupMenu"
>
<font-awesome-icon :icon="['fas', 'ellipsis-h']" />
<div
v-show="showMenu && isMenuOpen"
ref="menuRef"
class="menu"
>
<template
v-for="(popupMenuAction, idx) in popupMenuActions"
:key="idx"
>
<button
v-show="!popupMenuAction.visible || popupMenuAction.visible(row)"
class="btn btn-outline-primary btn-sm"
:class="{'ms-1': idx > 0}"
:title="popupMenuAction.title"
:style="popupMenuAction.style ? popupMenuAction.style : ''"
@click="(event) => popupMenuActionOnClick(event, popupMenuAction.action, popupMenuAction.canCloseMenu)"
>
<div style="display: flex;">
<div
:style="popupMenuAction.label ? 'margin-right: 4px;' : ''"
>
<font-awesome-icon
v-if="popupMenuAction.icon"
:icon="popupMenuAction.icon"
/>
</div>
<span v-if="popupMenuAction.label">
{{ popupMenuAction.label }}
</span>
</div>
</button>
</template>
</div>
</button>
</td>
<DatatableRowColumn
v-for="(column, index) in shownColumns"
Expand Down Expand Up @@ -152,7 +202,7 @@
<script setup>
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import {
toRefs, computed, ref,
toRefs, computed, ref, onMounted, watch
} from 'vue';
import DatatableRowColumn from './InfineonDatatableRowColumn.vue';

Expand All @@ -161,21 +211,23 @@ const props = defineProps({
rowIndex: { type: Number, default: undefined },
columns: { type: Array, default: () => [] },
canEdit: Boolean,
isMenuOpen: Boolean,
rowIsInEditMode: Boolean,
hiddenColumnKeys: { type: Array, default: () => [] },
additionalActions: { type: Object, default: () => {} }, // { label: '', action: (row) => {} }
popupMenuActions: { type: Array, default: () => [] },
});

const {
row, columns, rowIndex, hiddenColumnKeys, canEdit, additionalActions,
row, columns, rowIndex, hiddenColumnKeys, canEdit, additionalActions, popupMenuActions, popupMenuOpen, isMenuOpen
} = toRefs(props);
const emit = defineEmits(['startEditRow', 'saveRow', 'cancelRow', 'onRowButtonClick', 'editRow', 'editModeValue']);
const emit = defineEmits(['startEditRow', 'saveRow', 'cancelRow', 'onRowButtonClick', 'editRow', 'editModeValue', 'onMenuButtonClick']);

const shownColumns = computed(() => columns.value
.filter((c) => !c.hidable || !hiddenColumnKeys.value.includes(c.key)));
.filter((c) => !c.hidable || !hiddenColumnKeys.value.includes(c.key)));

const hiddenColumns = computed(() => columns.value
.filter((c) => c.hidable && hiddenColumnKeys.value.includes(c.key)));
.filter((c) => c.hidable && hiddenColumnKeys.value.includes(c.key)));

const calculateColspan = computed(() => {
let colspan = shownColumns.value.length;
Expand All @@ -191,6 +243,41 @@ const calculateColspan = computed(() => {
const editRow = ref(row.value);
const expanded = ref(false);

const menuRef = ref(null);
const menuButtonRef = ref(null);
const menuActionButtonRefs = ref([]);
const forcedMenuClose = ref(false);
const showMenu = ref(false);

function showPopupMenu(event) {
emit('onMenuButtonClick', row.value);
showMenu.value =!showMenu.value || !menuButtonRef.value.contains(event.target);
}

function popupMenuActionOnClick(_, action, canCloseMenu) {
if (canCloseMenu) {
forcedMenuClose.value = true;
}
action(row.value)
}

function closeMenuOnClickOutside(event) {
if (menuRef.value?.contains(event.target)) {
showMenu.value = !forcedMenuClose.value;
forcedMenuClose.value = false;
}
}

onMounted(() => {
document.addEventListener('click', closeMenuOnClickOutside);
});

watch(isMenuOpen, () => {
if (!isMenuOpen.value) {
showMenu.value = false;
}
})

function startEditRow() {
// aktuelle row in editier row kopieren
editRow.value = { ...row.value };
Expand All @@ -209,6 +296,17 @@ function cancelRow() {
</script>

<style scoped>
.menu {
position: absolute;
background-color: white;
border: 2px solid var(--bs-table-border-color);
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 8px;
margin-left: -3px;
margin-top: -2px;
z-index: 4656546797;
}
.row-odd {
--bs-table-accent-bg: var(--bs-table-striped-bg);
color: var(--bs-table-striped-color);
Expand Down
2 changes: 2 additions & 0 deletions lib/plugins/fontawesome.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
faAngleLeft,
faAngleRight,
faAngleDoubleRight,
faEllipsisH,
} from '@fortawesome/free-solid-svg-icons';

library.add(
Expand All @@ -28,4 +29,5 @@ library.add(
faAngleLeft,
faAngleRight,
faAngleDoubleRight,
faEllipsisH,
);
Loading