From 073e55b15cbc4d82d8a1e0e598ff3e1987bdbd54 Mon Sep 17 00:00:00 2001 From: Sergei Ogorelkov Date: Thu, 27 Apr 2023 16:28:12 +0400 Subject: [PATCH] [TSK-1327] Clickable links for string presenters and rich text editor (#3088) Signed-off-by: Sergei Ogorelkov --- common/config/rush/pnpm-lock.yaml | 11 ++++++- .../text-editor/src/components/extensions.ts | 5 ++- packages/ui/package.json | 3 +- packages/ui/src/components/LinkWrapper.svelte | 31 +++++++++++++++++++ packages/ui/src/index.ts | 1 + packages/ui/src/utils.ts | 22 +++++++++++++ .../src/components/EditVacancy.svelte | 14 ++++----- .../src/components/StringPresenter.svelte | 21 +++++-------- .../src/components/Header.svelte | 14 ++++----- tests/sanity/tests/recruit.spec.ts | 9 +++--- 10 files changed, 94 insertions(+), 37 deletions(-) create mode 100644 packages/ui/src/components/LinkWrapper.svelte diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 2e1fafbbaa..81b476b7cb 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -269,6 +269,7 @@ specifiers: '@types/xml2js': ~0.4.9 '@typescript-eslint/eslint-plugin': ^5.41.0 '@typescript-eslint/parser': ^5.41.0 + autolinker: 4.0.0 autoprefixer: ^10.4.14 body-parser: ~1.19.1 browserslist: 4.21.5 @@ -641,6 +642,7 @@ dependencies: '@types/xml2js': 0.4.11 '@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464 '@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4 + autolinker: 4.0.0 autoprefixer: 10.4.14_postcss@8.4.20 body-parser: 1.19.2 browserslist: 4.21.5 @@ -4267,6 +4269,12 @@ packages: engines: {node: '>=8.0.0'} dev: false + /autolinker/4.0.0: + resolution: {integrity: sha512-fl5Kh6BmEEZx+IWBfEirnRUU5+cOiV0OK7PEt0RBKvJMJ8GaRseIOeDU3FKf4j3CE5HVefcjHmhYPOcaVt0bZw==} + dependencies: + tslib: 2.4.1 + dev: false + /autoprefixer/10.4.14_postcss@8.4.20: resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==} engines: {node: ^10 || ^12 || >=14} @@ -16711,7 +16719,7 @@ packages: dev: false file:projects/ui.tgz_a1d864769aaf53d09b76fe134ab55e60: - resolution: {integrity: sha512-hn+I2VojXSb6t3PYHND/JyhwX8tZ8Jr7D+rzs1XjmVxhNMHnO+HA8QHzXyqZBjggTsBc8kUqWlnm44W5tt3m8Q==, tarball: file:projects/ui.tgz} + resolution: {integrity: sha512-TvtqATO5uYXW+jS7m2OEAikTIUwJ5JSDwTdxk0OqD62FO4nkED8FA63goDvEoBPUZtoFn7yTzy2cWiXepZNiXA==, tarball: file:projects/ui.tgz} id: file:projects/ui.tgz name: '@rush-temp/ui' version: 0.0.0 @@ -16719,6 +16727,7 @@ packages: '@types/jest': 28.1.8 '@typescript-eslint/eslint-plugin': 5.42.1_d506b9be61cb4ac2646ecbc6e0680464 '@typescript-eslint/parser': 5.42.1_eslint@8.27.0+typescript@4.8.4 + autolinker: 4.0.0 eslint: 8.27.0 eslint-config-standard-with-typescript: 23.0.0_c9fe9619f50f4e82337a86c3af25e566 eslint-plugin-import: 2.26.0_eslint@8.27.0 diff --git a/packages/text-editor/src/components/extensions.ts b/packages/text-editor/src/components/extensions.ts index 8f83a2ae13..8ca304fa95 100644 --- a/packages/text-editor/src/components/extensions.ts +++ b/packages/text-editor/src/components/extensions.ts @@ -56,7 +56,10 @@ export const defaultExtensions = [ Heading.configure({ levels: headingLevels }), - Link.configure({ openOnClick: false }), + Link.configure({ + openOnClick: true, + HTMLAttributes: { class: 'cursor-pointer', rel: 'noopener noreferrer', target: '_blank' } + }), ...tableExtensions, ...taskListExtensions ] diff --git a/packages/ui/package.json b/packages/ui/package.json index 682cf77fbf..68dac83c60 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -38,7 +38,8 @@ "@hcengineering/core": "^0.6.23", "just-clone": "~6.2.0", "svelte": "3.55.1", - "fast-equals": "^2.0.3" + "fast-equals": "^2.0.3", + "autolinker": "4.0.0" }, "repository": "https://github.com/hcenginneing/anticrm", "publishConfig": { diff --git a/packages/ui/src/components/LinkWrapper.svelte b/packages/ui/src/components/LinkWrapper.svelte new file mode 100644 index 0000000000..a0f672cd5a --- /dev/null +++ b/packages/ui/src/components/LinkWrapper.svelte @@ -0,0 +1,31 @@ + + + +{#if text} + + {@html replaceURLs(text)} +{/if} diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 1f417b88c6..cdbba5e231 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -86,6 +86,7 @@ export { default as Spinner } from './components/Spinner.svelte' export { default as Popup } from './components/Popup.svelte' export { default as CircleButton } from './components/CircleButton.svelte' export { default as Link } from './components/Link.svelte' +export { default as LinkWrapper } from './components/LinkWrapper.svelte' export { default as Like } from './components/Like.svelte' export { default as TimeSince } from './components/TimeSince.svelte' export { default as Dropdown } from './components/Dropdown.svelte' diff --git a/packages/ui/src/utils.ts b/packages/ui/src/utils.ts index b2a83e6ae8..ac99ce0875 100644 --- a/packages/ui/src/utils.ts +++ b/packages/ui/src/utils.ts @@ -17,6 +17,7 @@ import { generateId } from '@hcengineering/core' import type { Metadata } from '@hcengineering/platform' import { setMetadata } from '@hcengineering/platform' import { writable } from 'svelte/store' +import autolinker from 'autolinker' import { Notification, NotificationPosition, NotificationSeverity, notificationsStore } from '.' import { AnyComponent, AnySvelteComponent } from './types' @@ -128,3 +129,24 @@ export function mouseAttractor (op: () => void, diff = 2): (evt: MouseEvent) => } } } + +/** + * Replaces URLs with Links in a given block of text/HTML + * + * @example + * replaceURLs("Check out google.com") + * returns: "Check out google.com" + * + * @export + * @param {string} text + * @returns {string} string with replaced URLs + */ +export function replaceURLs (text: string): string { + return autolinker.link(text, { + urls: true, + phone: false, + email: false, + sanitizeHtml: true, + stripPrefix: false + }) +} diff --git a/plugins/recruit-resources/src/components/EditVacancy.svelte b/plugins/recruit-resources/src/components/EditVacancy.svelte index 79fa6a6c35..9e6fd1e726 100644 --- a/plugins/recruit-resources/src/components/EditVacancy.svelte +++ b/plugins/recruit-resources/src/components/EditVacancy.svelte @@ -21,7 +21,7 @@ import { Vacancy } from '@hcengineering/recruit' import { FullDescriptionBox } from '@hcengineering/text-editor' import tracker from '@hcengineering/tracker' - import { Button, Component, EditBox, Grid, IconMixin, IconMoreH, showPopup } from '@hcengineering/ui' + import { Button, Component, EditBox, Grid, IconMixin, IconMoreH, LinkWrapper, showPopup } from '@hcengineering/ui' import { ContextMenu, DocAttributeBar } from '@hcengineering/view-resources' import { createEventDispatcher } from 'svelte' import recruit from '../plugin' @@ -113,13 +113,11 @@ > {#if object.description} - {#if object.description.trim().startsWith('http://') || object.description.trim().startsWith('https://')} - - {object.description} - - {:else} - {object.description} - {/if} +
+ + + +
{/if}
diff --git a/plugins/view-resources/src/components/StringPresenter.svelte b/plugins/view-resources/src/components/StringPresenter.svelte index 2f602e4d1c..03c96d8066 100644 --- a/plugins/view-resources/src/components/StringPresenter.svelte +++ b/plugins/view-resources/src/components/StringPresenter.svelte @@ -1,26 +1,23 @@