Skip to content

Commit

Permalink
Merge pull request #48 from utomic-media/feat/warn-on-external-links
Browse files Browse the repository at this point in the history
Feat/warn on external links
  • Loading branch information
Dominic-Marcelino committed Mar 6, 2024
2 parents d8b5953 + 9e4ae8f commit d6f27f6
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 68 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Displays and disabled interfaces (== readonly) support a custom click-action. Th
#### Link target
- Set the link-target to the same, or a new tab

#### Warn before following external links
- Enabling this setting prompts users with a confirmation popup displaying the full link when clicking on external links
- If disabled, external links open directly.

#### Hide field value (_display only_)
- Hides the field value for a button only mode
- Mostly to be used in combinaiton with button labels
Expand Down Expand Up @@ -102,6 +106,11 @@ pnpm i directus-extension-field-actions

---

![](https://github.com/utomic-media/directus-extension-field-actions/raw/main/docs/screenshots/follow-link-confirmation-detail.png)
*↑ Link preview and verification on external links (optionally)*

---

![](https://github.com/utomic-media/directus-extension-field-actions/raw/main/docs/screenshots/interface-config-2023-03.png)
*↑ Interfaces settings*

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/screenshots/follow-link-confirmation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 30 additions & 26 deletions src/display/display.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<template>
<value-null v-if="!value" />
<span v-else class="action-display">
<!-- NOTE: @click.stop to prevent the opening of item page -->
<component
v-if="!hideFieldValue"
:is="(clickAction === 'link') ? 'a' : 'span'"
class="dynamic-wrapper"
:href="computedLink"
:is="(clickAction === 'link') ? linkWrapper : 'span'"
v-tooltip.left="actionTooltip"
:href="computedLink"
:target="openLinkAsNewTab ? '_blank' : '_self'"
rel="noopener noreferrer"
:safeMode="openLinkSafeMode === 'always'"
@click.stop
>
<span
:class="hasValueClickAction ? 'action-background' : ''"
Expand Down Expand Up @@ -36,24 +37,28 @@
</component>


<component
<!-- NOTE: @click.stop to prevent the opening of item page -->
<link-wrapper
v-if="showLink"
:is="(linkButtonLabel) ? 'v-button' : 'a'"
outlined
xSmall
:href="computedLink"
:target="openLinkAsNewTab ? '_blank' : '_self'"
rel="noopener noreferrer"
v-tooltip="`Follow link: ${computedLink}`"
@click.stop
:safeMode="openLinkSafeMode === 'always'"
:class="linkPosition === 'start' ? '-order-1' : 'order-1'"
@click.stop
>
<v-icon
name="open_in_new"
:color="linkButtonLabel ? 'primary' : ''"
/>
<span v-if="linkButtonLabel" class="ml-2">{{ linkButtonLabel }}</span>
</component>
<component
:is="(linkButtonLabel) ? 'v-button' : 'div'"
outlined
xSmall
v-tooltip="`Follow link: ${computedLink}`"
>
<v-icon
name="open_in_new"
:color="linkButtonLabel ? 'primary' : ''"
/>
<span v-if="linkButtonLabel" class="ml-2">{{ linkButtonLabel }}</span>
</component>
</link-wrapper>
</span>
</template>

Expand All @@ -65,6 +70,7 @@ import { computed } from 'vue';
import { useClipboard } from '../shared/composable/use-clipboard';
import { usePrefixedValues } from '../shared/composable/use-prefixed-values';
import { useStores } from '@directus/extensions-sdk';
import linkWrapper from '../shared/components/linkWrapper.vue';
const props = defineProps({
value: {
Expand Down Expand Up @@ -122,7 +128,11 @@ const props = defineProps({
openLinkAsNewTab: {
type: Boolean,
default: true
}
},
openLinkSafeMode: {
type: String,
default: 'never',
},
});
Expand All @@ -145,11 +155,6 @@ function valueClickAction(e: Event) {
e.stopPropagation();
copyValue();
}
if (props.clickAction === 'link') {
// We opened a link in a new tab and don't want to get into the details view of the item
e.stopPropagation();
}
// else go on with the default events
}
Expand Down Expand Up @@ -200,20 +205,19 @@ const actionTooltip = computed(() => {
display: inline-flex;
flex-direction: row;
align-items: center;
gap: 8px;
span,
a,
.v-button {
div {
display: inherit;
&.order-1 {
order: 1;
margin-left: 8px;
}
&.-order-1 {
order: -1;
margin-right: 8px;
}
}
Expand Down
108 changes: 66 additions & 42 deletions src/interface/interface.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,34 @@

<template>
<div class="action-interface">
<v-input
:model-value="value"
:disabled="disabled"
:type="inputType"
:placeholder="placeholder"
:min="min"
:max="max"
:step="step"
v-tooltip="actionTooltip"
@update:model-value="$emit('input', $event)"
@click="valueClickAction"
<component
:is="(clickAction === 'link') ? linkWrapper : 'div'"
:href="computedLink"
:target="openLinkAsNewTab ? '_blank' : '_self'"
:safeMode="openLinkSafeMode === 'always'"
class="dynamic-input-wrapper"
>
<template v-if="iconLeft" #prepend>
<v-icon :name="iconLeft" />
</template>
<v-input
:model-value="value"
:disabled="disabled"
:type="inputType"
:placeholder="placeholder"
:min="min"
:max="max"
:step="step"
v-tooltip="actionTooltip"
@update:model-value="$emit('input', $event)"
@click="valueClickAction"
>
<template v-if="iconLeft" #prepend>
<v-icon :name="iconLeft" />
</template>

<template v-if="iconRight" #append>
<v-icon :name="iconRight" />
</template>
</v-input>
<template v-if="iconRight" #append>
<v-icon :name="iconRight" />
</template>
</v-input>
</component>

<v-button
v-if="showCopy && isCopySupported"
Expand All @@ -39,27 +47,25 @@
</v-button>


<!-- TODO: button supports :to=routerLink and :href=custom link. Switch from custom a-tag to those. Use condition: href for full url and "to" for internal links (incomplete url) -->
<v-button
v-if="showLink"
:disabled="!value"
v-tooltip="value ? `Follow link: ${computedLink}` : `Can't follow empty link`"
icon
secondary
xLarge
<link-wrapper
:href="computedLink"
:target="openLinkAsNewTab ? '_blank' : '_self'"
:safeMode="openLinkSafeMode === 'always'"
:class="linkPosition === 'start' ? '-order-1' : 'order-1'"
>
<a
:href="computedLink"
:target="openLinkAsNewTab ? '_blank' : '_self'"
rel="noopener noreferrer"
@click.stop
<v-button
v-if="showLink"
:disabled="!value"
v-tooltip="value ? `Follow link: ${computedLink}` : `Can't follow empty link`"
icon
secondary
xLarge
>
<v-icon
name="open_in_new"
/>
</a>
</v-button>
</v-button>
</link-wrapper>
</div>
</template>

Expand All @@ -68,6 +74,7 @@ import { computed } from 'vue';
import { useClipboard } from '../shared/composable/use-clipboard';
import { usePrefixedValues } from '../shared/composable/use-prefixed-values';
import { useStores } from '@directus/extensions-sdk';
import linkWrapper from '../shared/components/linkWrapper.vue';
const props = defineProps({
value: {
Expand Down Expand Up @@ -138,7 +145,11 @@ const props = defineProps({
openLinkAsNewTab: {
type: Boolean,
default: true
}
},
openLinkSafeMode: {
type: String,
default: 'never',
},
});
const emit = defineEmits(['input']);
Expand Down Expand Up @@ -168,12 +179,6 @@ function valueClickAction(e: Event) {
e.stopPropagation();
copyValue();
}
if (props.clickAction === 'link' && props.disabled && props.value) {
e.stopPropagation();
window.open(computedLink.value, '_blank', 'noopener, noreferrer');
}
// else go on with the default events
}
Expand All @@ -189,23 +194,42 @@ const actionTooltip = computed(() => {



<style lang="scss">
// !NOTE: GLOBAL STYLES - use scoped styles for the component whenever possible!
.action-interface {
.v-input {
input:disabled {
/* disable click events on disabled inputs, so that the click event can be handled by the parent div
* For some reason we can't go with a normal :deep() selector in the scoped stye
*/
pointer-events: none;
}
}
}
</style>


<style scoped lang="scss">
.action-interface {
display: flex;
flex-direction: row;
align-items: center;
gap:8px;
.dynamic-input-wrapper {
width: 100%;
}
>div {
display: inherit;
&.order-1 {
order: 1;
margin-left: 8px;
}
&.-order-1 {
order: -1;
margin-right: 8px;
}
}
}
Expand Down
Loading

0 comments on commit d6f27f6

Please sign in to comment.