@pallasoft/ps-core-components 2.40.0 → 2.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ps-linked-text.d.ts +5 -0
- package/dist/components/ps-linked-text.js +9 -0
- package/dist/components/ps-portable-text.js +11 -5
- package/dist/constants/ps-linked-text-link-rel.d.ts +1 -0
- package/dist/constants/ps-linked-text-link-rel.js +4 -0
- package/dist/constants/ps-portable-text-auto-link-type.d.ts +1 -0
- package/dist/constants/ps-portable-text-auto-link-type.js +4 -0
- package/dist/constants/ps-portable-text-link-class-name.d.ts +1 -0
- package/dist/constants/ps-portable-text-link-class-name.js +4 -0
- package/dist/constants/ps-text-link-pattern-source.d.ts +1 -0
- package/dist/constants/ps-text-link-pattern-source.js +4 -0
- package/dist/constants/ps-text-link-protocols.d.ts +1 -0
- package/dist/constants/ps-text-link-protocols.js +4 -0
- package/dist/constants/ps-text-link-trailing-characters.d.ts +1 -0
- package/dist/constants/ps-text-link-trailing-characters.js +4 -0
- package/dist/constants/ps-text-wrap-class-name.d.ts +1 -0
- package/dist/constants/ps-text-wrap-class-name.js +4 -0
- package/dist/esm/components/ps-linked-text.js +5 -0
- package/dist/esm/components/ps-portable-text.js +11 -5
- package/dist/esm/constants/ps-linked-text-link-rel.js +1 -0
- package/dist/esm/constants/ps-portable-text-auto-link-type.js +1 -0
- package/dist/esm/constants/ps-portable-text-link-class-name.js +1 -0
- package/dist/esm/constants/ps-text-link-pattern-source.js +1 -0
- package/dist/esm/constants/ps-text-link-protocols.js +1 -0
- package/dist/esm/constants/ps-text-link-trailing-characters.js +1 -0
- package/dist/esm/constants/ps-text-wrap-class-name.js +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/models/ps-text-link-segment-data.js +1 -0
- package/dist/esm/services/ps-portable-text-link-service.js +53 -0
- package/dist/esm/services/ps-text-link-service.js +63 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/models/ps-text-link-segment-data.d.ts +4 -0
- package/dist/models/ps-text-link-segment-data.js +2 -0
- package/dist/services/ps-portable-text-link-service.d.ts +8 -0
- package/dist/services/ps-portable-text-link-service.js +57 -0
- package/dist/services/ps-text-link-service.d.ts +8 -0
- package/dist/services/ps-text-link-service.js +67 -0
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsLinkedText = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ps_linked_text_link_rel_1 = require("../constants/ps-linked-text-link-rel");
|
|
6
|
+
const ps_portable_text_link_class_name_1 = require("../constants/ps-portable-text-link-class-name");
|
|
7
|
+
const ps_text_link_service_1 = require("../services/ps-text-link-service");
|
|
8
|
+
const PsLinkedText = ({ linkClassName = ps_portable_text_link_class_name_1.PsPortableTextLinkClassName, text, }) => ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: ps_text_link_service_1.PsTextLinkService.segments(text).map((segment, index) => segment.href === null ? ((0, jsx_runtime_1.jsx)("span", { children: segment.text }, index)) : ((0, jsx_runtime_1.jsx)("a", { className: linkClassName, href: segment.href, rel: ps_linked_text_link_rel_1.PsLinkedTextLinkRel, target: '_blank', children: segment.text }, index))) }));
|
|
9
|
+
exports.PsLinkedText = PsLinkedText;
|
|
@@ -4,13 +4,19 @@ exports.PsPortableText = void 0;
|
|
|
4
4
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
5
|
const ps_design_system_1 = require("@pallasoft/ps-design-system");
|
|
6
6
|
const react_1 = require("@portabletext/react");
|
|
7
|
-
const
|
|
7
|
+
const ps_linked_text_link_rel_1 = require("../constants/ps-linked-text-link-rel");
|
|
8
|
+
const ps_portable_text_auto_link_type_1 = require("../constants/ps-portable-text-auto-link-type");
|
|
9
|
+
const ps_portable_text_link_class_name_1 = require("../constants/ps-portable-text-link-class-name");
|
|
10
|
+
const ps_text_wrap_class_name_1 = require("../constants/ps-text-wrap-class-name");
|
|
11
|
+
const ps_portable_text_link_service_1 = require("../services/ps-portable-text-link-service");
|
|
12
|
+
const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = '', containerStyles = {}, headerClasses = 'text-ink', isLargeBodyFont = false, linkClassName = ps_portable_text_link_class_name_1.PsPortableTextLinkClassName, LinkComponent = 'a', listClasses = 'text-ink', text, }) => {
|
|
8
13
|
const hasContainerStyles = Object.keys(containerStyles).length > 0;
|
|
9
14
|
const getBodyClass = hasContainerStyles ? '' : bodyClasses;
|
|
10
15
|
const getHeaderClass = hasContainerStyles ? '' : headerClasses;
|
|
11
16
|
const getListClass = hasContainerStyles ? '' : listClasses;
|
|
12
17
|
const components = {
|
|
13
18
|
marks: {
|
|
19
|
+
[ps_portable_text_auto_link_type_1.PsPortableTextAutoLinkType]: ({ children, value }) => ((0, jsx_runtime_1.jsx)("a", { href: value.href, target: '_blank', rel: ps_linked_text_link_rel_1.PsLinkedTextLinkRel, className: linkClassName, children: children })),
|
|
14
20
|
link: ({ children, value }) => {
|
|
15
21
|
const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined;
|
|
16
22
|
return ((0, jsx_runtime_1.jsx)(LinkComponent, { href: value.href, rel: rel, className: linkClassName, "aria-label": value.children, children: children }));
|
|
@@ -21,11 +27,11 @@ const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = '', conta
|
|
|
21
27
|
number: ({ children }) => (0, jsx_runtime_1.jsx)("ol", { className: 'mb-3 mt-1 list-decimal ml-4', children: children }),
|
|
22
28
|
},
|
|
23
29
|
listItem: {
|
|
24
|
-
bullet: ({ children }) => ((0, jsx_runtime_1.jsx)("li", { className: ps_design_system_1.PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', getListClass), children: children })),
|
|
25
|
-
number: ({ children }) => ((0, jsx_runtime_1.jsx)("li", { className: ps_design_system_1.PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', getListClass), children: children })),
|
|
30
|
+
bullet: ({ children }) => ((0, jsx_runtime_1.jsx)("li", { className: ps_design_system_1.PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', ps_text_wrap_class_name_1.PsTextWrapClassName, getListClass), children: children })),
|
|
31
|
+
number: ({ children }) => ((0, jsx_runtime_1.jsx)("li", { className: ps_design_system_1.PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', ps_text_wrap_class_name_1.PsTextWrapClassName, getListClass), children: children })),
|
|
26
32
|
},
|
|
27
33
|
block: {
|
|
28
|
-
normal: ({ children }) => ((0, jsx_runtime_1.jsx)("p", { className: ps_design_system_1.PallasoftClassNameService.merge(isLargeBodyFont ? 'text-body-1' : 'text-body-2', 'whitespace-pre-line', getBodyClass), children: children })),
|
|
34
|
+
normal: ({ children }) => ((0, jsx_runtime_1.jsx)("p", { className: ps_design_system_1.PallasoftClassNameService.merge(isLargeBodyFont ? 'text-body-1' : 'text-body-2', 'whitespace-pre-line', ps_text_wrap_class_name_1.PsTextWrapClassName, getBodyClass), children: children })),
|
|
29
35
|
h2: ({ children }) => ((0, jsx_runtime_1.jsx)("h2", { className: ps_design_system_1.PallasoftClassNameService.merge('text-header-1 py-2', getHeaderClass), children: children })),
|
|
30
36
|
h3: ({ children }) => ((0, jsx_runtime_1.jsx)("h3", { className: ps_design_system_1.PallasoftClassNameService.merge('text-header-2 py-2', getHeaderClass), children: children })),
|
|
31
37
|
h4: ({ children }) => ((0, jsx_runtime_1.jsx)("h4", { className: ps_design_system_1.PallasoftClassNameService.merge('text-header-3 py-2', getHeaderClass), children: children })),
|
|
@@ -33,6 +39,6 @@ const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = '', conta
|
|
|
33
39
|
h6: ({ children }) => ((0, jsx_runtime_1.jsx)("h6", { className: ps_design_system_1.PallasoftClassNameService.merge('text-header-4 py-1', getHeaderClass), children: children })),
|
|
34
40
|
},
|
|
35
41
|
};
|
|
36
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: containerClasses, style: containerStyles, children: (0, jsx_runtime_1.jsx)(react_1.PortableText, { value: text, components: components }) }));
|
|
42
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: containerClasses, style: containerStyles, children: (0, jsx_runtime_1.jsx)(react_1.PortableText, { value: ps_portable_text_link_service_1.PsPortableTextLinkService.linkify(text), components: components }) }));
|
|
37
43
|
};
|
|
38
44
|
exports.PsPortableText = PsPortableText;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsLinkedTextLinkRel = "noopener noreferrer nofollow";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsPortableTextAutoLinkType = "autoLink";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsPortableTextLinkClassName = "text-primary underline";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsTextLinkPatternSource = "<([^<>\\s]+)>|(https?:\\/\\/[^\\s<>]+)";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsTextLinkProtocols: string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsTextLinkTrailingCharacters = ".,;:!?)]}'\"";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PsTextWrapClassName = "[overflow-wrap:anywhere]";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { PsLinkedTextLinkRel } from '../constants/ps-linked-text-link-rel.js';
|
|
3
|
+
import { PsPortableTextLinkClassName } from '../constants/ps-portable-text-link-class-name.js';
|
|
4
|
+
import { PsTextLinkService } from '../services/ps-text-link-service.js';
|
|
5
|
+
export const PsLinkedText = ({ linkClassName = PsPortableTextLinkClassName, text, }) => (_jsx(_Fragment, { children: PsTextLinkService.segments(text).map((segment, index) => segment.href === null ? (_jsx("span", { children: segment.text }, index)) : (_jsx("a", { className: linkClassName, href: segment.href, rel: PsLinkedTextLinkRel, target: '_blank', children: segment.text }, index))) }));
|
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { PallasoftClassNameService } from '@pallasoft/ps-design-system';
|
|
3
3
|
import { PortableText as Text } from '@portabletext/react';
|
|
4
|
-
|
|
4
|
+
import { PsLinkedTextLinkRel } from '../constants/ps-linked-text-link-rel.js';
|
|
5
|
+
import { PsPortableTextAutoLinkType } from '../constants/ps-portable-text-auto-link-type.js';
|
|
6
|
+
import { PsPortableTextLinkClassName } from '../constants/ps-portable-text-link-class-name.js';
|
|
7
|
+
import { PsTextWrapClassName } from '../constants/ps-text-wrap-class-name.js';
|
|
8
|
+
import { PsPortableTextLinkService } from '../services/ps-portable-text-link-service.js';
|
|
9
|
+
export const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = '', containerStyles = {}, headerClasses = 'text-ink', isLargeBodyFont = false, linkClassName = PsPortableTextLinkClassName, LinkComponent = 'a', listClasses = 'text-ink', text, }) => {
|
|
5
10
|
const hasContainerStyles = Object.keys(containerStyles).length > 0;
|
|
6
11
|
const getBodyClass = hasContainerStyles ? '' : bodyClasses;
|
|
7
12
|
const getHeaderClass = hasContainerStyles ? '' : headerClasses;
|
|
8
13
|
const getListClass = hasContainerStyles ? '' : listClasses;
|
|
9
14
|
const components = {
|
|
10
15
|
marks: {
|
|
16
|
+
[PsPortableTextAutoLinkType]: ({ children, value }) => (_jsx("a", { href: value.href, target: '_blank', rel: PsLinkedTextLinkRel, className: linkClassName, children: children })),
|
|
11
17
|
link: ({ children, value }) => {
|
|
12
18
|
const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined;
|
|
13
19
|
return (_jsx(LinkComponent, { href: value.href, rel: rel, className: linkClassName, "aria-label": value.children, children: children }));
|
|
@@ -18,11 +24,11 @@ export const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = ''
|
|
|
18
24
|
number: ({ children }) => _jsx("ol", { className: 'mb-3 mt-1 list-decimal ml-4', children: children }),
|
|
19
25
|
},
|
|
20
26
|
listItem: {
|
|
21
|
-
bullet: ({ children }) => (_jsx("li", { className: PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', getListClass), children: children })),
|
|
22
|
-
number: ({ children }) => (_jsx("li", { className: PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', getListClass), children: children })),
|
|
27
|
+
bullet: ({ children }) => (_jsx("li", { className: PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', PsTextWrapClassName, getListClass), children: children })),
|
|
28
|
+
number: ({ children }) => (_jsx("li", { className: PallasoftClassNameService.merge('md:text-body-2 max-md:text-body-3', PsTextWrapClassName, getListClass), children: children })),
|
|
23
29
|
},
|
|
24
30
|
block: {
|
|
25
|
-
normal: ({ children }) => (_jsx("p", { className: PallasoftClassNameService.merge(isLargeBodyFont ? 'text-body-1' : 'text-body-2', 'whitespace-pre-line', getBodyClass), children: children })),
|
|
31
|
+
normal: ({ children }) => (_jsx("p", { className: PallasoftClassNameService.merge(isLargeBodyFont ? 'text-body-1' : 'text-body-2', 'whitespace-pre-line', PsTextWrapClassName, getBodyClass), children: children })),
|
|
26
32
|
h2: ({ children }) => (_jsx("h2", { className: PallasoftClassNameService.merge('text-header-1 py-2', getHeaderClass), children: children })),
|
|
27
33
|
h3: ({ children }) => (_jsx("h3", { className: PallasoftClassNameService.merge('text-header-2 py-2', getHeaderClass), children: children })),
|
|
28
34
|
h4: ({ children }) => (_jsx("h4", { className: PallasoftClassNameService.merge('text-header-3 py-2', getHeaderClass), children: children })),
|
|
@@ -30,5 +36,5 @@ export const PsPortableText = ({ bodyClasses = 'text-ink', containerClasses = ''
|
|
|
30
36
|
h6: ({ children }) => (_jsx("h6", { className: PallasoftClassNameService.merge('text-header-4 py-1', getHeaderClass), children: children })),
|
|
31
37
|
},
|
|
32
38
|
};
|
|
33
|
-
return (_jsx("div", { className: containerClasses, style: containerStyles, children: _jsx(Text, { value: text, components: components }) }));
|
|
39
|
+
return (_jsx("div", { className: containerClasses, style: containerStyles, children: _jsx(Text, { value: PsPortableTextLinkService.linkify(text), components: components }) }));
|
|
34
40
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsLinkedTextLinkRel = 'noopener noreferrer nofollow';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsPortableTextAutoLinkType = 'autoLink';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsPortableTextLinkClassName = 'text-primary underline';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsTextLinkPatternSource = '<([^<>\\s]+)>|(https?:\\/\\/[^\\s<>]+)';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsTextLinkProtocols = ['http:', 'https:', 'mailto:'];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsTextLinkTrailingCharacters = '.,;:!?)]}\'"';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PsTextWrapClassName = '[overflow-wrap:anywhere]';
|
package/dist/esm/index.js
CHANGED
|
@@ -35,6 +35,10 @@ export * from './components/ps-list-tile.js';
|
|
|
35
35
|
export * from './components/ps-metric-card.js';
|
|
36
36
|
export * from './components/ps-pill.js';
|
|
37
37
|
export * from './components/ps-portable-text.js';
|
|
38
|
+
export * from './components/ps-linked-text.js';
|
|
39
|
+
export * from './services/ps-text-link-service.js';
|
|
40
|
+
export * from './models/ps-text-link-segment-data.js';
|
|
41
|
+
export * from './constants/ps-portable-text-link-class-name.js';
|
|
38
42
|
export * from './components/ps-reflective-app-shell.js';
|
|
39
43
|
export * from './components/ps-scaled-preview.js';
|
|
40
44
|
export * from './components/ps-navigation-progress.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { PsPortableTextAutoLinkType } from '../constants/ps-portable-text-auto-link-type.js';
|
|
2
|
+
import { PsTextLinkService } from './ps-text-link-service.js';
|
|
3
|
+
export class PsPortableTextLinkService {
|
|
4
|
+
static linkify(blocks) {
|
|
5
|
+
return blocks.map((block) => (this.isBlock(block) ? this.linkifyBlock(block) : block));
|
|
6
|
+
}
|
|
7
|
+
static isBlock(value) {
|
|
8
|
+
return 'children' in value && Array.isArray(value.children);
|
|
9
|
+
}
|
|
10
|
+
static isSpan(value) {
|
|
11
|
+
return value._type === 'span' && 'text' in value && typeof value.text === 'string';
|
|
12
|
+
}
|
|
13
|
+
static linkifyBlock(block) {
|
|
14
|
+
const markDefs = block.markDefs === undefined ? [] : block.markDefs;
|
|
15
|
+
const added = [];
|
|
16
|
+
const children = [];
|
|
17
|
+
for (const child of block.children) {
|
|
18
|
+
if (!this.isSpan(child) || this.hasLinkMark(child, markDefs)) {
|
|
19
|
+
children.push(child);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const segments = PsTextLinkService.segments(child.text);
|
|
23
|
+
if (segments.length === 1 && segments[0].href === null) {
|
|
24
|
+
children.push(child);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const marks = child.marks === undefined ? [] : child.marks;
|
|
28
|
+
segments.forEach((segment, index) => {
|
|
29
|
+
const key = child._key === undefined ? undefined : `${child._key}-${index}`;
|
|
30
|
+
if (segment.href === null) {
|
|
31
|
+
const plain = { ...child, _key: key, text: segment.text };
|
|
32
|
+
children.push(plain);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const markKey = `${PsPortableTextAutoLinkType}-${added.length}`;
|
|
36
|
+
added.push({ _key: markKey, _type: PsPortableTextAutoLinkType, href: segment.href });
|
|
37
|
+
const linked = {
|
|
38
|
+
...child,
|
|
39
|
+
_key: key,
|
|
40
|
+
marks: [...marks, markKey],
|
|
41
|
+
text: segment.text,
|
|
42
|
+
};
|
|
43
|
+
children.push(linked);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return { ...block, children, markDefs: [...markDefs, ...added] };
|
|
47
|
+
}
|
|
48
|
+
static hasLinkMark(span, markDefs) {
|
|
49
|
+
if (span.marks === undefined)
|
|
50
|
+
return false;
|
|
51
|
+
return span.marks.some((mark) => markDefs.some((definition) => definition._key === mark && definition._type === 'link'));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { PsTextLinkPatternSource } from '../constants/ps-text-link-pattern-source.js';
|
|
2
|
+
import { PsTextLinkProtocols } from '../constants/ps-text-link-protocols.js';
|
|
3
|
+
import { PsTextLinkTrailingCharacters } from '../constants/ps-text-link-trailing-characters.js';
|
|
4
|
+
export class PsTextLinkService {
|
|
5
|
+
static segments(text) {
|
|
6
|
+
const segments = [];
|
|
7
|
+
const pattern = new RegExp(PsTextLinkPatternSource, 'g');
|
|
8
|
+
let cursor = 0;
|
|
9
|
+
let match = pattern.exec(text);
|
|
10
|
+
while (match !== null) {
|
|
11
|
+
this.pushText(segments, text.slice(cursor, match.index));
|
|
12
|
+
cursor = match.index + match[0].length;
|
|
13
|
+
if (match[1] !== undefined)
|
|
14
|
+
this.pushAngle(segments, match[0], match[1]);
|
|
15
|
+
else
|
|
16
|
+
this.pushBare(segments, match[2]);
|
|
17
|
+
match = pattern.exec(text);
|
|
18
|
+
}
|
|
19
|
+
this.pushText(segments, text.slice(cursor));
|
|
20
|
+
return segments;
|
|
21
|
+
}
|
|
22
|
+
static pushAngle(segments, raw, href) {
|
|
23
|
+
const url = this.parse(href);
|
|
24
|
+
if (url === null) {
|
|
25
|
+
this.pushText(segments, raw);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const label = url.protocol === 'mailto:' ? url.pathname : url.hostname.replace(/^www\./, '');
|
|
29
|
+
if (label === '') {
|
|
30
|
+
this.pushText(segments, raw);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
segments.push({ href, text: label });
|
|
34
|
+
}
|
|
35
|
+
static pushBare(segments, raw) {
|
|
36
|
+
let end = raw.length;
|
|
37
|
+
while (end > 0 && PsTextLinkTrailingCharacters.includes(raw.charAt(end - 1)))
|
|
38
|
+
end -= 1;
|
|
39
|
+
const href = raw.slice(0, end);
|
|
40
|
+
if (this.parse(href) === null) {
|
|
41
|
+
this.pushText(segments, raw);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
segments.push({ href, text: href });
|
|
45
|
+
this.pushText(segments, raw.slice(end));
|
|
46
|
+
}
|
|
47
|
+
static pushText(segments, text) {
|
|
48
|
+
if (text === '')
|
|
49
|
+
return;
|
|
50
|
+
const last = segments[segments.length - 1];
|
|
51
|
+
if (last !== undefined && last.href === null) {
|
|
52
|
+
last.text += text;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
segments.push({ href: null, text });
|
|
56
|
+
}
|
|
57
|
+
static parse(href) {
|
|
58
|
+
if (!URL.canParse(href))
|
|
59
|
+
return null;
|
|
60
|
+
const url = new URL(href);
|
|
61
|
+
return PsTextLinkProtocols.includes(url.protocol) ? url : null;
|
|
62
|
+
}
|
|
63
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,10 @@ export * from './components/ps-list-tile';
|
|
|
35
35
|
export * from './components/ps-metric-card';
|
|
36
36
|
export * from './components/ps-pill';
|
|
37
37
|
export * from './components/ps-portable-text';
|
|
38
|
+
export * from './components/ps-linked-text';
|
|
39
|
+
export * from './services/ps-text-link-service';
|
|
40
|
+
export * from './models/ps-text-link-segment-data';
|
|
41
|
+
export * from './constants/ps-portable-text-link-class-name';
|
|
38
42
|
export * from './components/ps-reflective-app-shell';
|
|
39
43
|
export * from './components/ps-scaled-preview';
|
|
40
44
|
export * from './components/ps-navigation-progress';
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,10 @@ __exportStar(require("./components/ps-list-tile"), exports);
|
|
|
51
51
|
__exportStar(require("./components/ps-metric-card"), exports);
|
|
52
52
|
__exportStar(require("./components/ps-pill"), exports);
|
|
53
53
|
__exportStar(require("./components/ps-portable-text"), exports);
|
|
54
|
+
__exportStar(require("./components/ps-linked-text"), exports);
|
|
55
|
+
__exportStar(require("./services/ps-text-link-service"), exports);
|
|
56
|
+
__exportStar(require("./models/ps-text-link-segment-data"), exports);
|
|
57
|
+
__exportStar(require("./constants/ps-portable-text-link-class-name"), exports);
|
|
54
58
|
__exportStar(require("./components/ps-reflective-app-shell"), exports);
|
|
55
59
|
__exportStar(require("./components/ps-scaled-preview"), exports);
|
|
56
60
|
__exportStar(require("./components/ps-navigation-progress"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TypedObject } from '@portabletext/types';
|
|
2
|
+
export declare class PsPortableTextLinkService {
|
|
3
|
+
static linkify(blocks: TypedObject[]): TypedObject[];
|
|
4
|
+
private static isBlock;
|
|
5
|
+
private static isSpan;
|
|
6
|
+
private static linkifyBlock;
|
|
7
|
+
private static hasLinkMark;
|
|
8
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsPortableTextLinkService = void 0;
|
|
4
|
+
const ps_portable_text_auto_link_type_1 = require("../constants/ps-portable-text-auto-link-type");
|
|
5
|
+
const ps_text_link_service_1 = require("./ps-text-link-service");
|
|
6
|
+
class PsPortableTextLinkService {
|
|
7
|
+
static linkify(blocks) {
|
|
8
|
+
return blocks.map((block) => (this.isBlock(block) ? this.linkifyBlock(block) : block));
|
|
9
|
+
}
|
|
10
|
+
static isBlock(value) {
|
|
11
|
+
return 'children' in value && Array.isArray(value.children);
|
|
12
|
+
}
|
|
13
|
+
static isSpan(value) {
|
|
14
|
+
return value._type === 'span' && 'text' in value && typeof value.text === 'string';
|
|
15
|
+
}
|
|
16
|
+
static linkifyBlock(block) {
|
|
17
|
+
const markDefs = block.markDefs === undefined ? [] : block.markDefs;
|
|
18
|
+
const added = [];
|
|
19
|
+
const children = [];
|
|
20
|
+
for (const child of block.children) {
|
|
21
|
+
if (!this.isSpan(child) || this.hasLinkMark(child, markDefs)) {
|
|
22
|
+
children.push(child);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const segments = ps_text_link_service_1.PsTextLinkService.segments(child.text);
|
|
26
|
+
if (segments.length === 1 && segments[0].href === null) {
|
|
27
|
+
children.push(child);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const marks = child.marks === undefined ? [] : child.marks;
|
|
31
|
+
segments.forEach((segment, index) => {
|
|
32
|
+
const key = child._key === undefined ? undefined : `${child._key}-${index}`;
|
|
33
|
+
if (segment.href === null) {
|
|
34
|
+
const plain = { ...child, _key: key, text: segment.text };
|
|
35
|
+
children.push(plain);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const markKey = `${ps_portable_text_auto_link_type_1.PsPortableTextAutoLinkType}-${added.length}`;
|
|
39
|
+
added.push({ _key: markKey, _type: ps_portable_text_auto_link_type_1.PsPortableTextAutoLinkType, href: segment.href });
|
|
40
|
+
const linked = {
|
|
41
|
+
...child,
|
|
42
|
+
_key: key,
|
|
43
|
+
marks: [...marks, markKey],
|
|
44
|
+
text: segment.text,
|
|
45
|
+
};
|
|
46
|
+
children.push(linked);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return { ...block, children, markDefs: [...markDefs, ...added] };
|
|
50
|
+
}
|
|
51
|
+
static hasLinkMark(span, markDefs) {
|
|
52
|
+
if (span.marks === undefined)
|
|
53
|
+
return false;
|
|
54
|
+
return span.marks.some((mark) => markDefs.some((definition) => definition._key === mark && definition._type === 'link'));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.PsPortableTextLinkService = PsPortableTextLinkService;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PsTextLinkSegmentData } from '../models/ps-text-link-segment-data';
|
|
2
|
+
export declare class PsTextLinkService {
|
|
3
|
+
static segments(text: string): PsTextLinkSegmentData[];
|
|
4
|
+
private static pushAngle;
|
|
5
|
+
private static pushBare;
|
|
6
|
+
private static pushText;
|
|
7
|
+
private static parse;
|
|
8
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PsTextLinkService = void 0;
|
|
4
|
+
const ps_text_link_pattern_source_1 = require("../constants/ps-text-link-pattern-source");
|
|
5
|
+
const ps_text_link_protocols_1 = require("../constants/ps-text-link-protocols");
|
|
6
|
+
const ps_text_link_trailing_characters_1 = require("../constants/ps-text-link-trailing-characters");
|
|
7
|
+
class PsTextLinkService {
|
|
8
|
+
static segments(text) {
|
|
9
|
+
const segments = [];
|
|
10
|
+
const pattern = new RegExp(ps_text_link_pattern_source_1.PsTextLinkPatternSource, 'g');
|
|
11
|
+
let cursor = 0;
|
|
12
|
+
let match = pattern.exec(text);
|
|
13
|
+
while (match !== null) {
|
|
14
|
+
this.pushText(segments, text.slice(cursor, match.index));
|
|
15
|
+
cursor = match.index + match[0].length;
|
|
16
|
+
if (match[1] !== undefined)
|
|
17
|
+
this.pushAngle(segments, match[0], match[1]);
|
|
18
|
+
else
|
|
19
|
+
this.pushBare(segments, match[2]);
|
|
20
|
+
match = pattern.exec(text);
|
|
21
|
+
}
|
|
22
|
+
this.pushText(segments, text.slice(cursor));
|
|
23
|
+
return segments;
|
|
24
|
+
}
|
|
25
|
+
static pushAngle(segments, raw, href) {
|
|
26
|
+
const url = this.parse(href);
|
|
27
|
+
if (url === null) {
|
|
28
|
+
this.pushText(segments, raw);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const label = url.protocol === 'mailto:' ? url.pathname : url.hostname.replace(/^www\./, '');
|
|
32
|
+
if (label === '') {
|
|
33
|
+
this.pushText(segments, raw);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
segments.push({ href, text: label });
|
|
37
|
+
}
|
|
38
|
+
static pushBare(segments, raw) {
|
|
39
|
+
let end = raw.length;
|
|
40
|
+
while (end > 0 && ps_text_link_trailing_characters_1.PsTextLinkTrailingCharacters.includes(raw.charAt(end - 1)))
|
|
41
|
+
end -= 1;
|
|
42
|
+
const href = raw.slice(0, end);
|
|
43
|
+
if (this.parse(href) === null) {
|
|
44
|
+
this.pushText(segments, raw);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
segments.push({ href, text: href });
|
|
48
|
+
this.pushText(segments, raw.slice(end));
|
|
49
|
+
}
|
|
50
|
+
static pushText(segments, text) {
|
|
51
|
+
if (text === '')
|
|
52
|
+
return;
|
|
53
|
+
const last = segments[segments.length - 1];
|
|
54
|
+
if (last !== undefined && last.href === null) {
|
|
55
|
+
last.text += text;
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
segments.push({ href: null, text });
|
|
59
|
+
}
|
|
60
|
+
static parse(href) {
|
|
61
|
+
if (!URL.canParse(href))
|
|
62
|
+
return null;
|
|
63
|
+
const url = new URL(href);
|
|
64
|
+
return ps_text_link_protocols_1.PsTextLinkProtocols.includes(url.protocol) ? url : null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.PsTextLinkService = PsTextLinkService;
|