|
Line 0
Link Here
|
|
|
1 |
<template> |
| 2 |
<div> |
| 3 |
<div id="dashboard-header"> |
| 4 |
<p> |
| 5 |
{{ $__("Customize your dashboard by adding widgets.") }} |
| 6 |
<a href="#" id="open-widget-picker" @click="toggleWidgetPicker"> |
| 7 |
{{ $__("Open Widget Picker") }} |
| 8 |
</a> |
| 9 |
</p> |
| 10 |
</div> |
| 11 |
</div> |
| 12 |
<div class="row"> |
| 13 |
<div class="col-md-6 dashboard-left-col"> |
| 14 |
<draggable |
| 15 |
:ghost="true" |
| 16 |
@drag="handleDrag" |
| 17 |
class="dragArea list-group w-full" |
| 18 |
:list="selectedWidgetsLeft" |
| 19 |
group="widgets" |
| 20 |
> |
| 21 |
<component |
| 22 |
v-for="(widget, index) in selectedWidgetsLeft" |
| 23 |
:key="widget.name" |
| 24 |
:is="widget" |
| 25 |
display="dashboard" |
| 26 |
dashboardColumn="left" |
| 27 |
@moveWidget="moveWidget" |
| 28 |
@removed="removeWidget(widget)" |
| 29 |
></component> |
| 30 |
</draggable> |
| 31 |
</div> |
| 32 |
<div class="col-md-6 dashboard-right-col"> |
| 33 |
<draggable |
| 34 |
:ghost="true" |
| 35 |
@drag="handleDrag" |
| 36 |
class="dragArea list-group w-full" |
| 37 |
:list="selectedWidgetsRight" |
| 38 |
group="widgets" |
| 39 |
> |
| 40 |
<component |
| 41 |
v-for="(widget, index) in selectedWidgetsRight" |
| 42 |
:key="widget.name" |
| 43 |
:is="widget" |
| 44 |
display="dashboard" |
| 45 |
dashboardColumn="right" |
| 46 |
@moveWidget="moveWidget" |
| 47 |
@removed="removeWidget(widget)" |
| 48 |
></component> |
| 49 |
</draggable> |
| 50 |
</div> |
| 51 |
</div> |
| 52 |
</template> |
| 53 |
|
| 54 |
<script> |
| 55 |
import { onMounted, ref, watch, inject } from "vue"; |
| 56 |
import WidgetPicker from "./WidgetPicker.vue"; |
| 57 |
import VueCookies from "vue-cookies"; |
| 58 |
import { $__ } from "@koha-vue/i18n"; |
| 59 |
import { VueDraggableNext } from "vue-draggable-next"; |
| 60 |
|
| 61 |
export default { |
| 62 |
setup(props) { |
| 63 |
const availableWidgets = props.availableWidgets; |
| 64 |
const selectedWidgetsLeft = ref([]); |
| 65 |
const selectedWidgetsRight = ref([]); |
| 66 |
|
| 67 |
const { setComponentDialog } = inject("mainStore"); |
| 68 |
|
| 69 |
const handleDrag = event => { |
| 70 |
const dropContext = event.relatedContext; |
| 71 |
if (dropContext) { |
| 72 |
const dropElement = dropContext.element; |
| 73 |
dropElement.style.border = "2px dotted #ccc"; |
| 74 |
event.onDragEnd(() => { |
| 75 |
dropElement.style.border = ""; |
| 76 |
}); |
| 77 |
} |
| 78 |
}; |
| 79 |
|
| 80 |
function removeWidget(widget) { |
| 81 |
const indexLeft = selectedWidgetsLeft.value.indexOf(widget); |
| 82 |
const indexRight = selectedWidgetsRight.value.indexOf(widget); |
| 83 |
if (indexLeft > -1) { |
| 84 |
selectedWidgetsLeft.value.splice(indexLeft, 1); |
| 85 |
} else if (indexRight > -1) { |
| 86 |
selectedWidgetsRight.value.splice(indexRight, 1); |
| 87 |
} |
| 88 |
} |
| 89 |
|
| 90 |
function addWidget(widget) { |
| 91 |
if ( |
| 92 |
!selectedWidgetsLeft.value.includes(widget) && |
| 93 |
!selectedWidgetsRight.value.includes(widget) |
| 94 |
) { |
| 95 |
if ( |
| 96 |
selectedWidgetsLeft.value.length <= |
| 97 |
selectedWidgetsRight.value.length |
| 98 |
) { |
| 99 |
selectedWidgetsLeft.value.unshift(widget); |
| 100 |
} else { |
| 101 |
selectedWidgetsRight.value.unshift(widget); |
| 102 |
} |
| 103 |
} |
| 104 |
} |
| 105 |
|
| 106 |
function moveWidget(widget) { |
| 107 |
if ( |
| 108 |
selectedWidgetsLeft.value.some( |
| 109 |
element => element.name === widget.componentName |
| 110 |
) |
| 111 |
) { |
| 112 |
selectedWidgetsRight.value.unshift( |
| 113 |
selectedWidgetsLeft.value.find( |
| 114 |
element => element.name === widget.componentName |
| 115 |
) |
| 116 |
); |
| 117 |
selectedWidgetsLeft.value = selectedWidgetsLeft.value.filter( |
| 118 |
element => element.name !== widget.componentName |
| 119 |
); |
| 120 |
} else if ( |
| 121 |
selectedWidgetsRight.value.some( |
| 122 |
element => element.name === widget.componentName |
| 123 |
) |
| 124 |
) { |
| 125 |
selectedWidgetsLeft.value.unshift( |
| 126 |
selectedWidgetsRight.value.find( |
| 127 |
element => element.name === widget.componentName |
| 128 |
) |
| 129 |
); |
| 130 |
selectedWidgetsRight.value = selectedWidgetsRight.value.filter( |
| 131 |
element => element.name !== widget.componentName |
| 132 |
); |
| 133 |
} |
| 134 |
} |
| 135 |
|
| 136 |
const toggleWidgetPicker = e => { |
| 137 |
setComponentDialog({ |
| 138 |
title: $__("Customize your dashboard widgets"), |
| 139 |
cancel_label: $__("Close"), |
| 140 |
componentPath: |
| 141 |
"@koha-vue/components/ModuleDashboard/WidgetPicker.vue", |
| 142 |
componentProps: { |
| 143 |
selectedWidgetsLeft: selectedWidgetsLeft.value, |
| 144 |
selectedWidgetsRight: selectedWidgetsRight.value, |
| 145 |
availableWidgets: availableWidgets, |
| 146 |
}, |
| 147 |
componentListeners: { |
| 148 |
added: widget => addWidget(widget), |
| 149 |
removed: widget => removeWidget(widget), |
| 150 |
}, |
| 151 |
}); |
| 152 |
}; |
| 153 |
|
| 154 |
onMounted(() => { |
| 155 |
const storedWidgets = VueCookies.get("dashboard-widgets"); |
| 156 |
if (storedWidgets) { |
| 157 |
const { left, right } = storedWidgets; |
| 158 |
left.forEach(widgetName => { |
| 159 |
const widget = availableWidgets.find( |
| 160 |
widget => widget.name === widgetName |
| 161 |
); |
| 162 |
if (widget) { |
| 163 |
selectedWidgetsLeft.value.push(widget); |
| 164 |
} |
| 165 |
}); |
| 166 |
right.forEach(widgetName => { |
| 167 |
const widget = availableWidgets.find( |
| 168 |
widget => widget.name === widgetName |
| 169 |
); |
| 170 |
if (widget) { |
| 171 |
selectedWidgetsRight.value.push(widget); |
| 172 |
} |
| 173 |
}); |
| 174 |
} else { |
| 175 |
availableWidgets.forEach(widget => addWidget(widget)); |
| 176 |
} |
| 177 |
}); |
| 178 |
|
| 179 |
watch( |
| 180 |
[selectedWidgetsLeft, selectedWidgetsRight], |
| 181 |
([left, right]) => { |
| 182 |
const leftWidgetNames = left.map(widget => widget.name); |
| 183 |
const rightWidgetNames = right.map(widget => widget.name); |
| 184 |
VueCookies.set( |
| 185 |
"dashboard-widgets", |
| 186 |
JSON.stringify({ |
| 187 |
left: leftWidgetNames, |
| 188 |
right: rightWidgetNames, |
| 189 |
}), |
| 190 |
"30d" |
| 191 |
); |
| 192 |
}, |
| 193 |
{ deep: true } |
| 194 |
); |
| 195 |
|
| 196 |
return { |
| 197 |
...props, |
| 198 |
selectedWidgetsLeft, |
| 199 |
selectedWidgetsRight, |
| 200 |
addWidget, |
| 201 |
handleDrag, |
| 202 |
removeWidget, |
| 203 |
moveWidget, |
| 204 |
toggleWidgetPicker, |
| 205 |
}; |
| 206 |
}, |
| 207 |
components: { |
| 208 |
WidgetPicker, |
| 209 |
draggable: VueDraggableNext, |
| 210 |
}, |
| 211 |
name: "ModuleDashboard", |
| 212 |
}; |
| 213 |
</script> |
| 214 |
<style> |
| 215 |
.sortable-ghost { |
| 216 |
height: 50px !important; |
| 217 |
} |
| 218 |
|
| 219 |
.sortable-ghost.sortable-chosen > * { |
| 220 |
visibility: hidden; |
| 221 |
} |
| 222 |
</style> |