Improved layout for items and sub-items

This commit is contained in:
Alicia Sykes 2022-04-06 00:04:47 +01:00
parent 151028c8cf
commit 57abd67cf9
5 changed files with 143 additions and 52 deletions

View File

@ -1,5 +1,5 @@
<template ref="container">
<div :class="`item-wrapper wrap-size-${size}`" >
<div :class="`item-wrapper wrap-size-${size} span-${makeColumnCount}`" >
<a @click="itemClicked"
@long-press="openContextMenu"
@contextmenu.prevent
@ -11,8 +11,7 @@
v-tooltip="getTooltipOptions()"
rel="noopener noreferrer" tabindex="0"
:id="`link-${item.id}`"
:style=
"`--open-icon:${unicodeOpeningIcon};color:${item.color};background:${item.backgroundColor}`"
:style="customStyle"
>
<!-- Item Text -->
<div :class="`tile-title ${!item.icon? 'bounce no-icon': ''}`" :id="`tile-${item.id}`" >
@ -77,6 +76,8 @@ export default {
itemSize: String,
parentSectionTitle: String, // Title of parent section (for add new)
isAddNew: Boolean, // Only set if 'fake' item used as Add New button
sectionWidth: Number, // Width of parent section
sectionDisplayData: Object,
},
components: {
Icon,
@ -88,6 +89,15 @@ export default {
EditModeIcon,
},
computed: {
makeColumnCount() {
if ((this.sectionDisplayData || {}).itemCountX) return this.sectionDisplayData.itemCountX;
if (this.sectionWidth < 380) return 1;
if (this.sectionWidth < 520) return 2;
if (this.sectionWidth < 730) return 3;
if (this.sectionWidth < 1000) return 4;
if (this.sectionWidth < 1300) return 5;
return 0;
},
/* Based on item props, adjust class names */
makeClassList() {
const { isAddNew, isEditMode, size } = this;
@ -183,6 +193,17 @@ export default {
&.wrap-size-large {
flex-basis: 12rem;
}
&.wrap-size-small {
flex-grow: revert;
&.span-1 { min-width: 100%; }
&.span-2 { min-width: 50%; }
&.span-3 { min-width: 33%; }
&.span-4 { min-width: 25%; }
&.span-5 { min-width: 20%; }
&.span-6 { min-width: 16%; }
&.span-7 { min-width: 14%; }
&.span-8 { min-width: 12.5%; }
}
}
.item {
@ -292,7 +313,6 @@ p.description {
align-items: center;
height: 2rem;
padding-top: 4px;
max-width: 14rem;
div img {
width: 2rem;
}

View File

@ -77,6 +77,7 @@ export default {
posX: Number, // The X coordinate for positioning
posY: Number, // The Y coordinate for positioning
show: Boolean, // Should show or hide the menu
disableEdit: Boolean, // Disable editing for certain items
},
computed: {
isMenuDisabled() {
@ -86,6 +87,7 @@ export default {
return this.$store.state.editMode;
},
isEditAllowed() {
if (this.disableEdit) return false;
return this.$store.getters.permissions.allowViewConfig;
},
},

View File

@ -11,7 +11,8 @@
:cutToHeight="displayData.cutToHeight"
@openEditSection="openEditSection"
@openContextMenu="openContextMenu"
:id="`section-outer-${groupId}`"
:id="sectionRef"
:ref="sectionRef"
>
<!-- If no items, show message -->
<div v-if="isEmpty" class="no-items">
@ -23,17 +24,13 @@
:style="gridStyle" :id="`section-${groupId}`"
> <!-- Show for each item -->
<template v-for="(item) in sortedItems">
<div v-if="item.subItems" :key="item.id" class="sub-items-group">
<template v-for="(subItem, subIndex) in item.subItems">
<SubItem
:key="subIndex"
:id="`${item.id}-sub-${subIndex}`"
:url="subItem.url"
:icon="subItem.icon"
:title="subItem.title"
/>
</template>
</div>
<SubItemGroup
v-if="item.subItems"
:key="item.id"
:itemId="item.id"
:title="item.title"
:subItems="item.subItems"
/>
<Item
v-else
:item="item"
@ -43,6 +40,8 @@
@itemClicked="$emit('itemClicked')"
@triggerModal="triggerModal"
:isAddNew="false"
:sectionWidth="sectionWidth"
:sectionDisplayData="displayData"
/>
</template>
<!-- When in edit mode, show additional item, for Add New item -->
@ -101,7 +100,7 @@
<script>
import router from '@/router';
import Item from '@/components/LinkItems/Item.vue';
import SubItem from '@/components/LinkItems/SubItem.vue';
import SubItemGroup from '@/components/LinkItems/SubItemGroup.vue';
import WidgetBase from '@/components/Widgets/WidgetBase';
import Collapsable from '@/components/LinkItems/Collapsable.vue';
import IframeModal from '@/components/LinkItems/IframeModal.vue';
@ -131,7 +130,7 @@ export default {
Collapsable,
ContextMenu,
Item,
SubItem,
SubItemGroup,
WidgetBase,
IframeModal,
EditSection,
@ -144,6 +143,8 @@ export default {
posX: undefined,
posY: undefined,
},
sectionWidth: 0,
resizeObserver: null,
};
},
computed: {
@ -169,6 +170,9 @@ export default {
isEmpty() {
return !this.hasItems && !this.hasWidgets;
},
sectionRef() {
return `section-outer-${this.groupId}`;
},
/* If the sortBy attribute is specified, then return sorted data */
sortedItems() {
let { items } = this;
@ -294,6 +298,22 @@ export default {
closeContextMenu() {
this.contextMenuOpen = false;
},
/* Calculate width of section, used to dynamically set number of columns */
calculateSectionWidth() {
const secElem = this.$refs[this.sectionRef];
if (secElem) this.sectionWidth = secElem.$el.clientWidth;
},
},
mounted() {
// Set the section width, and recalculate when section resized
this.resizeObserver = new ResizeObserver(this.calculateSectionWidth)
.observe(this.$refs[this.sectionRef].$el);
},
beforeDestroy() {
// If resize observer set, and element still present, then de-register
if (this.resizeObserver && this.$refs[this.sectionRef]) {
this.resizeObserver.unobserve(this.$refs[this.sectionRef].$el);
}
},
};
</script>
@ -375,18 +395,4 @@ export default {
}
}
.sub-items-group {
display: grid;
margin: 0.5rem;
padding: 0.1rem;
flex-grow: 1;
flex-basis: 6rem;
grid-template-columns: repeat(3, minmax(0, 1fr));
color: var(--item-text-color);
border: 1px solid var(--outline-color);
border-radius: var(--curve-factor);
text-decoration: none;
transition: all 0.2s ease-in-out 0s;
}
</style>

View File

@ -1,21 +1,36 @@
<template ref="container">
<div class="sub-item-wrapper">
<a @click="beforeLaunchItem"
<a @click="itemClicked"
@contextmenu.prevent
@long-press="openContextMenu"
@mouseup.right="openContextMenu"
v-longPress="true"
:href="hyperLinkHref"
:target="anchorTarget"
class="sub-item-link item"
v-tooltip="subItemTooltip"
rel="noopener noreferrer" tabindex="0"
:id="`link-${id}`"
class="sub-item-link item"
>
<!-- Item Icon -->
<Icon :icon="icon" :url="url" size="small" class="sub-icon-img bounce" />
</a>
<!-- Right-click context menu -->
<ContextMenu
:show="contextMenuOpen && !isAddNew"
v-click-outside="closeContextMenu"
:posX="contextPos.posX"
:posY="contextPos.posY"
:id="`context-menu-${id}`"
:disableEdit="true"
@launchItem="launchItem"
/>
</div>
</template>
<script>
import Icon from '@/components/LinkItems/ItemIcon.vue';
import ContextMenu from '@/components/LinkItems/ItemContextMenu';
import ItemMixin from '@/mixins/ItemMixin';
import { targetValidator } from '@/utils/ConfigHelpers';
@ -34,6 +49,7 @@ export default {
},
components: {
Icon,
ContextMenu,
},
computed: {
subItemTooltip() {
@ -43,23 +59,7 @@ export default {
data() {
return {};
},
methods: {
beforeLaunchItem(e) {
if (this.isEditMode) return;
if (e.altKey) {
e.preventDefault();
this.launchItem('modal');
} else if (this.accumulatedTarget === 'modal') {
this.launchItem('modal');
} else if (this.accumulatedTarget === 'workspace') {
this.launchItem('workspace');
} else if (this.accumulatedTarget === 'clipboard') {
this.launchItem('clipboard');
}
// Clear search bar
this.$emit('itemClicked');
},
},
methods: {},
};
</script>
@ -68,7 +68,7 @@ export default {
flex-grow: 1;
flex-basis: 6rem;
display: flex;
a {
a.sub-item-link {
border: none;
margin: 0.2rem;
.sub-icon-img {

View File

@ -0,0 +1,63 @@
<template>
<div class="sub-items-group" :style="`--sub-item-col-count: ${columnCount}`">
<p v-if="title" class="sub-item-group-title">{{ title }}</p>
<SubItem
v-for="(subItem, subIndex) in subItems"
:key="subIndex"
:id="`${itemId}-sub-${subIndex}`"
:url="subItem.url"
:icon="subItem.icon"
:title="subItem.title"
/>
</div>
</template>
<script>
import SubItem from '@/components/LinkItems/SubItem.vue';
export default {
props: {
itemId: String,
subItems: Array,
title: String,
subItemGridSize: Number,
},
components: {
SubItem,
},
computed: {
/* Determine number of columns to split items into, depending on number of items */
columnCount() {
if (this.subItemGridSize) return this.subItemGridSize;
const numItems = this.subItems.length;
if (numItems >= 10) return 4;
if (numItems >= 5) return 3;
if (numItems >= 2) return 2;
if (numItems === 1) return 1;
return 2;
},
},
};
</script>
<style scoped lang="scss">
.sub-items-group {
display: grid;
margin: 0.5rem;
padding: 0.1rem;
flex-grow: 1;
flex-basis: 6rem;
grid-template-columns: repeat(var(--sub-item-col-count, 3), minmax(0, 1fr));
color: var(--item-text-color);
border: 1px solid var(--outline-color);
border-radius: var(--curve-factor);
text-decoration: none;
transition: all 0.2s ease-in-out 0s;
color: var(--item-text-color);
p.sub-item-group-title {
margin: 0 auto;
cursor: default;
grid-column-start: span var(--sub-item-col-count, 3);
}
}
</style>