mirror of https://github.com/Lissy93/dashy.git
⚡ Many big improvments to items + sections
This commit is contained in:
parent
b1de7bc7e5
commit
a6f3c90722
|
@ -61,6 +61,7 @@ export default {
|
|||
text-align: center;
|
||||
height: fit-content;
|
||||
margin: 10px;
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -122,10 +122,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
editMenuOpen: false,
|
||||
customStyles: {
|
||||
color: this.item.color,
|
||||
background: this.item.backgroundColor,
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -312,7 +308,8 @@ p.description {
|
|||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 2rem;
|
||||
padding-top: 4px;
|
||||
padding-top: 0.25rem;
|
||||
padding-left: 0.5rem;
|
||||
div img {
|
||||
width: 2rem;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
:itemId="item.id"
|
||||
:title="item.title"
|
||||
:subItems="item.subItems"
|
||||
@triggerModal="triggerModal"
|
||||
/>
|
||||
<Item
|
||||
v-else
|
||||
|
@ -56,6 +57,7 @@
|
|||
:parentSectionTitle="title"
|
||||
key="add-new"
|
||||
class="add-new-item"
|
||||
:sectionWidth="sectionWidth"
|
||||
:itemSize="itemSize"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
class="sub-item-link item"
|
||||
>
|
||||
<!-- Item Icon -->
|
||||
<Icon :icon="icon" :url="url" size="small" class="sub-icon-img bounce" />
|
||||
<Icon :icon="item.icon" :url="item.url"
|
||||
size="small" v-bind:style="customStyles" class="sub-icon-img bounce" />
|
||||
</a>
|
||||
<!-- Right-click context menu -->
|
||||
<ContextMenu
|
||||
|
@ -32,20 +33,14 @@
|
|||
import Icon from '@/components/LinkItems/ItemIcon.vue';
|
||||
import ContextMenu from '@/components/LinkItems/ItemContextMenu';
|
||||
import ItemMixin from '@/mixins/ItemMixin';
|
||||
import { targetValidator } from '@/utils/ConfigHelpers';
|
||||
// import { targetValidator } from '@/utils/ConfigHelpers';
|
||||
|
||||
export default {
|
||||
name: 'Item',
|
||||
mixins: [ItemMixin],
|
||||
props: {
|
||||
id: String, // The unique ID of a tile (e.g. 001)
|
||||
title: String, // The main text of tile, required
|
||||
icon: String, // Optional path to icon, within public/img/tile-icons
|
||||
url: String, // URL to the resource, optional but recommended
|
||||
target: { // Where resource will open, either 'newtab', 'sametab' or 'modal'
|
||||
type: String,
|
||||
validator: targetValidator,
|
||||
},
|
||||
item: Object,
|
||||
},
|
||||
components: {
|
||||
Icon,
|
||||
|
@ -69,7 +64,6 @@ export default {
|
|||
flex-basis: 6rem;
|
||||
display: flex;
|
||||
a.sub-item-link {
|
||||
border: none;
|
||||
margin: 0.2rem;
|
||||
.sub-icon-img {
|
||||
margin: 0;
|
||||
|
|
|
@ -5,9 +5,8 @@
|
|||
v-for="(subItem, subIndex) in subItems"
|
||||
:key="subIndex"
|
||||
:id="`${itemId}-sub-${subIndex}`"
|
||||
:url="subItem.url"
|
||||
:icon="subItem.icon"
|
||||
:title="subItem.title"
|
||||
:item="subItem"
|
||||
@triggerModal="triggerModal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -37,6 +36,12 @@ export default {
|
|||
return 2;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Pass open modal emit event up */
|
||||
triggerModal(url) {
|
||||
this.$emit('triggerModal', url);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,30 +1,27 @@
|
|||
<template>
|
||||
<div :class="`minimal-section-inner ${selected ? 'selected' : ''} ${showAll ? 'show-all': ''}`">
|
||||
<div class="section-items" v-if="items && (selected || showAll)">
|
||||
<Item
|
||||
v-for="(item, index) in items"
|
||||
:item="item"
|
||||
:id="`${index}_${makeId(item.title)}`"
|
||||
:key="`${index}_${makeId(item.title)}`"
|
||||
:url="item.url"
|
||||
:title="item.title"
|
||||
:description="item.description"
|
||||
:icon="item.icon"
|
||||
:target="item.target"
|
||||
:color="item.color"
|
||||
:backgroundColor="item.backgroundColor"
|
||||
:statusCheckUrl="item.statusCheckUrl"
|
||||
:statusCheckHeaders="item.statusCheckHeaders"
|
||||
:itemSize="itemSize"
|
||||
:hotkey="item.hotkey"
|
||||
:enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)"
|
||||
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
|
||||
:statusCheckAcceptCodes="item.statusCheckAcceptCodes"
|
||||
:statusCheckMaxRedirects="item.statusCheckMaxRedirects"
|
||||
:statusCheckInterval="getStatusCheckInterval()"
|
||||
@itemClicked="$emit('itemClicked')"
|
||||
@triggerModal="triggerModal"
|
||||
/>
|
||||
<template v-for="(item) in items">
|
||||
<SubItemGroup
|
||||
v-if="item.subItems"
|
||||
:key="item.id"
|
||||
:itemId="item.id"
|
||||
:title="item.title"
|
||||
:subItems="item.subItems"
|
||||
@triggerModal="triggerModal"
|
||||
/>
|
||||
<Item
|
||||
v-else
|
||||
:item="item"
|
||||
:key="item.id"
|
||||
:itemSize="itemSize"
|
||||
:parentSectionTitle="title"
|
||||
@itemClicked="$emit('itemClicked')"
|
||||
@triggerModal="triggerModal"
|
||||
:isAddNew="false"
|
||||
:sectionDisplayData="displayData"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<div v-if="widgets && (selected && !showAll)" class="minimal-widget-wrap">
|
||||
<WidgetBase
|
||||
|
@ -50,6 +47,7 @@
|
|||
import router from '@/router';
|
||||
import Item from '@/components/LinkItems/Item.vue';
|
||||
import WidgetBase from '@/components/Widgets/WidgetBase';
|
||||
import SubItemGroup from '@/components/LinkItems/SubItemGroup.vue';
|
||||
import IframeModal from '@/components/LinkItems/IframeModal.vue';
|
||||
|
||||
export default {
|
||||
|
@ -75,6 +73,7 @@ export default {
|
|||
components: {
|
||||
Item,
|
||||
WidgetBase,
|
||||
SubItemGroup,
|
||||
IframeModal,
|
||||
},
|
||||
methods: {
|
||||
|
@ -83,6 +82,7 @@ export default {
|
|||
},
|
||||
/* Returns a unique lowercase string, based on name, for section ID */
|
||||
makeId(str) {
|
||||
if (!str) return 'unnamed-item';
|
||||
return str.replace(/\s+/g, '-').replace(/[^a-zA-Z ]/g, '').toLowerCase();
|
||||
},
|
||||
/* Opens the iframe modal */
|
||||
|
@ -105,7 +105,6 @@ export default {
|
|||
const parse = (section) => section.replace(' ', '-').toLowerCase().trim();
|
||||
const sectionIdentifier = parse(this.title);
|
||||
router.push({ path: `/home/${sectionIdentifier}` });
|
||||
this.closeContextMenu();
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="nav-outer">
|
||||
<div class="nav-outer" v-if="links && links.length > 0">
|
||||
<IconBurger
|
||||
:class="`burger ${!navVisible ? 'visible' : ''}`"
|
||||
@click="navVisible = !navVisible"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<section>
|
||||
<section v-bind:class="{ 'settings-hidden': !settingsVisible }">
|
||||
<SearchBar ref="SearchBar"
|
||||
@user-is-searchin="userIsTypingSomething"
|
||||
v-if="searchVisible"
|
||||
|
|
|
@ -520,6 +520,8 @@ export default {
|
|||
.widget-base {
|
||||
position: relative;
|
||||
padding: 0.75rem 0.5rem 0.5rem 0.5rem;
|
||||
background: var(--widget-base-background);
|
||||
box-shadow: var(--widget-base-shadow, none);
|
||||
// Refresh and full-page action buttons
|
||||
button.action-btn {
|
||||
height: 1rem;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<template>
|
||||
<nav class="side-bar">
|
||||
<div v-for="(section, index) in sections" :key="index" class="side-bar-section">
|
||||
<!-- Section button -->
|
||||
<div @click="openSection(index)" class="side-bar-item-container">
|
||||
<SideBarItem
|
||||
class="item"
|
||||
|
@ -8,6 +9,7 @@
|
|||
:title="section.name"
|
||||
/>
|
||||
</div>
|
||||
<!-- Section inner -->
|
||||
<transition name="slide">
|
||||
<SideBarSection
|
||||
v-if="isOpen[index]"
|
||||
|
@ -16,6 +18,7 @@
|
|||
/>
|
||||
</transition>
|
||||
</div>
|
||||
<!-- Show links for switching back to Home / Minimal views -->
|
||||
<div class="switch-view-buttons">
|
||||
<router-link to="/home">
|
||||
<IconHome class="view-icon" v-tooltip="$t('alternate-views.default')" />
|
||||
|
@ -66,8 +69,12 @@ export default {
|
|||
if (!this.initUrl) return;
|
||||
const process = (url) => (url ? url.replace(/[^\w\s]/gi, '').toLowerCase() : undefined);
|
||||
const compare = (item) => (process(item.url) === process(this.initUrl));
|
||||
this.sections.forEach((section, secIndex) => {
|
||||
if (section.items && section.items.findIndex(compare) !== -1) this.openSection(secIndex);
|
||||
this.sections.forEach((section, secIndx) => {
|
||||
if (!section.items) return; // Cancel if no items
|
||||
if (section.items.findIndex(compare) !== -1) this.openSection(secIndx);
|
||||
section.items.forEach((item) => { // Do the same for sub-items, if set
|
||||
if (item.subItems && item.subItems.findIndex(compare) !== -1) this.openSection(secIndx);
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<div class="sub-side-bar">
|
||||
<div v-for="(item, index) in items" :key="index">
|
||||
<SideBarItem
|
||||
v-if="!item.subItems"
|
||||
class="item"
|
||||
:icon="item.icon"
|
||||
:title="item.title"
|
||||
|
@ -9,6 +10,18 @@
|
|||
:target="item.target"
|
||||
@launch-app="launchApp"
|
||||
/>
|
||||
<div v-if="item.subItems" class="sub-item-group">
|
||||
<SideBarItem
|
||||
v-for="(subItem, subIndex) in item.subItems"
|
||||
:key="subIndex"
|
||||
class="item sub-item"
|
||||
:icon="subItem.icon"
|
||||
:title="subItem.title"
|
||||
:url="subItem.url"
|
||||
:target="subItem.target"
|
||||
@launch-app="launchApp"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -46,8 +59,10 @@ div.sub-side-bar {
|
|||
color: var(--side-bar-color);
|
||||
text-align: center;
|
||||
z-index: 3;
|
||||
.item:not(:last-child) {
|
||||
border-bottom: 1px dashed var(--side-bar-color);
|
||||
.sub-item-group {
|
||||
border: 1px dotted var(--side-bar-color);
|
||||
border-radius: 4px;
|
||||
background: #00000033;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ export default {
|
|||
width: calc(100% - var(--side-bar-width));
|
||||
.workspace-widget {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
margin: 0.5rem auto 1rem auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -25,6 +25,10 @@ export default {
|
|||
posX: undefined,
|
||||
posY: undefined,
|
||||
},
|
||||
customStyles: {
|
||||
color: this.item.color,
|
||||
background: this.item.backgroundColor,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -71,7 +75,7 @@ export default {
|
|||
/* Get href for anchor, if not in edit mode, or opening in modal/ workspace */
|
||||
hyperLinkHref() {
|
||||
const nothing = '#';
|
||||
const url = this.url || nothing;
|
||||
const url = this.url || this.item.url || nothing;
|
||||
if (this.isEditMode) return nothing;
|
||||
const noAnchorNeeded = ['modal', 'workspace', 'clipboard'];
|
||||
return noAnchorNeeded.includes(this.accumulatedTarget) ? nothing : url;
|
||||
|
@ -126,6 +130,7 @@ export default {
|
|||
},
|
||||
/* Called when an item is clicked, manages the opening of modal & resets the search field */
|
||||
itemClicked(e) {
|
||||
const url = this.url || this.item.url;
|
||||
if (this.isEditMode) {
|
||||
// If in edit mode, open settings, and don't launch app
|
||||
e.preventDefault();
|
||||
|
@ -135,16 +140,16 @@ export default {
|
|||
// For certain opening methods, prevent default and manually navigate
|
||||
if (e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
window.open(this.url, '_blank');
|
||||
window.open(url, '_blank');
|
||||
} else if (e.altKey || this.accumulatedTarget === 'modal') {
|
||||
e.preventDefault();
|
||||
this.$emit('triggerModal', this.url);
|
||||
this.$emit('triggerModal', url);
|
||||
} else if (this.accumulatedTarget === 'workspace') {
|
||||
e.preventDefault();
|
||||
router.push({ name: 'workspace', query: { url: this.url } });
|
||||
router.push({ name: 'workspace', query: { url } });
|
||||
} else if (this.accumulatedTarget === 'clipboard') {
|
||||
e.preventDefault();
|
||||
navigator.clipboard.writeText(this.url);
|
||||
navigator.clipboard.writeText(url);
|
||||
this.$toasted.show(this.$t('context-menus.item.copied-toast'));
|
||||
}
|
||||
// Emit event to clear search field, etc
|
||||
|
@ -157,7 +162,7 @@ export default {
|
|||
},
|
||||
/* Open item, using specified method */
|
||||
launchItem(method, link) {
|
||||
const url = link || this.url;
|
||||
const url = link || this.item.url;
|
||||
this.contextMenuOpen = false;
|
||||
switch (method) {
|
||||
case 'newtab':
|
||||
|
|
|
@ -55,6 +55,7 @@ module.exports = {
|
|||
'dracula',
|
||||
'one-dark',
|
||||
'lissy',
|
||||
'cherry-blossom',
|
||||
'nord-frost',
|
||||
'nord',
|
||||
'oblivion',
|
||||
|
|
Loading…
Reference in New Issue