Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/display-link-interface/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'prettier',
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: ['vue', '@typescript-eslint', 'prettier'],
rules: {
'prettier/prettier': 'warn',
'vue/multi-word-component-names': 'off',
},
};
8 changes: 8 additions & 0 deletions packages/display-link-interface/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
node_modules
src
!dist
!dist/*.ts
.eslintrc.*
*.png
tsconfig.json
17 changes: 17 additions & 0 deletions packages/display-link-interface/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Display Link extension for Directus

Display URLs, phone numbers, and emails with a link button in
[Directus](https://directus.io)

## Install

Search for "display link" in the Marketplace of your app settings, navigate to
the extension page, and click "Install Extension"

## Screenshots

![](https://raw.githubusercontent.com/jacoborus/directus-extension-display-link/main/screenshot.png)

![](https://raw.githubusercontent.com/jacoborus/directus-extension-display-link/main/screenshot-list.png)

![](https://raw.githubusercontent.com/jacoborus/directus-extension-display-link/main/screenshot-options.png)
52 changes: 52 additions & 0 deletions packages/display-link-interface/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@directus-labs/display-link-interface",
"version": "1.8.0",
"description": "Display URLs, phone numbers, and emails with a link button in Directus",
"author": "Jacobo Tabernero Rey <[email protected]>",
"license": "ISC",
"homepage": "https://github.com/directus-labs/extensions#readme",
"repository": {
"type": "git",
"url": "https://github.com/directus-labs/extensions.git"
},
"icon": "link",
"bugs": {
"url": "https://github.com/directus-labs/extensions/issues"
},
"keywords": [
"directus",
"directus-extension",
"directus-custom-display",
"link",
"url",
"email",
"telephone",
"display-url",
"display-phone-number",
"display-telephone"
],
"directus:extension": {
"type": "display",
"path": "dist/index.js",
"source": "src/index.ts",
"host": ">=10.10.10"
},
"scripts": {
"build": "directus-extension build",
"dev": "directus-extension build --watch --no-minify",
"prepublish": "npm run build",
"lint": "eslint"
},
"devDependencies": {
"@directus/extensions-sdk": "12.1.4",
"@typescript-eslint/eslint-plugin": "8.20.0",
"@typescript-eslint/parser": "8.20.0",
"eslint": "9.18.0",
"eslint-config-prettier": "10.0.1",
"eslint-plugin-prettier": "5.2.2",
"eslint-plugin-vue": "^9.7.0",
"prettier": "3.4.2",
"typescript": "5.7.3",
"vue": "^3.5.13"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 packages/display-link-interface/screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
155 changes: 155 additions & 0 deletions packages/display-link-interface/src/display.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<script setup lang="ts">
import { useStores } from '@directus/extensions-sdk';

type FontType = 'sans-serif' | 'serif' | 'monospace';

const props = withDefaults(
defineProps<{
value: string | number | null;
kind: 'url' | 'email' | 'tel';
showLinkButton: boolean;
icon: string;
showUrl: boolean;
font: FontType;
showClipboard: boolean;
openInNew: boolean;
prefix: string;
suffix: string;
showPrefix: boolean;
showSuffix: boolean;
}>(),
{
value: null,
kind: 'url',
showLinkButton: true,
icon: 'open_in_new',
showUrl: true,
font: 'sans-serif',
showClipboard: true,
openInNew: true,
prefix: '',
suffix: '',
showPrefix: false,
showSuffix: false,
},
);

const protocols = {
url: '',
email: 'mailto:',
tel: 'tel:',
};

const verbs = {
url: 'Open',
email: 'Send email to',
tel: 'Call',
};

const { useNotificationsStore } = useStores();
const notifStore = useNotificationsStore();

const prefix = props.showPrefix ? props.prefix : '';
const suffix = props.showSuffix ? props.suffix : '';
const displayUrl = `${prefix}${props.value}${suffix}`;
const url = `${props.prefix || ''}${props.value ?? ''}${props.suffix || ''}`;
const protocol = protocols[props.kind];
const href = `${protocol}${url}`;
const verb = verbs[props.kind];
const target = props.openInNew ? '_blank' : undefined;

async function copyToClipboard() {
try {
await navigator?.clipboard?.writeText(url);

notifStore.add({
title: 'Copied!',
type: 'success',
});
}
catch (error: unknown) {
if (error instanceof Error) {
notifStore.add({
title: 'Error!',
type: 'error',
error,
});
}
}
}
</script>

<template>
<v-null v-if="value === null" />
<template v-else>
<v-hover v-slot="{ hover }" class="ext-display-link">
<div class="ext-display-link__wrapper">
<a :href="href" :target="target" @click.stop>
<v-icon
v-if="showLinkButton"
v-tooltip="`${verb} ${url}`"
class="ext-display-link__icon"
:name="icon"
left
/>
</a>

<span v-if="showUrl" :class="[font]" class="ext-display-link__url">
{{ displayUrl }}
</span>

<transition name="fade">
<v-icon
v-if="showClipboard && (!showUrl || hover)"
v-tooltip="`Copy to clipboard \n ${url}`"
class="ext-display-link__clip"
right
name="content_copy"
@click.stop="copyToClipboard"
/>
</transition>
</div>
</v-hover>
</template>
</template>

<style>
.ext-display-link {
display: inline-block;
max-width: 100%;
overflow: hidden;
}
.ext-display-link__wrapper {
display: flex;
line-height: 18px;
}
.ext-display-link__url {
flex-shrink: 1;
overflow: hidden;
}
.ext-display-link__url.sans-serif {
font-family: var(--family-sans-serif);
}
.ext-display-link__url.serif {
font-family: var(--family-serif);
}
.ext-display-link__url.monospace {
font-family: var(--family-monospace);
}
.ext-display-link__icon,
.ext-display-link__clip {
--v-icon-size: 18px;
}
.ext-display-link__icon:hover,
.ext-display-link__clip:hover {
--v-icon-color: var(--primary);
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.2s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>
Loading
Loading