🔀 Rebased from master

This commit is contained in:
Alicia Sykes
2022-03-29 00:58:44 +01:00
50 changed files with 865 additions and 249 deletions

View File

@@ -231,8 +231,8 @@ export default {
const newItem = item;
newItem.id = this.itemId;
if (newItem.hotkey) newItem.hotkey = parseInt(newItem.hotkey, 10);
const strToTags = (str) => {
const tagArr = str.split(',');
const strToTags = (tags) => {
const tagArr = (typeof tags === 'string') ? tags.split(',') : tags;
return tagArr.map((tag) => tag.trim().toLowerCase().replace(/[^a-z0-9]+/, ''));
};
const strToBool = (str) => {

View File

@@ -17,6 +17,8 @@
<h3>{{ title }}</h3>
<EditModeIcon v-if="isEditMode" @click="openEditModal"
v-tooltip="editTooltip()" class="edit-mode-item" />
<OpenIcon @click.prevent.stop="openContextMenu" @contextmenu.prevent
class="edit-mode-item" />
</label>
<div class="collapsible-content">
<div class="content-inner">
@@ -31,6 +33,7 @@
import { localStorageKeys } from '@/utils/defaults';
import Icon from '@/components/LinkItems/ItemIcon.vue';
import EditModeIcon from '@/assets/interface-icons/interactive-editor-edit-mode.svg';
import OpenIcon from '@/assets/interface-icons/config-open-settings.svg';
export default {
name: 'CollapsableContainer',
@@ -48,6 +51,7 @@ export default {
components: {
Icon,
EditModeIcon,
OpenIcon,
},
computed: {
isEditMode() {
@@ -244,6 +248,8 @@ export default {
float: right;
right: 0.5rem;
top: 0.5rem;
margin-left: 0.2rem;
margin-right: 0.2rem;
}
/* Makes sections fill available space */

View File

@@ -3,7 +3,7 @@
classes="dashy-modal">
<div slot="top-right" @click="hide()">Close</div>
<a @click="hide()" class="close-button" title="Close">x</a>
<iframe v-if="url" :src="url" @keydown.esc="close" class="frame"/>
<iframe v-if="url" :src="url" @keydown.esc="close" class="frame" allow="fullscreen" />
<div v-else class="no-url">No URL Specified</div>
</modal>
</template>

View File

@@ -1,9 +1,9 @@
<template ref="container">
<div :class="`item-wrapper wrap-size-${itemSize}`">
<a @click="beforeLaunchItem"
<a @click="itemClicked"
@mouseup.right="openContextMenu"
@contextmenu.prevent
:href="hyperLinkHref"
:href="url"
:target="anchorTarget"
:class="`item ${makeClassList}`"
v-tooltip="getTooltipOptions()"
@@ -91,6 +91,7 @@ export default {
statusCheckInterval: Number, // Num seconds beteween repeating checks
statusCheckAllowInsecure: Boolean, // Status check ignore SSL certs
statusCheckAcceptCodes: String, // Allow status checks to pass with a code other than 200
statusCheckMaxRedirects: Number, // Specify max number of redirects
parentSectionTitle: String, // Title of parent section (for add new)
isAddNew: Boolean, // Only set if 'fake' item used as Add New button
},
@@ -136,30 +137,6 @@ export default {
};
},
methods: {
/* Called when an item is clicked, manages the opening of modal & resets the search field */
beforeLaunchItem(e) {
if (this.isEditMode) { // If in edit mode, open settings, don't launch app
this.openItemSettings();
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');
// Update the most/ last used ledger, for smart-sorting
if (!this.appConfig.disableSmartSort) {
this.incrementMostUsedCount(this.id);
this.incrementLastUsedCount(this.id);
}
},
/* Returns configuration object for the tooltip */
getTooltipOptions() {
if (!this.description && !this.provider) return {}; // If no description, then skip
@@ -179,7 +156,6 @@ export default {
classes: `item-description-tooltip tooltip-is-${this.itemSize}`,
};
},
/* Open the Edit Item modal form */
openItemSettings() {
this.editMenuOpen = true;
this.contextMenuOpen = false;

View File

@@ -45,11 +45,11 @@ export default {
return this.$store.getters.appConfig;
},
/* Determines the type of icon */
iconType: function iconType() {
iconType() {
return this.determineImageType(this.icon);
},
/* Gets the icon path, dependent on icon type */
iconPath: function iconPath() {
iconPath() {
if (this.broken) return this.getFallbackIcon();
return this.getIconPath(this.icon, this.url);
},
@@ -176,7 +176,7 @@ export default {
},
/* Fetches the path of local images, from Docker container */
getLocalImagePath(img) {
return `${iconCdns.localPath}/${img}`;
return `/${iconCdns.localPath}/${img}`;
},
/* Formats the URL for fetching the generative icons */
getGenerativeIcon(url, cdn) {

View File

@@ -54,6 +54,7 @@
:statusCheckInterval="statusCheckInterval"
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
:statusCheckAcceptCodes="item.statusCheckAcceptCodes"
:statusCheckMaxRedirects="item.statusCheckMaxRedirects"
@itemClicked="$emit('itemClicked')"
@triggerModal="triggerModal"
:isAddNew="false"
@@ -166,7 +167,7 @@ export default {
return this.$store.state.editMode;
},
itemSize() {
return this.$store.getters.iconSize;
return this.displayData.itemSize || this.$store.getters.iconSize;
},
sortOrder() {
return this.displayData.sortBy || defaultSortOrder;

View File

@@ -1,7 +1,7 @@
<template>
<transition name="slide">
<div class="context-menu" v-if="show && !isMenuDisabled"
:style="posX && posY ? `top:${posY}px;left:${posX}px;` : ''">
:style="posX && posY ? calcPosition() : ''">
<!-- Open Options -->
<ul class="menu-section">
<li @click="openSection()">
@@ -59,6 +59,13 @@ export default {
removeSection() {
this.$emit('removeSection');
},
calcPosition() {
const bounds = this.$parent.$el.getBoundingClientRect();
const left = this.posX < (bounds.right + bounds.left) / 2;
const position = `top:${this.posY}px;${left ? 'left' : 'right'}:\
${left ? this.posX : document.documentElement.clientWidth - this.posX}px;`;
return position;
},
},
};
</script>

View File

@@ -19,6 +19,7 @@
:enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)"
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
:statusCheckAcceptCodes="item.statusCheckAcceptCodes"
:statusCheckMaxRedirects="item.statusCheckMaxRedirects"
:statusCheckInterval="getStatusCheckInterval()"
@itemClicked="$emit('itemClicked')"
@triggerModal="triggerModal"

View File

@@ -5,15 +5,23 @@
@click="navVisible = !navVisible"
/>
<nav id="nav" v-if="navVisible">
<router-link
v-for="(link, index) in links"
:key="index"
:to="link.path"
:href="link.path"
:target="isUrl(link.path) ? '_blank' : ''"
rel="noopener noreferrer"
class="nav-item"
>{{link.title}}</router-link>
<!-- Render either router-link or anchor, depending if internal / external link -->
<template v-for="(link, index) in links">
<router-link v-if="!isUrl(link.path)"
:key="index"
:to="link.path"
class="nav-item"
>{{link.title}}
</router-link>
<a v-else
:key="index"
:href="link.path"
:target="determineTarget(link)"
class="nav-item"
rel="noopener noreferrer"
>{{link.title}}
</a>
</template>
</nav>
</div>
</template>
@@ -43,6 +51,16 @@ export default {
return screenWidth && screenWidth < 600;
},
isUrl: (str) => new RegExp(/(http|https):\/\/(\S+)(:[0-9]+)?/).test(str),
determineTarget(link) {
if (!link.target) return '_blank';
switch (link.target) {
case 'sametab': return '_self';
case 'newtab': return '_blank';
case 'parent': return '_parent';
case 'top': return '_top';
default: return undefined;
}
},
},
};
</script>

View File

@@ -476,10 +476,13 @@ export default {
/* Returns users specified widget options, or empty object */
widgetOptions() {
const options = this.widget.options || {};
const timeout = this.widget.timeout || 500;
const useProxy = this.appConfig.widgetsAlwaysUseProxy || !!this.widget.useProxy;
const updateInterval = this.widget.updateInterval !== undefined
? this.widget.updateInterval : null;
return { useProxy, updateInterval, ...options };
return {
timeout, useProxy, updateInterval, ...options,
};
},
/* A unique string to reference the widget by */
widgetRef() {

View File

@@ -1,6 +1,6 @@
<template>
<div class="web-content" :id="id">
<iframe :src="url" />
<iframe :src="url" allow="fullscreen" />
</div>
</template>